From: Rafał Miłecki Date: Sun, 7 Feb 2016 23:12:38 +0000 (+0000) Subject: mac80211: fix flowrings infinite loops in brcmfmac on BCM4366 X-Git-Tag: reboot~492 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=6ef21fb874fc46603ae625904e43b7807dfced64;p=openwrt%2Fopenwrt.git mac80211: fix flowrings infinite loops in brcmfmac on BCM4366 Signed-off-by: Rafał Miłecki SVN-Revision: 48653 --- diff --git a/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch b/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch new file mode 100644 index 0000000000..1fd016f7f6 --- /dev/null +++ b/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch @@ -0,0 +1,307 @@ +From: Hante Meuleman +Date: Sun, 7 Feb 2016 18:08:24 +0100 +Subject: [PATCH] brcmfmac: Increase nr of supported flowrings. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +New generation devices have firmware which has more than 256 flowrings. +E.g. following debugging message comes from 14e4:4365 BCM4366: +[ 194.606245] brcmfmac: brcmf_pcie_init_ringbuffers Nr of flowrings is 264 + +At various code places (related to flowrings) we were using u8 which +could lead to storing wrong number or infinite loops when indexing with +this type. This issue was quite easy to spot in brcmf_flowring_detach +where it led to infinite loop e.g. on failed initialization. + +This patch switches code to proper types and increases the maximum +number of supported flowrings to 512. + +Originally this change was sent in September 2015, but back it was +causing a regression on BCM43602 resulting in: +Unable to handle kernel NULL pointer dereference at virtual address ... + +The reason for this regression was missing update (s/u8/u16) of struct +brcmf_flowring_ring. This problem was handled in 9f64df9 ("brcmfmac: Fix +bug in flowring management."). Starting with that it's safe to apply +this original patch as it doesn't cause a regression anymore. + +This patch fixes an infinite loop on BCM4366 which is supported since +4.4 so it makes sense to apply it to stable 4.4+. + +Cc: # 4.4+ +Reviewed-by: Arend Van Spriel +Reviewed-by: Franky (Zhenhui) Lin +Reviewed-by: Pieter-Paul Giesberts +Signed-off-by: Hante Meuleman +Signed-off-by: Arend van Spriel +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c +@@ -32,7 +32,7 @@ + #define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256) + #define BRCMF_FLOWRING_INVALID_IFIDX 0xff + +-#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) ++#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16) + #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) + + static const u8 brcmf_flowring_prio2fifo[] = { +@@ -68,7 +68,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f + u8 prio, u8 ifidx) + { + struct brcmf_flowring_hash *hash; +- u8 hash_idx; ++ u16 hash_idx; + u32 i; + bool found; + bool sta; +@@ -88,6 +88,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); ++ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { +@@ -98,6 +99,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f + break; + } + hash_idx++; ++ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1); + } + if (found) + return hash[hash_idx].flowid; +@@ -111,7 +113,7 @@ u32 brcmf_flowring_create(struct brcmf_f + { + struct brcmf_flowring_ring *ring; + struct brcmf_flowring_hash *hash; +- u8 hash_idx; ++ u16 hash_idx; + u32 i; + bool found; + u8 fifo; +@@ -131,6 +133,7 @@ u32 brcmf_flowring_create(struct brcmf_f + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); ++ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { +@@ -140,6 +143,7 @@ u32 brcmf_flowring_create(struct brcmf_f + break; + } + hash_idx++; ++ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1); + } + if (found) { + for (i = 0; i < flow->nrofrings; i++) { +@@ -169,7 +173,7 @@ u32 brcmf_flowring_create(struct brcmf_f + } + + +-u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) ++u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid) + { + struct brcmf_flowring_ring *ring; + +@@ -179,7 +183,7 @@ u8 brcmf_flowring_tid(struct brcmf_flowr + } + + +-static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, ++static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid, + bool blocked) + { + struct brcmf_flowring_ring *ring; +@@ -228,10 +232,10 @@ static void brcmf_flowring_block(struct + } + + +-void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) ++void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid) + { + struct brcmf_flowring_ring *ring; +- u8 hash_idx; ++ u16 hash_idx; + struct sk_buff *skb; + + ring = flow->rings[flowid]; +@@ -253,7 +257,7 @@ void brcmf_flowring_delete(struct brcmf_ + } + + +-u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, ++u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid, + struct sk_buff *skb) + { + struct brcmf_flowring_ring *ring; +@@ -279,7 +283,7 @@ u32 brcmf_flowring_enqueue(struct brcmf_ + } + + +-struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) ++struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid) + { + struct brcmf_flowring_ring *ring; + struct sk_buff *skb; +@@ -300,7 +304,7 @@ struct sk_buff *brcmf_flowring_dequeue(s + } + + +-void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, ++void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid, + struct sk_buff *skb) + { + struct brcmf_flowring_ring *ring; +@@ -311,7 +315,7 @@ void brcmf_flowring_reinsert(struct brcm + } + + +-u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid) ++u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid) + { + struct brcmf_flowring_ring *ring; + +@@ -326,7 +330,7 @@ u32 brcmf_flowring_qlen(struct brcmf_flo + } + + +-void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid) ++void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid) + { + struct brcmf_flowring_ring *ring; + +@@ -340,10 +344,10 @@ void brcmf_flowring_open(struct brcmf_fl + } + + +-u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid) ++u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid) + { + struct brcmf_flowring_ring *ring; +- u8 hash_idx; ++ u16 hash_idx; + + ring = flow->rings[flowid]; + hash_idx = ring->hash_id; +@@ -384,7 +388,7 @@ void brcmf_flowring_detach(struct brcmf_ + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_tdls_entry *search; + struct brcmf_flowring_tdls_entry *remove; +- u8 flowid; ++ u16 flowid; + + for (flowid = 0; flowid < flow->nrofrings; flowid++) { + if (flow->rings[flowid]) +@@ -408,7 +412,7 @@ void brcmf_flowring_configure_addr_mode( + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + u32 i; +- u8 flowid; ++ u16 flowid; + + if (flow->addr_mode[ifidx] != addr_mode) { + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) { +@@ -434,7 +438,7 @@ void brcmf_flowring_delete_peer(struct b + struct brcmf_flowring_tdls_entry *prev; + struct brcmf_flowring_tdls_entry *search; + u32 i; +- u8 flowid; ++ u16 flowid; + bool sta; + + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h +@@ -16,7 +16,7 @@ + #define BRCMFMAC_FLOWRING_H + + +-#define BRCMF_FLOWRING_HASHSIZE 256 ++#define BRCMF_FLOWRING_HASHSIZE 512 /* has to be 2^x */ + #define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF + + +@@ -24,7 +24,7 @@ struct brcmf_flowring_hash { + u8 mac[ETH_ALEN]; + u8 fifo; + u8 ifidx; +- u8 flowid; ++ u16 flowid; + }; + + enum ring_status { +@@ -61,16 +61,16 @@ u32 brcmf_flowring_lookup(struct brcmf_f + u8 prio, u8 ifidx); + u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +-void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); +-void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); +-u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); +-u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, ++void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid); ++void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid); ++u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid); ++u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid, + struct sk_buff *skb); +-struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); +-void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, ++struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid); ++void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid, + struct sk_buff *skb); +-u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid); +-u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid); ++u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid); ++u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid); + struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings); + void brcmf_flowring_detach(struct brcmf_flowring *flow); + void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +@@ -677,7 +677,7 @@ static u32 brcmf_msgbuf_flowring_create( + } + + +-static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) ++static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid) + { + struct brcmf_flowring *flow = msgbuf->flow; + struct brcmf_commonring *commonring; +@@ -1310,7 +1310,7 @@ int brcmf_proto_msgbuf_rx_trigger(struct + } + + +-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid) ++void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid) + { + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct msgbuf_tx_flowring_delete_req *delete; +@@ -1415,6 +1415,13 @@ int brcmf_proto_msgbuf_attach(struct brc + u32 count; + + if_msgbuf = drvr->bus_if->msgbuf; ++ ++ if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) { ++ brcmf_err("driver not configured for this many flowrings %d\n", ++ if_msgbuf->nrof_flowrings); ++ if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1; ++ } ++ + msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); + if (!msgbuf) + goto fail; +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h +@@ -33,7 +33,7 @@ + + + int brcmf_proto_msgbuf_rx_trigger(struct device *dev); +-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); ++void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid); + int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); + void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); + #else