| 1 | |
| 2 | |
| 3 | * Queue between the tx operation and the bh workqueue.
|
| 4 | *
|
| 5 | * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
|
| 6 | * Copyright (c) 2010, ST-Ericsson
|
| 7 |
|
| 8 | #include <linux/sched.h>
|
| 9 | #include <net/mac80211.h>
|
| 10 |
|
| 11 | #include "queue.h"
|
| 12 | #include "wfx.h"
|
| 13 | #include "sta.h"
|
| 14 | #include "data_tx.h"
|
| 15 | #include "traces.h"
|
| 16 |
|
| 17 | void wfx_tx_lock(struct wfx_dev *wdev)
|
| 18 | {
|
| 19 | atomic_inc(&wdev->tx_lock);
|
| 20 | }
|
| 21 |
|
| 22 | void wfx_tx_unlock(struct wfx_dev *wdev)
|
| 23 | {
|
| 24 | int tx_lock = atomic_dec_return(&wdev->tx_lock);
|
| 25 |
|
| 26 | WARN(tx_lock < 0, "inconsistent tx_lock value");
|
| 27 | if (!tx_lock)
|
| 28 | wfx_bh_request_tx(wdev);
|
| 29 | }
|
| 30 |
|
| 31 | void wfx_tx_flush(struct wfx_dev *wdev)
|
| 32 | {
|
| 33 | int ret;
|
| 34 |
|
| 35 |
|
| 36 | if (wdev->chip_frozen)
|
| 37 | return;
|
| 38 |
|
| 39 | wfx_tx_lock(wdev);
|
| 40 | mutex_lock(&wdev->hif_cmd.lock);
|
| 41 | ret = wait_event_timeout(wdev->hif.tx_buffers_empty, !wdev->hif.tx_buffers_used,
|
| 42 | msecs_to_jiffies(3000));
|
| 43 | if (!ret) {
|
| 44 | dev_warn(wdev->dev, "cannot flush tx buffers (%d still busy)\n",
|
| 45 | wdev->hif.tx_buffers_used);
|
| 46 | wfx_pending_dump_old_frames(wdev, 3000);
|
| 47 |
|
| 48 | wdev->chip_frozen = true;
|
| 49 | }
|
| 50 | mutex_unlock(&wdev->hif_cmd.lock);
|
| 51 | wfx_tx_unlock(wdev);
|
| 52 | }
|
| 53 |
|
| 54 | void wfx_tx_lock_flush(struct wfx_dev *wdev)
|
| 55 | {
|
| 56 | wfx_tx_lock(wdev);
|
| 57 | wfx_tx_flush(wdev);
|
| 58 | }
|
| 59 |
|
| 60 | void wfx_tx_queues_init(struct wfx_vif *wvif)
|
| 61 | {
|
| 62 | |
| 63 | * ensure that it roughtly respect the priorities to avoid any shortage.
|
| 64 |
|
| 65 | const int priorities[IEEE80211_NUM_ACS] = { 1, 2, 64, 128 };
|
| 66 | int i;
|
| 67 |
|
| 68 | for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
|
| 69 | skb_queue_head_init(&wvif->tx_queue[i].normal);
|
| 70 | skb_queue_head_init(&wvif->tx_queue[i].cab);
|
| 71 | skb_queue_head_init(&wvif->tx_queue[i].offchan);
|
| 72 | wvif->tx_queue[i].priority = priorities[i];
|
| 73 | }
|
| 74 | }
|
| 75 |
|
| 76 | bool wfx_tx_queue_empty(struct wfx_vif *wvif, struct wfx_queue *queue)
|
| 77 | {
|
| 78 | return skb_queue_empty_lockless(&queue->normal) &&
|
| 79 | skb_queue_empty_lockless(&queue->cab) &&
|
| 80 | skb_queue_empty_lockless(&queue->offchan);
|
| 81 | }
|
| 82 |
|
| 83 | void wfx_tx_queues_check_empty(struct wfx_vif *wvif)
|
| 84 | {
|
| 85 | int i;
|
| 86 |
|
| 87 | for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
|
| 88 | WARN_ON(atomic_read(&wvif->tx_queue[i].pending_frames));
|
| 89 | WARN_ON(!wfx_tx_queue_empty(wvif, &wvif->tx_queue[i]));
|
| 90 | }
|
| 91 | }
|
| 92 |
|
| 93 | static void __wfx_tx_queue_drop(struct wfx_vif *wvif,
|
| 94 | struct sk_buff_head *skb_queue, struct sk_buff_head *dropped)
|
| 95 | {
|
| 96 | struct sk_buff *skb, *tmp;
|
| 97 |
|
| 98 | spin_lock_bh(&skb_queue->lock);
|
| 99 | skb_queue_walk_safe(skb_queue, skb, tmp) {
|
| 100 | __skb_unlink(skb, skb_queue);
|
| 101 | skb_queue_head(dropped, skb);
|
| 102 | }
|
| 103 | spin_unlock_bh(&skb_queue->lock);
|
| 104 | }
|
| 105 |
|
| 106 | void wfx_tx_queue_drop(struct wfx_vif *wvif, struct wfx_queue *queue,
|
| 107 | struct sk_buff_head *dropped)
|
| 108 | {
|
| 109 | __wfx_tx_queue_drop(wvif, &queue->normal, dropped);
|
| 110 | __wfx_tx_queue_drop(wvif, &queue->cab, dropped);
|
| 111 | __wfx_tx_queue_drop(wvif, &queue->offchan, dropped);
|
| 112 | wake_up(&wvif->wdev->tx_dequeue);
|
| 113 | }
|
| 114 |
|
| 115 | void wfx_tx_queues_put(struct wfx_vif *wvif, struct sk_buff *skb)
|
| 116 | {
|
| 117 | struct wfx_queue *queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
|
| 118 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
|
| 119 |
|
| 120 | if (tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
|
| 121 | skb_queue_tail(&queue->offchan, skb);
|
| 122 | else if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
|
| 123 | skb_queue_tail(&queue->cab, skb);
|
| 124 | else
|
| 125 | skb_queue_tail(&queue->normal, skb);
|
| 126 | }
|
| 127 |
|
| 128 | void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped)
|
| 129 | {
|
| 130 | struct wfx_queue *queue;
|
| 131 | struct wfx_vif *wvif;
|
| 132 | struct sk_buff *skb;
|
| 133 |
|
| 134 | WARN(!wdev->chip_frozen, "%s should only be used to recover a frozen device", __func__);
|
| 135 | while ((skb = skb_dequeue(&wdev->tx_pending)) != NULL) {
|
| 136 | wvif = wfx_skb_wvif(wdev, skb);
|
| 137 | if (wvif) {
|
| 138 | queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
|
| 139 | WARN_ON(skb_get_queue_mapping(skb) > 3);
|
| 140 | WARN_ON(!atomic_read(&queue->pending_frames));
|
| 141 | atomic_dec(&queue->pending_frames);
|
| 142 | }
|
| 143 | skb_queue_head(dropped, skb);
|
| 144 | }
|
| 145 | }
|
| 146 |
|
| 147 | struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id)
|
| 148 | {
|
| 149 | struct wfx_queue *queue;
|
| 150 | struct wfx_hif_req_tx *req;
|
| 151 | struct wfx_vif *wvif;
|
| 152 | struct wfx_hif_msg *hif;
|
| 153 | struct sk_buff *skb;
|
| 154 |
|
| 155 | spin_lock_bh(&wdev->tx_pending.lock);
|
| 156 | skb_queue_walk(&wdev->tx_pending, skb) {
|
| 157 | hif = (struct wfx_hif_msg *)skb->data;
|
| 158 | req = (struct wfx_hif_req_tx *)hif->body;
|
| 159 | if (req->packet_id != packet_id)
|
| 160 | continue;
|
| 161 | spin_unlock_bh(&wdev->tx_pending.lock);
|
| 162 | wvif = wfx_skb_wvif(wdev, skb);
|
| 163 | if (wvif) {
|
| 164 | queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
|
| 165 | WARN_ON(skb_get_queue_mapping(skb) > 3);
|
| 166 | WARN_ON(!atomic_read(&queue->pending_frames));
|
| 167 | atomic_dec(&queue->pending_frames);
|
| 168 | }
|
| 169 | skb_unlink(skb, &wdev->tx_pending);
|
| 170 | return skb;
|
| 171 | }
|
| 172 | spin_unlock_bh(&wdev->tx_pending.lock);
|
| 173 | WARN(1, "cannot find packet in pending queue");
|
| 174 | return NULL;
|
| 175 | }
|
| 176 |
|
| 177 | void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms)
|
| 178 | {
|
| 179 | ktime_t now = ktime_get();
|
| 180 | struct wfx_tx_priv *tx_priv;
|
| 181 | struct wfx_hif_req_tx *req;
|
| 182 | struct sk_buff *skb;
|
| 183 | bool first = true;
|
| 184 |
|
| 185 | spin_lock_bh(&wdev->tx_pending.lock);
|
| 186 | skb_queue_walk(&wdev->tx_pending, skb) {
|
| 187 | tx_priv = wfx_skb_tx_priv(skb);
|
| 188 | req = wfx_skb_txreq(skb);
|
| 189 | if (ktime_after(now, ktime_add_ms(tx_priv->xmit_timestamp, limit_ms))) {
|
| 190 | if (first) {
|
| 191 | dev_info(wdev->dev, "frames stuck in firmware since %dms or more:\n",
|
| 192 | limit_ms);
|
| 193 | first = false;
|
| 194 | }
|
| 195 | dev_info(wdev->dev, " id %08x sent %lldms ago\n",
|
| 196 | req->packet_id, ktime_ms_delta(now, tx_priv->xmit_timestamp));
|
| 197 | }
|
| 198 | }
|
| 199 | spin_unlock_bh(&wdev->tx_pending.lock);
|
| 200 | }
|
| 201 |
|
| 202 | unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb)
|
| 203 | {
|
| 204 | ktime_t now = ktime_get();
|
| 205 | struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb);
|
| 206 |
|
| 207 | return ktime_us_delta(now, tx_priv->xmit_timestamp);
|
| 208 | }
|
| 209 |
|
| 210 | bool wfx_tx_queues_has_cab(struct wfx_vif *wvif)
|
| 211 | {
|
| 212 | struct ieee80211_vif *vif = wvif_to_vif(wvif);
|
| 213 | int i;
|
| 214 |
|
| 215 | if (vif->type != NL80211_IFTYPE_AP)
|
| 216 | return false;
|
| 217 | for (i = 0; i < IEEE80211_NUM_ACS; ++i)
|
| 218 | |
| 219 | * all queued frames has same interface id
|
| 220 |
|
| 221 | if (!skb_queue_empty_lockless(&wvif->tx_queue[i].cab))
|
| 222 | return true;
|
| 223 | return false;
|
| 224 | }
|
| 225 |
|
| 226 | static int wfx_tx_queue_get_weight(struct wfx_queue *queue)
|
| 227 | {
|
| 228 | return atomic_read(&queue->pending_frames) * queue->priority;
|
| 229 | }
|
| 230 |
|
| 231 | static struct sk_buff *wfx_tx_queues_get_skb(struct wfx_dev *wdev)
|
| 232 | {
|
| 233 | struct wfx_queue *queues[IEEE80211_NUM_ACS * ARRAY_SIZE(wdev->vif)];
|
| 234 | int i, j, num_queues = 0;
|
| 235 | struct wfx_vif *wvif;
|
| 236 | struct wfx_hif_msg *hif;
|
| 237 | struct sk_buff *skb;
|
| 238 |
|
| 239 |
|
| 240 | wvif = NULL;
|
| 241 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
|
| 242 | for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
| 243 | WARN_ON(num_queues >= ARRAY_SIZE(queues));
|
| 244 | queues[num_queues] = &wvif->tx_queue[i];
|
| 245 | for (j = num_queues; j > 0; j--)
|
| 246 | if (wfx_tx_queue_get_weight(queues[j]) <
|
| 247 | wfx_tx_queue_get_weight(queues[j - 1]))
|
| 248 | swap(queues[j - 1], queues[j]);
|
| 249 | num_queues++;
|
| 250 | }
|
| 251 | }
|
| 252 |
|
| 253 | wvif = NULL;
|
| 254 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
|
| 255 | for (i = 0; i < num_queues; i++) {
|
| 256 | skb = skb_dequeue(&queues[i]->offchan);
|
| 257 | if (!skb)
|
| 258 | continue;
|
| 259 | hif = (struct wfx_hif_msg *)skb->data;
|
| 260 | |
| 261 | * The only interface allowed to send data during scan.
|
| 262 |
|
| 263 | WARN_ON(hif->interface != 2);
|
| 264 | atomic_inc(&queues[i]->pending_frames);
|
| 265 | trace_queues_stats(wdev, queues[i]);
|
| 266 | return skb;
|
| 267 | }
|
| 268 | }
|
| 269 |
|
| 270 | if (mutex_is_locked(&wdev->scan_lock))
|
| 271 | return NULL;
|
| 272 |
|
| 273 | wvif = NULL;
|
| 274 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
|
| 275 | if (!wvif->after_dtim_tx_allowed)
|
| 276 | continue;
|
| 277 | for (i = 0; i < num_queues; i++) {
|
| 278 | skb = skb_dequeue(&queues[i]->cab);
|
| 279 | if (!skb)
|
| 280 | continue;
|
| 281 | |
| 282 | * be AP, all queued frames has same interface id
|
| 283 |
|
| 284 | hif = (struct wfx_hif_msg *)skb->data;
|
| 285 | WARN_ON(hif->interface != wvif->id);
|
| 286 | WARN_ON(queues[i] != &wvif->tx_queue[skb_get_queue_mapping(skb)]);
|
| 287 | atomic_inc(&queues[i]->pending_frames);
|
| 288 | trace_queues_stats(wdev, queues[i]);
|
| 289 | return skb;
|
| 290 | }
|
| 291 |
|
| 292 | wvif->after_dtim_tx_allowed = false;
|
| 293 | schedule_work(&wvif->update_tim_work);
|
| 294 | }
|
| 295 |
|
| 296 | for (i = 0; i < num_queues; i++) {
|
| 297 | skb = skb_dequeue(&queues[i]->normal);
|
| 298 | if (skb) {
|
| 299 | atomic_inc(&queues[i]->pending_frames);
|
| 300 | trace_queues_stats(wdev, queues[i]);
|
| 301 | return skb;
|
| 302 | }
|
| 303 | }
|
| 304 | return NULL;
|
| 305 | }
|
| 306 |
|
| 307 | struct wfx_hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev)
|
| 308 | {
|
| 309 | struct wfx_tx_priv *tx_priv;
|
| 310 | struct sk_buff *skb;
|
| 311 |
|
| 312 | if (atomic_read(&wdev->tx_lock))
|
| 313 | return NULL;
|
| 314 | skb = wfx_tx_queues_get_skb(wdev);
|
| 315 | if (!skb)
|
| 316 | return NULL;
|
| 317 | skb_queue_tail(&wdev->tx_pending, skb);
|
| 318 | wake_up(&wdev->tx_dequeue);
|
| 319 | tx_priv = wfx_skb_tx_priv(skb);
|
| 320 | tx_priv->xmit_timestamp = ktime_get();
|
| 321 | return (struct wfx_hif_msg *)skb->data;
|
| 322 | }
|