staging: wfx: associate tx_queues to vifs

The device handles 4 queues (one per AC) for each virtual interface (and
maximum 4 virtual interfaces). Until now the driver unified the queue of
all interfaces and handled only 4 queues for whole device.

This architecture did not allow to balance the traffic between the vif. So,
this patch relocate the queues into the vif and change the API accordingly.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20200701150707.222985-2-Jerome.Pouiller@silabs.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Jérôme Pouiller 2020-07-01 17:06:55 +02:00 committed by Greg Kroah-Hartman
parent 7566103ea5
commit 2a30cb1634
6 changed files with 111 additions and 111 deletions

View file

@ -408,7 +408,7 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta,
// Auxiliary operations // Auxiliary operations
wfx_tx_manage_pm(wvif, hdr, tx_priv, sta); wfx_tx_manage_pm(wvif, hdr, tx_priv, sta);
wfx_tx_queues_put(wvif->wdev, skb); wfx_tx_queues_put(wvif, skb);
if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
schedule_work(&wvif->update_tim_work); schedule_work(&wvif->update_tim_work);
wfx_bh_request_tx(wvif->wdev); wfx_bh_request_tx(wvif->wdev);
@ -539,7 +539,7 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
const struct wfx_tx_priv *tx_priv; const struct wfx_tx_priv *tx_priv;
struct sk_buff *skb; struct sk_buff *skb;
skb = wfx_pending_get(wvif->wdev, arg->packet_id); skb = wfx_pending_get(wvif, arg->packet_id);
if (!skb) { if (!skb) {
dev_warn(wvif->wdev->dev, "received unknown packet_id (%#.8x) from chip\n", dev_warn(wvif->wdev->dev, "received unknown packet_id (%#.8x) from chip\n",
arg->packet_id); arg->packet_id);
@ -582,34 +582,50 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
wfx_skb_dtor(wvif, skb); wfx_skb_dtor(wvif, skb);
} }
static void wfx_flush_vif(struct wfx_vif *wvif, u32 queues,
struct sk_buff_head *dropped)
{
struct wfx_queue *queue;
int i;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (!(BIT(i) & queues))
continue;
queue = &wvif->tx_queue[i];
if (dropped)
wfx_tx_queue_drop(wvif, queue, dropped);
}
if (wvif->wdev->chip_frozen)
return;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (!(BIT(i) & queues))
continue;
queue = &wvif->tx_queue[i];
if (wait_event_timeout(wvif->wdev->tx_dequeue,
wfx_tx_queue_empty(wvif, queue),
msecs_to_jiffies(1000)) <= 0)
dev_warn(wvif->wdev->dev,
"frames queued while flushing tx queues?");
}
}
void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop) u32 queues, bool drop)
{ {
struct wfx_dev *wdev = hw->priv; struct wfx_dev *wdev = hw->priv;
struct sk_buff_head dropped; struct sk_buff_head dropped;
struct wfx_queue *queue;
struct wfx_vif *wvif; struct wfx_vif *wvif;
struct hif_msg *hif; struct hif_msg *hif;
struct sk_buff *skb; struct sk_buff *skb;
int vif_id = -1;
int i;
if (vif)
vif_id = ((struct wfx_vif *)vif->drv_priv)->id;
skb_queue_head_init(&dropped); skb_queue_head_init(&dropped);
for (i = 0; i < IEEE80211_NUM_ACS; i++) { if (vif) {
if (!(BIT(i) & queues)) wvif = (struct wfx_vif *)vif->drv_priv;
continue; wfx_flush_vif(wvif, queues, drop ? &dropped : NULL);
queue = &wdev->tx_queue[i]; } else {
if (drop) wvif = NULL;
wfx_tx_queue_drop(wdev, queue, vif_id, &dropped); while ((wvif = wvif_iterate(wdev, wvif)) != NULL)
if (wdev->chip_frozen) wfx_flush_vif(wvif, queues, drop ? &dropped : NULL);
continue;
if (wait_event_timeout(wdev->tx_dequeue,
wfx_tx_queue_empty(wdev, queue, vif_id),
msecs_to_jiffies(1000)) <= 0)
dev_warn(wdev->dev,
"frames queued while flushing tx queues?");
} }
wfx_tx_flush(wdev); wfx_tx_flush(wdev);
if (wdev->chip_frozen) if (wdev->chip_frozen)
@ -623,4 +639,3 @@ void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
wfx_skb_dtor(wvif, skb); wfx_skb_dtor(wvif, skb);
} }
} }

