[IEEE80211]: Fix softmac lockdep reports.

It seems I was actually able to hit this deadlock, on my quad G5 softmac
locks up more often than not. This fixes it by using an own workqueue
that can safely be flushed under RTNL.

Not sure if the patch is correct with the workqueue naming. And don't
think with the patch it doesn't continually lock up. It still does, just
doesn't invoke lockdep warnings all the time.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h
index 8911927..1ef6282 100644
--- a/include/net/ieee80211softmac.h
+++ b/include/net/ieee80211softmac.h
@@ -229,6 +229,8 @@
 	/* this lock protects this structure */
 	spinlock_t lock;
 
+	struct workqueue_struct *wq;
+
 	u8 running; /* SoftMAC started? */
 	u8 scanning;
 
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
index 4c0feb2..c4d122d 100644
--- a/net/ieee80211/softmac/ieee80211softmac_assoc.c
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -53,7 +53,7 @@
 	/* Set a timer for timeout */
 	/* FIXME: make timeout configurable */
 	if (likely(mac->running))
-		schedule_delayed_work(&mac->associnfo.timeout, 5 * HZ);
+		queue_delayed_work(mac->wq, &mac->associnfo.timeout, 5 * HZ);
 	spin_unlock_irqrestore(&mac->lock, flags);
 }
 
@@ -419,7 +419,7 @@
 				network->authenticated = 0;
 				/* we don't want to do this more than once ... */
 				network->auth_desynced_once = 1;
-				schedule_delayed_work(&mac->associnfo.work, 0);
+				queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
 				break;
 			}
 		default:
@@ -441,7 +441,7 @@
 
 	spin_lock_irqsave(&mac->lock, flags);
 	mac->associnfo.associating = 1;
-	schedule_delayed_work(&mac->associnfo.work, 0);
+	queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
 	spin_unlock_irqrestore(&mac->lock, flags);
 }
 
@@ -483,7 +483,7 @@
 		dprintkl(KERN_INFO PFX "reassoc request from unknown network\n");
 		return 0;
 	}
-	schedule_delayed_work(&mac->associnfo.work, 0);
+	queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
 
 	return 0;
 }
diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c
index 855fa0f..a53a751 100644
--- a/net/ieee80211/softmac/ieee80211softmac_auth.c
+++ b/net/ieee80211/softmac/ieee80211softmac_auth.c
@@ -62,7 +62,7 @@
 
 	/* add to list */
 	list_add_tail(&auth->list, &mac->auth_queue);
-	schedule_delayed_work(&auth->work, 0);
+	queue_delayed_work(mac->wq, &auth->work, 0);
 	spin_unlock_irqrestore(&mac->lock, flags);
 
 	return 0;
@@ -97,7 +97,7 @@
 		}
 		net->authenticated = 0;
 		/* add a timeout call so we eventually give up waiting for an auth reply */
-		schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
+		queue_delayed_work(mac->wq, &auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
 		auth->retry--;
 		spin_unlock_irqrestore(&mac->lock, flags);
 		if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state))
@@ -242,7 +242,7 @@
 			 * request. */
 			cancel_delayed_work(&aq->work);
 			INIT_DELAYED_WORK(&aq->work, &ieee80211softmac_auth_challenge_response);
-			schedule_delayed_work(&aq->work, 0);
+			queue_delayed_work(mac->wq, &aq->work, 0);
 			spin_unlock_irqrestore(&mac->lock, flags);
 			return 0;
 		case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
@@ -408,6 +408,6 @@
 	ieee80211softmac_deauth_from_net(mac, net);
 
 	/* let's try to re-associate */
-	schedule_delayed_work(&mac->associnfo.work, 0);
+	queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
 	return 0;
 }
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
index b3e33a4..8cef05b 100644
--- a/net/ieee80211/softmac/ieee80211softmac_event.c
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -172,7 +172,7 @@
 				/* User may have subscribed to ANY event, so
 				 * we tell them which event triggered it. */
 				eventptr->event_type = event;
-				schedule_delayed_work(&eventptr->work, 0);
+				queue_delayed_work(mac->wq, &eventptr->work, 0);
 			}
 		}
 }
diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c
index 6398e6e..07505ca 100644
--- a/net/ieee80211/softmac/ieee80211softmac_module.c
+++ b/net/ieee80211/softmac/ieee80211softmac_module.c
@@ -36,8 +36,13 @@
 	dev = alloc_ieee80211(sizeof(*softmac) + sizeof_priv);
 	if (!dev)
 		return NULL;
-
 	softmac = ieee80211_priv(dev);
+	softmac->wq = create_freezeable_workqueue("softmac");
+	if (!softmac->wq) {
+		free_ieee80211(dev);
+		return NULL;
+	}
+
 	softmac->dev = dev;
 	softmac->ieee = netdev_priv(dev);
 	spin_lock_init(&softmac->lock);
@@ -105,7 +110,7 @@
 		cancel_delayed_work(&eventptr->work);
 
 	spin_unlock_irqrestore(&sm->lock, flags);
-	flush_scheduled_work();
+	flush_workqueue(sm->wq);
 
 	/* now we should be save and no longer need locking... */
 	spin_lock_irqsave(&sm->lock, flags);
@@ -139,6 +144,7 @@
 	ieee80211softmac_clear_pending_work(sm);
 	kfree(sm->scaninfo);
 	kfree(sm->wpa.IE);
+	destroy_workqueue(sm->wq);
 	free_ieee80211(dev);
 }
 EXPORT_SYMBOL_GPL(free_ieee80211softmac);
diff --git a/net/ieee80211/softmac/ieee80211softmac_scan.c b/net/ieee80211/softmac/ieee80211softmac_scan.c
index abea364..bfab8d7 100644
--- a/net/ieee80211/softmac/ieee80211softmac_scan.c
+++ b/net/ieee80211/softmac/ieee80211softmac_scan.c
@@ -123,7 +123,7 @@
 				spin_unlock_irqrestore(&sm->lock, flags);
 				break;
 			}
-			schedule_delayed_work(&si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
+			queue_delayed_work(sm->wq, &si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
 			spin_unlock_irqrestore(&sm->lock, flags);
 			return;
 		} else {
@@ -190,7 +190,7 @@
 	sm->scaninfo->started = 1;
 	sm->scaninfo->stop = 0;
 	INIT_COMPLETION(sm->scaninfo->finished);
-	schedule_delayed_work(&sm->scaninfo->softmac_scan, 0);
+	queue_delayed_work(sm->wq, &sm->scaninfo->softmac_scan, 0);
 	spin_unlock_irqrestore(&sm->lock, flags);
 	return 0;
 }
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
index 8e8ad08..ac36767 100644
--- a/net/ieee80211/softmac/ieee80211softmac_wx.c
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -91,7 +91,7 @@
 		/* We must unlock to avoid deadlocks with the assoc workqueue
 		 * on the associnfo.mutex */
 		mutex_unlock(&sm->associnfo.mutex);
-		flush_scheduled_work();
+		flush_workqueue(sm->wq);
 		/* Avoid race! Check assoc status again. Maybe someone started an
 		 * association while we flushed. */
 		goto check_assoc_again;
@@ -114,7 +114,7 @@
 
 	sm->associnfo.associating = 1;
 	/* queue lower level code to do work (if necessary) */
-	schedule_delayed_work(&sm->associnfo.work, 0);
+	queue_delayed_work(sm->wq, &sm->associnfo.work, 0);
 
 	mutex_unlock(&sm->associnfo.mutex);
 
@@ -349,7 +349,7 @@
 		/* force reassociation */
 		mac->associnfo.bssvalid = 0;
 		if (mac->associnfo.associated)
-			schedule_delayed_work(&mac->associnfo.work, 0);
+			queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
 	} else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
 		/* the bssid we have is no longer fixed */
 		mac->associnfo.bssfixed = 0;
@@ -366,7 +366,7 @@
 		/* tell the other code that this bssid should be used no matter what */
 		mac->associnfo.bssfixed = 1;
 		/* queue associate if new bssid or (old one again and not associated) */
-		schedule_delayed_work(&mac->associnfo.work, 0);
+		queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
 	}
 
  out: