Index: sys/net80211/ieee80211.c =================================================================== --- sys/net80211/ieee80211.c +++ sys/net80211/ieee80211.c @@ -325,6 +325,7 @@ ieee80211_crypto_attach(ic); ieee80211_node_attach(ic); ieee80211_power_attach(ic); + ieee80211_tx_watchdog_attach(ic); ieee80211_proto_attach(ic); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_attach(ic); @@ -376,6 +377,7 @@ ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ ieee80211_proto_detach(ic); + ieee80211_tx_watchdog_detach(ic); ieee80211_crypto_detach(ic); ieee80211_power_detach(ic); ieee80211_node_detach(ic); Index: sys/net80211/ieee80211_freebsd.h =================================================================== --- sys/net80211/ieee80211_freebsd.h +++ sys/net80211/ieee80211_freebsd.h @@ -83,6 +83,23 @@ mtx_assert(IEEE80211_TX_LOCK_OBJ(_ic), MA_NOTOWNED) /* + * Watchdog lock. + */ +typedef struct { + char name[16]; /* e.g. "ath0_wt_lock" */ + struct mtx mtx; +} ieee80211_wt_lock_t; +#define IEEE80211_WT_LOCK_INIT(_ic, _name) do { \ + ieee80211_wt_lock_t *wl = &(_ic)->ic_wtlock; \ + snprintf(wl->name, sizeof(wl->name), "%s_wt_lock", _name); \ + mtx_init(&wl->mtx, wl->name, NULL, MTX_DEF); \ +} while (0) +#define IEEE80211_WT_LOCK_OBJ(_ic) (&(_ic)->ic_wtlock.mtx) +#define IEEE80211_WT_LOCK_DESTROY(_ic) mtx_destroy(IEEE80211_WT_LOCK_OBJ(_ic)) +#define IEEE80211_WT_LOCK(_ic) mtx_lock(IEEE80211_WT_LOCK_OBJ(_ic)) +#define IEEE80211_WT_UNLOCK(_ic) mtx_unlock(IEEE80211_WT_LOCK_OBJ(_ic)) + +/* * Node locking definitions. */ typedef struct { @@ -334,6 +351,11 @@ #define NET80211_TAG_RECV_PARAMS 2 struct ieee80211com; +void ieee80211_tx_watchdog_attach(struct ieee80211com *); +void ieee80211_tx_watchdog_refresh(struct ieee80211com *, int, int); +void ieee80211_tx_watchdog_stop(struct ieee80211com *); +void ieee80211_tx_watchdog_detach(struct ieee80211com *); + int ieee80211_parent_xmitpkt(struct ieee80211com *, struct mbuf *); int ieee80211_vap_xmitpkt(struct ieee80211vap *, struct mbuf *); Index: sys/net80211/ieee80211_freebsd.c =================================================================== --- sys/net80211/ieee80211_freebsd.c +++ sys/net80211/ieee80211_freebsd.c @@ -562,6 +562,82 @@ return (0); } +void +ieee80211_tx_watchdog_attach(struct ieee80211com *ic) +{ + if (ic->ic_flags_ext & IEEE80211_FEXT_WATCHDOG) { + IEEE80211_WT_LOCK_INIT(ic, ic->ic_name); + callout_init_mtx(&ic->ic_tx_watchdog, + IEEE80211_WT_LOCK_OBJ(ic), 0); + } +} + +static void +ieee80211_tx_watchdog(void *arg) +{ + struct ieee80211com *ic = arg; + + if (ic->ic_tx_timer > 0) { + if (--ic->ic_tx_timer == 0) { + ic_printf(ic, "device timeout\n"); + + counter_u64_add(ic->ic_oerrors, ic->ic_tx_queued); + ic->ic_tx_queued = 0; + + ieee80211_restart_all(ic); + return; + } + + callout_reset(&ic->ic_tx_watchdog, hz, ieee80211_tx_watchdog, + ic); + } +} + +void +ieee80211_tx_watchdog_refresh(struct ieee80211com *ic, int nframes, int update) +{ + if (ic->ic_flags_ext & IEEE80211_FEXT_WATCHDOG) { + IEEE80211_WT_LOCK(ic); + if (ic->ic_tx_queued + nframes < 0) + ic->ic_tx_queued = -nframes; + + if (ic->ic_tx_queued == 0) { + ic->ic_tx_timer = IEEE80211_WATCHDOG_TIMEOUT; + callout_reset(&ic->ic_tx_watchdog, hz, + ieee80211_tx_watchdog, ic); + } + + ic->ic_tx_queued += nframes; + + if (ic->ic_tx_queued == 0) + ic->ic_tx_timer = 0; + else if (update != 0) + ic->ic_tx_timer = IEEE80211_WATCHDOG_TIMEOUT; + IEEE80211_WT_UNLOCK(ic); + } +} + +void +ieee80211_tx_watchdog_stop(struct ieee80211com *ic) +{ + if (ic->ic_flags_ext & IEEE80211_FEXT_WATCHDOG) { + IEEE80211_WT_LOCK(ic); + callout_stop(&ic->ic_tx_watchdog); + ic->ic_tx_timer = 0; + ic->ic_tx_queued = 0; + IEEE80211_WT_UNLOCK(ic); + } +} + +void +ieee80211_tx_watchdog_detach(struct ieee80211com *ic) +{ + if (ic->ic_flags_ext & IEEE80211_FEXT_WATCHDOG) { + callout_drain(&ic->ic_tx_watchdog); + IEEE80211_WT_LOCK_DESTROY(ic); + } +} + /* * Transmit a frame to the parent interface. @@ -569,21 +645,27 @@ int ieee80211_parent_xmitpkt(struct ieee80211com *ic, struct mbuf *m) { - int error; + int error, nframes; + struct mbuf *mnext; + + nframes = 1; + for (mnext = m->m_nextpkt; mnext != NULL; mnext = mnext->m_nextpkt) + nframes++; /* * Assert the IC TX lock is held - this enforces the * processing -> queuing order is maintained */ IEEE80211_TX_LOCK_ASSERT(ic); + ieee80211_tx_watchdog_refresh(ic, nframes, 0); error = ic->ic_transmit(ic, m); if (error) { struct ieee80211_node *ni; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; - /* XXX number of fragments */ - if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, nframes); + ieee80211_tx_watchdog_refresh(ic, -nframes, 0); ieee80211_free_node(ni); ieee80211_free_mbuf(m); } Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c +++ sys/net80211/ieee80211_output.c @@ -530,9 +530,11 @@ if (params) (void) ieee80211_add_xmit_params(m, params); + ieee80211_tx_watchdog_refresh(ic, 1, 0); error = ic->ic_raw_xmit(ni, m, params); if (error) { if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); + ieee80211_tx_watchdog_refresh(ic, -1, 0); ieee80211_free_node(ni); } return (error); @@ -3489,7 +3491,9 @@ if (ni != NULL) { struct ifnet *ifp = ni->ni_vap->iv_ifp; + struct ieee80211com *ic = ni->ni_ic; + ieee80211_tx_watchdog_refresh(ic, -1, 1); if (status == 0) { if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); Index: sys/net80211/ieee80211_proto.c =================================================================== --- sys/net80211/ieee80211_proto.c +++ sys/net80211/ieee80211_proto.c @@ -1422,6 +1422,7 @@ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "down parent %s\n", ic->ic_name); ieee80211_runtask(ic, &ic->ic_parent_task); + ieee80211_tx_watchdog_stop(ic); } } } Index: sys/net80211/ieee80211_var.h =================================================================== --- sys/net80211/ieee80211_var.h +++ sys/net80211/ieee80211_var.h @@ -86,6 +86,8 @@ #define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000) #define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000)) +#define IEEE80211_WATCHDOG_TIMEOUT 5 + /* * 802.11 control state is split into a common portion that maps * 1-1 to a physical device and one or more "Virtual AP's" (VAP) @@ -120,12 +122,14 @@ const char *ic_name; /* usually device name */ ieee80211_com_lock_t ic_comlock; /* state update lock */ ieee80211_tx_lock_t ic_txlock; /* ic/vap TX lock */ + ieee80211_wt_lock_t ic_wtlock; /* watchdog lock */ LIST_ENTRY(ieee80211com) ic_next; /* on global list */ TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */ int ic_headroom; /* driver tx headroom needs */ enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ enum ieee80211_opmode ic_opmode; /* operation mode */ struct callout ic_inact; /* inactivity processing */ + struct callout ic_tx_watchdog; /* TX watchdog */ struct taskqueue *ic_tq; /* deferred state thread */ struct task ic_parent_task; /* deferred parent processing */ struct task ic_promisc_task;/* deferred promisc update */ @@ -139,6 +143,9 @@ counter_u64_t ic_ierrors; /* input errors */ counter_u64_t ic_oerrors; /* output errors */ + int16_t ic_tx_queued; /* number of queued frames */ + uint8_t ic_tx_timer; /* watchdog timer */ + uint32_t ic_flags; /* state flags */ uint32_t ic_flags_ext; /* extended state flags */ uint32_t ic_flags_ht; /* HT state flags */ @@ -597,6 +604,7 @@ #define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */ #define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/ #define IEEE80211_FEXT_UNIQMAC 0x00040000 /* CONF: user or computed mac */ +#define IEEE80211_FEXT_WATCHDOG 0x00080000 /* CONF: track tx activity */ #define IEEE80211_FEXT_BITS \ "\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \