[BACK]Return to linux_futex.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / compat / linux / common

Annotation of src/sys/compat/linux/common/linux_futex.c, Revision 1.24.2.1

1.24.2.1! uebayasi    1: /*     $NetBSD$ */
1.1       manu        2:
                      3: /*-
                      4:  * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
                      9:  * 1. Redistributions of source code must retain the above copyright
                     10:  *    notice, this list of conditions and the following 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:  * 3. All advertising materials mentioning features or use of this software
                     15:  *    must display the following acknowledgement:
                     16:  *     This product includes software developed by Emmanuel Dreyfus
                     17:  * 4. The name of the author may not be used to endorse or promote
                     18:  *    products derived from this software without specific prior written
                     19:  *    permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS''
                     22:  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
                     23:  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     24:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
                     25:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     26:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     27:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     28:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     29:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     30:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     31:  * POSSIBILITY OF SUCH DAMAGE.
                     32:  */
                     33:
                     34: #include <sys/cdefs.h>
1.24.2.1! uebayasi   35: __KERNEL_RCSID(1, "$NetBSD$");
1.1       manu       36:
1.8       njoly      37: #include <sys/param.h>
1.1       manu       38: #include <sys/time.h>
                     39: #include <sys/systm.h>
                     40: #include <sys/proc.h>
                     41: #include <sys/lwp.h>
                     42: #include <sys/queue.h>
1.9       rmind      43: #include <sys/condvar.h>
                     44: #include <sys/mutex.h>
1.17      christos   45: #include <sys/once.h>
1.9       rmind      46: #include <sys/kmem.h>
1.8       njoly      47: #include <sys/kernel.h>
1.17      christos   48: #include <sys/atomic.h>
1.1       manu       49:
1.8       njoly      50: #include <compat/linux/common/linux_types.h>
1.17      christos   51: #include <compat/linux/common/linux_emuldata.h>
                     52: #include <compat/linux/common/linux_exec.h>
1.8       njoly      53: #include <compat/linux/common/linux_signal.h>
1.1       manu       54: #include <compat/linux/common/linux_futex.h>
1.20      njoly      55: #include <compat/linux/common/linux_sched.h>
1.24.2.1! uebayasi   56: #include <compat/linux/common/linux_machdep.h>
1.1       manu       57: #include <compat/linux/linux_syscallargs.h>
                     58:
1.20      njoly      59: void linux_to_native_timespec(struct timespec *, struct linux_timespec *);
                     60:
1.2       manu       61: struct futex;
                     62:
                     63: struct waiting_proc {
1.17      christos   64:        lwp_t *wp_l;
                     65:        struct futex *wp_new_futex;
1.9       rmind      66:        kcondvar_t wp_futex_cv;
1.2       manu       67:        TAILQ_ENTRY(waiting_proc) wp_list;
1.24.2.1! uebayasi   68:        TAILQ_ENTRY(waiting_proc) wp_rqlist;
1.2       manu       69: };
                     70: struct futex {
                     71:        void *f_uaddr;
                     72:        int f_refcount;
                     73:        LIST_ENTRY(futex) f_list;
1.24.2.1! uebayasi   74:        TAILQ_HEAD(, waiting_proc) f_waiting_proc;
        !            75:        TAILQ_HEAD(, waiting_proc) f_requeue_proc;
1.2       manu       76: };
                     77:
                     78: static LIST_HEAD(futex_list, futex) futex_list;
1.17      christos   79: static kmutex_t futex_lock;
                     80:
1.24.2.1! uebayasi   81: #define FUTEX_LOCK     mutex_enter(&futex_lock)
        !            82: #define FUTEX_UNLOCK   mutex_exit(&futex_lock)
        !            83: #define FUTEX_LOCKASSERT       KASSERT(mutex_owned(&futex_lock))
1.17      christos   84:
1.24.2.1! uebayasi   85: #define FUTEX_SYSTEM_LOCK      KERNEL_LOCK(1, NULL)
        !            86: #define FUTEX_SYSTEM_UNLOCK    KERNEL_UNLOCK_ONE(0)
1.9       rmind      87:
                     88: #ifdef DEBUG_LINUX_FUTEX
1.24.2.1! uebayasi   89: int debug_futex = 1;
        !            90: #define FUTEXPRINTF(a) do { if (debug_futex) printf a; } while (0)
1.9       rmind      91: #else
                     92: #define FUTEXPRINTF(a)
                     93: #endif
1.2       manu       94:
1.17      christos   95: static ONCE_DECL(futex_once);
                     96:
                     97: static int
                     98: futex_init(void)
                     99: {
1.19      njoly     100:        FUTEXPRINTF(("futex_init: initializing futex\n"));
1.17      christos  101:        mutex_init(&futex_lock, MUTEX_DEFAULT, IPL_NONE);
                    102:        return 0;
                    103: }
                    104:
