kernel - All lwkt thread now start out mpsafe part 1/2
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 29 Aug 2010 00:18:34 +0000 (17:18 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 29 Aug 2010 00:18:34 +0000 (17:18 -0700)
* All callers of lwkt_init_thread(), lwkt_create() and lwkt_alloc_thread()
  now always pass TDF_MPSAFE and the flag is asserted in the low level
  thread creation code.

15 files changed:
sys/dev/netif/iwl/if_iwl.c
sys/kern/init_main.c
sys/kern/kern_intr.c
sys/kern/kern_nrandom.c
sys/kern/kern_timeout.c
sys/kern/lwkt_thread.c
sys/kern/subr_bus.c
sys/kern/subr_disk.c
sys/kern/usched_bsd4.c
sys/kern/usched_dummy.c
sys/kern/vfs_journal.c
sys/net/route.c
sys/vfs/devfs/devfs_core.c
sys/vfs/nfs/nfs_iod.c
sys/vfs/nfs/nfs_vfsops.c

index 7049c7f..e38bd3c 100644 (file)
@@ -479,6 +479,7 @@ iwl_service_loop(void *arg)
        struct ifnet *ifp = &iwl->iwl_ic.ic_if;
        struct netmsg *nmsg;
 
+       get_mplock();
        lwkt_serialize_enter(ifp->if_serializer);
        while ((nmsg = lwkt_waitport(&iwl->iwl_thread_port, 0))) {
                nmsg->nm_dispatch(nmsg);
@@ -486,8 +487,7 @@ iwl_service_loop(void *arg)
                        break;
        }
        lwkt_serialize_exit(ifp->if_serializer);
-
-       lwkt_exit();
+       rel_mplock();
 }
 
 void
@@ -503,7 +503,7 @@ iwl_create_thread(struct iwlcom *iwl, int unit)
        iwl->iwl_thread_port.mp_putport = iwl_put_port;
 
        lwkt_create(iwl_service_loop, iwl, NULL, &iwl->iwl_thread,
-                   0, unit % ncpus, "iwl%d", unit);
+                   TDF_MPSAFE, unit % ncpus, "iwl%d", unit);
 }
 
 static void
