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

Cache object: c7ba137ab20b555c60470fdcf8fd0a01


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