1.24.2.1! uebayasi  105: static struct waiting_proc *futex_wp_alloc(void);
        !           106: static void futex_wp_free(struct waiting_proc *);
        !           107: static struct futex *futex_get(void *);
        !           108: static void futex_ref(struct futex *);
1.2       manu      109: static void futex_put(struct futex *);
1.24.2.1! uebayasi  110: static int futex_sleep(struct futex **, lwp_t *, int, struct waiting_proc *);
1.17      christos  111: static int futex_wake(struct futex *, int, struct futex *, int);
                    112: static int futex_atomic_op(lwp_t *, int, void *);
                    113:
1.1       manu      114: int
1.10      dsl       115: linux_sys_futex(struct lwp *l, const struct linux_sys_futex_args *uap, register_t *retval)
1.1       manu      116: {
1.10      dsl       117:        /* {
1.1       manu      118:                syscallarg(int *) uaddr;
                    119:                syscallarg(int) op;
                    120:                syscallarg(int) val;
1.20      njoly     121:                syscallarg(const struct linux_timespec *) timeout;
1.1       manu      122:                syscallarg(int *) uaddr2;
                    123:                syscallarg(int) val3;
1.10      dsl       124:        } */
1.24.2.1! uebayasi  125:        struct linux_timespec lts;
        !           126:        struct timespec ts = { 0, 0 };
        !           127:        int error;
        !           128:
        !           129:        if ((SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) == LINUX_FUTEX_WAIT &&
        !           130:            SCARG(uap, timeout) != NULL) {
        !           131:                if ((error = copyin(SCARG(uap, timeout),
        !           132:                    &lts, sizeof(lts))) != 0) {
        !           133:                        return error;
        !           134:                }
        !           135:                linux_to_native_timespec(&ts, &lts);
        !           136:        }
        !           137:        return linux_do_futex(l, uap, retval, &ts);
        !           138: }
        !           139:
        !           140: int
        !           141: linux_do_futex(struct lwp *l, const struct linux_sys_futex_args *uap, register_t *retval, struct timespec *ts)
        !           142: {
        !           143:        /* {
        !           144:                syscallarg(int *) uaddr;
        !           145:                syscallarg(int) op;
        !           146:                syscallarg(int) val;
        !           147:                syscallarg(const struct linux_timespec *) timeout;
        !           148:                syscallarg(int *) uaddr2;
        !           149:                syscallarg(int) val3;
        !           150:        } */
1.1       manu      151:        int val;
                    152:        int ret;
                    153:        int error = 0;
1.2       manu      154:        struct futex *f;
                    155:        struct futex *newf;
1.17      christos  156:        int timeout_hz;
                    157:        struct futex *f2;
1.24.2.1! uebayasi  158:        struct waiting_proc *wp;
1.17      christos  159:        int op_ret;
                    160:
                    161:        RUN_ONCE(&futex_once, futex_init);
                    162:
                    163:        /*
                    164:         * Our implementation provides only private futexes. Most of the apps
                    165:         * should use private futexes but don't claim so. Therefore we treat
                    166:         * all futexes as private by clearing the FUTEX_PRIVATE_FLAG. It works
                    167:         * in most cases (ie. when futexes are not shared on file descriptor
                    168:         * or between different processes).
                    169:         */
                    170:        switch (SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) {
                    171:        case LINUX_FUTEX_WAIT:
                    172:                FUTEX_SYSTEM_LOCK;
1.1       manu      173:
                    174:                if ((error = copyin(SCARG(uap, uaddr),
1.17      christos  175:                    &val, sizeof(val))) != 0) {
                    176:                        FUTEX_SYSTEM_UNLOCK;
1.1       manu      177:                        return error;
1.17      christos  178:                }
1.1       manu      179:
1.17      christos  180:                if (val != SCARG(uap, val)) {
                    181:                        FUTEX_SYSTEM_UNLOCK;
1.1       manu      182:                        return EWOULDBLOCK;
1.17      christos  183:                }
1.1       manu      184:
1.9       rmind     185:                FUTEXPRINTF(("FUTEX_WAIT %d.%d: val = %d, uaddr = %p, "
1.13      ad        186:                    "*uaddr = %d, timeout = %lld.%09ld\n",
1.1       manu      187:                    l->l_proc->p_pid, l->l_lid, SCARG(uap, val),
1.24.2.1! uebayasi  188:                    SCARG(uap, uaddr), val, (long long)ts->tv_sec,
        !           189:                    ts->tv_nsec));
1.6       manu      190:
1.24.2.1! uebayasi  191:                if ((error = itimespecfix(ts)) != 0) {
1.20      njoly     192:                        FUTEX_SYSTEM_UNLOCK;
                    193:                        return error;
                    194:                }
1.24.2.1! uebayasi  195:                timeout_hz = tstohz(ts);
1.17      christos  196:
                    197:                /*
                    198:                 * If the user process requests a non null timeout,
                    199:                 * make sure we do not turn it into an infinite
                    200:                 * timeout because timeout_hz is 0.
                    201:                 *
                    202:                 * We use a minimal timeout of 1/hz. Maybe it would make
                    203:                 * sense to just return ETIMEDOUT without sleeping.
                    204:                 */
1.22      njoly     205:                if (SCARG(uap, timeout) != NULL && timeout_hz == 0)
1.17      christos  206:                        timeout_hz = 1;
                    207:
1.24.2.1! uebayasi  208:                wp = futex_wp_alloc();
        !           209:                FUTEX_LOCK;
        !           210:                f = futex_get(SCARG(uap, uaddr));
        !           211:                ret = futex_sleep(&f, l, timeout_hz, wp);
1.2       manu      212:                futex_put(f);
1.24.2.1! uebayasi  213:                FUTEX_UNLOCK;
        !           214:                futex_wp_free(wp);
1.2       manu      215:
1.9       rmind     216:                FUTEXPRINTF(("FUTEX_WAIT %d.%d: uaddr = %p, "
1.1       manu      217:                    "ret = %d\n", l->l_proc->p_pid, l->l_lid,
1.9       rmind     218:                    SCARG(uap, uaddr), ret));
1.1       manu      219:
1.17      christos  220:                FUTEX_SYSTEM_UNLOCK;
1.1       manu      221:                switch (ret) {
                    222:                case EWOULDBLOCK:       /* timeout */
                    223:                        return ETIMEDOUT;
                    224:                        break;
                    225:                case EINTR:             /* signal */
                    226:                        return EINTR;
                    227:                        break;
                    228:                case 0:                 /* FUTEX_WAKE received */
1.9       rmind     229:                        FUTEXPRINTF(("FUTEX_WAIT %d.%d: uaddr = %p, got it\n",
                    230:                            l->l_proc->p_pid, l->l_lid, SCARG(uap, uaddr)));
1.1       manu      231:                        return 0;
                    232:                        break;
                    233:                default:
1.9       rmind     234:                        FUTEXPRINTF(("FUTEX_WAIT: unexpected ret = %d\n", ret));
1.1       manu      235:                        break;
                    236:                }
                    237:
                    238:                /* NOTREACHED */
                    239:                break;
                    240:
                    241:        case LINUX_FUTEX_WAKE:
                    242:                /*
1.2       manu      243:                 * XXX: Linux is able cope with different addresses
1.1       manu      244:                 * corresponding to the same mapped memory in the sleeping
1.17      christos  245:                 * and the waker process(es).
1.1       manu      246:                 */
1.9       rmind     247:                FUTEXPRINTF(("FUTEX_WAKE %d.%d: uaddr = %p, val = %d\n",
                    248:                    l->l_proc->p_pid, l->l_lid,
                    249:                    SCARG(uap, uaddr), SCARG(uap, val)));
1.15      njoly     250:
1.24.2.1! uebayasi  251:                FUTEX_SYSTEM_LOCK;
        !           252:                FUTEX_LOCK;
        !           253:                f = futex_get(SCARG(uap, uaddr));
1.17      christos  254:                *retval = futex_wake(f, SCARG(uap, val), NULL, 0);
1.2       manu      255:                futex_put(f);
1.24.2.1! uebayasi  256:                FUTEX_UNLOCK;
1.17      christos  257:                FUTEX_SYSTEM_UNLOCK;
1.15      njoly     258:
1.2       manu      259:                break;
                    260:
                    261:        case LINUX_FUTEX_CMP_REQUEUE:
1.17      christos  262:                FUTEX_SYSTEM_LOCK;
                    263:
1.2       manu      264:                if ((error = copyin(SCARG(uap, uaddr),
1.17      christos  265:                    &val, sizeof(val))) != 0) {
                    266:                        FUTEX_SYSTEM_UNLOCK;
1.2       manu      267:                        return error;
1.17      christos  268:                }
1.2       manu      269:
1.17      christos  270:                if (val != SCARG(uap, val3)) {
                    271:                        FUTEX_SYSTEM_UNLOCK;
1.2       manu      272:                        return EAGAIN;
1.17      christos  273:                }
                    274:
1.24.2.1! uebayasi  275:                FUTEXPRINTF(("FUTEX_CMP_REQUEUE %d.%d: uaddr = %p, val = %d, "
        !           276:                    "uaddr2 = %p, val2 = %d\n",
        !           277:                    l->l_proc->p_pid, l->l_lid,
        !           278:                    SCARG(uap, uaddr), SCARG(uap, val), SCARG(uap, uaddr2),
        !           279:                    (int)(unsigned long)SCARG(uap, timeout)));
        !           280:
        !           281:                FUTEX_LOCK;
        !           282:                f = futex_get(SCARG(uap, uaddr));
        !           283:                newf = futex_get(SCARG(uap, uaddr2));
1.17      christos  284:                *retval = futex_wake(f, SCARG(uap, val), newf,
                    285:                    (int)(unsigned long)SCARG(uap, timeout));
                    286:                futex_put(f);
                    287:                futex_put(newf);
1.24.2.1! uebayasi  288:                FUTEX_UNLOCK;
1.17      christos  289:
                    290:                FUTEX_SYSTEM_UNLOCK;
                    291:                break;
1.1       manu      292:
1.2       manu      293:        case LINUX_FUTEX_REQUEUE:
1.17      christos  294:                FUTEX_SYSTEM_LOCK;
1.15      njoly     295:
1.24.2.1! uebayasi  296:                FUTEXPRINTF(("FUTEX_REQUEUE %d.%d: uaddr = %p, val = %d, "
        !           297:                    "uaddr2 = %p, val2 = %d\n",
        !           298:                    l->l_proc->p_pid, l->l_lid,
        !           299:                    SCARG(uap, uaddr), SCARG(uap, val), SCARG(uap, uaddr2),
        !           300:                    (int)(unsigned long)SCARG(uap, timeout)));
        !           301:
        !           302:                FUTEX_LOCK;
        !           303:                f = futex_get(SCARG(uap, uaddr));
        !           304:                newf = futex_get(SCARG(uap, uaddr2));
1.17      christos  305:                *retval = futex_wake(f, SCARG(uap, val), newf,
                    306:                    (int)(unsigned long)SCARG(uap, timeout));
1.2       manu      307:                futex_put(f);
                    308:                futex_put(newf);
1.24.2.1! uebayasi  309:                FUTEX_UNLOCK;
1.15      njoly     310:
1.17      christos  311:                FUTEX_SYSTEM_UNLOCK;
1.1       manu      312:                break;
                    313:
                    314:        case LINUX_FUTEX_FD:
1.9       rmind     315:                FUTEXPRINTF(("linux_sys_futex: unimplemented op %d\n",
                    316:                    SCARG(uap, op)));
1.16      njoly     317:                return ENOSYS;
1.17      christos  318:        case LINUX_FUTEX_WAKE_OP:
                    319:                FUTEX_SYSTEM_LOCK;
1.24.2.1! uebayasi  320:
        !           321:                FUTEXPRINTF(("FUTEX_WAKE_OP %d.%d: uaddr = %p, op = %d, "
        !           322:                    "val = %d, uaddr2 = %p, val2 = %d\n",
        !           323:                    l->l_proc->p_pid, l->l_lid,
        !           324:                    SCARG(uap, uaddr), SCARG(uap, op), SCARG(uap, val),
        !           325:                    SCARG(uap, uaddr2),
        !           326:                    (int)(unsigned long)SCARG(uap, timeout)));
        !           327:
        !           328:                FUTEX_LOCK;
        !           329:                f = futex_get(SCARG(uap, uaddr));
        !           330:                f2 = futex_get(SCARG(uap, uaddr2));
        !           331:                FUTEX_UNLOCK;
        !           332:
1.17      christos  333:                /*
                    334:                 * This function returns positive number as results and
                    335:                 * negative as errors
                    336:                 */
                    337:                op_ret = futex_atomic_op(l, SCARG(uap, val3), SCARG(uap, uaddr2));
1.24.2.1! uebayasi  338:                FUTEX_LOCK;
1.17      christos  339:                if (op_ret < 0) {
                    340:                        futex_put(f);
                    341:                        futex_put(f2);
1.24.2.1! uebayasi  342:                        FUTEX_UNLOCK;
1.17      christos  343:                        FUTEX_SYSTEM_UNLOCK;
1.24.2.1! uebayasi  344:                        return -op_ret;
1.17      christos  345:                }
                    346:
                    347:                ret = futex_wake(f, SCARG(uap, val), NULL, 0);
                    348:                futex_put(f);
                    349:                if (op_ret > 0) {
                    350:                        op_ret = 0;
                    351:                        /*
                    352:                         * Linux abuses the address of the timespec parameter
                    353:                         * as the number of retries
                    354:                         */
                    355:                        op_ret += futex_wake(f2,
                    356:                            (int)(unsigned long)SCARG(uap, timeout), NULL, 0);
                    357:                        ret += op_ret;
                    358:                }
                    359:                futex_put(f2);
1.24.2.1! uebayasi  360:                FUTEX_UNLOCK;
1.17      christos  361:                FUTEX_SYSTEM_UNLOCK;
1.24.2.1! uebayasi  362:                *retval = ret;
1.17      christos  363:                break;
1.1       manu      364:        default:
1.9       rmind     365:                FUTEXPRINTF(("linux_sys_futex: unknown op %d\n",
                    366:                    SCARG(uap, op)));
1.16      njoly     367:                return ENOSYS;
1.1       manu      368:        }
                    369:        return 0;
                    370: }
