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>
This commit is contained in:
committed by
John W. Linville
parent
ea416a793d
commit
3ac64beecd
@@ -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);
|
||||
|
Reference in New Issue
Block a user