View file

@ -349,8 +349,9 @@ struct wfx_dev *wfx_init_common(struct device *dev,
init_completion(&wdev->firmware_ready); init_completion(&wdev->firmware_ready);
INIT_DELAYED_WORK(&wdev->cooling_timeout_work, INIT_DELAYED_WORK(&wdev->cooling_timeout_work,
wfx_cooling_timeout_work); wfx_cooling_timeout_work);
skb_queue_head_init(&wdev->tx_pending);
init_waitqueue_head(&wdev->tx_dequeue);
wfx_init_hif_cmd(&wdev->hif_cmd); wfx_init_hif_cmd(&wdev->hif_cmd);
wfx_tx_queues_init(wdev);
if (devm_add_action_or_reset(dev, wfx_free_common, wdev)) if (devm_add_action_or_reset(dev, wfx_free_common, wdev))
return NULL; return NULL;

View file

@ -57,84 +57,57 @@ void wfx_tx_lock_flush(struct wfx_dev *wdev)
wfx_tx_flush(wdev); wfx_tx_flush(wdev);
} }
void wfx_tx_queues_init(struct wfx_dev *wdev) void wfx_tx_queues_init(struct wfx_vif *wvif)
{ {
int i; int i;
skb_queue_head_init(&wdev->tx_pending);
init_waitqueue_head(&wdev->tx_dequeue);
for (i = 0; i < IEEE80211_NUM_ACS; ++i) { for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
skb_queue_head_init(&wdev->tx_queue[i].normal); skb_queue_head_init(&wvif->tx_queue[i].normal);
skb_queue_head_init(&wdev->tx_queue[i].cab); skb_queue_head_init(&wvif->tx_queue[i].cab);
} }
} }
void wfx_tx_queues_check_empty(struct wfx_dev *wdev) void wfx_tx_queues_check_empty(struct wfx_vif *wvif)
{ {
int i; int i;
WARN_ON(!skb_queue_empty_lockless(&wdev->tx_pending));
for (i = 0; i < IEEE80211_NUM_ACS; ++i) { for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
WARN_ON(atomic_read(&wdev->tx_queue[i].pending_frames)); WARN_ON(atomic_read(&wvif->tx_queue[i].pending_frames));
WARN_ON(!skb_queue_empty_lockless(&wdev->tx_queue[i].normal)); WARN_ON(!skb_queue_empty_lockless(&wvif->tx_queue[i].normal));
WARN_ON(!skb_queue_empty_lockless(&wdev->tx_queue[i].cab)); WARN_ON(!skb_queue_empty_lockless(&wvif->tx_queue[i].cab));
} }
} }
static bool __wfx_tx_queue_empty(struct wfx_dev *wdev, bool wfx_tx_queue_empty(struct wfx_vif *wvif, struct wfx_queue *queue)
struct sk_buff_head *skb_queue, int vif_id)
{ {
struct hif_msg *hif_msg; return skb_queue_empty(&queue->normal) && skb_queue_empty(&queue->cab);
struct sk_buff *skb;
spin_lock_bh(&skb_queue->lock);
skb_queue_walk(skb_queue, skb) {
hif_msg = (struct hif_msg *)skb->data;
if (vif_id < 0 || hif_msg->interface == vif_id) {
spin_unlock_bh(&skb_queue->lock);
return false;
}
}
spin_unlock_bh(&skb_queue->lock);
return true;
} }
bool wfx_tx_queue_empty(struct wfx_dev *wdev, static void __wfx_tx_queue_drop(struct wfx_vif *wvif,
struct wfx_queue *queue, int vif_id) struct sk_buff_head *skb_queue,
{
return __wfx_tx_queue_empty(wdev, &queue->normal, vif_id) &&
__wfx_tx_queue_empty(wdev, &queue->cab, vif_id);
}
static void __wfx_tx_queue_drop(struct wfx_dev *wdev,
struct sk_buff_head *skb_queue, int vif_id,
struct sk_buff_head *dropped) struct sk_buff_head *dropped)
{ {
struct sk_buff *skb, *tmp; struct sk_buff *skb, *tmp;
struct hif_msg *hif_msg;
spin_lock_bh(&skb_queue->lock); spin_lock_bh(&skb_queue->lock);
skb_queue_walk_safe(skb_queue, skb, tmp) { skb_queue_walk_safe(skb_queue, skb, tmp) {
hif_msg = (struct hif_msg *)skb->data; __skb_unlink(skb, skb_queue);
if (vif_id < 0 || hif_msg->interface == vif_id) { skb_queue_head(dropped, skb);
__skb_unlink(skb, skb_queue);
skb_queue_head(dropped, skb);
}
} }
spin_unlock_bh(&skb_queue->lock); spin_unlock_bh(&skb_queue->lock);
} }
void wfx_tx_queue_drop(struct wfx_dev *wdev, struct wfx_queue *queue, void wfx_tx_queue_drop(struct wfx_vif *wvif, struct wfx_queue *queue,
int vif_id, struct sk_buff_head *dropped) struct sk_buff_head *dropped)
{ {
__wfx_tx_queue_drop(wdev, &queue->cab, vif_id, dropped); __wfx_tx_queue_drop(wvif, &queue->cab, dropped);
__wfx_tx_queue_drop(wdev, &queue->normal, vif_id, dropped); __wfx_tx_queue_drop(wvif, &queue->normal, dropped);
wake_up(&wdev->tx_dequeue); wake_up(&wvif->wdev->tx_dequeue);
} }
void wfx_tx_queues_put(struct wfx_dev *wdev, struct sk_buff *skb) void wfx_tx_queues_put(struct wfx_vif *wvif, struct sk_buff *skb)
{ {
struct wfx_queue *queue = &wdev->tx_queue[skb_get_queue_mapping(skb)]; struct wfx_queue *queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
@ -146,39 +119,45 @@ void wfx_tx_queues_put(struct wfx_dev *wdev, struct sk_buff *skb)
void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped) void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped)
{ {
struct wfx_queue *queue; struct wfx_queue *queue;
struct wfx_vif *wvif;
struct hif_msg *hif;
struct sk_buff *skb; struct sk_buff *skb;
WARN(!wdev->chip_frozen, "%s should only be used to recover a frozen device", WARN(!wdev->chip_frozen, "%s should only be used to recover a frozen device",
__func__); __func__);
while ((skb = skb_dequeue(&wdev->tx_pending)) != NULL) { while ((skb = skb_dequeue(&wdev->tx_pending)) != NULL) {
queue = &wdev->tx_queue[skb_get_queue_mapping(skb)]; hif = (struct hif_msg *)skb->data;
WARN_ON(skb_get_queue_mapping(skb) > 3); wvif = wdev_to_wvif(wdev, hif->interface);
WARN_ON(!atomic_read(&queue->pending_frames)); if (wvif) {
atomic_dec(&queue->pending_frames); queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
WARN_ON(skb_get_queue_mapping(skb) > 3);
WARN_ON(!atomic_read(&queue->pending_frames));
atomic_dec(&queue->pending_frames);
}
skb_queue_head(dropped, skb); skb_queue_head(dropped, skb);
} }
} }
struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id) struct sk_buff *wfx_pending_get(struct wfx_vif *wvif, u32 packet_id)
{ {
struct wfx_queue *queue; struct wfx_queue *queue;
struct hif_req_tx *req; struct hif_req_tx *req;
struct sk_buff *skb; struct sk_buff *skb;
spin_lock_bh(&wdev->tx_pending.lock); spin_lock_bh(&wvif->wdev->tx_pending.lock);
skb_queue_walk(&wdev->tx_pending, skb) { skb_queue_walk(&wvif->wdev->tx_pending, skb) {
req = wfx_skb_txreq(skb); req = wfx_skb_txreq(skb);
if (req->packet_id == packet_id) { if (req->packet_id == packet_id) {
spin_unlock_bh(&wdev->tx_pending.lock); spin_unlock_bh(&wvif->wdev->tx_pending.lock);
queue = &wdev->tx_queue[skb_get_queue_mapping(skb)]; queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
WARN_ON(skb_get_queue_mapping(skb) > 3); WARN_ON(skb_get_queue_mapping(skb) > 3);
WARN_ON(!atomic_read(&queue->pending_frames)); WARN_ON(!atomic_read(&queue->pending_frames));
atomic_dec(&queue->pending_frames); atomic_dec(&queue->pending_frames);
skb_unlink(skb, &wdev->tx_pending); skb_unlink(skb, &wvif->wdev->tx_pending);
return skb; return skb;
} }
} }
spin_unlock_bh(&wdev->tx_pending.lock); spin_unlock_bh(&wvif->wdev->tx_pending.lock);
WARN(1, "cannot find packet in pending queue"); WARN(1, "cannot find packet in pending queue");
return NULL; return NULL;
} }
@ -221,7 +200,6 @@ unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev,
bool wfx_tx_queues_has_cab(struct wfx_vif *wvif) bool wfx_tx_queues_has_cab(struct wfx_vif *wvif)
{ {
struct wfx_dev *wdev = wvif->wdev;
int i; int i;
if (wvif->vif->type != NL80211_IFTYPE_AP) if (wvif->vif->type != NL80211_IFTYPE_AP)
@ -229,33 +207,39 @@ bool wfx_tx_queues_has_cab(struct wfx_vif *wvif)
for (i = 0; i < IEEE80211_NUM_ACS; ++i) for (i = 0; i < IEEE80211_NUM_ACS; ++i)
// Note: since only AP can have mcast frames in queue and only // Note: since only AP can have mcast frames in queue and only
// one vif can be AP, all queued frames has same interface id // one vif can be AP, all queued frames has same interface id
if (!skb_queue_empty_lockless(&wdev->tx_queue[i].cab)) if (!skb_queue_empty_lockless(&wvif->tx_queue[i].cab))
return true; return true;
return false; return false;
} }
static struct sk_buff *wfx_tx_queues_get_skb(struct wfx_dev *wdev) static struct sk_buff *wfx_tx_queues_get_skb(struct wfx_dev *wdev)
{ {
struct wfx_queue *sorted_queues[IEEE80211_NUM_ACS]; struct wfx_queue *queues[IEEE80211_NUM_ACS * ARRAY_SIZE(wdev->vif)];
int i, j, num_queues = 0;
struct wfx_vif *wvif; struct wfx_vif *wvif;
struct hif_msg *hif; struct hif_msg *hif;
struct sk_buff *skb; struct sk_buff *skb;
int i, j;
// bubble sort // sort the queues
for (i = 0; i < IEEE80211_NUM_ACS; i++) { wvif = NULL;
sorted_queues[i] = &wdev->tx_queue[i]; while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
for (j = i; j > 0; j--) for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (atomic_read(&sorted_queues[j]->pending_frames) < WARN_ON(num_queues >= ARRAY_SIZE(queues));
atomic_read(&sorted_queues[j - 1]->pending_frames)) queues[num_queues] = &wvif->tx_queue[i];
swap(sorted_queues[j - 1], sorted_queues[j]); for (j = num_queues; j > 0; j--)
if (atomic_read(&queues[j]->pending_frames) <
atomic_read(&queues[j - 1]->pending_frames))
swap(queues[j - 1], queues[j]);
num_queues++;
}
} }
wvif = NULL; wvif = NULL;
while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
if (!wvif->after_dtim_tx_allowed) if (!wvif->after_dtim_tx_allowed)
continue; continue;
for (i = 0; i < IEEE80211_NUM_ACS; i++) { for (i = 0; i < num_queues; i++) {
skb = skb_dequeue(&sorted_queues[i]->cab); skb = skb_dequeue(&queues[i]->cab);
if (!skb) if (!skb)
continue; continue;
// Note: since only AP can have mcast frames in queue // Note: since only AP can have mcast frames in queue
@ -263,21 +247,20 @@ static struct sk_buff *wfx_tx_queues_get_skb(struct wfx_dev *wdev)
// same interface id // same interface id
hif = (struct hif_msg *)skb->data; hif = (struct hif_msg *)skb->data;
WARN_ON(hif->interface != wvif->id); WARN_ON(hif->interface != wvif->id);
WARN_ON(sorted_queues[i] != WARN_ON(queues[i] !=
&wdev->tx_queue[skb_get_queue_mapping(skb)]); &wvif->tx_queue[skb_get_queue_mapping(skb)]);
atomic_inc(&sorted_queues[i]->pending_frames); atomic_inc(&queues[i]->pending_frames);
return skb; return skb;
} }
// No more multicast to sent // No more multicast to sent
wvif->after_dtim_tx_allowed = false; wvif->after_dtim_tx_allowed = false;
schedule_work(&wvif->update_tim_work); schedule_work(&wvif->update_tim_work);
} }
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
skb = skb_dequeue(&sorted_queues[i]->normal); for (i = 0; i < num_queues; i++) {
skb = skb_dequeue(&queues[i]->normal);
if (skb) { if (skb) {
WARN_ON(sorted_queues[i] != atomic_inc(&queues[i]->pending_frames);
&wdev->tx_queue[skb_get_queue_mapping(skb)]);
atomic_inc(&sorted_queues[i]->pending_frames);
return skb; return skb;
} }
} }

