diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 3 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 14 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 25 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 14 | ||||
-rw-r--r-- | net/mac80211/iface.c | 157 |
5 files changed, 176 insertions, 37 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f82b18e996b2..5de1ca3f17b9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -52,9 +52,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy, | |||
52 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 52 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
53 | int ret; | 53 | int ret; |
54 | 54 | ||
55 | if (ieee80211_sdata_running(sdata)) | ||
56 | return -EBUSY; | ||
57 | |||
58 | ret = ieee80211_if_change_type(sdata, type); | 55 | ret = ieee80211_if_change_type(sdata, type); |
59 | if (ret) | 56 | if (ret) |
60 | return ret; | 57 | return ret; |
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 14123dce544b..6064b7b09e01 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -54,6 +54,20 @@ static inline int drv_add_interface(struct ieee80211_local *local, | |||
54 | return ret; | 54 | return ret; |
55 | } | 55 | } |
56 | 56 | ||
57 | static inline int drv_change_interface(struct ieee80211_local *local, | ||
58 | struct ieee80211_sub_if_data *sdata, | ||
59 | enum nl80211_iftype type) | ||
60 | { | ||
61 | int ret; | ||
62 | |||
63 | might_sleep(); | ||
64 | |||
65 | trace_drv_change_interface(local, sdata, type); | ||
66 | ret = local->ops->change_interface(&local->hw, &sdata->vif, type); | ||
67 | trace_drv_return_int(local, ret); | ||
68 | return ret; | ||
69 | } | ||
70 | |||
57 | static inline void drv_remove_interface(struct ieee80211_local *local, | 71 | static inline void drv_remove_interface(struct ieee80211_local *local, |
58 | struct ieee80211_vif *vif) | 72 | struct ieee80211_vif *vif) |
59 | { | 73 | { |
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index b5a95582d816..f6f3d89e43fa 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h | |||
@@ -136,6 +136,31 @@ TRACE_EVENT(drv_add_interface, | |||
136 | ) | 136 | ) |
137 | ); | 137 | ); |
138 | 138 | ||
139 | TRACE_EVENT(drv_change_interface, | ||
140 | TP_PROTO(struct ieee80211_local *local, | ||
141 | struct ieee80211_sub_if_data *sdata, | ||
142 | enum nl80211_iftype type), | ||
143 | |||
144 | TP_ARGS(local, sdata, type), | ||
145 | |||
146 | TP_STRUCT__entry( | ||
147 | LOCAL_ENTRY | ||
148 | VIF_ENTRY | ||
149 | __field(u32, new_type) | ||
150 | ), | ||
151 | |||
152 | TP_fast_assign( | ||
153 | LOCAL_ASSIGN; | ||
154 | VIF_ASSIGN; | ||
155 | __entry->new_type = type; | ||
156 | ), | ||
157 | |||
158 | TP_printk( | ||
159 | LOCAL_PR_FMT VIF_PR_FMT " new type:%d", | ||
160 | LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type | ||
161 | ) | ||
162 | ); | ||
163 | |||
139 | TRACE_EVENT(drv_remove_interface, | 164 | TRACE_EVENT(drv_remove_interface, |
140 | TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), | 165 | TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), |
141 | 166 | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f64837788681..d529bd5eab47 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -472,6 +472,16 @@ enum ieee80211_sub_if_data_flags { | |||
472 | IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), | 472 | IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), |
473 | }; | 473 | }; |
474 | 474 | ||
475 | /** | ||
476 | * enum ieee80211_sdata_state_bits - virtual interface state bits | ||
477 | * @SDATA_STATE_RUNNING: virtual interface is up & running; this | ||
478 | * mirrors netif_running() but is separate for interface type | ||
479 | * change handling while the interface is up | ||
480 | */ | ||
481 | enum ieee80211_sdata_state_bits { | ||
482 | SDATA_STATE_RUNNING, | ||
483 | }; | ||
484 | |||
475 | struct ieee80211_sub_if_data { | 485 | struct ieee80211_sub_if_data { |
476 | struct list_head list; | 486 | struct list_head list; |
477 | 487 | ||
@@ -485,6 +495,8 @@ struct ieee80211_sub_if_data { | |||
485 | 495 | ||
486 | unsigned int flags; | 496 | unsigned int flags; |
487 | 497 | ||
498 | unsigned long state; | ||
499 | |||
488 | int drop_unencrypted; | 500 | int drop_unencrypted; |
489 | 501 | ||
490 | char name[IFNAMSIZ]; | 502 | char name[IFNAMSIZ]; |
@@ -1087,7 +1099,7 @@ void ieee80211_recalc_idle(struct ieee80211_local *local); | |||
1087 | 1099 | ||
1088 | static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) | 1100 | static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) |
1089 | { | 1101 | { |
1090 | return netif_running(sdata->dev); | 1102 | return test_bit(SDATA_STATE_RUNNING, &sdata->state); |
1091 | } | 1103 | } |
1092 | 1104 | ||
1093 | /* tx handling */ | 1105 | /* tx handling */ |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index cba3d806d722..c1cc200ac81f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -148,7 +148,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, | |||
148 | return 0; | 148 | return 0; |
149 | } | 149 | } |
150 | 150 | ||
151 | static int ieee80211_open(struct net_device *dev) | 151 | /* |
152 | * NOTE: Be very careful when changing this function, it must NOT return | ||
153 | * an error on interface type changes that have been pre-checked, so most | ||
154 | * checks should be in ieee80211_check_concurrent_iface. | ||
155 | */ | ||
156 | static int ieee80211_do_open(struct net_device *dev, bool coming_up) | ||
152 | { | 157 | { |
153 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 158 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
154 | struct ieee80211_local *local = sdata->local; | 159 | struct ieee80211_local *local = sdata->local; |
@@ -157,15 +162,6 @@ static int ieee80211_open(struct net_device *dev) | |||
157 | int res; | 162 | int res; |
158 | u32 hw_reconf_flags = 0; | 163 | u32 hw_reconf_flags = 0; |
159 | 164 | ||
160 | /* fail early if user set an invalid address */ | ||
161 | if (!is_zero_ether_addr(dev->dev_addr) && | ||
162 | !is_valid_ether_addr(dev->dev_addr)) | ||
163 | return -EADDRNOTAVAIL; | ||
164 | |||
165 | res = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); | ||
166 | if (res) | ||
167 | return res; | ||
168 | |||
169 | switch (sdata->vif.type) { | 165 | switch (sdata->vif.type) { |
170 | case NL80211_IFTYPE_WDS: | 166 | case NL80211_IFTYPE_WDS: |
171 | if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) | 167 | if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) |
@@ -258,9 +254,11 @@ static int ieee80211_open(struct net_device *dev) | |||
258 | netif_carrier_on(dev); | 254 | netif_carrier_on(dev); |
259 | break; | 255 | break; |
260 | default: | 256 | default: |
261 | res = drv_add_interface(local, &sdata->vif); | 257 | if (coming_up) { |
262 | if (res) | 258 | res = drv_add_interface(local, &sdata->vif); |
263 | goto err_stop; | 259 | if (res) |
260 | goto err_stop; | ||
261 | } | ||
264 | 262 | ||
265 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | 263 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
266 | local->fif_other_bss++; | 264 | local->fif_other_bss++; |
@@ -316,7 +314,9 @@ static int ieee80211_open(struct net_device *dev) | |||
316 | hw_reconf_flags |= __ieee80211_recalc_idle(local); | 314 | hw_reconf_flags |= __ieee80211_recalc_idle(local); |
317 | mutex_unlock(&local->mtx); | 315 | mutex_unlock(&local->mtx); |
318 | 316 | ||
319 | local->open_count++; | 317 | if (coming_up) |
318 | local->open_count++; | ||
319 | |||
320 | if (hw_reconf_flags) { | 320 | if (hw_reconf_flags) { |
321 | ieee80211_hw_config(local, hw_reconf_flags); | 321 | ieee80211_hw_config(local, hw_reconf_flags); |
322 | /* | 322 | /* |
@@ -331,6 +331,8 @@ static int ieee80211_open(struct net_device *dev) | |||
331 | 331 | ||
332 | netif_tx_start_all_queues(dev); | 332 | netif_tx_start_all_queues(dev); |
333 | 333 | ||
334 | set_bit(SDATA_STATE_RUNNING, &sdata->state); | ||
335 | |||
334 | return 0; | 336 | return 0; |
335 | err_del_interface: | 337 | err_del_interface: |
336 | drv_remove_interface(local, &sdata->vif); | 338 | drv_remove_interface(local, &sdata->vif); |
@@ -344,19 +346,38 @@ static int ieee80211_open(struct net_device *dev) | |||
344 | return res; | 346 | return res; |
345 | } | 347 | } |
346 | 348 | ||
347 | static int ieee80211_stop(struct net_device *dev) | 349 | static int ieee80211_open(struct net_device *dev) |
348 | { | 350 | { |
349 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 351 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
352 | int err; | ||
353 | |||
354 | /* fail early if user set an invalid address */ | ||
355 | if (!is_zero_ether_addr(dev->dev_addr) && | ||
356 | !is_valid_ether_addr(dev->dev_addr)) | ||
357 | return -EADDRNOTAVAIL; | ||
358 | |||
359 | err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); | ||
360 | if (err) | ||
361 | return err; | ||
362 | |||
363 | return ieee80211_do_open(dev, true); | ||
364 | } | ||
365 | |||
366 | static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | ||
367 | bool going_down) | ||
368 | { | ||
350 | struct ieee80211_local *local = sdata->local; | 369 | struct ieee80211_local *local = sdata->local; |
351 | unsigned long flags; | 370 | unsigned long flags; |
352 | struct sk_buff *skb, *tmp; | 371 | struct sk_buff *skb, *tmp; |
353 | u32 hw_reconf_flags = 0; | 372 | u32 hw_reconf_flags = 0; |
354 | int i; | 373 | int i; |
355 | 374 | ||
375 | clear_bit(SDATA_STATE_RUNNING, &sdata->state); | ||
376 | |||
356 | /* | 377 | /* |
357 | * Stop TX on this interface first. | 378 | * Stop TX on this interface first. |
358 | */ | 379 | */ |
359 | netif_tx_stop_all_queues(dev); | 380 | netif_tx_stop_all_queues(sdata->dev); |
360 | 381 | ||
361 | /* | 382 | /* |
362 | * Purge work for this interface. | 383 | * Purge work for this interface. |
@@ -394,11 +415,12 @@ static int ieee80211_stop(struct net_device *dev) | |||
394 | if (sdata->vif.type == NL80211_IFTYPE_AP) | 415 | if (sdata->vif.type == NL80211_IFTYPE_AP) |
395 | local->fif_pspoll--; | 416 | local->fif_pspoll--; |
396 | 417 | ||
397 | netif_addr_lock_bh(dev); | 418 | netif_addr_lock_bh(sdata->dev); |
398 | spin_lock_bh(&local->filter_lock); | 419 | spin_lock_bh(&local->filter_lock); |
399 | __hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len); | 420 | __hw_addr_unsync(&local->mc_list, &sdata->dev->mc, |
421 | sdata->dev->addr_len); | ||
400 | spin_unlock_bh(&local->filter_lock); | 422 | spin_unlock_bh(&local->filter_lock); |
401 | netif_addr_unlock_bh(dev); | 423 | netif_addr_unlock_bh(sdata->dev); |
402 | 424 | ||
403 | ieee80211_configure_filter(local); | 425 | ieee80211_configure_filter(local); |
404 | 426 | ||
@@ -432,7 +454,8 @@ static int ieee80211_stop(struct net_device *dev) | |||
432 | WARN_ON(!list_empty(&sdata->u.ap.vlans)); | 454 | WARN_ON(!list_empty(&sdata->u.ap.vlans)); |
433 | } | 455 | } |
434 | 456 | ||
435 | local->open_count--; | 457 | if (going_down) |
458 | local->open_count--; | ||
436 | 459 | ||
437 | switch (sdata->vif.type) { | 460 | switch (sdata->vif.type) { |
438 | case NL80211_IFTYPE_AP_VLAN: | 461 | case NL80211_IFTYPE_AP_VLAN: |
@@ -504,7 +527,8 @@ static int ieee80211_stop(struct net_device *dev) | |||
504 | */ | 527 | */ |
505 | ieee80211_free_keys(sdata); | 528 | ieee80211_free_keys(sdata); |
506 | 529 | ||
507 | drv_remove_interface(local, &sdata->vif); | 530 | if (going_down) |
531 | drv_remove_interface(local, &sdata->vif); | ||
508 | } | 532 | } |
509 | 533 | ||
510 | sdata->bss = NULL; | 534 | sdata->bss = NULL; |
@@ -540,6 +564,13 @@ static int ieee80211_stop(struct net_device *dev) | |||
540 | } | 564 | } |
541 | } | 565 | } |
542 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 566 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
567 | } | ||
568 | |||
569 | static int ieee80211_stop(struct net_device *dev) | ||
570 | { | ||
571 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
572 | |||
573 | ieee80211_do_stop(sdata, true); | ||
543 | 574 | ||
544 | return 0; | 575 | return 0; |
545 | } | 576 | } |
@@ -857,9 +888,72 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
857 | ieee80211_debugfs_add_netdev(sdata); | 888 | ieee80211_debugfs_add_netdev(sdata); |
858 | } | 889 | } |
859 | 890 | ||
891 | static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, | ||
892 | enum nl80211_iftype type) | ||
893 | { | ||
894 | struct ieee80211_local *local = sdata->local; | ||
895 | int ret, err; | ||
896 | |||
897 | ASSERT_RTNL(); | ||
898 | |||
899 | if (!local->ops->change_interface) | ||
900 | return -EBUSY; | ||
901 | |||
902 | switch (sdata->vif.type) { | ||
903 | case NL80211_IFTYPE_AP: | ||
904 | case NL80211_IFTYPE_STATION: | ||
905 | case NL80211_IFTYPE_ADHOC: | ||
906 | /* | ||
907 | * Could maybe also all others here? | ||
908 | * Just not sure how that interacts | ||
909 | * with the RX/config path e.g. for | ||
910 | * mesh. | ||
911 | */ | ||
912 | break; | ||
913 | default: | ||
914 | return -EBUSY; | ||
915 | } | ||
916 | |||
917 | switch (type) { | ||
918 | case NL80211_IFTYPE_AP: | ||
919 | case NL80211_IFTYPE_STATION: | ||
920 | case NL80211_IFTYPE_ADHOC: | ||
921 | /* | ||
922 | * Could probably support everything | ||
923 | * but WDS here (WDS do_open can fail | ||
924 | * under memory pressure, which this | ||
925 | * code isn't prepared to handle). | ||
926 | */ | ||
927 | break; | ||
928 | default: | ||
929 | return -EBUSY; | ||
930 | } | ||
931 | |||
932 | ret = ieee80211_check_concurrent_iface(sdata, type); | ||
933 | if (ret) | ||
934 | return ret; | ||
935 | |||
936 | ieee80211_do_stop(sdata, false); | ||
937 | |||
938 | ieee80211_teardown_sdata(sdata->dev); | ||
939 | |||
940 | ret = drv_change_interface(local, sdata, type); | ||
941 | if (ret) | ||
942 | type = sdata->vif.type; | ||
943 | |||
944 | ieee80211_setup_sdata(sdata, type); | ||
945 | |||
946 | err = ieee80211_do_open(sdata->dev, false); | ||
947 | WARN(err, "type change: do_open returned %d", err); | ||
948 | |||
949 | return ret; | ||
950 | } | ||
951 | |||
860 | int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, | 952 | int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, |
861 | enum nl80211_iftype type) | 953 | enum nl80211_iftype type) |
862 | { | 954 | { |
955 | int ret; | ||
956 | |||
863 | ASSERT_RTNL(); | 957 | ASSERT_RTNL(); |
864 | 958 | ||
865 | if (type == sdata->vif.type) | 959 | if (type == sdata->vif.type) |
@@ -870,18 +964,15 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, | |||
870 | type == NL80211_IFTYPE_ADHOC) | 964 | type == NL80211_IFTYPE_ADHOC) |
871 | return -EOPNOTSUPP; | 965 | return -EOPNOTSUPP; |
872 | 966 | ||
873 | /* | 967 | if (ieee80211_sdata_running(sdata)) { |
874 | * We could, here, on changes between IBSS/STA/MESH modes, | 968 | ret = ieee80211_runtime_change_iftype(sdata, type); |
875 | * invoke an MLME function instead that disassociates etc. | 969 | if (ret) |
876 | * and goes into the requested mode. | 970 | return ret; |
877 | */ | 971 | } else { |
878 | 972 | /* Purge and reset type-dependent state. */ | |
879 | if (ieee80211_sdata_running(sdata)) | 973 | ieee80211_teardown_sdata(sdata->dev); |
880 | return -EBUSY; | 974 | ieee80211_setup_sdata(sdata, type); |
881 | 975 | } | |
882 | /* Purge and reset type-dependent state. */ | ||
883 | ieee80211_teardown_sdata(sdata->dev); | ||
884 | ieee80211_setup_sdata(sdata, type); | ||
885 | 976 | ||
886 | /* reset some values that shouldn't be kept across type changes */ | 977 | /* reset some values that shouldn't be kept across type changes */ |
887 | sdata->vif.bss_conf.basic_rates = | 978 | sdata->vif.bss_conf.basic_rates = |