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 /*      $NetBSD: uipc_sem.c,v 1.26 2008/10/22 11:17:08 ad Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 2003, 2007 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Jason R. Thorpe of Wasabi Systems, Inc, and by Andrew Doran.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   29  * POSSIBILITY OF SUCH DAMAGE.
   30  */
   31 
   32 /*
   33  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
   34  * All rights reserved.
   35  *
   36  * Redistribution and use in source and binary forms, with or without
   37  * modification, are permitted provided that the following conditions
   38  * are met:
   39  * 1. Redistributions of source code must retain the above copyright
   40  *    notice, this list of conditions and the following disclaimer.
   41  * 2. Redistributions in binary form must reproduce the above copyright
   42  *    notice, this list of conditions and the following disclaimer in the
   43  *    documentation and/or other materials provided with the distribution.
   44  *
   45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   55  * SUCH DAMAGE.
   56  */
   57 
   58 #include <sys/cdefs.h>
   59 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.26 2008/10/22 11:17:08 ad Exp $");
   60 
   61 #include "opt_posix.h"
   62 
   63 #include <sys/param.h>
   64 #include <sys/systm.h>
   65 #include <sys/kernel.h>
   66 #include <sys/proc.h>
   67 #include <sys/ksem.h>
   68 #include <sys/syscall.h>
   69 #include <sys/stat.h>
   70 #include <sys/kmem.h>
   71 #include <sys/fcntl.h>
   72 #include <sys/kauth.h>
   73 #include <sys/sysctl.h>
   74 
   75 #include <sys/mount.h>
   76 
   77 #include <sys/syscallargs.h>
   78 
   79 #define SEM_MAX 128
   80 #define SEM_MAX_NAMELEN 14
   81 #define SEM_VALUE_MAX (~0U)
   82 #define SEM_HASHTBL_SIZE 13
   83 
   84 #define SEM_TO_ID(x)    (((x)->ks_id))
   85 #define SEM_HASH(id)    ((id) % SEM_HASHTBL_SIZE)
   86 
   87 MALLOC_DEFINE(M_SEM, "p1003_1b_sem", "p1003_1b semaphores");
   88 
   89 /*
   90  * Note: to read the ks_name member, you need either the ks_interlock
   91  * or the ksem_slock.  To write the ks_name member, you need both.  Make
   92  * sure the order is ksem_slock -> ks_interlock.
   93  */
   94 struct ksem {
   95         LIST_ENTRY(ksem) ks_entry;      /* global list entry */
   96         LIST_ENTRY(ksem) ks_hash;       /* hash list entry */
   97         kmutex_t ks_interlock;          /* lock on this ksem */
   98         kcondvar_t ks_cv;               /* condition variable */
   99         unsigned int ks_ref;            /* number of references */
  100         char *ks_name;                  /* if named, this is the name */
  101         size_t ks_namelen;              /* length of name */
  102         mode_t ks_mode;                 /* protection bits */
  103         uid_t ks_uid;                   /* creator uid */
  104         gid_t ks_gid;                   /* creator gid */
  105         unsigned int ks_value;          /* current value */
  106         unsigned int ks_waiters;        /* number of waiters */
  107         semid_t ks_id;                  /* unique identifier */
  108 };
  109 
  110 struct ksem_ref {
  111         LIST_ENTRY(ksem_ref) ksr_list;
  112         struct ksem *ksr_ksem;
  113 };
  114 
  115 struct ksem_proc {
  116         krwlock_t kp_lock;
  117         LIST_HEAD(, ksem_ref) kp_ksems;
  118 };
  119 
  120 LIST_HEAD(ksem_list, ksem);
  121 
  122 /*
  123  * ksem_slock protects ksem_head and nsems.  Only named semaphores go
  124  * onto ksem_head.
  125  */
  126 static kmutex_t ksem_mutex;
  127 static struct ksem_list ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
  128 static struct ksem_list ksem_hash[SEM_HASHTBL_SIZE];
  129 static u_int sem_max = SEM_MAX;
  130 static int nsems = 0;
  131 
  132 /*
  133  * ksem_counter is the last assigned semid_t.  It needs to be COMPAT_NETBSD32
  134  * friendly, even though semid_t itself is defined as uintptr_t.
  135  */
  136 static uint32_t ksem_counter = 1;
  137 
  138 static specificdata_key_t ksem_specificdata_key;
  139 
  140 static void
  141 ksem_free(struct ksem *ks)
  142 {
  143 
  144         KASSERT(mutex_owned(&ks->ks_interlock));
  145 
  146         /*
  147          * If the ksem is anonymous (or has been unlinked), then
  148          * this is the end if its life.
  149          */
  150         if (ks->ks_name == NULL) {
  151                 mutex_exit(&ks->ks_interlock);
  152                 mutex_destroy(&ks->ks_interlock);
  153                 cv_destroy(&ks->ks_cv);
  154 
  155                 mutex_enter(&ksem_mutex);
  156                 nsems--;
  157                 LIST_REMOVE(ks, ks_hash);
  158                 mutex_exit(&ksem_mutex);
  159 
  160                 kmem_free(ks, sizeof(*ks));
  161                 return;
  162         }
  163         mutex_exit(&ks->ks_interlock);
  164 }
  165 
  166 static inline void
  167 ksem_addref(struct ksem *ks)
  168 {
  169 
  170         KASSERT(mutex_owned(&ks->ks_interlock));
  171         ks->ks_ref++;
  172         KASSERT(ks->ks_ref != 0);
  173 }
  174 
  175 static inline void
  176 ksem_delref(struct ksem *ks)
  177 {
  178 
  179         KASSERT(mutex_owned(&ks->ks_interlock));
  180         KASSERT(ks->ks_ref != 0);
  181         if (--ks->ks_ref == 0) {
  182                 ksem_free(ks);
  183                 return;
  184         }
  185         mutex_exit(&ks->ks_interlock);
  186 }
  187 
  188 static struct ksem_proc *
  189 ksem_proc_alloc(void)
  190 {
  191         struct ksem_proc *kp;
  192 
  193         kp = kmem_alloc(sizeof(*kp), KM_SLEEP);
  194         rw_init(&kp->kp_lock);
  195         LIST_INIT(&kp->kp_ksems);
  196 
  197         return (kp);
  198 }
  199 
  200 static void
  201 ksem_proc_dtor(void *arg)
  202 {
  203         struct ksem_proc *kp = arg;
  204         struct ksem_ref *ksr;
  205 
  206         rw_enter(&kp->kp_lock, RW_WRITER);
  207 
  208         while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) {
  209                 LIST_REMOVE(ksr, ksr_list);
  210                 mutex_enter(&ksr->ksr_ksem->ks_interlock);
  211                 ksem_delref(ksr->ksr_ksem);
  212                 kmem_free(ksr, sizeof(*ksr));
  213         }
  214 
  215         rw_exit(&kp->kp_lock);
  216         rw_destroy(&kp->kp_lock);
  217         kmem_free(kp, sizeof(*kp));
  218 }
  219 
  220 static void
  221 ksem_add_proc(struct proc *p, struct ksem *ks)
  222 {
  223         struct ksem_proc *kp;
  224         struct ksem_ref *ksr;
  225 
  226         kp = proc_getspecific(p, ksem_specificdata_key);
  227         if (kp == NULL) {
  228                 kp = ksem_proc_alloc();
  229                 proc_setspecific(p, ksem_specificdata_key, kp);
  230         }
  231 
  232         ksr = kmem_alloc(sizeof(*ksr), KM_SLEEP);
  233         ksr->ksr_ksem = ks;
  234 
  235         rw_enter(&kp->kp_lock, RW_WRITER);
  236         LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list);
  237         rw_exit(&kp->kp_lock);
  238 }
  239 
  240 /* We MUST have a write lock on the ksem_proc list! */
  241 static struct ksem_ref *
  242 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks)
  243 {
  244         struct ksem_ref *ksr;
  245 
  246         KASSERT(mutex_owned(&ks->ks_interlock));
  247         LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
  248                 if (ksr->ksr_ksem == ks) {
  249                         ksem_delref(ks);
  250                         LIST_REMOVE(ksr, ksr_list);
  251                         return (ksr);
  252                 }
  253         }
  254 #ifdef DIAGNOSTIC
  255         panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks);
  256 #endif
  257         return (NULL);
  258 }
  259 
  260 static int
  261 ksem_perm(struct lwp *l, struct ksem *ks)
  262 {
  263         kauth_cred_t uc;
  264 
  265         KASSERT(mutex_owned(&ks->ks_interlock));
  266         uc = l->l_cred;
  267         if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
  268             (kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
  269             (ks->ks_mode & S_IWOTH) != 0 ||
  270             kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, NULL) == 0)
  271                 return (0);
  272         return (EPERM);
  273 }
  274 
  275 static struct ksem *
  276 ksem_lookup_byid(semid_t id)
  277 {
  278         struct ksem *ks;
  279 
  280         KASSERT(mutex_owned(&ksem_mutex));
  281         LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) {
  282                 if (ks->ks_id == id)
  283                         return ks;
  284         }
  285         return NULL;
  286 }
  287 
  288 static struct ksem *
  289 ksem_lookup_byname(const char *name)
  290 {
  291         struct ksem *ks;
  292 
  293         KASSERT(mutex_owned(&ksem_mutex));
  294         LIST_FOREACH(ks, &ksem_head, ks_entry) {
  295                 if (strcmp(ks->ks_name, name) == 0) {
  296                         mutex_enter(&ks->ks_interlock);
  297                         return (ks);
  298                 }
  299         }
  300         return (NULL);
  301 }
  302 
  303 static int
  304 ksem_create(struct lwp *l, const char *name, struct ksem **ksret,
  305     mode_t mode, unsigned int value)
  306 {
  307         struct ksem *ret;
  308         kauth_cred_t uc;
  309         size_t len;
  310 
  311         uc = l->l_cred;
  312         if (value > SEM_VALUE_MAX)
  313                 return (EINVAL);
  314         ret = kmem_zalloc(sizeof(*ret), KM_SLEEP);
  315         if (name != NULL) {
  316                 len = strlen(name);
  317                 if (len > SEM_MAX_NAMELEN) {
  318                         kmem_free(ret, sizeof(*ret));
  319                         return (ENAMETOOLONG);
  320                 }
  321                 /* name must start with a '/' but not contain one. */
  322                 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
  323                         kmem_free(ret, sizeof(*ret));
  324                         return (EINVAL);
  325                 }
  326                 ret->ks_namelen = len + 1;
  327                 ret->ks_name = kmem_alloc(ret->ks_namelen, KM_SLEEP);
  328                 strlcpy(ret->ks_name, name, len + 1);
  329         } else
  330                 ret->ks_name = NULL;
  331         ret->ks_mode = mode;
  332         ret->ks_value = value;
  333         ret->ks_ref = 1;
  334         ret->ks_waiters = 0;
  335         ret->ks_uid = kauth_cred_geteuid(uc);
  336         ret->ks_gid = kauth_cred_getegid(uc);
  337         mutex_init(&ret->ks_interlock, MUTEX_DEFAULT, IPL_NONE);
  338         cv_init(&ret->ks_cv, "psem");
  339 
  340         mutex_enter(&ksem_mutex);
  341         if (nsems >= sem_max) {
  342                 mutex_exit(&ksem_mutex);
  343                 if (ret->ks_name != NULL)
  344                         kmem_free(ret->ks_name, ret->ks_namelen);
  345                 kmem_free(ret, sizeof(*ret));
  346                 return (ENFILE);
  347         }
  348         nsems++;
  349         while (ksem_lookup_byid(ksem_counter) != NULL) {
  350                 ksem_counter++;
  351                 /* 0 is a special value for libpthread */
  352                 if (ksem_counter == 0)
  353                         ksem_counter++;
  354         }
  355         ret->ks_id = ksem_counter;
  356         LIST_INSERT_HEAD(&ksem_hash[SEM_HASH(ret->ks_id)], ret, ks_hash);
  357         mutex_exit(&ksem_mutex);
  358 
  359         *ksret = ret;
  360         return (0);
  361 }
  362 
  363 int
  364 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, register_t *retval)
  365 {
  366         /* {
  367                 unsigned int value;
  368                 semid_t *idp;
  369         } */
  370 
  371         return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
  372 }
  373 
  374 int
  375 do_ksem_init(struct lwp *l, unsigned int value, semid_t *idp,
  376     copyout_t docopyout)
  377 {
  378         struct ksem *ks;
  379         semid_t id;
  380         int error;
  381 
  382         /* Note the mode does not matter for anonymous semaphores. */
  383         error = ksem_create(l, NULL, &ks, 0, value);
  384         if (error)
  385                 return (error);
  386         id = SEM_TO_ID(ks);
  387         error = (*docopyout)(&id, idp, sizeof(id));
  388         if (error) {
  389                 mutex_enter(&ks->ks_interlock);
  390                 ksem_delref(ks);
  391                 return (error);
  392         }
  393 
  394         ksem_add_proc(l->l_proc, ks);
  395 
  396         return (0);
  397 }
  398 
  399 int
  400 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap, register_t *retval)
  401 {
  402         /* {
  403                 const char *name;
  404                 int oflag;
  405                 mode_t mode;
  406                 unsigned int value;
  407                 semid_t *idp;
  408         } */
  409 
  410         return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
  411             SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
  412 }
  413 
  414 int
  415 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
  416      unsigned int value, semid_t *idp, copyout_t docopyout)
  417 {
  418         char name[SEM_MAX_NAMELEN + 1];
  419         size_t done;
  420         int error;
  421         struct ksem *ksnew, *ks;
  422         semid_t id;
  423 
  424         error = copyinstr(semname, name, sizeof(name), &done);
  425         if (error)
  426                 return (error);
  427 
  428         ksnew = NULL;
  429         mutex_enter(&ksem_mutex);
  430         ks = ksem_lookup_byname(name);
  431 
  432         /* Found one? */
  433         if (ks != NULL) {
  434                 /* Check for exclusive create. */
  435                 if (oflag & O_EXCL) {
  436                         mutex_exit(&ks->ks_interlock);
  437                         mutex_exit(&ksem_mutex);
  438                         return (EEXIST);
  439                 }
  440  found_one:
  441                 /*
  442                  * Verify permissions.  If we can access it, add
  443                  * this process's reference.
  444                  */
  445                 KASSERT(mutex_owned(&ks->ks_interlock));
  446                 error = ksem_perm(l, ks);
  447                 if (error == 0)
  448                         ksem_addref(ks);
  449                 mutex_exit(&ks->ks_interlock);
  450                 mutex_exit(&ksem_mutex);
  451                 if (error)
  452                         return (error);
  453 
  454                 id = SEM_TO_ID(ks);
  455                 error = (*docopyout)(&id, idp, sizeof(id));
  456                 if (error) {
  457                         mutex_enter(&ks->ks_interlock);
  458                         ksem_delref(ks);
  459                         return (error);
  460                 }
  461 
  462                 ksem_add_proc(l->l_proc, ks);
  463 
  464                 return (0);
  465         }
  466 
  467         /*
  468          * didn't ask for creation? error.
  469          */
  470         if ((oflag & O_CREAT) == 0) {
  471                 mutex_exit(&ksem_mutex);
  472                 return (ENOENT);
  473         }
  474 
  475         /*
  476          * We may block during creation, so drop the lock.
  477          */
  478         mutex_exit(&ksem_mutex);
  479         error = ksem_create(l, name, &ksnew, mode, value);
  480         if (error != 0)
  481                 return (error);
  482 
  483         id = SEM_TO_ID(ksnew);
  484         error = (*docopyout)(&id, idp, sizeof(id));
  485         if (error) {
  486                 kmem_free(ksnew->ks_name, ksnew->ks_namelen);
  487                 ksnew->ks_name = NULL;
  488 
  489                 mutex_enter(&ksnew->ks_interlock);
  490                 ksem_delref(ksnew);
  491                 return (error);
  492         }
  493 
  494         /*
  495          * We need to make sure we haven't lost a race while
  496          * allocating during creation.
  497          */
  498         mutex_enter(&ksem_mutex);
  499         if ((ks = ksem_lookup_byname(name)) != NULL) {
  500                 if (oflag & O_EXCL) {
  501                         mutex_exit(&ks->ks_interlock);
  502                         mutex_exit(&ksem_mutex);
  503 
  504                         kmem_free(ksnew->ks_name, ksnew->ks_namelen);
  505                         ksnew->ks_name = NULL;
  506 
  507                         mutex_enter(&ksnew->ks_interlock);
  508                         ksem_delref(ksnew);
  509                         return (EEXIST);
  510                 }
  511                 goto found_one;
  512         } else {
  513                 /* ksnew already has its initial reference. */
  514                 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
  515                 mutex_exit(&ksem_mutex);
  516 
  517                 ksem_add_proc(l->l_proc, ksnew);
  518         }
  519         return (error);
  520 }
  521 
  522 /* We must have a read lock on the ksem_proc list! */
  523 static struct ksem *
  524 ksem_lookup_proc(struct ksem_proc *kp, semid_t id)
  525 {
  526         struct ksem_ref *ksr;
  527 
  528         LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
  529                 if (id == SEM_TO_ID(ksr->ksr_ksem)) {
  530                         mutex_enter(&ksr->ksr_ksem->ks_interlock);
  531                         return (ksr->ksr_ksem);
  532                 }
  533         }
  534 
  535         return (NULL);
  536 }
  537 
  538 int
  539 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap, register_t *retval)
  540 {
  541         /* {
  542                 const char *name;
  543         } */
  544         char name[SEM_MAX_NAMELEN + 1], *cp;
  545         size_t done, len;
  546         struct ksem *ks;
  547         int error;
  548 
  549         error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
  550         if (error)
  551                 return error;
  552 
  553         mutex_enter(&ksem_mutex);
  554         ks = ksem_lookup_byname(name);
  555         if (ks == NULL) {
  556                 mutex_exit(&ksem_mutex);
  557                 return (ENOENT);
  558         }
  559 
  560         KASSERT(mutex_owned(&ks->ks_interlock));
  561 
  562         LIST_REMOVE(ks, ks_entry);
  563         cp = ks->ks_name;
  564         len = ks->ks_namelen;
  565         ks->ks_name = NULL;
  566 
  567         mutex_exit(&ksem_mutex);
  568 
  569         if (ks->ks_ref == 0)
  570                 ksem_free(ks);
  571         else
  572                 mutex_exit(&ks->ks_interlock);
  573 
  574         kmem_free(cp, len);
  575 
  576         return (0);
  577 }
  578 
  579 int
  580 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap, register_t *retval)
  581 {
  582         /* {
  583                 semid_t id;
  584         } */
  585         struct ksem_proc *kp;
  586         struct ksem_ref *ksr;
  587         struct ksem *ks;
  588 
  589         kp = proc_getspecific(l->l_proc, ksem_specificdata_key);
  590         if (kp == NULL)
  591                 return (EINVAL);
  592 
  593         rw_enter(&kp->kp_lock, RW_WRITER);
  594 
  595         ks = ksem_lookup_proc(kp, SCARG(uap, id));
  596         if (ks == NULL) {
  597                 rw_exit(&kp->kp_lock);
  598                 return (EINVAL);
  599         }
  600 
  601         KASSERT(mutex_owned(&ks->ks_interlock));
  602         if (ks->ks_name == NULL) {
  603                 mutex_exit(&ks->ks_interlock);
  604                 rw_exit(&kp->kp_lock);
  605                 return (EINVAL);
  606         }
  607 
  608         ksr = ksem_drop_proc(kp, ks);
  609         rw_exit(&kp->kp_lock);
  610         kmem_free(ksr, sizeof(*ksr));
  611 
  612         return (0);
  613 }
  614 
  615 int
  616 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap, register_t *retval)
  617 {
  618         /* {
  619                 semid_t id;
  620         } */
  621         struct ksem_proc *kp;
  622         struct ksem *ks;
  623         int error;
  624 
  625         kp = proc_getspecific(l->l_proc, ksem_specificdata_key);
  626         if (kp == NULL)
  627                 return (EINVAL);
  628 
  629         rw_enter(&kp->kp_lock, RW_READER);
  630         ks = ksem_lookup_proc(kp, SCARG(uap, id));
  631         rw_exit(&kp->kp_lock);
  632         if (ks == NULL)
  633                 return (EINVAL);
  634 
  635         KASSERT(mutex_owned(&ks->ks_interlock));
  636         if (ks->ks_value == SEM_VALUE_MAX) {
  637                 error = EOVERFLOW;
  638                 goto out;
  639         }
  640         ++ks->ks_value;
  641         if (ks->ks_waiters)
  642                 cv_broadcast(&ks->ks_cv);
  643         error = 0;
  644  out:
  645         mutex_exit(&ks->ks_interlock);
  646         return (error);
  647 }
  648 
  649 static int
  650 ksem_wait(struct lwp *l, semid_t id, int tryflag)
  651 {
  652         struct ksem_proc *kp;
  653         struct ksem *ks;
  654         int error;
  655 
  656         kp = proc_getspecific(l->l_proc, ksem_specificdata_key);
  657         if (kp == NULL)
  658                 return (EINVAL);
  659 
  660         rw_enter(&kp->kp_lock, RW_READER);
  661         ks = ksem_lookup_proc(kp, id);
  662         rw_exit(&kp->kp_lock);
  663         if (ks == NULL)
  664                 return (EINVAL);
  665 
  666         KASSERT(mutex_owned(&ks->ks_interlock));
  667         ksem_addref(ks);
  668         while (ks->ks_value == 0) {
  669                 ks->ks_waiters++;
  670                 if (tryflag)
  671                         error = EAGAIN;
  672                 else
  673                         error = cv_wait_sig(&ks->ks_cv, &ks->ks_interlock);
  674                 ks->ks_waiters--;
  675                 if (error)
  676                         goto out;
  677         }
  678         ks->ks_value--;
  679         error = 0;
  680  out:
  681         ksem_delref(ks);
  682         return (error);
  683 }
  684 
  685 int
  686 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap, register_t *retval)
  687 {
  688         /* {
  689                 semid_t id;
  690         } */
  691 
  692         return ksem_wait(l, SCARG(uap, id), 0);
  693 }
  694 
  695 int
  696 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap, register_t *retval)
  697 {
  698         /* {
  699                 semid_t id;
  700         } */
  701 
  702         return ksem_wait(l, SCARG(uap, id), 1);
  703 }
  704 
  705 int
  706 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap, register_t *retval)
  707 {
  708         /* {
  709                 semid_t id;
  710                 unsigned int *value;
  711         } */
  712         struct ksem_proc *kp;
  713         struct ksem *ks;
  714         unsigned int val;
  715 
  716         kp = proc_getspecific(l->l_proc, ksem_specificdata_key);
  717         if (kp == NULL)
  718                 return (EINVAL);
  719 
  720         rw_enter(&kp->kp_lock, RW_READER);
  721         ks = ksem_lookup_proc(kp, SCARG(uap, id));
  722         rw_exit(&kp->kp_lock);
  723         if (ks == NULL)
  724                 return (EINVAL);
  725 
  726         KASSERT(mutex_owned(&ks->ks_interlock));
  727         val = ks->ks_value;
  728         mutex_exit(&ks->ks_interlock);
  729 
  730         return (copyout(&val, SCARG(uap, value), sizeof(val)));
  731 }
  732 
  733 int
  734 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap, register_t *retval)
  735 {
  736         /* {
  737                 semid_t id;
  738         } */
  739         struct ksem_proc *kp;
  740         struct ksem_ref *ksr;
  741         struct ksem *ks;
  742 
  743         kp = proc_getspecific(l->l_proc, ksem_specificdata_key);
  744         if (kp == NULL)
  745                 return (EINVAL);
  746 
  747         rw_enter(&kp->kp_lock, RW_WRITER);
  748 
  749         ks = ksem_lookup_proc(kp, SCARG(uap, id));
  750         if (ks == NULL) {
  751                 rw_exit(&kp->kp_lock);
  752                 return (EINVAL);
  753         }
  754 
  755         KASSERT(mutex_owned(&ks->ks_interlock));
  756 
  757         /*
  758          * XXX This misses named semaphores which have been unlink'd,
  759          * XXX but since behavior of destroying a named semaphore is
  760          * XXX undefined, this is technically allowed.
  761          */
  762         if (ks->ks_name != NULL) {
  763                 mutex_exit(&ks->ks_interlock);
  764                 rw_exit(&kp->kp_lock);
  765                 return (EINVAL);
  766         }
  767 
  768         if (ks->ks_waiters) {
  769                 mutex_exit(&ks->ks_interlock);
  770                 rw_exit(&kp->kp_lock);
  771                 return (EBUSY);
  772         }
  773 
  774         ksr = ksem_drop_proc(kp, ks);
  775         rw_exit(&kp->kp_lock);
  776         kmem_free(ksr, sizeof(*ksr));
  777 
  778         return (0);
  779 }
  780 
  781 static void
  782 ksem_forkhook(struct proc *p2, struct proc *p1)
  783 {
  784         struct ksem_proc *kp1, *kp2;
  785         struct ksem_ref *ksr, *ksr1;
  786 
  787         kp1 = proc_getspecific(p1, ksem_specificdata_key);
  788         if (kp1 == NULL)
  789                 return;
  790 
  791         kp2 = ksem_proc_alloc();
  792 
  793         rw_enter(&kp1->kp_lock, RW_READER);
  794 
  795         if (!LIST_EMPTY(&kp1->kp_ksems)) {
  796                 LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) {
  797                         ksr1 = kmem_alloc(sizeof(*ksr), KM_SLEEP);
  798                         ksr1->ksr_ksem = ksr->ksr_ksem;
  799                         mutex_enter(&ksr->ksr_ksem->ks_interlock);
  800                         ksem_addref(ksr->ksr_ksem);
  801                         mutex_exit(&ksr->ksr_ksem->ks_interlock);
  802                         LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list);
  803                 }
  804         }
  805 
  806         rw_exit(&kp1->kp_lock);
  807         proc_setspecific(p2, ksem_specificdata_key, kp2);
  808 }
  809 
  810 static void
  811 ksem_exechook(struct proc *p, void *arg)
  812 {
  813         struct ksem_proc *kp;
  814 
  815         kp = proc_getspecific(p, ksem_specificdata_key);
  816         if (kp != NULL) {
  817                 proc_setspecific(p, ksem_specificdata_key, NULL);
  818                 ksem_proc_dtor(kp);
  819         }
  820 }
  821 
  822 void
  823 ksem_init(void)
  824 {
  825         int i, error;
  826 
  827         mutex_init(&ksem_mutex, MUTEX_DEFAULT, IPL_NONE);
  828         exechook_establish(ksem_exechook, NULL);
  829         forkhook_establish(ksem_forkhook);
  830 
  831         for (i = 0; i < SEM_HASHTBL_SIZE; i++)
  832                 LIST_INIT(&ksem_hash[i]);
  833 
  834         error = proc_specific_key_create(&ksem_specificdata_key,
  835                                          ksem_proc_dtor);
  836         KASSERT(error == 0);
  837         posix_semaphores = 200112;
  838 }
  839 
  840 /*
  841  * Sysctl initialization and nodes.
  842  */
  843 
  844 SYSCTL_SETUP(sysctl_posix_sem_setup, "sysctl kern.posix subtree setup")
  845 {
  846         const struct sysctlnode *node = NULL;
  847 
  848         sysctl_createv(clog, 0, NULL, NULL,
  849                 CTLFLAG_PERMANENT,
  850                 CTLTYPE_NODE, "kern", NULL,
  851                 NULL, 0, NULL, 0,
  852                 CTL_KERN, CTL_EOL);
  853         sysctl_createv(clog, 0, NULL, &node,
  854                 CTLFLAG_PERMANENT,
  855                 CTLTYPE_NODE, "posix",
  856                 SYSCTL_DESCR("POSIX options"),
  857                 NULL, 0, NULL, 0,
  858                 CTL_KERN, CTL_CREATE, CTL_EOL);
  859 
  860         if (node == NULL)
  861                 return;
  862 
  863         sysctl_createv(clog, 0, &node, NULL,
  864                 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  865                 CTLTYPE_INT, "semmax",
  866                 SYSCTL_DESCR("Maximal number of semaphores"),
  867                 NULL, 0, &sem_max, 0,
  868                 CTL_CREATE, CTL_EOL);
  869 }

Cache object: 30a156ef55523d14e7299693580794bc


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