1.2       manu      371:
1.24.2.1! uebayasi  372: static struct waiting_proc *
        !           373: futex_wp_alloc(void)
        !           374: {
        !           375:        struct waiting_proc *wp;
        !           376:
        !           377:        wp = kmem_zalloc(sizeof(*wp), KM_SLEEP);
        !           378:        cv_init(&wp->wp_futex_cv, "futex");
        !           379:        return wp;
        !           380: }
        !           381:
        !           382: static void
        !           383: futex_wp_free(struct waiting_proc *wp)
        !           384: {
        !           385:
        !           386:        cv_destroy(&wp->wp_futex_cv);
        !           387:        kmem_free(wp, sizeof(*wp));
        !           388: }
        !           389:
1.2       manu      390: static struct futex *
1.24.2.1! uebayasi  391: futex_get(void *uaddr)
1.2       manu      392: {
1.17      christos  393:        struct futex *f;
1.2       manu      394:
1.24.2.1! uebayasi  395:        FUTEX_LOCKASSERT;
1.15      njoly     396:
1.2       manu      397:        LIST_FOREACH(f, &futex_list, f_list) {
                    398:                if (f->f_uaddr == uaddr) {
                    399:                        f->f_refcount++;
                    400:                        return f;
                    401:                }
                    402:        }
                    403:
                    404:        /* Not found, create it */
1.17      christos  405:        f = kmem_zalloc(sizeof(*f), KM_SLEEP);
                    406:        f->f_uaddr = uaddr;
                    407:        f->f_refcount = 1;
                    408:        TAILQ_INIT(&f->f_waiting_proc);
1.24.2.1! uebayasi  409:        TAILQ_INIT(&f->f_requeue_proc);
1.17      christos  410:        LIST_INSERT_HEAD(&futex_list, f, f_list);
1.2       manu      411:
1.17      christos  412:        return f;
1.2       manu      413: }
                    414:
