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, struct image_params *imgp);
   76 static void sem_exithook(void *arg, struct proc *p);
   77 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
   78     int flags);
   79 static int sem_hasopen(struct thread *td, struct ksem *ks);
   80 
   81 static int kern_sem_close(struct thread *td, semid_t id);
   82 static int kern_sem_post(struct thread *td, semid_t id);
   83 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
   84     struct timespec *abstime);
   85 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
   86     semid_t *idp);
   87 static int kern_sem_open(struct thread *td, int dir, const char *name,
   88     int oflag, mode_t mode, unsigned int value, semid_t *idp);
   89 static int kern_sem_unlink(struct thread *td, const char *name);
   90 
   91 #ifndef SEM_MAX
   92 #define SEM_MAX 30
   93 #endif
   94 
   95 #define SEM_MAX_NAMELEN 14
   96 
   97 #define SEM_TO_ID(x)    ((intptr_t)(x))
   98 #define ID_TO_SEM(x)    id_to_sem(x)
   99 
  100 /*
  101  * available semaphores go here, this includes sem_init and any semaphores
  102  * created via sem_open that have not yet been unlinked.
  103  */
  104 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
  105 /*
  106  * semaphores still in use but have been sem_unlink()'d go here.
  107  */
  108 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
  109 
  110 static struct mtx sem_lock;
  111 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
  112 
  113 static int nsems = 0;
  114 SYSCTL_DECL(_p1003_1b);
  115 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
  116 
  117 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
  118 
  119 #ifdef SEM_DEBUG
  120 #define DP(x)   printf x
  121 #else
  122 #define DP(x)
  123 #endif
  124 
  125 static __inline
  126 void
  127 sem_ref(struct ksem *ks)
  128 {
  129 
  130         mtx_assert(&sem_lock, MA_OWNED);
  131         ks->ks_ref++;
  132         DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
  133 }
  134 
  135 static __inline
  136 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 struct ksem *id_to_sem(semid_t id);
  147 
  148 static __inline
  149 struct ksem *
  150 id_to_sem(semid_t id)
  151 {
  152         struct ksem *ks;
  153 
  154         mtx_assert(&sem_lock, MA_OWNED);
  155         DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
  156         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  157                 DP(("id_to_sem: ks = %p\n", ks));
  158                 if (ks == (struct ksem *)id)
  159                         return (ks);
  160         }
  161         return (NULL);
  162 }
  163 
  164 static struct ksem *
  165 sem_lookup_byname(const char *name)
  166 {
  167         struct ksem *ks;
  168 
  169         mtx_assert(&sem_lock, MA_OWNED);
  170         LIST_FOREACH(ks, &ksem_head, ks_entry)
  171                 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
  172                         return (ks);
  173         return (NULL);
  174 }
  175 
  176 static int
  177 sem_create(struct thread *td, const char *name, struct ksem **ksret,
  178     mode_t mode, unsigned int value)
  179 {
  180         struct ksem *ret;
  181         struct proc *p;
  182         struct ucred *uc;
  183         size_t len;
  184         int error;
  185 
  186         DP(("sem_create\n"));
  187         p = td->td_proc;
  188         uc = td->td_ucred;
  189         if (value > SEM_VALUE_MAX)
  190                 return (EINVAL);
  191         ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
  192         if (name != NULL) {
  193                 len = strlen(name);
  194                 if (len > SEM_MAX_NAMELEN) {
  195                         free(ret, M_SEM);
  196                         return (ENAMETOOLONG);
  197                 }
  198                 /* name must start with a '/' but not contain one. */
  199                 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
  200                         free(ret, M_SEM);
  201                         return (EINVAL);
  202                 }
  203                 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
  204                 strcpy(ret->ks_name, name);
  205         } else {
  206                 ret->ks_name = NULL;
  207         }
  208         ret->ks_mode = mode;
  209         ret->ks_value = value;
  210         ret->ks_ref = 1;
  211         ret->ks_waiters = 0;
  212         ret->ks_uid = uc->cr_uid;
  213         ret->ks_gid = uc->cr_gid;
  214         ret->ks_onlist = 0;
  215         cv_init(&ret->ks_cv, "sem");
  216         LIST_INIT(&ret->ks_users);
  217 #ifdef MAC
  218         mac_init_posix_sem(ret);
  219         mac_create_posix_sem(uc, ret);
  220 #endif
  221         if (name != NULL)
  222                 sem_enter(td->td_proc, ret);
  223         *ksret = ret;
  224         mtx_lock(&sem_lock);
  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                 nsems++;
  231                 error = 0;
  232         }
  233         mtx_unlock(&sem_lock);
  234         return (error);
  235 }
  236 
  237 #ifndef _SYS_SYSPROTO_H_
  238 struct ksem_init_args {
  239         unsigned int value;
  240         semid_t *idp;
  241 };
  242 int ksem_init(struct thread *td, struct ksem_init_args *uap);
  243 #endif
  244 int
  245 ksem_init(struct thread *td, struct ksem_init_args *uap)
  246 {
  247         int error;
  248 
  249         error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
  250         return (error);
  251 }
  252 
  253 static int
  254 kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
  255 {
  256         struct ksem *ks;
  257         semid_t id;
  258         int error;
  259 
  260         error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
  261         if (error)
  262                 return (error);
  263         id = SEM_TO_ID(ks);
  264         if (dir == UIO_USERSPACE) {
  265                 error = copyout(&id, idp, sizeof(id));
  266                 if (error) {
  267                         mtx_lock(&sem_lock);
  268                         sem_rel(ks);
  269                         mtx_unlock(&sem_lock);
  270                         return (error);
  271                 }
  272         } else {
  273                 *idp = id;
  274         }
  275         mtx_lock(&sem_lock);
  276         LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
  277         ks->ks_onlist = 1;
  278         mtx_unlock(&sem_lock);
  279         return (error);
  280 }
  281 
  282 #ifndef _SYS_SYSPROTO_H_
  283 struct ksem_open_args {
  284         char *name;
  285         int oflag;
  286         mode_t mode;
  287         unsigned int value;
  288         semid_t *idp;   
  289 };
  290 int ksem_open(struct thread *td, struct ksem_open_args *uap);
  291 #endif
  292 int
  293 ksem_open(struct thread *td, struct ksem_open_args *uap)
  294 {
  295         char name[SEM_MAX_NAMELEN + 1];
  296         size_t done;
  297         int error;
  298 
  299         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
  300         if (error)
  301                 return (error);
  302         DP((">>> sem_open start\n"));
  303         error = kern_sem_open(td, UIO_USERSPACE,
  304             name, uap->oflag, uap->mode, uap->value, uap->idp);
  305         DP(("<<< sem_open end\n"));
  306         return (error);
  307 }
  308 
  309 static int
  310 kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
  311     mode_t mode, unsigned int value, semid_t *idp)
  312 {
  313         struct ksem *ksnew, *ks;
  314         int error;
  315         semid_t id;
  316 
  317         ksnew = NULL;
  318         mtx_lock(&sem_lock);
  319         ks = sem_lookup_byname(name);
  320         /*
  321          * If we found it but O_EXCL is set, error.
  322          */
  323         if (ks != NULL && (oflag & O_EXCL) != 0) {
  324                 mtx_unlock(&sem_lock);
  325                 return (EEXIST);
  326         }
  327         /*
  328          * If we didn't find it...
  329          */
  330         if (ks == NULL) {
  331                 /*
  332                  * didn't ask for creation? error.
  333                  */
  334                 if ((oflag & O_CREAT) == 0) {
  335                         mtx_unlock(&sem_lock);
  336                         return (ENOENT);
  337                 }
  338                 /*
  339                  * We may block during creation, so drop the lock.
  340                  */
  341                 mtx_unlock(&sem_lock);
  342                 error = sem_create(td, name, &ksnew, mode, value);
  343                 if (error != 0)
  344                         return (error);
  345                 id = SEM_TO_ID(ksnew);
  346                 if (dir == UIO_USERSPACE) {
  347                         DP(("about to copyout! %d to %p\n", id, idp));
  348                         error = copyout(&id, idp, sizeof(id));
  349                         if (error) {
  350                                 mtx_lock(&sem_lock);
  351                                 sem_leave(td->td_proc, ksnew);
  352                                 sem_rel(ksnew);
  353                                 mtx_unlock(&sem_lock);
  354                                 return (error);
  355                         }
  356                 } else {
  357                         DP(("about to set! %d to %p\n", id, idp));
  358                         *idp = id;
  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 *sem_getuser(struct proc *p, struct ksem *ks);
  458 
  459 static __inline struct kuser *
  460 sem_getuser(struct proc *p, struct ksem *ks)
  461 {
  462         struct kuser *k;
  463 
  464         LIST_FOREACH(k, &ks->ks_users, ku_next)
  465                 if (k->ku_pid == p->p_pid)
  466                         return (k);
  467         return (NULL);
  468 }
  469 
  470 static int
  471 sem_hasopen(struct thread *td, struct ksem *ks)
  472 {
  473         
  474         return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
  475             || sem_getuser(td->td_proc, ks) != NULL);
  476 }
  477 
  478 static int
  479 sem_leave(struct proc *p, struct ksem *ks)
  480 {
  481         struct kuser *k;
  482 
  483         DP(("sem_leave: ks = %p\n", ks));
  484         k = sem_getuser(p, ks);
  485         DP(("sem_leave: ks = %p, k = %p\n", ks, k));
  486         if (k != NULL) {
  487                 LIST_REMOVE(k, ku_next);
  488                 sem_rel(ks);
  489                 DP(("sem_leave: about to free k\n"));
  490                 free(k, M_SEM);
  491                 DP(("sem_leave: returning\n"));
  492                 return (0);
  493         }
  494         return (EINVAL);
  495 }
  496 
  497 static void
  498 sem_enter(p, ks)
  499         struct proc *p;
  500         struct ksem *ks;
  501 {
  502         struct kuser *ku, *k;
  503 
  504         ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
  505         ku->ku_pid = p->p_pid;
  506         mtx_lock(&sem_lock);
  507         k = sem_getuser(p, ks);
  508         if (k != NULL) {
  509                 mtx_unlock(&sem_lock);
  510                 free(ku, M_TEMP);
  511                 return;
  512         }
  513         LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
  514         sem_ref(ks);
  515         mtx_unlock(&sem_lock);
  516 }
  517 
  518 #ifndef _SYS_SYSPROTO_H_
  519 struct ksem_unlink_args {
  520         char *name;
  521 };
  522 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
  523 #endif
  524 int
  525 ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
  526 {
  527         char name[SEM_MAX_NAMELEN + 1];
  528         size_t done;
  529         int error;
  530 
  531         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
  532         return (error ? error :
  533             kern_sem_unlink(td, name));
  534 }
  535 
  536 static int
  537 kern_sem_unlink(struct thread *td, const char *name)
  538 {
  539         struct ksem *ks;
  540         int error;
  541 
  542         mtx_lock(&sem_lock);
  543         ks = sem_lookup_byname(name);
  544         if (ks != NULL) {
  545 #ifdef MAC
  546                 error = mac_check_posix_sem_unlink(td->td_ucred, ks);
  547                 if (error) {
  548                         mtx_unlock(&sem_lock);
  549                         return (error);
  550                 }
  551 #endif
  552                 error = sem_perm(td, ks);
  553         } else
  554                 error = ENOENT;
  555         DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
  556         if (error == 0) {
  557                 LIST_REMOVE(ks, ks_entry);
  558                 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry); 
  559                 sem_rel(ks);
  560         }
  561         mtx_unlock(&sem_lock);
  562         return (error);
  563 }
  564 
  565 #ifndef _SYS_SYSPROTO_H_
  566 struct ksem_close_args {
  567         semid_t id;
  568 };
  569 int ksem_close(struct thread *td, struct ksem_close_args *uap);
  570 #endif
  571 int
  572 ksem_close(struct thread *td, struct ksem_close_args *uap)
  573 {
  574 
  575         return (kern_sem_close(td, uap->id));
  576 }
  577 
  578 static int
  579 kern_sem_close(struct thread *td, semid_t id)
  580 {
  581         struct ksem *ks;
  582         int error;
  583 
  584         error = EINVAL;
  585         mtx_lock(&sem_lock);
  586         ks = ID_TO_SEM(id);
  587         /* this is not a valid operation for unnamed sems */
  588         if (ks != NULL && ks->ks_name != NULL)
  589                 error = sem_leave(td->td_proc, ks);
  590         mtx_unlock(&sem_lock);
  591         return (error);
  592 }
  593 
  594 #ifndef _SYS_SYSPROTO_H_
  595 struct ksem_post_args {
  596         semid_t id;
  597 };
  598 int ksem_post(struct thread *td, struct ksem_post_args *uap);
  599 #endif
  600 int
  601 ksem_post(struct thread *td, struct ksem_post_args *uap)
  602 {
  603 
  604         return (kern_sem_post(td, uap->id));
  605 }
  606 
  607 static int
  608 kern_sem_post(struct thread *td, semid_t id)
  609 {
  610         struct ksem *ks;
  611         int error;
  612 
  613         mtx_lock(&sem_lock);
  614         ks = ID_TO_SEM(id);
  615         if (ks == NULL || !sem_hasopen(td, ks)) {
  616                 error = EINVAL;
  617                 goto err;
  618         }
  619 #ifdef MAC
  620         error = mac_check_posix_sem_post(td->td_ucred, ks);
  621         if (error)
  622                 goto err;
  623 #endif
  624         if (ks->ks_value == SEM_VALUE_MAX) {
  625                 error = EOVERFLOW;
  626                 goto err;
  627         }
  628         ++ks->ks_value;
  629         if (ks->ks_waiters > 0)
  630                 cv_signal(&ks->ks_cv);
  631         error = 0;
  632 err:
  633         mtx_unlock(&sem_lock);
  634         return (error);
  635 }
  636 
  637 #ifndef _SYS_SYSPROTO_H_
  638 struct ksem_wait_args {
  639         semid_t id;
  640 };
  641 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
  642 #endif
  643 int
  644 ksem_wait(struct thread *td, struct ksem_wait_args *uap)
  645 {
  646 
  647         return (kern_sem_wait(td, uap->id, 0, NULL));
  648 }
  649 
  650 #ifndef _SYS_SYSPROTO_H_
  651 struct ksem_timedwait_args {
  652         semid_t id;
  653         const struct timespec *abstime;
  654 };
  655 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
  656 #endif
  657 int
  658 ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
  659 {
  660         struct timespec abstime;
  661         struct timespec *ts;
  662         int error;
  663 
  664         /* We allow a null timespec (wait forever). */
  665         if (uap->abstime == NULL)
  666                 ts = NULL;
  667         else {
  668                 error = copyin(uap->abstime, &abstime, sizeof(abstime));
  669                 if (error != 0)
  670                         return (error);
  671                 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
  672                         return (EINVAL);
  673                 ts = &abstime;
  674         }
  675         return (kern_sem_wait(td, uap->id, 0, ts));
  676 }
  677 
  678 #ifndef _SYS_SYSPROTO_H_
  679 struct ksem_trywait_args {
  680         semid_t id;
  681 };
  682 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
  683 #endif
  684 int
  685 ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
  686 {
  687 
  688         return (kern_sem_wait(td, uap->id, 1, NULL));
  689 }
  690 
  691 static int
  692 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
  693     struct timespec *abstime)
  694 {
  695         struct timespec ts1, ts2;
  696         struct timeval tv;
  697         struct ksem *ks;
  698         int error;
  699 
  700         DP((">>> kern_sem_wait entered!\n"));
  701         mtx_lock(&sem_lock);
  702         ks = ID_TO_SEM(id);
  703         if (ks == NULL) {
  704                 DP(("kern_sem_wait ks == NULL\n"));
  705                 error = EINVAL;
  706                 goto err;
  707         }
  708         sem_ref(ks);
  709         if (!sem_hasopen(td, ks)) {
  710                 DP(("kern_sem_wait hasopen failed\n"));
  711                 error = EINVAL;
  712                 goto err;
  713         }
  714 #ifdef MAC
  715         error = mac_check_posix_sem_wait(td->td_ucred, ks);
  716         if (error) {
  717                 DP(("kern_sem_wait mac failed\n"));
  718                 goto err;
  719         }
  720 #endif
  721         DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
  722         if (ks->ks_value == 0) {
  723                 ks->ks_waiters++;
  724                 if (tryflag != 0)
  725                         error = EAGAIN;
  726                 else if (abstime == NULL)
  727                         error = cv_wait_sig(&ks->ks_cv, &sem_lock);
  728                 else {
  729                         for (;;) {
  730                                 ts1 = *abstime;
  731                                 getnanotime(&ts2);
  732                                 timespecsub(&ts1, &ts2);
  733                                 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
  734                                 if (tv.tv_sec < 0) {
  735                                         error = ETIMEDOUT;
  736                                         break;
  737                                 }
  738                                 error = cv_timedwait_sig(&ks->ks_cv,
  739                                     &sem_lock, tvtohz(&tv));
  740                                 if (error != EWOULDBLOCK)
  741                                         break;
  742                         }
  743                 }
  744                 ks->ks_waiters--;
  745                 if (error)
  746                         goto err;
  747         }
  748         ks->ks_value--;
  749         error = 0;
  750 err:
  751         if (ks != NULL)
  752                 sem_rel(ks);
  753         mtx_unlock(&sem_lock);
  754         DP(("<<< kern_sem_wait leaving, error = %d\n", error));
  755         return (error);
  756 }
  757 
  758 #ifndef _SYS_SYSPROTO_H_
  759 struct ksem_getvalue_args {
  760         semid_t id;
  761         int *val;
  762 };
  763 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
  764 #endif
  765 int
  766 ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
  767 {
  768         struct ksem *ks;
  769         int error, val;
  770 
  771         mtx_lock(&sem_lock);
  772         ks = ID_TO_SEM(uap->id);
  773         if (ks == NULL || !sem_hasopen(td, ks)) {
  774                 mtx_unlock(&sem_lock);
  775                 return (EINVAL);
  776         }
  777 #ifdef MAC
  778         error = mac_check_posix_sem_getvalue(td->td_ucred, ks);
  779         if (error) {
  780                 mtx_unlock(&sem_lock);
  781                 return (error);
  782         }
  783 #endif
  784         val = ks->ks_value;
  785         mtx_unlock(&sem_lock);
  786         error = copyout(&val, uap->val, sizeof(val));
  787         return (error);
  788 }
  789 
  790 #ifndef _SYS_SYSPROTO_H_
  791 struct ksem_destroy_args {
  792         semid_t id;
  793 };
  794 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
  795 #endif
  796 int
  797 ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
  798 {
  799         struct ksem *ks;
  800         int error;
  801 
  802         mtx_lock(&sem_lock);
  803         ks = ID_TO_SEM(uap->id);
  804         if (ks == NULL || !sem_hasopen(td, ks) ||
  805             ks->ks_name != NULL) {
  806                 error = EINVAL;
  807                 goto err;
  808         }
  809 #ifdef MAC
  810         error = mac_check_posix_sem_destroy(td->td_ucred, ks);
  811         if (error)
  812                 goto err;
  813 #endif
  814         if (ks->ks_waiters != 0) {
  815                 error = EBUSY;
  816                 goto err;
  817         }
  818         sem_rel(ks);
  819         error = 0;
  820 err:
  821         mtx_unlock(&sem_lock);
  822         return (error);
  823 }
  824 
  825 /*
  826  * Count the number of kusers associated with a proc, so as to guess at how
  827  * many to allocate when forking.
  828  */
  829 static int
  830 sem_count_proc(struct proc *p)
  831 {
  832         struct ksem *ks;
  833         struct kuser *ku;
  834         int count;
  835 
  836         mtx_assert(&sem_lock, MA_OWNED);
  837 
  838         count = 0;
  839         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  840                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  841                         if (ku->ku_pid == p->p_pid)
  842                                 count++;
  843                 }
  844         }
  845         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
  846                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  847                         if (ku->ku_pid == p->p_pid)
  848                                 count++;
  849                 }
  850         }
  851         return (count);
  852 }
  853 
  854 /*
  855  * When a process forks, the child process must gain a reference to each open
  856  * semaphore in the parent process, whether it is unlinked or not.  This
  857  * requires allocating a kuser structure for each semaphore reference in the
  858  * new process.  Because the set of semaphores in the parent can change while
  859  * the fork is in progress, we have to handle races -- first we attempt to
  860  * allocate enough storage to acquire references to each of the semaphores,
  861  * then we enter the semaphores and release the temporary references.
  862  */
  863 static void
  864 sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
  865 {
  866         struct ksem *ks, **sem_array;
  867         int count, i, new_count;
  868         struct kuser *ku;
  869 
  870         mtx_lock(&sem_lock);
  871         count = sem_count_proc(p1);
  872         if (count == 0) {
  873                 mtx_unlock(&sem_lock);
  874                 return;
  875         }
  876 race_lost:
  877         mtx_assert(&sem_lock, MA_OWNED);
  878         mtx_unlock(&sem_lock);
  879         sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
  880         mtx_lock(&sem_lock);
  881         new_count = sem_count_proc(p1);
  882         if (count < new_count) {
  883                 /* Lost race, repeat and allocate more storage. */
  884                 free(sem_array, M_TEMP);
  885                 count = new_count;
  886                 goto race_lost;
  887         }
  888         /*
  889          * Given an array capable of storing an adequate number of semaphore
  890          * references, now walk the list of semaphores and acquire a new
  891          * reference for any semaphore opened by p1.
  892          */
  893         count = new_count;
  894         i = 0;
  895         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  896                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  897                         if (ku->ku_pid == p1->p_pid) {
  898                                 sem_ref(ks);
  899                                 sem_array[i] = ks;
  900                                 i++;
  901                                 break;
  902                         }
  903                 }
  904         }
  905         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
  906                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
  907                         if (ku->ku_pid == p1->p_pid) {
  908                                 sem_ref(ks);
  909                                 sem_array[i] = ks;
  910                                 i++;
  911                                 break;
  912                         }
  913                 }
  914         }
  915         mtx_unlock(&sem_lock);
  916         KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
  917         /*
  918          * Now cause p2 to enter each of the referenced semaphores, then
  919          * release our temporary reference.  This is pretty inefficient.
  920          * Finally, free our temporary array.
  921          */
  922         for (i = 0; i < count; i++) {
  923                 sem_enter(p2, sem_array[i]);
  924                 mtx_lock(&sem_lock);
  925                 sem_rel(sem_array[i]);
  926                 mtx_unlock(&sem_lock);
  927         }
  928         free(sem_array, M_TEMP);
  929 }
  930 
  931 static void
  932 sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
  933 {
  934         sem_exithook(arg, p);           
  935 }
  936 
  937 static void
  938 sem_exithook(void *arg, struct proc *p)
  939 {
  940         struct ksem *ks, *ksnext;
  941 
  942         mtx_lock(&sem_lock);
  943         ks = LIST_FIRST(&ksem_head);
  944         while (ks != NULL) {
  945                 ksnext = LIST_NEXT(ks, ks_entry);
  946                 sem_leave(p, ks);
  947                 ks = ksnext;
  948         }
  949         ks = LIST_FIRST(&ksem_deadhead);
  950         while (ks != NULL) {
  951                 ksnext = LIST_NEXT(ks, ks_entry);
  952                 sem_leave(p, ks);
  953                 ks = ksnext;
  954         }
  955         mtx_unlock(&sem_lock);
  956 }
  957 
  958 static int
  959 sem_modload(struct module *module, int cmd, void *arg)
  960 {
  961         int error = 0;
  962 
  963         switch (cmd) {
  964         case MOD_LOAD:
  965                 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
  966                 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
  967                 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
  968                 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
  969                     NULL, EVENTHANDLER_PRI_ANY);
  970                 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exechook,
  971                     NULL, EVENTHANDLER_PRI_ANY);
  972                 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
  973                 break;
  974         case MOD_UNLOAD:
  975                 if (nsems != 0) {
  976                         error = EOPNOTSUPP;
  977                         break;
  978                 }
  979                 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
  980                 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
  981                 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
  982                 mtx_destroy(&sem_lock);
  983                 break;
  984         case MOD_SHUTDOWN:
  985                 break;
  986         default:
  987                 error = EINVAL;
  988                 break;
  989         }
  990         return (error);
  991 }
  992 
  993 static moduledata_t sem_mod = {
  994         "sem",
  995         &sem_modload,
  996         NULL
  997 };
  998 
  999 SYSCALL_MODULE_HELPER(ksem_init);
 1000 SYSCALL_MODULE_HELPER(ksem_open);
 1001 SYSCALL_MODULE_HELPER(ksem_unlink);
 1002 SYSCALL_MODULE_HELPER(ksem_close);
 1003 SYSCALL_MODULE_HELPER(ksem_post);
 1004 SYSCALL_MODULE_HELPER(ksem_wait);
 1005 SYSCALL_MODULE_HELPER(ksem_timedwait);
 1006 SYSCALL_MODULE_HELPER(ksem_trywait);
 1007 SYSCALL_MODULE_HELPER(ksem_getvalue);
 1008 SYSCALL_MODULE_HELPER(ksem_destroy);
 1009 
 1010 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
 1011 MODULE_VERSION(sem, 1);

Cache object: ccb6cae8011dc884e0cd7ee57e4505e4


[ 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.