Skip site navigation (1)Skip section navigation (2)
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>