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:
Johannes Berg
2009-08-17 16:16:53 +02:00
committed by John W. Linville
parent ea416a793d
commit 3ac64beecd
28 changed files with 298 additions and 170 deletions

View File

@@ -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,

View File

@@ -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
)
);

View File

@@ -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;

View File

@@ -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);
}
/*

View File

@@ -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);

View File

@@ -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,

View File

@@ -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) {