diff options
-rw-r--r-- | include/net/cfg80211.h | 3 | ||||
-rw-r--r-- | include/uapi/linux/nl80211.h | 6 | ||||
-rw-r--r-- | net/wireless/core.c | 44 | ||||
-rw-r--r-- | net/wireless/core.h | 11 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 28 |
5 files changed, 91 insertions, 1 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f3539a15c411..6510ccf53a54 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -3194,6 +3194,7 @@ struct cfg80211_cached_keys; | |||
3194 | * @ibss_dfs_possible: (private) IBSS may change to a DFS channel | 3194 | * @ibss_dfs_possible: (private) IBSS may change to a DFS channel |
3195 | * @event_list: (private) list for internal event processing | 3195 | * @event_list: (private) list for internal event processing |
3196 | * @event_lock: (private) lock for event list | 3196 | * @event_lock: (private) lock for event list |
3197 | * @owner_nlportid: (private) owner socket port ID | ||
3197 | */ | 3198 | */ |
3198 | struct wireless_dev { | 3199 | struct wireless_dev { |
3199 | struct wiphy *wiphy; | 3200 | struct wiphy *wiphy; |
@@ -3241,6 +3242,8 @@ struct wireless_dev { | |||
3241 | unsigned long cac_start_time; | 3242 | unsigned long cac_start_time; |
3242 | unsigned int cac_time_ms; | 3243 | unsigned int cac_time_ms; |
3243 | 3244 | ||
3245 | u32 owner_nlportid; | ||
3246 | |||
3244 | #ifdef CONFIG_CFG80211_WEXT | 3247 | #ifdef CONFIG_CFG80211_WEXT |
3245 | /* wext data */ | 3248 | /* wext data */ |
3246 | struct { | 3249 | struct { |
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1ba9d626aa83..5e405fd55a71 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -1579,6 +1579,10 @@ enum nl80211_commands { | |||
1579 | * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. | 1579 | * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. |
1580 | * As specified in the &enum nl80211_tdls_peer_capability. | 1580 | * As specified in the &enum nl80211_tdls_peer_capability. |
1581 | * | 1581 | * |
1582 | * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface | ||
1583 | * creation then the new interface will be owned by the netlink socket | ||
1584 | * that created it and will be destroyed when the socket is closed | ||
1585 | * | ||
1582 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 1586 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
1583 | * @__NL80211_ATTR_AFTER_LAST: internal use | 1587 | * @__NL80211_ATTR_AFTER_LAST: internal use |
1584 | */ | 1588 | */ |
@@ -1914,6 +1918,8 @@ enum nl80211_attrs { | |||
1914 | 1918 | ||
1915 | NL80211_ATTR_TDLS_PEER_CAPABILITY, | 1919 | NL80211_ATTR_TDLS_PEER_CAPABILITY, |
1916 | 1920 | ||
1921 | NL80211_ATTR_IFACE_SOCKET_OWNER, | ||
1922 | |||
1917 | /* add attributes here, update the policy in nl80211.c */ | 1923 | /* add attributes here, update the policy in nl80211.c */ |
1918 | 1924 | ||
1919 | __NL80211_ATTR_AFTER_LAST, | 1925 | __NL80211_ATTR_AFTER_LAST, |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 086cddd03ba6..5a63c3cbda2e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -260,6 +260,45 @@ static void cfg80211_event_work(struct work_struct *work) | |||
260 | rtnl_unlock(); | 260 | rtnl_unlock(); |
261 | } | 261 | } |
262 | 262 | ||
263 | void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) | ||
264 | { | ||
265 | struct cfg80211_iface_destroy *item; | ||
266 | |||
267 | ASSERT_RTNL(); | ||
268 | |||
269 | spin_lock_irq(&rdev->destroy_list_lock); | ||
270 | while ((item = list_first_entry_or_null(&rdev->destroy_list, | ||
271 | struct cfg80211_iface_destroy, | ||
272 | list))) { | ||
273 | struct wireless_dev *wdev, *tmp; | ||
274 | u32 nlportid = item->nlportid; | ||
275 | |||
276 | list_del(&item->list); | ||
277 | kfree(item); | ||
278 | spin_unlock_irq(&rdev->destroy_list_lock); | ||
279 | |||
280 | list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) { | ||
281 | if (nlportid == wdev->owner_nlportid) | ||
282 | rdev_del_virtual_intf(rdev, wdev); | ||
283 | } | ||
284 | |||
285 | spin_lock_irq(&rdev->destroy_list_lock); | ||
286 | } | ||
287 | spin_unlock_irq(&rdev->destroy_list_lock); | ||
288 | } | ||
289 | |||
290 | static void cfg80211_destroy_iface_wk(struct work_struct *work) | ||
291 | { | ||
292 | struct cfg80211_registered_device *rdev; | ||
293 | |||
294 | rdev = container_of(work, struct cfg80211_registered_device, | ||
295 | destroy_work); | ||
296 | |||
297 | rtnl_lock(); | ||
298 | cfg80211_destroy_ifaces(rdev); | ||
299 | rtnl_unlock(); | ||
300 | } | ||
301 | |||
263 | /* exported functions */ | 302 | /* exported functions */ |
264 | 303 | ||
265 | struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | 304 | struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) |
@@ -318,6 +357,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
318 | rdev->wiphy.dev.class = &ieee80211_class; | 357 | rdev->wiphy.dev.class = &ieee80211_class; |
319 | rdev->wiphy.dev.platform_data = rdev; | 358 | rdev->wiphy.dev.platform_data = rdev; |
320 | 359 | ||
360 | INIT_LIST_HEAD(&rdev->destroy_list); | ||
361 | spin_lock_init(&rdev->destroy_list_lock); | ||
362 | INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); | ||
363 | |||
321 | #ifdef CONFIG_CFG80211_DEFAULT_PS | 364 | #ifdef CONFIG_CFG80211_DEFAULT_PS |
322 | rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; | 365 | rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; |
323 | #endif | 366 | #endif |
@@ -675,6 +718,7 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
675 | cancel_work_sync(&rdev->conn_work); | 718 | cancel_work_sync(&rdev->conn_work); |
676 | flush_work(&rdev->event_work); | 719 | flush_work(&rdev->event_work); |
677 | cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); | 720 | cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); |
721 | flush_work(&rdev->destroy_work); | ||
678 | 722 | ||
679 | #ifdef CONFIG_PM | 723 | #ifdef CONFIG_PM |
680 | if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) | 724 | if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 5b1fdcadd469..6f6f75609852 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -80,6 +80,10 @@ struct cfg80211_registered_device { | |||
80 | 80 | ||
81 | struct cfg80211_coalesce *coalesce; | 81 | struct cfg80211_coalesce *coalesce; |
82 | 82 | ||
83 | spinlock_t destroy_list_lock; | ||
84 | struct list_head destroy_list; | ||
85 | struct work_struct destroy_work; | ||
86 | |||
83 | /* must be last because of the way we do wiphy_priv(), | 87 | /* must be last because of the way we do wiphy_priv(), |
84 | * and it should at least be aligned to NETDEV_ALIGN */ | 88 | * and it should at least be aligned to NETDEV_ALIGN */ |
85 | struct wiphy wiphy __aligned(NETDEV_ALIGN); | 89 | struct wiphy wiphy __aligned(NETDEV_ALIGN); |
@@ -232,6 +236,13 @@ struct cfg80211_beacon_registration { | |||
232 | u32 nlportid; | 236 | u32 nlportid; |
233 | }; | 237 | }; |
234 | 238 | ||
239 | struct cfg80211_iface_destroy { | ||
240 | struct list_head list; | ||
241 | u32 nlportid; | ||
242 | }; | ||
243 | |||
244 | void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); | ||
245 | |||
235 | /* free object */ | 246 | /* free object */ |
236 | void cfg80211_dev_free(struct cfg80211_registered_device *rdev); | 247 | void cfg80211_dev_free(struct cfg80211_registered_device *rdev); |
237 | 248 | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 052c1bf8ffac..b25b5ce4076d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -385,6 +385,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
385 | [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, | 385 | [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, |
386 | [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, | 386 | [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, |
387 | [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, | 387 | [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, |
388 | [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, | ||
388 | }; | 389 | }; |
389 | 390 | ||
390 | /* policy for the key attributes */ | 391 | /* policy for the key attributes */ |
@@ -2514,6 +2515,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
2514 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; | 2515 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; |
2515 | u32 flags; | 2516 | u32 flags; |
2516 | 2517 | ||
2518 | /* to avoid failing a new interface creation due to pending removal */ | ||
2519 | cfg80211_destroy_ifaces(rdev); | ||
2520 | |||
2517 | memset(¶ms, 0, sizeof(params)); | 2521 | memset(¶ms, 0, sizeof(params)); |
2518 | 2522 | ||
2519 | if (!info->attrs[NL80211_ATTR_IFNAME]) | 2523 | if (!info->attrs[NL80211_ATTR_IFNAME]) |
@@ -2563,6 +2567,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
2563 | return PTR_ERR(wdev); | 2567 | return PTR_ERR(wdev); |
2564 | } | 2568 | } |
2565 | 2569 | ||
2570 | if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER]) | ||
2571 | wdev->owner_nlportid = info->snd_portid; | ||
2572 | |||
2566 | switch (type) { | 2573 | switch (type) { |
2567 | case NL80211_IFTYPE_MESH_POINT: | 2574 | case NL80211_IFTYPE_MESH_POINT: |
2568 | if (!info->attrs[NL80211_ATTR_MESH_ID]) | 2575 | if (!info->attrs[NL80211_ATTR_MESH_ID]) |
@@ -11649,9 +11656,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb, | |||
11649 | rcu_read_lock(); | 11656 | rcu_read_lock(); |
11650 | 11657 | ||
11651 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { | 11658 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { |
11652 | list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) | 11659 | bool schedule_destroy_work = false; |
11660 | |||
11661 | list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) { | ||
11653 | cfg80211_mlme_unregister_socket(wdev, notify->portid); | 11662 | cfg80211_mlme_unregister_socket(wdev, notify->portid); |
11654 | 11663 | ||
11664 | if (wdev->owner_nlportid == notify->portid) | ||
11665 | schedule_destroy_work = true; | ||
11666 | } | ||
11667 | |||
11655 | spin_lock_bh(&rdev->beacon_registrations_lock); | 11668 | spin_lock_bh(&rdev->beacon_registrations_lock); |
11656 | list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, | 11669 | list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, |
11657 | list) { | 11670 | list) { |
@@ -11662,6 +11675,19 @@ static int nl80211_netlink_notify(struct notifier_block * nb, | |||
11662 | } | 11675 | } |
11663 | } | 11676 | } |
11664 | spin_unlock_bh(&rdev->beacon_registrations_lock); | 11677 | spin_unlock_bh(&rdev->beacon_registrations_lock); |
11678 | |||
11679 | if (schedule_destroy_work) { | ||
11680 | struct cfg80211_iface_destroy *destroy; | ||
11681 | |||
11682 | destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC); | ||
11683 | if (destroy) { | ||
11684 | destroy->nlportid = notify->portid; | ||
11685 | spin_lock(&rdev->destroy_list_lock); | ||
11686 | list_add(&destroy->list, &rdev->destroy_list); | ||
11687 | spin_unlock(&rdev->destroy_list_lock); | ||
11688 | schedule_work(&rdev->destroy_work); | ||
11689 | } | ||
11690 | } | ||
11665 | } | 11691 | } |
11666 | 11692 | ||
11667 | rcu_read_unlock(); | 11693 | rcu_read_unlock(); |