Since interrupt handlers are running asynchronously it is difficult to
make sure that one isn't running on another cpu while you're frobbing
data structures that you share with it.  This is an issue for example
in our network drivers, where we want to clean up the rings when we
bring the interface down, but where the interrupt handler might still
be processing incoming packets.  Currently we rely on the kernel lock
to prevent this from happening.

The diff below adds an interface (called intr_sync()) to help solving
this problem.  The idea is that after you've called intr_sync() you
can be sure that an interrupt handler that was running before the
intr_sync() call will have finished when intr_sync() returns.  This
means that if your interrupt handler checks the IFF_RUNNING flag
before touhcing the rings, the function that brings down the interface
can:

1. clear the IFF_RUNNING flag
2. call intr_sync()
3. free the rings

The intr_sync() function does its job by scheduling a task and make
sure that it runs on the same CPU as the interrupt handler.  dlg@
tells me this is what Linux does.  The current implementation "knows"
that all interrupt handlers run on the primary CPU.  But this is
something that may change in the near future (I have a working diff
for sparc64 to do this).  That's why intr_sync() has a void *
argument.  The idea is that you pass the "cookie" you got back when
establishing the interrupt handler.  The MD code should be able to
figure out the CPU the handler istied to from that.

Unfortunately this means we'll need to implement this function for all
our architectures before we can use it.  But I can probably split of
the guts of it into a common function.

Does this make sense?  I'd like to get all the bikeshedding out of the
way before I go on and implement this for all our architectures.


Index: sparc64/intr.c
===================================================================
RCS file: /cvs/src/sys/arch/sparc64/sparc64/intr.c,v
retrieving revision 1.52
diff -u -p -r1.52 intr.c
--- sparc64/intr.c      5 Oct 2014 11:40:37 -0000       1.52
+++ sparc64/intr.c      12 Sep 2015 12:36:44 -0000
@@ -45,6 +45,8 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/task.h>
 
 #include <dev/cons.h>
 
@@ -310,6 +312,41 @@ intr_establish(int level, struct intrhan
 #endif
 
        splx(s);
+}
+
+void
+intr_sync_task(void *arg)
+{
+       CPU_INFO_ITERATOR cii;
+       struct cpu_info *ci;
+       volatile int *done = arg;
+
+       CPU_INFO_FOREACH(cii, ci) {
+               if (CPU_IS_PRIMARY(ci)) {
+                       sched_peg_curproc(ci);
+                       break;
+               }
+       }
+
+       *done = 1;
+       wakeup(arg);
+
+       atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
+}
+
+void
+intr_sync(void *ih)
+{
+       struct sleep_state sls;
+       struct task task;
+       volatile int done = 0;
+
+       task_set(&task, intr_sync_task, (void *)&done);
+       task_add(systq, &task);
+       while (!done) {
+               sleep_setup(&sls, &done, PWAIT, "isync");
+               sleep_finish(&sls, !done);
+       }
 }
 
 void *
Index: include/intr.h
===================================================================
RCS file: /cvs/src/sys/arch/sparc64/include/intr.h,v
retrieving revision 1.16
diff -u -p -r1.16 intr.h
--- include/intr.h      17 May 2013 18:26:37 -0000      1.16
+++ include/intr.h      12 Sep 2015 12:36:44 -0000
@@ -84,6 +84,8 @@ void    intr_establish(int, struct intrh
 
 #define        IPL_MPSAFE      0x100
 
+void    intr_sync(void *);
+
 void   *softintr_establish(int, void (*)(void *), void *);
 void    softintr_disestablish(void *);
 void    softintr_schedule(void *);
Index: dev/vnet.c
===================================================================
RCS file: /cvs/src/sys/arch/sparc64/dev/vnet.c,v
retrieving revision 1.46
diff -u -p -r1.46 vnet.c
--- dev/vnet.c  24 Jun 2015 09:40:53 -0000      1.46
+++ dev/vnet.c  12 Sep 2015 12:36:45 -0000
@@ -1436,6 +1436,9 @@ vnet_stop(struct ifnet *ifp)
        cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
        cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
 
+       intr_sync(sc->sc_tx_ih);
+       intr_sync(sc->sc_rx_ih);
+
        hv_ldc_tx_qconf(lc->lc_id, 0, 0);
        hv_ldc_rx_qconf(lc->lc_id, 0, 0);
        lc->lc_tx_seqid = 0;

Reply via email to