Running "ifconfig ral0 debug down up" can leave slow systems, such
 as edd@'s soekris, with an unusable wireless interface until reboot.

The net80211 layer will run a scan when the interface comes up.
The scan hops from channel to channel every 200msec. This hopping is
controlled via a timeout that runs at IPL_SOFTCLOCK. When the scan
reaches the last channel it terminates.

The problem with soekris is that, in noisy environments, they take so
much time printing debug messsages about received frames from
ieee80211_input() to the serial console that another RX interrupt
will run next at IPL_NET. This prevents the scan from running its 200msc
timeout handler at IPL_SOFTCLOCK, and the scan never finishes.

With the diff below, we print the message from a work queue at IPL_TTY
instead (idea from guenther@). This allows the soekris to finish the scan.
The box is still hardly responsive during the scan but at least concurrent
SSH sessions remain somewhat responsive and eventually the soekris recovers
completely.

Index: ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.119
diff -u -p -r1.119 ieee80211_input.c
--- ieee80211_input.c   5 Apr 2011 11:48:28 -0000       1.119
+++ ieee80211_input.c   12 Jul 2012 15:42:24 -0000
@@ -42,6 +42,7 @@
 #include <sys/errno.h>
 #include <sys/proc.h>
 #include <sys/sysctl.h>
+#include <sys/workq.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -131,6 +132,9 @@ void        ieee80211_recv_bar(struct ieee80211
 void   ieee80211_bar_tid(struct ieee80211com *, struct ieee80211_node *,
            u_int8_t, u_int16_t);
 #endif
+void   ieee80211_input_print_frame(struct ieee80211com *,  struct ifnet *,
+           struct ieee80211_frame *, struct ieee80211_rxinfo *);
+void   ieee80211_input_print_frame_task(void *, void *);
 
 /*
  * Retrieve the length in bytes of an 802.11 header.
@@ -152,6 +156,71 @@ ieee80211_get_hdrlen(const struct ieee80
        return size;
 }
 
+/* Work queue task that prints a received frame.  Avoids printf() from
+ * interrupt context at IPL_NET making slow machines unusable when many
+ * rames are received and the interface is put in debug mode. */
+void
+ieee80211_input_print_frame_task(void *arg1, void *arg2)
+{
+       char *msg = arg1;
+
+       printf(msg);
+       free(msg, M_DEVBUF);
+}
+
+void
+ieee80211_input_print_frame(struct ieee80211com *ic,  struct ifnet *ifp,
+    struct ieee80211_frame *wh, struct ieee80211_rxinfo *rxi)
+{
+       int doprint, error;
+       char *msg;
+       u_int8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+       /* avoid to print too many frames */
+       doprint = 0;
+       switch (subtype) {
+       case IEEE80211_FC0_SUBTYPE_BEACON:
+               if (ic->ic_state == IEEE80211_S_SCAN)
+                       doprint = 1;
+               break;
+#ifndef IEEE80211_STA_ONLY
+       case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+               if (ic->ic_opmode == IEEE80211_M_IBSS)
+                       doprint = 1;
+               break;
+#endif
+       default:
+               doprint = 1;
+               break;
+       }
+#ifdef IEEE80211_DEBUG
+       doprint += ieee80211_debug;
+#endif
+       if (!doprint)
+               return;
+
+       msg = malloc(1024, M_DEVBUF, M_NOWAIT);
+       if (msg == NULL)
+               return;
+
+       snprintf(msg, 1024, "%s: received %s from %s rssi %d mode %s\n",
+           ifp->if_xname,
+           ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
+           ether_sprintf(wh->i_addr2), rxi->rxi_rssi,
+           ieee80211_phymode_name[ieee80211_chan2mode(
+               ic, ic->ic_bss->ni_chan)]);
+
+       if (ic->ic_print_workq) {
+               error = workq_add_task(ic->ic_print_workq, 0,
+                   ieee80211_input_print_frame_task, msg, NULL);
+               if (error)
+                       free(msg, M_DEVBUF);
+       } else {
+               printf(msg);
+               free(msg, M_DEVBUF);
+       }
+}
+
 /*
  * Process a received frame.  The node associated with the sender
  * should be supplied.  If nothing was found in the node table then
@@ -467,37 +536,8 @@ ieee80211_input(struct ifnet *ifp, struc
                        goto out;
                }
 
-               if (ifp->if_flags & IFF_DEBUG) {
-                       /* avoid to print too many frames */
-                       int doprint = 0;
-
-                       switch (subtype) {
-                       case IEEE80211_FC0_SUBTYPE_BEACON:
-                               if (ic->ic_state == IEEE80211_S_SCAN)
-                                       doprint = 1;
-                               break;
-#ifndef IEEE80211_STA_ONLY
-                       case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
-                               if (ic->ic_opmode == IEEE80211_M_IBSS)
-                                       doprint = 1;
-                               break;
-#endif
-                       default:
-                               doprint = 1;
-                               break;
-                       }
-#ifdef IEEE80211_DEBUG
-                       doprint += ieee80211_debug;
-#endif
-                       if (doprint)
-                               printf("%s: received %s from %s rssi %d mode 
%s\n",
-                                   ifp->if_xname,
-                                   ieee80211_mgt_subtype_name[subtype
-                                   >> IEEE80211_FC0_SUBTYPE_SHIFT],
-                                   ether_sprintf(wh->i_addr2), rxi->rxi_rssi,
-                                   
ieee80211_phymode_name[ieee80211_chan2mode(ic,
-                                   ic->ic_bss->ni_chan)]);
-               }
+               if (ifp->if_flags & IFF_DEBUG)
+                       ieee80211_input_print_frame(ic, ifp, wh, rxi);
 #if NBPFILTER > 0
                if (ic->ic_rawbpf)
                        bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN);
