diff options
author | Arend van Spriel <arend@broadcom.com> | 2013-04-23 06:53:15 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-04-23 14:17:05 -0400 |
commit | 1aac1e91171586e4d0981e40484a7c93d9b1f289 (patch) | |
tree | 81f174cf957bc2ae54f36cbc550f1316b17a24be /drivers/net/wireless/brcm80211/brcmfmac | |
parent | a786b38d542f62e19ffa8adbcbfc82065cd13a11 (diff) |
brcmfmac: add credit borrowing mechanism
The firmware provides credits to the driver per WMM-AC. When
only AC_BE are to be transmitted to the firmware the driver
may use credits from other priorities to send AC_BE packets
towards the firmware.
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 95 |
1 files changed, 87 insertions, 8 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index f9153b115dcf..5352dc1fdf3c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/netdevice.h> | 21 | #include <linux/netdevice.h> |
22 | #include <linux/etherdevice.h> | 22 | #include <linux/etherdevice.h> |
23 | #include <linux/err.h> | 23 | #include <linux/err.h> |
24 | #include <linux/jiffies.h> | ||
24 | #include <uapi/linux/nl80211.h> | 25 | #include <uapi/linux/nl80211.h> |
25 | #include <net/cfg80211.h> | 26 | #include <net/cfg80211.h> |
26 | 27 | ||
@@ -271,6 +272,9 @@ struct brcmf_skbuff_cb { | |||
271 | brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ | 272 | brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ |
272 | BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) | 273 | BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) |
273 | 274 | ||
275 | /* How long to defer borrowing in jiffies */ | ||
276 | #define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10) | ||
277 | |||
274 | /** | 278 | /** |
275 | * enum brcmf_fws_fifo - fifo indices used by dongle firmware. | 279 | * enum brcmf_fws_fifo - fifo indices used by dongle firmware. |
276 | * | 280 | * |
@@ -425,9 +429,11 @@ struct brcmf_fws_info { | |||
425 | struct work_struct fws_dequeue_work; | 429 | struct work_struct fws_dequeue_work; |
426 | u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; | 430 | u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; |
427 | int fifo_credit[BRCMF_FWS_FIFO_COUNT]; | 431 | int fifo_credit[BRCMF_FWS_FIFO_COUNT]; |
432 | int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]; | ||
428 | int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; | 433 | int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; |
429 | u32 fifo_credit_map; | 434 | u32 fifo_credit_map; |
430 | u32 fifo_delay_map; | 435 | u32 fifo_delay_map; |
436 | unsigned long borrow_defer_timestamp; | ||
431 | }; | 437 | }; |
432 | 438 | ||
433 | /* | 439 | /* |
@@ -997,9 +1003,34 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, | |||
997 | static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, | 1003 | static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, |
998 | u8 fifo, u8 credits) | 1004 | u8 fifo, u8 credits) |
999 | { | 1005 | { |
1006 | int lender_ac; | ||
1007 | int *borrowed; | ||
1008 | int *fifo_credit; | ||
1009 | |||
1000 | if (!credits) | 1010 | if (!credits) |
1001 | return; | 1011 | return; |
1002 | 1012 | ||
1013 | if ((fifo == BRCMF_FWS_FIFO_AC_BE) && | ||
1014 | (fws->credits_borrowed[0])) { | ||
1015 | for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0; | ||
1016 | lender_ac--) { | ||
1017 | borrowed = &fws->credits_borrowed[lender_ac]; | ||
1018 | if (*borrowed) { | ||
1019 | fws->fifo_credit_map |= (1 << lender_ac); | ||
1020 | fifo_credit = &fws->fifo_credit[lender_ac]; | ||
1021 | if (*borrowed >= credits) { | ||
1022 | *borrowed -= credits; | ||
1023 | *fifo_credit += credits; | ||
1024 | return; | ||
1025 | } else { | ||
1026 | credits -= *borrowed; | ||
1027 | *fifo_credit += *borrowed; | ||
1028 | *borrowed = 0; | ||
1029 | } | ||
1030 | } | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1003 | fws->fifo_credit_map |= 1 << fifo; | 1034 | fws->fifo_credit_map |= 1 << fifo; |
1004 | fws->fifo_credit[fifo] += credits; | 1035 | fws->fifo_credit[fifo] += credits; |
1005 | } | 1036 | } |
@@ -1658,6 +1689,26 @@ fail: | |||
1658 | fws->stats.rollback_success++; | 1689 | fws->stats.rollback_success++; |
1659 | } | 1690 | } |
1660 | 1691 | ||
1692 | static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) | ||
1693 | { | ||
1694 | int lender_ac; | ||
1695 | |||
1696 | if (time_after(fws->borrow_defer_timestamp, jiffies)) | ||
1697 | return -ENAVAIL; | ||
1698 | |||
1699 | for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) { | ||
1700 | if (fws->fifo_credit[lender_ac]) { | ||
1701 | fws->credits_borrowed[lender_ac]++; | ||
1702 | fws->fifo_credit[lender_ac]--; | ||
1703 | if (fws->fifo_credit[lender_ac] == 0) | ||
1704 | fws->fifo_credit_map &= ~(1 << lender_ac); | ||
1705 | brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac); | ||
1706 | return 0; | ||
1707 | } | ||
1708 | } | ||
1709 | return -ENAVAIL; | ||
1710 | } | ||
1711 | |||
1661 | static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, | 1712 | static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, |
1662 | struct sk_buff *skb) | 1713 | struct sk_buff *skb) |
1663 | { | 1714 | { |
@@ -1691,8 +1742,17 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, | |||
1691 | return 0; | 1742 | return 0; |
1692 | } | 1743 | } |
1693 | 1744 | ||
1745 | if (fifo != BRCMF_FWS_FIFO_AC_BE) | ||
1746 | fws->borrow_defer_timestamp = jiffies + | ||
1747 | BRCMF_FWS_BORROW_DEFER_PERIOD; | ||
1748 | |||
1694 | if (!(*credit)) { | 1749 | if (!(*credit)) { |
1695 | brcmf_dbg(TRACE, "exit: credits depleted\n"); | 1750 | /* Try to borrow a credit from other queue */ |
1751 | if (fifo == BRCMF_FWS_FIFO_AC_BE && | ||
1752 | brcmf_fws_borrow_credit(fws) == 0) | ||
1753 | return 0; | ||
1754 | |||
1755 | brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo); | ||
1696 | return -ENAVAIL; | 1756 | return -ENAVAIL; |
1697 | } | 1757 | } |
1698 | (*credit)--; | 1758 | (*credit)--; |
@@ -1741,6 +1801,7 @@ rollback: | |||
1741 | int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) | 1801 | int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) |
1742 | { | 1802 | { |
1743 | struct brcmf_pub *drvr = ifp->drvr; | 1803 | struct brcmf_pub *drvr = ifp->drvr; |
1804 | struct brcmf_fws_info *fws = drvr->fws; | ||
1744 | struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); | 1805 | struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); |
1745 | struct ethhdr *eh = (struct ethhdr *)(skb->data); | 1806 | struct ethhdr *eh = (struct ethhdr *)(skb->data); |
1746 | ulong flags; | 1807 | ulong flags; |
@@ -1755,7 +1816,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) | |||
1755 | if (ntohs(eh->h_proto) == ETH_P_PAE) | 1816 | if (ntohs(eh->h_proto) == ETH_P_PAE) |
1756 | atomic_inc(&ifp->pend_8021x_cnt); | 1817 | atomic_inc(&ifp->pend_8021x_cnt); |
1757 | 1818 | ||
1758 | if (!brcmf_fws_fc_active(drvr->fws)) { | 1819 | if (!brcmf_fws_fc_active(fws)) { |
1759 | /* If the protocol uses a data header, apply it */ | 1820 | /* If the protocol uses a data header, apply it */ |
1760 | brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb); | 1821 | brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb); |
1761 | 1822 | ||
@@ -1765,7 +1826,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) | |||
1765 | 1826 | ||
1766 | /* set control buffer information */ | 1827 | /* set control buffer information */ |
1767 | skcb->if_flags = 0; | 1828 | skcb->if_flags = 0; |
1768 | skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifp, eh->h_dest); | 1829 | skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest); |
1769 | skcb->state = BRCMF_FWS_SKBSTATE_NEW; | 1830 | skcb->state = BRCMF_FWS_SKBSTATE_NEW; |
1770 | brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); | 1831 | brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); |
1771 | if (!multicast) | 1832 | if (!multicast) |
@@ -1777,15 +1838,17 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) | |||
1777 | 1838 | ||
1778 | brcmf_fws_lock(drvr, flags); | 1839 | brcmf_fws_lock(drvr, flags); |
1779 | if (skcb->mac->suppressed || | 1840 | if (skcb->mac->suppressed || |
1780 | brcmf_fws_mac_desc_closed(drvr->fws, skcb->mac, fifo) || | 1841 | brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) || |
1781 | brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) || | 1842 | brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) || |
1782 | (!multicast && | 1843 | (!multicast && |
1783 | brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) { | 1844 | brcmf_fws_consume_credit(fws, fifo, skb) < 0)) { |
1784 | /* enqueue the packet in delayQ */ | 1845 | /* enqueue the packet in delayQ */ |
1785 | drvr->fws->fifo_delay_map |= 1 << fifo; | 1846 | drvr->fws->fifo_delay_map |= 1 << fifo; |
1786 | brcmf_fws_enq(drvr->fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); | 1847 | brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); |
1787 | } else { | 1848 | } else { |
1788 | brcmf_fws_commit_skb(drvr->fws, fifo, skb); | 1849 | if (brcmf_fws_commit_skb(fws, fifo, skb)) |
1850 | if (!multicast) | ||
1851 | brcmf_skb_pick_up_credit(fws, fifo, skb); | ||
1789 | } | 1852 | } |
1790 | brcmf_fws_unlock(drvr, flags); | 1853 | brcmf_fws_unlock(drvr, flags); |
1791 | return 0; | 1854 | return 0; |
@@ -1858,7 +1921,23 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) | |||
1858 | BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) | 1921 | BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) |
1859 | credit++; | 1922 | credit++; |
1860 | } | 1923 | } |
1861 | fws->fifo_credit[fifo] -= credit; | 1924 | if ((fifo == BRCMF_FWS_FIFO_AC_BE) && |
1925 | (credit == fws->fifo_credit[fifo])) { | ||
1926 | fws->fifo_credit[fifo] -= credit; | ||
1927 | while (brcmf_fws_borrow_credit(fws) == 0) { | ||
1928 | skb = brcmf_fws_deq(fws, fifo); | ||
1929 | if (!skb) { | ||
1930 | brcmf_fws_return_credits(fws, fifo, 1); | ||
1931 | break; | ||
1932 | } | ||
1933 | if (brcmf_fws_commit_skb(fws, fifo, skb)) { | ||
1934 | brcmf_fws_return_credits(fws, fifo, 1); | ||
1935 | break; | ||
1936 | } | ||
1937 | } | ||
1938 | } else { | ||
1939 | fws->fifo_credit[fifo] -= credit; | ||
1940 | } | ||
1862 | } | 1941 | } |
1863 | brcmf_fws_unlock(fws->drvr, flags); | 1942 | brcmf_fws_unlock(fws->drvr, flags); |
1864 | } | 1943 | } |