@@ -458,7 +458,6 @@ static void wl1271_fw_status(struct wl1271 *wl,
struct wl1271_fw_status *status)
{
struct timespec ts;
- u32 total = 0;
int i;
wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
@@ -478,13 +477,8 @@ static void wl1271_fw_status(struct wl1271 *wl,
wl->tx_blocks_freed[i] =
le32_to_cpu(status->tx_released_blks[i]);
wl->tx_blocks_available += cnt;
- total += cnt;
}
- /* if more blocks are available now, schedule some tx work */
- if (total && !skb_queue_empty(&wl->tx_queue))
- ieee80211_queue_work(wl->hw, &wl->tx_work);
-
/* update the host-chipset time offset */
getnstimeofday(&ts);
wl->time_offset = (timespec_to_ns(&ts) >> 10) -
@@ -499,6 +493,7 @@ static void wl1271_irq_work(struct work_struct *work)
u32 intr;
int loopcount = WL1271_IRQ_MAX_LOOPS;
unsigned long flags;
+ u32 prev_tx_blocks;
struct wl1271 *wl =
container_of(work, struct wl1271, irq_work);
@@ -519,6 +514,7 @@ static void wl1271_irq_work(struct work_struct *work)
spin_unlock_irqrestore(&wl->wl_lock, flags);
loopcount--;
+ prev_tx_blocks = wl->tx_blocks_available;
wl1271_fw_status(wl, wl->fw_status);
intr = le32_to_cpu(wl->fw_status->intr);
if (!intr) {
@@ -537,6 +533,17 @@ static void wl1271_irq_work(struct work_struct *work)
(wl->tx_results_count & 0xff))
wl1271_tx_complete(wl);
+ /* Check if any tx blocks were freed */
+ if ((wl->tx_blocks_available > prev_tx_blocks) &&
+ !skb_queue_empty(&wl->tx_queue)) {
+ /*
+ * In order to avoid starvation of the TX path,
+ * call the work function directly.
+ */
+ cancel_work_sync(&wl->tx_work);
+ wl1271_tx_work_locked(wl);
+ }
+
wl1271_rx(wl, wl->fw_status);
}
@@ -204,9 +204,8 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
return enabled_rates;
}
-void wl1271_tx_work(struct work_struct *work)
+void wl1271_tx_work_locked(struct wl1271 *wl)
{
- struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
struct sk_buff *skb;
bool woken_up = false;
u32 sta_rates = 0;
@@ -223,8 +222,6 @@ void wl1271_tx_work(struct work_struct *work)
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
- mutex_lock(&wl->mutex);
-
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
@@ -286,7 +283,14 @@ out_ack:
out:
if (woken_up)
wl1271_ps_elp_sleep(wl);
+}
+void wl1271_tx_work(struct work_struct *work)
+{
+ struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
+
+ mutex_lock(&wl->mutex);
+ wl1271_tx_work_locked(wl);
mutex_unlock(&wl->mutex);
}
@@ -140,6 +140,7 @@ static inline int wl1271_tx_get_queue(int queue)
}
void wl1271_tx_work(struct work_struct *work);
+void wl1271_tx_work_locked(struct wl1271 *wl);
void wl1271_tx_complete(struct wl1271 *wl);
void wl1271_tx_reset(struct wl1271 *wl);
void wl1271_tx_flush(struct wl1271 *wl);