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
@@ -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 void drv_configure_filter(struct ieee80211_local *local,
|
||||
unsigned int changed_flags,
|
||||
unsigned int *total_flags,
|
||||
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,
|
||||
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,
|
||||
|
@@ -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
|
||||
)
|
||||
);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user