|
@@ -21,6 +21,7 @@
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/etherdevice.h>
|
|
|
#include <linux/err.h>
|
|
|
+#include <linux/jiffies.h>
|
|
|
#include <uapi/linux/nl80211.h>
|
|
|
#include <net/cfg80211.h>
|
|
|
|
|
@@ -271,6 +272,9 @@ struct brcmf_skbuff_cb {
|
|
|
brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
|
|
|
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.
|
|
|
*
|
|
@@ -425,9 +429,11 @@ struct brcmf_fws_info {
|
|
|
struct work_struct fws_dequeue_work;
|
|
|
u32 fifo_enqpkt[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];
|
|
|
u32 fifo_credit_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,
|
|
|
static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
|
|
|
u8 fifo, u8 credits)
|
|
|
{
|
|
|
+ int lender_ac;
|
|
|
+ int *borrowed;
|
|
|
+ int *fifo_credit;
|
|
|
+
|
|
|
if (!credits)
|
|
|
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[fifo] += credits;
|
|
|
}
|
|
@@ -1658,6 +1689,26 @@ fail:
|
|
|
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,
|
|
|
struct sk_buff *skb)
|
|
|
{
|
|
@@ -1691,8 +1742,17 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ if (fifo != BRCMF_FWS_FIFO_AC_BE)
|
|
|
+ fws->borrow_defer_timestamp = jiffies +
|
|
|
+ BRCMF_FWS_BORROW_DEFER_PERIOD;
|
|
|
+
|
|
|
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;
|
|
|
}
|
|
|
(*credit)--;
|
|
@@ -1741,6 +1801,7 @@ rollback:
|
|
|
int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
|
|
|
{
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
|
|
+ struct brcmf_fws_info *fws = drvr->fws;
|
|
|
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
|
|
|
struct ethhdr *eh = (struct ethhdr *)(skb->data);
|
|
|
ulong flags;
|
|
@@ -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)
|
|
|
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 */
|
|
|
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)
|
|
|
|
|
|
/* set control buffer information */
|
|
|
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;
|
|
|
brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
|
|
|
if (!multicast)
|
|
@@ -1777,15 +1838,17 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
|
|
|
|
|
|
brcmf_fws_lock(drvr, flags);
|
|
|
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)) ||
|
|
|
(!multicast &&
|
|
|
- brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) {
|
|
|
+ brcmf_fws_consume_credit(fws, fifo, skb) < 0)) {
|
|
|
/* enqueue the packet in delayQ */
|
|
|
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 {
|
|
|
- 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);
|
|
|
return 0;
|
|
@@ -1858,7 +1921,23 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
|
|
|
BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
|
|
|
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);
|
|
|
}
|