Date: Fri, 19 Jun 2015 01:44:18 +0000 (UTC) From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r284588 - head/sys/dev/iwn Message-ID: <201506190144.t5J1iIbH083926@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: adrian Date: Fri Jun 19 01:44:17 2015 New Revision: 284588 URL: https://svnweb.freebsd.org/changeset/base/284588 Log: First cut at attempting to buffer frames until we see a beacon. The iwn(4) firmware forgets most of its channel state after an RXON command. This means that any beacons its seen on passive 5GHz channels are forgotten upon an association/authorisation request. This unfortuantely means that 5GHz association almost always fails - the assoc and/or auth frames are dropped with a status of "passive channel, haven't seen a beacon yet." (0x90.) So: * add an xmit queue, global, to buffer frames * modify the xmit path to use the mbuf tag from net80211 to specify raw frame details * buffer xmit frames from both raw and non-raw paths * if a beacon is seen in the RX path, schedule a taskqueue to send said frames and un-buffer things. * flush frames during state change back to INIT, or NIC down/up/detach. This isn't the final shape I'd like this to be in but it certainly is better than 5GHz "not working at all". Tested: * Intel 5100, STA mode (before spilling coffee) * Intel 5300, STA mode (after spilling coffee) Story: * This has been bugging me at work for months, which I just worked around by throwing an ath(4) into my Lenovo T400 cardbus slot. * Our ops director discovered indeed FreeBSD runs well on the Lenovo T420p, except for that pesky 5GHz thing. So now developers also can have a T420p running FreeBSD to do work with. Their #1 feedback to me - "boy it'd be nice if 5GHz wifi worked." * .. then, I was at NANOG but stuck with 5GHz only wifi and no ath(4) NIC to put in a laptop - and I snapped. Thus, the reason this is actually work related. MFC after: 2 weeks Sponsored by: Norse Corp, Inc. Modified: head/sys/dev/iwn/if_iwn.c head/sys/dev/iwn/if_iwnvar.h Modified: head/sys/dev/iwn/if_iwn.c ============================================================================== --- head/sys/dev/iwn/if_iwn.c Fri Jun 19 01:21:10 2015 (r284587) +++ head/sys/dev/iwn/if_iwn.c Fri Jun 19 01:44:17 2015 (r284588) @@ -232,6 +232,7 @@ static int iwn_tx_data(struct iwn_softc static int iwn_tx_data_raw(struct iwn_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *params); +static void iwn_xmit_task(void *arg0, int pending); static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void iwn_start(struct ifnet *); @@ -682,6 +683,9 @@ iwn_attach(device_t dev) TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc); TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc); TASK_INIT(&sc->sc_panic_task, 0, iwn_panicked, sc); + TASK_INIT(&sc->sc_xmit_task, 0, iwn_xmit_task, sc); + + mbufq_init(&sc->sc_xmit_queue, 1024); sc->sc_tq = taskqueue_create("iwn_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->sc_tq); @@ -1360,6 +1364,28 @@ iwn_vap_delete(struct ieee80211vap *vap) free(ivp, M_80211_VAP); } +static void +iwn_xmit_queue_drain(struct iwn_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + IWN_LOCK_ASSERT(sc); + while ((m = mbufq_dequeue(&sc->sc_xmit_queue)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + ieee80211_free_node(ni); + m_freem(m); + } +} + +static int +iwn_xmit_queue_enqueue(struct iwn_softc *sc, struct mbuf *m) +{ + + IWN_LOCK_ASSERT(sc); + return (mbufq_enqueue(&sc->sc_xmit_queue, m)); +} + static int iwn_detach(device_t dev) { @@ -1373,6 +1399,11 @@ iwn_detach(device_t dev) if (ifp != NULL) { ic = ifp->if_l2com; + /* Free the mbuf queue and node references */ + IWN_LOCK(sc); + iwn_xmit_queue_drain(sc); + IWN_UNLOCK(sc); + ieee80211_draintask(ic, &sc->sc_reinit_task); ieee80211_draintask(ic, &sc->sc_radioon_task); ieee80211_draintask(ic, &sc->sc_radiooff_task); @@ -2831,6 +2862,9 @@ iwn_newstate(struct ieee80211vap *vap, e sc->rxon->filter &= ~htole32(IWN_FILTER_BSS); sc->calib.state = IWN_CALIB_STATE_INIT; + /* Wait until we hear a beacon before we transmit */ + sc->sc_beacon_wait = 1; + if ((error = iwn_auth(sc, vap)) != 0) { device_printf(sc->sc_dev, "%s: could not move to auth state\n", __func__); @@ -2846,6 +2880,9 @@ iwn_newstate(struct ieee80211vap *vap, e break; } + /* Wait until we hear a beacon before we transmit */ + sc->sc_beacon_wait = 1; + /* * !RUN -> RUN requires setting the association id * which is done with a firmware cmd. We also defer @@ -2859,6 +2896,12 @@ iwn_newstate(struct ieee80211vap *vap, e case IEEE80211_S_INIT: sc->calib.state = IWN_CALIB_STATE_INIT; + /* + * Purge the xmit queue so we don't have old frames + * during a new association attempt. + */ + sc->sc_beacon_wait = 0; + iwn_xmit_queue_drain(sc); break; default: @@ -3066,6 +3109,32 @@ iwn_rx_done(struct iwn_softc *sc, struct } } + /* + * If it's a beacon and we're waiting, then do the + * wakeup. This should unblock raw_xmit/start. + */ + if (sc->sc_beacon_wait) { + uint8_t type, subtype; + /* NB: Re-assign wh */ + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + /* + * This assumes at this point we've received our own + * beacon. + */ + DPRINTF(sc, IWN_DEBUG_TRACE, + "%s: beacon_wait, type=%d, subtype=%d\n", + __func__, type, subtype); + if (type == IEEE80211_FC0_TYPE_MGT && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, + "%s: waking things up\n", __func__); + /* queue taskqueue to transmit! */ + taskqueue_enqueue(sc->sc_tq, &sc->sc_xmit_task); + } + } + IWN_UNLOCK(sc); /* Send the frame to the 802.11 layer. */ @@ -4802,6 +4871,51 @@ iwn_tx_data_raw(struct iwn_softc *sc, st return 0; } +static void +iwn_xmit_task(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + int error; + struct ieee80211_bpf_params p; + int have_p; + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__); + + IWN_LOCK(sc); + /* + * Dequeue frames, attempt to transmit, + * then disable beaconwait when we're done. + */ + while ((m = mbufq_dequeue(&sc->sc_xmit_queue)) != NULL) { + have_p = 0; + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + + /* Get xmit params if appropriate */ + if (ieee80211_get_xmit_params(m, &p) == 0) + have_p = 1; + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: m=%p, have_p=%d\n", + __func__, m, have_p); + + /* If we have xmit params, use them */ + if (have_p) + error = iwn_tx_data_raw(sc, m, ni, &p); + else + error = iwn_tx_data(sc, m, ni); + + if (error != 0) { + ieee80211_free_node(ni); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + } + } + + sc->sc_beacon_wait = 0; + IWN_UNLOCK(sc); +} + static int iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) @@ -4819,7 +4933,25 @@ iwn_raw_xmit(struct ieee80211_node *ni, return ENETDOWN; } + /* XXX? net80211 doesn't set this on xmit'ed raw frames? */ + m->m_pkthdr.rcvif = (void *) ni; + IWN_LOCK(sc); + + /* queue frame if we have to */ + if (sc->sc_beacon_wait) { + if (iwn_xmit_queue_enqueue(sc, m) != 0) { + m_freem(m); + ieee80211_free_node(ni); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + IWN_UNLOCK(sc); + return (ENOBUFS); + } + /* Queued, so just return OK */ + IWN_UNLOCK(sc); + return (0); + } + if (params == NULL) { /* * Legacy path; interpret frame contents to decide @@ -4866,6 +4998,14 @@ iwn_start_locked(struct ifnet *ifp) IWN_LOCK_ASSERT(sc); + /* + * If we're waiting for a beacon, we can just exit out here + * and wait for the taskqueue to be kicked. + */ + if (sc->sc_beacon_wait) { + return; + } + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || Modified: head/sys/dev/iwn/if_iwnvar.h ============================================================================== --- head/sys/dev/iwn/if_iwnvar.h Fri Jun 19 01:21:10 2015 (r284587) +++ head/sys/dev/iwn/if_iwnvar.h Fri Jun 19 01:44:17 2015 (r284588) @@ -309,6 +309,7 @@ struct iwn_softc { struct task sc_radioon_task; struct task sc_radiooff_task; struct task sc_panic_task; + struct task sc_xmit_task; /* Taskqueue */ struct taskqueue *sc_tq; @@ -385,6 +386,9 @@ struct iwn_softc { /* Are we doing a scan? */ int sc_is_scanning; + /* Are we waiting for a beacon before xmit? */ + int sc_beacon_wait; + struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES]; int (*sc_ampdu_rx_start)(struct ieee80211_node *, @@ -417,6 +421,13 @@ struct iwn_softc { #define IWN_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) uint32_t ucode_rev; + + /* + * Global queue for queuing xmit frames + * when we can't yet transmit (eg raw + * frames whilst waiting for beacons.) + */ + struct mbufq sc_xmit_queue; }; #define IWN_LOCK_INIT(_sc) \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201506190144.t5J1iIbH083926>