diff options
author | Ron Rindjunsky <ron.rindjunsky@intel.com> | 2007-12-25 10:00:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:01:00 -0500 |
commit | b580781e038968fb2529460e8b61e3bf77de112a (patch) | |
tree | 7767e01c739010a2070c2a7534c6bd87a60b3b1b /net/mac80211/rx.c | |
parent | 16c5f15c73e97e22a1fcc6518da32bdcf98aec3d (diff) |
mac80211: A-MPDU Rx handling aggregation reordering
This patch handles the reordering of the Rx A-MPDU.
This issue occurs when the sequence of the internal MPDUs is not in the
right order. such a case can be encountered for example when some MPDUs from
previous aggregations were recieved, while others failed, so current A-MPDU
will contain a mix of re-transmited MPDUs and new ones.
Signed-off-by: Ron Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mac80211/rx.c')
-rw-r--r-- | net/mac80211/rx.c | 185 |
1 files changed, 183 insertions, 2 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a58a94b21401..e7b1eb8da25c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -330,7 +330,6 @@ u32 ieee80211_rx_load_stats(struct ieee80211_local *local, | |||
330 | 330 | ||
331 | /* Divide channel_use by 8 to avoid wrapping around the counter */ | 331 | /* Divide channel_use by 8 to avoid wrapping around the counter */ |
332 | load >>= CHAN_UTIL_SHIFT; | 332 | load >>= CHAN_UTIL_SHIFT; |
333 | local->channel_use_raw += load; | ||
334 | 333 | ||
335 | return load; | 334 | return load; |
336 | } | 335 | } |
@@ -1749,6 +1748,186 @@ void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1749 | sta_info_put(sta); | 1748 | sta_info_put(sta); |
1750 | } | 1749 | } |
1751 | 1750 | ||
1751 | #define SEQ_MODULO 0x1000 | ||
1752 | #define SEQ_MASK 0xfff | ||
1753 | |||
1754 | static inline int seq_less(u16 sq1, u16 sq2) | ||
1755 | { | ||
1756 | return (((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1)); | ||
1757 | } | ||
1758 | |||
1759 | static inline u16 seq_inc(u16 sq) | ||
1760 | { | ||
1761 | return ((sq + 1) & SEQ_MASK); | ||
1762 | } | ||
1763 | |||
1764 | static inline u16 seq_sub(u16 sq1, u16 sq2) | ||
1765 | { | ||
1766 | return ((sq1 - sq2) & SEQ_MASK); | ||
1767 | } | ||
1768 | |||
1769 | |||
1770 | u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, | ||
1771 | struct tid_ampdu_rx *tid_agg_rx, | ||
1772 | struct sk_buff *skb, u16 mpdu_seq_num, | ||
1773 | int bar_req) | ||
1774 | { | ||
1775 | struct ieee80211_local *local = hw_to_local(hw); | ||
1776 | struct ieee80211_rx_status status; | ||
1777 | u16 head_seq_num, buf_size; | ||
1778 | int index; | ||
1779 | u32 pkt_load; | ||
1780 | |||
1781 | buf_size = tid_agg_rx->buf_size; | ||
1782 | head_seq_num = tid_agg_rx->head_seq_num; | ||
1783 | |||
1784 | /* frame with out of date sequence number */ | ||
1785 | if (seq_less(mpdu_seq_num, head_seq_num)) { | ||
1786 | dev_kfree_skb(skb); | ||
1787 | return 1; | ||
1788 | } | ||
1789 | |||
1790 | /* if frame sequence number exceeds our buffering window size or | ||
1791 | * block Ack Request arrived - release stored frames */ | ||
1792 | if ((!seq_less(mpdu_seq_num, head_seq_num + buf_size)) || (bar_req)) { | ||
1793 | /* new head to the ordering buffer */ | ||
1794 | if (bar_req) | ||
1795 | head_seq_num = mpdu_seq_num; | ||
1796 | else | ||
1797 | head_seq_num = | ||
1798 | seq_inc(seq_sub(mpdu_seq_num, buf_size)); | ||
1799 | /* release stored frames up to new head to stack */ | ||
1800 | while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { | ||
1801 | index = seq_sub(tid_agg_rx->head_seq_num, | ||
1802 | tid_agg_rx->ssn) | ||
1803 | % tid_agg_rx->buf_size; | ||
1804 | |||
1805 | if (tid_agg_rx->reorder_buf[index]) { | ||
1806 | /* release the reordered frames to stack */ | ||
1807 | memcpy(&status, | ||
1808 | tid_agg_rx->reorder_buf[index]->cb, | ||
1809 | sizeof(status)); | ||
1810 | pkt_load = ieee80211_rx_load_stats(local, | ||
1811 | tid_agg_rx->reorder_buf[index], | ||
1812 | &status); | ||
1813 | __ieee80211_rx_handle_packet(hw, | ||
1814 | tid_agg_rx->reorder_buf[index], | ||
1815 | &status, pkt_load); | ||
1816 | tid_agg_rx->stored_mpdu_num--; | ||
1817 | tid_agg_rx->reorder_buf[index] = NULL; | ||
1818 | } | ||
1819 | tid_agg_rx->head_seq_num = | ||
1820 | seq_inc(tid_agg_rx->head_seq_num); | ||
1821 | } | ||
1822 | if (bar_req) | ||
1823 | return 1; | ||
1824 | } | ||
1825 | |||
1826 | /* now the new frame is always in the range of the reordering */ | ||
1827 | /* buffer window */ | ||
1828 | index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) | ||
1829 | % tid_agg_rx->buf_size; | ||
1830 | /* check if we already stored this frame */ | ||
1831 | if (tid_agg_rx->reorder_buf[index]) { | ||
1832 | dev_kfree_skb(skb); | ||
1833 | return 1; | ||
1834 | } | ||
1835 | |||
1836 | /* if arrived mpdu is in the right order and nothing else stored */ | ||
1837 | /* release it immediately */ | ||
1838 | if (mpdu_seq_num == tid_agg_rx->head_seq_num && | ||
1839 | tid_agg_rx->stored_mpdu_num == 0) { | ||
1840 | tid_agg_rx->head_seq_num = | ||
1841 | seq_inc(tid_agg_rx->head_seq_num); | ||
1842 | return 0; | ||
1843 | } | ||
1844 | |||
1845 | /* put the frame in the reordering buffer */ | ||
1846 | tid_agg_rx->reorder_buf[index] = skb; | ||
1847 | tid_agg_rx->stored_mpdu_num++; | ||
1848 | /* release the buffer until next missing frame */ | ||
1849 | index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) | ||
1850 | % tid_agg_rx->buf_size; | ||
1851 | while (tid_agg_rx->reorder_buf[index]) { | ||
1852 | /* release the reordered frame back to stack */ | ||
1853 | memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, | ||
1854 | sizeof(status)); | ||
1855 | pkt_load = ieee80211_rx_load_stats(local, | ||
1856 | tid_agg_rx->reorder_buf[index], | ||
1857 | &status); | ||
1858 | __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index], | ||
1859 | &status, pkt_load); | ||
1860 | tid_agg_rx->stored_mpdu_num--; | ||
1861 | tid_agg_rx->reorder_buf[index] = NULL; | ||
1862 | tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); | ||
1863 | index = seq_sub(tid_agg_rx->head_seq_num, | ||
1864 | tid_agg_rx->ssn) % tid_agg_rx->buf_size; | ||
1865 | } | ||
1866 | return 1; | ||
1867 | } | ||
1868 | |||
1869 | u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, | ||
1870 | struct sk_buff *skb) | ||
1871 | { | ||
1872 | struct ieee80211_hw *hw = &local->hw; | ||
1873 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
1874 | struct sta_info *sta; | ||
1875 | struct tid_ampdu_rx *tid_agg_rx; | ||
1876 | u16 fc, sc; | ||
1877 | u16 mpdu_seq_num; | ||
1878 | u8 ret = 0, *qc; | ||
1879 | int tid; | ||
1880 | |||
1881 | sta = sta_info_get(local, hdr->addr2); | ||
1882 | if (!sta) | ||
1883 | return ret; | ||
1884 | |||
1885 | fc = le16_to_cpu(hdr->frame_control); | ||
1886 | |||
1887 | /* filter the QoS data rx stream according to | ||
1888 | * STA/TID and check if this STA/TID is on aggregation */ | ||
1889 | if (!WLAN_FC_IS_QOS_DATA(fc)) | ||
1890 | goto end_reorder; | ||
1891 | |||
1892 | qc = skb->data + ieee80211_get_hdrlen(fc) - QOS_CONTROL_LEN; | ||
1893 | tid = qc[0] & QOS_CONTROL_TID_MASK; | ||
1894 | tid_agg_rx = &(sta->ampdu_mlme.tid_rx[tid]); | ||
1895 | |||
1896 | if (tid_agg_rx->state != HT_AGG_STATE_OPERATIONAL) | ||
1897 | goto end_reorder; | ||
1898 | |||
1899 | /* null data frames are excluded */ | ||
1900 | if (unlikely(fc & IEEE80211_STYPE_QOS_NULLFUNC)) | ||
1901 | goto end_reorder; | ||
1902 | |||
1903 | /* new un-ordered ampdu frame - process it */ | ||
1904 | |||
1905 | /* reset session timer */ | ||
1906 | if (tid_agg_rx->timeout) { | ||
1907 | unsigned long expires = | ||
1908 | jiffies + (tid_agg_rx->timeout / 1000) * HZ; | ||
1909 | mod_timer(&tid_agg_rx->session_timer, expires); | ||
1910 | } | ||
1911 | |||
1912 | /* if this mpdu is fragmented - terminate rx aggregation session */ | ||
1913 | sc = le16_to_cpu(hdr->seq_ctrl); | ||
1914 | if (sc & IEEE80211_SCTL_FRAG) { | ||
1915 | ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, | ||
1916 | tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP); | ||
1917 | ret = 1; | ||
1918 | goto end_reorder; | ||
1919 | } | ||
1920 | |||
1921 | /* according to mpdu sequence number deal with reordering buffer */ | ||
1922 | mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; | ||
1923 | ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, | ||
1924 | mpdu_seq_num, 0); | ||
1925 | end_reorder: | ||
1926 | if (sta) | ||
1927 | sta_info_put(sta); | ||
1928 | return ret; | ||
1929 | } | ||
1930 | |||
1752 | /* | 1931 | /* |
1753 | * This is the receive path handler. It is called by a low level driver when an | 1932 | * This is the receive path handler. It is called by a low level driver when an |
1754 | * 802.11 MPDU is received from the hardware. | 1933 | * 802.11 MPDU is received from the hardware. |
@@ -1779,8 +1958,10 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1779 | } | 1958 | } |
1780 | 1959 | ||
1781 | pkt_load = ieee80211_rx_load_stats(local, skb, status); | 1960 | pkt_load = ieee80211_rx_load_stats(local, skb, status); |
1961 | local->channel_use_raw += pkt_load; | ||
1782 | 1962 | ||
1783 | __ieee80211_rx_handle_packet(hw, skb, status, pkt_load); | 1963 | if (!ieee80211_rx_reorder_ampdu(local, skb)) |
1964 | __ieee80211_rx_handle_packet(hw, skb, status, pkt_load); | ||
1784 | 1965 | ||
1785 | rcu_read_unlock(); | 1966 | rcu_read_unlock(); |
1786 | } | 1967 | } |