brcmfmac: Take bus flowcontrol at credit mgmt into account.
On bus flow control (no more host bus resources to send packets to device) the netif flow control was toggled, however credit management should also take this status into account. Since there are multiple sources handling this flow control necessary spinlocks were added to protect flow control related data/states. Reviewed-by: Arend Van Spriel <arend@broadcom.com> Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
51f6dd9da2
commit
df50f75696
|
@ -583,6 +583,7 @@ enum brcmf_netif_stop_reason {
|
||||||
* @bssidx: index of bss associated with this interface.
|
* @bssidx: index of bss associated with this interface.
|
||||||
* @mac_addr: assigned mac address.
|
* @mac_addr: assigned mac address.
|
||||||
* @netif_stop: bitmap indicates reason why netif queues are stopped.
|
* @netif_stop: bitmap indicates reason why netif queues are stopped.
|
||||||
|
* @netif_stop_lock: spinlock for update netif_stop from multiple sources.
|
||||||
* @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
|
* @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
|
||||||
* @pend_8021x_wait: used for signalling change in count.
|
* @pend_8021x_wait: used for signalling change in count.
|
||||||
*/
|
*/
|
||||||
|
@ -598,6 +599,7 @@ struct brcmf_if {
|
||||||
s32 bssidx;
|
s32 bssidx;
|
||||||
u8 mac_addr[ETH_ALEN];
|
u8 mac_addr[ETH_ALEN];
|
||||||
u8 netif_stop;
|
u8 netif_stop;
|
||||||
|
spinlock_t netif_stop_lock;
|
||||||
atomic_t pend_8021x_cnt;
|
atomic_t pend_8021x_cnt;
|
||||||
wait_queue_head_t pend_8021x_wait;
|
wait_queue_head_t pend_8021x_wait;
|
||||||
};
|
};
|
||||||
|
|
|
@ -240,11 +240,15 @@ done:
|
||||||
void brcmf_txflowblock_if(struct brcmf_if *ifp,
|
void brcmf_txflowblock_if(struct brcmf_if *ifp,
|
||||||
enum brcmf_netif_stop_reason reason, bool state)
|
enum brcmf_netif_stop_reason reason, bool state)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (!ifp)
|
if (!ifp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
|
brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
|
||||||
ifp->bssidx, ifp->netif_stop, reason, state);
|
ifp->bssidx, ifp->netif_stop, reason, state);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ifp->netif_stop_lock, flags);
|
||||||
if (state) {
|
if (state) {
|
||||||
if (!ifp->netif_stop)
|
if (!ifp->netif_stop)
|
||||||
netif_stop_queue(ifp->ndev);
|
netif_stop_queue(ifp->ndev);
|
||||||
|
@ -254,6 +258,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp,
|
||||||
if (!ifp->netif_stop)
|
if (!ifp->netif_stop)
|
||||||
netif_wake_queue(ifp->ndev);
|
netif_wake_queue(ifp->ndev);
|
||||||
}
|
}
|
||||||
|
spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void brcmf_txflowblock(struct device *dev, bool state)
|
void brcmf_txflowblock(struct device *dev, bool state)
|
||||||
|
@ -264,6 +269,8 @@ void brcmf_txflowblock(struct device *dev, bool state)
|
||||||
|
|
||||||
brcmf_dbg(TRACE, "Enter\n");
|
brcmf_dbg(TRACE, "Enter\n");
|
||||||
|
|
||||||
|
brcmf_fws_bus_blocked(drvr, state);
|
||||||
|
|
||||||
for (i = 0; i < BRCMF_MAX_IFS; i++)
|
for (i = 0; i < BRCMF_MAX_IFS; i++)
|
||||||
brcmf_txflowblock_if(drvr->iflist[i],
|
brcmf_txflowblock_if(drvr->iflist[i],
|
||||||
BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state);
|
BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state);
|
||||||
|
@ -779,6 +786,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
|
||||||
ifp->bssidx = bssidx;
|
ifp->bssidx = bssidx;
|
||||||
|
|
||||||
init_waitqueue_head(&ifp->pend_8021x_wait);
|
init_waitqueue_head(&ifp->pend_8021x_wait);
|
||||||
|
spin_lock_init(&ifp->netif_stop_lock);
|
||||||
|
|
||||||
if (mac_addr != NULL)
|
if (mac_addr != NULL)
|
||||||
memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
|
memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
|
||||||
|
|
|
@ -2369,12 +2369,12 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
|
||||||
} else {
|
} else {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&bus->txqlock);
|
|
||||||
|
|
||||||
if (pktq_len(&bus->txq) >= TXHI) {
|
if (pktq_len(&bus->txq) >= TXHI) {
|
||||||
bus->txoff = true;
|
bus->txoff = true;
|
||||||
brcmf_txflowblock(bus->sdiodev->dev, true);
|
brcmf_txflowblock(bus->sdiodev->dev, true);
|
||||||
}
|
}
|
||||||
|
spin_unlock_bh(&bus->txqlock);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (pktq_plen(&bus->txq, prec) > qcount[prec])
|
if (pktq_plen(&bus->txq, prec) > qcount[prec])
|
||||||
|
|
|
@ -431,6 +431,7 @@ struct brcmf_fws_info {
|
||||||
u32 fifo_credit_map;
|
u32 fifo_credit_map;
|
||||||
u32 fifo_delay_map;
|
u32 fifo_delay_map;
|
||||||
unsigned long borrow_defer_timestamp;
|
unsigned long borrow_defer_timestamp;
|
||||||
|
bool bus_flow_blocked;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1833,6 +1834,7 @@ 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 ||
|
||||||
|
fws->bus_flow_blocked ||
|
||||||
brcmf_fws_mac_desc_closed(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 &&
|
||||||
|
@ -1905,7 +1907,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
|
||||||
|
|
||||||
brcmf_dbg(TRACE, "enter: fws=%p\n", fws);
|
brcmf_dbg(TRACE, "enter: fws=%p\n", fws);
|
||||||
brcmf_fws_lock(fws->drvr, flags);
|
brcmf_fws_lock(fws->drvr, flags);
|
||||||
for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) {
|
for (fifo = NL80211_NUM_ACS; fifo >= 0 && !fws->bus_flow_blocked;
|
||||||
|
fifo--) {
|
||||||
brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo,
|
brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo,
|
||||||
fws->fifo_credit[fifo]);
|
fws->fifo_credit[fifo]);
|
||||||
for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
|
for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
|
||||||
|
@ -1915,9 +1918,12 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
|
||||||
if (brcmf_skbcb(skb)->if_flags &
|
if (brcmf_skbcb(skb)->if_flags &
|
||||||
BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
|
BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
|
||||||
credit++;
|
credit++;
|
||||||
|
if (fws->bus_flow_blocked)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
|
if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
|
||||||
(credit == fws->fifo_credit[fifo])) {
|
(credit == fws->fifo_credit[fifo]) &&
|
||||||
|
(!fws->bus_flow_blocked)) {
|
||||||
fws->fifo_credit[fifo] -= credit;
|
fws->fifo_credit[fifo] -= credit;
|
||||||
while (brcmf_fws_borrow_credit(fws) == 0) {
|
while (brcmf_fws_borrow_credit(fws) == 0) {
|
||||||
skb = brcmf_fws_deq(fws, fifo);
|
skb = brcmf_fws_deq(fws, fifo);
|
||||||
|
@ -1929,6 +1935,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
|
||||||
brcmf_fws_return_credits(fws, fifo, 1);
|
brcmf_fws_return_credits(fws, fifo, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (fws->bus_flow_blocked)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fws->fifo_credit[fifo] -= credit;
|
fws->fifo_credit[fifo] -= credit;
|
||||||
|
@ -2060,3 +2068,12 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
|
||||||
}
|
}
|
||||||
brcmf_fws_unlock(fws->drvr, flags);
|
brcmf_fws_unlock(fws->drvr, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
|
||||||
|
{
|
||||||
|
struct brcmf_fws_info *fws = drvr->fws;
|
||||||
|
|
||||||
|
fws->bus_flow_blocked = flow_blocked;
|
||||||
|
if (!flow_blocked)
|
||||||
|
brcmf_fws_schedule_deq(fws);
|
||||||
|
}
|
||||||
|
|
|
@ -29,5 +29,6 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp);
|
||||||
void brcmf_fws_add_interface(struct brcmf_if *ifp);
|
void brcmf_fws_add_interface(struct brcmf_if *ifp);
|
||||||
void brcmf_fws_del_interface(struct brcmf_if *ifp);
|
void brcmf_fws_del_interface(struct brcmf_if *ifp);
|
||||||
void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
|
void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
|
||||||
|
void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
|
||||||
|
|
||||||
#endif /* FWSIGNAL_H_ */
|
#endif /* FWSIGNAL_H_ */
|
||||||
|
|
|
@ -82,6 +82,7 @@ struct brcmf_usbdev_info {
|
||||||
int tx_high_watermark;
|
int tx_high_watermark;
|
||||||
int tx_freecount;
|
int tx_freecount;
|
||||||
bool tx_flowblock;
|
bool tx_flowblock;
|
||||||
|
spinlock_t tx_flowblock_lock;
|
||||||
|
|
||||||
struct brcmf_usbreq *tx_reqs;
|
struct brcmf_usbreq *tx_reqs;
|
||||||
struct brcmf_usbreq *rx_reqs;
|
struct brcmf_usbreq *rx_reqs;
|
||||||
|
@ -411,6 +412,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
|
struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
|
||||||
struct brcmf_usbdev_info *devinfo = req->devinfo;
|
struct brcmf_usbdev_info *devinfo = req->devinfo;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status,
|
brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status,
|
||||||
req->skb);
|
req->skb);
|
||||||
|
@ -419,11 +421,13 @@ static void brcmf_usb_tx_complete(struct urb *urb)
|
||||||
brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0);
|
brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0);
|
||||||
req->skb = NULL;
|
req->skb = NULL;
|
||||||
brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
|
brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
|
||||||
|
spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
|
||||||
if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
|
if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
|
||||||
devinfo->tx_flowblock) {
|
devinfo->tx_flowblock) {
|
||||||
brcmf_txflowblock(devinfo->dev, false);
|
brcmf_txflowblock(devinfo->dev, false);
|
||||||
devinfo->tx_flowblock = false;
|
devinfo->tx_flowblock = false;
|
||||||
}
|
}
|
||||||
|
spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void brcmf_usb_rx_complete(struct urb *urb)
|
static void brcmf_usb_rx_complete(struct urb *urb)
|
||||||
|
@ -568,6 +572,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
|
||||||
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
|
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
|
||||||
struct brcmf_usbreq *req;
|
struct brcmf_usbreq *req;
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
brcmf_dbg(USB, "Enter, skb=%p\n", skb);
|
brcmf_dbg(USB, "Enter, skb=%p\n", skb);
|
||||||
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
|
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
|
||||||
|
@ -599,11 +604,13 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
|
||||||
if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
|
if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
|
||||||
!devinfo->tx_flowblock) {
|
!devinfo->tx_flowblock) {
|
||||||
brcmf_txflowblock(dev, true);
|
brcmf_txflowblock(dev, true);
|
||||||
devinfo->tx_flowblock = true;
|
devinfo->tx_flowblock = true;
|
||||||
}
|
}
|
||||||
|
spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -1164,6 +1171,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
|
||||||
|
|
||||||
/* Initialize the spinlocks */
|
/* Initialize the spinlocks */
|
||||||
spin_lock_init(&devinfo->qlock);
|
spin_lock_init(&devinfo->qlock);
|
||||||
|
spin_lock_init(&devinfo->tx_flowblock_lock);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&devinfo->rx_freeq);
|
INIT_LIST_HEAD(&devinfo->rx_freeq);
|
||||||
INIT_LIST_HEAD(&devinfo->rx_postq);
|
INIT_LIST_HEAD(&devinfo->rx_postq);
|
||||||
|
|
Loading…
Reference in New Issue