From fa56dddd6720c8d4b9fa4c942377d2a019cf3708 Mon Sep 17 00:00:00 2001 From: Alina Friedrichsen Date: Tue, 10 Mar 2009 00:49:46 +0100 Subject: mac80211: ieee80211_ibss_commit() cleanup Don't call ieee80211_sta_find_ibss() directly, like it's done in STA mode, so that the commit() call is more harmless respectively has less site-effects. Signed-off-by: Alina Friedrichsen Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index f4becc12904..3201e1f9636 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -812,8 +812,9 @@ int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata) ifibss->ibss_join_req = jiffies; ifibss->state = IEEE80211_IBSS_MLME_SEARCH; + set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); - return ieee80211_sta_find_ibss(sdata); + return 0; } int ieee80211_ibss_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len) -- cgit v1.2.3 From 633e24ed95b6c87b42f201ecb65c12a75a5a7eef Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Thu, 12 Mar 2009 09:20:40 -0700 Subject: cfg80211/nl80211: remove usage of CONFIG_NL80211 The scan capability added to cfg80211/nl80211 introduced a dependency on nl80211 by cfg80211. We can thus no longer have just cfg80211 without nl80211. Specifically, cfg80211_scan_done() calls nl80211_send_scan_aborted() or nl80211_send_scan_done(). Now we remove the option for user to select nl80211. It will always be compiled if user selects cfg80211. Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- net/wireless/Kconfig | 13 ------------- net/wireless/Makefile | 3 +-- net/wireless/nl80211.h | 26 -------------------------- 3 files changed, 1 insertion(+), 41 deletions(-) (limited to 'net') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 092ae6faccc..d1d18f34d27 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -10,19 +10,6 @@ config CFG80211_REG_DEBUG If unsure, say N. -config NL80211 - bool "nl80211 new netlink interface support" - depends on CFG80211 - default y - ---help--- - This option turns on the new netlink interface - (nl80211) support in cfg80211. - - If =n, drivers using mac80211 will be configured via - wireless extension support provided by that subsystem. - - If unsure, say Y. - config WIRELESS_OLD_REGULATORY bool "Old wireless static regulatory definitions" default y diff --git a/net/wireless/Makefile b/net/wireless/Makefile index dad43c24f69..c157b4d8014 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,8 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o -cfg80211-$(CONFIG_NL80211) += nl80211.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index e65a3c38c52..5b5fe1339de 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -3,7 +3,6 @@ #include "core.h" -#ifdef CONFIG_NL80211 extern int nl80211_init(void); extern void nl80211_exit(void); extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); @@ -12,30 +11,5 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); extern void nl80211_send_reg_change_event(struct regulatory_request *request); -#else -static inline int nl80211_init(void) -{ - return 0; -} -static inline void nl80211_exit(void) -{ -} -static inline void nl80211_notify_dev_rename( - struct cfg80211_registered_device *rdev) -{ -} -static inline void -nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{} -static inline void nl80211_send_scan_aborted( - struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{} -static inline void -nl80211_send_reg_change_event(struct regulatory_request *request) -{ -} -#endif /* CONFIG_NL80211 */ #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3 From 176be728ee7d32cfd33702d82c0733e51f66ab5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 23:49:28 +0100 Subject: mac80211: remove ieee80211_num_regular_queues This inline is useless and actually makes the code _longer_ rather than shorter. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 2 +- net/mac80211/tx.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 841b8450b3d..aaf7793583a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1834,7 +1834,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->flags |= IEEE80211_STA_CREATE_IBSS | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL; - if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4) + if (sdata->local->hw.queues >= 4) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 457238a2f3f..038460b0a48 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1666,8 +1666,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, } /* receiver and we are QoS enabled, use a QoS type frame */ - if (sta_flags & WLAN_STA_WME && - ieee80211_num_regular_queues(&local->hw) >= 4) { + if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } @@ -1802,7 +1801,7 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) int i, j; struct ieee80211_tx_stored_packet *store; - for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) { + for (i = 0; i < local->hw.queues; i++) { if (!test_bit(i, local->queues_pending)) continue; store = &local->pending_packet[i]; @@ -1827,7 +1826,7 @@ void ieee80211_tx_pending(unsigned long data) int i, ret; netif_tx_lock_bh(dev); - for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) { + for (i = 0; i < local->hw.queues; i++) { /* Check that this queue is ok */ if (__netif_subqueue_stopped(local->mdev, i) && !test_bit(i, local->queues_pending_run)) -- cgit v1.2.3 From 11432379fd2a3854a3408424d8dcd99afd811573 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 12 Mar 2009 14:04:34 +0100 Subject: mac80211: start pending scan after probe/auth/assoc timed out If a scan is queued in STA mode while the interface is in state direct probe, authenticate or associate the scan is delayed until the interface enters disabled or associated state. But in case of direct probe-, authentication- or association- timeout sta_work will not be scheduled anymore (without external trigger) and thus the pending scan is not executed and prevents a new scan from being triggered (-EBUSY). Fix this by queueing the sta work again after direct probe-, authentication- and association- timeout. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aaf7793583a..a55879663b3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -682,6 +682,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; ifmgd->direct_probe_tries++; if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -697,6 +698,13 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); + + /* + * We might have a pending scan which had no chance to run yet + * due to state == IEEE80211_STA_MLME_DIRECT_PROBE. + * Hence, queue the STAs work again + */ + queue_work(local->hw.workqueue, &ifmgd->work); return; } @@ -721,6 +729,7 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; ifmgd->auth_tries++; if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -732,6 +741,13 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); + + /* + * We might have a pending scan which had no chance to run yet + * due to state == IEEE80211_STA_MLME_AUTHENTICATE. + * Hence, queue the STAs work again + */ + queue_work(local->hw.workqueue, &ifmgd->work); return; } @@ -878,6 +894,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; ifmgd->assoc_tries++; if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { @@ -889,6 +906,12 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); + /* + * We might have a pending scan which had no chance to run yet + * due to state == IEEE80211_STA_MLME_ASSOCIATE. + * Hence, queue the STAs work again + */ + queue_work(local->hw.workqueue, &ifmgd->work); return; } -- cgit v1.2.3 From b5bde374f0f61f5d97114d400ade8fc96bf6f10d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 11:19:45 +0100 Subject: mac80211: fix warnings in ieee80211_if_config The last warning can never trigger, and the explicit AP_VLAN check is pointless if we move the config_interface check down, in practice config_interface is required anyway. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f38db4d37e5..dac68d476bf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -161,12 +161,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) if (WARN_ON(!netif_running(sdata->dev))) return 0; - if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) - return -EINVAL; - - if (!local->ops->config_interface) - return 0; - memset(&conf, 0, sizeof(conf)); if (sdata->vif.type == NL80211_IFTYPE_STATION) @@ -183,6 +177,9 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) return -EINVAL; } + if (!local->ops->config_interface) + return 0; + switch (sdata->vif.type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: @@ -224,9 +221,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) } } - if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID))) - return -EINVAL; - conf.changed = changed; return local->ops->config_interface(local_to_hw(local), -- cgit v1.2.3 From 25420604c8967ff24f087dd7b9cd4b278567d39a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 11:43:36 +0100 Subject: mac80211: stop queues across suspend/resume Even though userland probably cannot submit packets, there might still be some coming, and that's no good when the driver doesn't expect them. Stop the queues across suspend/resume. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/pm.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fbb91f1aebb..ad12c2a03a9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -598,6 +598,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_PS, IEEE80211_QUEUE_STOP_REASON_CSA, IEEE80211_QUEUE_STOP_REASON_AGGREGATION, + IEEE80211_QUEUE_STOP_REASON_SUSPEND, }; struct ieee80211_master_priv { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 44525f51707..c923ceb089a 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -11,6 +11,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) struct ieee80211_if_init_conf conf; struct sta_info *sta; + ieee80211_stop_queues_by_reason(hw, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); + flush_workqueue(local->hw.workqueue); /* disable keys */ @@ -113,5 +116,8 @@ int __ieee80211_resume(struct ieee80211_hw *hw) ieee80211_configure_filter(local); netif_addr_unlock_bh(local->mdev); + ieee80211_wake_queues_by_reason(hw, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); + return 0; } -- cgit v1.2.3 From aae89831df03e5282a8f5c0ee46432cfb677fc5c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 12:52:10 +0100 Subject: wireless: radiotap updates Radiotap was updated to include a "bad PLCP" flag and standardise the "bad FCS" flag in the "flags" rather than "RX flags" field, this patch updates Linux to that standard. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/rx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 66f7ecf51b9..fcc0a599579 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -142,6 +142,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_FLAGS */ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) *pos |= IEEE80211_RADIOTAP_F_FCS; + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) + *pos |= IEEE80211_RADIOTAP_F_BADFCS; if (status->flag & RX_FLAG_SHORTPRE) *pos |= IEEE80211_RADIOTAP_F_SHORTPRE; pos++; @@ -204,9 +206,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* ensure 2 byte alignment for the 2 byte field as required */ if ((pos - (unsigned char *)rthdr) & 1) pos++; - /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */ - if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) - *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS); + if (status->flag & RX_FLAG_FAILED_PLCP_CRC) + *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADPLCP); pos += 2; } -- cgit v1.2.3 From ec30415f7935f0ff92f93a4ac87233ca3007a78a Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 13 Mar 2009 20:26:52 +0530 Subject: mac80211: Populate HT limitation with TKIP/WEP to the handler for SIOCSIWENCODE too Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- net/mac80211/wext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 935c63ed3df..e55d2834764 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -630,7 +630,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev, struct ieee80211_sub_if_data *sdata; int idx, i, alg = ALG_WEP; u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - int remove = 0; + int remove = 0, ret; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -656,11 +656,20 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev, return 0; } - return ieee80211_set_encryption( + ret = ieee80211_set_encryption( sdata, bcaddr, idx, alg, remove, !sdata->default_key, keybuf, erq->length); + + if (!ret) { + if (remove) + sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED; + else + sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED; + } + + return ret; } -- cgit v1.2.3 From 8fdc621dc743b87879ccf0177969864b09388d9a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 Mar 2009 09:34:01 +0100 Subject: nl80211: export supported commands This makes nl80211 export the supported commands (command groups) per wiphy so userspace has an idea what it can do -- this will be required reading for userspace when we introduce auth/assoc /or/ connect for older hardware that cannot separate auth and assoc. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ab9d8f14e15..58ee1b1aff8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -131,6 +131,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_rates, *nl_rate; struct nlattr *nl_modes; + struct nlattr *nl_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; struct ieee80211_rate *rate; @@ -242,6 +243,32 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } nla_nest_end(msg, nl_bands); + nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); + if (!nl_cmds) + goto nla_put_failure; + + i = 0; +#define CMD(op, n) \ + do { \ + if (dev->ops->op) { \ + i++; \ + NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \ + } \ + } while (0) + + CMD(add_virtual_intf, NEW_INTERFACE); + CMD(change_virtual_intf, SET_INTERFACE); + CMD(add_key, NEW_KEY); + CMD(add_beacon, NEW_BEACON); + CMD(add_station, NEW_STATION); + CMD(add_mpath, NEW_MPATH); + CMD(set_mesh_params, SET_MESH_PARAMS); + CMD(change_bss, SET_BSS); + CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); + +#undef CMD + nla_nest_end(msg, nl_cmds); + return genlmsg_end(msg, hdr); nla_put_failure: -- cgit v1.2.3 From 7f0216a49bea717b9606b81c60f2f0b6152123eb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 Mar 2009 09:42:49 +0100 Subject: mac80211: acquire sta_lock for station suspend/resume To avoid concurrent manipulations of the sta list (which shouldn't be possible at this point, but anyway) we need to hold the sta_lock around iterating the list. At the same time, we do not need to iterate the list at all if the driver doesn't want to be notified. Signed-off-by: Johannes Berg Acked-by: Bob Copeland Signed-off-by: John W. Linville --- net/mac80211/pm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index c923ceb089a..ef7be1ce2c8 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -10,6 +10,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) struct ieee80211_sub_if_data *sdata; struct ieee80211_if_init_conf conf; struct sta_info *sta; + unsigned long flags; ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); @@ -21,9 +22,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ieee80211_disable_keys(sdata); /* remove STAs */ - list_for_each_entry(sta, &local->sta_list, list) { - - if (local->ops->sta_notify) { + if (local->ops->sta_notify) { + spin_lock_irqsave(&local->sta_lock, flags); + list_for_each_entry(sta, &local->sta_list, list) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, @@ -32,11 +33,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) local->ops->sta_notify(hw, &sdata->vif, STA_NOTIFY_REMOVE, &sta->sta); } + spin_unlock_irqrestore(&local->sta_lock, flags); } /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && netif_running(sdata->dev)) { @@ -64,6 +65,7 @@ int __ieee80211_resume(struct ieee80211_hw *hw) struct ieee80211_sub_if_data *sdata; struct ieee80211_if_init_conf conf; struct sta_info *sta; + unsigned long flags; int res; /* restart hardware */ @@ -75,7 +77,6 @@ int __ieee80211_resume(struct ieee80211_hw *hw) /* add interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && netif_running(sdata->dev)) { @@ -87,9 +88,9 @@ int __ieee80211_resume(struct ieee80211_hw *hw) } /* add STAs back */ - list_for_each_entry(sta, &local->sta_list, list) { - - if (local->ops->sta_notify) { + if (local->ops->sta_notify) { + spin_lock_irqsave(&local->sta_lock, flags); + list_for_each_entry(sta, &local->sta_list, list) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, @@ -98,6 +99,7 @@ int __ieee80211_resume(struct ieee80211_hw *hw) local->ops->sta_notify(hw, &sdata->vif, STA_NOTIFY_ADD, &sta->sta); } + spin_unlock_irqrestore(&local->sta_lock, flags); } /* add back keys */ -- cgit v1.2.3 From 722f069a6dc95d7c6c2cdfbe3413899a3b768f9c Mon Sep 17 00:00:00 2001 From: Sujith Date: Tue, 17 Mar 2009 08:50:06 +0530 Subject: mac80211: Tear down aggregation sessions for suspend/resume When the driver has been notified with a STA_REMOVE, it tears down the internal ADDBA state. On resume, trying to initiate aggregation would fail because mac80211 has not cleared the operational state for that . This can be fixed by tearing down the existing sessions on a suspend. Also, the driver can initiate a new BA session when suspend is in progress. This is fixed by marking the station as being in suspend state and denying ADDBA requests for such STAs. Signed-off-by: Sujith Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-rx.c | 8 ++++++++ net/mac80211/agg-tx.c | 9 +++++++++ net/mac80211/pm.c | 25 +++++++++++++++++++++++++ net/mac80211/sta_info.h | 3 +++ 4 files changed, 45 insertions(+) (limited to 'net') diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index a95affc9462..07656d830bc 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -197,6 +197,14 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; + if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Suspend in progress. " + "Denying ADDBA request\n"); +#endif + goto end_no_lock; + } + /* sanity check for incoming parameters: * check if configuration can support the BA policy * and if buffer size does not exceeds max value */ diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 1df116d4d6e..e5776ef1717 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -257,6 +257,15 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto unlock; } + if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Suspend in progress. " + "Denying BA session request\n"); +#endif + ret = -EINVAL; + goto unlock; + } + spin_lock_bh(&sta->lock); sdata = sta->sdata; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index ef7be1ce2c8..1e6152ac677 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -21,6 +21,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) list_for_each_entry(sdata, &local->interfaces, list) ieee80211_disable_keys(sdata); + /* Tear down aggregation sessions */ + + rcu_read_lock(); + + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { + list_for_each_entry_rcu(sta, &local->sta_list, list) { + set_sta_flags(sta, WLAN_STA_SUSPEND); + ieee80211_sta_tear_down_BA_sessions(sta); + } + } + + rcu_read_unlock(); + /* remove STAs */ if (local->ops->sta_notify) { spin_lock_irqsave(&local->sta_lock, flags); @@ -102,6 +115,18 @@ int __ieee80211_resume(struct ieee80211_hw *hw) spin_unlock_irqrestore(&local->sta_lock, flags); } + /* Clear Suspend state so that ADDBA requests can be processed */ + + rcu_read_lock(); + + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { + list_for_each_entry_rcu(sta, &local->sta_list, list) { + clear_sta_flags(sta, WLAN_STA_SUSPEND); + } + } + + rcu_read_unlock(); + /* add back keys */ list_for_each_entry(sdata, &local->interfaces, list) if (netif_running(sdata->dev)) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 1f45573c580..5b223b216e5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -35,6 +35,8 @@ * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next * frame to this station is transmitted. * @WLAN_STA_MFP: Management frame protection is used with this STA. + * @WLAN_STA_SUSPEND: Set/cleared during a suspend/resume cycle. + * Used to deny ADDBA requests (both TX and RX). */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -48,6 +50,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_PSPOLL = 1<<8, WLAN_STA_CLEAR_PS_FILT = 1<<9, WLAN_STA_MFP = 1<<10, + WLAN_STA_SUSPEND = 1<<11 }; #define STA_TID_NUM 16 -- cgit v1.2.3 From 3b85875a252dbbd95c2e04d73639719a0a79634e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 09:55:09 +0100 Subject: nl80211: rework locking When I added scanning to cfg80211, we got a lock dependency like this: rtnl --> cfg80211_mtx nl80211, on the other hand, has the reverse lock dependency: cfg80211_mtx --> rtnl which clearly is a bad idea. This patch reworks nl80211 to take these two locks in the other order to fix the possible, and easily triggerable, deadlock. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 271 ++++++++++++++++++++++++++++++------------------- 1 file changed, 166 insertions(+), 105 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 58ee1b1aff8..a3ecf8d7389 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -602,9 +602,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; + ifindex = dev->ifindex; type = dev->ieee80211_ptr->iftype; dev_put(dev); @@ -641,17 +644,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (!err) flags = &_flags; } - rtnl_lock(); + err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type, flags, ¶ms); dev = __dev_get_by_index(&init_net, ifindex); WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); - rtnl_unlock(); - unlock: cfg80211_put_dev(drv); + unlock_rtnl: + rtnl_unlock(); return err; } @@ -674,9 +677,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + rtnl_lock(); + drv = cfg80211_get_dev_from_info(info); - if (IS_ERR(drv)) - return PTR_ERR(drv); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto unlock_rtnl; + } if (!drv->ops->add_virtual_intf || !(drv->wiphy.interface_modes & (1 << type))) { @@ -690,18 +697,17 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); } - rtnl_lock(); err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); err = drv->ops->add_virtual_intf(&drv->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); - rtnl_unlock(); - unlock: cfg80211_put_dev(drv); + unlock_rtnl: + rtnl_unlock(); return err; } @@ -711,9 +717,11 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) int ifindex, err; struct net_device *dev; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; ifindex = dev->ifindex; dev_put(dev); @@ -722,12 +730,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); - rtnl_unlock(); out: cfg80211_put_dev(drv); + unlock_rtnl: + rtnl_unlock(); return err; } @@ -779,9 +787,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->get_key) { err = -EOPNOTSUPP; @@ -809,10 +819,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (mac_addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - rtnl_lock(); err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr, &cookie, get_key_callback); - rtnl_unlock(); if (err) goto out; @@ -830,6 +838,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -858,9 +869,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) return -EINVAL; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) func = drv->ops->set_default_key; @@ -872,13 +885,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = func(&drv->wiphy, dev, key_idx); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -948,22 +963,25 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->add_key) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -984,22 +1002,26 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->del_key) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -1013,9 +1035,11 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) struct beacon_parameters params; int haveinfo = 0; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; switch (info->genlhdr->cmd) { case NL80211_CMD_NEW_BEACON: @@ -1076,13 +1100,14 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = call(&drv->wiphy, dev, ¶ms); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -1092,22 +1117,25 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) int err; struct net_device *dev; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->del_beacon) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_beacon(&drv->wiphy, dev); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -1273,14 +1301,18 @@ static int nl80211_dump_station(struct sk_buff *skb, return -EINVAL; } - netdev = dev_get_by_index(&init_net, ifidx); - if (!netdev) - return -ENODEV; + rtnl_lock(); + + netdev = __dev_get_by_index(&init_net, ifidx); + if (!netdev) { + err = -ENODEV; + goto out_rtnl; + } dev = cfg80211_get_dev_from_ifindex(ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); - goto out_put_netdev; + goto out_rtnl; } if (!dev->ops->dump_station) { @@ -1288,15 +1320,13 @@ static int nl80211_dump_station(struct sk_buff *skb, goto out_err; } - rtnl_lock(); - while (1) { err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, mac_addr, &sinfo); if (err == -ENOENT) break; if (err) - goto out_err_rtnl; + goto out_err; if (nl80211_send_station(skb, NETLINK_CB(cb->skb).pid, @@ -1312,12 +1342,10 @@ static int nl80211_dump_station(struct sk_buff *skb, out: cb->args[1] = sta_idx; err = skb->len; - out_err_rtnl: - rtnl_unlock(); out_err: cfg80211_put_dev(dev); - out_put_netdev: - dev_put(netdev); + out_rtnl: + rtnl_unlock(); return err; } @@ -1338,19 +1366,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->get_station) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); - rtnl_unlock(); - if (err) goto out; @@ -1367,10 +1394,12 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); - out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1438,9 +1467,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); if (err) @@ -1451,15 +1482,16 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); - rtnl_unlock(); out: if (params.vlan) dev_put(params.vlan); cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1501,9 +1533,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ¶ms.station_flags)) return -EINVAL; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); if (err) @@ -1514,15 +1548,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); - rtnl_unlock(); out: if (params.vlan) dev_put(params.vlan); cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1536,22 +1571,25 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->del_station) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1632,14 +1670,18 @@ static int nl80211_dump_mpath(struct sk_buff *skb, return -EINVAL; } - netdev = dev_get_by_index(&init_net, ifidx); - if (!netdev) - return -ENODEV; + rtnl_lock(); + + netdev = __dev_get_by_index(&init_net, ifidx); + if (!netdev) { + err = -ENODEV; + goto out_rtnl; + } dev = cfg80211_get_dev_from_ifindex(ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); - goto out_put_netdev; + goto out_rtnl; } if (!dev->ops->dump_mpath) { @@ -1647,15 +1689,13 @@ static int nl80211_dump_mpath(struct sk_buff *skb, goto out_err; } - rtnl_lock(); - while (1) { err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, dst, next_hop, &pinfo); if (err == -ENOENT) break; if (err) - goto out_err_rtnl; + goto out_err; if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, @@ -1670,12 +1710,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, out: cb->args[1] = path_idx; err = skb->len; - out_err_rtnl: - rtnl_unlock(); out_err: cfg80211_put_dev(dev); - out_put_netdev: - dev_put(netdev); + out_rtnl: + rtnl_unlock(); return err; } @@ -1697,19 +1735,18 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->get_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); - rtnl_unlock(); - if (err) goto out; @@ -1726,10 +1763,12 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); - out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1750,22 +1789,25 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->change_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) @@ -1785,22 +1827,25 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->add_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1814,22 +1859,25 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->del_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_mpath(&drv->wiphy, dev, dst); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1862,22 +1910,25 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); } + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->change_bss) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1972,10 +2023,12 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, struct nlattr *pinfoattr; struct sk_buff *msg; + rtnl_lock(); + /* Look up our device */ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->get_mesh_params) { err = -EOPNOTSUPP; @@ -1983,9 +2036,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, } /* Get the mesh params */ - rtnl_lock(); err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); - rtnl_unlock(); if (err) goto out; @@ -2034,13 +2085,16 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = genlmsg_unicast(msg, info->snd_pid); goto out; -nla_put_failure: + nla_put_failure: genlmsg_cancel(msg, hdr); err = -EMSGSIZE; -out: + out: /* Cleanup */ cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -2087,9 +2141,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) parent_attr, nl80211_meshconf_params_policy)) return -EINVAL; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->set_mesh_params) { err = -EOPNOTSUPP; @@ -2136,14 +2192,15 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) nla_get_u16); /* Apply changes */ - rtnl_lock(); err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); - rtnl_unlock(); out: /* cleanup */ cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -2310,19 +2367,22 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); } + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; - if (drv->ops->set_mgmt_extra_ie) { - rtnl_lock(); + if (drv->ops->set_mgmt_extra_ie) err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); - rtnl_unlock(); - } else + else err = -EOPNOTSUPP; cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -2339,9 +2399,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) enum ieee80211_band band; size_t ie_len; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; wiphy = &drv->wiphy; @@ -2350,11 +2412,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); - if (drv->scan_req) { err = -EBUSY; - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { @@ -2362,7 +2422,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) n_channels++; if (!n_channels) { err = -EINVAL; - goto out_unlock; + goto out; } } else { for (band = 0; band < IEEE80211_NUM_BANDS; band++) @@ -2376,7 +2436,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) if (n_ssids > wiphy->max_scan_ssids) { err = -EINVAL; - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_IE]) @@ -2390,7 +2450,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) + ie_len, GFP_KERNEL); if (!request) { err = -ENOMEM; - goto out_unlock; + goto out; } request->channels = (void *)((char *)request + sizeof(*request)); @@ -2461,11 +2521,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) drv->scan_req = NULL; kfree(request); } - out_unlock: - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } -- cgit v1.2.3 From a9a6ffffd05f97e6acbdeafc595e269855829751 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Mar 2009 14:06:44 +0200 Subject: mac80211: don't drop nullfunc frames during software scan ieee80211_tx_h_check_assoc() was dropping everything else than probe requests during software scan. So the nullfunc frame with the power save bit was dropped and AP never received it. This meant that AP never buffered any frames for the station during software scan. Fix this by allowing to transmit both probe request and nullfunc frames during software scan. Tested with stlc45xx. Signed-off-by: Kalle Valo Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/scan.c | 13 +++++++++++++ net/mac80211/tx.c | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 5030a3c8750..46f35dc6acc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -409,6 +409,19 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, return 0; } + /* + * Hardware/driver doesn't support hw_scan, so use software + * scanning instead. First send a nullfunc frame with power save + * bit on so that AP will buffer the frames for us while we are not + * listening, then send probe requests to each channel and wait for + * the responses. After all channels are scanned, tune back to the + * original channel and send a nullfunc frame with power save bit + * off to trigger the AP to send us all the buffered frames. + * + * Note that while local->sw_scanning is true everything else but + * nullfunc frames and probe requests will be dropped in + * ieee80211_tx_h_check_assoc(). + */ local->sw_scanning = true; if (local->ops->sw_scan_start) local->ops->sw_scan_start(local_to_hw(local)); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 038460b0a48..f3f240c6901 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -193,7 +193,19 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) return TX_CONTINUE; if (unlikely(tx->local->sw_scanning) && - !ieee80211_is_probe_req(hdr->frame_control)) + !ieee80211_is_probe_req(hdr->frame_control) && + !ieee80211_is_nullfunc(hdr->frame_control)) + /* + * When software scanning only nullfunc frames (to notify + * the sleep state to the AP) and probe requests (for the + * active scan) are allowed, all other frames should not be + * sent and we should not get here, but if we do + * nonetheless, drop them to avoid sending them + * off-channel. See the link below and + * ieee80211_start_scan() for more. + * + * http://article.gmane.org/gmane.linux.kernel.wireless.general/30089 + */ return TX_DROP; if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) -- cgit v1.2.3 From 4b4698c443c9db62b220c41a1793872d6ebe82e1 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:19 +0200 Subject: mac80211: Fix a typo in assoc vs. reassoc check Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a55879663b3..4c753bb43ba 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -103,7 +103,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) u32 rates = 0; size_t e_ies_len; - if (ifmgd->flags & IEEE80211_IBSS_PREV_BSSID_SET) { + if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { e_ies = sdata->u.mgd.ie_reassocreq; e_ies_len = sdata->u.mgd.ie_reassocreq_len; } else { -- cgit v1.2.3 From a299542e97ec1939fdca7db6d3d82c0aa9bf8b9a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:20 +0200 Subject: mac80211: Fix reassociation by not clearing previous BSSID We must not clear the previous BSSID when roaming to another AP within the same ESS for reassociation to be used properly. It is fine to clear this when the SSID changes, so let's move the code into ieee80211_sta_set_ssid(). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4c753bb43ba..1f49b63d8dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1888,8 +1888,6 @@ int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; - if (ifmgd->ssid_len) ifmgd->flags |= IEEE80211_STA_SSID_SET; else @@ -1908,6 +1906,10 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size ifmgd = &sdata->u.mgd; if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) { + /* + * Do not use reassociation if SSID is changed (different ESS). + */ + ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid)); memcpy(ifmgd->ssid, ssid, len); ifmgd->ssid_len = len; -- cgit v1.2.3 From 6039f6d23fe792d615da5449e9fa1c6b43caacf6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:21 +0200 Subject: nl80211: Event notifications for MLME events Add new nl80211 event notifications (and a new multicast group, "mlme") for informing user space about received and processed Authentication, (Re)Association Response, Deauthentication, and Disassociation frames in station and IBSS modes (i.e., MLME SAP interface primitives MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm, MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11 management frame since we already have the frame in that format and it includes all the needed information. This is the initial step in providing MLME SAP interface for authentication and association with nl80211. In other words, kernel code will act as the MLME and a user space application can control it as the SME. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 9 +++++-- net/wireless/Makefile | 2 +- net/wireless/mlme.c | 46 ++++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 12 +++++++++ 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 net/wireless/mlme.c (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1f49b63d8dd..6dc7a61bc18 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1085,11 +1085,13 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: ieee80211_auth_completed(sdata); + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); break; case WLAN_AUTH_SHARED_KEY: - if (ifmgd->auth_transaction == 4) + if (ifmgd->auth_transaction == 4) { ieee80211_auth_completed(sdata); - else + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); + } else ieee80211_auth_challenge(sdata, mgmt, len); break; } @@ -1125,6 +1127,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, true, false, 0); ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED; + cfg80211_send_rx_deauth(sdata->dev, (u8 *) mgmt, len); } @@ -1154,6 +1157,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, } ieee80211_set_disassoc(sdata, false, false, reason_code); + cfg80211_send_rx_disassoc(sdata->dev, (u8 *) mgmt, len); } @@ -1370,6 +1374,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_set_associated(sdata, changed); ieee80211_associated(sdata); + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); } diff --git a/net/wireless/Makefile b/net/wireless/Makefile index c157b4d8014..6d1e7b27b75 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c new file mode 100644 index 00000000000..bec5721b6f9 --- /dev/null +++ b/net/wireless/mlme.c @@ -0,0 +1,46 @@ +/* + * cfg80211 MLME SAP interface + * + * Copyright (c) 2009, Jouni Malinen + */ + +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_auth(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_auth); + +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_assoc(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_assoc); + +void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_deauth(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_deauth); + +void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_disassoc(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_disassoc); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a3ecf8d7389..c034c2418cb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2830,6 +2830,9 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_scan, }, }; +static struct genl_multicast_group nl80211_mlme_mcgrp = { + .name = "mlme", +}; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { @@ -2975,6 +2978,71 @@ nla_put_failure: nlmsg_free(msg); } +static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, + enum nl80211_commands cmd) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_AUTHENTICATE); +} + +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE); +} + +void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DEAUTHENTICATE); +} + +void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DISASSOCIATE); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -3003,6 +3071,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5b5fe1339de..b77af4ab80b 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -11,5 +11,17 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); extern void nl80211_send_reg_change_event(struct regulatory_request *request); +extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3 From 636a5d3625993c5ca59abc81794b9ded93cdb740 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:22 +0200 Subject: nl80211: Add MLME primitives to support external SME This patch adds new nl80211 commands to allow user space to request authentication and association (and also deauthentication and disassociation). The commands are structured to allow separate authentication and association steps, i.e., the interface between kernel and user space is similar to the MLME SAP interface in IEEE 802.11 standard and an user space application takes the role of the SME. The patch introduces MLME-AUTHENTICATE.request, MLME-{,RE}ASSOCIATE.request, MLME-DEAUTHENTICATE.request, and MLME-DISASSOCIATE.request primitives. The authentication and association commands request the actual operations in two steps (assuming the driver supports this; if not, separate authentication step is skipped; this could end up being a separate "connect" command). The initial implementation for mac80211 uses the current net/mac80211/mlme.c for actual sending and processing of management frames and the new nl80211 commands will just stop the current state machine from moving automatically from authentication to association. Future cleanup may move more of the MLME operations into cfg80211. The goal of this design is to provide more control of authentication and association process to user space without having to move the full MLME implementation. This should be enough to allow IEEE 802.11r FT protocol and 802.11s SAE authentication to be implemented. Obviously, this will also bring the extra benefit of not having to use WEXT for association requests with mac80211. An example implementation of a user space SME using the new nl80211 commands is available for wpa_supplicant. This patch is enough to get IEEE 802.11r FT protocol working with over-the-air mechanism (over-the-DS will need additional MLME primitives for handling the FT Action frames). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 140 +++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 7 +- net/mac80211/mlme.c | 45 ++++++-- net/mac80211/wext.c | 3 + net/wireless/nl80211.c | 255 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 439 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 58693e52d45..223e536e842 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1300,6 +1300,142 @@ static int ieee80211_scan(struct wiphy *wiphy, return ieee80211_request_scan(sdata, req); } +static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP; + break; + default: + return -EOPNOTSUPP; + } + + memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN); + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + + /* TODO: req->chan */ + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + + if (req->ssid) { + sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; + } + + kfree(sdata->u.mgd.sme_auth_ie); + sdata->u.mgd.sme_auth_ie = NULL; + sdata->u.mgd.sme_auth_ie_len = 0; + if (req->ie) { + sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL); + if (sdata->u.mgd.sme_auth_ie == NULL) + return -ENOMEM; + memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len); + sdata->u.mgd.sme_auth_ie_len = req->ie_len; + } + + sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; + sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE; + ieee80211_sta_req_auth(sdata); + return 0; +} + +static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + struct ieee80211_sub_if_data *sdata; + int ret; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || + !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) + return -ENOLINK; /* not authenticated */ + + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + + /* TODO: req->chan */ + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + + if (req->ssid) { + sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; + } else + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; + + ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); + if (ret) + return ret; + + sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; + sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE; + ieee80211_sta_req_auth(sdata); + return 0; +} + +static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + /* TODO: req->ie */ + return ieee80211_sta_deauthenticate(sdata, req->reason_code); +} + +static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + /* TODO: req->ie */ + return ieee80211_sta_disassociate(sdata, req->reason_code); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1333,4 +1469,8 @@ struct cfg80211_ops mac80211_config_ops = { .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, + .auth = ieee80211_auth, + .assoc = ieee80211_assoc, + .deauth = ieee80211_deauth, + .disassoc = ieee80211_disassoc, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ad12c2a03a9..7b96d95f48b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -256,6 +256,7 @@ struct mesh_preq_queue { #define IEEE80211_STA_TKIP_WEP_USED BIT(14) #define IEEE80211_STA_CSA_RECEIVED BIT(15) #define IEEE80211_STA_MFP_ENABLED BIT(16) +#define IEEE80211_STA_EXT_SME BIT(17) /* flags for MLME request */ #define IEEE80211_STA_REQ_SCAN 0 #define IEEE80211_STA_REQ_DIRECT_PROBE 1 @@ -266,6 +267,7 @@ struct mesh_preq_queue { #define IEEE80211_AUTH_ALG_OPEN BIT(0) #define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) #define IEEE80211_AUTH_ALG_LEAP BIT(2) +#define IEEE80211_AUTH_ALG_FT BIT(3) struct ieee80211_if_managed { struct timer_list timer; @@ -335,6 +337,9 @@ struct ieee80211_if_managed { size_t ie_deauth_len; u8 *ie_disassoc; size_t ie_disassoc_len; + + u8 *sme_auth_ie; + size_t sme_auth_ie_len; }; enum ieee80211_ibss_flags { @@ -970,7 +975,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - char *ie, size_t len); + const char *ie, size_t len); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); void ieee80211_scan_failed(struct ieee80211_local *local); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6dc7a61bc18..d1bcc843877 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -730,6 +730,8 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + u8 *ies; + size_t ies_len; ifmgd->auth_tries++; if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -755,7 +757,14 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticate with AP %pM\n", sdata->dev->name, ifmgd->bssid); - ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, NULL, 0, + if (ifmgd->flags & IEEE80211_STA_EXT_SME) { + ies = ifmgd->sme_auth_ie; + ies_len = ifmgd->sme_auth_ie_len; + } else { + ies = NULL; + ies_len = 0; + } + ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len, ifmgd->bssid, 0); ifmgd->auth_transaction = 2; @@ -870,7 +879,8 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) int wep_privacy; int privacy_invoked; - if (!ifmgd || (ifmgd->flags & IEEE80211_STA_MIXED_CELL)) + if (!ifmgd || (ifmgd->flags & (IEEE80211_STA_MIXED_CELL | + IEEE80211_STA_EXT_SME))) return 0; bss = ieee80211_rx_bss_get(local, ifmgd->bssid, @@ -998,7 +1008,11 @@ static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name); ifmgd->flags |= IEEE80211_STA_AUTHENTICATED; - ieee80211_associate(sdata); + if (ifmgd->flags & IEEE80211_STA_EXT_SME) { + /* Wait for SME to request association */ + ifmgd->state = IEEE80211_STA_MLME_DISABLED; + } else + ieee80211_associate(sdata); } @@ -1084,6 +1098,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, switch (ifmgd->auth_alg) { case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: + case WLAN_AUTH_FT: ieee80211_auth_completed(sdata); cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); break; @@ -1117,9 +1132,10 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n", sdata->dev->name, reason_code); - if (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) { ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; mod_timer(&ifmgd->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1150,7 +1166,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", sdata->dev->name, reason_code); - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; mod_timer(&ifmgd->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1664,6 +1681,8 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata) ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY; else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP) ifmgd->auth_alg = WLAN_AUTH_LEAP; + else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT) + ifmgd->auth_alg = WLAN_AUTH_FT; else ifmgd->auth_alg = WLAN_AUTH_OPEN; ifmgd->auth_transaction = -1; @@ -1687,7 +1706,8 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) u16 capa_val = WLAN_CAPABILITY_ESS; struct ieee80211_channel *chan = local->oper_channel; - if (ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL)) { capa_mask |= WLAN_CAPABILITY_PRIVACY; @@ -1884,7 +1904,11 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) ieee80211_set_disassoc(sdata, true, true, WLAN_REASON_DEAUTH_LEAVING); - set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) || + ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) + set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); + else if (ifmgd->flags & IEEE80211_STA_EXT_SME) + set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); queue_work(local->hw.workqueue, &ifmgd->work); } } @@ -1953,7 +1977,8 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) return ieee80211_sta_commit(sdata); } -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len) +int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, + const char *ie, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index e55d2834764..ce21d66b102 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -137,6 +137,7 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, if (ret) return ret; sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } @@ -224,6 +225,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, if (ret) return ret; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) @@ -287,6 +289,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); if (ret) return ret; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c034c2418cb..9e1318d1d4b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -111,6 +111,11 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, + + [NL80211_ATTR_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, }; /* message building helper */ @@ -265,6 +270,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(set_mesh_params, SET_MESH_PARAMS); CMD(change_bss, SET_BSS); CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); + CMD(auth, AUTHENTICATE); + CMD(assoc, ASSOCIATE); + CMD(deauth, DEAUTHENTICATE); + CMD(disassoc, DISASSOCIATE); #undef CMD nla_nest_end(msg, nl_cmds); @@ -2646,6 +2655,228 @@ static int nl80211_dump_scan(struct sk_buff *skb, return err; } +static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_auth_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->auth) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + req.chan = ieee80211_get_channel( + wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!req.chan) { + err = -EINVAL; + goto out; + } + } + + if (info->attrs[NL80211_ATTR_SSID]) { + req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + } + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + req.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + } + + err = drv->ops->auth(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_assoc_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->assoc) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_SSID]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + req.chan = ieee80211_get_channel( + wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!req.chan) { + err = -EINVAL; + goto out; + } + } + + if (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out; + } + req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->assoc(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_deauth_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->deauth) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_REASON_CODE]) + req.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->deauth(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_disassoc_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->disassoc) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_REASON_CODE]) + req.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->disassoc(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2829,6 +3060,30 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .dumpit = nl80211_dump_scan, }, + { + .cmd = NL80211_CMD_AUTHENTICATE, + .doit = nl80211_authenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_ASSOCIATE, + .doit = nl80211_associate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DEAUTHENTICATE, + .doit = nl80211_deauthenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISASSOCIATE, + .doit = nl80211_disassociate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", -- cgit v1.2.3 From 827b1fb44b7e41377a5498b9d070a11dfae2c283 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 11:44:18 +0100 Subject: mac80211: resume properly, add suspend/resume test When mac80211 resumes, it currently doesn't reconfigure the interfaces entirely and also doesn't reconfigure BSS information -- fix this. Also, to be able to test this, add a debugfs file that just calls the suspend/resume code to see what happens when we go through that, without needing the time-consuming suspend/resume cycle. (Original version broke the build for CONFIG_PM=n. Define alternative functions for that situation. -- JWL) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/debugfs.c | 24 ++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 12 ++++++++++++ net/mac80211/pm.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) (limited to 'net') diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e37f557de3f..210b9b6fecd 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -40,6 +40,10 @@ static const struct file_operations name## _ops = { \ local->debugfs.name = debugfs_create_file(#name, 0400, phyd, \ local, &name## _ops); +#define DEBUGFS_ADD_MODE(name, mode) \ + local->debugfs.name = debugfs_create_file(#name, mode, phyd, \ + local, &name## _ops); + #define DEBUGFS_DEL(name) \ debugfs_remove(local->debugfs.name); \ local->debugfs.name = NULL; @@ -113,6 +117,24 @@ static const struct file_operations tsf_ops = { .open = mac80211_open_file_generic }; +static ssize_t reset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + + rtnl_lock(); + __ieee80211_suspend(&local->hw); + __ieee80211_resume(&local->hw); + rtnl_unlock(); + + return count; +} + +static const struct file_operations reset_ops = { + .write = reset_write, + .open = mac80211_open_file_generic, +}; + /* statistics stuff */ #define DEBUGFS_STATS_FILE(name, buflen, fmt, value...) \ @@ -254,6 +276,7 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(tsf); + DEBUGFS_ADD_MODE(reset, 0200); statsd = debugfs_create_dir("statistics", phyd); local->debugfs.statistics = statsd; @@ -308,6 +331,7 @@ void debugfs_hw_del(struct ieee80211_local *local) DEBUGFS_DEL(total_ps_buffered); DEBUGFS_DEL(wep_iv); DEBUGFS_DEL(tsf); + DEBUGFS_DEL(reset); DEBUGFS_STATS_DEL(transmitted_fragment_count); DEBUGFS_STATS_DEL(multicast_transmitted_frame_count); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7b96d95f48b..547cfac218e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -780,6 +780,7 @@ struct ieee80211_local { struct dentry *total_ps_buffered; struct dentry *wep_iv; struct dentry *tsf; + struct dentry *reset; struct dentry *statistics; struct local_debugfsdentries_statsdentries { struct dentry *transmitted_fragment_count; @@ -1059,8 +1060,19 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, u8 pwr_constr_elem_len); /* Suspend/resume */ +#ifdef CONFIG_PM int __ieee80211_suspend(struct ieee80211_hw *hw); int __ieee80211_resume(struct ieee80211_hw *hw); +#else +static inline int __ieee80211_suspend(struct ieee80211_hw *hw) +{ + return 0; +} +static inline int __ieee80211_resume(struct ieee80211_hw *hw) +{ + return 0; +} +#endif /* utility functions/constants */ extern void *mac80211_wiphy_privid; /* for wiphy privid */ diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 1e6152ac677..02730232649 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -143,6 +143,35 @@ int __ieee80211_resume(struct ieee80211_hw *hw) ieee80211_configure_filter(local); netif_addr_unlock_bh(local->mdev); + /* Finally also reconfigure all the BSS information */ + list_for_each_entry(sdata, &local->interfaces, list) { + u32 changed = ~0; + if (!netif_running(sdata->dev)) + continue; + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + /* disable beacon change bits */ + changed &= ~IEEE80211_IFCC_BEACON; + /* fall through */ + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + WARN_ON(ieee80211_if_config(sdata, changed)); + ieee80211_bss_info_change_notify(sdata, ~0); + break; + case NL80211_IFTYPE_WDS: + break; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* ignore virtual */ + break; + case NL80211_IFTYPE_UNSPECIFIED: + case __NL80211_IFTYPE_AFTER_LAST: + WARN_ON(1); + break; + } + } + ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); -- cgit v1.2.3 From d7873cb9abb5d8b4b9f7f5749af06e4e03798733 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 15:53:16 +0200 Subject: mac80211: Fix memleak in nl80211 authentication on deinit This file was forgotten from the quilt patch that added MLME primitives, so the kfree on interface removal is missing. Fix this potential memleak by freeing the temporary Authentication frame IEs from SME when the interface is being removed. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/iface.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f9f27b9cadb..6b56dc2208e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -660,6 +660,7 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.mgd.ie_reassocreq); kfree(sdata->u.mgd.ie_deauth); kfree(sdata->u.mgd.ie_disassoc); + kfree(sdata->u.mgd.sme_auth_ie); break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: -- cgit v1.2.3 From 65fc73ac4a310945dfeceac961726c2765ad2ec0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:16 +0200 Subject: nl80211: Remove NL80211_CMD_SET_MGMT_EXTRA_IE The functionality that NL80211_CMD_SET_MGMT_EXTRA_IE provided can now be achieved with cleaner design by adding IE(s) into NL80211_CMD_TRIGGER_SCAN, NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. Since this is a very recently added command and there are no known (or known planned) applications using NL80211_CMD_SET_MGMT_EXTRA_IE and taken into account how much extra complexity it adds to the IE processing we have now (and need to add in the future to fix IE order in couple of frames), it looks like the best option is to just remove the implementation of this command for now. The enum values themselves are left to avoid changing the nl80211 command or attribute numbers. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 86 ---------------------------------------------- net/mac80211/ieee80211_i.h | 15 -------- net/mac80211/iface.c | 7 ---- net/mac80211/mlme.c | 36 ++----------------- net/mac80211/util.c | 29 +++------------- net/wireless/nl80211.c | 47 ------------------------- 6 files changed, 7 insertions(+), 213 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 223e536e842..f5c15c9a00c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1181,91 +1181,6 @@ static int ieee80211_set_channel(struct wiphy *wiphy, return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); } -static int set_mgmt_extra_ie_sta(struct ieee80211_sub_if_data *sdata, - u8 subtype, u8 *ies, size_t ies_len) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - switch (subtype) { - case IEEE80211_STYPE_PROBE_REQ >> 4: - if (local->ops->hw_scan) - break; - kfree(ifmgd->ie_probereq); - ifmgd->ie_probereq = ies; - ifmgd->ie_probereq_len = ies_len; - return 0; - case IEEE80211_STYPE_PROBE_RESP >> 4: - kfree(ifmgd->ie_proberesp); - ifmgd->ie_proberesp = ies; - ifmgd->ie_proberesp_len = ies_len; - return 0; - case IEEE80211_STYPE_AUTH >> 4: - kfree(ifmgd->ie_auth); - ifmgd->ie_auth = ies; - ifmgd->ie_auth_len = ies_len; - return 0; - case IEEE80211_STYPE_ASSOC_REQ >> 4: - kfree(ifmgd->ie_assocreq); - ifmgd->ie_assocreq = ies; - ifmgd->ie_assocreq_len = ies_len; - return 0; - case IEEE80211_STYPE_REASSOC_REQ >> 4: - kfree(ifmgd->ie_reassocreq); - ifmgd->ie_reassocreq = ies; - ifmgd->ie_reassocreq_len = ies_len; - return 0; - case IEEE80211_STYPE_DEAUTH >> 4: - kfree(ifmgd->ie_deauth); - ifmgd->ie_deauth = ies; - ifmgd->ie_deauth_len = ies_len; - return 0; - case IEEE80211_STYPE_DISASSOC >> 4: - kfree(ifmgd->ie_disassoc); - ifmgd->ie_disassoc = ies; - ifmgd->ie_disassoc_len = ies_len; - return 0; - } - - return -EOPNOTSUPP; -} - -static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, - struct net_device *dev, - struct mgmt_extra_ie_params *params) -{ - struct ieee80211_sub_if_data *sdata; - u8 *ies; - size_t ies_len; - int ret = -EOPNOTSUPP; - - if (params->ies) { - ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); - if (ies == NULL) - return -ENOMEM; - ies_len = params->ies_len; - } else { - ies = NULL; - ies_len = 0; - } - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - ret = set_mgmt_extra_ie_sta(sdata, params->subtype, - ies, ies_len); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - if (ret) - kfree(ies); - return ret; -} - #ifdef CONFIG_PM static int ieee80211_suspend(struct wiphy *wiphy) { @@ -1465,7 +1380,6 @@ struct cfg80211_ops mac80211_config_ops = { .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_channel = ieee80211_set_channel, - .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 547cfac218e..f69e84ab961 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -323,21 +323,6 @@ struct ieee80211_if_managed { int wmm_last_param_set; /* Extra IE data for management frames */ - u8 *ie_probereq; - size_t ie_probereq_len; - u8 *ie_proberesp; - size_t ie_proberesp_len; - u8 *ie_auth; - size_t ie_auth_len; - u8 *ie_assocreq; - size_t ie_assocreq_len; - u8 *ie_reassocreq; - size_t ie_reassocreq_len; - u8 *ie_deauth; - size_t ie_deauth_len; - u8 *ie_disassoc; - size_t ie_disassoc_len; - u8 *sme_auth_ie; size_t sme_auth_ie_len; }; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6b56dc2208e..34f4798a98f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -653,13 +653,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.mgd.extra_ie); kfree(sdata->u.mgd.assocreq_ies); kfree(sdata->u.mgd.assocresp_ies); - kfree(sdata->u.mgd.ie_probereq); - kfree(sdata->u.mgd.ie_proberesp); - kfree(sdata->u.mgd.ie_auth); - kfree(sdata->u.mgd.ie_assocreq); - kfree(sdata->u.mgd.ie_reassocreq); - kfree(sdata->u.mgd.ie_deauth); - kfree(sdata->u.mgd.ie_disassoc); kfree(sdata->u.mgd.sme_auth_ie); break; case NL80211_IFTYPE_WDS: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d1bcc843877..b0808efcedf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -82,38 +82,23 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss, /* frame sending functions */ -static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len) -{ - if (ies) - memcpy(skb_put(skb, ies_len), ies, ies_len); -} - static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *ht_ie, *e_ies; + u8 *pos, *ies, *ht_ie; int i, len, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; u32 rates = 0; - size_t e_ies_len; - - if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { - e_ies = sdata->u.mgd.ie_reassocreq; - e_ies_len = sdata->u.mgd.ie_reassocreq_len; - } else { - e_ies = sdata->u.mgd.ie_assocreq; - e_ies_len = sdata->u.mgd.ie_assocreq_len; - } skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + ifmgd->extra_ie_len + - ifmgd->ssid_len + e_ies_len); + ifmgd->ssid_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -304,8 +289,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } - add_extra_ies(skb, e_ies, e_ies_len); - kfree(ifmgd->assocreq_ies); ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies; ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL); @@ -323,19 +306,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *ies; - size_t ies_len; - if (stype == IEEE80211_STYPE_DEAUTH) { - ies = sdata->u.mgd.ie_deauth; - ies_len = sdata->u.mgd.ie_deauth_len; - } else { - ies = sdata->u.mgd.ie_disassoc; - ies_len = sdata->u.mgd.ie_disassoc_len; - } - - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + - ies_len); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for " "deauth/disassoc frame\n", sdata->dev->name); @@ -353,8 +325,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); - add_extra_ies(skb, ies, ies_len); - ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e0431a1d218..444bb14c95e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -846,16 +846,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - const u8 *ie_auth = NULL; - int ie_auth_len = 0; - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - ie_auth_len = sdata->u.mgd.ie_auth_len; - ie_auth = sdata->u.mgd.ie_auth; - } skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 6 + extra_len + ie_auth_len); + sizeof(*mgmt) + 6 + extra_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for auth " "frame\n", sdata->dev->name); @@ -877,8 +870,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); - if (ie_auth) - memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len); ieee80211_tx_skb(sdata, skb, encrypt); } @@ -891,20 +882,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, struct ieee80211_supported_band *sband; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL; - int i, extra_preq_ie_len = 0; - - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - extra_preq_ie_len = sdata->u.mgd.ie_probereq_len; - extra_preq_ie = sdata->u.mgd.ie_probereq; - break; - default: - break; - } + u8 *pos, *supp_rates, *esupp_rates = NULL; + int i; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + - ie_len + extra_preq_ie_len); + ie_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", sdata->dev->name); @@ -953,9 +935,6 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, if (ie) memcpy(skb_put(skb, ie_len), ie, ie_len); - if (extra_preq_ie) - memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie, - extra_preq_ie_len); ieee80211_tx_skb(sdata, skb, 0); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9e1318d1d4b..44c79972be5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -269,7 +269,6 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(add_mpath, NEW_MPATH); CMD(set_mesh_params, SET_MESH_PARAMS); CMD(change_bss, SET_BSS); - CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); CMD(auth, AUTHENTICATE); CMD(assoc, ASSOCIATE); CMD(deauth, DEAUTHENTICATE); @@ -2355,46 +2354,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } -static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *drv; - int err; - struct net_device *dev; - struct mgmt_extra_ie_params params; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) - return -EINVAL; - params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); - if (params.subtype > 15) - return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ - - if (info->attrs[NL80211_ATTR_IE]) { - params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - rtnl_lock(); - - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); - if (err) - goto out_rtnl; - - if (drv->ops->set_mgmt_extra_ie) - err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); - else - err = -EOPNOTSUPP; - - cfg80211_put_dev(drv); - dev_put(dev); - out_rtnl: - rtnl_unlock(); - - return err; -} - static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3043,12 +3002,6 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, - { - .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, - .doit = nl80211_set_mgmt_extra_ie, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, { .cmd = NL80211_CMD_TRIGGER_SCAN, .doit = nl80211_trigger_scan, -- cgit v1.2.3 From 255e737eab645ec6037baeca04a5e0a7c3b1f459 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:17 +0200 Subject: nl80211: Add more through validation of MLME command parameters Check that the used authentication type and reason code are valid here so that drivers/mac80211 do not need to care about this. In addition, remove the unnecessary validation of SSID attribute length which is taken care of by netlink policy. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44c79972be5..6f38ee7a3c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2614,6 +2614,14 @@ static int nl80211_dump_scan(struct sk_buff *skb, return err; } +static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) +{ + return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM || + auth_type == NL80211_AUTHTYPE_SHARED_KEY || + auth_type == NL80211_AUTHTYPE_FT || + auth_type == NL80211_AUTHTYPE_NETWORK_EAP; +} + static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -2666,6 +2674,10 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(req.auth_type)) { + err = -EINVAL; + goto out; + } } err = drv->ops->auth(&drv->wiphy, dev, &req); @@ -2718,10 +2730,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } - if (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out; - } req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); @@ -2769,9 +2777,15 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (info->attrs[NL80211_ATTR_REASON_CODE]) + if (info->attrs[NL80211_ATTR_REASON_CODE]) { req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (req.reason_code == 0) { + /* Reason Code 0 is reserved */ + err = -EINVAL; + goto out; + } + } if (info->attrs[NL80211_ATTR_IE]) { req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); @@ -2817,9 +2831,15 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (info->attrs[NL80211_ATTR_REASON_CODE]) + if (info->attrs[NL80211_ATTR_REASON_CODE]) { req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (req.reason_code == 0) { + /* Reason Code 0 is reserved */ + err = -EINVAL; + goto out; + } + } if (info->attrs[NL80211_ATTR_IE]) { req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); -- cgit v1.2.3 From 35a8efe1a67ba5d7bb7492f67f52ed2aa4925892 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:18 +0200 Subject: nl80211: Check that netif_runnin is true in cfg80211 code We do not want to require all the drivers using cfg80211 to need to do this or to be prepared to handle these commands when the interface is down. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 25 ------------------------- net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f5c15c9a00c..b5810b4c79a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -728,10 +728,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, int err; int layer2_update; - /* Prevent a race with changing the rate control algorithm */ - if (!netif_running(dev)) - return -ENETDOWN; - if (params->vlan) { sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); @@ -860,9 +856,6 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; int err; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) @@ -913,9 +906,6 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, struct mesh_path *mpath; struct sta_info *sta; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) @@ -1202,9 +1192,6 @@ static int ieee80211_scan(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION && @@ -1220,9 +1207,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) @@ -1282,9 +1266,6 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; int ret; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) @@ -1323,9 +1304,6 @@ static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; @@ -1339,9 +1317,6 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6f38ee7a3c9..6bb73a3a339 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1556,6 +1556,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); out: @@ -1808,6 +1813,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); out: @@ -1846,6 +1856,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); out: @@ -2380,6 +2395,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (drv->scan_req) { err = -EBUSY; goto out; @@ -2641,6 +2661,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC]) { err = -EINVAL; goto out; @@ -2709,6 +2734,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_SSID]) { err = -EINVAL; @@ -2767,6 +2797,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC]) { err = -EINVAL; goto out; @@ -2821,6 +2856,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC]) { err = -EINVAL; goto out; -- cgit v1.2.3 From eec60b037a875513d9715dcdb90b13ed81fc5f26 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:19 +0200 Subject: nl80211: Check iftype in cfg80211 code We do not want to require all the drivers using cfg80211 to need to do this. In addition, make the error values consistent by using EOPNOTSUPP instead of semi-random assortment of errno values. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 40 ---------------------------------- net/wireless/nl80211.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b5810b4c79a..e677b751d46 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -540,9 +540,6 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - old = sdata->u.ap.beacon; if (old) @@ -559,9 +556,6 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - old = sdata->u.ap.beacon; if (!old) @@ -577,9 +571,6 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - old = sdata->u.ap.beacon; if (!old) @@ -858,9 +849,6 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); sta = sta_info_get(local, next_hop); if (!sta) { @@ -908,9 +896,6 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); sta = sta_info_get(local, next_hop); @@ -979,9 +964,6 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); mpath = mesh_path_lookup(dst, sdata); if (!mpath) { @@ -1003,9 +985,6 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); mpath = mesh_path_lookup_by_idx(idx, sdata); if (!mpath) { @@ -1025,8 +1004,6 @@ static int ieee80211_get_mesh_params(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config)); return 0; } @@ -1044,9 +1021,6 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - /* Set the config options which we are interested in setting */ conf = &(sdata->u.mesh.mshcfg); if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask)) @@ -1094,9 +1068,6 @@ static int ieee80211_change_bss(struct wiphy *wiphy, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - if (params->use_cts_prot >= 0) { sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; changed |= BSS_CHANGED_ERP_CTS_PROT; @@ -1209,9 +1180,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; @@ -1268,9 +1236,6 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) return -ENOLINK; /* not authenticated */ @@ -1305,8 +1270,6 @@ static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; /* TODO: req->ie */ return ieee80211_sta_deauthenticate(sdata, req->reason_code); @@ -1319,9 +1282,6 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - /* TODO: req->ie */ return ieee80211_sta_disassociate(sdata, req->reason_code); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6bb73a3a339..a7d0b94f6b5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1049,6 +1049,11 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) if (err) goto unlock_rtnl; + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { + err = -EOPNOTSUPP; + goto out; + } + switch (info->genlhdr->cmd) { case NL80211_CMD_NEW_BEACON: /* these are required for NEW_BEACON */ @@ -1136,6 +1141,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { + err = -EOPNOTSUPP; + goto out; + } err = drv->ops->del_beacon(&drv->wiphy, dev); out: @@ -1324,7 +1333,7 @@ static int nl80211_dump_station(struct sk_buff *skb, } if (!dev->ops->dump_station) { - err = -ENOSYS; + err = -EOPNOTSUPP; goto out_err; } @@ -1698,10 +1707,15 @@ static int nl80211_dump_mpath(struct sk_buff *skb, } if (!dev->ops->dump_mpath) { - err = -ENOSYS; + err = -EOPNOTSUPP; goto out_err; } + if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + while (1) { err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, dst, next_hop, &pinfo); @@ -1759,6 +1773,11 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); if (err) goto out; @@ -1813,6 +1832,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -1856,6 +1880,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -1944,6 +1973,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { + err = -EOPNOTSUPP; + goto out; + } + err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); out: @@ -2661,6 +2695,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -2734,6 +2773,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -2797,6 +2841,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -2856,6 +2905,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; -- cgit v1.2.3 From 2e097dc65673ed421bbc2e49f52c125aa43a8ee6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:04 -0400 Subject: cfg80211: force last_request to be set for OLD_REG if regdom is EU Although EU is a bogus alpha2 we need to process the send request as our code depends on last_request being set. Cc: stable@kernel.org Reported-by: Quentin Armitage Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index eb8b8ed1615..ead9dccb547 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2135,11 +2135,14 @@ int regulatory_init(void) /* * The old code still requests for a new regdomain and if * you have CRDA you get it updated, otherwise you get - * stuck with the static values. We ignore "EU" code as - * that is not a valid ISO / IEC 3166 alpha2 + * stuck with the static values. Since "EU" is not a valid + * ISO / IEC 3166 alpha2 code we can't expect userpace to + * give us a regulatory domain for it. We need last_request + * iniitalized though so lets just send a request which we + * know will be ignored... this crap will be removed once + * OLD_REG dies. */ - if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') - err = regulatory_hint_core(ieee80211_regdom); + err = regulatory_hint_core(ieee80211_regdom); #else cfg80211_regdomain = cfg80211_world_regdom; -- cgit v1.2.3 From cc0b6fe88e99096868bdbacbf486c97299533b5a Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:05 -0400 Subject: cfg80211: fix incorrect assumption on last_request for 11d The incorrect assumption is the last regulatory request (last_request) is always a country IE when processing country IEs. Although this is true 99% of the time the first time this happens this could not be true. This fixes an oops in the branch check for the last_request when accessing drv_last_ie. The access was done under the assumption the struct won't be null. Note to stable: to port to 29 replace as follows, only 29 has country IE code: s|NL80211_REGDOM_SET_BY_COUNTRY_IE|REGDOM_SET_BY_COUNTRY_IE Cc: stable@kernel.org Reported-by: Quentin Armitage Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ead9dccb547..9afc9168748 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1601,6 +1601,10 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, assert_cfg80211_lock(); + if (unlikely(last_request->initiator != + NL80211_REGDOM_SET_BY_COUNTRY_IE)) + return false; + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy) @@ -1663,7 +1667,9 @@ void regulatory_hint_11d(struct wiphy *wiphy, * we optimize an early check to exit out early if we don't have to * do anything */ - if (likely(wiphy_idx_valid(last_request->wiphy_idx))) { + if (likely(last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy_idx_valid(last_request->wiphy_idx))) { struct cfg80211_registered_device *drv_last_ie; drv_last_ie = -- cgit v1.2.3 From 6ee7d33056f6e6fc7437d980dcc741816deedd0f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:06 -0400 Subject: cfg80211: make regdom module parameter available oustide of OLD_REG It seems a few users are using this module parameter although its not recommended. People are finding it useful despite there being utilities for setting this in userspace. I'm not aware of any distribution using this though. Until userspace and distributions catch up with a default userspace automatic replacement (GeoClue integration would be nirvana) we copy the ieee80211_regdom module parameter from OLD_REG to the new reg code to help these users migrate. Users who are using the non-valid ISO / IEC 3166 alpha "EU" in their ieee80211_regdom module parameter and migrate to non-OLD_REG enabled system will world roam. This also schedules removal of this same ieee80211_regdom module parameter circa March 2010. Hope is by then nirvana is reached and users will abandoned the module parameter completely. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9afc9168748..ac048a158d8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -122,9 +122,14 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = #ifdef CONFIG_WIRELESS_OLD_REGULATORY static char *ieee80211_regdom = "US"; +#else +static char *ieee80211_regdom = "00"; +#endif + module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); +#ifdef CONFIG_WIRELESS_OLD_REGULATORY /* * We assume 40 MHz bandwidth for the old regulatory work. * We make emphasis we are using the exact same frequencies @@ -2152,7 +2157,7 @@ int regulatory_init(void) #else cfg80211_regdomain = cfg80211_world_regdom; - err = regulatory_hint_core("00"); + err = regulatory_hint_core(ieee80211_regdom); #endif if (err) { if (err == -ENOMEM) -- cgit v1.2.3 From 86f04680df4a136a4a90501572dc2f31f8426581 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:07 -0400 Subject: cfg80211: remove code about country IE support with OLD_REG We had left in code to allow interested developers to add support for parsing country IEs when OLD_REG was enabled. This never happened and since we're going to remove OLD_REG lets just remove these comments and code for it. This code path was never being entered so this has no functional change. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ac048a158d8..6327e1617ac 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1420,16 +1420,6 @@ new_request: return r; } - /* - * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled - * AND if CRDA is NOT present nothing will happen, if someone - * wants to bother with 11d with OLD_REG you can add a timer. - * If after x amount of time nothing happens you can call: - * - * return set_regdom(country_ie_regdomain); - * - * to intersect with the static rd - */ return call_crda(last_request->alpha2); } @@ -2033,28 +2023,21 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) */ BUG_ON(!country_ie_regdomain); + BUG_ON(rd == country_ie_regdomain); - if (rd != country_ie_regdomain) { - /* - * Intersect what CRDA returned and our what we - * had built from the Country IE received - */ + /* + * Intersect what CRDA returned and our what we + * had built from the Country IE received + */ - intersected_rd = regdom_intersect(rd, country_ie_regdomain); + intersected_rd = regdom_intersect(rd, country_ie_regdomain); - reg_country_ie_process_debug(rd, country_ie_regdomain, - intersected_rd); + reg_country_ie_process_debug(rd, + country_ie_regdomain, + intersected_rd); - kfree(country_ie_regdomain); - country_ie_regdomain = NULL; - } else { - /* - * This would happen when CRDA was not present and - * OLD_REGULATORY was enabled. We intersect our Country - * IE rd and what was set on cfg80211 originally - */ - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); - } + kfree(country_ie_regdomain); + country_ie_regdomain = NULL; if (!intersected_rd) return -EINVAL; -- cgit v1.2.3 From ac7f9cfa2c3b810e0adfb889ad407a8c79a84dbe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2009 17:07:59 +0100 Subject: cfg80211: accept no-op interface mode changes When somebody tries to set the interface mode to the existing mode, don't ask the driver but silently accept the setting. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 28 +++++++++++++++++++++------- net/wireless/wext-compat.c | 11 +++++++++-- 2 files changed, 30 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a7d0b94f6b5..8808431bd58 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -607,6 +607,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) enum nl80211_iftype type; struct net_device *dev; u32 _flags, *flags = NULL; + bool change = false; memset(¶ms, 0, sizeof(params)); @@ -620,11 +621,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) type = dev->ieee80211_ptr->iftype; dev_put(dev); - err = -EINVAL; if (info->attrs[NL80211_ATTR_IFTYPE]) { - type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); - if (type > NL80211_IFTYPE_MAX) + enum nl80211_iftype ntype; + + ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); + if (type != ntype) + change = true; + type = ntype; + if (type > NL80211_IFTYPE_MAX) { + err = -EINVAL; goto unlock; + } } if (!drv->ops->change_virtual_intf || @@ -640,6 +647,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + change = true; } if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { @@ -649,12 +657,18 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], &_flags); - if (!err) - flags = &_flags; + if (err) + goto unlock; + + flags = &_flags; + change = true; } - err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, - type, flags, ¶ms); + if (change) + err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, + type, flags, ¶ms); + else + err = 0; dev = __dev_get_by_index(&init_net, ifindex); WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index b84a9b4fe96..0fd1db6e95b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -66,6 +66,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, struct cfg80211_registered_device *rdev; struct vif_params vifparams; enum nl80211_iftype type; + int ret; if (!wdev) return -EOPNOTSUPP; @@ -96,10 +97,16 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, return -EINVAL; } + if (type == wdev->iftype) + return 0; + memset(&vifparams, 0, sizeof(vifparams)); - return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, - NULL, &vifparams); + ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, + NULL, &vifparams); + WARN_ON(!ret && wdev->iftype != type); + + return ret; } EXPORT_SYMBOL(cfg80211_wext_siwmode); -- cgit v1.2.3 From 7986cf9581767d250ca0e5a554541bb276e08d21 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2009 17:08:43 +0100 Subject: mac80211: remove mixed-cell and userspace MLME code Neither can currently be set from userspace, so there's no regression potential, and neither will be supported from userspace since the new userspace APIs allow the SME, which is in userspace, to control all we need. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 9 ++++----- net/mac80211/iface.c | 3 +-- net/mac80211/mlme.c | 3 +-- net/mac80211/rx.c | 13 ++++--------- net/mac80211/wext.c | 17 +---------------- 5 files changed, 11 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f69e84ab961..564167fbb9a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -247,8 +247,9 @@ struct mesh_preq_queue { #define IEEE80211_STA_ASSOCIATED BIT(4) #define IEEE80211_STA_PROBEREQ_POLL BIT(5) #define IEEE80211_STA_CREATE_IBSS BIT(6) -#define IEEE80211_STA_MIXED_CELL BIT(7) +/* hole at 7, please re-use */ #define IEEE80211_STA_WMM_ENABLED BIT(8) +/* hole at 9, please re-use */ #define IEEE80211_STA_AUTO_SSID_SEL BIT(10) #define IEEE80211_STA_AUTO_BSSID_SEL BIT(11) #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) @@ -411,7 +412,6 @@ struct ieee80211_if_mesh { * * @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets * @IEEE80211_SDATA_PROMISC: interface is promisc - * @IEEE80211_SDATA_USERSPACE_MLME: userspace MLME is active * @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between * associated stations and deliver multicast frames both @@ -420,9 +420,8 @@ struct ieee80211_if_mesh { enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), IEEE80211_SDATA_PROMISC = BIT(1), - IEEE80211_SDATA_USERSPACE_MLME = BIT(2), - IEEE80211_SDATA_OPERATING_GMODE = BIT(3), - IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(4), + IEEE80211_SDATA_OPERATING_GMODE = BIT(2), + IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), }; struct ieee80211_sub_if_data { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 34f4798a98f..dd2a276fa8c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -261,8 +261,7 @@ static int ieee80211_open(struct net_device *dev) ieee80211_bss_info_change_notify(sdata, changed); ieee80211_enable_keys(sdata); - if (sdata->vif.type == NL80211_IFTYPE_STATION && - !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)) + if (sdata->vif.type == NL80211_IFTYPE_STATION) netif_carrier_off(dev); else netif_carrier_on(dev); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b0808efcedf..c05be09b9c6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -849,8 +849,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) int wep_privacy; int privacy_invoked; - if (!ifmgd || (ifmgd->flags & (IEEE80211_STA_MIXED_CELL | - IEEE80211_STA_EXT_SME))) + if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME)) return 0; bss = ieee80211_rx_bss_get(local, ifmgd->bssid, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fcc0a599579..47d395a5192 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1877,18 +1877,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) if (ieee80211_vif_is_mesh(&sdata->vif)) return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status); - if (sdata->vif.type != NL80211_IFTYPE_STATION && - sdata->vif.type != NL80211_IFTYPE_ADHOC) - return RX_DROP_MONITOR; - + if (sdata->vif.type != NL80211_IFTYPE_ADHOC) + return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) - return RX_DROP_MONITOR; + if (sdata->vif.type == NL80211_IFTYPE_STATION) return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status); - } - return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); + return RX_DROP_MONITOR; } static void ieee80211_rx_michael_mic_report(struct net_device *dev, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index ce21d66b102..deb4ecec122 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -129,9 +129,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) - return -EOPNOTSUPP; - if (sdata->vif.type == NL80211_IFTYPE_STATION) { int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); if (ret) @@ -208,14 +205,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { - if (len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - memcpy(sdata->u.mgd.ssid, ssid, len); - sdata->u.mgd.ssid_len = len; - return 0; - } - if (data->flags) sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; else @@ -274,11 +263,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_STATION) { int ret; - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { - memcpy(sdata->u.mgd.bssid, (u8 *) &ap_addr->sa_data, - ETH_ALEN); - return 0; - } + if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL; -- cgit v1.2.3 From 3cf335d527ba6af80f4143f3c9e5136afdb143af Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:06 +0200 Subject: mac80211: decrease execution of the associated timer Currently the timer is triggering every two seconds (IEEE80211_MONITORING_INTERVAL). Decrease the timer to only trigger during data idle periods to avoid waking up CPU unnecessary. The timer will still trigger during idle periods, that needs to be fixed later. There's also a functional change that probe requests are sent only when the data path is idle, earlier they were sent also while there was activity on the data path. This is also preparation for the beacon filtering support. Thanks to Johannes Berg for the idea. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 15 +++++++++++++++ net/mac80211/rx.c | 3 +++ 3 files changed, 20 insertions(+) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 564167fbb9a..055bb776408 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1083,6 +1083,8 @@ void ieee80211_dynamic_ps_timer(unsigned long data); void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int powersave); +void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c05be09b9c6..209abb073df 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -909,6 +909,21 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); } +void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr) +{ + /* + * We can postpone the mgd.timer whenever receiving unicast frames + * from AP because we know that the connection is working both ways + * at that time. But multicast frames (and hence also beacons) must + * be ignored here, because we need to trigger the timer during + * data idle periods for sending the periodical probe request to + * the AP. + */ + if (!is_multicast_ether_addr(hdr->addr1)) + mod_timer(&sdata->u.mgd.timer, + jiffies + IEEE80211_MONITORING_INTERVAL); +} static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 47d395a5192..dbfb2846535 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -856,6 +856,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (!(rx->flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; + if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) + ieee80211_sta_rx_notify(rx->sdata, hdr); + sta->rx_fragments++; sta->rx_bytes += rx->skb->len; sta->last_signal = rx->status->signal; -- cgit v1.2.3 From 15b7b0629c8213905926394dc73d600e0ca250ce Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:14 +0200 Subject: mac80211: track beacons separately from the rx path activity Separate beacon and rx path tracking in preparation for the beacon filtering support. At the same time change ieee80211_associated() to look a bit simpler. Probe requests are now sent only after IEEE80211_PROBE_IDLE_TIME, which is now set to 60 seconds. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 77 +++++++++++++++++++++++++++------------------- net/mac80211/rx.c | 6 +++- 3 files changed, 52 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 055bb776408..8a617a7fc09 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -308,6 +308,7 @@ struct ieee80211_if_managed { unsigned long request; unsigned long last_probe; + unsigned long last_beacon; unsigned int flags; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 209abb073df..8f30f4d19da 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -30,7 +30,7 @@ #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 #define IEEE80211_MONITORING_INTERVAL (2 * HZ) -#define IEEE80211_PROBE_INTERVAL (60 * HZ) +#define IEEE80211_PROBE_IDLE_TIME (60 * HZ) #define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) /* utils */ @@ -930,7 +930,7 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int disassoc; + bool disassoc = false; /* TODO: start monitoring current AP signal quality and number of * missed beacons. Scan other channels every now and then and search @@ -945,36 +945,39 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) if (!sta) { printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n", sdata->dev->name, ifmgd->bssid); - disassoc = 1; - } else { - disassoc = 0; - if (time_after(jiffies, - sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { - if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { - printk(KERN_DEBUG "%s: No ProbeResp from " - "current AP %pM - assume out of " - "range\n", - sdata->dev->name, ifmgd->bssid); - disassoc = 1; - } else - ieee80211_send_probe_req(sdata, ifmgd->bssid, - ifmgd->ssid, - ifmgd->ssid_len, - NULL, 0); - ifmgd->flags ^= IEEE80211_STA_PROBEREQ_POLL; - } else { - ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; - if (time_after(jiffies, ifmgd->last_probe + - IEEE80211_PROBE_INTERVAL)) { - ifmgd->last_probe = jiffies; - ieee80211_send_probe_req(sdata, ifmgd->bssid, - ifmgd->ssid, - ifmgd->ssid_len, - NULL, 0); - } - } + disassoc = true; + goto unlock; + } + + if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) && + time_after(jiffies, sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { + printk(KERN_DEBUG "%s: no probe response from AP %pM " + "- disassociating\n", + sdata->dev->name, ifmgd->bssid); + disassoc = true; + ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; + goto unlock; + } + + if (time_after(jiffies, + ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { + printk(KERN_DEBUG "%s: beacon loss from AP %pM " + "- sending probe request\n", + sdata->dev->name, ifmgd->bssid); + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); + goto unlock; + + } + + if (time_after(jiffies, sta->last_rx + IEEE80211_PROBE_IDLE_TIME)) { + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); } + unlock: rcu_read_unlock(); if (disassoc) @@ -1374,6 +1377,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, bss_conf->assoc_capability = capab_info; ieee80211_set_associated(sdata, changed); + /* + * initialise the time of last beacon to be the association time, + * otherwise beacon loss check will trigger immediately + */ + ifmgd->last_beacon = jiffies; + ieee80211_associated(sdata); cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); } @@ -1422,9 +1431,12 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, size_t len, struct ieee80211_rx_status *rx_status) { + struct ieee80211_if_managed *ifmgd; size_t baselen; struct ieee802_11_elems elems; + ifmgd = &sdata->u.mgd; + if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) return; /* ignore ProbeResp to foreign address */ @@ -1439,11 +1451,14 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, /* direct probe may be part of the association flow */ if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE, - &sdata->u.mgd.request)) { + &ifmgd->request)) { printk(KERN_DEBUG "%s direct probe responded\n", sdata->dev->name); ieee80211_authenticate(sdata); } + + if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) + ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; } static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index dbfb2846535..eff59f36e8e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -850,7 +850,11 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. */ - sta->last_rx = jiffies; + if (rx->sdata->vif.type == NL80211_IFTYPE_STATION && + ieee80211_is_beacon(hdr->frame_control)) { + rx->sdata->u.mgd.last_beacon = jiffies; + } else + sta->last_rx = jiffies; } if (!(rx->flags & IEEE80211_RX_RA_MATCH)) -- cgit v1.2.3 From 9050bdd8589c373e01e41ddbd9a192de2ff01ef0 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:21 +0200 Subject: mac80211: disable power save when scanning When software scanning we need to disable power save so that all possible probe responses and beacons are received. For hardware scanning assume that hardware will take care of that and document that assumption. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/scan.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 46f35dc6acc..3bf9839f591 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -214,6 +214,66 @@ void ieee80211_scan_failed(struct ieee80211_local *local) local->scan_req = NULL; } +/* + * inform AP that we will go to sleep so that it will buffer the frames + * while we scan + */ +static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + bool ps = false; + + /* FIXME: what to do when local->pspolling is true? */ + + del_timer_sync(&local->dynamic_ps_timer); + cancel_work_sync(&local->dynamic_ps_enable_work); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + ps = true; + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } + + if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) + /* + * If power save was enabled, no need to send a nullfunc + * frame because AP knows that we are sleeping. But if the + * hardware is creating the nullfunc frame for power save + * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not + * enabled) and power save was enabled, the firmware just + * sent a null frame with power save disabled. So we need + * to send a new nullfunc frame to inform the AP that we + * are again sleeping. + */ + ieee80211_send_nullfunc(local, sdata, 1); +} + +/* inform AP that we are awake again, unless power save is enabled */ +static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + + if (!local->powersave) + ieee80211_send_nullfunc(local, sdata, 0); + else { + /* + * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware + * will send a nullfunc frame with the powersave bit set + * even though the AP already knows that we are sleeping. + * This could be avoided by sending a null frame with power + * save bit disabled before enabling the power save, but + * this doesn't gain anything. + * + * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need + * to send a nullfunc frame because AP already knows that + * we are sleeping, let's just enable power save mode in + * hardware. + */ + local->hw.conf.flags |= IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } +} + void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); @@ -268,7 +328,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) /* Tell AP we're back */ if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_scan_ps_disable(sdata); netif_tx_wake_all_queues(sdata->dev); } } else @@ -441,7 +501,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { netif_tx_stop_all_queues(sdata->dev); - ieee80211_send_nullfunc(local, sdata, 1); + ieee80211_scan_ps_enable(sdata); } } else netif_tx_stop_all_queues(sdata->dev); -- cgit v1.2.3 From a08c1c1ac0c26229ca1ca45d554b209a56edc8be Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:28 +0200 Subject: cfg80211: add feature to hold bss In beacon filtering there needs to be a way to not expire the BSS even when no beacons are received. Add an interface to cfg80211 to hold BSS and make sure that it's not expired. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/wireless/core.h | 2 ++ net/wireless/scan.c | 27 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/core.h b/net/wireless/core.h index 6acd483a61f..97a6fd8b2b0 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -90,6 +90,8 @@ struct cfg80211_internal_bss { struct rb_node rbn; unsigned long ts; struct kref ref; + bool hold; + /* must be last because of priv member */ struct cfg80211_bss pub; }; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 280dbcd02c1..2a00e362f5f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -80,7 +80,8 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + if (bss->hold || + !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; list_del(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); @@ -471,6 +472,30 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_unlink_bss); +void cfg80211_hold_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss->hold = true; +} +EXPORT_SYMBOL(cfg80211_hold_bss); + +void cfg80211_unhold_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss->hold = false; +} +EXPORT_SYMBOL(cfg80211_unhold_bss); + #ifdef CONFIG_WIRELESS_EXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, -- cgit v1.2.3 From 04de83815993714a7ba2618f637fa1092a5f664b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:35 +0200 Subject: mac80211: add beacon filtering support Add IEEE80211_HW_BEACON_FILTERING flag so that driver inform that it supports beacon filtering. Drivers need to call the new function ieee80211_beacon_loss() to notify about beacon loss. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 3 +++ net/mac80211/mlme.c | 49 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8a617a7fc09..acba78e1a5c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -275,6 +275,7 @@ struct ieee80211_if_managed { struct timer_list chswitch_timer; struct work_struct work; struct work_struct chswitch_work; + struct work_struct beacon_loss_work; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; @@ -1086,6 +1087,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); +void ieee80211_beacon_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index dd2a276fa8c..91e8e1bacaa 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -477,6 +477,9 @@ static int ieee80211_stop(struct net_device *dev) */ cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.chswitch_work); + + cancel_work_sync(&sdata->u.mgd.beacon_loss_work); + /* * When we get here, the interface is marked down. * Call synchronize_rcu() to wait for the RX path diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8f30f4d19da..7ecda9d59d8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -610,6 +610,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); + cfg80211_hold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); } @@ -751,6 +753,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local_to_hw(local)->conf; + struct ieee80211_bss *bss; struct sta_info *sta; u32 changed = 0, config_changed = 0; @@ -774,6 +778,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_sta_tear_down_BA_sessions(sta); + bss = ieee80211_rx_bss_get(local, ifmgd->bssid, + conf->channel->center_freq, + ifmgd->ssid, ifmgd->ssid_len); + + if (bss) { + cfg80211_unhold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); + } + if (self_disconnected) { if (deauth) ieee80211_send_deauth_disassoc(sdata, @@ -925,6 +938,33 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, jiffies + IEEE80211_MONITORING_INTERVAL); } +void ieee80211_beacon_loss_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.beacon_loss_work); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " + "- sending probe request\n", sdata->dev->name, + sdata->u.mgd.bssid); + + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); + + mod_timer(&ifmgd->timer, jiffies + IEEE80211_MONITORING_INTERVAL); +} + +void ieee80211_beacon_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + queue_work(sdata->local->hw.workqueue, + &sdata->u.mgd.beacon_loss_work); +} +EXPORT_SYMBOL(ieee80211_beacon_loss); + static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -959,7 +999,13 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) goto unlock; } - if (time_after(jiffies, + /* + * Beacon filtering is only enabled with power save and then the + * stack should not check for beacon loss. + */ + if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) && + (local->hw.conf.flags & IEEE80211_CONF_PS)) && + time_after(jiffies, ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { printk(KERN_DEBUG "%s: beacon loss from AP %pM " "- sending probe request\n", @@ -1869,6 +1915,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); + INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, -- cgit v1.2.3 From 2b874e83c970b45c328ab12239b066a43505454c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 14:10:22 +0100 Subject: mac80211: rate control status only for controlled packets This patch changes mac80211 to not notify the rate control algorithm's tx_status() method when reporting status for a packet that didn't go through the rate control algorithm's get_rate() method. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/rate.c | 6 ++++-- net/mac80211/rate.h | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3fa7ab28506..4641f00a1e5 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -219,10 +219,12 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, info->control.rates[i].count = 1; } - if (sta && sdata->force_unicast_rateidx > -1) + if (sta && sdata->force_unicast_rateidx > -1) { info->control.rates[0].idx = sdata->force_unicast_rateidx; - else + } else { ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); + info->flags |= IEEE80211_TX_INTFL_RCALGO; + } /* * try to enforce the maximum rate the user wanted diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index b9164c9a956..2ab5ad9e71c 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -44,8 +44,10 @@ static inline void rate_control_tx_status(struct ieee80211_local *local, struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); + if (likely(info->flags & IEEE80211_TX_INTFL_RCALGO)) + ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); } -- cgit v1.2.3 From 3832c287f11ba001bbe48e9be8c59cb9f71f6b43 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Mar 2009 08:46:57 +0100 Subject: mac80211: fix RX path My previous patch ("mac80211: remove mixed-cell and userspace MLME code") was too obvious to me, so obvious that a stupid bug crept in. The IBSS RX function must be invoked for IBSS, of course, not anything != IBSS. Reported-by: Larry Finger Signed-off-by: Johannes Berg Tested-by: Larry Finger Signed-off-by: John W. Linville --- net/mac80211/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index eff59f36e8e..64ebe664eff 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1884,7 +1884,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) if (ieee80211_vif_is_mesh(&sdata->vif)) return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status); - if (sdata->vif.type != NL80211_IFTYPE_ADHOC) + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); if (sdata->vif.type == NL80211_IFTYPE_STATION) -- cgit v1.2.3 From 4bbf4d56583dd52c429d88f43cb614bdbe5deea6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Mar 2009 09:35:46 +0100 Subject: cfg80211: fix locking in nl80211_set_wiphy Luis reports that there's a circular locking dependency; this is because cfg80211_dev_rename() will acquire the cfg80211_mutex while the device mutex is held, while this normally is done the other way around. The solution is to open-code the device-getting in nl80211_set_wiphy and require holding the mutex around cfg80211_dev_rename rather than acquiring it within. Also fix a bug -- rtnl locking is expected by drivers so we need to provide it. Reported-by: Luis R. Rodriguez Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 30 ++++++++++-------------------- net/wireless/core.h | 3 +++ net/wireless/nl80211.c | 28 ++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 17fe3904974..d1f556535f6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -87,7 +87,7 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) } /* requires cfg80211_mutex to be held! */ -static struct cfg80211_registered_device * +struct cfg80211_registered_device * __cfg80211_drv_from_info(struct genl_info *info) { int ifindex; @@ -176,13 +176,14 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv) mutex_unlock(&drv->mtx); } +/* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { struct cfg80211_registered_device *drv; int wiphy_idx, taken = -1, result, digits; - mutex_lock(&cfg80211_mutex); + assert_cfg80211_lock(); /* prohibit calling the thing phy%d when %d is not its number */ sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); @@ -195,30 +196,23 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, * deny the name if it is phy where is printed * without leading zeroes. taken == strlen(newname) here */ - result = -EINVAL; if (taken == strlen(PHY_NAME) + digits) - goto out_unlock; + return -EINVAL; } /* Ignore nop renames */ - result = 0; if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) - goto out_unlock; + return 0; /* Ensure another device does not already have this name. */ - list_for_each_entry(drv, &cfg80211_drv_list, list) { - result = -EINVAL; + list_for_each_entry(drv, &cfg80211_drv_list, list) if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0) - goto out_unlock; - } + return -EINVAL; - /* this will only check for collisions in sysfs - * which is not even always compiled in. - */ result = device_rename(&rdev->wiphy.dev, newname); if (result) - goto out_unlock; + return result; if (rdev->wiphy.debugfsdir && !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, @@ -228,13 +222,9 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n", newname); - result = 0; -out_unlock: - mutex_unlock(&cfg80211_mutex); - if (result == 0) - nl80211_notify_dev_rename(rdev); + nl80211_notify_dev_rename(rdev); - return result; + return 0; } /* exported functions */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 97a6fd8b2b0..d43daa236ef 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -99,6 +99,9 @@ struct cfg80211_internal_bss { struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); +struct cfg80211_registered_device * +__cfg80211_drv_from_info(struct genl_info *info); + /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8808431bd58..353e1a4ece8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -366,16 +366,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) int result = 0, rem_txq_params = 0; struct nlattr *nl_txq_params; - rdev = cfg80211_get_dev_from_info(info); - if (IS_ERR(rdev)) - return PTR_ERR(rdev); + rtnl_lock(); + + mutex_lock(&cfg80211_mutex); + + rdev = __cfg80211_drv_from_info(info); + if (IS_ERR(rdev)) { + result = PTR_ERR(rdev); + goto unlock; + } - if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { + mutex_lock(&rdev->mtx); + + if (info->attrs[NL80211_ATTR_WIPHY_NAME]) result = cfg80211_dev_rename( rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); - if (result) - goto bad_res; - } + + mutex_unlock(&cfg80211_mutex); + + if (result) + goto bad_res; if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { struct ieee80211_txq_params txq_params; @@ -471,7 +481,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) bad_res: - cfg80211_put_dev(rdev); + mutex_unlock(&rdev->mtx); + unlock: + rtnl_unlock(); return result; } -- cgit v1.2.3 From 2de8e0d999b8790861cd3749bec2236ccc1c8110 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:35 +0100 Subject: mac80211: rewrite fragmentation Fragmentation currently uses an allocated array to store the fragment skbs, and then keeps track of which have been sent and which are still pending etc. This is rather complicated; make it simpler by just chaining the fragments into skb->next and removing from that list when sent. Also simplifies all code that needs to touch fragments, since it now only needs to walk the skb->next list. This is a prerequisite for fixing the stored packet code, which I need to do for proper aggregation packet storing. Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 7 - net/mac80211/tx.c | 322 +++++++++++++++++++++------------------------ net/mac80211/util.c | 19 +-- net/mac80211/wep.c | 21 +-- net/mac80211/wpa.c | 28 ++-- 5 files changed, 173 insertions(+), 224 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index acba78e1a5c..785f6363a6f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -149,11 +149,6 @@ struct ieee80211_tx_data { struct ieee80211_channel *channel; - /* Extra fragments (in addition to the first fragment - * in skb) */ - struct sk_buff **extra_frag; - int num_extra_frag; - u16 ethertype; unsigned int flags; }; @@ -191,8 +186,6 @@ struct ieee80211_rx_data { struct ieee80211_tx_stored_packet { struct sk_buff *skb; - struct sk_buff **extra_frag; - int num_extra_frag; }; struct beacon_data { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f3f240c6901..51bf49cc75b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -34,8 +34,7 @@ #define IEEE80211_TX_OK 0 #define IEEE80211_TX_AGAIN 1 -#define IEEE80211_TX_FRAG_AGAIN 2 -#define IEEE80211_TX_PENDING 3 +#define IEEE80211_TX_PENDING 2 /* misc utils */ @@ -702,17 +701,62 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) return TX_CONTINUE; } +static int ieee80211_fragment(struct ieee80211_local *local, + struct sk_buff *skb, int hdrlen, + int frag_threshold) +{ + struct sk_buff *tail = skb, *tmp; + int per_fragm = frag_threshold - hdrlen - FCS_LEN; + int pos = hdrlen + per_fragm; + int rem = skb->len - hdrlen - per_fragm; + + if (WARN_ON(rem < 0)) + return -EINVAL; + + while (rem) { + int fraglen = per_fragm; + + if (fraglen > rem) + fraglen = rem; + rem -= fraglen; + tmp = dev_alloc_skb(local->tx_headroom + + frag_threshold + + IEEE80211_ENCRYPT_HEADROOM + + IEEE80211_ENCRYPT_TAILROOM); + if (!tmp) + return -ENOMEM; + tail->next = tmp; + tail = tmp; + skb_reserve(tmp, local->tx_headroom + + IEEE80211_ENCRYPT_HEADROOM); + /* copy control information */ + memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); + skb_copy_queue_mapping(tmp, skb); + tmp->priority = skb->priority; + tmp->do_not_encrypt = skb->do_not_encrypt; + tmp->dev = skb->dev; + tmp->iif = skb->iif; + + /* copy header and data */ + memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen); + memcpy(skb_put(tmp, fraglen), skb->data + pos, fraglen); + + pos += fraglen; + } + + skb->len = hdrlen + per_fragm; + return 0; +} + static ieee80211_tx_result debug_noinline ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - size_t hdrlen, per_fragm, num_fragm, payload_len, left; - struct sk_buff **frags, *first, *frag; - int i; - u16 seq; - u8 *pos; + struct sk_buff *skb = tx->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; int frag_threshold = tx->local->fragmentation_threshold; + int hdrlen; + int fragnum; if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) return TX_CONTINUE; @@ -725,58 +769,35 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) return TX_DROP; - first = tx->skb; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - payload_len = first->len - hdrlen; - per_fragm = frag_threshold - hdrlen - FCS_LEN; - num_fragm = DIV_ROUND_UP(payload_len, per_fragm); - - frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC); - if (!frags) - goto fail; - - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); - seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ; - pos = first->data + hdrlen + per_fragm; - left = payload_len - per_fragm; - for (i = 0; i < num_fragm - 1; i++) { - struct ieee80211_hdr *fhdr; - size_t copylen; - - if (left <= 0) - goto fail; - /* reserve enough extra head and tail room for possible - * encryption */ - frag = frags[i] = - dev_alloc_skb(tx->local->tx_headroom + - frag_threshold + - IEEE80211_ENCRYPT_HEADROOM + - IEEE80211_ENCRYPT_TAILROOM); - if (!frag) - goto fail; - - /* Make sure that all fragments use the same priority so - * that they end up using the same TX queue */ - frag->priority = first->priority; + /* internal error, why is TX_FRAGMENTED set? */ + if (WARN_ON(skb->len <= frag_threshold)) + return TX_DROP; - skb_reserve(frag, tx->local->tx_headroom + - IEEE80211_ENCRYPT_HEADROOM); + /* + * Now fragment the frame. This will allocate all the fragments and + * chain them (using skb as the first fragment) to skb->next. + * During transmission, we will remove the successfully transmitted + * fragments from this list. When the low-level driver rejects one + * of the fragments then we will simply pretend to accept the skb + * but store it away as pending. + */ + if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold)) + return TX_DROP; - /* copy TX information */ - info = IEEE80211_SKB_CB(frag); - memcpy(info, first->cb, sizeof(frag->cb)); + /* update duration/seq/flags of fragments */ + fragnum = 0; + do { + int next_len; + const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); - /* copy/fill in 802.11 header */ - fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen); - memcpy(fhdr, first->data, hdrlen); - fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG)); + hdr = (void *)skb->data; + info = IEEE80211_SKB_CB(skb); - if (i == num_fragm - 2) { - /* clear MOREFRAGS bit for the last fragment */ - fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS); - } else { + if (skb->next) { + hdr->frame_control |= morefrags; + next_len = skb->next->len; /* * No multi-rate retries for fragmented frames, that * would completely throw off the NAV at other STAs. @@ -787,37 +808,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) info->control.rates[4].idx = -1; BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5); info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE; + } else { + hdr->frame_control &= ~morefrags; + next_len = 0; } - - /* copy data */ - copylen = left > per_fragm ? per_fragm : left; - memcpy(skb_put(frag, copylen), pos, copylen); - - skb_copy_queue_mapping(frag, first); - - frag->do_not_encrypt = first->do_not_encrypt; - frag->dev = first->dev; - frag->iif = first->iif; - - pos += copylen; - left -= copylen; - } - skb_trim(first, hdrlen + per_fragm); - - tx->num_extra_frag = num_fragm - 1; - tx->extra_frag = frags; + hdr->duration_id = ieee80211_duration(tx, 0, next_len); + hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG); + fragnum++; + } while ((skb = skb->next)); return TX_CONTINUE; - - fail: - if (frags) { - for (i = 0; i < num_fragm - 1; i++) - if (frags[i]) - dev_kfree_skb(frags[i]); - kfree(frags); - } - I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment); - return TX_DROP; } static ieee80211_tx_result debug_noinline @@ -845,27 +845,19 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - int next_len, i; - int group_addr = is_multicast_ether_addr(hdr->addr1); - - if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) { - hdr->duration_id = ieee80211_duration(tx, group_addr, 0); - return TX_CONTINUE; - } - - hdr->duration_id = ieee80211_duration(tx, group_addr, - tx->extra_frag[0]->len); + struct sk_buff *skb = tx->skb; + struct ieee80211_hdr *hdr; + int next_len; + bool group_addr; - for (i = 0; i < tx->num_extra_frag; i++) { - if (i + 1 < tx->num_extra_frag) - next_len = tx->extra_frag[i + 1]->len; - else - next_len = 0; + do { + hdr = (void *) skb->data; + next_len = skb->next ? skb->next->len : 0; + group_addr = is_multicast_ether_addr(hdr->addr1); - hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data; - hdr->duration_id = ieee80211_duration(tx, 0, next_len); - } + hdr->duration_id = + ieee80211_duration(tx, group_addr, next_len); + } while ((skb = skb->next)); return TX_CONTINUE; } @@ -873,19 +865,16 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) { - int i; + struct sk_buff *skb = tx->skb; if (!tx->sta) return TX_CONTINUE; tx->sta->tx_packets++; - tx->sta->tx_fragments++; - tx->sta->tx_bytes += tx->skb->len; - if (tx->extra_frag) { - tx->sta->tx_fragments += tx->num_extra_frag; - for (i = 0; i < tx->num_extra_frag; i++) - tx->sta->tx_bytes += tx->extra_frag[i]->len; - } + do { + tx->sta->tx_fragments++; + tx->sta->tx_bytes += skb->len; + } while ((skb = skb->next)); return TX_CONTINUE; } @@ -1099,45 +1088,36 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, return 0; } -static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, +static int __ieee80211_tx(struct ieee80211_local *local, struct ieee80211_tx_data *tx) { + struct sk_buff *skb = tx->skb, *next; struct ieee80211_tx_info *info; - int ret, i; + int ret; + bool fragm = false; - if (skb) { + local->mdev->trans_start = jiffies; + + while (skb) { if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) return IEEE80211_TX_PENDING; - ret = local->ops->tx(local_to_hw(local), skb); - if (ret) - return IEEE80211_TX_AGAIN; - local->mdev->trans_start = jiffies; - ieee80211_led_tx(local, 1); - } - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (!tx->extra_frag[i]) - continue; - info = IEEE80211_SKB_CB(tx->extra_frag[i]); + if (fragm) { + info = IEEE80211_SKB_CB(skb); info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); - if (ieee80211_queue_stopped(&local->hw, - skb_get_queue_mapping(tx->extra_frag[i]))) - return IEEE80211_TX_FRAG_AGAIN; - - ret = local->ops->tx(local_to_hw(local), - tx->extra_frag[i]); - if (ret) - return IEEE80211_TX_FRAG_AGAIN; - local->mdev->trans_start = jiffies; - ieee80211_led_tx(local, 1); - tx->extra_frag[i] = NULL; } - kfree(tx->extra_frag); - tx->extra_frag = NULL; + + next = skb->next; + ret = local->ops->tx(local_to_hw(local), skb); + if (ret != NETDEV_TX_OK) + return IEEE80211_TX_AGAIN; + tx->skb = skb = next; + ieee80211_led_tx(local, 1); + fragm = true; } + return IEEE80211_TX_OK; } @@ -1149,7 +1129,6 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; ieee80211_tx_result res = TX_DROP; - int i; #define CALL_TXH(txh) \ res = txh(tx); \ @@ -1173,11 +1152,13 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) txh_done: if (unlikely(res == TX_DROP)) { I802_DEBUG_INC(tx->local->tx_handlers_drop); - dev_kfree_skb(skb); - for (i = 0; i < tx->num_extra_frag; i++) - if (tx->extra_frag[i]) - dev_kfree_skb(tx->extra_frag[i]); - kfree(tx->extra_frag); + while (skb) { + struct sk_buff *next; + + next = skb->next; + dev_kfree_skb(skb); + skb = next; + } return -1; } else if (unlikely(res == TX_QUEUED)) { I802_DEBUG_INC(tx->local->tx_handlers_queued); @@ -1194,7 +1175,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int ret, i; + int ret; u16 queue; queue = skb_get_queue_mapping(skb); @@ -1225,7 +1206,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) goto out; retry: - ret = __ieee80211_tx(local, skb, &tx); + ret = __ieee80211_tx(local, &tx); if (ret) { struct ieee80211_tx_stored_packet *store; @@ -1240,9 +1221,6 @@ retry: store = &local->pending_packet[queue]; - if (ret == IEEE80211_TX_FRAG_AGAIN) - skb = NULL; - set_bit(queue, local->queues_pending); smp_mb(); /* @@ -1260,22 +1238,23 @@ retry: clear_bit(queue, local->queues_pending); goto retry; } - store->skb = skb; - store->extra_frag = tx.extra_frag; - store->num_extra_frag = tx.num_extra_frag; + store->skb = tx.skb; } out: rcu_read_unlock(); return 0; drop: - if (skb) - dev_kfree_skb(skb); - for (i = 0; i < tx.num_extra_frag; i++) - if (tx.extra_frag[i]) - dev_kfree_skb(tx.extra_frag[i]); - kfree(tx.extra_frag); rcu_read_unlock(); + + skb = tx.skb; + while (skb) { + struct sk_buff *next; + + next = skb->next; + dev_kfree_skb(skb); + skb = next; + } return 0; } @@ -1810,17 +1789,21 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) { - int i, j; - struct ieee80211_tx_stored_packet *store; + struct sk_buff *skb; + int i; for (i = 0; i < local->hw.queues; i++) { if (!test_bit(i, local->queues_pending)) continue; - store = &local->pending_packet[i]; - kfree_skb(store->skb); - for (j = 0; j < store->num_extra_frag; j++) - kfree_skb(store->extra_frag[j]); - kfree(store->extra_frag); + + skb = local->pending_packet[i].skb; + while (skb) { + struct sk_buff *next; + + next = skb->next; + dev_kfree_skb(skb); + skb = next; + } clear_bit(i, local->queues_pending); } } @@ -1854,14 +1837,11 @@ void ieee80211_tx_pending(unsigned long data) netif_start_subqueue(local->mdev, i); store = &local->pending_packet[i]; - tx.extra_frag = store->extra_frag; - tx.num_extra_frag = store->num_extra_frag; tx.flags = 0; - ret = __ieee80211_tx(local, store->skb, &tx); - if (ret) { - if (ret == IEEE80211_TX_FRAG_AGAIN) - store->skb = NULL; - } else { + tx.skb = store->skb; + ret = __ieee80211_tx(local, &tx); + store->skb = tx.skb; + if (!ret) { clear_bit(i, local->queues_pending); ieee80211_wake_queue(&local->hw, i); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 444bb14c95e..021166c8cce 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -166,18 +166,13 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; - - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - if (tx->extra_frag) { - struct ieee80211_hdr *fhdr; - int i; - for (i = 0; i < tx->num_extra_frag; i++) { - fhdr = (struct ieee80211_hdr *) - tx->extra_frag[i]->data; - fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - } - } + struct sk_buff *skb = tx->skb; + struct ieee80211_hdr *hdr; + + do { + hdr = (struct ieee80211_hdr *) skb->data; + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + } while ((skb = skb->next)); } int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 7043ddc7549..ef73105b306 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -329,24 +329,17 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ieee80211_tx_result ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx) { - int i; + struct sk_buff *skb; ieee80211_tx_set_protected(tx); - if (wep_encrypt_skb(tx, tx->skb) < 0) { - I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); - return TX_DROP; - } - - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (wep_encrypt_skb(tx, tx->extra_frag[i])) { - I802_DEBUG_INC(tx->local-> - tx_handlers_drop_wep); - return TX_DROP; - } + skb = tx->skb; + do { + if (wep_encrypt_skb(tx, skb) < 0) { + I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); + return TX_DROP; } - } + } while ((skb = skb->next)); return TX_CONTINUE; } diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 9101b48ec2a..4f8bfea278f 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -196,19 +196,13 @@ ieee80211_tx_result ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; - int i; ieee80211_tx_set_protected(tx); - if (tkip_encrypt_skb(tx, skb) < 0) - return TX_DROP; - - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (tkip_encrypt_skb(tx, tx->extra_frag[i])) - return TX_DROP; - } - } + do { + if (tkip_encrypt_skb(tx, skb) < 0) + return TX_DROP; + } while ((skb = skb->next)); return TX_CONTINUE; } @@ -428,19 +422,13 @@ ieee80211_tx_result ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; - int i; ieee80211_tx_set_protected(tx); - if (ccmp_encrypt_skb(tx, skb) < 0) - return TX_DROP; - - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (ccmp_encrypt_skb(tx, tx->extra_frag[i])) - return TX_DROP; - } - } + do { + if (ccmp_encrypt_skb(tx, skb) < 0) + return TX_DROP; + } while ((skb = skb->next)); return TX_CONTINUE; } -- cgit v1.2.3 From f0e72851f7ad108fed20426b46a18ab5fcd5729f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:36 +0100 Subject: mac80211: fix A-MPDU queue assignment Internally, mac80211 requires the skb's queue mapping to be set to the AC queue, not the virtual A-MPDU queue. This is not done correctly currently, this patch moves the code down to directly before the driver is invoked and adds a comment that it will be moved into the driver later. Since this requires __ieee80211_tx() to have the sta pointer, make sure to provide it in ieee80211_tx_pending(). Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/tx.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 51bf49cc75b..0d97cad84b1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1024,13 +1024,8 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, spin_lock_irqsave(&tx->sta->lock, flags); state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; - if (*state == HT_AGG_STATE_OPERATIONAL) { + if (*state == HT_AGG_STATE_OPERATIONAL) info->flags |= IEEE80211_TX_CTL_AMPDU; - if (local->hw.ampdu_queues) - skb_set_queue_mapping( - skb, tx->local->hw.queues + - tx->sta->tid_to_tx_q[tid]); - } spin_unlock_irqrestore(&tx->sta->lock, flags); } @@ -1103,10 +1098,29 @@ static int __ieee80211_tx(struct ieee80211_local *local, skb_get_queue_mapping(skb))) return IEEE80211_TX_PENDING; - if (fragm) { - info = IEEE80211_SKB_CB(skb); + info = IEEE80211_SKB_CB(skb); + + if (fragm) info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); + + /* + * Internally, we need to have the queue mapping point to + * the real AC queue, not the virtual A-MPDU queue. This + * now finally sets the queue to what the driver wants. + * We will later move this down into the only driver that + * needs it, iwlwifi. + */ + if (tx->sta && local->hw.ampdu_queues && + info->flags & IEEE80211_TX_CTL_AMPDU) { + unsigned long flags; + u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); + int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + + spin_lock_irqsave(&tx->sta->lock, flags); + skb_set_queue_mapping(skb, local->hw.queues + + tx->sta->tid_to_tx_q[tid]); + spin_unlock_irqrestore(&tx->sta->lock, flags); } next = skb->next; @@ -1817,9 +1831,11 @@ void ieee80211_tx_pending(unsigned long data) struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; struct ieee80211_tx_stored_packet *store; + struct ieee80211_hdr *hdr; struct ieee80211_tx_data tx; int i, ret; + rcu_read_lock(); netif_tx_lock_bh(dev); for (i = 0; i < local->hw.queues; i++) { /* Check that this queue is ok */ @@ -1839,6 +1855,8 @@ void ieee80211_tx_pending(unsigned long data) store = &local->pending_packet[i]; tx.flags = 0; tx.skb = store->skb; + hdr = (struct ieee80211_hdr *)tx.skb->data; + tx.sta = sta_info_get(local, hdr->addr1); ret = __ieee80211_tx(local, &tx); store->skb = tx.skb; if (!ret) { @@ -1847,6 +1865,7 @@ void ieee80211_tx_pending(unsigned long data) } } netif_tx_unlock_bh(dev); + rcu_read_unlock(); } /* functions for drivers to get certain frames */ -- cgit v1.2.3 From 2a577d98712a284a612dd51d69db5cb989810dc2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:37 +0100 Subject: mac80211: rework the pending packets code The pending packets code is quite incomprehensible, uses memory barriers nobody really understands, etc. This patch reworks it entirely, using the queue spinlock, proper stop bits and the skb queues themselves to indicate whether packets are pending or not (rather than a separate variable like before). Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 9 +-- net/mac80211/main.c | 2 + net/mac80211/tx.c | 144 +++++++++++++++++++++++++-------------------- net/mac80211/util.c | 22 ++++--- 4 files changed, 98 insertions(+), 79 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 785f6363a6f..6ce62e553dc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -184,10 +184,6 @@ struct ieee80211_rx_data { u16 tkip_iv16; }; -struct ieee80211_tx_stored_packet { - struct sk_buff *skb; -}; - struct beacon_data { u8 *head, *tail; int head_len, tail_len; @@ -583,6 +579,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_CSA, IEEE80211_QUEUE_STOP_REASON_AGGREGATION, IEEE80211_QUEUE_STOP_REASON_SUSPEND, + IEEE80211_QUEUE_STOP_REASON_PENDING, }; struct ieee80211_master_priv { @@ -639,9 +636,7 @@ struct ieee80211_local { struct sta_info *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; - unsigned long queues_pending[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)]; - unsigned long queues_pending_run[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)]; - struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_QUEUES]; + struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; /* number of interfaces with corresponding IFF_ flags */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index dac68d476bf..a7430e98c53 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -781,6 +781,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, sta_info_init(local); + for (i = 0; i < IEEE80211_MAX_QUEUES; i++) + skb_queue_head_init(&local->pending[i]); tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, (unsigned long)local); tasklet_disable(&local->tx_pending_tasklet); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0d97cad84b1..ee1b77f8a80 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1189,12 +1189,14 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int ret; + struct sk_buff *next; + unsigned long flags; + int ret, retries; u16 queue; queue = skb_get_queue_mapping(skb); - WARN_ON(test_bit(queue, local->queues_pending)); + WARN_ON(!skb_queue_empty(&local->pending[queue])); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); @@ -1219,40 +1221,52 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) if (invoke_tx_handlers(&tx)) goto out; -retry: + retries = 0; + retry: ret = __ieee80211_tx(local, &tx); - if (ret) { - struct ieee80211_tx_stored_packet *store; - + switch (ret) { + case IEEE80211_TX_OK: + break; + case IEEE80211_TX_AGAIN: /* * Since there are no fragmented frames on A-MPDU * queues, there's no reason for a driver to reject * a frame there, warn and drop it. */ - if (ret != IEEE80211_TX_PENDING) - if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) - goto drop; + if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) + goto drop; + /* fall through */ + case IEEE80211_TX_PENDING: + skb = tx.skb; - store = &local->pending_packet[queue]; + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - set_bit(queue, local->queues_pending); - smp_mb(); - /* - * When the driver gets out of buffers during sending of - * fragments and calls ieee80211_stop_queue, the netif - * subqueue is stopped. There is, however, a small window - * in which the PENDING bit is not yet set. If a buffer - * gets available in that window (i.e. driver calls - * ieee80211_wake_queue), we would end up with ieee80211_tx - * called with the PENDING bit still set. Prevent this by - * continuing transmitting here when that situation is - * possible to have happened. - */ - if (!__netif_subqueue_stopped(local->mdev, queue)) { - clear_bit(queue, local->queues_pending); + if (__netif_subqueue_stopped(local->mdev, queue)) { + do { + next = skb->next; + skb->next = NULL; + skb_queue_tail(&local->pending[queue], skb); + } while ((skb = next)); + + /* + * Make sure nobody will enable the queue on us + * (without going through the tasklet) nor disable the + * netdev queue underneath the pending handling code. + */ + __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, + &local->queue_stop_reasons[queue]); + + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + } else { + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + + retries++; + if (WARN(retries > 10, "tx refused but queue active")) + goto drop; goto retry; } - store->skb = tx.skb; } out: rcu_read_unlock(); @@ -1263,8 +1277,6 @@ retry: skb = tx.skb; while (skb) { - struct sk_buff *next; - next = skb->next; dev_kfree_skb(skb); skb = next; @@ -1803,23 +1815,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) { - struct sk_buff *skb; int i; - for (i = 0; i < local->hw.queues; i++) { - if (!test_bit(i, local->queues_pending)) - continue; - - skb = local->pending_packet[i].skb; - while (skb) { - struct sk_buff *next; - - next = skb->next; - dev_kfree_skb(skb); - skb = next; - } - clear_bit(i, local->queues_pending); - } + for (i = 0; i < local->hw.queues; i++) + skb_queue_purge(&local->pending[i]); } /* @@ -1830,40 +1829,57 @@ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; - struct ieee80211_tx_stored_packet *store; struct ieee80211_hdr *hdr; + unsigned long flags; struct ieee80211_tx_data tx; int i, ret; + bool next; rcu_read_lock(); netif_tx_lock_bh(dev); - for (i = 0; i < local->hw.queues; i++) { - /* Check that this queue is ok */ - if (__netif_subqueue_stopped(local->mdev, i) && - !test_bit(i, local->queues_pending_run)) - continue; - if (!test_bit(i, local->queues_pending)) { - clear_bit(i, local->queues_pending_run); - ieee80211_wake_queue(&local->hw, i); + for (i = 0; i < local->hw.queues; i++) { + /* + * If queue is stopped by something other than due to pending + * frames, or we have no pending frames, proceed to next queue. + */ + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + next = false; + if (local->queue_stop_reasons[i] != + BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) || + skb_queue_empty(&local->pending[i])) + next = true; + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + + if (next) continue; - } - clear_bit(i, local->queues_pending_run); + /* + * start the queue now to allow processing our packets, + * we're under the tx lock here anyway so nothing will + * happen as a result of this + */ netif_start_subqueue(local->mdev, i); - store = &local->pending_packet[i]; - tx.flags = 0; - tx.skb = store->skb; - hdr = (struct ieee80211_hdr *)tx.skb->data; - tx.sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &tx); - store->skb = tx.skb; - if (!ret) { - clear_bit(i, local->queues_pending); - ieee80211_wake_queue(&local->hw, i); + while (!skb_queue_empty(&local->pending[i])) { + tx.flags = 0; + tx.skb = skb_dequeue(&local->pending[i]); + hdr = (struct ieee80211_hdr *)tx.skb->data; + tx.sta = sta_info_get(local, hdr->addr1); + + ret = __ieee80211_tx(local, &tx); + if (ret != IEEE80211_TX_OK) { + skb_queue_head(&local->pending[i], tx.skb); + break; + } } + + /* Start regular packet processing again. */ + if (skb_queue_empty(&local->pending[i])) + ieee80211_wake_queue_by_reason(&local->hw, i, + IEEE80211_QUEUE_STOP_REASON_PENDING); } + netif_tx_unlock_bh(dev); rcu_read_unlock(); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 021166c8cce..0247d8022f5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -365,16 +365,16 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, __clear_bit(reason, &local->queue_stop_reasons[queue]); + if (!skb_queue_empty(&local->pending[queue]) && + local->queue_stop_reasons[queue] == + BIT(IEEE80211_QUEUE_STOP_REASON_PENDING)) + tasklet_schedule(&local->tx_pending_tasklet); + if (local->queue_stop_reasons[queue] != 0) /* someone still has this queue stopped */ return; - if (test_bit(queue, local->queues_pending)) { - set_bit(queue, local->queues_pending_run); - tasklet_schedule(&local->tx_pending_tasklet); - } else { - netif_wake_subqueue(local->mdev, queue); - } + netif_wake_subqueue(local->mdev, queue); } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -420,9 +420,15 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; } - __set_bit(reason, &local->queue_stop_reasons[queue]); + /* + * Only stop if it was previously running, this is necessary + * for correct pending packets handling because there we may + * start (but not wake) the queue and rely on that. + */ + if (!local->queue_stop_reasons[queue]) + netif_stop_subqueue(local->mdev, queue); - netif_stop_subqueue(local->mdev, queue); + __set_bit(reason, &local->queue_stop_reasons[queue]); } void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, -- cgit v1.2.3 From 1870cd71e87da1a1afb904f2c84086f487a07135 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:38 +0100 Subject: mac80211: clean up __ieee80211_tx args __ieee80211_tx takes a struct ieee80211_tx_data argument, but only uses a few of its members, namely 'skb' and 'sta'. Make that explicit, so that less internal knowledge is required in ieee80211_tx_pending and the possibility of introducing errors here is removed. Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/tx.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ee1b77f8a80..b909e4090e9 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1084,9 +1084,10 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, } static int __ieee80211_tx(struct ieee80211_local *local, - struct ieee80211_tx_data *tx) + struct sk_buff **skbp, + struct sta_info *sta) { - struct sk_buff *skb = tx->skb, *next; + struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; int ret; bool fragm = false; @@ -1111,23 +1112,23 @@ static int __ieee80211_tx(struct ieee80211_local *local, * We will later move this down into the only driver that * needs it, iwlwifi. */ - if (tx->sta && local->hw.ampdu_queues && + if (sta && local->hw.ampdu_queues && info->flags & IEEE80211_TX_CTL_AMPDU) { unsigned long flags; u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - spin_lock_irqsave(&tx->sta->lock, flags); + spin_lock_irqsave(&sta->lock, flags); skb_set_queue_mapping(skb, local->hw.queues + - tx->sta->tid_to_tx_q[tid]); - spin_unlock_irqrestore(&tx->sta->lock, flags); + sta->tid_to_tx_q[tid]); + spin_unlock_irqrestore(&sta->lock, flags); } next = skb->next; ret = local->ops->tx(local_to_hw(local), skb); if (ret != NETDEV_TX_OK) return IEEE80211_TX_AGAIN; - tx->skb = skb = next; + *skbp = skb = next; ieee80211_led_tx(local, 1); fragm = true; } @@ -1223,7 +1224,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) retries = 0; retry: - ret = __ieee80211_tx(local, &tx); + ret = __ieee80211_tx(local, &tx.skb, tx.sta); switch (ret) { case IEEE80211_TX_OK: break; @@ -1831,7 +1832,6 @@ void ieee80211_tx_pending(unsigned long data) struct net_device *dev = local->mdev; struct ieee80211_hdr *hdr; unsigned long flags; - struct ieee80211_tx_data tx; int i, ret; bool next; @@ -1862,14 +1862,15 @@ void ieee80211_tx_pending(unsigned long data) netif_start_subqueue(local->mdev, i); while (!skb_queue_empty(&local->pending[i])) { - tx.flags = 0; - tx.skb = skb_dequeue(&local->pending[i]); - hdr = (struct ieee80211_hdr *)tx.skb->data; - tx.sta = sta_info_get(local, hdr->addr1); + struct sk_buff *skb = skb_dequeue(&local->pending[i]); + struct sta_info *sta; + + hdr = (struct ieee80211_hdr *)skb->data; + sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &tx); + ret = __ieee80211_tx(local, &skb, sta); if (ret != IEEE80211_TX_OK) { - skb_queue_head(&local->pending[i], tx.skb); + skb_queue_head(&local->pending[i], skb); break; } } -- cgit v1.2.3 From b1720231ca07dee3382980f3b25e6581bd2e54e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:39 +0100 Subject: mac80211: unify and fix TX aggregation start When TX aggregation becomes operational, we do a number of steps: 1) print a debug message 2) wake the virtual queue 3) notify the driver Unfortunately, 1) and 3) are only done if the driver is first to reply to the aggregation request, it is, however, possible that the remote station replies before the driver! Thus, unify the code for this and call the new function ieee80211_agg_tx_operational in both places where TX aggregation can become operational. Additionally, rename the driver notification from IEEE80211_AMPDU_TX_RESUME to IEEE80211_AMPDU_TX_OPERATIONAL. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-tx.c | 63 ++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e5776ef1717..fd718e2b29f 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -404,6 +404,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); +static void ieee80211_agg_tx_operational(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); +#endif + + if (local->hw.ampdu_queues) { + /* + * Wake up the A-MPDU queue, we stopped it earlier, + * this will in turn wake the entire AC. + */ + ieee80211_wake_queue_by_reason(&local->hw, + local->hw.queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } + + local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, + &sta->sta, tid, NULL); +} + void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) { struct ieee80211_local *local = hw_to_local(hw); @@ -446,20 +467,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) *state |= HT_ADDBA_DRV_READY_MSK; - if (*state == HT_AGG_STATE_OPERATIONAL) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); -#endif - if (hw->ampdu_queues) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - } + if (*state == HT_AGG_STATE_OPERATIONAL) + ieee80211_agg_tx_operational(local, sta, tid); out: spin_unlock_bh(&sta->lock); @@ -646,9 +655,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len) { - struct ieee80211_hw *hw = &local->hw; - u16 capab; - u16 tid, start_seq_num; + u16 capab, tid; u8 *state; capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); @@ -682,26 +689,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, *state |= HT_ADDBA_RECEIVED_MSK; - if (hw->ampdu_queues && *state != curstate && - *state == HT_AGG_STATE_OPERATIONAL) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - sta->ampdu_mlme.addba_req_num[tid] = 0; + if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL) + ieee80211_agg_tx_operational(local, sta, tid); - if (local->ops->ampdu_action) { - (void)local->ops->ampdu_action(hw, - IEEE80211_AMPDU_TX_RESUME, - &sta->sta, tid, &start_seq_num); - } -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + sta->ampdu_mlme.addba_req_num[tid] = 0; } else { sta->ampdu_mlme.addba_req_num[tid]++; ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); -- cgit v1.2.3 From a220858d30604902f650074bfac5a7598bc97ea4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:40 +0100 Subject: mac80211: add skb length sanity checking We just found a bug in zd1211rw where it would reject packets in the ->tx() method but leave them modified, which would cause retransmit attempts with completely bogus skbs, eventually leading to a panic due to not having enough headroom in those. This patch adds a sanity check to mac80211 to catch such driver mistakes; in this case we warn and drop the skb. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b909e4090e9..a0e00c6339c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1089,7 +1089,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, { struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; - int ret; + int ret, len; bool fragm = false; local->mdev->trans_start = jiffies; @@ -1125,7 +1125,12 @@ static int __ieee80211_tx(struct ieee80211_local *local, } next = skb->next; + len = skb->len; ret = local->ops->tx(local_to_hw(local), skb); + if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { + dev_kfree_skb(skb); + ret = NETDEV_TX_OK; + } if (ret != NETDEV_TX_OK) return IEEE80211_TX_AGAIN; *skbp = skb = next; -- cgit v1.2.3 From cd8ffc800ce18e558335c4946b2217864fc16045 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:41 +0100 Subject: mac80211: fix aggregation to not require queue stop Instead of stopping the entire AC queue when enabling aggregation (which was only done for hardware with aggregation queues) buffer the packets for each station, and release them to the pending skb queue once aggregation is turned on successfully. We get a little more code, but it becomes conceptually simpler and we can remove the entire virtual queue mechanism from mac80211 in a follow-up patch. This changes how mac80211 behaves towards drivers that support aggregation but have no hardware queues -- those drivers will now not be handed packets while the aggregation session is being established, but only after it has been fully established. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-tx.c | 136 ++++++++++++++++++++++++++----------------- net/mac80211/ieee80211_i.h | 8 +++ net/mac80211/main.c | 2 + net/mac80211/sta_info.c | 5 ++ net/mac80211/sta_info.h | 2 + net/mac80211/tx.c | 142 ++++++++++++++++++++++++++++++++++++--------- 6 files changed, 217 insertions(+), 78 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index fd718e2b29f..64b839bfbf1 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -132,16 +132,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; if (local->hw.ampdu_queues) { - if (initiator) { - /* - * Stop the AC queue to avoid issues where we send - * unaggregated frames already before the delba. - */ - ieee80211_stop_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - /* * Pretend the driver woke the queue, just in case * it disabled it before the session was stopped. @@ -158,6 +148,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { *state = HT_AGG_STATE_OPERATIONAL; + /* + * We may have pending packets get stuck in this case... + * Not bothering with a workaround for now. + */ } return ret; @@ -226,13 +220,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "rejecting on voice AC\n"); -#endif - return -EINVAL; - } - rcu_read_lock(); sta = sta_info_get(local, ra); @@ -267,6 +254,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } spin_lock_bh(&sta->lock); + spin_lock(&local->ampdu_lock); sdata = sta->sdata; @@ -308,21 +296,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ret = -ENOSPC; goto err_unlock_sta; } - - /* - * If we successfully allocate the session, we can't have - * anything going on on the queue this TID maps into, so - * stop it for now. This is a "virtual" stop using the same - * mechanism that drivers will use. - * - * XXX: queue up frames for this session in the sta_info - * struct instead to avoid hitting all other STAs. - */ - ieee80211_stop_queue_by_reason( - &local->hw, hw->queues + qn, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); } + /* + * While we're asking the driver about the aggregation, + * stop the AC queue so that we don't have to worry + * about frames that came in while we were doing that, + * which would require us to put them to the AC pending + * afterwards which just makes the code more complex. + */ + ieee80211_stop_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + /* prepare A-MPDU MLME for Tx aggregation */ sta->ampdu_mlme.tid_tx[tid] = kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); @@ -336,6 +322,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_return_queue; } + skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); + /* Tx timer */ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = sta_addba_resp_timer_expired; @@ -362,6 +350,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } sta->tid_to_tx_q[tid] = qn; + /* Driver vetoed or OKed, but we can take packets again now */ + ieee80211_wake_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); /* send an addBA request */ @@ -388,15 +382,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->ampdu_mlme.tid_tx[tid] = NULL; err_return_queue: if (qn >= 0) { - /* We failed, so start queue again right away. */ - ieee80211_wake_queue_by_reason(hw, hw->queues + qn, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); /* give queue back to pool */ spin_lock(&local->queue_stop_reason_lock); local->ampdu_ac_queue[qn] = -1; spin_unlock(&local->queue_stop_reason_lock); } + ieee80211_wake_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); err_unlock_sta: + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); unlock: rcu_read_unlock(); @@ -404,6 +399,45 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); +/* + * splice packets from the STA's pending to the local pending, + * requires a call to ieee80211_agg_splice_finish and holding + * local->ampdu_lock across both calls. + */ +static void ieee80211_agg_splice_packets(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + unsigned long flags; + u16 queue = ieee80211_ac_from_tid(tid); + + ieee80211_stop_queue_by_reason( + &local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + + if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + /* mark queue as pending, it is stopped already */ + __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, + &local->queue_stop_reasons[queue]); + /* copy over remaining packets */ + skb_queue_splice_tail_init( + &sta->ampdu_mlme.tid_tx[tid]->pending, + &local->pending[queue]); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } +} + +static void ieee80211_agg_splice_finish(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + u16 queue = ieee80211_ac_from_tid(tid); + + ieee80211_wake_queue_by_reason( + &local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); +} + +/* caller must hold sta->lock */ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { @@ -411,15 +445,16 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); #endif - if (local->hw.ampdu_queues) { - /* - * Wake up the A-MPDU queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } + spin_lock(&local->ampdu_lock); + ieee80211_agg_splice_packets(local, sta, tid); + /* + * NB: we rely on sta->lock being taken in the TX + * processing here when adding to the pending queue, + * otherwise we could only change the state of the + * session to OPERATIONAL _here_. + */ + ieee80211_agg_splice_finish(local, sta, tid); + spin_unlock(&local->ampdu_lock); local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, &sta->sta, tid, NULL); @@ -602,22 +637,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); spin_lock_bh(&sta->lock); + spin_lock(&local->ampdu_lock); - if (*state & HT_AGG_STATE_INITIATOR_MSK && - hw->ampdu_queues) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } + ieee80211_agg_splice_packets(local, sta, tid); *state = HT_AGG_STATE_IDLE; + /* from now on packets are no longer put onto sta->pending */ sta->ampdu_mlme.addba_req_num[tid] = 0; kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; + + ieee80211_agg_splice_finish(local, sta, tid); + + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); rcu_read_unlock(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6ce62e553dc..32345b479ad 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -639,6 +639,14 @@ struct ieee80211_local { struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; + /* + * This lock is used to prevent concurrent A-MPDU + * session start/stop processing, this thus also + * synchronises the ->ampdu_action() callback to + * drivers and limits it to one at a time. + */ + spinlock_t ampdu_lock; + /* number of interfaces with corresponding IFF_ flags */ atomic_t iff_allmultis, iff_promiscs; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a7430e98c53..756284e0bbd 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -795,6 +795,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); + spin_lock_init(&local->ampdu_lock); + return local_to_hw(local); } EXPORT_SYMBOL(ieee80211_alloc_hw); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4ba3c540fcf..dd3593c1fd2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -239,6 +239,11 @@ void sta_info_destroy(struct sta_info *sta) tid_tx = sta->ampdu_mlme.tid_tx[i]; if (tid_tx) { del_timer_sync(&tid_tx->addba_resp_timer); + /* + * STA removed while aggregation session being + * started? Bit odd, but purge frames anyway. + */ + skb_queue_purge(&tid_tx->pending); kfree(tid_tx); } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5b223b216e5..18fd5d1a442 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -73,11 +73,13 @@ enum ieee80211_sta_info_flags { * struct tid_ampdu_tx - TID aggregation information (Tx). * * @addba_resp_timer: timer for peer's response to addba request + * @pending: pending frames queue -- use sta's spinlock to protect * @ssn: Starting Sequence Number expected to be aggregated. * @dialog_token: dialog token for aggregation session */ struct tid_ampdu_tx { struct timer_list addba_resp_timer; + struct sk_buff_head pending; u16 ssn; u8 dialog_token; }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a0e00c6339c..906ab785db4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -984,9 +984,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *sdata; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int hdrlen, tid; u8 *qc, *state; + bool queued = false; memset(tx, 0, sizeof(*tx)); tx->skb = skb; @@ -1013,20 +1013,53 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, */ } + /* + * If this flag is set to true anywhere, and we get here, + * we are doing the needed processing, so remove the flag + * now. + */ + info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING; + hdr = (struct ieee80211_hdr *) skb->data; tx->sta = sta_info_get(local, hdr->addr1); - if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) { + if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && + (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { unsigned long flags; + struct tid_ampdu_tx *tid_tx; + qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; spin_lock_irqsave(&tx->sta->lock, flags); + /* + * XXX: This spinlock could be fairly expensive, but see the + * comment in agg-tx.c:ieee80211_agg_tx_operational(). + * One way to solve this would be to do something RCU-like + * for managing the tid_tx struct and using atomic bitops + * for the actual state -- by introducing an actual + * 'operational' bit that would be possible. It would + * require changing ieee80211_agg_tx_operational() to + * set that bit, and changing the way tid_tx is managed + * everywhere, including races between that bit and + * tid_tx going away (tid_tx being added can be easily + * committed to memory before the 'operational' bit). + */ + tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; - if (*state == HT_AGG_STATE_OPERATIONAL) + if (*state == HT_AGG_STATE_OPERATIONAL) { info->flags |= IEEE80211_TX_CTL_AMPDU; + } else if (*state != HT_AGG_STATE_IDLE) { + /* in progress */ + queued = true; + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + __skb_queue_tail(&tid_tx->pending, skb); + } spin_unlock_irqrestore(&tx->sta->lock, flags); + + if (unlikely(queued)) + return TX_QUEUED; } if (is_multicast_ether_addr(hdr->addr1)) { @@ -1077,7 +1110,14 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, } if (unlikely(!dev)) return -ENODEV; - /* initialises tx with control */ + /* + * initialises tx with control + * + * return value is safe to ignore here because this function + * can only be invoked for multicast frames + * + * XXX: clean up + */ __ieee80211_tx_prepare(tx, skb, dev); dev_put(dev); return 0; @@ -1188,7 +1228,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } -static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) +static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, + bool txpending) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; @@ -1202,11 +1243,11 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) queue = skb_get_queue_mapping(skb); - WARN_ON(!skb_queue_empty(&local->pending[queue])); + WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue])); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); - return 0; + return; } rcu_read_lock(); @@ -1214,10 +1255,13 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) /* initialises tx */ res_prepare = __ieee80211_tx_prepare(&tx, skb, dev); - if (res_prepare == TX_DROP) { + if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); rcu_read_unlock(); - return 0; + return; + } else if (unlikely(res_prepare == TX_QUEUED)) { + rcu_read_unlock(); + return; } sta = tx.sta; @@ -1251,7 +1295,12 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) do { next = skb->next; skb->next = NULL; - skb_queue_tail(&local->pending[queue], skb); + if (unlikely(txpending)) + skb_queue_head(&local->pending[queue], + skb); + else + skb_queue_tail(&local->pending[queue], + skb); } while ((skb = next)); /* @@ -1276,7 +1325,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) } out: rcu_read_unlock(); - return 0; + return; drop: rcu_read_unlock(); @@ -1287,7 +1336,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) dev_kfree_skb(skb); skb = next; } - return 0; } /* device xmit handlers */ @@ -1346,7 +1394,6 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) FOUND_SDATA, UNKNOWN_ADDRESS, } monitor_iface = NOT_MONITOR; - int ret; if (skb->iif) odev = dev_get_by_index(&init_net, skb->iif); @@ -1360,7 +1407,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) "originating device\n", dev->name); #endif dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && @@ -1389,7 +1436,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) else if (mesh_nexthop_lookup(skb, osdata)) { dev_put(odev); - return 0; + return NETDEV_TX_OK; } if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0) IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh, @@ -1451,7 +1498,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) { dev_kfree_skb(skb); dev_put(odev); - return 0; + return NETDEV_TX_OK; } if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -1460,10 +1507,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) u.ap); if (likely(monitor_iface != UNKNOWN_ADDRESS)) info->control.vif = &osdata->vif; - ret = ieee80211_tx(odev, skb); + + ieee80211_tx(odev, skb, false); dev_put(odev); - return ret; + return NETDEV_TX_OK; } int ieee80211_monitor_start_xmit(struct sk_buff *skb, @@ -1827,6 +1875,54 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) skb_queue_purge(&local->pending[i]); } +static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + struct ieee80211_hdr *hdr; + struct net_device *dev; + int ret; + bool result = true; + + /* does interface still exist? */ + dev = dev_get_by_index(&init_net, skb->iif); + if (!dev) { + dev_kfree_skb(skb); + return true; + } + + /* validate info->control.vif against skb->iif */ + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); + + if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) { + dev_kfree_skb(skb); + result = true; + goto out; + } + + if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { + ieee80211_tx(dev, skb, true); + } else { + hdr = (struct ieee80211_hdr *)skb->data; + sta = sta_info_get(local, hdr->addr1); + + ret = __ieee80211_tx(local, &skb, sta); + if (ret != IEEE80211_TX_OK) + result = false; + } + + out: + dev_put(dev); + + return result; +} + /* * Transmit all pending packets. Called from tasklet, locks master device * TX lock so that no new packets can come in. @@ -1835,9 +1931,8 @@ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; - struct ieee80211_hdr *hdr; unsigned long flags; - int i, ret; + int i; bool next; rcu_read_lock(); @@ -1868,13 +1963,8 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = skb_dequeue(&local->pending[i]); - struct sta_info *sta; - - hdr = (struct ieee80211_hdr *)skb->data; - sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &skb, sta); - if (ret != IEEE80211_TX_OK) { + if (!ieee80211_tx_pending_skb(local, skb)) { skb_queue_head(&local->pending[i], skb); break; } -- cgit v1.2.3 From e4e72fb4de93e3d4047a4ee3f08778422e17ed0d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:42 +0100 Subject: mac80211/iwlwifi: move virtual A-MDPU queue bookkeeping to iwlwifi This patch removes all the virtual A-MPDU-queue bookkeeping from mac80211. Curiously, iwlwifi already does its own bookkeeping, so it doesn't require much changes except where it needs to handle starting and stopping the queues in mac80211. To handle the queue stop/wake properly, we rewrite the software queue number for aggregation frames and internally to iwlwifi keep track of the queues that map into the same AC queue, and only talk to mac80211 about the AC queue. The implementation requires calling two new functions, iwl_stop_queue and iwl_wake_queue instead of the mac80211 counterparts. Signed-off-by: Johannes Berg Cc: Reinette Chattre Signed-off-by: John W. Linville --- net/mac80211/agg-tx.c | 44 +++-------------------------------- net/mac80211/ieee80211_i.h | 7 +----- net/mac80211/main.c | 9 ------- net/mac80211/sta_info.c | 12 ---------- net/mac80211/sta_info.h | 2 -- net/mac80211/tx.c | 19 --------------- net/mac80211/util.c | 58 ++++++---------------------------------------- 7 files changed, 11 insertions(+), 140 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 64b839bfbf1..947aaaad35d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -131,14 +131,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; - if (local->hw.ampdu_queues) { - /* - * Pretend the driver woke the queue, just in case - * it disabled it before the session was stopped. - */ - ieee80211_wake_queue( - &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]); - } *state = HT_AGG_STATE_REQ_STOP_BA_MSK | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); @@ -206,7 +198,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) struct sta_info *sta; struct ieee80211_sub_if_data *sdata; u8 *state; - int i, qn = -1, ret = 0; + int ret = 0; u16 start_seq_num; if (WARN_ON(!local->ops->ampdu_action)) @@ -275,29 +267,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_unlock_sta; } - if (hw->ampdu_queues) { - spin_lock(&local->queue_stop_reason_lock); - /* reserve a new queue for this session */ - for (i = 0; i < local->hw.ampdu_queues; i++) { - if (local->ampdu_ac_queue[i] < 0) { - qn = i; - local->ampdu_ac_queue[qn] = - ieee80211_ac_from_tid(tid); - break; - } - } - spin_unlock(&local->queue_stop_reason_lock); - - if (qn < 0) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "BA request denied - " - "queue unavailable for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - ret = -ENOSPC; - goto err_unlock_sta; - } - } - /* * While we're asking the driver about the aggregation, * stop the AC queue so that we don't have to worry @@ -319,7 +288,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) tid); #endif ret = -ENOMEM; - goto err_return_queue; + goto err_wake_queue; } skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); @@ -348,7 +317,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) *state = HT_AGG_STATE_IDLE; goto err_free; } - sta->tid_to_tx_q[tid] = qn; /* Driver vetoed or OKed, but we can take packets again now */ ieee80211_wake_queue_by_reason( @@ -380,13 +348,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) err_free: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; - err_return_queue: - if (qn >= 0) { - /* give queue back to pool */ - spin_lock(&local->queue_stop_reason_lock); - local->ampdu_ac_queue[qn] = -1; - spin_unlock(&local->queue_stop_reason_lock); - } + err_wake_queue: ieee80211_wake_queue_by_reason( &local->hw, ieee80211_ac_from_tid(tid), IEEE80211_QUEUE_STOP_REASON_AGGREGATION); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 32345b479ad..e6ed78cb16b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -594,12 +594,7 @@ struct ieee80211_local { const struct ieee80211_ops *ops; - /* AC queue corresponding to each AMPDU queue */ - s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES]; - unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES]; - - unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES + - IEEE80211_MAX_AMPDU_QUEUES]; + unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 756284e0bbd..a6f1d8a869b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -774,11 +774,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); - for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++) - local->ampdu_ac_queue[i] = -1; - /* using an s8 won't work with more than that */ - BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127); - sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) @@ -874,10 +869,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) */ if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; - if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES) - hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES; - if (hw->queues < 4) - hw->ampdu_queues = 0; mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv), "wmaster%d", ieee80211_master_setup, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index dd3593c1fd2..c5f14e6bbde 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -203,17 +203,6 @@ void sta_info_destroy(struct sta_info *sta) if (tid_rx) tid_rx->shutdown = true; - /* - * The stop callback cannot find this station any more, but - * it didn't complete its work -- start the queue if necessary - */ - if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK && - sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK && - local->hw.ampdu_queues) - ieee80211_wake_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[i], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - spin_unlock_bh(&sta->lock); /* @@ -292,7 +281,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, * enable session_timer's data differentiation. refer to * sta_rx_agg_session_timer_expired for useage */ sta->timer_to_tid[i] = i; - sta->tid_to_tx_q[i] = -1; /* rx */ sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; sta->ampdu_mlme.tid_rx[i] = NULL; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 18fd5d1a442..5534d489f50 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -206,7 +206,6 @@ struct sta_ampdu_mlme { * @tid_seq: per-TID sequence numbers for sending to this STA * @ampdu_mlme: A-MPDU state machine state * @timer_to_tid: identity mapping to ID timers - * @tid_to_tx_q: map tid to tx queue (invalid == negative values) * @llid: Local link ID * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state @@ -281,7 +280,6 @@ struct sta_info { */ struct sta_ampdu_mlme ampdu_mlme; u8 timer_to_tid[STA_TID_NUM]; - s8 tid_to_tx_q[STA_TID_NUM]; #ifdef CONFIG_MAC80211_MESH /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 906ab785db4..3fb04a86444 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1145,25 +1145,6 @@ static int __ieee80211_tx(struct ieee80211_local *local, info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); - /* - * Internally, we need to have the queue mapping point to - * the real AC queue, not the virtual A-MPDU queue. This - * now finally sets the queue to what the driver wants. - * We will later move this down into the only driver that - * needs it, iwlwifi. - */ - if (sta && local->hw.ampdu_queues && - info->flags & IEEE80211_TX_CTL_AMPDU) { - unsigned long flags; - u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); - int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - - spin_lock_irqsave(&sta->lock, flags); - skb_set_queue_mapping(skb, local->hw.queues + - sta->tid_to_tx_q[tid]); - spin_unlock_irqrestore(&sta->lock, flags); - } - next = skb->next; len = skb->len; ret = local->ops->tx(local_to_hw(local), skb); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0247d8022f5..fdf432f1455 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -339,29 +339,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - if (queue >= hw->queues) { - if (local->ampdu_ac_queue[queue - hw->queues] < 0) - return; - - /* - * for virtual aggregation queues, we need to refcount the - * internal mac80211 disable (multiple times!), keep track of - * driver disable _and_ make sure the regular queue is - * actually enabled. - */ - if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) - local->amdpu_ac_stop_refcnt[queue - hw->queues]--; - else - __clear_bit(reason, &local->queue_stop_reasons[queue]); - - if (local->queue_stop_reasons[queue] || - local->amdpu_ac_stop_refcnt[queue - hw->queues]) - return; - - /* now go on to treat the corresponding regular queue */ - queue = local->ampdu_ac_queue[queue - hw->queues]; - reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; - } + if (WARN_ON(queue >= hw->queues)) + return; __clear_bit(reason, &local->queue_stop_reasons[queue]); @@ -400,25 +379,8 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - if (queue >= hw->queues) { - if (local->ampdu_ac_queue[queue - hw->queues] < 0) - return; - - /* - * for virtual aggregation queues, we need to refcount the - * internal mac80211 disable (multiple times!), keep track of - * driver disable _and_ make sure the regular queue is - * actually enabled. - */ - if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) - local->amdpu_ac_stop_refcnt[queue - hw->queues]++; - else - __set_bit(reason, &local->queue_stop_reasons[queue]); - - /* now go on to treat the corresponding regular queue */ - queue = local->ampdu_ac_queue[queue - hw->queues]; - reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; - } + if (WARN_ON(queue >= hw->queues)) + return; /* * Only stop if it was previously running, this is necessary @@ -474,15 +436,9 @@ EXPORT_SYMBOL(ieee80211_stop_queues); int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) { struct ieee80211_local *local = hw_to_local(hw); - unsigned long flags; - if (queue >= hw->queues) { - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - queue = local->ampdu_ac_queue[queue - hw->queues]; - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (queue < 0) - return true; - } + if (WARN_ON(queue >= hw->queues)) + return true; return __netif_subqueue_stopped(local->mdev, queue); } @@ -497,7 +453,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (i = 0; i < hw->queues + hw->ampdu_queues; i++) + for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -- cgit v1.2.3 From 8a5117d80fe93de5df5b56480054f7df1fd20755 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 24 Mar 2009 21:21:07 -0400 Subject: cfg80211: default CONFIG_WIRELESS_OLD_REGULATORY to n And update description and feature-removal schedule according to the new plan. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/Kconfig | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index d1d18f34d27..3c3bc9e579e 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -12,36 +12,17 @@ config CFG80211_REG_DEBUG config WIRELESS_OLD_REGULATORY bool "Old wireless static regulatory definitions" - default y + default n ---help--- This option enables the old static regulatory information - and uses it within the new framework. This is available - temporarily as an option to help prevent immediate issues - due to the switch to the new regulatory framework which - does require a new userspace application which has the - database of regulatory information (CRDA) and another for - setting regulatory domains (iw). - - For more information see: - - http://wireless.kernel.org/en/developers/Regulatory/CRDA - http://wireless.kernel.org/en/users/Documentation/iw - - It is important to note though that if you *do* have CRDA present - and if this option is enabled CRDA *will* be called to update the - regulatory domain (for US and JP only). Support for letting the user - set the regulatory domain through iw is also supported. This option - mainly exists to leave around for a kernel release some old static - regulatory domains that were defined and to keep around the old - ieee80211_regdom module parameter. This is being phased out and you - should stop using them ASAP. - - Note: You will need CRDA if you want 802.11d support - - Say Y unless you have installed a new userspace application. - Also say Y if have one currently depending on the ieee80211_regdom - module parameter and cannot port it to use the new userspace - interfaces. + and uses it within the new framework. This option is available + for historical reasons and it is advised to leave it off. + + For details see: + + http://wireless.kernel.org/en/developers/Regulatory + + Say N and if you say Y, please tell us why. The default is N. config WIRELESS_EXT bool "Wireless extensions" -- cgit v1.2.3