Index: ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.68
diff -u -p -r1.68 ieee80211_node.c
--- ieee80211_node.c    25 Jan 2012 17:04:02 -0000      1.68
+++ ieee80211_node.c    12 Jul 2012 15:17:18 -0000
@@ -45,6 +45,7 @@
 #include <sys/proc.h>
 #include <sys/sysctl.h>
 #include <sys/tree.h>
+#include <sys/workq.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -153,6 +154,7 @@ ieee80211_node_attach(struct ifnet *ifp)
                ic->ic_max_aid = IEEE80211_AID_DEF;
        else if (ic->ic_max_aid > IEEE80211_AID_MAX)
                ic->ic_max_aid = IEEE80211_AID_MAX;
+       ic->ic_print_workq = workq_create(ifp->if_xname, 1, IPL_TTY);
 #ifndef IEEE80211_STA_ONLY
        size = howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t);
        ic->ic_aid_bitmap = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
@@ -227,6 +229,10 @@ ieee80211_node_detach(struct ifnet *ifp)
        timeout_del(&ic->ic_node_cache_timeout);
 #endif
        timeout_del(&ic->ic_rsn_timeout);
+       if (ic->ic_print_workq) {
+               workq_destroy(ic->ic_print_workq);
+               ic->ic_print_workq = NULL;
+       }
 }
 
 /*
Index: ieee80211_var.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.62
diff -u -p -r1.62 ieee80211_var.h
--- ieee80211_var.h     21 Jan 2012 19:42:16 -0000      1.62
+++ ieee80211_var.h     12 Jul 2012 15:12:53 -0000
@@ -320,6 +320,8 @@ struct ieee80211com {
        u_int8_t                ic_sup_mcs[16];
        u_int8_t                ic_dialog_token;
 
+       struct workq            *ic_print_workq;
+
        LIST_HEAD(, ieee80211_vap) ic_vaps;
 };
 #define        ic_if           ic_ac.ac_if

Reply via email to