1.24.2.1! uebayasi  415: static void
        !           416: futex_ref(struct futex *f)
        !           417: {
        !           418:
        !           419:        FUTEX_LOCKASSERT;
        !           420:
        !           421:        f->f_refcount++;
        !           422: }
        !           423:
1.2       manu      424: static void
1.9       rmind     425: futex_put(struct futex *f)
1.2       manu      426: {
1.11      ad        427:
1.24.2.1! uebayasi  428:        FUTEX_LOCKASSERT;
        !           429:
1.2       manu      430:        f->f_refcount--;
                    431:        if (f->f_refcount == 0) {
1.15      njoly     432:                KASSERT(TAILQ_EMPTY(&f->f_waiting_proc));
1.24.2.1! uebayasi  433:                KASSERT(TAILQ_EMPTY(&f->f_requeue_proc));
1.2       manu      434:                LIST_REMOVE(f, f_list);
1.9       rmind     435:                kmem_free(f, sizeof(*f));
1.2       manu      436:        }
                    437: }
                    438:
                    439: static int
1.24.2.1! uebayasi  440: futex_sleep(struct futex **fp, lwp_t *l, int timeout, struct waiting_proc *wp)
1.2       manu      441: {
1.24.2.1! uebayasi  442:        struct futex *f, *newf;
1.2       manu      443:        int ret;
                    444:
1.24.2.1! uebayasi  445:        FUTEX_LOCKASSERT;
        !           446:
        !           447:        f = *fp;
1.17      christos  448:        wp->wp_l = l;
                    449:        wp->wp_new_futex = NULL;
1.9       rmind     450:
1.24.2.1! uebayasi  451: requeue:
1.2       manu      452:        TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list);
1.17      christos  453:        ret = cv_timedwait_sig(&wp->wp_futex_cv, &futex_lock, timeout);
1.2       manu      454:        TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
                    455:
1.24.2.1! uebayasi  456:        /* if futex_wake() tells us to requeue ... */
        !           457:        newf = wp->wp_new_futex;
        !           458:        if (ret == 0 && newf != NULL) {
        !           459:                /* ... requeue ourselves on the new futex */
        !           460:                futex_put(f);
        !           461:                wp->wp_new_futex = NULL;
        !           462:                TAILQ_REMOVE(&newf->f_requeue_proc, wp, wp_rqlist);
        !           463:                *fp = f = newf;
        !           464:                goto requeue;
1.2       manu      465:        }
                    466:        return ret;
                    467: }
                    468:
                    469: static int
1.17      christos  470: futex_wake(struct futex *f, int n, struct futex *newf, int n2)
1.2       manu      471: {
1.24.2.1! uebayasi  472:        struct waiting_proc *wp, *wpnext;
1.17      christos  473:        int count;
1.2       manu      474:
1.24.2.1! uebayasi  475:        FUTEX_LOCKASSERT;
        !           476:
1.17      christos  477:        count = newf ? 0 : 1;
1.15      njoly     478:
1.24.2.1! uebayasi  479:        /*
        !           480:         * first, wake up any threads sleeping on this futex.
        !           481:         * note that sleeping threads are not in the process of requeueing.
        !           482:         */
        !           483:
1.2       manu      484:        TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) {
1.24.2.1! uebayasi  485:                KASSERT(wp->wp_new_futex == NULL);
        !           486:
        !           487:                FUTEXPRINTF(("futex_wake: signal f %p l %p ref %d\n",
        !           488:                             f, wp->wp_l, f->f_refcount));
        !           489:                cv_signal(&wp->wp_futex_cv);
        !           490:                if (count <= n) {
        !           491:                        count++;
        !           492:                } else {
        !           493:                        if (newf == NULL)
        !           494:                                break;
        !           495:
        !           496:                        /* matching futex_put() is called by the other thread. */
        !           497:                        futex_ref(newf);
        !           498:                        wp->wp_new_futex = newf;
        !           499:                        TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist);
        !           500:                        FUTEXPRINTF(("futex_wake: requeue newf %p l %p ref %d\n",
        !           501:                                     newf, wp->wp_l, newf->f_refcount));
        !           502:                        if (count - n >= n2)
        !           503:                                goto out;
        !           504:                }
        !           505:        }
        !           506:
        !           507:        /*
        !           508:         * next, deal with threads that are requeuing to this futex.
        !           509:         * we don't need to signal these threads, any thread on the
        !           510:         * requeue list has already been signaled but hasn't had a chance
        !           511:         * to run and requeue itself yet.  if we would normally wake
        !           512:         * a thread, just remove the requeue info.  if we would normally
        !           513:         * requeue a thread, change the requeue target.
        !           514:         */
        !           515:
        !           516:        TAILQ_FOREACH_SAFE(wp, &f->f_requeue_proc, wp_rqlist, wpnext) {
        !           517:                KASSERT(wp->wp_new_futex == f);
        !           518:
        !           519:                FUTEXPRINTF(("futex_wake: unrequeue f %p l %p ref %d\n",
        !           520:                             f, wp->wp_l, f->f_refcount));
        !           521:                wp->wp_new_futex = NULL;
        !           522:                TAILQ_REMOVE(&f->f_requeue_proc, wp, wp_rqlist);
        !           523:                futex_put(f);
        !           524:
1.2       manu      525:                if (count <= n) {
                    526:                        count++;
                    527:                } else {
1.9       rmind     528:                        if (newf == NULL)
1.24.2.1! uebayasi  529:                                break;
        !           530:
        !           531:                        /* matching futex_put() is called by the other thread. */
        !           532:                        futex_ref(newf);
        !           533:                        wp->wp_new_futex = newf;
        !           534:                        TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist);
        !           535:                        FUTEXPRINTF(("futex_wake: rerequeue newf %p l %p ref %d\n",
        !           536:                                     newf, wp->wp_l, newf->f_refcount));
1.17      christos  537:                        if (count - n >= n2)
                    538:                                break;
1.2       manu      539:                }
                    540:        }
                    541:
