[BACK]Return to subr_ipi.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

Annotation of src/sys/kern/subr_ipi.c, Revision 1.3

1.3     ! rmind       1: /*     $NetBSD: subr_ipi.c,v 1.2 2014/05/25 15:34:19 rmind Exp $       */
1.1       rmind       2:
                      3: /*-
                      4:  * Copyright (c) 2014 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Mindaugas Rasiukevicius.
                      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: /*
1.2       rmind      33:  * Inter-processor interrupt (IPI) interface: asynchronous IPIs to
                     34:  * invoke functions with a constant argument and synchronous IPIs
                     35:  * with the cross-call support.
1.1       rmind      36:  */
                     37:
                     38: #include <sys/cdefs.h>
1.3     ! rmind      39: __KERNEL_RCSID(0, "$NetBSD: subr_ipi.c,v 1.2 2014/05/25 15:34:19 rmind Exp $");
1.1       rmind      40:
                     41: #include <sys/param.h>
                     42: #include <sys/types.h>
                     43:
                     44: #include <sys/atomic.h>
                     45: #include <sys/evcnt.h>
                     46: #include <sys/cpu.h>
                     47: #include <sys/ipi.h>
1.3     ! rmind      48: #include <sys/intr.h>
1.1       rmind      49: #include <sys/kcpuset.h>
                     50: #include <sys/kmem.h>
                     51: #include <sys/lock.h>
1.2       rmind      52: #include <sys/mutex.h>
                     53:
                     54: /*
                     55:  * An array of the IPI handlers used for asynchronous invocation.
                     56:  * The lock protects the slot allocation.
                     57:  */
                     58:
                     59: typedef struct {
                     60:        ipi_func_t      func;
                     61:        void *          arg;
                     62: } ipi_intr_t;
                     63:
                     64: static kmutex_t                ipi_mngmt_lock;
                     65: static ipi_intr_t      ipi_intrs[IPI_MAXREG]   __cacheline_aligned;
1.1       rmind      66:
                     67: /*
                     68:  * Per-CPU mailbox for IPI messages: it is a single cache line storing
1.2       rmind      69:  * up to IPI_MSG_MAX messages.  This interface is built on top of the
                     70:  * synchronous IPIs.
1.1       rmind      71:  */
                     72:
                     73: #define        IPI_MSG_SLOTS   (CACHE_LINE_SIZE / sizeof(ipi_msg_t *))
                     74: #define        IPI_MSG_MAX     IPI_MSG_SLOTS
                     75:
                     76: typedef struct {
                     77:        ipi_msg_t *     msg[IPI_MSG_SLOTS];
                     78: } ipi_mbox_t;
                     79:
1.2       rmind      80:
                     81: /* Mailboxes for the synchronous IPIs. */
1.1       rmind      82: static ipi_mbox_t *    ipi_mboxes      __read_mostly;
                     83: static struct evcnt    ipi_mboxfull_ev __cacheline_aligned;
1.2       rmind      84: static void            ipi_msg_cpu_handler(void *);
                     85:
                     86: /* Handler for the synchronous IPIs - it must be zero. */
                     87: #define        IPI_SYNCH_ID    0
