diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-11-06 08:13:34 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-11-09 16:14:09 -0500 |
commit | a729cff8ad5120d0d5172ec28a3843d1cb458f79 (patch) | |
tree | 96e85c0805050ba03a2df2a4278640da8f0454c5 | |
parent | 1f074bd8eb7a4a210a5119cd7220f89da6c7a2c3 (diff) |
mac80211: implement wifi TX status
Implement the socket wifi TX status error
queue reflection in mac80211.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/net/mac80211.h | 5 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
-rw-r--r-- | net/mac80211/main.c | 18 | ||||
-rw-r--r-- | net/mac80211/status.c | 38 | ||||
-rw-r--r-- | net/mac80211/tx.c | 56 |
5 files changed, 115 insertions, 6 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b9b9c9452131..2714646b298f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -518,7 +518,7 @@ struct ieee80211_tx_rate { | |||
518 | * @flags: transmit info flags, defined above | 518 | * @flags: transmit info flags, defined above |
519 | * @band: the band to transmit on (use for checking for races) | 519 | * @band: the band to transmit on (use for checking for races) |
520 | * @antenna_sel_tx: antenna to use, 0 for automatic diversity | 520 | * @antenna_sel_tx: antenna to use, 0 for automatic diversity |
521 | * @pad: padding, ignore | 521 | * @ack_frame_id: internal frame ID for TX status, used internally |
522 | * @control: union for control data | 522 | * @control: union for control data |
523 | * @status: union for status data | 523 | * @status: union for status data |
524 | * @driver_data: array of driver_data pointers | 524 | * @driver_data: array of driver_data pointers |
@@ -535,8 +535,7 @@ struct ieee80211_tx_info { | |||
535 | 535 | ||
536 | u8 antenna_sel_tx; | 536 | u8 antenna_sel_tx; |
537 | 537 | ||
538 | /* 2 byte hole */ | 538 | u16 ack_frame_id; |
539 | u8 pad[2]; | ||
540 | 539 | ||
541 | union { | 540 | union { |
542 | struct { | 541 | struct { |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4bef6eca1722..76e656bf78f9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
25 | #include <linux/etherdevice.h> | 25 | #include <linux/etherdevice.h> |
26 | #include <linux/leds.h> | 26 | #include <linux/leds.h> |
27 | #include <linux/idr.h> | ||
27 | #include <net/ieee80211_radiotap.h> | 28 | #include <net/ieee80211_radiotap.h> |
28 | #include <net/cfg80211.h> | 29 | #include <net/cfg80211.h> |
29 | #include <net/mac80211.h> | 30 | #include <net/mac80211.h> |
@@ -1017,6 +1018,9 @@ struct ieee80211_local { | |||
1017 | u32 hw_roc_cookie; | 1018 | u32 hw_roc_cookie; |
1018 | bool hw_roc_for_tx; | 1019 | bool hw_roc_for_tx; |
1019 | 1020 | ||
1021 | struct idr ack_status_frames; | ||
1022 | spinlock_t ack_status_lock; | ||
1023 | |||
1020 | /* dummy netdev for use w/ NAPI */ | 1024 | /* dummy netdev for use w/ NAPI */ |
1021 | struct net_device napi_dev; | 1025 | struct net_device napi_dev; |
1022 | 1026 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 8e9327bca910..e323d4e6647b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -596,6 +596,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
596 | WIPHY_FLAG_4ADDR_STATION | | 596 | WIPHY_FLAG_4ADDR_STATION | |
597 | WIPHY_FLAG_REPORTS_OBSS; | 597 | WIPHY_FLAG_REPORTS_OBSS; |
598 | 598 | ||
599 | wiphy->features = NL80211_FEATURE_SK_TX_STATUS; | ||
600 | |||
599 | if (!ops->set_key) | 601 | if (!ops->set_key) |
600 | wiphy->flags |= WIPHY_FLAG_IBSS_RSN; | 602 | wiphy->flags |= WIPHY_FLAG_IBSS_RSN; |
601 | 603 | ||
@@ -669,6 +671,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
669 | INIT_WORK(&local->sched_scan_stopped_work, | 671 | INIT_WORK(&local->sched_scan_stopped_work, |
670 | ieee80211_sched_scan_stopped_work); | 672 | ieee80211_sched_scan_stopped_work); |
671 | 673 | ||
674 | spin_lock_init(&local->ack_status_lock); | ||
675 | idr_init(&local->ack_status_frames); | ||
676 | /* preallocate at least one entry */ | ||
677 | idr_pre_get(&local->ack_status_frames, GFP_KERNEL); | ||
678 | |||
672 | sta_info_init(local); | 679 | sta_info_init(local); |
673 | 680 | ||
674 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { | 681 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { |
@@ -1044,6 +1051,13 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) | |||
1044 | } | 1051 | } |
1045 | EXPORT_SYMBOL(ieee80211_unregister_hw); | 1052 | EXPORT_SYMBOL(ieee80211_unregister_hw); |
1046 | 1053 | ||
1054 | static int ieee80211_free_ack_frame(int id, void *p, void *data) | ||
1055 | { | ||
1056 | WARN_ONCE(1, "Have pending ack frames!\n"); | ||
1057 | kfree_skb(p); | ||
1058 | return 0; | ||
1059 | } | ||
1060 | |||
1047 | void ieee80211_free_hw(struct ieee80211_hw *hw) | 1061 | void ieee80211_free_hw(struct ieee80211_hw *hw) |
1048 | { | 1062 | { |
1049 | struct ieee80211_local *local = hw_to_local(hw); | 1063 | struct ieee80211_local *local = hw_to_local(hw); |
@@ -1054,6 +1068,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) | |||
1054 | if (local->wiphy_ciphers_allocated) | 1068 | if (local->wiphy_ciphers_allocated) |
1055 | kfree(local->hw.wiphy->cipher_suites); | 1069 | kfree(local->hw.wiphy->cipher_suites); |
1056 | 1070 | ||
1071 | idr_for_each(&local->ack_status_frames, | ||
1072 | ieee80211_free_ack_frame, NULL); | ||
1073 | idr_destroy(&local->ack_status_frames); | ||
1074 | |||
1057 | wiphy_free(local->hw.wiphy); | 1075 | wiphy_free(local->hw.wiphy); |
1058 | } | 1076 | } |
1059 | EXPORT_SYMBOL(ieee80211_free_hw); | 1077 | EXPORT_SYMBOL(ieee80211_free_hw); |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 94702f103cfc..83b800d17a9a 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -548,6 +548,24 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
548 | } | 548 | } |
549 | } | 549 | } |
550 | 550 | ||
551 | if (unlikely(info->ack_frame_id)) { | ||
552 | struct sk_buff *ack_skb; | ||
553 | unsigned long flags; | ||
554 | |||
555 | spin_lock_irqsave(&local->ack_status_lock, flags); | ||
556 | ack_skb = idr_find(&local->ack_status_frames, | ||
557 | info->ack_frame_id); | ||
558 | if (ack_skb) | ||
559 | idr_remove(&local->ack_status_frames, | ||
560 | info->ack_frame_id); | ||
561 | spin_unlock_irqrestore(&local->ack_status_lock, flags); | ||
562 | |||
563 | /* consumes ack_skb */ | ||
564 | if (ack_skb) | ||
565 | skb_complete_wifi_ack(ack_skb, | ||
566 | info->flags & IEEE80211_TX_STAT_ACK); | ||
567 | } | ||
568 | |||
551 | /* this was a transmitted frame, but now we want to reuse it */ | 569 | /* this was a transmitted frame, but now we want to reuse it */ |
552 | skb_orphan(skb); | 570 | skb_orphan(skb); |
553 | 571 | ||
@@ -621,6 +639,26 @@ EXPORT_SYMBOL(ieee80211_report_low_ack); | |||
621 | 639 | ||
622 | void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) | 640 | void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) |
623 | { | 641 | { |
642 | struct ieee80211_local *local = hw_to_local(hw); | ||
643 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
644 | |||
645 | if (unlikely(info->ack_frame_id)) { | ||
646 | struct sk_buff *ack_skb; | ||
647 | unsigned long flags; | ||
648 | |||
649 | spin_lock_irqsave(&local->ack_status_lock, flags); | ||
650 | ack_skb = idr_find(&local->ack_status_frames, | ||
651 | info->ack_frame_id); | ||
652 | if (ack_skb) | ||
653 | idr_remove(&local->ack_status_frames, | ||
654 | info->ack_frame_id); | ||
655 | spin_unlock_irqrestore(&local->ack_status_lock, flags); | ||
656 | |||
657 | /* consumes ack_skb */ | ||
658 | if (ack_skb) | ||
659 | dev_kfree_skb_any(ack_skb); | ||
660 | } | ||
661 | |||
624 | dev_kfree_skb_any(skb); | 662 | dev_kfree_skb_any(skb); |
625 | } | 663 | } |
626 | EXPORT_SYMBOL(ieee80211_free_txskb); | 664 | EXPORT_SYMBOL(ieee80211_free_txskb); |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a543d26058db..ab6cb56bc74d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1684,8 +1684,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1684 | int nh_pos, h_pos; | 1684 | int nh_pos, h_pos; |
1685 | struct sta_info *sta = NULL; | 1685 | struct sta_info *sta = NULL; |
1686 | bool wme_sta = false, authorized = false, tdls_auth = false; | 1686 | bool wme_sta = false, authorized = false, tdls_auth = false; |
1687 | struct sk_buff *tmp_skb; | ||
1688 | bool tdls_direct = false; | 1687 | bool tdls_direct = false; |
1688 | bool multicast; | ||
1689 | u32 info_flags = 0; | ||
1690 | u16 info_id = 0; | ||
1689 | 1691 | ||
1690 | if (unlikely(skb->len < ETH_HLEN)) { | 1692 | if (unlikely(skb->len < ETH_HLEN)) { |
1691 | ret = NETDEV_TX_OK; | 1693 | ret = NETDEV_TX_OK; |
@@ -1872,7 +1874,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1872 | * if it is a multicast address (which can only happen | 1874 | * if it is a multicast address (which can only happen |
1873 | * in AP mode) | 1875 | * in AP mode) |
1874 | */ | 1876 | */ |
1875 | if (!is_multicast_ether_addr(hdr.addr1)) { | 1877 | multicast = is_multicast_ether_addr(hdr.addr1); |
1878 | if (!multicast) { | ||
1876 | rcu_read_lock(); | 1879 | rcu_read_lock(); |
1877 | sta = sta_info_get(sdata, hdr.addr1); | 1880 | sta = sta_info_get(sdata, hdr.addr1); |
1878 | if (sta) { | 1881 | if (sta) { |
@@ -1913,11 +1916,54 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1913 | goto fail; | 1916 | goto fail; |
1914 | } | 1917 | } |
1915 | 1918 | ||
1919 | if (unlikely(!multicast && skb->sk && | ||
1920 | skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { | ||
1921 | struct sk_buff *orig_skb = skb; | ||
1922 | |||
1923 | skb = skb_clone(skb, GFP_ATOMIC); | ||
1924 | if (skb) { | ||
1925 | unsigned long flags; | ||
1926 | int id, r; | ||
1927 | |||
1928 | spin_lock_irqsave(&local->ack_status_lock, flags); | ||
1929 | r = idr_get_new_above(&local->ack_status_frames, | ||
1930 | orig_skb, 1, &id); | ||
1931 | if (r == -EAGAIN) { | ||
1932 | idr_pre_get(&local->ack_status_frames, | ||
1933 | GFP_ATOMIC); | ||
1934 | r = idr_get_new_above(&local->ack_status_frames, | ||
1935 | orig_skb, 1, &id); | ||
1936 | } | ||
1937 | if (WARN_ON(!id) || id > 0xffff) { | ||
1938 | idr_remove(&local->ack_status_frames, id); | ||
1939 | r = -ERANGE; | ||
1940 | } | ||
1941 | spin_unlock_irqrestore(&local->ack_status_lock, flags); | ||
1942 | |||
1943 | if (!r) { | ||
1944 | info_id = id; | ||
1945 | info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; | ||
1946 | } else if (skb_shared(skb)) { | ||
1947 | kfree_skb(orig_skb); | ||
1948 | } else { | ||
1949 | kfree_skb(skb); | ||
1950 | skb = orig_skb; | ||
1951 | } | ||
1952 | } else { | ||
1953 | /* couldn't clone -- lose tx status ... */ | ||
1954 | skb = orig_skb; | ||
1955 | } | ||
1956 | } | ||
1957 | |||
1916 | /* | 1958 | /* |
1917 | * If the skb is shared we need to obtain our own copy. | 1959 | * If the skb is shared we need to obtain our own copy. |
1918 | */ | 1960 | */ |
1919 | if (skb_shared(skb)) { | 1961 | if (skb_shared(skb)) { |
1920 | tmp_skb = skb; | 1962 | struct sk_buff *tmp_skb = skb; |
1963 | |||
1964 | /* can't happen -- skb is a clone if info_id != 0 */ | ||
1965 | WARN_ON(info_id); | ||
1966 | |||
1921 | skb = skb_clone(skb, GFP_ATOMIC); | 1967 | skb = skb_clone(skb, GFP_ATOMIC); |
1922 | kfree_skb(tmp_skb); | 1968 | kfree_skb(tmp_skb); |
1923 | 1969 | ||
@@ -2018,6 +2064,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
2018 | memset(info, 0, sizeof(*info)); | 2064 | memset(info, 0, sizeof(*info)); |
2019 | 2065 | ||
2020 | dev->trans_start = jiffies; | 2066 | dev->trans_start = jiffies; |
2067 | |||
2068 | info->flags = info_flags; | ||
2069 | info->ack_frame_id = info_id; | ||
2070 | |||
2021 | ieee80211_xmit(sdata, skb); | 2071 | ieee80211_xmit(sdata, skb); |
2022 | 2072 | ||
2023 | return NETDEV_TX_OK; | 2073 | return NETDEV_TX_OK; |