1.24.2.1! uebayasi  542: out:
1.2       manu      543:        return count;
                    544: }
1.17      christos  545:
                    546: static int
                    547: futex_atomic_op(lwp_t *l, int encoded_op, void *uaddr)
                    548: {
1.23      rmind     549:        const int op = (encoded_op >> 28) & 7;
                    550:        const int cmp = (encoded_op >> 24) & 15;
                    551:        const int cmparg = (encoded_op << 20) >> 20;
1.17      christos  552:        int oparg = (encoded_op << 8) >> 20;
1.23      rmind     553:        int error, oldval, cval;
1.17      christos  554:
                    555:        if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
                    556:                oparg = 1 << oparg;
                    557:
                    558:        /* XXX: linux verifies access here and returns EFAULT */
                    559:
1.23      rmind     560:        if (copyin(uaddr, &cval, sizeof(int)) != 0)
                    561:                return -EFAULT;
                    562:
                    563:        for (;;) {
                    564:                int nval;
                    565:
                    566:                switch (op) {
                    567:                case FUTEX_OP_SET:
                    568:                        nval = oparg;
                    569:                        break;
                    570:                case FUTEX_OP_ADD:
                    571:                        nval = cval + oparg;
                    572:                        break;
                    573:                case FUTEX_OP_OR:
                    574:                        nval = cval | oparg;
                    575:                        break;
                    576:                case FUTEX_OP_ANDN:
                    577:                        nval = cval & ~oparg;
                    578:                        break;
                    579:                case FUTEX_OP_XOR:
                    580:                        nval = cval ^ oparg;
                    581:                        break;
                    582:                default:
                    583:                        return -ENOSYS;
                    584:                }
                    585:
                    586:                error = ucas_int(uaddr, cval, nval, &oldval);
                    587:                if (oldval == cval || error) {
                    588:                        break;
                    589:                }
                    590:                cval = oldval;
1.17      christos  591:        }
                    592:
1.23      rmind     593:        if (error)
                    594:                return -EFAULT;
1.17      christos  595:
                    596:        switch (cmp) {
                    597:        case FUTEX_OP_CMP_EQ:
                    598:                return (oldval == cmparg);
                    599:        case FUTEX_OP_CMP_NE:
                    600:                return (oldval != cmparg);
                    601:        case FUTEX_OP_CMP_LT:
                    602:                return (oldval < cmparg);
                    603:        case FUTEX_OP_CMP_GE:
                    604:                return (oldval >= cmparg);
                    605:        case FUTEX_OP_CMP_LE:
                    606:                return (oldval <= cmparg);
                    607:        case FUTEX_OP_CMP_GT:
                    608:                return (oldval > cmparg);
                    609:        default:
                    610:                return -ENOSYS;
                    611:        }
                    612: }
                    613:
                    614: int
                    615: linux_sys_set_robust_list(struct lwp *l,
                    616:     const struct linux_sys_set_robust_list_args *uap, register_t *retval)
                    617: {
1.24.2.1! uebayasi  618:        /* {
        !           619:                syscallarg(struct linux_robust_list_head *) head;
        !           620:                syscallarg(size_t) len;
        !           621:        } */
        !           622:        struct linux_emuldata *led;
1.17      christos  623:
1.24.2.1! uebayasi  624:        if (SCARG(uap, len) != sizeof(struct linux_robust_list_head))
1.24      christos  625:                return EINVAL;
1.24.2.1! uebayasi  626:        led = l->l_emuldata;
        !           627:        led->led_robust_head = SCARG(uap, head);
1.17      christos  628:        *retval = 0;
                    629:        return 0;
                    630: }
                    631:
                    632: int
                    633: linux_sys_get_robust_list(struct lwp *l,
                    634:     const struct linux_sys_get_robust_list_args *uap, register_t *retval)
                    635: {
1.24.2.1! uebayasi  636:        /* {
        !           637:                syscallarg(int) pid;
        !           638:                syscallarg(struct linux_robust_list_head **) head;
        !           639:                syscallarg(size_t *) len;
        !           640:        } */
        !           641:        struct proc *p;
1.17      christos  642:        struct linux_emuldata *led;
1.24.2.1! uebayasi  643:        struct linux_robust_list_head *head;
        !           644:        size_t len;
1.17      christos  645:        int error = 0;
                    646:
1.24.2.1! uebayasi  647:        p = l->l_proc;
1.17      christos  648:        if (!SCARG(uap, pid)) {
1.24.2.1! uebayasi  649:                led = l->l_emuldata;
        !           650:                head = led->led_robust_head;
1.17      christos  651:        } else {
1.24.2.1! uebayasi  652:                mutex_enter(p->p_lock);
        !           653:                l = lwp_find(p, SCARG(uap, pid));
        !           654:                if (l != NULL) {
        !           655:                        led = l->l_emuldata;
        !           656:                        head = led->led_robust_head;
        !           657:                }
        !           658:                mutex_exit(p->p_lock);
        !           659:                if (l == NULL) {
1.17      christos  660:                        return ESRCH;
                    661:                }
                    662:        }
1.24.2.1! uebayasi  663: #ifdef __arch64__
        !           664:        if (p->p_flag & PK_32) {
        !           665:                uint32_t u32;
        !           666:
        !           667:                u32 = 12;
        !           668:                error = copyout(&u32, SCARG(uap, len), sizeof(u32));
        !           669:                if (error)
        !           670:                        return error;
        !           671:                u32 = (uint32_t)(uintptr_t)head;
        !           672:                return copyout(&u32, SCARG(uap, head), sizeof(u32));
        !           673:        }
        !           674: #endif
1.17      christos  675:
1.24.2.1! uebayasi  676:        len = sizeof(*head);
1.24      christos  677:        error = copyout(&len, SCARG(uap, len), sizeof(len));
1.17      christos  678:        if (error)
                    679:                return error;
1.24.2.1! uebayasi  680:        return copyout(&head, SCARG(uap, head), sizeof(head));
1.17      christos  681: }
                    682:
                    683: static int
                    684: handle_futex_death(void *uaddr, pid_t pid, int pi)
                    685: {
                    686:        int uval, nval, mval;
                    687:        struct futex *f;
                    688:
                    689: retry:
1.24.2.1! uebayasi  690:        if (copyin(uaddr, &uval, sizeof(uval)))
1.17      christos  691:                return EFAULT;
                    692:
                    693:        if ((uval & FUTEX_TID_MASK) == pid) {
                    694:                mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
                    695:                nval = atomic_cas_32(uaddr, uval, mval);
                    696:
                    697:                if (nval == -1)
                    698:                        return EFAULT;
                    699:
                    700:                if (nval != uval)
                    701:                        goto retry;
                    702:
                    703:                if (!pi && (uval & FUTEX_WAITERS)) {
1.24.2.1! uebayasi  704:                        FUTEX_LOCK;
        !           705:                        f = futex_get(uaddr);
1.17      christos  706:                        futex_wake(f, 1, NULL, 0);
1.24.2.1! uebayasi  707:                        FUTEX_UNLOCK;
1.17      christos  708:                }
                    709:        }
                    710:
                    711:        return 0;
                    712: }
                    713:
                    714: static int
