Commit 1aac1e91 authored by Arend van Spriel's avatar Arend van Spriel Committed by John W. Linville

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: default avatarHante Meuleman <meuleman@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a786b38d
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/jiffies.h>
#include <uapi/linux/nl80211.h> #include <uapi/linux/nl80211.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
...@@ -271,6 +272,9 @@ struct brcmf_skbuff_cb { ...@@ -271,6 +272,9 @@ struct brcmf_skbuff_cb {
brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
/* How long to defer borrowing in jiffies */
#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
/** /**
* enum brcmf_fws_fifo - fifo indices used by dongle firmware. * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
* *
...@@ -425,9 +429,11 @@ struct brcmf_fws_info { ...@@ -425,9 +429,11 @@ struct brcmf_fws_info {
struct work_struct fws_dequeue_work; struct work_struct fws_dequeue_work;
u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
int fifo_credit[BRCMF_FWS_FIFO_COUNT]; int fifo_credit[BRCMF_FWS_FIFO_COUNT];
int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
u32 fifo_credit_map; u32 fifo_credit_map;
u32 fifo_delay_map; u32 fifo_delay_map;
unsigned long borrow_defer_timestamp;
}; };
/* /*
...@@ -997,9 +1003,34 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, ...@@ -997,9 +1003,34 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
u8 fifo, u8 credits) u8 fifo, u8 credits)
{ {
int lender_ac;
int *borrowed;
int *fifo_credit;
if (!credits) if (!credits)
return; return;
if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
(fws->credits_borrowed[0])) {
for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
lender_ac--) {
borrowed = &fws->credits_borrowed[lender_ac];
if (*borrowed) {
fws->fifo_credit_map |= (1 << lender_ac);
fifo_credit = &fws->fifo_credit[lender_ac];
if (*borrowed >= credits) {
*borrowed -= credits;
*fifo_credit += credits;
return;
} else {
credits -= *borrowed;
*fifo_credit += *borrowed;
*borrowed = 0;
}
}
}
}
fws->fifo_credit_map |= 1 << fifo; fws->fifo_credit_map |= 1 << fifo;
fws->fifo_credit[fifo] += credits; fws->fifo_credit[fifo] += credits;
} }
...@@ -1658,6 +1689,26 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb) ...@@ -1658,6 +1689,26 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
fws->stats.rollback_success++; fws->stats.rollback_success++;
} }
static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
{
int lender_ac;
if (time_after(fws->borrow_defer_timestamp, jiffies))
return -ENAVAIL;
for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
if (fws->fifo_credit[lender_ac]) {
fws->credits_borrowed[lender_ac]++;
fws->fifo_credit[lender_ac]--;
if (fws->fifo_credit[lender_ac] == 0)
fws->fifo_credit_map &= ~(1 << lender_ac);
brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac);
return 0;
}
}
return -ENAVAIL;
}
static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -1691,8 +1742,17 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, ...@@ -1691,8 +1742,17 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
return 0; return 0;
} }
if (fifo != BRCMF_FWS_FIFO_AC_BE)
fws->borrow_defer_timestamp = jiffies +
BRCMF_FWS_BORROW_DEFER_PERIOD;
if (!(*credit)) { if (!(*credit)) {
brcmf_dbg(TRACE, "exit: credits depleted\n"); /* Try to borrow a credit from other queue */
if (fifo == BRCMF_FWS_FIFO_AC_BE &&
brcmf_fws_borrow_credit(fws) == 0)
return 0;
brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo);
return -ENAVAIL; return -ENAVAIL;
} }
(*credit)--; (*credit)--;
...@@ -1741,6 +1801,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, ...@@ -1741,6 +1801,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
{ {
struct brcmf_pub *drvr = ifp->drvr; struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_fws_info *fws = drvr->fws;
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
struct ethhdr *eh = (struct ethhdr *)(skb->data); struct ethhdr *eh = (struct ethhdr *)(skb->data);
ulong flags; ulong flags;
...@@ -1755,7 +1816,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) ...@@ -1755,7 +1816,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
if (ntohs(eh->h_proto) == ETH_P_PAE) if (ntohs(eh->h_proto) == ETH_P_PAE)
atomic_inc(&ifp->pend_8021x_cnt); atomic_inc(&ifp->pend_8021x_cnt);
if (!brcmf_fws_fc_active(drvr->fws)) { if (!brcmf_fws_fc_active(fws)) {
/* If the protocol uses a data header, apply it */ /* If the protocol uses a data header, apply it */
brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb); brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);
...@@ -1765,7 +1826,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) ...@@ -1765,7 +1826,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
/* set control buffer information */ /* set control buffer information */
skcb->if_flags = 0; skcb->if_flags = 0;
skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifp, eh->h_dest); skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest);
skcb->state = BRCMF_FWS_SKBSTATE_NEW; skcb->state = BRCMF_FWS_SKBSTATE_NEW;
brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
if (!multicast) if (!multicast)
...@@ -1777,15 +1838,17 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) ...@@ -1777,15 +1838,17 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
brcmf_fws_lock(drvr, flags); brcmf_fws_lock(drvr, flags);
if (skcb->mac->suppressed || if (skcb->mac->suppressed ||
brcmf_fws_mac_desc_closed(drvr->fws, skcb->mac, fifo) || brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) ||
brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) || brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) ||
(!multicast && (!multicast &&
brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) { brcmf_fws_consume_credit(fws, fifo, skb) < 0)) {
/* enqueue the packet in delayQ */ /* enqueue the packet in delayQ */
drvr->fws->fifo_delay_map |= 1 << fifo; drvr->fws->fifo_delay_map |= 1 << fifo;
brcmf_fws_enq(drvr->fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
} else { } else {
brcmf_fws_commit_skb(drvr->fws, fifo, skb); if (brcmf_fws_commit_skb(fws, fifo, skb))
if (!multicast)
brcmf_skb_pick_up_credit(fws, fifo, skb);
} }
brcmf_fws_unlock(drvr, flags); brcmf_fws_unlock(drvr, flags);
return 0; return 0;
...@@ -1858,7 +1921,23 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ...@@ -1858,7 +1921,23 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
credit++; credit++;
} }
fws->fifo_credit[fifo] -= credit; if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
(credit == fws->fifo_credit[fifo])) {
fws->fifo_credit[fifo] -= credit;
while (brcmf_fws_borrow_credit(fws) == 0) {
skb = brcmf_fws_deq(fws, fifo);
if (!skb) {
brcmf_fws_return_credits(fws, fifo, 1);
break;
}
if (brcmf_fws_commit_skb(fws, fifo, skb)) {
brcmf_fws_return_credits(fws, fifo, 1);
break;
}
}
} else {
fws->fifo_credit[fifo] -= credit;
}
} }
brcmf_fws_unlock(fws->drvr, flags); brcmf_fws_unlock(fws->drvr, flags);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment