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/kern_umtx.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, Jeffrey Roberson <jeff@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice unmodified, this list of conditions, and the following
   10  *    disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  *
   26  * $FreeBSD: releng/5.1/sys/kern/kern_umtx.c 115784 2003-06-03 20:43:18Z jeff $
   27  *
   28  */
   29 
   30 #include <sys/param.h>
   31 #include <sys/kernel.h>
   32 #include <sys/lock.h>
   33 #include <sys/malloc.h>
   34 #include <sys/mutex.h>
   35 #include <sys/proc.h>
   36 #include <sys/signalvar.h>
   37 #include <sys/sysent.h>
   38 #include <sys/systm.h>
   39 #include <sys/sysproto.h>
   40 #include <sys/sx.h>
   41 #include <sys/thr.h>
   42 #include <sys/umtx.h>
   43 
   44 struct umtx_q {
   45         LIST_ENTRY(umtx_q)      uq_next;        /* Linked list for the hash. */
   46         TAILQ_HEAD(, thread) uq_tdq;    /* List of threads blocked here. */
   47         struct umtx     *uq_umtx;       /* Pointer key component. */
   48         pid_t           uq_pid;         /* Pid key component. */
   49 };
   50 
   51 #define UMTX_QUEUES     128
   52 #define UMTX_HASH(pid, umtx)                                            \
   53     (((uintptr_t)pid + ((uintptr_t)umtx & ~65535)) % UMTX_QUEUES)
   54 
   55 LIST_HEAD(umtx_head, umtx_q);
   56 static struct umtx_head queues[UMTX_QUEUES];
   57 static MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory");
   58 
   59 static struct mtx umtx_lock;
   60 MTX_SYSINIT(umtx, &umtx_lock, "umtx", MTX_DEF);
   61 
   62 #define UMTX_LOCK()     mtx_lock(&umtx_lock);
   63 #define UMTX_UNLOCK()   mtx_unlock(&umtx_lock);
   64 
   65 
   66 static struct umtx_q *umtx_lookup(struct thread *, struct umtx *umtx);
   67 static struct umtx_q *umtx_insert(struct thread *, struct umtx *umtx);
   68 
   69 static struct umtx_q *
   70 umtx_lookup(struct thread *td, struct umtx *umtx)
   71 {
   72         struct umtx_head *head;
   73         struct umtx_q *uq;
   74         pid_t pid;
   75 
   76         pid = td->td_proc->p_pid;
   77 
   78         head = &queues[UMTX_HASH(td->td_proc->p_pid, umtx)];
   79 
   80         LIST_FOREACH(uq, head, uq_next) {
   81                 if (uq->uq_pid == pid && uq->uq_umtx == umtx)
   82                         return (uq);
   83         }
   84 
   85         return (NULL);
   86 }
   87 
   88 /*
   89  * Insert a thread onto the umtx queue.
   90  */
   91 static struct umtx_q *
   92 umtx_insert(struct thread *td, struct umtx *umtx)
   93 {
   94         struct umtx_head *head;
   95         struct umtx_q *uq;
   96         pid_t pid;
   97 
   98         pid = td->td_proc->p_pid;
   99 
  100         if ((uq = umtx_lookup(td, umtx)) == NULL) {
  101                 struct umtx_q *ins;
  102 
  103                 UMTX_UNLOCK();
  104                 ins = malloc(sizeof(*uq), M_UMTX, M_ZERO | M_WAITOK);
  105                 UMTX_LOCK();
  106 
  107                 /*
  108                  * Some one else could have succeeded while we were blocked
  109                  * waiting on memory.
  110                  */
  111                 if ((uq = umtx_lookup(td, umtx)) == NULL) {
  112                         head = &queues[UMTX_HASH(pid, umtx)];
  113                         uq = ins;
  114                         uq->uq_pid = pid;
  115                         uq->uq_umtx = umtx;
  116                         LIST_INSERT_HEAD(head, uq, uq_next);
  117                         TAILQ_INIT(&uq->uq_tdq);
  118                 } else
  119                         free(ins, M_UMTX);
  120         }
  121 
  122         /*
  123          * Insert us onto the end of the TAILQ.
  124          */
  125         TAILQ_INSERT_TAIL(&uq->uq_tdq, td, td_umtx);
  126 
  127         return (uq);
  128 }
  129 
  130 static void
  131 umtx_remove(struct umtx_q *uq, struct thread *td)
  132 {
  133         TAILQ_REMOVE(&uq->uq_tdq, td, td_umtx);
  134 
  135         if (TAILQ_EMPTY(&uq->uq_tdq)) {
  136                 LIST_REMOVE(uq, uq_next);
  137                 free(uq, M_UMTX);
  138         }
  139 }
  140 
  141 int
  142 _umtx_lock(struct thread *td, struct _umtx_lock_args *uap)
  143     /* struct umtx *umtx */
  144 {
  145         struct umtx_q *uq;
  146         struct umtx *umtx;
  147         intptr_t owner;
  148         intptr_t old;
  149         int error;
  150 
  151         uq = NULL;
  152 
  153         /*
  154          * Care must be exercised when dealing with this structure.  It
  155          * can fault on any access.
  156          */
  157         umtx = uap->umtx;       
  158 
  159         for (;;) {
  160                 /*
  161                  * Try the uncontested case.  This should be done in userland.
  162                  */
  163                 owner = casuptr((intptr_t *)&umtx->u_owner,
  164                     UMTX_UNOWNED, (intptr_t)td);
  165 
  166                 /* The address was invalid. */
  167                 if (owner == -1)
  168                         return (EFAULT);
  169 
  170                 /* The acquire succeeded. */
  171                 if (owner == UMTX_UNOWNED)
  172                         return (0);
  173 
  174                 /* If no one owns it but it is contested try to acquire it. */
  175                 if (owner == UMTX_CONTESTED) {
  176                         owner = casuptr((intptr_t *)&umtx->u_owner,
  177                             UMTX_CONTESTED, ((intptr_t)td | UMTX_CONTESTED));
  178 
  179                         /* The address was invalid. */
  180                         if (owner == -1)
  181                                 return (EFAULT);
  182 
  183                         if (owner == MTX_CONTESTED);
  184                                 return (0);
  185 
  186                         /* If this failed the lock has changed, restart. */
  187                         continue;
  188                 }
  189 
  190 
  191                 UMTX_LOCK();
  192                 uq = umtx_insert(td, umtx);
  193                 UMTX_UNLOCK();
  194 
  195                 /*
  196                  * Set the contested bit so that a release in user space
  197                  * knows to use the system call for unlock.  If this fails
  198                  * either some one else has acquired the lock or it has been
  199                  * released.
  200                  */
  201                 old = casuptr((intptr_t *)&umtx->u_owner, owner,
  202                     owner | UMTX_CONTESTED);
  203 
  204                 /* The address was invalid. */
  205                 if (old == -1) {
  206                         UMTX_LOCK();
  207                         umtx_remove(uq, td);
  208                         UMTX_UNLOCK();
  209                         return (EFAULT);
  210                 }
  211 
  212                 /*
  213                  * We set the contested bit, sleep. Otherwise the lock changed
  214                  * and we need to retry.
  215                  */
  216                 UMTX_LOCK();
  217                 if (old == owner)
  218                         error = msleep(td, &umtx_lock,
  219                             td->td_priority | PCATCH, "umtx", 0);
  220                 else
  221                         error = 0;
  222 
  223                 umtx_remove(uq, td);
  224                 UMTX_UNLOCK();
  225 
  226                 /*
  227                  * If we caught a signal we might have to retry or exit 
  228                  * immediately.
  229                  */
  230                 if (error)
  231                         return (error);
  232         }
  233 
  234         return (0);
  235 }
  236 
  237 int
  238 _umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap)
  239     /* struct umtx *umtx */
  240 {
  241         struct thread *blocked;
  242         struct umtx *umtx;
  243         struct umtx_q *uq;
  244         intptr_t owner;
  245         intptr_t old;
  246 
  247         umtx = uap->umtx;
  248 
  249         /*
  250          * Make sure we own this mtx.
  251          *
  252          * XXX Need a {fu,su}ptr this is not correct on arch where
  253          * sizeof(intptr_t) != sizeof(long).
  254          */
  255         if ((owner = fuword(&umtx->u_owner)) == -1)
  256                 return (EFAULT);
  257 
  258         if ((struct thread *)(owner & ~UMTX_CONTESTED) != td)
  259                 return (EPERM);
  260         /*
  261          * If we own it but it isn't contested then we can just release and
  262          * return.
  263          */
  264         if ((owner & UMTX_CONTESTED) == 0) {
  265                 owner = casuptr((intptr_t *)&umtx->u_owner,
  266                     (intptr_t)td, UMTX_UNOWNED);
  267 
  268                 if (owner == -1)
  269                         return (EFAULT);
  270                 /*
  271                  * If this failed someone modified the memory without going
  272                  * through this api.
  273                  */
  274                 if (owner != (intptr_t)td)
  275                         return (EINVAL);
  276 
  277                 return (0);
  278         }
  279 
  280         old = casuptr((intptr_t *)&umtx->u_owner, owner, UMTX_CONTESTED);
  281 
  282         if (old == -1)
  283                 return (EFAULT);
  284 
  285         /*
  286          * This will only happen if someone modifies the lock without going
  287          * through this api.
  288          */
  289         if (old != owner)
  290                 return (EINVAL);
  291 
  292         /*
  293          * We have to wake up one of the blocked threads.
  294          */
  295         UMTX_LOCK();
  296         uq = umtx_lookup(td, umtx);
  297         if (uq != NULL) {
  298                 blocked = TAILQ_FIRST(&uq->uq_tdq);
  299                 wakeup(blocked);
  300         }
  301 
  302         UMTX_UNLOCK();
  303 
  304         return (0);
  305 }

Cache object: 7403f45ec1239dff79b456db23580b9a


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