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

Cache object: 7d96cc85c48a30da71ffb9694a5cb701


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