diff options
author | Hante Meuleman <meuleman@broadcom.com> | 2014-07-30 07:20:06 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-07-31 13:45:28 -0400 |
commit | 17ca5c718414d605f0060336e071fb77359be790 (patch) | |
tree | 0a8d5b85e55c907516dd902aa0722bd92ce50f2d | |
parent | bd4f82e3b2899829c7f87dde0d20daeb780ad014 (diff) |
brcmfmac: Fix msgbuf flow control.
Msgbuf flow control was using a function to flow off and on which
was not supported without proptx enabled. Also flow control needs
to be handled per ifidx.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@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>
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 61 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/flowring.h | 3 |
3 files changed, 62 insertions, 8 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 1825f736fd45..5e4317dbc2b0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h | |||
@@ -121,12 +121,12 @@ struct brcmf_fws_mac_descriptor; | |||
121 | * | 121 | * |
122 | * @BRCMF_NETIF_STOP_REASON_FWS_FC: | 122 | * @BRCMF_NETIF_STOP_REASON_FWS_FC: |
123 | * netif stopped due to firmware signalling flow control. | 123 | * netif stopped due to firmware signalling flow control. |
124 | * @BRCMF_NETIF_STOP_REASON_BLOCK_BUS: | 124 | * @BRCMF_NETIF_STOP_REASON_FLOW: |
125 | * netif stopped due to bus blocking. | 125 | * netif stopped due to flowring full. |
126 | */ | 126 | */ |
127 | enum brcmf_netif_stop_reason { | 127 | enum brcmf_netif_stop_reason { |
128 | BRCMF_NETIF_STOP_REASON_FWS_FC = 1, | 128 | BRCMF_NETIF_STOP_REASON_FWS_FC = 1, |
129 | BRCMF_NETIF_STOP_REASON_BLOCK_BUS = 2 | 129 | BRCMF_NETIF_STOP_REASON_FLOW = 2 |
130 | }; | 130 | }; |
131 | 131 | ||
132 | /** | 132 | /** |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 26cbb7c4ec4c..07009046fda5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c | |||
@@ -158,6 +158,51 @@ u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) | |||
158 | } | 158 | } |
159 | 159 | ||
160 | 160 | ||
161 | static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, | ||
162 | bool blocked) | ||
163 | { | ||
164 | struct brcmf_flowring_ring *ring; | ||
165 | struct brcmf_bus *bus_if; | ||
166 | struct brcmf_pub *drvr; | ||
167 | struct brcmf_if *ifp; | ||
168 | bool currently_blocked; | ||
169 | int i; | ||
170 | u8 ifidx; | ||
171 | unsigned long flags; | ||
172 | |||
173 | spin_lock_irqsave(&flow->block_lock, flags); | ||
174 | |||
175 | ring = flow->rings[flowid]; | ||
176 | ifidx = brcmf_flowring_ifidx_get(flow, flowid); | ||
177 | |||
178 | currently_blocked = false; | ||
179 | for (i = 0; i < flow->nrofrings; i++) { | ||
180 | if (flow->rings[i]) { | ||
181 | ring = flow->rings[i]; | ||
182 | if ((ring->status == RING_OPEN) && | ||
183 | (brcmf_flowring_ifidx_get(flow, i) == ifidx)) { | ||
184 | if (ring->blocked) { | ||
185 | currently_blocked = true; | ||
186 | break; | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | ring->blocked = blocked; | ||
192 | if (currently_blocked == blocked) { | ||
193 | spin_unlock_irqrestore(&flow->block_lock, flags); | ||
194 | return; | ||
195 | } | ||
196 | |||
197 | bus_if = dev_get_drvdata(flow->dev); | ||
198 | drvr = bus_if->drvr; | ||
199 | ifp = drvr->iflist[ifidx]; | ||
200 | brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); | ||
201 | |||
202 | spin_unlock_irqrestore(&flow->block_lock, flags); | ||
203 | } | ||
204 | |||
205 | |||
161 | void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) | 206 | void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) |
162 | { | 207 | { |
163 | struct brcmf_flowring_ring *ring; | 208 | struct brcmf_flowring_ring *ring; |
@@ -167,6 +212,7 @@ void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) | |||
167 | ring = flow->rings[flowid]; | 212 | ring = flow->rings[flowid]; |
168 | if (!ring) | 213 | if (!ring) |
169 | return; | 214 | return; |
215 | brcmf_flowring_block(flow, flowid, false); | ||
170 | hash_idx = ring->hash_id; | 216 | hash_idx = ring->hash_id; |
171 | flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; | 217 | flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; |
172 | memset(flow->hash[hash_idx].mac, 0, ETH_ALEN); | 218 | memset(flow->hash[hash_idx].mac, 0, ETH_ALEN); |
@@ -193,9 +239,16 @@ void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, | |||
193 | 239 | ||
194 | if (!ring->blocked && | 240 | if (!ring->blocked && |
195 | (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { | 241 | (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { |
196 | brcmf_txflowblock(flow->dev, true); | 242 | brcmf_flowring_block(flow, flowid, true); |
197 | brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); | 243 | brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); |
198 | ring->blocked = 1; | 244 | /* To prevent (work around) possible race condition, check |
245 | * queue len again. It is also possible to use locking to | ||
246 | * protect, but that is undesirable for every enqueue and | ||
247 | * dequeue. This simple check will solve a possible race | ||
248 | * condition if it occurs. | ||
249 | */ | ||
250 | if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) | ||
251 | brcmf_flowring_block(flow, flowid, false); | ||
199 | } | 252 | } |
200 | } | 253 | } |
201 | 254 | ||
@@ -213,9 +266,8 @@ struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) | |||
213 | 266 | ||
214 | if (ring->blocked && | 267 | if (ring->blocked && |
215 | (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { | 268 | (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { |
216 | brcmf_txflowblock(flow->dev, false); | 269 | brcmf_flowring_block(flow, flowid, false); |
217 | brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); | 270 | brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); |
218 | ring->blocked = 0; | ||
219 | } | 271 | } |
220 | 272 | ||
221 | return skb; | 273 | return skb; |
@@ -283,6 +335,7 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) | |||
283 | if (flow) { | 335 | if (flow) { |
284 | flow->dev = dev; | 336 | flow->dev = dev; |
285 | flow->nrofrings = nrofrings; | 337 | flow->nrofrings = nrofrings; |
338 | spin_lock_init(&flow->block_lock); | ||
286 | for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) | 339 | for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) |
287 | flow->addr_mode[i] = ADDR_INDIRECT; | 340 | flow->addr_mode[i] = ADDR_INDIRECT; |
288 | for (i = 0; i < ARRAY_SIZE(flow->hash); i++) | 341 | for (i = 0; i < ARRAY_SIZE(flow->hash); i++) |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index 677f4b8065f6..cb9644ca6ece 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h | |||
@@ -35,7 +35,7 @@ enum ring_status { | |||
35 | 35 | ||
36 | struct brcmf_flowring_ring { | 36 | struct brcmf_flowring_ring { |
37 | u8 hash_id; | 37 | u8 hash_id; |
38 | u8 blocked; | 38 | bool blocked; |
39 | enum ring_status status; | 39 | enum ring_status status; |
40 | struct sk_buff_head skblist; | 40 | struct sk_buff_head skblist; |
41 | }; | 41 | }; |
@@ -44,6 +44,7 @@ struct brcmf_flowring { | |||
44 | struct device *dev; | 44 | struct device *dev; |
45 | struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; | 45 | struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; |
46 | struct brcmf_flowring_ring **rings; | 46 | struct brcmf_flowring_ring **rings; |
47 | spinlock_t block_lock; | ||
47 | enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; | 48 | enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; |
48 | u16 nrofrings; | 49 | u16 nrofrings; |
49 | }; | 50 | }; |