index fe22b97..d562436 100644 (file)
@@ -161,8 +161,12 @@ sysinit_add(struct sysinit **set, struct sysinit **set_end)
 void
 mi_proc0init(struct globaldata *gd, struct user *proc0paddr)
 {
-       lwkt_init_thread(&thread0, proc0paddr, LWKT_THREAD_STACK, 0, gd);
+       lwkt_init_thread(&thread0, proc0paddr, LWKT_THREAD_STACK,
+                        TDF_MPSAFE, gd);
        lwkt_set_comm(&thread0, "thread0");
+#ifdef SMP
+       thread0.td_mpcount = 1; /* will hold mplock initially */
+#endif
        RB_INIT(&proc0.p_lwp_tree);
        spin_init(&proc0.p_spin);
        proc0.p_lasttid = 0;    /* +1 = next TID */
index c0a46dd..f4efa9d 100644 (file)
@@ -88,9 +88,6 @@ static void ithread_emergency(void *arg);
 static void report_stray_interrupt(int intr, struct intr_info *info);
 static void int_moveto_destcpu(int *, int *, int);
 static void int_moveto_origcpu(int, int);
-#ifdef SMP
-static void intr_get_mplock(void);
-#endif
 
 int intr_info_size = sizeof(intr_info_ary) / sizeof(intr_info_ary[0]);
 
@@ -101,18 +98,6 @@ static struct thread emergency_intr_thread;
 #define ISTATE_NORMAL          1
 #define ISTATE_LIVELOCKED      2
 
-#ifdef SMP
-static int intr_mpsafe = 1;
-static int intr_migrate = 0;
-static int intr_migrate_count;
-TUNABLE_INT("kern.intr_mpsafe", &intr_mpsafe);
-SYSCTL_INT(_kern, OID_AUTO, intr_mpsafe,
-        CTLFLAG_RW, &intr_mpsafe, 0, "Run INTR_MPSAFE handlers without the BGL");
-SYSCTL_INT(_kern, OID_AUTO, intr_migrate,
-        CTLFLAG_RW, &intr_migrate, 0, "Migrate to cpu holding BGL");
-SYSCTL_INT(_kern, OID_AUTO, intr_migrate_count,
-        CTLFLAG_RW, &intr_migrate_count, 0, "");
-#endif
 static int livelock_limit = 40000;
 static int livelock_lowater = 20000;
 static int livelock_debug = -1;
@@ -236,8 +221,9 @@ register_int(int intr, inthand2_t *handler, void *arg, const char *name,
      */
     if (emergency_intr_thread.td_kstack == NULL) {
        lwkt_create(ithread_emergency, NULL, NULL,
-                   &emergency_intr_thread, TDF_STOPREQ|TDF_INTTHREAD, -1,
-                   "ithread emerg");
+                   &emergency_intr_thread,
+                   TDF_STOPREQ|TDF_INTTHREAD|TDF_MPSAFE,
+                   -1, "ithread emerg");
        systimer_init_periodic_nq(&emergency_intr_timer,
                    emergency_intr_timer_callback, &emergency_intr_thread, 
                    (emergency_intr_enable ? emergency_intr_freq : 1));
@@ -769,15 +755,10 @@ ithread_handler(void *arg)
         * always operate with the BGL.
         */
 #ifdef SMP
-       if (intr_mpsafe == 0) {
-           if (mpheld == 0) {
-               intr_get_mplock();
-               mpheld = 1;
-           }
-       } else if (info->i_mplock_required != mpheld) {
+       if (info->i_mplock_required != mpheld) {
            if (info->i_mplock_required) {
                KKASSERT(mpheld == 0);
-               intr_get_mplock();
+               get_mplock();
                mpheld = 1;
            } else {
                KKASSERT(mpheld != 0);
@@ -785,11 +766,6 @@ ithread_handler(void *arg)
                mpheld = 0;
            }
        }
-
-       /*
-        * scheduled cpu may have changed, see intr_get_mplock()
-        */
-       gd = mycpu;
 #endif
 
        /*
@@ -866,12 +842,6 @@ ithread_handler(void *arg)
             */
            if (ill_count <= livelock_limit) {
                if (info->i_running == 0) {
-#ifdef SMP
-                   if (mpheld && intr_migrate) {
-                       rel_mplock();
-                       mpheld = 0;
-                   }
-#endif
                    lwkt_deschedule_self(gd->gd_curthread);
                    lwkt_switch();
                }
@@ -925,34 +895,6 @@ ithread_handler(void *arg)
     /* not reached */
 }
 
-#ifdef SMP
-
-/*
- * An interrupt thread is trying to get the MP lock.  To avoid cpu-bound
- * code in the kernel on cpu X from interfering we chase the MP lock.
- */
-static void
-intr_get_mplock(void)
-{
-    int owner;
-
-    if (intr_migrate == 0) {
-       get_mplock();
-       return;
-    }
-    while (try_mplock() == 0) {
-       owner = owner_mplock();
-       if (owner >= 0 && owner != mycpu->gd_cpuid) {
-               lwkt_migratecpu(owner);
-               ++intr_migrate_count;
-       } else {
-               lwkt_switch();
-       }
-    }
-}
-
-#endif
-
 /*
  * Emergency interrupt polling thread.  The thread begins execution
  * outside a critical section with the BGL held.
@@ -975,6 +917,8 @@ ithread_emergency(void *arg __unused)
     intrec_t rec, nrec;
     int intr;
 
+    get_mplock();
+
     for (;;) {
        for (intr = 0; intr < max_installed_hard_intr; ++intr) {
            info = &intr_info_ary[intr];
index 5ad67d6..2a140bb 100644 (file)
 
 #include <sys/thread2.h>
 #include <sys/spinlock2.h>
+#include <sys/mplock2.h>
 
 /*
  * Portability note: The u_char/unsigned char type is used where
@@ -568,6 +569,8 @@ read_random_unlimited(void *buf, u_int nbytes)
 /*
  * Random number generator helper thread.  This limits code overhead from
  * high frequency events by delaying the clearing of rand_thread_signal.
+ *
+ * MPSAFE thread
  */
 static
 void
@@ -575,10 +578,12 @@ rand_thread_loop(void *dummy)
 {
        int count;
 
+       get_mplock();
+
        for (;;) {
                NANOUP_EVENT ();
                spin_lock_wr(&rand_spin);
-               count = (int)(L15_Byte() * hz / (256 * 10) + hz / 10);
+               count = (int)(L15_Byte() * hz / (256 * 10) + hz / 10 + 1);
                spin_unlock_wr(&rand_spin);
                tsleep(rand_td, 0, "rwait", count);
                crit_enter();
@@ -594,7 +599,8 @@ static
 void
 rand_thread_init(void)
 {
-       lwkt_create(rand_thread_loop, NULL, &rand_td, NULL, 0, 0, "random");
+       lwkt_create(rand_thread_loop, NULL, &rand_td, NULL,
+                   TDF_MPSAFE, 0, "random");
 }
 
 SYSINIT(rand, SI_SUB_HELPER_THREADS, SI_ORDER_ANY, rand_thread_init, 0);
index 1639197..f27f209 100644 (file)
@@ -177,8 +177,8 @@ swi_softclock_setup(void *arg)
                 * the cpu they were scheduled on.
                 */
                lwkt_create(softclock_handler, sc, NULL,
-                           &sc->thread, TDF_STOPREQ|TDF_INTTHREAD, cpu,
-                           "softclock %d", cpu);
+                           &sc->thread, TDF_STOPREQ|TDF_INTTHREAD|TDF_MPSAFE,
+                           cpu, "softclock %d", cpu);
        }
 }
 
@@ -237,9 +237,9 @@ hardclock_softtick(globaldata_t gd)
  * a critical section is sufficient to interlock sc->curticks and protect
  * us from remote IPI's / list removal.
  *
- * The thread starts with the MP lock held and not in a critical section.
- * The loop itself is MP safe while individual callbacks may or may not
- * be, so we obtain or release the MP lock as appropriate.
+ * The thread starts with the MP lock released and not in a critical
+ * section.  The loop itself is MP safe while individual callbacks
+ * may or may not be, so we obtain or release the MP lock as appropriate.
  */
 static void
 softclock_handler(void *arg)
@@ -250,7 +250,7 @@ softclock_handler(void *arg)
        void (*c_func)(void *);
        void *c_arg;
 #ifdef SMP
-       int mpsafe = 0;
+       int mpsafe = 1;
 #endif
 
        lwkt_setpri_self(TDPRI_SOFT_NORM);
index 346ffb0..1311f37 100644 (file)
@@ -374,6 +374,9 @@ lwkt_init_thread(thread_t td, void *stack, int stksize, int flags,
 {
     globaldata_t mygd = mycpu;
 
+    /* all threads start mpsafe now */
+    KKASSERT(flags & TDF_MPSAFE);
+
     bzero(td, sizeof(struct thread));
     td->td_kstack = stack;
     td->td_kstack_size = stksize;
@@ -1570,9 +1573,8 @@ lwkt_preempted_proc(void)
  * rel_mplock() at the start of the new thread.
  */
 int
-lwkt_create(void (*func)(void *), void *arg,
-    struct thread **tdp, thread_t template, int tdflags, int cpu,
-    const char *fmt, ...)
+lwkt_create(void (*func)(void *), void *arg, struct thread **tdp,
+           thread_t template, int tdflags, int cpu, const char *fmt, ...)
 {
     thread_t td;
     __va_list ap;
index 26972e6..312c29c 100644 (file)
@@ -1665,8 +1665,7 @@ device_probe_and_attach(device_t dev)
 
 /*
  * Device is known to be alive, do the attach asynchronously.
- *
- * The MP lock is held by all threads.
+ * However, serialize the attaches with the mp lock.
  */
 static void
 device_attach_async(device_t dev)
@@ -1675,7 +1674,8 @@ device_attach_async(device_t dev)
 
        atomic_add_int(&numasyncthreads, 1);
        lwkt_create(device_attach_thread, dev, &td, NULL,
-                   0, 0, (dev->desc ? dev->desc : "devattach"));
+                   TDF_MPSAFE, 0,
+                   (dev->desc ? dev->desc : "devattach"));
 }
 
 static void
@@ -1683,9 +1683,11 @@ device_attach_thread(void *arg)
 {
        device_t dev = arg;
 
+       get_mplock();   /* XXX replace with devattach_token later */
        (void)device_doattach(dev);
        atomic_subtract_int(&numasyncthreads, 1);
        wakeup(&numasyncthreads);
+       rel_mplock();   /* XXX replace with devattach_token later */
 }
 
 /*
index 81f3a67..b9e84e1 100644 (file)
@@ -375,8 +375,12 @@ disk_msg_core(void *arg)
        disk_msg_t msg;
        int run;
 
+       lwkt_gettoken(&disklist_token);
        lwkt_initport_thread(&disk_msg_port, curthread);
-       wakeup(curthread);
+       wakeup(curthread);      /* synchronous startup */
+       lwkt_reltoken(&disklist_token);
+
+       get_mplock();   /* not mpsafe yet? */
        run = 1;
 
        while (run) {
@@ -1275,10 +1279,11 @@ disk_init(void)
         */
        lwkt_initport_replyonly(&disk_dispose_port, disk_msg_autofree_reply);
 
+       lwkt_gettoken(&disklist_token);
        lwkt_create(disk_msg_core, /*args*/NULL, &td_core, NULL,
-                   0, 0, "disk_msg_core");
-
+                   TDF_MPSAFE, 0, "disk_msg_core");
        tsleep(td_core, 0, "diskcore", 0);
+       lwkt_reltoken(&disklist_token);
 }
 
 static void
index ede271e..8c208c2 100644 (file)
@@ -1070,10 +1070,13 @@ bsd4_setrunqueue_locked(struct lwp *lp)
 /*
  * For SMP systems a user scheduler helper thread is created for each
  * cpu and is used to allow one cpu to wakeup another for the purposes of
- * scheduling userland threads from setrunqueue().  UP systems do not
- * need the helper since there is only one cpu.  We can't use the idle
- * thread for this because we need to hold the MP lock.  Additionally,
- * doing things this way allows us to HLT idle cpus on MP systems.
+ * scheduling userland threads from setrunqueue().
+ *
+ * UP systems do not need the helper since there is only one cpu.
+ *
+ * We can't use the idle thread for this because we might block.
+ * Additionally, doing things this way allows us to HLT idle cpus
+ * on MP systems.
  *
  * MPSAFE
  */
@@ -1096,11 +1099,9 @@ sched_thread(void *dummy)
     dd = &bsd4_pcpu[cpuid];
 
     /*
-     * The scheduler thread does not need to hold the MP lock.  Since we
-     * are woken up only when no user processes are scheduled on a cpu, we
-     * can run at an ultra low priority.
+     * Since we are woken up only when no user processes are scheduled
+     * on a cpu, we can run at an ultra low priority.
      */
-    rel_mplock();
     lwkt_setpri_self(TDPRI_USER_SCHEDULER);
 
     for (;;) {
@@ -1194,7 +1195,7 @@ sched_thread_cpu_init(void)
            kprintf(" %d", i);
 
        lwkt_create(sched_thread, NULL, NULL, &dd->helper_thread, 
-                   TDF_STOPREQ, i, "usched %d", i);
+                   TDF_STOPREQ | TDF_MPSAFE, i, "usched %d", i);
 
        /*
         * Allow user scheduling on the target cpu.  cpu #0 has already
index aff357e..fb22eb8 100644 (file)
@@ -455,6 +455,8 @@ dummy_exiting(struct lwp *plp, struct lwp *lp)
  * is possible to deschedule an LWKT thread and then do some work before
  * switching away.  The thread can be rescheduled at any time, even before
  * we switch away.
+ *
+ * MPSAFE
  */
 #ifdef SMP
 
@@ -474,11 +476,6 @@ dummy_sched_thread(void *dummy)
     dd = &dummy_pcpu[cpuid];
     cpumask = 1 << cpuid;
 
-    /*
-     * Our Scheduler helper thread does not need to hold the MP lock
-     */
-    rel_mplock();
-
     for (;;) {
        lwkt_deschedule_self(gd->gd_curthread);         /* interlock */
        atomic_set_int(&dummy_rdyprocmask, cpumask);
@@ -540,7 +537,7 @@ dummy_sched_thread_cpu_init(void)
            kprintf(" %d", i);
 
        lwkt_create(dummy_sched_thread, NULL, NULL, &dd->helper_thread, 
-                   TDF_STOPREQ, i, "dsched %d", i);
+                   TDF_STOPREQ | TDF_MPSAFE, i, "dsched %d", i);
 
        /*
         * Allow user scheduling on the target cpu.  cpu #0 has already
index 0845f57..f13c184 100644 (file)
@@ -90,6 +90,7 @@
 
 #include <sys/file2.h>
 #include <sys/thread2.h>
+#include <sys/mplock2.h>
 #include <sys/spinlock2.h>
 
 static void journal_wthread(void *info);
@@ -119,14 +120,16 @@ journal_create_threads(struct journal *jo)
        jo->flags &= ~(MC_JOURNAL_STOP_REQ | MC_JOURNAL_STOP_IMM);
        jo->flags |= MC_JOURNAL_WACTIVE;
        lwkt_create(journal_wthread, jo, NULL, &jo->wthread,
-                       TDF_STOPREQ, -1, "journal w:%.*s", JIDMAX, jo->id);
+                   TDF_STOPREQ | TDF_MPSAFE,
+                   -1, "journal w:%.*s", JIDMAX, jo->id);
        lwkt_setpri(&jo->wthread, TDPRI_KERN_DAEMON);
        lwkt_schedule(&jo->wthread);
 
        if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX) {
            jo->flags |= MC_JOURNAL_RACTIVE;
            lwkt_create(journal_rthread, jo, NULL, &jo->rthread,
-                       TDF_STOPREQ, -1, "journal r:%.*s", JIDMAX, jo->id);
+                       TDF_STOPREQ | TDF_MPSAFE,
+                       -1, "journal r:%.*s", JIDMAX, jo->id);
            lwkt_setpri(&jo->rthread, TDPRI_KERN_DAEMON);
            lwkt_schedule(&jo->rthread);
        }
@@ -172,6 +175,9 @@ journal_wthread(void *info)
     size_t bytes;
     size_t res;
 
+    /* not MPSAFE yet */
+    get_mplock();
+
     for (;;) {
        /*
         * Calculate the number of bytes available to write.  This buffer
@@ -288,6 +294,7 @@ journal_wthread(void *info)
     jo->flags &= ~MC_JOURNAL_WACTIVE;
     wakeup(jo);
     wakeup(&jo->fifo.windex);
+    rel_mplock();
 }
 
 /*
@@ -308,6 +315,9 @@ journal_rthread(void *info)
     transid = 0;
     error = 0;
 
+    /* not MPSAFE yet */
+    get_mplock();
+
     for (;;) {
        /*
         * We have been asked to stop
@@ -403,6 +413,7 @@ journal_rthread(void *info)
     jo->flags &= ~MC_JOURNAL_RACTIVE;
     wakeup(jo);
     wakeup(&jo->fifo.windex);
+    rel_mplock();
 }
 
 /*
index 712973e..c7d8b5e 100644 (file)
@@ -90,6 +90,7 @@
 
 #include <sys/thread2.h>
 #include <sys/msgport2.h>
+#include <sys/mplock2.h>
 #include <net/netmsg2.h>
 
 #ifdef MPLS
@@ -153,7 +154,7 @@ route_init(void)
 
        for (cpu = 0; cpu < ncpus; cpu++) {
                lwkt_create(rtable_service_loop, NULL, &rtd, NULL,
-                           0, cpu, "rtable_cpu %d", cpu);
+                           TDF_MPSAFE, cpu, "rtable_cpu %d", cpu);
                rt_ports[cpu] = &rtd->td_msgport;
        }
 }
@@ -197,6 +198,8 @@ rtable_service_loop(void *dummy __unused)
        struct netmsg *netmsg;
        thread_t td = curthread;
 
+       get_mplock();   /* XXX is this mpsafe yet? */
+
        while ((netmsg = lwkt_waitport(&td->td_msgport, 0)) != NULL) {
                netmsg->nm_dispatch(netmsg);
        }
index d31ce8c..1db3c47 100644 (file)
 #include <sys/types.h>
 #include <sys/lock.h>
 #include <sys/msgport.h>
-#include <sys/msgport2.h>
-#include <sys/spinlock2.h>
 #include <sys/sysctl.h>
 #include <sys/ucred.h>
 #include <sys/param.h>
-#include <sys/sysref2.h>
 #include <sys/systm.h>
 #include <sys/devfs.h>
 #include <sys/devfs_rules.h>
 #include <sys/hotplug.h>
 #include <sys/udev.h>
 
+#include <sys/msgport2.h>
+#include <sys/spinlock2.h>
+#include <sys/mplock2.h>
+#include <sys/sysref2.h>
+
 MALLOC_DEFINE(M_DEVFS, "devfs", "Device File System (devfs) allocations");
 DEVFS_DECLARE_CLONE_BITMAP(ops_id);
 /*
@@ -1071,9 +1073,14 @@ devfs_msg_core(void *arg)
 {
        devfs_msg_t msg;
 
-       devfs_run = 1;
        lwkt_initport_thread(&devfs_msg_port, curthread);
+
+       lockmgr(&devfs_lock, LK_EXCLUSIVE);
+       devfs_run = 1;
        wakeup(td_core);
+       lockmgr(&devfs_lock, LK_RELEASE);
+
+       get_mplock();   /* mpsafe yet? */
 
        while (devfs_run) {
                msg = (devfs_msg_t)lwkt_waitport(&devfs_msg_port, 0);
@@ -1083,7 +1090,10 @@ devfs_msg_core(void *arg)
                devfs_msg_exec(msg);
                lwkt_replymsg(&msg->hdr, 0);
        }
+
+       rel_mplock();
        wakeup(td_core);
+
        lwkt_exit();
 }
 
@@ -2362,12 +2372,12 @@ devfs_init(void)
        /* Initialize *THE* devfs lock */
        lockinit(&devfs_lock, "devfs_core lock", 0, 0);
 
-
+       lockmgr(&devfs_lock, LK_EXCLUSIVE);
        lwkt_create(devfs_msg_core, /*args*/NULL, &td_core, NULL,
-                   0, 0, "devfs_msg_core");
-
+                   TDF_MPSAFE, 0, "devfs_msg_core");
        while (devfs_run == 0)
-               tsleep(td_core, 0, "devfsc", 0);
+               lksleep(td_core, &devfs_lock, 0, "devfsc", 0);
+       lockmgr(&devfs_lock, LK_RELEASE);
 
        devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_init finished\n");
 }
index b1547b4..33cbd25 100644 (file)
 #include <sys/mutex.h>
 
 #include <sys/signal2.h>
+#include <sys/thread2.h>
 #include <sys/mutex2.h>
+#include <sys/mplock2.h>
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
-#include <sys/thread2.h>
 
 #include "rpcv2.h"
 #include "nfsproto.h"
@@ -71,6 +72,9 @@
 #include "nfsnode.h"
 #include "nfsrtt.h"
 
+/*
+ * nfs service connection reader thread
+ */
 void
 nfssvc_iod_reader(void *arg)
 {
@@ -79,6 +83,8 @@ nfssvc_iod_reader(void *arg)
        struct nfsreq *req;
        int error;
 
+       get_mplock();
+
        if (nmp->nm_rxstate == NFSSVC_INIT)
                nmp->nm_rxstate = NFSSVC_PENDING;
        crit_enter();
@@ -149,6 +155,8 @@ nfssvc_iod_reader(void *arg)
 }
 
 /*
+ * nfs service connection writer thread
+ *
  * The writer sits on the send side of the client's socket and
  * does both the initial processing of BIOs and also transmission
  * and retransmission of nfsreq's.
@@ -165,8 +173,11 @@ nfssvc_iod_writer(void *arg)
        struct vnode *vp;
        nfsm_info_t info;
 
+       get_mplock();
+
        if (nmp->nm_txstate == NFSSVC_INIT)
                nmp->nm_txstate = NFSSVC_PENDING;
+
        crit_enter();
        for (;;) {
                if (nmp->nm_txstate == NFSSVC_WAITING) {
index 88431aa..c291c7a 100644 (file)
@@ -1121,9 +1121,9 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
         * Start the reader and writer threads.
         */
        lwkt_create(nfssvc_iod_reader, nmp, &nmp->nm_rxthread,
-                   NULL, 0, rxcpu, "nfsiod_rx");
+                   NULL, TDF_MPSAFE, rxcpu, "nfsiod_rx");
        lwkt_create(nfssvc_iod_writer, nmp, &nmp->nm_txthread,
-                   NULL, 0, txcpu, "nfsiod_tx");
+                   NULL, TDF_MPSAFE, txcpu, "nfsiod_tx");
 
        return (0);
 bad: