Annotation of Gnu-Mach/kern/gsync.c, revision 1.1.1.2

1.1       root        1: /* Copyright (C) 2016 Free Software Foundation, Inc.
                      2:    Contributed by Agustina Arzille <[email protected]>, 2016.
                      3: 
                      4:    This program is free software; you can redistribute it and/or
                      5:    modify it under the terms of the GNU General Public License
                      6:    as published by the Free Software Foundation; either
1.1.1.2 ! root        7:    version 2 of the license, or (at your option) any later version.
1.1       root        8: 
                      9:    This program is distributed in the hope that it will be useful,
                     10:    but WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
                     12:    GNU General Public License for more details.
                     13: 
                     14:    You should have received a copy of the GNU General Public
                     15:    License along with this program; if not, see
                     16:    <http://www.gnu.org/licenses/>.
                     17: */
                     18: 
                     19: #include <kern/gsync.h>
                     20: #include <kern/sched_prim.h>
                     21: #include <kern/thread.h>
                     22: #include <kern/lock.h>
                     23: #include <kern/list.h>
                     24: #include <vm/vm_map.h>
                     25: 
                     26: /* An entry in the global hash table. */
                     27: struct gsync_hbucket
                     28: {
                     29:   struct list entries;
                     30:   decl_simple_lock_data (, lock)
                     31: };
                     32: 
                     33: /* A key used to uniquely identify an address that a thread is
                     34:  * waiting on. Its members' values depend on whether said
                     35:  * address is shared or task-local. */
                     36: struct gsync_key
                     37: {
                     38:   unsigned long u;
                     39:   unsigned long v;
                     40: };
                     41: 
                     42: /* A thread that is blocked on an address with 'gsync_wait'. */
                     43: struct gsync_waiter
                     44: {
                     45:   struct list link;
                     46:   struct gsync_key key;
                     47:   thread_t waiter;
                     48: };
                     49: 
                     50: #define GSYNC_NBUCKETS   512
                     51: static struct gsync_hbucket gsync_buckets[GSYNC_NBUCKETS];
                     52: 
                     53: void gsync_setup (void)
                     54: {
                     55:   int i;
                     56:   for (i = 0; i < GSYNC_NBUCKETS; ++i)
                     57:     {
                     58:       list_init (&gsync_buckets[i].entries);
                     59:       simple_lock_init (&gsync_buckets[i].lock);
                     60:     }
                     61: }
                     62: 
                     63: /* Convenience comparison functions for gsync_key's. */
                     64: 
                     65: static inline int
                     66: gsync_key_eq (const struct gsync_key *lp,
                     67:   const struct gsync_key *rp)
                     68: {
                     69:   return (lp->u == rp->u && lp->v == rp->v);
                     70: }
                     71: 
                     72: static inline int
                     73: gsync_key_lt (const struct gsync_key *lp,
                     74:   const struct gsync_key *rp)
                     75: {
                     76:   return (lp->u < rp->u || (lp->u == rp->u && lp->v < rp->v));
                     77: }
                     78: 
                     79: #define MIX2_LL(x, y)   ((((x) << 5) | ((x) >> 27)) ^ (y))
                     80: 
                     81: static inline unsigned int
                     82: gsync_key_hash (const struct gsync_key *keyp)
                     83: {
                     84:   unsigned int ret = sizeof (void *);
                     85: #ifndef __LP64__
                     86:   ret = MIX2_LL (ret, keyp->u);
                     87:   ret = MIX2_LL (ret, keyp->v);
                     88: #else
                     89:   ret = MIX2_LL (ret, keyp->u & ~0U);
                     90:   ret = MIX2_LL (ret, keyp->u >> 32);
                     91:   ret = MIX2_LL (ret, keyp->v & ~0U);
                     92:   ret = MIX2_LL (ret, keyp->v >> 32);
                     93: #endif
                     94:   return (ret);
                     95: }
                     96: 
                     97: /* Test if the passed VM Map can access the address ADDR. The
                     98:  * parameter FLAGS is used to specify the width and protection
                     99:  * of the address. */
                    100: static int
                    101: valid_access_p (vm_map_t map, vm_offset_t addr, int flags)
                    102: {
                    103:   vm_prot_t prot = VM_PROT_READ |
                    104:     ((flags & GSYNC_MUTATE) ? VM_PROT_WRITE : 0);
                    105:   vm_offset_t size = sizeof (unsigned int) *
                    106:     ((flags & GSYNC_QUAD) ? 2 : 1);
                    107: 
                    108:   vm_map_entry_t entry;
                    109:   return (vm_map_lookup_entry (map, addr, &entry) &&
                    110:     entry->vme_end >= addr + size &&
                    111:     (prot & entry->protection) == prot);
                    112: }
                    113: 
                    114: /* Given a task and an address, initialize the key at *KEYP and
                    115:  * return the corresponding bucket in the global hash table. */
                    116: static int
                    117: gsync_fill_key (task_t task, vm_offset_t addr,
                    118:   int flags, struct gsync_key *keyp)
                    119: {
                    120:   if (flags & GSYNC_SHARED)
                    121:     {
                    122:       /* For a shared address, we need the VM object
                    123:        * and offset as the keys. */
                    124:       vm_map_t map = task->map;
                    125:       vm_prot_t prot = VM_PROT_READ |
                    126:         ((flags & GSYNC_MUTATE) ? VM_PROT_WRITE : 0);
                    127:       vm_map_version_t ver;
                    128:       vm_prot_t rpr;
                    129:       vm_object_t obj;
                    130:       vm_offset_t off;
                    131:       boolean_t wired_p;
                    132: 
                    133:       if (unlikely (vm_map_lookup (&map, addr, prot, &ver,
                    134:           &obj, &off, &rpr, &wired_p) != KERN_SUCCESS))
                    135:         return (-1);
                    136: 
                    137:       /* The VM object is returned locked. However, we check the
                    138:        * address' accessibility later, so we can release it. */
                    139:       vm_object_unlock (obj);
                    140: 
                    141:       keyp->u = (unsigned long)obj;
                    142:       keyp->v = (unsigned long)off;
                    143:     }
                    144:   else
                    145:     {
                    146:       /* Task-local address. The keys are the task's map and
                    147:        * the virtual address itself. */
                    148:       keyp->u = (unsigned long)task->map;
                    149:       keyp->v = (unsigned long)addr;
                    150:     }
                    151: 
                    152:   return ((int)(gsync_key_hash (keyp) % GSYNC_NBUCKETS));
                    153: }
                    154: 
                    155: static inline struct gsync_waiter*
                    156: node_to_waiter (struct list *nodep)
                    157: {
                    158:   return (list_entry (nodep, struct gsync_waiter, link));
                    159: }
                    160: 
                    161: static inline struct list*
                    162: gsync_find_key (const struct list *entries,
                    163:   const struct gsync_key *keyp, int *exactp)
                    164: {
                    165:   /* Look for a key that matches. We take advantage of the fact
                    166:    * that the entries are sorted to break out of the loop as
                    167:    * early as possible. */
                    168:   struct list *runp;
                    169:   list_for_each (entries, runp)
                    170:     {
                    171:       struct gsync_waiter *p = node_to_waiter (runp);
                    172:       if (gsync_key_lt (keyp, &p->key))
                    173:         break;
                    174:       else if (gsync_key_eq (keyp, &p->key))
                    175:         {
                    176:           if (exactp != 0)
                    177:             *exactp = 1;
                    178:           break;
                    179:         }
                    180:     }
                    181: 
                    182:   return (runp);
                    183: }
                    184: 
                    185: kern_return_t gsync_wait (task_t task, vm_offset_t addr,
                    186:   unsigned int lo, unsigned int hi, natural_t msec, int flags)
                    187: {
1.1.1.2 ! root      188:   if (unlikely (task != current_task()))
        !           189:     /* Not implemented yet.  */
        !           190:     return (KERN_FAILURE);
        !           191: 
1.1       root      192:   struct gsync_waiter w;
                    193:   int bucket = gsync_fill_key (task, addr, flags, &w.key);
                    194: 
                    195:   if (unlikely (bucket < 0))
                    196:     return (KERN_INVALID_ADDRESS);
                    197: 
1.1.1.2 ! root      198:   /* Test that the address is actually valid for the
1.1       root      199:    * given task. Do so with the read-lock held in order
                    200:    * to prevent memory deallocations. */
                    201:   vm_map_lock_read (task->map);
                    202: 
1.1.1.2 ! root      203:   struct gsync_hbucket *hbp = gsync_buckets + bucket;
        !           204:   simple_lock (&hbp->lock);
        !           205: 
1.1       root      206:   if (unlikely (!valid_access_p (task->map, addr, flags)))
                    207:     {
                    208:       simple_unlock (&hbp->lock);
                    209:       vm_map_unlock_read (task->map);
                    210:       return (KERN_INVALID_ADDRESS);
                    211:     }
                    212: 
                    213:   /* Before doing any work, check that the expected value(s)
                    214:    * match the contents of the address. Otherwise, the waiting
                    215:    * thread could potentially miss a wakeup. */
                    216:   if (((unsigned int *)addr)[0] != lo ||
                    217:       ((flags & GSYNC_QUAD) &&
                    218:         ((unsigned int *)addr)[1] != hi))
                    219:     {
                    220:       simple_unlock (&hbp->lock);
                    221:       vm_map_unlock_read (task->map);
                    222:       return (KERN_INVALID_ARGUMENT);
                    223:     }
                    224: 
                    225:   vm_map_unlock_read (task->map);
                    226: 
                    227:   /* Look for the first entry in the hash bucket that
                    228:    * compares strictly greater than this waiter. */
                    229:   struct list *runp;
                    230:   list_for_each (&hbp->entries, runp)
                    231:     {
                    232:       struct gsync_waiter *p = node_to_waiter (runp);
                    233:       if (gsync_key_lt (&w.key, &p->key))
                    234:         break;
                    235:     }
                    236: 
                    237:   /* Finally, add ourselves to the list and go to sleep. */
                    238:   list_add (runp->prev, runp, &w.link);
                    239:   w.waiter = current_thread ();
                    240: 
                    241:   if (flags & GSYNC_TIMED)
                    242:     thread_will_wait_with_timeout (w.waiter, msec);
                    243:   else
                    244:     thread_will_wait (w.waiter);
                    245: 
                    246:   thread_sleep (0, (simple_lock_t)&hbp->lock, TRUE);
                    247: 
                    248:   /* We're back. */
                    249:   kern_return_t ret = current_thread()->wait_result;
                    250:   if (ret != THREAD_AWAKENED)
                    251:     {
                    252:       /* We were interrupted or timed out. */
                    253:       simple_lock (&hbp->lock);
                    254:       if (w.link.next != 0)
                    255:         list_remove (&w.link);
                    256:       simple_unlock (&hbp->lock);
                    257: 
                    258:       /* Map the error code. */
                    259:       ret = ret == THREAD_INTERRUPTED ?
                    260:         KERN_INTERRUPTED : KERN_TIMEDOUT;
                    261:     }
                    262:   else
                    263:     ret = KERN_SUCCESS;
                    264: 
                    265:   return (ret);
                    266: }
                    267: 
                    268: /* Remove a waiter from the queue, wake it up, and
                    269:  * return the next node. */
                    270: static inline struct list*
                    271: dequeue_waiter (struct list *nodep)
                    272: {
                    273:   struct list *nextp = list_next (nodep);
                    274:   list_remove (nodep);
                    275:   list_node_init (nodep);
                    276:   clear_wait (node_to_waiter(nodep)->waiter,
                    277:     THREAD_AWAKENED, FALSE);
                    278:   return (nextp);
                    279: }
                    280: 
                    281: kern_return_t gsync_wake (task_t task,
                    282:   vm_offset_t addr, unsigned int val, int flags)
                    283: {
1.1.1.2 ! root      284:   if (unlikely (task != current_task()))
        !           285:     /* Not implemented yet.  */
        !           286:     return (KERN_FAILURE);
        !           287: 
1.1       root      288:   struct gsync_key key;
                    289:   int bucket = gsync_fill_key (task, addr, flags, &key);
                    290: 
                    291:   if (unlikely (bucket < 0))
                    292:     return (KERN_INVALID_ADDRESS);
                    293: 
                    294:   kern_return_t ret = KERN_INVALID_ARGUMENT;
                    295: 
1.1.1.2 ! root      296:   vm_map_lock_read (task->map);
1.1       root      297:   struct gsync_hbucket *hbp = gsync_buckets + bucket;
                    298:   simple_lock (&hbp->lock);
                    299: 
                    300:   if (unlikely (!valid_access_p (task->map, addr, flags)))
                    301:     {
                    302:       simple_unlock (&hbp->lock);
                    303:       vm_map_unlock_read (task->map);
                    304:       return (KERN_INVALID_ADDRESS);
                    305:     }
                    306: 
                    307:   if (flags & GSYNC_MUTATE)
                    308:     /* Set the contents of the address to the specified value,
                    309:      * even if we don't end up waking any threads. Note that
                    310:      * the buckets' simple locks give us atomicity. */
                    311:     *(unsigned int *)addr = val;
                    312: 
                    313:   vm_map_unlock_read (task->map);
                    314: 
                    315:   int found = 0;
                    316:   struct list *runp = gsync_find_key (&hbp->entries, &key, &found);
                    317:   if (found)
                    318:     {
                    319:       do
                    320:         runp = dequeue_waiter (runp);
                    321:       while ((flags & GSYNC_BROADCAST) &&
                    322:         !list_end (&hbp->entries, runp) &&
                    323:         gsync_key_eq (&node_to_waiter(runp)->key, &key));
                    324: 
                    325:       ret = KERN_SUCCESS;
                    326:     }
                    327: 
                    328:   simple_unlock (&hbp->lock);
                    329:   return (ret);
                    330: }
                    331: 
                    332: kern_return_t gsync_requeue (task_t task, vm_offset_t src,
                    333:   vm_offset_t dst, boolean_t wake_one, int flags)
                    334: {
1.1.1.2 ! root      335:   if (unlikely (task != current_task()))
        !           336:     /* Not implemented yet.  */
        !           337:     return (KERN_FAILURE);
        !           338: 
1.1       root      339:   struct gsync_key src_k, dst_k;
                    340:   int src_bkt = gsync_fill_key (task, src, flags, &src_k);
                    341:   int dst_bkt = gsync_fill_key (task, dst, flags, &dst_k);
                    342: 
                    343:   if ((src_bkt | dst_bkt) < 0)
                    344:     return (KERN_INVALID_ADDRESS);
                    345: 
                    346:   vm_map_lock_read (task->map);
                    347: 
                    348:   /* We don't actually dereference or modify the contents
                    349:    * of the addresses, but we still check that they can
                    350:    * be accessed by the task. */
                    351:   if (unlikely (!valid_access_p (task->map, src, flags) ||
                    352:       !valid_access_p (task->map, dst, flags)))
                    353:     {
                    354:       vm_map_unlock_read (task->map);
                    355:       return (KERN_INVALID_ADDRESS);
                    356:     }
                    357: 
                    358:   vm_map_unlock_read (task->map);
                    359: 
                    360:   /* If we're asked to unconditionally wake up a waiter, then
                    361:    * we need to remove a maximum of two threads from the queue. */
                    362:   unsigned int nw = 1 + wake_one;
                    363:   struct gsync_hbucket *bp1 = gsync_buckets + src_bkt;
                    364:   struct gsync_hbucket *bp2 = gsync_buckets + dst_bkt;
                    365: 
                    366:   /* Acquire the locks in order, to prevent any potential deadlock. */
                    367:   if (bp1 == bp2)
                    368:     simple_lock (&bp1->lock);
                    369:   else if ((unsigned long)bp1 < (unsigned long)bp2)
                    370:     {
                    371:       simple_lock (&bp1->lock);
                    372:       simple_lock (&bp2->lock);
                    373:     }
                    374:   else
                    375:     {
                    376:       simple_lock (&bp2->lock);
                    377:       simple_lock (&bp1->lock);
                    378:     }
                    379: 
                    380:   kern_return_t ret = KERN_SUCCESS;
                    381:   int exact;
                    382:   struct list *inp = gsync_find_key (&bp1->entries, &src_k, &exact);
                    383: 
                    384:   if (!exact)
                    385:     /* There are no waiters in the source queue. */
                    386:     ret = KERN_INVALID_ARGUMENT;
                    387:   else
                    388:     {
                    389:       struct list *outp = gsync_find_key (&bp2->entries, &dst_k, 0);
                    390: 
                    391:       /* We're going to need a node that points one past the
                    392:        * end of the waiters in the source queue. */
                    393:       struct list *endp = inp;
                    394: 
                    395:       do
                    396:         {
                    397:           /* Modify the keys while iterating. */
                    398:           node_to_waiter(endp)->key = dst_k;
                    399:           endp = list_next (endp);
                    400:         }
                    401:       while (((flags & GSYNC_BROADCAST) || --nw != 0) &&
                    402:         !list_end (&bp1->entries, endp) &&
                    403:         gsync_key_eq (&node_to_waiter(endp)->key, &src_k));
                    404: 
                    405:       /* Splice the list by removing waiters from the source queue
                    406:        * and inserting them into the destination queue. */
                    407:       inp->prev->next = endp;
                    408:       endp->prev->next = outp->next;
                    409:       endp->prev = inp->prev;
                    410: 
                    411:       outp->next = inp;
                    412:       inp->prev = outp;
                    413: 
                    414:       if (wake_one)
                    415:         (void)dequeue_waiter (inp);
                    416:     }
                    417: 
                    418:   /* Release the locks and we're done.*/
                    419:   simple_unlock (&bp1->lock);
                    420:   if (bp1 != bp2)
                    421:     simple_unlock (&bp2->lock);
                    422: 
                    423:   return (ret);
                    424: }
                    425: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.