DDE runs each Linux "cpu" as CPU 0 in separate threads and its
local_irq_save() only disables interrupts on the current thread, so the
softirq pending word (irq_stat[0].__softirq_pending) is shared without
real mutual exclusion.  __raise_softirq_irqoff() expands to a plain
read-modify-write (local_softirq_pending() |= bit) and __do_softirq()
clears the word with a plain store, so a raise can be lost:

  - the IRQ thread raises NET_RX from __napi_schedule();
  - the networking threads raise NET_TX via raise_softirq_irqoff();
  - the softirq thread reads the word and then clears it.

A NET_RX raise that lands between the softirq thread's read and its
clear (or between two plain |= operations) is overwritten.  The NAPI
entry stays on the poll list but no softirq is pending, so the softirq
thread blocks on dde_softirq_sem and the network stack stops.

Make the raise and the read-and-clear atomic: dde_or_softirq_pending()
uses __sync_fetch_and_or() and dde_softirq_pending_xchg() uses
__sync_lock_test_and_set(..., 0); __raise_softirq_irqoff() and
__do_softirq() use them.

* libdde_linux26/contrib/include/linux/interrupt.h: declare the helpers
and use dde_or_softirq_pending() in __raise_softirq_irqoff().
* libdde_linux26/lib/src/arch/l4/softirq.c (__do_softirq): read and
clear the pending word atomically.
---
 libdde_linux26/contrib/include/linux/interrupt.h |  7 ++++++-
 libdde_linux26/lib/src/arch/l4/softirq.c         | 15 +++++++++++----
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/libdde_linux26/contrib/include/linux/interrupt.h 
b/libdde_linux26/contrib/include/linux/interrupt.h
index 9127f6b..2a75eb0 100644
--- a/libdde_linux26/contrib/include/linux/interrupt.h
+++ b/libdde_linux26/contrib/include/linux/interrupt.h
@@ -227,6 +227,11 @@ static inline int disable_irq_wake(unsigned int irq)
 #define or_softirq_pending(x)  (local_softirq_pending() |= (x))
 #endif
 
+#ifdef DDE_LINUX
+extern void dde_or_softirq_pending(unsigned long mask);
+extern unsigned long dde_softirq_pending_xchg(unsigned long val);
+#endif
+
 /* Some architectures might implement lazy enabling/disabling of
  * interrupts. In some cases, such as stop_machine, we might want
  * to ensure that after a local_irq_disable(), interrupts have
@@ -271,7 +276,7 @@ asmlinkage void do_softirq(void);
 asmlinkage void __do_softirq(void);
 extern void open_softirq(int nr, void (*action)(struct softirq_action *));
 extern void softirq_init(void);
-#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } 
while (0)
+#define __raise_softirq_irqoff(nr) do { dde_or_softirq_pending(1UL << (nr)); } 
while (0)
 extern void raise_softirq_irqoff(unsigned int nr);
 extern void raise_softirq(unsigned int nr);
 
diff --git a/libdde_linux26/lib/src/arch/l4/softirq.c 
b/libdde_linux26/lib/src/arch/l4/softirq.c
index 2efa24e..d53f690 100644
--- a/libdde_linux26/lib/src/arch/l4/softirq.c
+++ b/libdde_linux26/lib/src/arch/l4/softirq.c
@@ -11,6 +11,16 @@ DECLARE_INITVAR(dde26_softirq);
 ddekit_thread_t *dde_softirq_thread;
 ddekit_sem_t   *dde_softirq_sem;
 
+void dde_or_softirq_pending(unsigned long mask)
+{
+       __sync_fetch_and_or(&local_softirq_pending(), mask);
+}
+
+unsigned long dde_softirq_pending_xchg(unsigned long val)
+{
+       return __sync_lock_test_and_set(&local_softirq_pending(), val);
+}
+
 /* struct tasklet_head is not defined in a header in Linux 2.6 */
 struct tasklet_head
 {
@@ -197,10 +207,7 @@ void __do_softirq(void)
        int retries = MAX_SOFTIRQ_RETRIES;
        do {
                struct softirq_action *h = softirq_vec;
-               unsigned long pending = local_softirq_pending();
-
-               /* reset softirq count */
-               set_softirq_pending(0);
+               unsigned long pending = dde_softirq_pending_xchg(0);
 
                /* While we have a softirq pending... */
                while (pending) {
-- 
2.47.3


Reply via email to