1.1       rmind      88:
                     89: #ifndef MULTIPROCESSOR
                     90: #define        cpu_ipi(ci)     KASSERT(ci == NULL)
                     91: #endif
                     92:
                     93: void
                     94: ipi_sysinit(void)
                     95: {
                     96:        const size_t len = ncpu * sizeof(ipi_mbox_t);
                     97:
1.2       rmind      98:        /* Initialise the per-CPU bit fields. */
                     99:        for (u_int i = 0; i < ncpu; i++) {
                    100:                struct cpu_info *ci = cpu_lookup(i);
                    101:                memset(&ci->ci_ipipend, 0, sizeof(ci->ci_ipipend));
                    102:        }
                    103:        mutex_init(&ipi_mngmt_lock, MUTEX_DEFAULT, IPL_NONE);
                    104:        memset(ipi_intrs, 0, sizeof(ipi_intrs));
                    105:
1.1       rmind     106:        /* Allocate per-CPU IPI mailboxes. */
                    107:        ipi_mboxes = kmem_zalloc(len, KM_SLEEP);
                    108:        KASSERT(ipi_mboxes != NULL);
                    109:
1.2       rmind     110:        /*
                    111:         * Register the handler for synchronous IPIs.  This mechanism
                    112:         * is built on top of the asynchronous interface.  Slot zero is
                    113:         * reserved permanently; it is also handy to use zero as a failure
                    114:         * for other registers (as it is potentially less error-prone).
                    115:         */
                    116:        ipi_intrs[IPI_SYNCH_ID].func = ipi_msg_cpu_handler;
                    117:
1.1       rmind     118:        evcnt_attach_dynamic(&ipi_mboxfull_ev, EVCNT_TYPE_MISC, NULL,
                    119:           "ipi", "full");
                    120: }
                    121:
                    122: /*
1.2       rmind     123:  * ipi_register: register an asynchronous IPI handler.
                    124:  *
                    125:  * => Returns IPI ID which is greater than zero; on failure - zero.
                    126:  */
                    127: u_int
                    128: ipi_register(ipi_func_t func, void *arg)
                    129: {
                    130:        mutex_enter(&ipi_mngmt_lock);
                    131:        for (u_int i = 0; i < IPI_MAXREG; i++) {
                    132:                if (ipi_intrs[i].func == NULL) {
                    133:                        /* Register the function. */
                    134:                        ipi_intrs[i].func = func;
                    135:                        ipi_intrs[i].arg = arg;
                    136:                        mutex_exit(&ipi_mngmt_lock);
                    137:
                    138:                        KASSERT(i != IPI_SYNCH_ID);
                    139:                        return i;
                    140:                }
                    141:        }
                    142:        mutex_exit(&ipi_mngmt_lock);
                    143:        printf("WARNING: ipi_register: table full, increase IPI_MAXREG\n");
                    144:        return 0;
                    145: }
                    146:
                    147: /*
                    148:  * ipi_unregister: release the IPI handler given the ID.
                    149:  */
                    150: void
                    151: ipi_unregister(u_int ipi_id)
                    152: {
                    153:        ipi_msg_t ipimsg = { .func = (ipi_func_t)nullop };
                    154:
                    155:        KASSERT(ipi_id != IPI_SYNCH_ID);
                    156:        KASSERT(ipi_id < IPI_MAXREG);
                    157:
                    158:        /* Release the slot. */
                    159:        mutex_enter(&ipi_mngmt_lock);
                    160:        KASSERT(ipi_intrs[ipi_id].func != NULL);
                    161:        ipi_intrs[ipi_id].func = NULL;
                    162:
                    163:        /* Ensure that there are no IPIs in flight. */
                    164:        kpreempt_disable();
                    165:        ipi_broadcast(&ipimsg);
                    166:        ipi_wait(&ipimsg);
                    167:        kpreempt_enable();
                    168:        mutex_exit(&ipi_mngmt_lock);
                    169: }
                    170:
                    171: /*
                    172:  * ipi_trigger: asynchronously send an IPI to the specified CPU.
                    173:  */
                    174: void
                    175: ipi_trigger(u_int ipi_id, struct cpu_info *ci)
                    176: {
                    177:        const u_int i = ipi_id >> IPI_BITW_SHIFT;
                    178:        const uint32_t bitm = 1U << (ipi_id & IPI_BITW_MASK);
                    179:
                    180:        KASSERT(ipi_id < IPI_MAXREG);
                    181:        KASSERT(kpreempt_disabled());
                    182:        KASSERT(curcpu() != ci);
                    183:
                    184:        /* Mark as pending and send an IPI. */
                    185:        if (membar_consumer(), (ci->ci_ipipend[i] & bitm) == 0) {
                    186:                atomic_or_32(&ci->ci_ipipend[i], bitm);
                    187:                cpu_ipi(ci);
                    188:        }
                    189: }
                    190:
                    191: /*
1.3     ! rmind     192:  * ipi_trigger_multi: same as ipi_trigger() but sends to the multiple
        !           193:  * CPUs given the target CPU set.
        !           194:  */
        !           195: void
        !           196: ipi_trigger_multi(u_int ipi_id, const kcpuset_t *target)
        !           197: {
        !           198:        const cpuid_t selfid = cpu_index(curcpu());
        !           199:        CPU_INFO_ITERATOR cii;
        !           200:        struct cpu_info *ci;
        !           201:
        !           202:        KASSERT(kpreempt_disabled());
        !           203:        KASSERT(target != NULL);
        !           204:
        !           205:        for (CPU_INFO_FOREACH(cii, ci)) {
        !           206:                const cpuid_t cpuid = cpu_index(ci);
        !           207:
        !           208:                if (!kcpuset_isset(target, cpuid) || cpuid == selfid) {
        !           209:                        continue;
        !           210:                }
        !           211:                ipi_trigger(ipi_id, ci);
        !           212:        }
        !           213:        if (kcpuset_isset(target, selfid)) {
        !           214:                int s = splhigh();
        !           215:                ipi_cpu_handler();
        !           216:                splx(s);
        !           217:        }
        !           218: }
        !           219:
        !           220: /*
1.1       rmind     221:  * put_msg: insert message into the mailbox.
                    222:  */
                    223: static inline void
                    224: put_msg(ipi_mbox_t *mbox, ipi_msg_t *msg)
                    225: {
                    226:        int count = SPINLOCK_BACKOFF_MIN;
                    227: again:
                    228:        for (u_int i = 0; i < IPI_MSG_MAX; i++) {
                    229:                if (__predict_true(mbox->msg[i] == NULL) &&
                    230:                    atomic_cas_ptr(&mbox->msg[i], NULL, msg) == NULL) {
                    231:                        return;
                    232:                }
                    233:        }
                    234:
                    235:        /* All slots are full: we have to spin-wait. */
                    236:        ipi_mboxfull_ev.ev_count++;
                    237:        SPINLOCK_BACKOFF(count);
                    238:        goto again;
                    239: }
                    240:
                    241: /*
                    242:  * ipi_cpu_handler: the IPI handler.
                    243:  */
                    244: void
                    245: ipi_cpu_handler(void)
                    246: {
1.2       rmind     247:        struct cpu_info * const ci = curcpu();
                    248:
                    249:        /*
                    250:         * Handle asynchronous IPIs: inspect per-CPU bit field, extract
                    251:         * IPI ID numbers and execute functions in those slots.
                    252:         */
                    253:        for (u_int i = 0; i < IPI_BITWORDS; i++) {
                    254:                uint32_t pending, bit;
                    255:
                    256:                if (ci->ci_ipipend[i] == 0) {
                    257:                        continue;
                    258:                }
                    259:                pending = atomic_swap_32(&ci->ci_ipipend[i], 0);
                    260: #ifndef __HAVE_ATOMIC_AS_MEMBAR
                    261:                membar_producer();
                    262: #endif
                    263:                while ((bit = ffs(pending)) != 0) {
                    264:                        const u_int ipi_id = (i << IPI_BITW_SHIFT) | --bit;
                    265:                        ipi_intr_t *ipi_hdl = &ipi_intrs[ipi_id];
                    266:
                    267:                        pending &= ~(1U << bit);
                    268:                        KASSERT(ipi_hdl->func != NULL);
                    269:                        ipi_hdl->func(ipi_hdl->arg);
                    270:                }
                    271:        }
                    272: }
                    273:
                    274: /*
                    275:  * ipi_msg_cpu_handler: handle synchronous IPIs - iterate mailbox,
                    276:  * execute the passed functions and acknowledge the messages.
                    277:  */
                    278: static void
                    279: ipi_msg_cpu_handler(void *arg __unused)
                    280: {
1.1       rmind     281:        const struct cpu_info * const ci = curcpu();
                    282:        ipi_mbox_t *mbox = &ipi_mboxes[cpu_index(ci)];
                    283:
                    284:        for (u_int i = 0; i < IPI_MSG_MAX; i++) {
                    285:                ipi_msg_t *msg;
                    286:
                    287:                /* Get the message. */
                    288:                if ((msg = mbox->msg[i]) == NULL) {
                    289:                        continue;
                    290:                }
                    291:                mbox->msg[i] = NULL;
                    292:
                    293:                /* Execute the handler. */
                    294:                KASSERT(msg->func);
                    295:                msg->func(msg->arg);
                    296:
                    297:                /* Ack the request. */
                    298:                atomic_dec_uint(&msg->_pending);
                    299:        }
                    300: }
                    301:
                    302: /*
                    303:  * ipi_unicast: send an IPI to a single CPU.
                    304:  *
                    305:  * => The CPU must be remote; must not be local.
                    306:  * => The caller must ipi_wait() on the message for completion.
                    307:  */
                    308: void
                    309: ipi_unicast(ipi_msg_t *msg, struct cpu_info *ci)
                    310: {
                    311:        const cpuid_t id = cpu_index(ci);
                    312:
                    313:        KASSERT(msg->func != NULL);
                    314:        KASSERT(kpreempt_disabled());
                    315:        KASSERT(curcpu() != ci);
                    316:
                    317:        msg->_pending = 1;
                    318:        membar_producer();
                    319:
                    320:        put_msg(&ipi_mboxes[id], msg);
1.2       rmind     321:        ipi_trigger(IPI_SYNCH_ID, ci);
1.1       rmind     322: }
                    323:
                    324: /*
                    325:  * ipi_multicast: send an IPI to each CPU in the specified set.
                    326:  *
                    327:  * => The caller must ipi_wait() on the message for completion.
                    328:  */
                    329: void
                    330: ipi_multicast(ipi_msg_t *msg, const kcpuset_t *target)
                    331: {
                    332:        const struct cpu_info * const self = curcpu();
                    333:        CPU_INFO_ITERATOR cii;
                    334:        struct cpu_info *ci;
                    335:        u_int local;
                    336:
                    337:        KASSERT(msg->func != NULL);
                    338:        KASSERT(kpreempt_disabled());
                    339:
                    340:        local = !!kcpuset_isset(target, cpu_index(self));
                    341:        msg->_pending = kcpuset_countset(target) - local;
                    342:        membar_producer();
                    343:
                    344:        for (CPU_INFO_FOREACH(cii, ci)) {
                    345:                cpuid_t id;
                    346:
                    347:                if (__predict_false(ci == self)) {
                    348:                        continue;
                    349:                }
                    350:                id = cpu_index(ci);
                    351:                if (!kcpuset_isset(target, id)) {
                    352:                        continue;
                    353:                }
                    354:                put_msg(&ipi_mboxes[id], msg);
1.2       rmind     355:                ipi_trigger(IPI_SYNCH_ID, ci);
1.1       rmind     356:        }
                    357:        if (local) {
                    358:                msg->func(msg->arg);
                    359:        }
                    360: }
                    361:
                    362: /*
                    363:  * ipi_broadcast: send an IPI to all CPUs.
                    364:  *
                    365:  * => The caller must ipi_wait() on the message for completion.
                    366:  */
                    367: void
                    368: ipi_broadcast(ipi_msg_t *msg)
                    369: {
                    370:        const struct cpu_info * const self = curcpu();
                    371:        CPU_INFO_ITERATOR cii;
                    372:        struct cpu_info *ci;
                    373:
                    374:        KASSERT(msg->func != NULL);
                    375:        KASSERT(kpreempt_disabled());
                    376:
                    377:        msg->_pending = ncpu - 1;
                    378:        membar_producer();
                    379:
                    380:        /* Broadcast IPIs for remote CPUs. */
                    381:        for (CPU_INFO_FOREACH(cii, ci)) {
                    382:                cpuid_t id;
                    383:
                    384:                if (__predict_false(ci == self)) {
                    385:                        continue;
                    386:                }
                    387:                id = cpu_index(ci);
                    388:                put_msg(&ipi_mboxes[id], msg);
1.2       rmind     389:                ipi_trigger(IPI_SYNCH_ID, ci);
1.1       rmind     390:        }
                    391:
                    392:        /* Finally, execute locally. */
                    393:        msg->func(msg->arg);
                    394: }
                    395:
                    396: /*
                    397:  * ipi_wait: spin-wait until the message is processed.
                    398:  */
                    399: void
                    400: ipi_wait(ipi_msg_t *msg)
                    401: {
                    402:        int count = SPINLOCK_BACKOFF_MIN;
                    403:
                    404:        while (msg->_pending) {
                    405:                KASSERT(msg->_pending < ncpu);
                    406:                SPINLOCK_BACKOFF(count);
                    407:        }
                    408: }

CVSweb <webmaster@jp.NetBSD.org>