diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-01-20 07:55:21 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-02-06 14:48:24 -0500 |
commit | f09603a259ffef69ad4516a04eb06cd65ac522fe (patch) | |
tree | 3f826769c697eb15a76771e25291bbb54f9f58ab /net | |
parent | 71ec375c75095002f36f083ceb32bbb8725734ae (diff) |
mac80211: add sta_state callback
(based on Eliad's patch)
Add a callback to notify the low-level driver whenever
the state of a station changes. The driver is only
notified when the station is actually in the mac80211
hash table, not for pre-insert state transitions.
To allow the driver to replace sta_add/remove calls
with this, call extra transitions with the NOTEXIST
state.
This callback can fail, so we need to be careful in
handling it when a station is inserted, particularly
in the IBSS case where we still keep the station entry
around for mac80211 purposes.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/driver-ops.h | 22 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 32 | ||||
-rw-r--r-- | net/mac80211/main.c | 3 | ||||
-rw-r--r-- | net/mac80211/pm.c | 10 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 106 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 9 | ||||
-rw-r--r-- | net/mac80211/util.c | 10 |
7 files changed, 164 insertions, 28 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b8673f6100df..4bd266ec5333 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -478,6 +478,28 @@ static inline void drv_sta_remove(struct ieee80211_local *local, | |||
478 | trace_drv_return_void(local); | 478 | trace_drv_return_void(local); |
479 | } | 479 | } |
480 | 480 | ||
481 | static inline __must_check | ||
482 | int drv_sta_state(struct ieee80211_local *local, | ||
483 | struct ieee80211_sub_if_data *sdata, | ||
484 | struct sta_info *sta, | ||
485 | enum ieee80211_sta_state old_state, | ||
486 | enum ieee80211_sta_state new_state) | ||
487 | { | ||
488 | int ret = 0; | ||
489 | |||
490 | might_sleep(); | ||
491 | |||
492 | sdata = get_bss_sdata(sdata); | ||
493 | check_sdata_in_driver(sdata); | ||
494 | |||
495 | trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); | ||
496 | if (local->ops->sta_state) | ||
497 | ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, | ||
498 | old_state, new_state); | ||
499 | trace_drv_return_int(local, ret); | ||
500 | return ret; | ||
501 | } | ||
502 | |||
481 | static inline int drv_conf_tx(struct ieee80211_local *local, | 503 | static inline int drv_conf_tx(struct ieee80211_local *local, |
482 | struct ieee80211_sub_if_data *sdata, u16 queue, | 504 | struct ieee80211_sub_if_data *sdata, u16 queue, |
483 | const struct ieee80211_tx_queue_params *params) | 505 | const struct ieee80211_tx_queue_params *params) |
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 6e9df8fd8fb8..384e2f08c187 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h | |||
@@ -635,6 +635,38 @@ TRACE_EVENT(drv_sta_notify, | |||
635 | ) | 635 | ) |
636 | ); | 636 | ); |
637 | 637 | ||
638 | TRACE_EVENT(drv_sta_state, | ||
639 | TP_PROTO(struct ieee80211_local *local, | ||
640 | struct ieee80211_sub_if_data *sdata, | ||
641 | struct ieee80211_sta *sta, | ||
642 | enum ieee80211_sta_state old_state, | ||
643 | enum ieee80211_sta_state new_state), | ||
644 | |||
645 | TP_ARGS(local, sdata, sta, old_state, new_state), | ||
646 | |||
647 | TP_STRUCT__entry( | ||
648 | LOCAL_ENTRY | ||
649 | VIF_ENTRY | ||
650 | STA_ENTRY | ||
651 | __field(u32, old_state) | ||
652 | __field(u32, new_state) | ||
653 | ), | ||
654 | |||
655 | TP_fast_assign( | ||
656 | LOCAL_ASSIGN; | ||
657 | VIF_ASSIGN; | ||
658 | STA_ASSIGN; | ||
659 | __entry->old_state = old_state; | ||
660 | __entry->new_state = new_state; | ||
661 | ), | ||
662 | |||
663 | TP_printk( | ||
664 | LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " state: %d->%d", | ||
665 | LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, | ||
666 | __entry->old_state, __entry->new_state | ||
667 | ) | ||
668 | ); | ||
669 | |||
638 | TRACE_EVENT(drv_sta_add, | 670 | TRACE_EVENT(drv_sta_add, |
639 | TP_PROTO(struct ieee80211_local *local, | 671 | TP_PROTO(struct ieee80211_local *local, |
640 | struct ieee80211_sub_if_data *sdata, | 672 | struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6192caadfab9..f4fc540aac17 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -535,6 +535,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
535 | int priv_size, i; | 535 | int priv_size, i; |
536 | struct wiphy *wiphy; | 536 | struct wiphy *wiphy; |
537 | 537 | ||
538 | if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) | ||
539 | return NULL; | ||
540 | |||
538 | /* Ensure 32-byte alignment of our private data and hw private data. | 541 | /* Ensure 32-byte alignment of our private data and hw private data. |
539 | * We use the wiphy priv data for both our ieee80211_local and for | 542 | * We use the wiphy priv data for both our ieee80211_local and for |
540 | * the driver's private data | 543 | * the driver's private data |
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index c65ff471acce..af49ac4f0826 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c | |||
@@ -97,9 +97,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | |||
97 | /* tear down aggregation sessions and remove STAs */ | 97 | /* tear down aggregation sessions and remove STAs */ |
98 | mutex_lock(&local->sta_mtx); | 98 | mutex_lock(&local->sta_mtx); |
99 | list_for_each_entry(sta, &local->sta_list, list) { | 99 | list_for_each_entry(sta, &local->sta_list, list) { |
100 | if (sta->uploaded) | 100 | if (sta->uploaded) { |
101 | enum ieee80211_sta_state state; | ||
102 | |||
101 | drv_sta_remove(local, sta->sdata, &sta->sta); | 103 | drv_sta_remove(local, sta->sdata, &sta->sta); |
102 | 104 | ||
105 | state = sta->sta_state; | ||
106 | for (; state > IEEE80211_STA_NOTEXIST; state--) | ||
107 | WARN_ON(drv_sta_state(local, sdata, sta, | ||
108 | state, state - 1)); | ||
109 | } | ||
110 | |||
103 | mesh_plink_quiesce(sta); | 111 | mesh_plink_quiesce(sta); |
104 | } | 112 | } |
105 | mutex_unlock(&local->sta_mtx); | 113 | mutex_unlock(&local->sta_mtx); |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 464bc691644b..fcd9027c6699 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -351,6 +351,38 @@ static int sta_info_insert_check(struct sta_info *sta) | |||
351 | return 0; | 351 | return 0; |
352 | } | 352 | } |
353 | 353 | ||
354 | static int sta_info_insert_drv_state(struct ieee80211_local *local, | ||
355 | struct ieee80211_sub_if_data *sdata, | ||
356 | struct sta_info *sta) | ||
357 | { | ||
358 | enum ieee80211_sta_state state; | ||
359 | int err = 0; | ||
360 | |||
361 | for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) { | ||
362 | err = drv_sta_state(local, sdata, sta, state, state + 1); | ||
363 | if (err) | ||
364 | break; | ||
365 | } | ||
366 | |||
367 | if (!err) { | ||
368 | sta->uploaded = true; | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | ||
373 | printk(KERN_DEBUG | ||
374 | "%s: failed to move IBSS STA %pM to state %d (%d) - keeping it anyway.\n", | ||
375 | sdata->name, sta->sta.addr, state + 1, err); | ||
376 | err = 0; | ||
377 | } | ||
378 | |||
379 | /* unwind on error */ | ||
380 | for (; state > IEEE80211_STA_NOTEXIST; state--) | ||
381 | WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1)); | ||
382 | |||
383 | return err; | ||
384 | } | ||
385 | |||
354 | /* | 386 | /* |
355 | * should be called with sta_mtx locked | 387 | * should be called with sta_mtx locked |
356 | * this function replaces the mutex lock | 388 | * this function replaces the mutex lock |
@@ -392,8 +424,11 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) | |||
392 | printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " | 424 | printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " |
393 | "driver (%d) - keeping it anyway.\n", | 425 | "driver (%d) - keeping it anyway.\n", |
394 | sdata->name, sta->sta.addr, err); | 426 | sdata->name, sta->sta.addr, err); |
395 | } else | 427 | } else { |
396 | sta->uploaded = true; | 428 | err = sta_info_insert_drv_state(local, sdata, sta); |
429 | if (err) | ||
430 | goto out_err; | ||
431 | } | ||
397 | } | 432 | } |
398 | 433 | ||
399 | if (!dummy_reinsert) { | 434 | if (!dummy_reinsert) { |
@@ -759,15 +794,19 @@ int __must_check __sta_info_destroy(struct sta_info *sta) | |||
759 | RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); | 794 | RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); |
760 | 795 | ||
761 | while (sta->sta_state > IEEE80211_STA_NONE) { | 796 | while (sta->sta_state > IEEE80211_STA_NONE) { |
762 | int err = sta_info_move_state(sta, sta->sta_state - 1); | 797 | ret = sta_info_move_state(sta, sta->sta_state - 1); |
763 | if (err) { | 798 | if (ret) { |
764 | WARN_ON_ONCE(1); | 799 | WARN_ON_ONCE(1); |
765 | break; | 800 | break; |
766 | } | 801 | } |
767 | } | 802 | } |
768 | 803 | ||
769 | if (sta->uploaded) | 804 | if (sta->uploaded) { |
770 | drv_sta_remove(local, sdata, &sta->sta); | 805 | drv_sta_remove(local, sdata, &sta->sta); |
806 | ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE, | ||
807 | IEEE80211_STA_NOTEXIST); | ||
808 | WARN_ON_ONCE(ret != 0); | ||
809 | } | ||
771 | 810 | ||
772 | /* | 811 | /* |
773 | * At this point, after we wait for an RCU grace period, | 812 | * At this point, after we wait for an RCU grace period, |
@@ -1404,20 +1443,58 @@ int sta_info_move_state(struct sta_info *sta, | |||
1404 | if (sta->sta_state == new_state) | 1443 | if (sta->sta_state == new_state) |
1405 | return 0; | 1444 | return 0; |
1406 | 1445 | ||
1446 | /* check allowed transitions first */ | ||
1447 | |||
1448 | switch (new_state) { | ||
1449 | case IEEE80211_STA_NONE: | ||
1450 | if (sta->sta_state != IEEE80211_STA_AUTH) | ||
1451 | return -EINVAL; | ||
1452 | break; | ||
1453 | case IEEE80211_STA_AUTH: | ||
1454 | if (sta->sta_state != IEEE80211_STA_NONE && | ||
1455 | sta->sta_state != IEEE80211_STA_ASSOC) | ||
1456 | return -EINVAL; | ||
1457 | break; | ||
1458 | case IEEE80211_STA_ASSOC: | ||
1459 | if (sta->sta_state != IEEE80211_STA_AUTH && | ||
1460 | sta->sta_state != IEEE80211_STA_AUTHORIZED) | ||
1461 | return -EINVAL; | ||
1462 | break; | ||
1463 | case IEEE80211_STA_AUTHORIZED: | ||
1464 | if (sta->sta_state != IEEE80211_STA_ASSOC) | ||
1465 | return -EINVAL; | ||
1466 | break; | ||
1467 | default: | ||
1468 | WARN(1, "invalid state %d", new_state); | ||
1469 | return -EINVAL; | ||
1470 | } | ||
1471 | |||
1472 | printk(KERN_DEBUG "%s: moving STA %pM to state %d\n", | ||
1473 | sta->sdata->name, sta->sta.addr, new_state); | ||
1474 | |||
1475 | /* | ||
1476 | * notify the driver before the actual changes so it can | ||
1477 | * fail the transition | ||
1478 | */ | ||
1479 | if (test_sta_flag(sta, WLAN_STA_INSERTED)) { | ||
1480 | int err = drv_sta_state(sta->local, sta->sdata, sta, | ||
1481 | sta->sta_state, new_state); | ||
1482 | if (err) | ||
1483 | return err; | ||
1484 | } | ||
1485 | |||
1486 | /* reflect the change in all state variables */ | ||
1487 | |||
1407 | switch (new_state) { | 1488 | switch (new_state) { |
1408 | case IEEE80211_STA_NONE: | 1489 | case IEEE80211_STA_NONE: |
1409 | if (sta->sta_state == IEEE80211_STA_AUTH) | 1490 | if (sta->sta_state == IEEE80211_STA_AUTH) |
1410 | clear_bit(WLAN_STA_AUTH, &sta->_flags); | 1491 | clear_bit(WLAN_STA_AUTH, &sta->_flags); |
1411 | else | ||
1412 | return -EINVAL; | ||
1413 | break; | 1492 | break; |
1414 | case IEEE80211_STA_AUTH: | 1493 | case IEEE80211_STA_AUTH: |
1415 | if (sta->sta_state == IEEE80211_STA_NONE) | 1494 | if (sta->sta_state == IEEE80211_STA_NONE) |
1416 | set_bit(WLAN_STA_AUTH, &sta->_flags); | 1495 | set_bit(WLAN_STA_AUTH, &sta->_flags); |
1417 | else if (sta->sta_state == IEEE80211_STA_ASSOC) | 1496 | else if (sta->sta_state == IEEE80211_STA_ASSOC) |
1418 | clear_bit(WLAN_STA_ASSOC, &sta->_flags); | 1497 | clear_bit(WLAN_STA_ASSOC, &sta->_flags); |
1419 | else | ||
1420 | return -EINVAL; | ||
1421 | break; | 1498 | break; |
1422 | case IEEE80211_STA_ASSOC: | 1499 | case IEEE80211_STA_ASSOC: |
1423 | if (sta->sta_state == IEEE80211_STA_AUTH) { | 1500 | if (sta->sta_state == IEEE80211_STA_AUTH) { |
@@ -1426,24 +1503,19 @@ int sta_info_move_state(struct sta_info *sta, | |||
1426 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP) | 1503 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP) |
1427 | atomic_dec(&sta->sdata->u.ap.num_sta_authorized); | 1504 | atomic_dec(&sta->sdata->u.ap.num_sta_authorized); |
1428 | clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); | 1505 | clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); |
1429 | } else | 1506 | } |
1430 | return -EINVAL; | ||
1431 | break; | 1507 | break; |
1432 | case IEEE80211_STA_AUTHORIZED: | 1508 | case IEEE80211_STA_AUTHORIZED: |
1433 | if (sta->sta_state == IEEE80211_STA_ASSOC) { | 1509 | if (sta->sta_state == IEEE80211_STA_ASSOC) { |
1434 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP) | 1510 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP) |
1435 | atomic_inc(&sta->sdata->u.ap.num_sta_authorized); | 1511 | atomic_inc(&sta->sdata->u.ap.num_sta_authorized); |
1436 | set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); | 1512 | set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); |
1437 | } else | 1513 | } |
1438 | return -EINVAL; | ||
1439 | break; | 1514 | break; |
1440 | default: | 1515 | default: |
1441 | WARN(1, "invalid state %d", new_state); | 1516 | break; |
1442 | return -EINVAL; | ||
1443 | } | 1517 | } |
1444 | 1518 | ||
1445 | printk(KERN_DEBUG "%s: moving STA %pM to state %d\n", | ||
1446 | sta->sdata->name, sta->sta.addr, new_state); | ||
1447 | sta->sta_state = new_state; | 1519 | sta->sta_state = new_state; |
1448 | 1520 | ||
1449 | return 0; | 1521 | return 0; |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index da4b03c1c3bc..2ee808860007 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -75,15 +75,6 @@ enum ieee80211_sta_info_flags { | |||
75 | WLAN_STA_INSERTED, | 75 | WLAN_STA_INSERTED, |
76 | }; | 76 | }; |
77 | 77 | ||
78 | enum ieee80211_sta_state { | ||
79 | /* NOTE: These need to be ordered correctly! */ | ||
80 | IEEE80211_STA_NOTEXIST, | ||
81 | IEEE80211_STA_NONE, | ||
82 | IEEE80211_STA_AUTH, | ||
83 | IEEE80211_STA_ASSOC, | ||
84 | IEEE80211_STA_AUTHORIZED, | ||
85 | }; | ||
86 | |||
87 | #define STA_TID_NUM 16 | 78 | #define STA_TID_NUM 16 |
88 | #define ADDBA_RESP_INTERVAL HZ | 79 | #define ADDBA_RESP_INTERVAL HZ |
89 | #define HT_AGG_MAX_RETRIES 15 | 80 | #define HT_AGG_MAX_RETRIES 15 |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d4966e88aa49..8f8b4ecc776f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -1184,8 +1184,16 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1184 | /* add STAs back */ | 1184 | /* add STAs back */ |
1185 | mutex_lock(&local->sta_mtx); | 1185 | mutex_lock(&local->sta_mtx); |
1186 | list_for_each_entry(sta, &local->sta_list, list) { | 1186 | list_for_each_entry(sta, &local->sta_list, list) { |
1187 | if (sta->uploaded) | 1187 | if (sta->uploaded) { |
1188 | enum ieee80211_sta_state state; | ||
1189 | |||
1188 | WARN_ON(drv_sta_add(local, sta->sdata, &sta->sta)); | 1190 | WARN_ON(drv_sta_add(local, sta->sdata, &sta->sta)); |
1191 | |||
1192 | for (state = IEEE80211_STA_NOTEXIST; | ||
1193 | state < sta->sta_state - 1; state++) | ||
1194 | WARN_ON(drv_sta_state(local, sta->sdata, sta, | ||
1195 | state, state + 1)); | ||
1196 | } | ||
1189 | } | 1197 | } |
1190 | mutex_unlock(&local->sta_mtx); | 1198 | mutex_unlock(&local->sta_mtx); |
1191 | 1199 | ||