Commit f9e3033f authored by Dedy Lansky's avatar Dedy Lansky Committed by Kalle Valo

wil6210: fix net queue stop/wake

Driver calls to netif_tx_stop_all_queues/netif_tx_wake_all_queues are
inconsistent. In several cases, driver can get to a situation where net
queues are stopped forever and data cannot be sent.

The fix is to stop net queues if there is at least one vring which is
"full" and to wake net queues if all vrings are not "full".
Signed-off-by: default avatarDedy Lansky <qca_dlansky@qca.qualcomm.com>
Signed-off-by: default avatarMaya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 40bea976
......@@ -213,7 +213,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
memset(&sta->stats, 0, sizeof(sta->stats));
}
static bool wil_ap_is_connected(struct wil6210_priv *wil)
static bool wil_is_connected(struct wil6210_priv *wil)
{
int i;
......@@ -267,7 +267,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
wil_bcast_fini(wil);
netif_tx_stop_all_queues(ndev);
wil_update_net_queues_bh(wil, NULL, true);
netif_carrier_off(ndev);
if (test_bit(wil_status_fwconnected, wil->status)) {
......@@ -283,8 +283,12 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
if (!wil_ap_is_connected(wil))
if (!wil_is_connected(wil)) {
wil_update_net_queues_bh(wil, NULL, true);
clear_bit(wil_status_fwconnected, wil->status);
} else {
wil_update_net_queues_bh(wil, NULL, false);
}
break;
default:
break;
......@@ -516,6 +520,8 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_LIST_HEAD(&wil->pending_wmi_ev);
INIT_LIST_HEAD(&wil->probe_client_pending);
spin_lock_init(&wil->wmi_ev_lock);
spin_lock_init(&wil->net_queue_lock);
wil->net_queue_stopped = 1;
init_waitqueue_head(&wil->wq);
wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
......
......@@ -214,7 +214,7 @@ int wil_if_add(struct wil6210_priv *wil)
netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
netif_tx_stop_all_queues(ndev);
wil_update_net_queues_bh(wil, NULL, true);
rc = register_netdev(ndev);
if (rc < 0) {
......
......@@ -88,6 +88,18 @@ static inline int wil_vring_wmark_high(struct vring *vring)
return vring->size/4;
}
/* returns true if num avail descriptors is lower than wmark_low */
static inline int wil_vring_avail_low(struct vring *vring)
{
return wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring);
}
/* returns true if num avail descriptors is higher than wmark_high */
static inline int wil_vring_avail_high(struct vring *vring)
{
return wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring);
}
/* wil_val_in_range - check if value in [min,max) */
static inline bool wil_val_in_range(int val, int min, int max)
{
......@@ -1780,6 +1792,89 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
return rc;
}
/**
* Check status of tx vrings and stop/wake net queues if needed
*
* This function does one of two checks:
* In case check_stop is true, will check if net queues need to be stopped. If
* the conditions for stopping are met, netif_tx_stop_all_queues() is called.
* In case check_stop is false, will check if net queues need to be waked. If
* the conditions for waking are met, netif_tx_wake_all_queues() is called.
* vring is the vring which is currently being modified by either adding
* descriptors (tx) into it or removing descriptors (tx complete) from it. Can
* be null when irrelevant (e.g. connect/disconnect events).
*
* The implementation is to stop net queues if modified vring has low
* descriptor availability. Wake if all vrings are not in low descriptor
* availability and modified vring has high descriptor availability.
*/
static inline void __wil_update_net_queues(struct wil6210_priv *wil,
struct vring *vring,
bool check_stop)
{
int i;
if (vring)
wil_dbg_txrx(wil, "vring %d, check_stop=%d, stopped=%d",
(int)(vring - wil->vring_tx), check_stop,
wil->net_queue_stopped);
else
wil_dbg_txrx(wil, "check_stop=%d, stopped=%d",
check_stop, wil->net_queue_stopped);
if (check_stop == wil->net_queue_stopped)
/* net queues already in desired state */
return;
if (check_stop) {
if (!vring || unlikely(wil_vring_avail_low(vring))) {
/* not enough room in the vring */
netif_tx_stop_all_queues(wil_to_ndev(wil));
wil->net_queue_stopped = true;
wil_dbg_txrx(wil, "netif_tx_stop called\n");
}
return;
}
/* check wake */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *cur_vring = &wil->vring_tx[i];
struct vring_tx_data *txdata = &wil->vring_tx_data[i];
if (!cur_vring->va || !txdata->enabled || cur_vring == vring)
continue;
if (wil_vring_avail_low(cur_vring)) {
wil_dbg_txrx(wil, "vring %d full, can't wake\n",
(int)(cur_vring - wil->vring_tx));
return;
}
}
if (!vring || wil_vring_avail_high(vring)) {
/* enough room in the vring */
wil_dbg_txrx(wil, "calling netif_tx_wake\n");
netif_tx_wake_all_queues(wil_to_ndev(wil));
wil->net_queue_stopped = false;
}
}
void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring,
bool check_stop)
{
spin_lock(&wil->net_queue_lock);
__wil_update_net_queues(wil, vring, check_stop);
spin_unlock(&wil->net_queue_lock);
}
void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring,
bool check_stop)
{
spin_lock_bh(&wil->net_queue_lock);
__wil_update_net_queues(wil, vring, check_stop);
spin_unlock_bh(&wil->net_queue_lock);
}
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
......@@ -1822,14 +1917,10 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/* set up vring entry */
rc = wil_tx_vring(wil, vring, skb);
/* do we still have enough room in the vring? */
if (unlikely(wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))) {
netif_tx_stop_all_queues(wil_to_ndev(wil));
wil_dbg_txrx(wil, "netif_tx_stop : ring full\n");
}
switch (rc) {
case 0:
/* shall we stop net queues? */
wil_update_net_queues_bh(wil, vring, true);
/* statistics will be updated on the tx_complete */
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
......@@ -1978,10 +2069,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
txdata->last_idle = get_cycles();
}
if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) {
wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n");
netif_tx_wake_all_queues(wil_to_ndev(wil));
}
/* shall we wake net queues? */
if (done)
wil_update_net_queues(wil, vring, false);
return done;
}
......@@ -624,6 +624,8 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker
*/
spinlock_t wmi_ev_lock;
spinlock_t net_queue_lock; /* guarding stop/wake netif queue */
int net_queue_stopped; /* netif_tx_stop_all_queues invoked */
struct napi_struct napi_rx;
struct napi_struct napi_tx;
/* keep alive */
......@@ -886,6 +888,10 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size);
int wil_bcast_init(struct wil6210_priv *wil);
void wil_bcast_fini(struct wil6210_priv *wil);
void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring,
bool should_stop);
void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring,
bool check_stop);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
int wil_tx_complete(struct wil6210_priv *wil, int ringid);
void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
......
......@@ -548,7 +548,6 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (rc) {
netif_tx_stop_all_queues(ndev);
netif_carrier_off(ndev);
wil_err(wil,
"%s: cfg80211_connect_result with failure\n",
......@@ -588,7 +587,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
wil->sta[evt->cid].status = wil_sta_connected;
set_bit(wil_status_fwconnected, wil->status);
netif_tx_wake_all_queues(ndev);
wil_update_net_queues_bh(wil, NULL, false);
out:
if (rc)
......
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