aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-01-22 16:32:46 -0500
committerJohannes Berg <johannes.berg@intel.com>2015-01-23 04:54:22 -0500
commit4afaff176a968457df18eeebc1aad910b6154761 (patch)
treeb72d595e376af86d6e3217a5d608fb690864cec2 /net/mac80211
parent14f2ae83d07a0a0d499d4760dd4a1bffd310b6ae (diff)
mac80211: avoid races related to suspend flow
When we go to suspend, there is complex set of states that avoids races. The quiescing variable is set whlie __ieee80211_suspend is running. Then suspended is set. The code makes sure there is no window without any of these flags. The problem is that workers can still be enqueued while we are quiescing. This leads to situations where the driver is already suspending and other flows like disassociation are handled by a worker. To fix this, we need to check quiescing and suspended flags in the worker itself and not only before enqueueing it. I also add here extensive documentation to ease the understanding of these complex issues. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/ieee80211_i.h30
-rw-r--r--net/mac80211/iface.c7
-rw-r--r--net/mac80211/util.c15
3 files changed, 40 insertions, 12 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 6e1b184183fb..bdb3e3bc7503 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1886,6 +1886,36 @@ void __ieee80211_flush_queues(struct ieee80211_local *local,
1886 struct ieee80211_sub_if_data *sdata, 1886 struct ieee80211_sub_if_data *sdata,
1887 unsigned int queues, bool drop); 1887 unsigned int queues, bool drop);
1888 1888
1889static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
1890{
1891 /*
1892 * If quiescing is set, we are racing with __ieee80211_suspend.
1893 * __ieee80211_suspend flushes the workers after setting quiescing,
1894 * and we check quiescing / suspended before enqueing new workers.
1895 * We should abort the worker to avoid the races below.
1896 */
1897 if (local->quiescing)
1898 return false;
1899
1900 /*
1901 * We might already be suspended if the following scenario occurs:
1902 * __ieee80211_suspend Control path
1903 *
1904 * if (local->quiescing)
1905 * return;
1906 * local->quiescing = true;
1907 * flush_workqueue();
1908 * queue_work(...);
1909 * local->suspended = true;
1910 * local->quiescing = false;
1911 * worker starts running...
1912 */
1913 if (local->suspended)
1914 return false;
1915
1916 return true;
1917}
1918
1889void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 1919void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1890 u16 transaction, u16 auth_alg, u16 status, 1920 u16 transaction, u16 auth_alg, u16 status,
1891 const u8 *extra, size_t extra_len, const u8 *bssid, 1921 const u8 *extra, size_t extra_len, const u8 *bssid,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 4371c123a95e..81a27516813e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1170,12 +1170,7 @@ static void ieee80211_iface_work(struct work_struct *work)
1170 if (local->scanning) 1170 if (local->scanning)
1171 return; 1171 return;
1172 1172
1173 /* 1173 if (!ieee80211_can_run_worker(local))
1174 * ieee80211_queue_work() should have picked up most cases,
1175 * here we'll pick the rest.
1176 */
1177 if (WARN(local->suspended,
1178 "interface work scheduled while going to suspend\n"))
1179 return; 1174 return;
1180 1175
1181 /* first process frames */ 1176 /* first process frames */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index fbd37d43dfce..c65d03f3c167 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -744,16 +744,19 @@ EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
744 744
745/* 745/*
746 * Nothing should have been stuffed into the workqueue during 746 * Nothing should have been stuffed into the workqueue during
747 * the suspend->resume cycle. If this WARN is seen then there 747 * the suspend->resume cycle. Since we can't check each caller
748 * is a bug with either the driver suspend or something in 748 * of this function if we are already quiescing / suspended,
749 * mac80211 stuffing into the workqueue which we haven't yet 749 * check here and don't WARN since this can actually happen when
750 * cleared during mac80211's suspend cycle. 750 * the rx path (for example) is racing against __ieee80211_suspend
751 * and suspending / quiescing was set after the rx path checked
752 * them.
751 */ 753 */
752static bool ieee80211_can_queue_work(struct ieee80211_local *local) 754static bool ieee80211_can_queue_work(struct ieee80211_local *local)
753{ 755{
754 if (WARN(local->suspended && !local->resuming, 756 if (local->quiescing || (local->suspended && !local->resuming)) {
755 "queueing ieee80211 work while going to suspend\n")) 757 pr_warn("queueing ieee80211 work while going to suspend\n");
756 return false; 758 return false;
759 }
757 760
758 return true; 761 return true;
759} 762}