Commit 270a6c1f authored by Arend Van Spriel's avatar Arend Van Spriel Committed by Kalle Valo

brcmfmac: rework headroom check in .start_xmit()

Since commit 9cc4b7cb ("brcmfmac: Make skb header writable
before use") the headroom usage has been fixed. However, the
driver was keeping statistics that got lost. So reworking the
code so we get those driver statistics back for debugging.

Cc: James Hughes <james.hughes@raspberrypi.org>
Reviewed-by: default avatarHante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: default avatarFranky Lin <franky.lin@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent a833f3d4
...@@ -112,6 +112,17 @@ struct brcmf_bus_msgbuf { ...@@ -112,6 +112,17 @@ struct brcmf_bus_msgbuf {
}; };
/**
* struct brcmf_bus_stats - bus statistic counters.
*
* @pktcowed: packets cowed for extra headroom/unorphan.
* @pktcow_failed: packets dropped due to failed cow-ing.
*/
struct brcmf_bus_stats {
atomic_t pktcowed;
atomic_t pktcow_failed;
};
/** /**
* struct brcmf_bus - interface structure between common and bus layer * struct brcmf_bus - interface structure between common and bus layer
* *
...@@ -120,8 +131,8 @@ struct brcmf_bus_msgbuf { ...@@ -120,8 +131,8 @@ struct brcmf_bus_msgbuf {
* @dev: device pointer of bus device. * @dev: device pointer of bus device.
* @drvr: public driver information. * @drvr: public driver information.
* @state: operational state of the bus interface. * @state: operational state of the bus interface.
* @stats: statistics shared between common and bus layer.
* @maxctl: maximum size for rxctl request message. * @maxctl: maximum size for rxctl request message.
* @tx_realloc: number of tx packets realloced for headroom.
* @chip: device identifier of the dongle chip. * @chip: device identifier of the dongle chip.
* @always_use_fws_queue: bus wants use queue also when fwsignal is inactive. * @always_use_fws_queue: bus wants use queue also when fwsignal is inactive.
* @wowl_supported: is wowl supported by bus driver. * @wowl_supported: is wowl supported by bus driver.
...@@ -137,8 +148,8 @@ struct brcmf_bus { ...@@ -137,8 +148,8 @@ struct brcmf_bus {
struct device *dev; struct device *dev;
struct brcmf_pub *drvr; struct brcmf_pub *drvr;
enum brcmf_bus_state state; enum brcmf_bus_state state;
struct brcmf_bus_stats stats;
uint maxctl; uint maxctl;
atomic_t tx_realloc;
u32 chip; u32 chip;
u32 chiprev; u32 chiprev;
bool always_use_fws_queue; bool always_use_fws_queue;
......
...@@ -199,6 +199,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, ...@@ -199,6 +199,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr; struct brcmf_pub *drvr = ifp->drvr;
struct ethhdr *eh; struct ethhdr *eh;
int head_delta;
brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
...@@ -211,13 +212,21 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, ...@@ -211,13 +212,21 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
goto done; goto done;
} }
/* Make sure there's enough writable headroom*/ /* Make sure there's enough writeable headroom */
ret = skb_cow_head(skb, drvr->hdrlen); if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) {
if (ret < 0) { head_delta = drvr->hdrlen - skb_headroom(skb);
brcmf_err("%s: skb_cow_head failed\n",
brcmf_ifname(ifp)); brcmf_dbg(INFO, "%s: insufficient headroom (%d)\n",
dev_kfree_skb(skb); brcmf_ifname(ifp), head_delta);
goto done; atomic_inc(&drvr->bus_if->stats.pktcowed);
ret = pskb_expand_head(skb, ALIGN(head_delta, NET_SKB_PAD), 0,
GFP_ATOMIC);
if (ret < 0) {
brcmf_err("%s: failed to expand headroom\n",
brcmf_ifname(ifp));
atomic_inc(&drvr->bus_if->stats.pktcow_failed);
goto done;
}
} }
/* validate length for ether packet */ /* validate length for ether packet */
......
...@@ -2037,6 +2037,7 @@ brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) ...@@ -2037,6 +2037,7 @@ brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
{ {
struct brcmf_bus_stats *stats;
u16 head_pad; u16 head_pad;
u8 *dat_buf; u8 *dat_buf;
...@@ -2046,16 +2047,18 @@ static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) ...@@ -2046,16 +2047,18 @@ static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
head_pad = ((unsigned long)dat_buf % bus->head_align); head_pad = ((unsigned long)dat_buf % bus->head_align);
if (head_pad) { if (head_pad) {
if (skb_headroom(pkt) < head_pad) { if (skb_headroom(pkt) < head_pad) {
atomic_inc(&bus->sdiodev->bus_if->tx_realloc); stats = &bus->sdiodev->bus_if->stats;
head_pad = 0; atomic_inc(&stats->pktcowed);
if (skb_cow(pkt, head_pad)) if (skb_cow_head(pkt, head_pad)) {
atomic_inc(&stats->pktcow_failed);
return -ENOMEM; return -ENOMEM;
}
} }
skb_push(pkt, head_pad); skb_push(pkt, head_pad);
dat_buf = (u8 *)(pkt->data); dat_buf = (u8 *)(pkt->data);
memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
} }
return head_pad; memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
return 0;
} }
/** /**
......
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