View file

@ -25,18 +25,17 @@ void wfx_tx_unlock(struct wfx_dev *wdev);
void wfx_tx_flush(struct wfx_dev *wdev); void wfx_tx_flush(struct wfx_dev *wdev);
void wfx_tx_lock_flush(struct wfx_dev *wdev); void wfx_tx_lock_flush(struct wfx_dev *wdev);
void wfx_tx_queues_init(struct wfx_dev *wdev); void wfx_tx_queues_init(struct wfx_vif *wvif);
void wfx_tx_queues_check_empty(struct wfx_dev *wdev); void wfx_tx_queues_check_empty(struct wfx_vif *wvif);
bool wfx_tx_queues_has_cab(struct wfx_vif *wvif); bool wfx_tx_queues_has_cab(struct wfx_vif *wvif);
void wfx_tx_queues_put(struct wfx_dev *wdev, struct sk_buff *skb); void wfx_tx_queues_put(struct wfx_vif *wvif, struct sk_buff *skb);
struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev); struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev);
bool wfx_tx_queue_empty(struct wfx_dev *wdev, struct wfx_queue *queue, bool wfx_tx_queue_empty(struct wfx_vif *wvif, struct wfx_queue *queue);
int vif_id); void wfx_tx_queue_drop(struct wfx_vif *wvif, struct wfx_queue *queue,
void wfx_tx_queue_drop(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff_head *dropped);
int vif_id, struct sk_buff_head *dropped);
struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id); struct sk_buff *wfx_pending_get(struct wfx_vif *wvif, u32 packet_id);
void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped); void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped);
unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev,
struct sk_buff *skb); struct sk_buff *skb);