1.24.2.1! uebayasi  715: fetch_robust_entry(struct lwp *l, struct linux_robust_list **entry,
1.17      christos  716:     struct linux_robust_list **head, int *pi)
                    717: {
1.24.2.1! uebayasi  718:        struct linux_emuldata *led;
1.17      christos  719:        unsigned long uentry;
                    720:
1.24.2.1! uebayasi  721:        led = l->l_emuldata;
        !           722: #ifdef __arch64__
        !           723:        if (l->l_proc->p_flag & PK_32) {
        !           724:                uint32_t u32;
        !           725:
        !           726:                if (copyin(head, &u32, sizeof(u32)))
        !           727:                        return EFAULT;
        !           728:                uentry = (unsigned long)u32;
        !           729:        } else
        !           730: #endif
        !           731:        if (copyin(head, &uentry, sizeof(uentry)))
1.17      christos  732:                return EFAULT;
                    733:
                    734:        *entry = (void *)(uentry & ~1UL);
                    735:        *pi = uentry & 1;
                    736:
                    737:        return 0;
                    738: }
                    739:
                    740: /* This walks the list of robust futexes, releasing them. */
                    741: void
1.24.2.1! uebayasi  742: release_futexes(struct lwp *l)
1.17      christos  743: {
1.24      christos  744:        struct linux_robust_list_head head;
1.21      mrg       745:        struct linux_robust_list *entry, *next_entry = NULL, *pending;
1.17      christos  746:        unsigned int limit = 2048, pi, next_pi, pip;
                    747:        struct linux_emuldata *led;
                    748:        unsigned long futex_offset;
                    749:        int rc;
                    750:
1.24.2.1! uebayasi  751:        led = l->l_emuldata;
        !           752:        if (led->led_robust_head == NULL)
1.24      christos  753:                return;
1.17      christos  754:
1.24.2.1! uebayasi  755: #ifdef __arch64__
        !           756:        if (l->l_proc->p_flag & PK_32) {
        !           757:                uint32_t u32s[3];
        !           758:
        !           759:                if (copyin(led->led_robust_head, u32s, sizeof(u32s)))
        !           760:                        return;
        !           761:
        !           762:                head.list.next = (void *)(uintptr_t)u32s[0];
        !           763:                head.futex_offset = (unsigned long)u32s[1];
        !           764:                head.pending_list = (void *)(uintptr_t)u32s[2];
        !           765:        } else
        !           766: #endif
        !           767:        if (copyin(led->led_robust_head, &head, sizeof(head)))
1.17      christos  768:                return;
                    769:
1.24.2.1! uebayasi  770:        if (fetch_robust_entry(l, &entry, &head.list.next, &pi))
1.17      christos  771:                return;
                    772:
1.24.2.1! uebayasi  773: #ifdef __arch64__
        !           774:        if (l->l_proc->p_flag & PK_32) {
        !           775:                uint32_t u32;
        !           776:
        !           777:                if (copyin(led->led_robust_head, &u32, sizeof(u32)))
        !           778:                        return;
        !           779:
        !           780:                head.futex_offset = (unsigned long)u32;
        !           781:        } else
        !           782: #endif
1.24      christos  783:        if (copyin(&head.futex_offset, &futex_offset, sizeof(unsigned long)))
1.17      christos  784:                return;
                    785:
1.24.2.1! uebayasi  786:        if (fetch_robust_entry(l, &pending, &head.pending_list, &pip))
1.17      christos  787:                return;
                    788:
1.24      christos  789:        while (entry != &head.list) {
1.24.2.1! uebayasi  790:                rc = fetch_robust_entry(l, &next_entry, &entry->next, &next_pi);
1.17      christos  791:
                    792:                if (entry != pending)
                    793:                        if (handle_futex_death((char *)entry + futex_offset,
1.24.2.1! uebayasi  794:                            l->l_lid, pi))
1.17      christos  795:                                return;
                    796:
                    797:                if (rc)
                    798:                        return;
                    799:
                    800:                entry = next_entry;
                    801:                pi = next_pi;
                    802:
                    803:                if (!--limit)
                    804:                        break;
                    805:
                    806:                yield();        /* XXX why? */
                    807:        }
                    808:
                    809:        if (pending)
                    810:                handle_futex_death((char *)pending + futex_offset,
1.24.2.1! uebayasi  811:                    l->l_lid, pip);
1.17      christos  812: }

CVSweb <webmaster@jp.NetBSD.org>