summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-08-17 16:16:53 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-08-20 11:35:58 -0400
commit3ac64beecd27400d12cc7afb4108eef26c499f6a (patch)
treeda0220085f68e30fe61ba9b8833dc6311d6dc25e /net/mac80211
parentea416a793d2b611f22b42ba094fd2e5bd30fff43 (diff)
downloadlinux-3ac64beecd27400d12cc7afb4108eef26c499f6a.tar.bz2
mac80211: allow configure_filter callback to sleep
Over time, a whole bunch of drivers have come up with their own scheme to delay the configure_filter operation to a workqueue. To be able to simplify things, allow configure_filter to sleep, and add a new prepare_multicast callback that drivers that need the multicast address list implement. This new callback must be atomic, but most drivers either don't care or just calculate a hash which can be done atomically and then uploaded to the hardware non-atomically. A cursory look suggests that at76c50x-usb, ar9170, mwl8k (which is actually very broken now), rt2x00, wl1251, wl1271 and zd1211 should make use of this new capability. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/driver-ops.h24
-rw-r--r--net/mac80211/driver-trace.h36
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/iface.c15
-rw-r--r--net/mac80211/main.c24
-rw-r--r--net/mac80211/scan.c16
-rw-r--r--net/mac80211/util.c2
7 files changed, 77 insertions, 43 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 4100c361a99d..d231c9323ad1 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -55,16 +55,32 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
trace_drv_bss_info_changed(local, vif, info, changed);
}
+static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
+ int mc_count,
+ struct dev_addr_list *mc_list)
+{
+ u64 ret = 0;
+
+ if (local->ops->prepare_multicast)
+ ret = local->ops->prepare_multicast(&local->hw, mc_count,
+ mc_list);
+
+ trace_drv_prepare_multicast(local, mc_count, ret);
+
+ return ret;
+}
+
static inline void drv_configure_filter(struct ieee80211_local *local,
unsigned int changed_flags,
unsigned int *total_flags,
- int mc_count,
- struct dev_addr_list *mc_list)
+ u64 multicast)
{
+ might_sleep();
+
local->ops->configure_filter(&local->hw, changed_flags, total_flags,
- mc_count, mc_list);
+ multicast);
trace_drv_configure_filter(local, changed_flags, total_flags,
- mc_count);
+ multicast);
}
static inline int drv_set_tim(struct ieee80211_local *local,
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 5a10da2d70fd..37b9051afcf3 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -191,31 +191,55 @@ TRACE_EVENT(drv_bss_info_changed,
)
);
+TRACE_EVENT(drv_prepare_multicast,
+ TP_PROTO(struct ieee80211_local *local, int mc_count, u64 ret),
+
+ TP_ARGS(local, mc_count, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, mc_count)
+ __field(u64, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->mc_count = mc_count;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " prepare mc (%d): %llx",
+ LOCAL_PR_ARG, __entry->mc_count,
+ (unsigned long long) __entry->ret
+ )
+);
+
TRACE_EVENT(drv_configure_filter,
TP_PROTO(struct ieee80211_local *local,
unsigned int changed_flags,
unsigned int *total_flags,
- int mc_count),
+ u64 multicast),
- TP_ARGS(local, changed_flags, total_flags, mc_count),
+ TP_ARGS(local, changed_flags, total_flags, multicast),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(unsigned int, changed)
__field(unsigned int, total)
- __field(int, mc)
+ __field(u64, multicast)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->changed = changed_flags;
__entry->total = *total_flags;
- __entry->mc = mc_count;
+ __entry->multicast = multicast;
),
TP_printk(
- LOCAL_PR_FMT " changed:%#x total:%#x mc:%d",
- LOCAL_PR_ARG, __entry->changed, __entry->total, __entry->mc
+ LOCAL_PR_FMT " changed:%#x total:%#x",
+ LOCAL_PR_ARG, __entry->changed, __entry->total
)
);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a6abc7dfd903..a07f01736a91 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -636,6 +636,9 @@ struct ieee80211_local {
/* protects the aggregated multicast list and filter calls */
spinlock_t filter_lock;
+ /* used for uploading changed mc list */
+ struct work_struct reconfig_filter;
+
/* aggregated multicast list */
struct dev_addr_list *mc_list;
int mc_count;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index e8fb03b91a44..b161301056df 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -227,9 +227,7 @@ static int ieee80211_open(struct net_device *dev)
if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
local->fif_other_bss++;
- spin_lock_bh(&local->filter_lock);
ieee80211_configure_filter(local);
- spin_unlock_bh(&local->filter_lock);
break;
default:
conf.vif = &sdata->vif;
@@ -241,17 +239,13 @@ static int ieee80211_open(struct net_device *dev)
if (ieee80211_vif_is_mesh(&sdata->vif)) {
local->fif_other_bss++;
- spin_lock_bh(&local->filter_lock);
ieee80211_configure_filter(local);
- spin_unlock_bh(&local->filter_lock);
ieee80211_start_mesh(sdata);
} else if (sdata->vif.type == NL80211_IFTYPE_AP) {
local->fif_pspoll++;
- spin_lock_bh(&local->filter_lock);
ieee80211_configure_filter(local);
- spin_unlock_bh(&local->filter_lock);
}
changed |= ieee80211_reset_erp_info(sdata);
@@ -404,10 +398,11 @@ static int ieee80211_stop(struct net_device *dev)
spin_lock_bh(&local->filter_lock);
__dev_addr_unsync(&local->mc_list, &local->mc_count,
&dev->mc_list, &dev->mc_count);
- ieee80211_configure_filter(local);
spin_unlock_bh(&local->filter_lock);
netif_addr_unlock_bh(dev);
+ ieee80211_configure_filter(local);
+
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -458,9 +453,7 @@ static int ieee80211_stop(struct net_device *dev)
if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
local->fif_other_bss--;
- spin_lock_bh(&local->filter_lock);
ieee80211_configure_filter(local);
- spin_unlock_bh(&local->filter_lock);
break;
case NL80211_IFTYPE_STATION:
del_timer_sync(&sdata->u.mgd.chswitch_timer);
@@ -503,9 +496,7 @@ static int ieee80211_stop(struct net_device *dev)
local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
- spin_lock_bh(&local->filter_lock);
ieee80211_configure_filter(local);
- spin_unlock_bh(&local->filter_lock);
ieee80211_stop_mesh(sdata);
}
@@ -622,8 +613,8 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
spin_lock_bh(&local->filter_lock);
__dev_addr_sync(&local->mc_list, &local->mc_count,
&dev->mc_list, &dev->mc_count);
- ieee80211_configure_filter(local);
spin_unlock_bh(&local->filter_lock);
+ ieee80211_queue_work(&local->hw, &local->reconfig_filter);
}
/*
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index b03fd84777fa..05f923575fee 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr {
} __attribute__ ((packed));
-/* must be called under mdev tx lock */
void ieee80211_configure_filter(struct ieee80211_local *local)
{
+ u64 mc;
unsigned int changed_flags;
unsigned int new_flags = 0;
@@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
if (atomic_read(&local->iff_allmultis))
new_flags |= FIF_ALLMULTI;
- if (local->monitors)
+ if (local->monitors || local->scanning)
new_flags |= FIF_BCN_PRBRESP_PROMISC;
if (local->fif_fcsfail)
@@ -80,20 +80,30 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
if (local->fif_pspoll)
new_flags |= FIF_PSPOLL;
+ spin_lock_bh(&local->filter_lock);
changed_flags = local->filter_flags ^ new_flags;
+ mc = drv_prepare_multicast(local, local->mc_count, local->mc_list);
+ spin_unlock_bh(&local->filter_lock);
+
/* be a bit nasty */
new_flags |= (1<<31);
- drv_configure_filter(local, changed_flags, &new_flags,
- local->mc_count,
- local->mc_list);
+ drv_configure_filter(local, changed_flags, &new_flags, mc);
WARN_ON(new_flags & (1<<31));
local->filter_flags = new_flags & ~(1<<31);
}
+static void ieee80211_reconfig_filter(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, reconfig_filter);
+
+ ieee80211_configure_filter(local);
+}
+
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
struct ieee80211_channel *chan, *scan_chan;
@@ -692,6 +702,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
INIT_WORK(&local->restart_work, ieee80211_restart_work);
+ INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
+
INIT_WORK(&local->dynamic_ps_enable_work,
ieee80211_dynamic_ps_enable_work);
INIT_WORK(&local->dynamic_ps_disable_work,
@@ -946,6 +958,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
rtnl_unlock();
+ cancel_work_sync(&local->reconfig_filter);
+
ieee80211_clear_tx_pending(local);
sta_info_stop(local);
rate_control_deinitialize(local);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index e091cbc3434f..1e04be6b9129 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -292,13 +292,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (was_hw_scan)
goto done;
- spin_lock_bh(&local->filter_lock);
- local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mc_count,
- local->mc_list);
- spin_unlock_bh(&local->filter_lock);
+ ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
@@ -376,13 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
- spin_lock_bh(&local->filter_lock);
- local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mc_count,
- local->mc_list);
- spin_unlock_bh(&local->filter_lock);
+ ieee80211_configure_filter(local);
/* TODO: start scan as soon as all nullfunc frames are ACKed */
ieee80211_queue_delayed_work(&local->hw,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e55d57f559ec..5eb306377c63 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1076,9 +1076,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* reconfigure hardware */
ieee80211_hw_config(local, ~0);
- spin_lock_bh(&local->filter_lock);
ieee80211_configure_filter(local);
- spin_unlock_bh(&local->filter_lock);
/* Finally also reconfigure all the BSS information */
list_for_each_entry(sdata, &local->interfaces, list) {