The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/uipc_sem.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
    3  * Copyright (c) 2003-2005 SPARTA, Inc.
    4  * Copyright (c) 2005 Robert N. M. Watson
    5  * All rights reserved.
    6  *
    7  * This software was developed for the FreeBSD Project in part by Network
    8  * Associates Laboratories, the Security Research Division of Network
    9  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
   10  * as part of the DARPA CHATS research program.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD$");
   36 
   37 #include "opt_mac.h"
   38 #include "opt_posix.h"
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/sysproto.h>
   43 #include <sys/eventhandler.h>
   44 #include <sys/kernel.h>
   45 #include <sys/ksem.h>
   46 #include <sys/priv.h>
   47 #include <sys/proc.h>
   48 #include <sys/posix4.h>
   49 #include <sys/lock.h>
   50 #include <sys/mutex.h>
   51 #include <sys/module.h>
   52 #include <sys/condvar.h>
   53 #include <sys/sem.h>
   54 #include <sys/uio.h>
   55 #include <sys/semaphore.h>
   56 #include <sys/syscall.h>
   57 #include <sys/stat.h>
   58 #include <sys/sysent.h>
   59 #include <sys/sysctl.h>
   60 #include <sys/time.h>
   61 #include <sys/malloc.h>
   62 #include <sys/fcntl.h>
   63 #include <sys/_semaphore.h>
   64 
   65 #include <security/mac/mac_framework.h>
   66 
   67 static int sem_count_proc(struct proc *p);
   68 static struct ksem *sem_lookup_byname(const char *name);
   69 static int sem_create(struct thread *td, const char *name,
   70     struct ksem **ksret, mode_t mode, unsigned int value);
   71 static void sem_free(struct ksem *ksnew);
   72 static int sem_perm(struct thread *td, struct ksem *ks);
   73 static void sem_enter(struct proc *p, struct ksem *ks);
   74 static int sem_leave(struct proc *p, struct ksem *ks);
   75 static void sem_exechook(void *arg, struct proc *p,
   76     struct image_params *imgp);
   77 static void sem_exithook(void *arg, struct proc *p);
   78 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
   79     int flags);
   80 static int sem_hasopen(struct thread *td, struct ksem *ks);
   81 
   82 static int kern_sem_close(struct thread *td, semid_t id);
   83 static int kern_sem_post(struct thread *td, semid_t id);
   84 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
   85     struct timespec *abstime);
   86 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
   87     semid_t *idp);
   88 static int kern_sem_open(struct thread *td, int dir, const char *name,
   89     int oflag, mode_t mode, unsigned int value, semid_t *idp);
   90 static int kern_sem_unlink(struct thread *td, const char *name);
   91 
   92 #ifndef SEM_MAX
   93 #define SEM_MAX 30
   94 #endif
   95 
   96 #define SEM_MAX_NAMELEN 14
   97 
   98 #define SEM_TO_ID(x)    ((intptr_t)(x))
   99 #define ID_TO_SEM(x)    id_to_sem(x)
  100 
  101 /*
  102  * Available semaphores go here, this includes sem_init and any semaphores
  103  * created via sem_open that have not yet been unlinked.
  104  */
  105 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
  106 
  107 /*
  108  * Semaphores still in use but have been sem_unlink()'d go here.
  109  */
  110 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
  111 
  112 static struct mtx sem_lock;
  113 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
  114 
  115 static int nsems = 0;
  116 SYSCTL_DECL(_p1003_1b);
  117 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
  118 
  119 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
  120 
  121 #ifdef SEM_DEBUG
  122 #define DP(x)   printf x
  123 #else
  124 #define DP(x)
  125 #endif
  126 
  127 static __inline void
  128 sem_ref(struct ksem *ks)
  129 {
  130 
  131         mtx_assert(&sem_lock, MA_OWNED);
  132         ks->ks_ref++;
  133         DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
  134 }
  135 
  136 static __inline void
  137 sem_rel(struct ksem *ks)
  138 {
  139 
  140         mtx_assert(&sem_lock, MA_OWNED);
  141         DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
  142         if (--ks->ks_ref == 0)
  143                 sem_free(ks);
  144 }
  145 
  146 static __inline
  147 struct ksem *
  148 id_to_sem(semid_t id)
  149 {
  150         struct ksem *ks;
  151 
  152         mtx_assert(&sem_lock, MA_OWNED);
  153         DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
  154         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  155                 DP(("id_to_sem: ks = %p\n", ks));
  156                 if (ks == (struct ksem *)id)
  157                         return (ks);
  158         }
  159         return (NULL);
  160 }
  161 
  162 static struct ksem *
  163 sem_lookup_byname(const char *name)
  164 {
  165         struct ksem *ks;
  166 
  167         mtx_assert(&sem_lock, MA_OWNED);
  168         LIST_FOREACH(ks, &ksem_head, ks_entry)
  169                 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
  170                         return (ks);
  171         return (NULL);
  172 }
  173 
  174 static int
  175 sem_create(struct thread *td, const char *name, struct ksem **ksret,
  176     mode_t mode, unsigned int value)
  177 {
  178         struct ksem *ret;
  179         struct proc *p;
  180         struct ucred *uc;
  181         size_t len;
  182         int error;
  183 
  184         DP(("sem_create\n"));
  185         p = td->td_proc;
  186         uc = td->td_ucred;
  187         if (value > SEM_VALUE_MAX)
  188                 return (EINVAL);
  189         ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
  190         if (name != NULL) {
  191                 len = strlen(name);
  192                 if (len > SEM_MAX_NAMELEN) {
  193                         free(ret, M_SEM);
  194                         return (ENAMETOOLONG);
  195                 }
  196 
  197                 /* Name must start with a '/' but not contain one. */
  198                 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
  199                         free(ret, M_SEM);
  200                         return (EINVAL);
  201                 }
  202                 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
  203                 strcpy(ret->ks_name, name);
  204         } else {
  205                 ret->ks_name = NULL;
  206         }
  207         ret->ks_mode = mode;
  208         ret->ks_value = value;
  209         ret->ks_ref = 1;
  210         ret->ks_waiters = 0;
  211         ret->ks_uid = uc->cr_uid;
  212         ret->ks_gid = uc->cr_gid;
  213         ret->ks_onlist = 0;
  214         cv_init(&ret->ks_cv, "sem");
  215         LIST_INIT(&ret->ks_users);
  216 #ifdef MAC
  217         mac_init_posix_sem(ret);
  218         mac_create_posix_sem(uc, ret);
  219 #endif
  220         if (name != NULL)
  221                 sem_enter(td->td_proc, ret);
  222         *ksret = ret;
  223         mtx_lock(&sem_lock);
  224         nsems++;
  225         if (nsems > p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
  226                 sem_leave(td->td_proc, ret);
  227                 sem_free(ret);
  228                 error = ENFILE;
  229         } else
  230                 error = 0;
  231         mtx_unlock(&sem_lock);
  232         return (error);
  233 }
  234 
  235 #ifndef _SYS_SYSPROTO_H_
  236 struct ksem_init_args {
  237         unsigned int value;
  238         semid_t *idp;
  239 };
  240 int ksem_init(struct thread *td, struct ksem_init_args *uap);
  241 #endif
  242 int
  243 ksem_init(struct thread *td, struct ksem_init_args *uap)
  244 {
  245 
  246         return (kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp));
  247 }
  248 
  249 static int
  250 kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
  251 {
  252         struct ksem *ks;
  253         semid_t id;
  254         int error;
  255 
  256         error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
  257         if (error)
  258                 return (error);
  259         id = SEM_TO_ID(ks);
  260         if (dir == UIO_USERSPACE) {
  261                 error = copyout(&id, idp, sizeof(id));
  262                 if (error) {
  263                         mtx_lock(&sem_lock);
  264                         sem_rel(ks);
  265                         mtx_unlock(&sem_lock);
  266                         return (error);
  267                 }
  268         } else {
  269                 *idp = id;
  270         }
  271         mtx_lock(&sem_lock);
  272         LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
  273         ks->ks_onlist = 1;
  274         mtx_unlock(&sem_lock);
  275         return (error);
  276 }
  277 
  278 #ifndef _SYS_SYSPROTO_H_
  279 struct ksem_open_args {
  280         char *name;
  281         int oflag;
  282         mode_t mode;
  283         unsigned int value;
  284         semid_t *idp;   
  285 };
  286 int ksem_open(struct thread *td, struct ksem_open_args *uap);
  287 #endif
  288 int
  289 ksem_open(struct thread *td, struct ksem_open_args *uap)
  290 {
  291         char name[SEM_MAX_NAMELEN + 1];
  292         size_t done;
  293         int error;
  294 
  295         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
  296         if (error)
  297                 return (error);
  298         DP((">>> sem_open start\n"));
  299         error = kern_sem_open(td, UIO_USERSPACE,
  300             name, uap->oflag, uap->mode, uap->value, uap->idp);
  301         DP(("<<< sem_open end\n"));
  302         return (error);
  303 }
  304 
  305 static int
  306 kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
  307     mode_t mode, unsigned int value, semid_t *idp)
  308 {
  309         struct ksem *ksnew, *ks;
  310         int error;
  311         semid_t id;
  312 
  313         ksnew = NULL;
  314         mtx_lock(&sem_lock);
  315         ks = sem_lookup_byname(name);
  316 
  317         /*
  318          * If we found it but O_EXCL is set, error.
  319          */
  320         if (ks != NULL && (oflag & O_EXCL) != 0) {
  321                 mtx_unlock(&sem_lock);
  322                 return (EEXIST);
  323         }
  324 
  325         /*
  326          * If we didn't find it...
  327          */
  328         if (ks == NULL) {
  329                 /*
  330                  * didn't ask for creation? error.
  331                  */
  332                 if ((oflag & O_CREAT) == 0) {
  333                         mtx_unlock(&sem_lock);
  334                         return (ENOENT);
  335                 }
  336 
  337                 /*
  338                  * We may block during creation, so drop the lock.
  339                  */
  340                 mtx_unlock(&sem_lock);
  341                 error = sem_create(td, name, &ksnew, mode, value);
  342                 if (error != 0)
  343                         return (error);
  344                 id = SEM_TO_ID(ksnew);
  345                 if (dir == UIO_USERSPACE) {
  346                         DP(("about to copyout! %d to %p\n", id, idp));
  347                         error = copyout(&id, idp, sizeof(id));
  348                         if (error) {
  349                                 mtx_lock(&sem_lock);
  350                                 sem_leave(td->td_proc, ksnew);
  351                                 sem_rel(ksnew);
  352                                 mtx_unlock(&sem_lock);
  353                                 return (error);
  354                         }
  355                 } else {
  356                         DP(("about to set! %d to %p\n", id, idp));
  357                         *idp = id;
  358                 }
  359 
  360                 /*
  361                  * We need to make sure we haven't lost a race while
  362                  * allocating during creation.
  363                  */
  364                 mtx_lock(&sem_lock);
  365                 ks = sem_lookup_byname(name);
  366                 if (ks != NULL) {
  367                         /* we lost... */
  368                         sem_leave(td->td_proc, ksnew);
  369                         sem_rel(ksnew);
  370                         /* we lost and we can't loose... */
  371                         if ((oflag & O_EXCL) != 0) {
  372                                 mtx_unlock(&sem_lock);
  373                                 return (EEXIST);
  374                         }
  375                 } else {
  376                         DP(("sem_create: about to add to list...\n"));
  377                         LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 
  378                         DP(("sem_create: setting list bit...\n"));
  379                         ksnew->ks_onlist = 1;
  380                         DP(("sem_create: done, about to unlock...\n"));
  381                 }
  382         } else {
  383 #ifdef MAC
  384                 error = mac_check_posix_sem_open(td->td_ucred, ks);
  385                 if (error)
  386                         goto err_open;
  387 #endif
  388                 /*
  389                  * if we aren't the creator, then enforce permissions.
  390                  */
  391                 error = sem_perm(td, ks);
  392                 if (error)
  393                         goto err_open;
  394                 sem_ref(ks);
  395                 mtx_unlock(&sem_lock);
  396                 id = SEM_TO_ID(ks);
  397                 if (dir == UIO_USERSPACE) {
  398                         error = copyout(&id, idp, sizeof(id));
  399                         if (error) {
  400                                 mtx_lock(&sem_lock);
  401                                 sem_rel(ks);
  402                                 mtx_unlock(&sem_lock);
  403                                 return (error);
  404                         }
  405                 } else {
  406                         *idp = id;
  407                 }
  408                 sem_enter(td->td_proc, ks);
  409                 mtx_lock(&sem_lock);
  410                 sem_rel(ks);
  411         }
  412 err_open:
  413         mtx_unlock(&sem_lock);
  414         return (error);
  415 }
  416 
  417 static int
  418 sem_perm(struct thread *td, struct ksem *ks)
  419 {
  420         struct ucred *uc;
  421 
  422         /*
  423          * XXXRW: This permission routine appears to be incorrect.  If the
  424          * user matches, we shouldn't go on to the group if the user
  425          * permissions don't allow the action?  Not changed for now.  To fix,
  426          * change from a series of if (); if (); to if () else if () else...
  427          */
  428         uc = td->td_ucred;
  429         DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
  430             uc->cr_uid, uc->cr_gid,
  431              ks->ks_uid, ks->ks_gid, ks->ks_mode));
  432         if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
  433                 return (0);
  434         if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
  435                 return (0);
  436         if ((ks->ks_mode & S_IWOTH) != 0)
  437                 return (0);
  438         return (priv_check(td, PRIV_SEM_WRITE));
  439 }
  440 
  441 static void
  442 sem_free(struct ksem *ks)
  443 {
  444 
  445 #ifdef MAC
  446         mac_destroy_posix_sem(ks);
  447 #endif
  448         nsems--;
  449         if (ks->ks_onlist)
  450                 LIST_REMOVE(ks, ks_entry);
  451         if (ks->ks_name != NULL)
  452                 free(ks->ks_name, M_SEM);
  453         cv_destroy(&ks->ks_cv);
  454         free(ks, M_SEM);
  455 }
  456 
  457 static __inline struct kuser *
  458 sem_getuser(struct proc *p, struct ksem *ks)
  459 {
  460         struct kuser *k;
  461 
  462         LIST_FOREACH(k, &ks->ks_users, ku_next)
  463                 if (k->ku_pid == p->p_pid)
  464                         return (k);
  465         return (NULL);
  466 }
  467 
  468 static int
  469 sem_hasopen(struct thread *td, struct ksem *ks)
  470 {
  471         
  472         return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
  473             || sem_getuser(td->td_proc, ks) != NULL);
  474 }
  475 
  476 static int
  477 sem_leave(struct proc *p, struct ksem *ks)
  478 {
  479         struct kuser *k;
  480 
  481         DP(("sem_leave: ks = %p\n", ks));
  482         k = sem_getuser(p, ks);
  483         DP(("sem_leave: ks = %p, k = %p\n", ks, k));
  484         if (k != NULL) {
  485                 LIST_REMOVE(k, ku_next);
  486                 sem_rel(ks);
  487                 DP(("sem_leave: about to free k\n"));
  488                 free(k, M_SEM);
  489                 DP(("sem_leave: returning\n"));
  490                 return (0);
  491         }
  492         return (EINVAL);
  493 }
  494 
  495 static void
  496 sem_enter(struct proc *p, struct ksem *ks)
  497 {
  498         struct kuser *ku, *k;
  499 
  500         ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
  501         ku->ku_pid = p->p_pid;
  502         mtx_lock(&sem_lock);
  503         k = sem_getuser(p, ks);
  504         if (k != NULL) {
  505                 mtx_unlock(&sem_lock);
  506                 free(ku, M_TEMP);
  507                 return;
  508         }
  509         LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
  510         sem_ref(ks);
  511         mtx_unlock(&sem_lock);
  512 }
  513 
  514 #ifndef _SYS_SYSPROTO_H_
  515 struct ksem_unlink_args {
  516         char *name;
  517 };
  518 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
  519 #endif
  520 int
  521 ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
  522 {
  523         char name[SEM_MAX_NAMELEN + 1];
  524         size_t done;
  525         int error;
  526 
  527         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
  528         return (error ? error :
  529             kern_sem_unlink(td, name));
  530 }
  531 
  532 static int
  533 kern_sem_unlink(struct thread *td, const char *name)
  534 {
  535         struct ksem *ks;
  536         int error;
  537 
  538         mtx_lock(&sem_lock);
  539         ks = sem_lookup_byname(name);
  540         if (ks != NULL) {
  541 #ifdef MAC
  542                 error = mac_check_posix_sem_unlink(td->td_ucred, ks);
  543                 if (error) {
  544                         mtx_unlock(&sem_lock);
  545                         return (error);
  546                 }
  547 #endif
  548                 error = sem_perm(td, ks);
  549         } else
  550                 error = ENOENT;
  551         DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
  552         if (error == 0) {
  553                 LIST_REMOVE(ks, ks_entry);
  554                 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry); 
  555                 sem_rel(ks);
  556         }
  557         mtx_unlock(&sem_lock);
  558         return (error);
  559 }
  560 
  561 #ifndef _SYS_SYSPROTO_H_
  562 struct ksem_close_args {
  563         semid_t id;
  564 };
  565 int ksem_close(struct thread *td, struct ksem_close_args *uap);
  566 #endif
  567 int
  568 ksem_close(struct thread *td, struct ksem_close_args *uap)
  569 {
  570 
  571         return (kern_sem_close(td, uap->id));
  572 }
  573 
  574 static int
  575 kern_sem_close(struct thread *td, semid_t id)
  576 {
  577         struct ksem *ks;
  578         int error;
  579 
  580         error = EINVAL;
  581         mtx_lock(&sem_lock);
  582         ks = ID_TO_SEM(id);
  583 
  584         /*
  585          * This is not a valid operation for unnamed sems.
  586          */
  587         if (ks != NULL && ks->ks_name != NULL)
  588                 error = sem_leave(td->td_proc, ks);
  589         mtx_unlock(&sem_lock);
  590         return (error);
  591 }
  592 
  593 #ifndef _SYS_SYSPROTO_H_
  594 struct ksem_post_args {
  595         semid_t id;
  596 };
  597 int ksem_post(struct thread *td, struct ksem_post_args *uap);
  598 #endif
  599 int
  600 ksem_post(struct thread *td, struct ksem_post_args *uap)
  601 {
  602 
  603         return (kern_sem_post(td, uap->id));
  604 }
  605 
  606 static int
  607 kern_sem_post(struct thread *td, semid_t id)
  608 {
  609         struct ksem *ks;
  610         int error;
  611 
  612         mtx_lock(&sem_lock);
  613         ks = ID_TO_SEM(id);
  614         if (ks == NULL || !sem_hasopen(td, ks)) {
  615                 error = EINVAL;
  616                 goto err;
  617         }
  618 #ifdef MAC
  619         error = mac_check_posix_sem_post(td->td_ucred, ks);
  620         if (error)
  621                 goto err;
  622 #endif
  623         if (ks->ks_value == SEM_VALUE_MAX) {
  624                 error = EOVERFLOW;
  625                 goto err;
  626         }
  627         ++ks->ks_value;
  628         if (ks->ks_waiters > 0)
  629                 cv_signal(&ks->ks_cv);
  630         error = 0;
  631 err:
  632         mtx_unlock(&sem_lock);
  633         return (error);
  634 }
  635 
  636 #ifndef _SYS_SYSPROTO_H_
  637 struct ksem_wait_args {
  638         semid_t id;
  639 };
  640 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
  641 #endif
  642 int
  643 ksem_wait(struct thread *td, struct ksem_wait_args *uap)
  644 {
  645 
  646         return (kern_sem_wait(td, uap->id, 0, NULL));
  647 }
  648 
  649 #ifndef _SYS_SYSPROTO_H_
  650 struct ksem_timedwait_args {
  651         semid_t id;
  652         const struct timespec *abstime;
  653 };
  654 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
  655 #endif
  656 int
  657 ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
  658 {
  659         struct timespec abstime;
  660         struct timespec *ts;
  661         int error;
  662 
  663         /*
  664          * We allow a null timespec (wait forever).
  665          */
  666         if (uap->abstime == NULL)
  667                 ts = NULL;
  668         else {
  669                 error = copyin(uap->abstime, &abstime, sizeof(abstime));
  670                 if (error != 0)
  671                         return (error);
  672                 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
  673                         return (EINVAL);
  674                 ts = &abstime;
  675         }
  676         return (kern_sem_wait(td, uap->id, 0, ts));
  677 }
  678 
  679 #ifndef _SYS_SYSPROTO_H_
  680 struct ksem_trywait_args {
  681         semid_t id;
  682 };
  683 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
  684 #endif
  685 int
  686 ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
  687 {
  688 
  689         return (kern_sem_wait(td, uap->id, 1, NULL));
  690 }
  691 
  692 static int
  693 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
  694     struct timespec *abstime)
  695 {
  696         struct timespec ts1, ts2;
  697         struct timeval tv;
  698         struct ksem *ks;
  699         int error;
  700 
  701         DP((">>> kern_sem_wait entered!\n"));
  702         mtx_lock(&sem_lock);
  703         ks = ID_TO_SEM(id);
  704         if (ks == NULL) {
  705                 DP(("kern_sem_wait ks == NULL\n"));
  706                 error = EINVAL;
  707                 goto err;
  708         }
  709         sem_ref(ks);
  710         if (!sem_hasopen(td, ks)) {
  711                 DP(("kern_sem_wait hasopen failed\n"));
  712                 error = EINVAL;
  713                 goto err;
  714         }
  715 #ifdef MAC
  716         error = mac_check_posix_sem_wait(td->td_ucred, ks);
  717         if (error) {
  718                 DP(("kern_sem_wait mac failed\n"));
  719                 goto err;
  720         }
  721 #endif
  722         DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
  723         if (ks->ks_value == 0) {
  724                 ks->ks_waiters++;
  725                 if (tryflag != 0)
  726                         error = EAGAIN;
  727                 else if (abstime == NULL)
  728                         error = cv_wait_sig(&ks->ks_cv, &sem_lock);
  729                 else {
  730                         for (;;) {
  731                                 ts1 = *abstime;
  732                                 getnanotime(&ts2);
  733                                 timespecsub(&ts1, &ts2);
  734                                 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
  735                                 if (tv.tv_sec < 0) {
  736                                         error = ETIMEDOUT;
  737                                         break;
  738                                 }
  739                                 error = cv_timedwait_sig(&ks->ks_cv,
  740                                     &sem_lock, tvtohz(&tv));
  741                                 if (error != EWOULDBLOCK)
  742                                         break;
  743                         }
  744                 }
  745                 ks->ks_waiters--;
  746                 if (error)
  747                         goto err;
  748         }
  749         ks->ks_value--;
  750         error = 0;
  751 err:
  752         if (ks != NULL)
  753                 sem_rel(ks);
  754         mtx_unlock(&sem_lock);
  755         DP(("<<< kern_sem_wait leaving, error = %d\n", error));
  756         return (error);
  757 }
  758 
  759 #ifndef _SYS_SYSPROTO_H_
  760 struct ksem_getvalue_args {
  761         semid_t id;
  762         int *val;
  763 };
  764 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
  765 #endif
  766 int
  767 ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
  768 {
  769         struct ksem *ks;
  770         int error, val;
  771 
  772         mtx_lock(&sem_lock);
  773         ks = ID_TO_SEM(uap->id);
  774         if (ks == NULL || !sem_hasopen(td, ks)) {
  775                 mtx_unlock(&sem_lock);
  776                 return (EINVAL);
  777         }
  778 #ifdef MAC
  779         error = mac_check_posix_sem_getvalue(td->td_ucred, ks);
  780         if (error) {
  781                 mtx_unlock(&sem_lock);
  782                 return (error);
  783         }
  784 #endif
  785         val = ks->ks_value;
  786         mtx_unlock(&sem_lock);
  787         error = copyout(&val, uap->val, sizeof(val));
  788         return (error);
  789 }
  790 
  791 #ifndef _SYS_SYSPROTO_H_
  792 struct ksem_destroy_args {
  793         semid_t id;
  794 };
  795 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
  796 #endif
  797 int
  798 ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
  799 {
  800         struct ksem *ks;
  801         int error;
  802 
  803         mtx_lock(&sem_lock);
  804         ks = ID_TO_SEM(uap->id);
  805         if (ks == NULL || !sem_hasopen(td, ks) ||
  806             ks->ks_name != NULL) {
  807                 error = EINVAL;
  808                 goto err;
  809         }
  810 #ifdef MAC
  811         error = mac_check_posix_sem_destroy(td->td_ucred, ks);
  812         if (error)
  813                 goto err;
  814 #endif
  815         if (ks->ks_waiters != 0) {
  816                 error = EBUSY;
  817                 goto err;
  818         }
  819         sem_rel(ks);
  820         error = 0;
  821 err:
  822         mtx_unlock(&sem_lock);
  823         return (error);
  824 }
  825 
  826 /*
  827  * Count the number of kusers associated with a proc, so as to guess at how
  828  * many to allocate when forking.
  829  */
  830 static int
  831 sem_count_proc(struct proc *p)
  832 {
  833         struct ksem *ks;
  834         struct kuser *ku;
  835         int count;
  836 
  837         mtx_assert(&sem_lock, MA_OWNED);
  838 
  839         count = 0;
  840         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  841                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  842                         if (ku->ku_pid == p->p_pid)
  843                                 count++;
  844                 }
  845         }
  846         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
  847                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  848                         if (ku->ku_pid == p->p_pid)
  849                                 count++;
  850                 }
  851         }
  852         return (count);
  853 }
  854 
  855 /*
  856  * When a process forks, the child process must gain a reference to each open
  857  * semaphore in the parent process, whether it is unlinked or not.  This
  858  * requires allocating a kuser structure for each semaphore reference in the
  859  * new process.  Because the set of semaphores in the parent can change while
  860  * the fork is in progress, we have to handle races -- first we attempt to
  861  * allocate enough storage to acquire references to each of the semaphores,
  862  * then we enter the semaphores and release the temporary references.
  863  */
  864 static void
  865 sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
  866 {
  867         struct ksem *ks, **sem_array;
  868         int count, i, new_count;
  869         struct kuser *ku;
  870 
  871         mtx_lock(&sem_lock);
  872         count = sem_count_proc(p1);
  873         if (count == 0) {
  874                 mtx_unlock(&sem_lock);
  875                 return;
  876         }
  877 race_lost:
  878         mtx_assert(&sem_lock, MA_OWNED);
  879         mtx_unlock(&sem_lock);
  880         sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
  881         mtx_lock(&sem_lock);
  882         new_count = sem_count_proc(p1);
  883         if (count < new_count) {
  884                 /* Lost race, repeat and allocate more storage. */
  885                 free(sem_array, M_TEMP);
  886                 count = new_count;
  887                 goto race_lost;
  888         }
  889 
  890         /*
  891          * Given an array capable of storing an adequate number of semaphore
  892          * references, now walk the list of semaphores and acquire a new
  893          * reference for any semaphore opened by p1.
  894          */
  895         count = new_count;
  896         i = 0;
  897         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  898                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  899                         if (ku->ku_pid == p1->p_pid) {
  900                                 sem_ref(ks);
  901                                 sem_array[i] = ks;
  902                                 i++;
  903                                 break;
  904                         }
  905                 }
  906         }
  907         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
  908                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  909                         if (ku->ku_pid == p1->p_pid) {
  910                                 sem_ref(ks);
  911                                 sem_array[i] = ks;
  912                                 i++;
  913                                 break;
  914                         }
  915                 }
  916         }
  917         mtx_unlock(&sem_lock);
  918         KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
  919 
  920         /*
  921          * Now cause p2 to enter each of the referenced semaphores, then
  922          * release our temporary reference.  This is pretty inefficient.
  923          * Finally, free our temporary array.
  924          */
  925         for (i = 0; i < count; i++) {
  926                 sem_enter(p2, sem_array[i]);
  927                 mtx_lock(&sem_lock);
  928                 sem_rel(sem_array[i]);
  929                 mtx_unlock(&sem_lock);
  930         }
  931         free(sem_array, M_TEMP);
  932 }
  933 
  934 static void
  935 sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
  936 {
  937         sem_exithook(arg, p);           
  938 }
  939 
  940 static void
  941 sem_exithook(void *arg, struct proc *p)
  942 {
  943         struct ksem *ks, *ksnext;
  944 
  945         mtx_lock(&sem_lock);
  946         ks = LIST_FIRST(&ksem_head);
  947         while (ks != NULL) {
  948                 ksnext = LIST_NEXT(ks, ks_entry);
  949                 sem_leave(p, ks);
  950                 ks = ksnext;
  951         }
  952         ks = LIST_FIRST(&ksem_deadhead);
  953         while (ks != NULL) {
  954                 ksnext = LIST_NEXT(ks, ks_entry);
  955                 sem_leave(p, ks);
  956                 ks = ksnext;
  957         }
  958         mtx_unlock(&sem_lock);
  959 }
  960 
  961 static int
  962 sem_modload(struct module *module, int cmd, void *arg)
  963 {
  964         int error = 0;
  965 
  966         switch (cmd) {
  967         case MOD_LOAD:
  968                 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
  969                 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
  970                 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
  971                 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit,
  972                     sem_exithook, NULL, EVENTHANDLER_PRI_ANY);
  973                 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec,
  974                     sem_exechook, NULL, EVENTHANDLER_PRI_ANY);
  975                 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork,
  976                     sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
  977                 break;
  978 
  979         case MOD_UNLOAD:
  980                 if (nsems != 0) {
  981                         error = EOPNOTSUPP;
  982                         break;
  983                 }
  984                 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
  985                 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
  986                 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
  987                 mtx_destroy(&sem_lock);
  988                 break;
  989 
  990         case MOD_SHUTDOWN:
  991                 break;
  992         default:
  993                 error = EINVAL;
  994                 break;
  995         }
  996         return (error);
  997 }
  998 
  999 static moduledata_t sem_mod = {
 1000         "sem",
 1001         &sem_modload,
 1002         NULL
 1003 };
 1004 
 1005 SYSCALL_MODULE_HELPER(ksem_init);
 1006 SYSCALL_MODULE_HELPER(ksem_open);
 1007 SYSCALL_MODULE_HELPER(ksem_unlink);
 1008 SYSCALL_MODULE_HELPER(ksem_close);
 1009 SYSCALL_MODULE_HELPER(ksem_post);
 1010 SYSCALL_MODULE_HELPER(ksem_wait);
 1011 SYSCALL_MODULE_HELPER(ksem_timedwait);
 1012 SYSCALL_MODULE_HELPER(ksem_trywait);
 1013 SYSCALL_MODULE_HELPER(ksem_getvalue);
 1014 SYSCALL_MODULE_HELPER(ksem_destroy);
 1015 
 1016 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
 1017 MODULE_VERSION(sem, 1);

Cache object: 3ace7e848934df24af64f89217b32da6


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.