summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-06-11 17:09:41 +0200
committerJohannes Berg <johannes.berg@intel.com>2012-06-13 10:11:31 +0200
commite979e33c3972044e1be5e46552a02c3b9c0bc7a7 (patch)
treed34aea2a3a922b13cb85f5cf4077cad105cd50b2
parent535588e61a007416f46cf08b4ccb6cc73b3f6fb0 (diff)
downloadlinux-e979e33c3972044e1be5e46552a02c3b9c0bc7a7.tar.bz2
mac80211: allow cancelling dependent ROCs
In my redesign of remain-on-channel I forgot that an item could be cancelled when it's a dependent item that is part of another item. Allow cancelling such items by removing them from the dependents list. Note that when we cancel the main item, all its dependents are also cancelled. It would be possible to not do that, but would need tricks to promote an item from dependent to top-level and is tricky in the HW ROC case. Reported-by: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/mac80211/cfg.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 93d203cf8c12..9a974579ba89 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2305,6 +2305,21 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
mutex_lock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ struct ieee80211_roc_work *dep, *tmp2;
+
+ list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
+ if (!mgmt_tx && (unsigned long)dep != cookie)
+ continue;
+ else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
+ continue;
+ /* found dependent item -- just remove it */
+ list_del(&dep->list);
+ mutex_unlock(&local->mtx);
+
+ ieee80211_roc_notify_destroy(dep);
+ return 0;
+ }
+
if (!mgmt_tx && (unsigned long)roc != cookie)
continue;
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
@@ -2319,6 +2334,13 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
return -ENOENT;
}
+ /*
+ * We found the item to cancel, so do that. Note that it
+ * may have dependents, which we also cancel (and send
+ * the expired signal for.) Not doing so would be quite
+ * tricky here, but we may need to fix it later.
+ */
+
if (local->ops->remain_on_channel) {
if (found->started) {
ret = drv_cancel_remain_on_channel(local);