From 7623225f905263424c7254dc6a07bff083a498dd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 11 Oct 2010 14:46:52 -0400 Subject: Revert "wireless: Use first phyX name available when registering phy devices." This reverts commit 5a254ffe3ffdfa84fe076009bd8e88da412180d2. The commit failed to take into account that allocated wireless devices (wiphys) are not added into the device list upon allocation, but only when they are registered. Therefore, it opened up a race between allocating and registering a name, so that if two processes allocate and register concurrently ("alloc, alloc, register, register" rather than "alloc, register, alloc, register") the code will attempt to use the same name twice. Signed-off-by: Johannes Berg --- net/wireless/core.c | 54 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index 1684ad91763..9c21ebf9780 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -178,10 +178,26 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { struct cfg80211_registered_device *rdev2; - int result; + int wiphy_idx, taken = -1, result, digits; assert_cfg80211_lock(); + /* prohibit calling the thing phy%d when %d is not its number */ + sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); + if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { + /* count number of places needed to print wiphy_idx */ + digits = 1; + while (wiphy_idx /= 10) + digits++; + /* + * deny the name if it is phy where is printed + * without leading zeroes. taken == strlen(newname) here + */ + if (taken == strlen(PHY_NAME) + digits) + return -EINVAL; + } + + /* Ignore nop renames */ if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) return 0; @@ -189,7 +205,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, /* Ensure another device does not already have this name. */ list_for_each_entry(rdev2, &cfg80211_rdev_list, list) if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) - return -EEXIST; + return -EINVAL; result = device_rename(&rdev->wiphy.dev, newname); if (result) @@ -304,11 +320,9 @@ static void cfg80211_event_work(struct work_struct *work) struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { static int wiphy_counter; - int i; - struct cfg80211_registered_device *rdev, *rdev2; + + struct cfg80211_registered_device *rdev; int alloc_size; - char nname[IFNAMSIZ + 1]; - bool found = false; WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); @@ -332,37 +346,17 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { wiphy_counter--; - goto too_many_devs; - } - - /* 64k wiphy devices is enough for anyone! */ - for (i = 0; i < 0xFFFF; i++) { - found = false; - snprintf(nname, sizeof(nname)-1, PHY_NAME "%d", i); - nname[sizeof(nname)-1] = 0; - list_for_each_entry(rdev2, &cfg80211_rdev_list, list) - if (strcmp(nname, dev_name(&rdev2->wiphy.dev)) == 0) { - found = true; - break; - } - - if (!found) - break; - } - - if (unlikely(found)) { -too_many_devs: mutex_unlock(&cfg80211_mutex); - /* ugh, too many devices already! */ + /* ugh, wrapped! */ kfree(rdev); return NULL; } - /* give it a proper name */ - dev_set_name(&rdev->wiphy.dev, "%s", nname); - mutex_unlock(&cfg80211_mutex); + /* give it a proper name */ + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + mutex_init(&rdev->mtx); mutex_init(&rdev->devlist_mtx); INIT_LIST_HEAD(&rdev->netdev_list); -- cgit v1.2.3-70-g09d2 From 5a5c731aa59cc2c44ca20f45b1a577cd4f5435e2 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 7 Oct 2010 16:39:20 -0700 Subject: wireless: Set some stats used by /proc/net/wireless (wext) Some stats for /proc/net/wireless (and wext in general) are not being set. This patch addresses a few of those with values easily obtained from mac80211 core. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- include/net/cfg80211.h | 4 ++++ net/mac80211/cfg.c | 4 +++- net/wireless/wext-compat.c | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0778d04b3bb..f920a06f363 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -403,6 +403,7 @@ struct station_parameters { * @STATION_INFO_TX_PACKETS: @tx_packets filled * @STATION_INFO_TX_RETRIES: @tx_retries filled * @STATION_INFO_TX_FAILED: @tx_failed filled + * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -417,6 +418,7 @@ enum station_info_flags { STATION_INFO_TX_PACKETS = 1<<9, STATION_INFO_TX_RETRIES = 1<<10, STATION_INFO_TX_FAILED = 1<<11, + STATION_INFO_RX_DROP_MISC = 1<<12, }; /** @@ -468,6 +470,7 @@ struct rate_info { * @tx_packets: packets transmitted to this station * @tx_retries: cumulative retry counts * @tx_failed: number of failed transmissions (retries exceeded, no ACK) + * @rx_dropped_misc: Dropped for un-specified reason. * @generation: generation number for nl80211 dumps. * This number should increase every time the list of stations * changes, i.e. when a station is added or removed, so that @@ -487,6 +490,7 @@ struct station_info { u32 tx_packets; u32 tx_retries; u32 tx_failed; + u32 rx_dropped_misc; int generation; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ecf9b7166ed..25fb351e00f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -329,7 +329,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_PACKETS | STATION_INFO_TX_RETRIES | STATION_INFO_TX_FAILED | - STATION_INFO_TX_BITRATE; + STATION_INFO_TX_BITRATE | + STATION_INFO_RX_DROP_MISC; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; @@ -338,6 +339,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->tx_packets = sta->tx_packets; sinfo->tx_retries = sta->tx_retry_count; sinfo->tx_failed = sta->tx_retry_failed; + sinfo->rx_dropped_misc = sta->rx_dropped; if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 6002265289c..12222ee6ebf 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1366,6 +1366,10 @@ struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) } wstats.qual.updated |= IW_QUAL_NOISE_INVALID; + if (sinfo.filled & STATION_INFO_RX_DROP_MISC) + wstats.discard.misc = sinfo.rx_dropped_misc; + if (sinfo.filled & STATION_INFO_TX_FAILED) + wstats.discard.retries = sinfo.tx_failed; return &wstats; } -- cgit v1.2.3-70-g09d2 From 8610c29a2c9f273886b1c31ae4d92c69d4326262 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 9 Oct 2010 02:39:29 +0200 Subject: cfg80211: add channel utilization stats to the survey command Using these, user space can calculate a relative channel utilization with arbitrary intervals by regularly taking snapshots of the survey results. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- include/linux/nl80211.h | 15 +++++++++++++++ include/net/cfg80211.h | 20 ++++++++++++++++++++ net/wireless/nl80211.c | 15 +++++++++++++++ 3 files changed, 50 insertions(+) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c08709fe36f..0edb2566c14 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1413,6 +1413,16 @@ enum nl80211_reg_rule_flags { * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use @@ -1422,6 +1432,11 @@ enum nl80211_survey_info { NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_CHANNEL_TIME, + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + NL80211_SURVEY_INFO_CHANNEL_TIME_TX, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f920a06f363..24d5b586927 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -294,6 +294,11 @@ struct key_params { * * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in * @SURVEY_INFO_IN_USE: channel is currently being used + * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in + * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in + * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in + * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in + * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in * * Used by the driver to indicate which info in &struct survey_info * it has filled in during the get_survey(). @@ -301,6 +306,11 @@ struct key_params { enum survey_info_flags { SURVEY_INFO_NOISE_DBM = 1<<0, SURVEY_INFO_IN_USE = 1<<1, + SURVEY_INFO_CHANNEL_TIME = 1<<2, + SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3, + SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4, + SURVEY_INFO_CHANNEL_TIME_RX = 1<<5, + SURVEY_INFO_CHANNEL_TIME_TX = 1<<6, }; /** @@ -310,6 +320,11 @@ enum survey_info_flags { * @filled: bitflag of flags from &enum survey_info_flags * @noise: channel noise in dBm. This and all following fields are * optional + * @channel_time: amount of time in ms the radio spent on the channel + * @channel_time_busy: amount of time the primary channel was sensed busy + * @channel_time_ext_busy: amount of time the extension channel was sensed busy + * @channel_time_rx: amount of time the radio spent receiving data + * @channel_time_tx: amount of time the radio spent transmitting data * * Used by dump_survey() to report back per-channel survey information. * @@ -318,6 +333,11 @@ enum survey_info_flags { */ struct survey_info { struct ieee80211_channel *channel; + u64 channel_time; + u64 channel_time_busy; + u64 channel_time_ext_busy; + u64 channel_time_rx; + u64 channel_time_tx; u32 filled; s8 noise; }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 882dc921103..c506241f863 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3153,6 +3153,21 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, survey->noise); if (survey->filled & SURVEY_INFO_IN_USE) NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, + survey->channel_time); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + survey->channel_time_busy); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + survey->channel_time_ext_busy); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + survey->channel_time_rx); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + survey->channel_time_tx); nla_nest_end(msg, infoattr); -- cgit v1.2.3-70-g09d2 From cfd8e12f42746df396ecbdf7a1d8e92e8e4dbb97 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 11 Oct 2010 10:28:59 -0700 Subject: wireless: Print wiphy name in sysfs. The index cannot be used to reliably reconstruct a phy name, so explicitly add the phy name to sysfs so that scripts can figure out the parent phy device for a particular wireless interface. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- net/wireless/sysfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 74a9e3cce45..4294fa22bb2 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -35,6 +35,14 @@ SHOW_FMT(index, "%d", wiphy_idx); SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; + return sprintf(buf, "%s\n", dev_name(&wiphy->dev)); +} + + static ssize_t addresses_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -57,6 +65,7 @@ static struct device_attribute ieee80211_dev_attrs[] = { __ATTR_RO(macaddress), __ATTR_RO(address_mask), __ATTR_RO(addresses), + __ATTR_RO(name), {} }; -- cgit v1.2.3-70-g09d2 From 271733cf844a2f5f186ef3b40c26d6397b71039a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Oct 2010 12:06:23 +0200 Subject: cfg80211: notify drivers about frame registrations Drivers may need to adjust their filters according to frame registrations, so notify them about them. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 7 +++++++ net/wireless/mlme.c | 23 ++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 24d5b586927..2a7936d7851 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1147,6 +1147,9 @@ struct cfg80211_pmksa { * allows the driver to adjust the dynamic ps timeout value. * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. * + * @mgmt_frame_register: Notify driver that a management frame type was + * registered. Note that this callback may not sleep, and cannot run + * concurrently with itself. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy); @@ -1297,6 +1300,10 @@ struct cfg80211_ops { int (*set_cqm_rssi_config)(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst); + + void (*mgmt_frame_register)(struct wiphy *wiphy, + struct net_device *dev, + u16 frame_type, bool reg); }; /* diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index caf11a42750..26838d903b9 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -764,6 +764,8 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, u16 frame_type, const u8 *match_data, int match_len) { + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_mgmt_registration *reg, *nreg; int err = 0; u16 mgmt_type; @@ -810,22 +812,37 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, nreg->frame_type = cpu_to_le16(frame_type); list_add(&nreg->list, &wdev->mgmt_registrations); + if (rdev->ops->mgmt_frame_register) + rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, + frame_type, true); + out: spin_unlock_bh(&wdev->mgmt_registrations_lock); + return err; } void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) { + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_mgmt_registration *reg, *tmp; spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { - if (reg->nlpid == nlpid) { - list_del(®->list); - kfree(reg); + if (reg->nlpid != nlpid) + continue; + + if (rdev->ops->mgmt_frame_register) { + u16 frame_type = le16_to_cpu(reg->frame_type); + + rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, + frame_type, false); } + + list_del(®->list); + kfree(reg); } spin_unlock_bh(&wdev->mgmt_registrations_lock); -- cgit v1.2.3-70-g09d2 From 9ebad4ab87f2ffa6eca825327721e647c7457264 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Oct 2010 13:41:35 +0200 Subject: radiotap: fix vendor namespace parsing There's a bug with radiotap vendor namespace parsing if you don't register for the given namespace extensions. Fix this by passing only the unknown vendor namespaces and the registered data to frontends, but not both. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/radiotap.c | 58 +++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 26 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index c774bc0f155..dbe35e138e9 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c @@ -201,7 +201,7 @@ int ieee80211_radiotap_iterator_next( { while (1) { int hit = 0; - int pad, align, size, subns, vnslen; + int pad, align, size, subns; uint32_t oui; /* if no more EXT bits, that's it */ @@ -261,6 +261,27 @@ int ieee80211_radiotap_iterator_next( if (pad) iterator->_arg += align - pad; + if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { + int vnslen; + + if ((unsigned long)iterator->_arg + size - + (unsigned long)iterator->_rtheader > + (unsigned long)iterator->_max_length) + return -EINVAL; + + oui = (*iterator->_arg << 16) | + (*(iterator->_arg + 1) << 8) | + *(iterator->_arg + 2); + subns = *(iterator->_arg + 3); + + find_ns(iterator, oui, subns); + + vnslen = get_unaligned_le16(iterator->_arg + 4); + iterator->_next_ns_data = iterator->_arg + size + vnslen; + if (!iterator->current_namespace) + size += vnslen; + } + /* * this is what we will return to user, but we need to * move on first so next call has something fresh to test @@ -287,40 +308,25 @@ int ieee80211_radiotap_iterator_next( /* these special ones are valid in each bitmap word */ switch (iterator->_arg_index % 32) { case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: - iterator->_bitmap_shifter >>= 1; - iterator->_arg_index++; - iterator->_reset_on_ext = 1; - vnslen = get_unaligned_le16(iterator->this_arg + 4); - iterator->_next_ns_data = iterator->_arg + vnslen; - oui = (*iterator->this_arg << 16) | - (*(iterator->this_arg + 1) << 8) | - *(iterator->this_arg + 2); - subns = *(iterator->this_arg + 3); - - find_ns(iterator, oui, subns); - iterator->is_radiotap_ns = 0; - /* allow parsers to show this information */ + /* + * If parser didn't register this vendor + * namespace with us, allow it to show it + * as 'raw. Do do that, set argument index + * to vendor namespace. + */ iterator->this_arg_index = IEEE80211_RADIOTAP_VENDOR_NAMESPACE; - iterator->this_arg_size += vnslen; - if ((unsigned long)iterator->this_arg + - iterator->this_arg_size - - (unsigned long)iterator->_rtheader > - (unsigned long)(unsigned long)iterator->_max_length) - return -EINVAL; - hit = 1; - break; + if (!iterator->current_namespace) + hit = 1; + goto next_entry; case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: - iterator->_bitmap_shifter >>= 1; - iterator->_arg_index++; - iterator->_reset_on_ext = 1; iterator->current_namespace = &radiotap_ns; iterator->is_radiotap_ns = 1; - break; + goto next_entry; case IEEE80211_RADIOTAP_EXT: /* * bit 31 was set, there is more -- cgit v1.2.3-70-g09d2