View file

@ -805,6 +805,7 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
hif_set_macaddr(wvif, vif->addr); hif_set_macaddr(wvif, vif->addr);
wfx_tx_queues_init(wvif);
wfx_tx_policy_init(wvif); wfx_tx_policy_init(wvif);
wvif = NULL; wvif = NULL;
while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
@ -823,6 +824,7 @@ void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv; struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300)); wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300));
wfx_tx_queues_check_empty(wvif);
mutex_lock(&wdev->conf_mutex); mutex_lock(&wdev->conf_mutex);
WARN(wvif->link_id_map != 1, "corrupted state"); WARN(wvif->link_id_map != 1, "corrupted state");
@ -855,5 +857,5 @@ void wfx_stop(struct ieee80211_hw *hw)
{ {
struct wfx_dev *wdev = hw->priv; struct wfx_dev *wdev = hw->priv;
wfx_tx_queues_check_empty(wdev); WARN_ON(!skb_queue_empty_lockless(&wdev->tx_pending));
} }

View file

@ -48,7 +48,6 @@ struct wfx_dev {
struct mutex conf_mutex; struct mutex conf_mutex;
struct wfx_hif_cmd hif_cmd; struct wfx_hif_cmd hif_cmd;
struct wfx_queue tx_queue[4];
struct sk_buff_head tx_pending; struct sk_buff_head tx_pending;
wait_queue_head_t tx_dequeue; wait_queue_head_t tx_dequeue;
atomic_t tx_lock; atomic_t tx_lock;
@ -75,6 +74,7 @@ struct wfx_vif {
struct delayed_work beacon_loss_work; struct delayed_work beacon_loss_work;
struct wfx_queue tx_queue[4];
struct tx_policy_cache tx_policy_cache; struct tx_policy_cache tx_policy_cache;
struct work_struct tx_policy_upload_work; struct work_struct tx_policy_upload_work;