Date: Sun, 28 Feb 2016 21:15:58 +0200 From: "Andriy Voskoboinyk" <s3erios@gmail.com> To: "freebsd-wireless@freebsd.org" <freebsd-wireless@freebsd.org>, "Adrian Chadd" <adrian@freebsd.org> Subject: Re: software scan fix - please test (Was: why we can't use the net80211 taskqueue for everything) Message-ID: <op.ydj44wgyiew4ia@localhost> In-Reply-To: <op.yc4mcrg5iew4ia@localhost> References: <CAJ-VmoneUBz4Vt3hFj8S4G_o8ptd3Z-NL5%2B6HuG33C3C_x-2jQ@mail.gmail.com> <op.yc4mcrg5iew4ia@localhost>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --] Sat, 20 Feb 2016 12:06:17 +0200 було написано Andriy Voskoboinyk <s3erios@gmail.com>: >> hi, >> >> andriy has a few reviews out that tidy up some things, which I'd reply >> to, but .. reviews is offline. So, here's the 30 second version: >> >> * the net80211 taskqueue runs the software scan engine, and the >> software scan engine currently sleeps whilst it's running. >> >> This means that if you put newstate, deferred transmit, etc into the >> net80211 taskqueue, then it just won't run during scan. >> >> The net80211 software scan thing should be modified to not sleep >> whilst it's waiting for scan results and instead just kick off another >> timer event to finish that part of the loop. Then yes, we can just >> migrate * to the net80211 task queue and use it for all serialisation >> of a wifi driver. >> >> (And yes, I'd like to see that done ASAP..) >> >> Thanks, >> >> >> -adrian > > Hi, > > I have replaced sleeping on conditional variable inside scan task > with scan_curchan task rescheduling (so this problem should be fixed > now). > > For everyone, who wishes to test: apply the attached patch > (merged from D5133, D5137, D5139, D5140, D5142, D5143, D5145, D5147, > D5148 and D5152) > and rebuild + install the kernel. Scan should work as before. > > Thanks! Newer patch includes fix for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197498#c7 (D5482) - so, scanning for adhoc/hostap mode should work now. [-- Attachment #2 --] Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c (revision 296108) +++ sys/net80211/ieee80211_output.c (working copy) @@ -849,6 +849,15 @@ return (ret); } +static void +ieee80211_nulldata_transmitted(struct ieee80211_node *ni, void *arg, + int status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + wakeup(vap); +} + /* * Send a null data frame to the specified node. If the station * is setup for QoS then a QoS Null Data frame is constructed. @@ -937,6 +946,11 @@ vap->iv_opmode != IEEE80211_M_HOSTAP) wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; } + if ((ic->ic_flags & IEEE80211_F_SCAN) && + (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) { + ieee80211_add_callback(m, ieee80211_nulldata_transmitted, + NULL); + } m->m_len = m->m_pkthdr.len = hdrlen; m->m_flags |= M_ENCAP; /* mark encapsulated */ Index: sys/net80211/ieee80211_proto.c =================================================================== --- sys/net80211/ieee80211_proto.c (revision 296108) +++ sys/net80211/ieee80211_proto.c (working copy) @@ -1800,13 +1800,19 @@ * We have been requested to drop back to the INIT before * proceeding to the new state. */ + /* Suppress 'pending state transition lost' warning */ + vap->iv_nstate = IEEE80211_S_INIT; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s arg %d\n", __func__, ieee80211_state_name[vap->iv_state], - ieee80211_state_name[IEEE80211_S_INIT], arg); - vap->iv_newstate(vap, IEEE80211_S_INIT, arg); + ieee80211_state_name[vap->iv_nstate], arg); + vap->iv_newstate(vap, vap->iv_nstate, 0); IEEE80211_LOCK_ASSERT(ic); - vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT; + vap->iv_flags_ext &= ~(IEEE80211_FEXT_REINIT | + IEEE80211_FEXT_STATEWAIT); + /* enqueue new state transition after cancel_scan() task */ + ieee80211_new_state_locked(vap, nstate, arg); + goto done; } ostate = vap->iv_state; Index: sys/net80211/ieee80211_scan.c =================================================================== --- sys/net80211/ieee80211_scan.c (revision 296108) +++ sys/net80211/ieee80211_scan.c (working copy) @@ -453,8 +453,9 @@ } /* - * Public access to scan_next for drivers that manage - * scanning themselves (e.g. for firmware-based devices). + * Manually switch to the next channel in the channel list. + * Provided for drivers that manage scanning themselves + * (e.g. for firmware-based devices). */ void ieee80211_scan_next(struct ieee80211vap *vap) @@ -465,8 +466,9 @@ } /* - * Public access to scan_next for drivers that are not able to scan single - * channels (e.g. for firmware-based devices). + * Manually stop a scan that is currently running. + * Provided for drivers that are not able to scan single channels + * (e.g. for firmware-based devices). */ void ieee80211_scan_done(struct ieee80211vap *vap) @@ -486,7 +488,7 @@ } /* - * Probe the curent channel, if allowed, while scanning. + * Probe the current channel, if allowed, while scanning. * If the channel is not marked passive-only then send * a probe request immediately. Otherwise mark state and * listen for beacons on the channel; if we receive something Index: sys/net80211/ieee80211_scan_sw.c =================================================================== --- sys/net80211/ieee80211_scan_sw.c (revision 296108) +++ sys/net80211/ieee80211_scan_sw.c (working copy) @@ -54,17 +54,18 @@ struct scan_state { struct ieee80211_scan_state base; /* public state */ - u_int ss_iflags; /* flags used internally */ -#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ -#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ -#define ISCAN_CANCEL 0x0004 /* cancel current scan */ -#define ISCAN_ABORT 0x0008 /* end the scan immediately */ - unsigned long ss_chanmindwell; /* min dwell on curchan */ - unsigned long ss_scanend; /* time scan must stop */ - u_int ss_duration; /* duration for next scan */ - struct task ss_scan_task; /* scan execution */ - struct cv ss_scan_cv; /* scan signal */ - struct callout ss_scan_timer; /* scan timer */ + u_int ss_iflags; /* flags used internally */ +#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ +#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ +#define ISCAN_CANCEL 0x0004 /* cancel current scan */ +#define ISCAN_ABORT 0x0008 /* end the scan immediately */ +#define ISCAN_RUNNING 0x0010 /* scan was started */ + + unsigned long ss_chanmindwell; /* min dwell on curchan */ + unsigned long ss_scanend; /* time scan must stop */ + u_int ss_duration; /* duration for next scan */ + struct task ss_scan_start; /* scan start */ + struct timeout_task ss_scan_curchan; /* scan execution */ }; #define SCAN_PRIVATE(ss) ((struct scan_state *) ss) @@ -98,8 +99,12 @@ static void scan_curchan(struct ieee80211_scan_state *, unsigned long); static void scan_mindwell(struct ieee80211_scan_state *); -static void scan_signal(void *); -static void scan_task(void *, int); +static void scan_signal(struct ieee80211_scan_state *, int); +static void scan_signal_locked(struct ieee80211_scan_state *, int); +static void scan_start(void *, int); +static void scan_curchan_task(void *, int); +static void scan_end(struct ieee80211_scan_state *, int); +static void scan_done(struct ieee80211_scan_state *, int); MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); @@ -109,12 +114,10 @@ struct ieee80211_scan_state *ss = ic->ic_scan; if (ss != NULL) { - IEEE80211_LOCK(ic); - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; - scan_signal(ss); - IEEE80211_UNLOCK(ic); - ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); - callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer); + scan_signal(ss, ISCAN_ABORT); + ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); + taskqueue_drain_timeout(ic->ic_tq, + &SCAN_PRIVATE(ss)->ss_scan_curchan); KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scan still running")); @@ -148,16 +151,13 @@ ieee80211_swscan_vdetach(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss; + struct ieee80211_scan_state *ss = ic->ic_scan; IEEE80211_LOCK_ASSERT(ic); - ss = ic->ic_scan; - if (ss != NULL && ss->ss_vap == vap) { - if (ic->ic_flags & IEEE80211_F_SCAN) { - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; - scan_signal(ss); - } - } + + if (ss != NULL && ss->ss_vap == vap && + (ic->ic_flags & IEEE80211_F_SCAN)) + scan_signal_locked(ss, ISCAN_ABORT); } static void @@ -236,7 +236,7 @@ ic->ic_flags |= IEEE80211_F_SCAN; /* Start scan task */ - ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); } return 1; } else { @@ -411,7 +411,8 @@ ss->ss_maxdwell = duration; ic->ic_flags |= IEEE80211_F_SCAN; ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; - ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + ieee80211_runtask(ic, + &SCAN_PRIVATE(ss)->ss_scan_start); } else { /* XXX msg+stat */ } @@ -426,11 +427,8 @@ return (ic->ic_flags & IEEE80211_F_SCAN); } -/* - * Cancel any scan currently going on for the specified vap. - */ static void -ieee80211_swscan_cancel_scan(struct ieee80211vap *vap) +cancel_scan(struct ieee80211vap *vap, int any, const char *func) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; @@ -437,22 +435,21 @@ IEEE80211_LOCK(ic); if ((ic->ic_flags & IEEE80211_F_SCAN) && - ss->ss_vap == vap && + (any || ss->ss_vap == vap) && (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: cancel %s scan\n", __func__, + "%s: cancel %s scan\n", func, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); - /* clear bg scan NOPICK and mark cancel request */ + /* clear bg scan NOPICK */ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; - /* wake up the scan task */ - scan_signal(ss); + /* mark cancel request and wake up the scan task */ + scan_signal_locked(ss, ISCAN_CANCEL); } else { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n", - __func__, + func, !! (ic->ic_flags & IEEE80211_F_SCAN), (ss->ss_vap == vap ? "match" : "nomatch"), !! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL)); @@ -461,74 +458,57 @@ } /* + * Cancel any scan currently going on for the specified vap. + */ +static void +ieee80211_swscan_cancel_scan(struct ieee80211vap *vap) +{ + cancel_scan(vap, 0, __func__); +} + +/* * Cancel any scan currently going on. */ static void ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - - IEEE80211_LOCK(ic); - if ((ic->ic_flags & IEEE80211_F_SCAN) && - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: cancel %s scan\n", __func__, - ss->ss_flags & IEEE80211_SCAN_ACTIVE ? - "active" : "passive"); - - /* clear bg scan NOPICK and mark cancel request */ - ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; - /* wake up the scan task */ - scan_signal(ss); - } else { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n", - __func__, - !! (ic->ic_flags & IEEE80211_F_SCAN), - (ss->ss_vap == vap ? "match" : "nomatch"), - !! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL)); - } - IEEE80211_UNLOCK(ic); + cancel_scan(vap, 1, __func__); } /* - * Public access to scan_next for drivers that manage - * scanning themselves (e.g. for firmware-based devices). + * Manually switch to the next channel in the channel list. + * Provided for drivers that manage scanning themselves + * (e.g. for firmware-based devices). */ static void ieee80211_swscan_scan_next(struct ieee80211vap *vap) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); /* wake up the scan task */ - IEEE80211_LOCK(ic); - scan_signal(ss); - IEEE80211_UNLOCK(ic); + scan_signal(ss, 0); } /* - * Public access to scan_next for drivers that are not able to scan single - * channels (e.g. for firmware-based devices). + * Manually stop a scan that is currently running. + * Provided for drivers that are not able to scan single channels + * (e.g. for firmware-based devices). */ static void ieee80211_swscan_scan_done(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss; + struct ieee80211_scan_state *ss = ic->ic_scan; IEEE80211_LOCK_ASSERT(ic); - ss = ic->ic_scan; - scan_signal(ss); + scan_signal_locked(ss, 0); } /* - * Probe the curent channel, if allowed, while scanning. + * Probe the current channel, if allowed, while scanning. * If the channel is not marked passive-only then send * a probe request immediately. Otherwise mark state and * listen for beacons on the channel; if we receive something @@ -568,28 +548,48 @@ scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = ss->ss_ic; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: calling; maxdwell=%lu\n", __func__, maxdwell); - IEEE80211_LOCK(vap->iv_ic); + IEEE80211_LOCK(ic); if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) ieee80211_probe_curchan(vap, 0); - callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, - maxdwell, scan_signal, ss); - IEEE80211_UNLOCK(vap->iv_ic); + taskqueue_enqueue_timeout(ic->ic_tq, + &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell); + IEEE80211_UNLOCK(ic); } static void -scan_signal(void *arg) +scan_signal(struct ieee80211_scan_state *ss, int iflags) { - struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + struct ieee80211com *ic = ss->ss_ic; - IEEE80211_LOCK_ASSERT(ss->ss_ic); - cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv); + IEEE80211_UNLOCK_ASSERT(ic); + + IEEE80211_LOCK(ic); + scan_signal_locked(ss, iflags); + IEEE80211_UNLOCK(ic); } +static void +scan_signal_locked(struct ieee80211_scan_state *ss, int iflags) +{ + struct scan_state *ss_priv = SCAN_PRIVATE(ss); + struct timeout_task *scan_task = &ss_priv->ss_scan_curchan; + struct ieee80211com *ic = ss->ss_ic; + + IEEE80211_LOCK_ASSERT(ic); + + ss_priv->ss_iflags |= iflags; + if (ss_priv->ss_iflags & ISCAN_RUNNING) { + if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0) + taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0); + } +} + /* * Handle mindwell requirements completed; initiate a channel * change to the next channel asap. @@ -597,38 +597,35 @@ static void scan_mindwell(struct ieee80211_scan_state *ss) { - struct ieee80211com *ic = ss->ss_ic; + struct ieee80211vap *vap = ss->ss_vap; - IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); - IEEE80211_LOCK(ic); - scan_signal(ss); - IEEE80211_UNLOCK(ic); + scan_signal(ss, 0); } static void -scan_task(void *arg, int pending) +scan_start(void *arg, int pending) { #define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + struct scan_state *ss_priv = SCAN_PRIVATE(ss); struct ieee80211vap *vap = ss->ss_vap; struct ieee80211com *ic = ss->ss_ic; - struct ieee80211_channel *chan; - unsigned long maxdwell, scanend; - int scandone = 0; IEEE80211_LOCK(ic); if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) { + (ss_priv->ss_iflags & ISCAN_ABORT)) { /* Cancelled before we started */ - goto done; + scan_done(ss, 0); + return; } if (ss->ss_next == ss->ss_last) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no channels to scan\n", __func__); - scandone = 1; - goto done; + scan_done(ss, 1); + return; } if (vap->iv_opmode == IEEE80211_M_STA && @@ -636,110 +633,134 @@ if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { /* Enable station power save mode */ vap->iv_sta_ps(vap, 1); - /* - * Use an 1ms delay so the null data frame has a chance - * to go out. - * XXX Should use M_TXCB mechanism to eliminate this. - */ - cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv, - IEEE80211_LOCK_OBJ(ic), msecs_to_ticks(1)); - if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) - goto done; + /* Wait until null data frame will be ACK'ed */ + mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH, + "sta_ps", msecs_to_ticks(10)); + if (ss_priv->ss_iflags & ISCAN_ABORT) { + scan_done(ss, 0); + return; + } } } - scanend = ticks + SCAN_PRIVATE(ss)->ss_duration; + ss_priv->ss_scanend = ticks + ss_priv->ss_duration; /* XXX scan state can change! Re-validate scan state! */ IEEE80211_UNLOCK(ic); + ic->ic_scan_start(ic); /* notify driver */ + + scan_curchan_task(ss, 0); +} + +static void +scan_curchan_task(void *arg, int pending) +{ + struct ieee80211_scan_state *ss = arg; + struct scan_state *ss_priv = SCAN_PRIVATE(ss); + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = ss->ss_ic; + struct ieee80211_channel *chan; + unsigned long maxdwell; + int scandone; + IEEE80211_LOCK(ic); +end: + scandone = (ss->ss_next >= ss->ss_last) || + (ss_priv->ss_iflags & ISCAN_CANCEL) != 0; - for (;;) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: loop start; scandone=%d\n", + __func__, + scandone); - scandone = (ss->ss_next >= ss->ss_last) || - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; + if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || + (ss_priv->ss_iflags & ISCAN_ABORT) || + time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { + ss_priv->ss_iflags &= ~ISCAN_RUNNING; + scan_end(ss, scandone); + return; + } else + ss_priv->ss_iflags |= ISCAN_RUNNING; - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: loop start; scandone=%d\n", - __func__, - scandone); + chan = ss->ss_chans[ss->ss_next++]; - if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) || - time_after(ticks + ss->ss_mindwell, scanend)) - break; + /* + * Watch for truncation due to the scan end time. + */ + if (time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend)) + maxdwell = ss_priv->ss_scanend - ticks; + else + maxdwell = ss->ss_maxdwell; - chan = ss->ss_chans[ss->ss_next++]; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + ieee80211_channel_type_char(ic->ic_curchan), + ieee80211_chan2ieee(ic, chan), + ieee80211_channel_type_char(chan), + (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && + (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? + "active" : "passive", + ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); - /* - * Watch for truncation due to the scan end time. - */ - if (time_after(ticks + ss->ss_maxdwell, scanend)) - maxdwell = scanend - ticks; - else - maxdwell = ss->ss_maxdwell; + /* + * Potentially change channel and phy mode. + */ + ic->ic_curchan = chan; + ic->ic_rt = ieee80211_get_ratetable(chan); + IEEE80211_UNLOCK(ic); + /* + * Perform the channel change and scan unlocked so the driver + * may sleep. Once set_channel returns the hardware has + * completed the channel change. + */ + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", - __func__, - ieee80211_chan2ieee(ic, ic->ic_curchan), - ieee80211_channel_type_char(ic->ic_curchan), - ieee80211_chan2ieee(ic, chan), - ieee80211_channel_type_char(chan), - (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && - (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? - "active" : "passive", - ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); + /* + * Scan curchan. Drivers for "intelligent hardware" + * override ic_scan_curchan to tell the device to do + * the work. Otherwise we manage the work ourselves; + * sending a probe request (as needed), and arming the + * timeout to switch channels after maxdwell ticks. + * + * scan_curchan should only pause for the time required to + * prepare/initiate the hardware for the scan (if at all). + */ + ic->ic_scan_curchan(ss, maxdwell); + IEEE80211_LOCK(ic); - /* - * Potentially change channel and phy mode. - */ - ic->ic_curchan = chan; - ic->ic_rt = ieee80211_get_ratetable(chan); - IEEE80211_UNLOCK(ic); - /* - * Perform the channel change and scan unlocked so the driver - * may sleep. Once set_channel returns the hardware has - * completed the channel change. - */ - ic->ic_set_channel(ic); - ieee80211_radiotap_chan_change(ic); + /* XXX scan state can change! Re-validate scan state! */ - /* - * Scan curchan. Drivers for "intelligent hardware" - * override ic_scan_curchan to tell the device to do - * the work. Otherwise we manage the work outselves; - * sending a probe request (as needed), and arming the - * timeout to switch channels after maxdwell ticks. - * - * scan_curchan should only pause for the time required to - * prepare/initiate the hardware for the scan (if at all), the - * below condvar is used to sleep for the channels dwell time - * and allows it to be signalled for abort. - */ - ic->ic_scan_curchan(ss, maxdwell); - IEEE80211_LOCK(ic); + ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell; + /* clear mindwell lock and initial channel change flush */ + ss_priv->ss_iflags &= ~ISCAN_REP; - /* XXX scan state can change! Re-validate scan state! */ + if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) + goto end; - SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; - /* clear mindwell lock and initial channel change flush */ - SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: waiting\n", __func__); + IEEE80211_UNLOCK(ic); +} - if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))) - continue; +static void +scan_end(struct ieee80211_scan_state *ss, int scandone) +{ + struct scan_state *ss_priv = SCAN_PRIVATE(ss); + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = ss->ss_ic; - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: waiting\n", __func__); - /* Wait to be signalled to scan the next channel */ - cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic)); - } + IEEE80211_LOCK_ASSERT(ic); IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__); - if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) - goto done; + if (ss_priv->ss_iflags & ISCAN_ABORT) { + scan_done(ss, scandone); + return; + } IEEE80211_UNLOCK(ic); ic->ic_scan_end(ic); /* notify driver */ @@ -750,8 +771,7 @@ * Since a cancellation may have occured during one of the * driver calls (whilst unlocked), update scandone. */ - if (scandone == 0 && - ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) { + if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { /* XXX printf? */ if_printf(vap->iv_ifp, "%s: OOPS! scan cancelled during driver call (1)!\n", @@ -776,7 +796,7 @@ IEEE80211_LOCK(ic); } /* clear internal flags and any indication of a pick */ - SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + ss_priv->ss_iflags &= ~ISCAN_REP; ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; /* @@ -786,15 +806,15 @@ * notify the driver to end the scan above to avoid having * rx frames alter the scan candidate list. */ - if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && + if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 && !ss->ss_ops->scan_end(ss, vap) && (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && - time_before(ticks + ss->ss_mindwell, scanend)) { + time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: done, restart " "[ticks %u, dwell min %lu scanend %lu]\n", __func__, - ticks, ss->ss_mindwell, scanend); + ticks, ss->ss_mindwell, ss_priv->ss_scanend); ss->ss_next = 0; /* reset to begining */ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) vap->iv_stats.is_scan_active++; @@ -802,7 +822,7 @@ vap->iv_stats.is_scan_passive++; ss->ss_ops->scan_restart(ss, vap); /* XXX? */ - ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + ieee80211_runtask(ic, &ss_priv->ss_scan_start); IEEE80211_UNLOCK(ic); return; } @@ -814,14 +834,13 @@ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", __func__, scandone ? "done" : "stopped", - ticks, ss->ss_mindwell, scanend); + ticks, ss->ss_mindwell, ss_priv->ss_scanend); /* * Since a cancellation may have occured during one of the * driver calls (whilst unlocked), update scandone. */ - if (scandone == 0 && - ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) { + if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { /* XXX printf? */ if_printf(vap->iv_ifp, "%s: OOPS! scan cancelled during driver call (2)!\n", @@ -829,6 +848,18 @@ scandone = 1; } + scan_done(ss, scandone); +} + +static void +scan_done(struct ieee80211_scan_state *ss, int scandone) +{ + struct scan_state *ss_priv = SCAN_PRIVATE(ss); + struct ieee80211com *ic = ss->ss_ic; + struct ieee80211vap *vap = ss->ss_vap; + + IEEE80211_LOCK_ASSERT(ic); + /* * Clear the SCAN bit first in case frames are * pending on the station power save queue. If @@ -835,8 +866,8 @@ * we defer this then the dispatch of the frames * may generate a request to cancel scanning. */ -done: ic->ic_flags &= ~IEEE80211_F_SCAN; + /* * Drop out of power save mode when a scan has * completed. If this scan was prematurely terminated @@ -853,7 +884,8 @@ ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; } } - SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); + ss_priv->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); + ss_priv->ss_scanend = 0; ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); IEEE80211_UNLOCK(ic); #undef ISCAN_REP @@ -947,9 +979,9 @@ ic->ic_scan = NULL; return; } - callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0); - cv_init(&ss->ss_scan_cv, "scan"); - TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss); + TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss); + TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0, + scan_curchan_task, ss); ic->ic_scan = &ss->base; ss->base.ss_ic = ic;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?op.ydj44wgyiew4ia>
