18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson
68c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <net/mac80211.h>
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include "queue.h"
128c2ecf20Sopenharmony_ci#include "cw1200.h"
138c2ecf20Sopenharmony_ci#include "debug.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* private */ struct cw1200_queue_item
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	struct list_head	head;
188c2ecf20Sopenharmony_ci	struct sk_buff		*skb;
198c2ecf20Sopenharmony_ci	u32			packet_id;
208c2ecf20Sopenharmony_ci	unsigned long		queue_timestamp;
218c2ecf20Sopenharmony_ci	unsigned long		xmit_timestamp;
228c2ecf20Sopenharmony_ci	struct cw1200_txpriv	txpriv;
238c2ecf20Sopenharmony_ci	u8			generation;
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic inline void __cw1200_queue_lock(struct cw1200_queue *queue)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
298c2ecf20Sopenharmony_ci	if (queue->tx_locked_cnt++ == 0) {
308c2ecf20Sopenharmony_ci		pr_debug("[TX] Queue %d is locked.\n",
318c2ecf20Sopenharmony_ci			 queue->queue_id);
328c2ecf20Sopenharmony_ci		ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
398c2ecf20Sopenharmony_ci	BUG_ON(!queue->tx_locked_cnt);
408c2ecf20Sopenharmony_ci	if (--queue->tx_locked_cnt == 0) {
418c2ecf20Sopenharmony_ci		pr_debug("[TX] Queue %d is unlocked.\n",
428c2ecf20Sopenharmony_ci			 queue->queue_id);
438c2ecf20Sopenharmony_ci		ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
488c2ecf20Sopenharmony_ci					 u8 *queue_id, u8 *item_generation,
498c2ecf20Sopenharmony_ci					 u8 *item_id)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	*item_id		= (packet_id >>  0) & 0xFF;
528c2ecf20Sopenharmony_ci	*item_generation	= (packet_id >>  8) & 0xFF;
538c2ecf20Sopenharmony_ci	*queue_id		= (packet_id >> 16) & 0xFF;
548c2ecf20Sopenharmony_ci	*queue_generation	= (packet_id >> 24) & 0xFF;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
588c2ecf20Sopenharmony_ci					    u8 item_generation, u8 item_id)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	return ((u32)item_id << 0) |
618c2ecf20Sopenharmony_ci		((u32)item_generation << 8) |
628c2ecf20Sopenharmony_ci		((u32)queue_id << 16) |
638c2ecf20Sopenharmony_ci		((u32)queue_generation << 24);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
678c2ecf20Sopenharmony_ci				 struct list_head *gc_list)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item, *tmp;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	list_for_each_entry_safe(item, tmp, gc_list, head) {
728c2ecf20Sopenharmony_ci		list_del(&item->head);
738c2ecf20Sopenharmony_ci		stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
748c2ecf20Sopenharmony_ci		kfree(item);
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void cw1200_queue_register_post_gc(struct list_head *gc_list,
798c2ecf20Sopenharmony_ci					  struct cw1200_queue_item *item)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct cw1200_queue_item *gc_item;
828c2ecf20Sopenharmony_ci	gc_item = kmemdup(item, sizeof(struct cw1200_queue_item),
838c2ecf20Sopenharmony_ci			GFP_ATOMIC);
848c2ecf20Sopenharmony_ci	BUG_ON(!gc_item);
858c2ecf20Sopenharmony_ci	list_add_tail(&gc_item->head, gc_list);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void __cw1200_queue_gc(struct cw1200_queue *queue,
898c2ecf20Sopenharmony_ci			      struct list_head *head,
908c2ecf20Sopenharmony_ci			      bool unlock)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
938c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item = NULL, *tmp;
948c2ecf20Sopenharmony_ci	bool wakeup_stats = false;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	list_for_each_entry_safe(item, tmp, &queue->queue, head) {
978c2ecf20Sopenharmony_ci		if (jiffies - item->queue_timestamp < queue->ttl)
988c2ecf20Sopenharmony_ci			break;
998c2ecf20Sopenharmony_ci		--queue->num_queued;
1008c2ecf20Sopenharmony_ci		--queue->link_map_cache[item->txpriv.link_id];
1018c2ecf20Sopenharmony_ci		spin_lock_bh(&stats->lock);
1028c2ecf20Sopenharmony_ci		--stats->num_queued;
1038c2ecf20Sopenharmony_ci		if (!--stats->link_map_cache[item->txpriv.link_id])
1048c2ecf20Sopenharmony_ci			wakeup_stats = true;
1058c2ecf20Sopenharmony_ci		spin_unlock_bh(&stats->lock);
1068c2ecf20Sopenharmony_ci		cw1200_debug_tx_ttl(stats->priv);
1078c2ecf20Sopenharmony_ci		cw1200_queue_register_post_gc(head, item);
1088c2ecf20Sopenharmony_ci		item->skb = NULL;
1098c2ecf20Sopenharmony_ci		list_move_tail(&item->head, &queue->free_pool);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (wakeup_stats)
1138c2ecf20Sopenharmony_ci		wake_up(&stats->wait_link_id_empty);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (queue->overfull) {
1168c2ecf20Sopenharmony_ci		if (queue->num_queued <= (queue->capacity >> 1)) {
1178c2ecf20Sopenharmony_ci			queue->overfull = false;
1188c2ecf20Sopenharmony_ci			if (unlock)
1198c2ecf20Sopenharmony_ci				__cw1200_queue_unlock(queue);
1208c2ecf20Sopenharmony_ci		} else if (item) {
1218c2ecf20Sopenharmony_ci			unsigned long tmo = item->queue_timestamp + queue->ttl;
1228c2ecf20Sopenharmony_ci			mod_timer(&queue->gc, tmo);
1238c2ecf20Sopenharmony_ci			cw1200_pm_stay_awake(&stats->priv->pm_state,
1248c2ecf20Sopenharmony_ci					     tmo - jiffies);
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void cw1200_queue_gc(struct timer_list *t)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	LIST_HEAD(list);
1328c2ecf20Sopenharmony_ci	struct cw1200_queue *queue =
1338c2ecf20Sopenharmony_ci		from_timer(queue, t, gc);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
1368c2ecf20Sopenharmony_ci	__cw1200_queue_gc(queue, &list, true);
1378c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
1388c2ecf20Sopenharmony_ci	cw1200_queue_post_gc(queue->stats, &list);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ciint cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
1428c2ecf20Sopenharmony_ci			    size_t map_capacity,
1438c2ecf20Sopenharmony_ci			    cw1200_queue_skb_dtor_t skb_dtor,
1448c2ecf20Sopenharmony_ci			    struct cw1200_common *priv)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	memset(stats, 0, sizeof(*stats));
1478c2ecf20Sopenharmony_ci	stats->map_capacity = map_capacity;
1488c2ecf20Sopenharmony_ci	stats->skb_dtor = skb_dtor;
1498c2ecf20Sopenharmony_ci	stats->priv = priv;
1508c2ecf20Sopenharmony_ci	spin_lock_init(&stats->lock);
1518c2ecf20Sopenharmony_ci	init_waitqueue_head(&stats->wait_link_id_empty);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	stats->link_map_cache = kcalloc(map_capacity, sizeof(int),
1548c2ecf20Sopenharmony_ci					GFP_KERNEL);
1558c2ecf20Sopenharmony_ci	if (!stats->link_map_cache)
1568c2ecf20Sopenharmony_ci		return -ENOMEM;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return 0;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ciint cw1200_queue_init(struct cw1200_queue *queue,
1628c2ecf20Sopenharmony_ci		      struct cw1200_queue_stats *stats,
1638c2ecf20Sopenharmony_ci		      u8 queue_id,
1648c2ecf20Sopenharmony_ci		      size_t capacity,
1658c2ecf20Sopenharmony_ci		      unsigned long ttl)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	size_t i;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	memset(queue, 0, sizeof(*queue));
1708c2ecf20Sopenharmony_ci	queue->stats = stats;
1718c2ecf20Sopenharmony_ci	queue->capacity = capacity;
1728c2ecf20Sopenharmony_ci	queue->queue_id = queue_id;
1738c2ecf20Sopenharmony_ci	queue->ttl = ttl;
1748c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue->queue);
1758c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue->pending);
1768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue->free_pool);
1778c2ecf20Sopenharmony_ci	spin_lock_init(&queue->lock);
1788c2ecf20Sopenharmony_ci	timer_setup(&queue->gc, cw1200_queue_gc, 0);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	queue->pool = kcalloc(capacity, sizeof(struct cw1200_queue_item),
1818c2ecf20Sopenharmony_ci			      GFP_KERNEL);
1828c2ecf20Sopenharmony_ci	if (!queue->pool)
1838c2ecf20Sopenharmony_ci		return -ENOMEM;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	queue->link_map_cache = kcalloc(stats->map_capacity, sizeof(int),
1868c2ecf20Sopenharmony_ci					GFP_KERNEL);
1878c2ecf20Sopenharmony_ci	if (!queue->link_map_cache) {
1888c2ecf20Sopenharmony_ci		kfree(queue->pool);
1898c2ecf20Sopenharmony_ci		queue->pool = NULL;
1908c2ecf20Sopenharmony_ci		return -ENOMEM;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	for (i = 0; i < capacity; ++i)
1948c2ecf20Sopenharmony_ci		list_add_tail(&queue->pool[i].head, &queue->free_pool);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return 0;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ciint cw1200_queue_clear(struct cw1200_queue *queue)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	int i;
2028c2ecf20Sopenharmony_ci	LIST_HEAD(gc_list);
2038c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
2048c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item, *tmp;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
2078c2ecf20Sopenharmony_ci	queue->generation++;
2088c2ecf20Sopenharmony_ci	list_splice_tail_init(&queue->queue, &queue->pending);
2098c2ecf20Sopenharmony_ci	list_for_each_entry_safe(item, tmp, &queue->pending, head) {
2108c2ecf20Sopenharmony_ci		WARN_ON(!item->skb);
2118c2ecf20Sopenharmony_ci		cw1200_queue_register_post_gc(&gc_list, item);
2128c2ecf20Sopenharmony_ci		item->skb = NULL;
2138c2ecf20Sopenharmony_ci		list_move_tail(&item->head, &queue->free_pool);
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	queue->num_queued = 0;
2168c2ecf20Sopenharmony_ci	queue->num_pending = 0;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	spin_lock_bh(&stats->lock);
2198c2ecf20Sopenharmony_ci	for (i = 0; i < stats->map_capacity; ++i) {
2208c2ecf20Sopenharmony_ci		stats->num_queued -= queue->link_map_cache[i];
2218c2ecf20Sopenharmony_ci		stats->link_map_cache[i] -= queue->link_map_cache[i];
2228c2ecf20Sopenharmony_ci		queue->link_map_cache[i] = 0;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	spin_unlock_bh(&stats->lock);
2258c2ecf20Sopenharmony_ci	if (queue->overfull) {
2268c2ecf20Sopenharmony_ci		queue->overfull = false;
2278c2ecf20Sopenharmony_ci		__cw1200_queue_unlock(queue);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
2308c2ecf20Sopenharmony_ci	wake_up(&stats->wait_link_id_empty);
2318c2ecf20Sopenharmony_ci	cw1200_queue_post_gc(stats, &gc_list);
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_civoid cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	kfree(stats->link_map_cache);
2388c2ecf20Sopenharmony_ci	stats->link_map_cache = NULL;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_civoid cw1200_queue_deinit(struct cw1200_queue *queue)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	cw1200_queue_clear(queue);
2448c2ecf20Sopenharmony_ci	del_timer_sync(&queue->gc);
2458c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue->free_pool);
2468c2ecf20Sopenharmony_ci	kfree(queue->pool);
2478c2ecf20Sopenharmony_ci	kfree(queue->link_map_cache);
2488c2ecf20Sopenharmony_ci	queue->pool = NULL;
2498c2ecf20Sopenharmony_ci	queue->link_map_cache = NULL;
2508c2ecf20Sopenharmony_ci	queue->capacity = 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cisize_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
2548c2ecf20Sopenharmony_ci				   u32 link_id_map)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	size_t ret;
2578c2ecf20Sopenharmony_ci	int i, bit;
2588c2ecf20Sopenharmony_ci	size_t map_capacity = queue->stats->map_capacity;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (!link_id_map)
2618c2ecf20Sopenharmony_ci		return 0;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
2648c2ecf20Sopenharmony_ci	if (link_id_map == (u32)-1) {
2658c2ecf20Sopenharmony_ci		ret = queue->num_queued - queue->num_pending;
2668c2ecf20Sopenharmony_ci	} else {
2678c2ecf20Sopenharmony_ci		ret = 0;
2688c2ecf20Sopenharmony_ci		for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
2698c2ecf20Sopenharmony_ci			if (link_id_map & bit)
2708c2ecf20Sopenharmony_ci				ret += queue->link_map_cache[i];
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
2748c2ecf20Sopenharmony_ci	return ret;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ciint cw1200_queue_put(struct cw1200_queue *queue,
2788c2ecf20Sopenharmony_ci		     struct sk_buff *skb,
2798c2ecf20Sopenharmony_ci		     struct cw1200_txpriv *txpriv)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int ret = 0;
2828c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (txpriv->link_id >= queue->stats->map_capacity)
2858c2ecf20Sopenharmony_ci		return -EINVAL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
2888c2ecf20Sopenharmony_ci	if (!WARN_ON(list_empty(&queue->free_pool))) {
2898c2ecf20Sopenharmony_ci		struct cw1200_queue_item *item = list_first_entry(
2908c2ecf20Sopenharmony_ci			&queue->free_pool, struct cw1200_queue_item, head);
2918c2ecf20Sopenharmony_ci		BUG_ON(item->skb);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		list_move_tail(&item->head, &queue->queue);
2948c2ecf20Sopenharmony_ci		item->skb = skb;
2958c2ecf20Sopenharmony_ci		item->txpriv = *txpriv;
2968c2ecf20Sopenharmony_ci		item->generation = 0;
2978c2ecf20Sopenharmony_ci		item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
2988c2ecf20Sopenharmony_ci							    queue->queue_id,
2998c2ecf20Sopenharmony_ci							    item->generation,
3008c2ecf20Sopenharmony_ci							    item - queue->pool);
3018c2ecf20Sopenharmony_ci		item->queue_timestamp = jiffies;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		++queue->num_queued;
3048c2ecf20Sopenharmony_ci		++queue->link_map_cache[txpriv->link_id];
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		spin_lock_bh(&stats->lock);
3078c2ecf20Sopenharmony_ci		++stats->num_queued;
3088c2ecf20Sopenharmony_ci		++stats->link_map_cache[txpriv->link_id];
3098c2ecf20Sopenharmony_ci		spin_unlock_bh(&stats->lock);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		/* TX may happen in parallel sometimes.
3128c2ecf20Sopenharmony_ci		 * Leave extra queue slots so we don't overflow.
3138c2ecf20Sopenharmony_ci		 */
3148c2ecf20Sopenharmony_ci		if (queue->overfull == false &&
3158c2ecf20Sopenharmony_ci		    queue->num_queued >=
3168c2ecf20Sopenharmony_ci		    (queue->capacity - (num_present_cpus() - 1))) {
3178c2ecf20Sopenharmony_ci			queue->overfull = true;
3188c2ecf20Sopenharmony_ci			__cw1200_queue_lock(queue);
3198c2ecf20Sopenharmony_ci			mod_timer(&queue->gc, jiffies);
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci	} else {
3228c2ecf20Sopenharmony_ci		ret = -ENOENT;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
3258c2ecf20Sopenharmony_ci	return ret;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ciint cw1200_queue_get(struct cw1200_queue *queue,
3298c2ecf20Sopenharmony_ci		     u32 link_id_map,
3308c2ecf20Sopenharmony_ci		     struct wsm_tx **tx,
3318c2ecf20Sopenharmony_ci		     struct ieee80211_tx_info **tx_info,
3328c2ecf20Sopenharmony_ci		     const struct cw1200_txpriv **txpriv)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int ret = -ENOENT;
3358c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item;
3368c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
3378c2ecf20Sopenharmony_ci	bool wakeup_stats = false;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
3408c2ecf20Sopenharmony_ci	list_for_each_entry(item, &queue->queue, head) {
3418c2ecf20Sopenharmony_ci		if (link_id_map & BIT(item->txpriv.link_id)) {
3428c2ecf20Sopenharmony_ci			ret = 0;
3438c2ecf20Sopenharmony_ci			break;
3448c2ecf20Sopenharmony_ci		}
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!WARN_ON(ret)) {
3488c2ecf20Sopenharmony_ci		*tx = (struct wsm_tx *)item->skb->data;
3498c2ecf20Sopenharmony_ci		*tx_info = IEEE80211_SKB_CB(item->skb);
3508c2ecf20Sopenharmony_ci		*txpriv = &item->txpriv;
3518c2ecf20Sopenharmony_ci		(*tx)->packet_id = item->packet_id;
3528c2ecf20Sopenharmony_ci		list_move_tail(&item->head, &queue->pending);
3538c2ecf20Sopenharmony_ci		++queue->num_pending;
3548c2ecf20Sopenharmony_ci		--queue->link_map_cache[item->txpriv.link_id];
3558c2ecf20Sopenharmony_ci		item->xmit_timestamp = jiffies;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		spin_lock_bh(&stats->lock);
3588c2ecf20Sopenharmony_ci		--stats->num_queued;
3598c2ecf20Sopenharmony_ci		if (!--stats->link_map_cache[item->txpriv.link_id])
3608c2ecf20Sopenharmony_ci			wakeup_stats = true;
3618c2ecf20Sopenharmony_ci		spin_unlock_bh(&stats->lock);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
3648c2ecf20Sopenharmony_ci	if (wakeup_stats)
3658c2ecf20Sopenharmony_ci		wake_up(&stats->wait_link_id_empty);
3668c2ecf20Sopenharmony_ci	return ret;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ciint cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	int ret = 0;
3728c2ecf20Sopenharmony_ci	u8 queue_generation, queue_id, item_generation, item_id;
3738c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item;
3748c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
3778c2ecf20Sopenharmony_ci			      &item_generation, &item_id);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	item = &queue->pool[item_id];
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
3828c2ecf20Sopenharmony_ci	BUG_ON(queue_id != queue->queue_id);
3838c2ecf20Sopenharmony_ci	if (queue_generation != queue->generation) {
3848c2ecf20Sopenharmony_ci		ret = -ENOENT;
3858c2ecf20Sopenharmony_ci	} else if (item_id >= (unsigned) queue->capacity) {
3868c2ecf20Sopenharmony_ci		WARN_ON(1);
3878c2ecf20Sopenharmony_ci		ret = -EINVAL;
3888c2ecf20Sopenharmony_ci	} else if (item->generation != item_generation) {
3898c2ecf20Sopenharmony_ci		WARN_ON(1);
3908c2ecf20Sopenharmony_ci		ret = -ENOENT;
3918c2ecf20Sopenharmony_ci	} else {
3928c2ecf20Sopenharmony_ci		--queue->num_pending;
3938c2ecf20Sopenharmony_ci		++queue->link_map_cache[item->txpriv.link_id];
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		spin_lock_bh(&stats->lock);
3968c2ecf20Sopenharmony_ci		++stats->num_queued;
3978c2ecf20Sopenharmony_ci		++stats->link_map_cache[item->txpriv.link_id];
3988c2ecf20Sopenharmony_ci		spin_unlock_bh(&stats->lock);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		item->generation = ++item_generation;
4018c2ecf20Sopenharmony_ci		item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
4028c2ecf20Sopenharmony_ci							    queue_id,
4038c2ecf20Sopenharmony_ci							    item_generation,
4048c2ecf20Sopenharmony_ci							    item_id);
4058c2ecf20Sopenharmony_ci		list_move(&item->head, &queue->queue);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
4088c2ecf20Sopenharmony_ci	return ret;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ciint cw1200_queue_requeue_all(struct cw1200_queue *queue)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item, *tmp;
4148c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
4158c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
4188c2ecf20Sopenharmony_ci		--queue->num_pending;
4198c2ecf20Sopenharmony_ci		++queue->link_map_cache[item->txpriv.link_id];
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		spin_lock_bh(&stats->lock);
4228c2ecf20Sopenharmony_ci		++stats->num_queued;
4238c2ecf20Sopenharmony_ci		++stats->link_map_cache[item->txpriv.link_id];
4248c2ecf20Sopenharmony_ci		spin_unlock_bh(&stats->lock);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		++item->generation;
4278c2ecf20Sopenharmony_ci		item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
4288c2ecf20Sopenharmony_ci							    queue->queue_id,
4298c2ecf20Sopenharmony_ci							    item->generation,
4308c2ecf20Sopenharmony_ci							    item - queue->pool);
4318c2ecf20Sopenharmony_ci		list_move(&item->head, &queue->queue);
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ciint cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int ret = 0;
4418c2ecf20Sopenharmony_ci	u8 queue_generation, queue_id, item_generation, item_id;
4428c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item;
4438c2ecf20Sopenharmony_ci	struct cw1200_queue_stats *stats = queue->stats;
4448c2ecf20Sopenharmony_ci	struct sk_buff *gc_skb = NULL;
4458c2ecf20Sopenharmony_ci	struct cw1200_txpriv gc_txpriv;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
4488c2ecf20Sopenharmony_ci			      &item_generation, &item_id);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	item = &queue->pool[item_id];
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
4538c2ecf20Sopenharmony_ci	BUG_ON(queue_id != queue->queue_id);
4548c2ecf20Sopenharmony_ci	if (queue_generation != queue->generation) {
4558c2ecf20Sopenharmony_ci		ret = -ENOENT;
4568c2ecf20Sopenharmony_ci	} else if (item_id >= (unsigned) queue->capacity) {
4578c2ecf20Sopenharmony_ci		WARN_ON(1);
4588c2ecf20Sopenharmony_ci		ret = -EINVAL;
4598c2ecf20Sopenharmony_ci	} else if (item->generation != item_generation) {
4608c2ecf20Sopenharmony_ci		WARN_ON(1);
4618c2ecf20Sopenharmony_ci		ret = -ENOENT;
4628c2ecf20Sopenharmony_ci	} else {
4638c2ecf20Sopenharmony_ci		gc_txpriv = item->txpriv;
4648c2ecf20Sopenharmony_ci		gc_skb = item->skb;
4658c2ecf20Sopenharmony_ci		item->skb = NULL;
4668c2ecf20Sopenharmony_ci		--queue->num_pending;
4678c2ecf20Sopenharmony_ci		--queue->num_queued;
4688c2ecf20Sopenharmony_ci		++queue->num_sent;
4698c2ecf20Sopenharmony_ci		++item->generation;
4708c2ecf20Sopenharmony_ci		/* Do not use list_move_tail here, but list_move:
4718c2ecf20Sopenharmony_ci		 * try to utilize cache row.
4728c2ecf20Sopenharmony_ci		 */
4738c2ecf20Sopenharmony_ci		list_move(&item->head, &queue->free_pool);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		if (queue->overfull &&
4768c2ecf20Sopenharmony_ci		    (queue->num_queued <= (queue->capacity >> 1))) {
4778c2ecf20Sopenharmony_ci			queue->overfull = false;
4788c2ecf20Sopenharmony_ci			__cw1200_queue_unlock(queue);
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (gc_skb)
4848c2ecf20Sopenharmony_ci		stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return ret;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciint cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
4908c2ecf20Sopenharmony_ci			 struct sk_buff **skb,
4918c2ecf20Sopenharmony_ci			 const struct cw1200_txpriv **txpriv)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	int ret = 0;
4948c2ecf20Sopenharmony_ci	u8 queue_generation, queue_id, item_generation, item_id;
4958c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item;
4968c2ecf20Sopenharmony_ci	cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
4978c2ecf20Sopenharmony_ci			      &item_generation, &item_id);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	item = &queue->pool[item_id];
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
5028c2ecf20Sopenharmony_ci	BUG_ON(queue_id != queue->queue_id);
5038c2ecf20Sopenharmony_ci	if (queue_generation != queue->generation) {
5048c2ecf20Sopenharmony_ci		ret = -ENOENT;
5058c2ecf20Sopenharmony_ci	} else if (item_id >= (unsigned) queue->capacity) {
5068c2ecf20Sopenharmony_ci		WARN_ON(1);
5078c2ecf20Sopenharmony_ci		ret = -EINVAL;
5088c2ecf20Sopenharmony_ci	} else if (item->generation != item_generation) {
5098c2ecf20Sopenharmony_ci		WARN_ON(1);
5108c2ecf20Sopenharmony_ci		ret = -ENOENT;
5118c2ecf20Sopenharmony_ci	} else {
5128c2ecf20Sopenharmony_ci		*skb = item->skb;
5138c2ecf20Sopenharmony_ci		*txpriv = &item->txpriv;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
5168c2ecf20Sopenharmony_ci	return ret;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_civoid cw1200_queue_lock(struct cw1200_queue *queue)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
5228c2ecf20Sopenharmony_ci	__cw1200_queue_lock(queue);
5238c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_civoid cw1200_queue_unlock(struct cw1200_queue *queue)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
5298c2ecf20Sopenharmony_ci	__cw1200_queue_unlock(queue);
5308c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cibool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
5348c2ecf20Sopenharmony_ci				     unsigned long *timestamp,
5358c2ecf20Sopenharmony_ci				     u32 pending_frame_id)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct cw1200_queue_item *item;
5388c2ecf20Sopenharmony_ci	bool ret;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_lock_bh(&queue->lock);
5418c2ecf20Sopenharmony_ci	ret = !list_empty(&queue->pending);
5428c2ecf20Sopenharmony_ci	if (ret) {
5438c2ecf20Sopenharmony_ci		list_for_each_entry(item, &queue->pending, head) {
5448c2ecf20Sopenharmony_ci			if (item->packet_id != pending_frame_id)
5458c2ecf20Sopenharmony_ci				if (time_before(item->xmit_timestamp,
5468c2ecf20Sopenharmony_ci						*timestamp))
5478c2ecf20Sopenharmony_ci					*timestamp = item->xmit_timestamp;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci	spin_unlock_bh(&queue->lock);
5518c2ecf20Sopenharmony_ci	return ret;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cibool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
5558c2ecf20Sopenharmony_ci				 u32 link_id_map)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	bool empty = true;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	spin_lock_bh(&stats->lock);
5608c2ecf20Sopenharmony_ci	if (link_id_map == (u32)-1) {
5618c2ecf20Sopenharmony_ci		empty = stats->num_queued == 0;
5628c2ecf20Sopenharmony_ci	} else {
5638c2ecf20Sopenharmony_ci		int i;
5648c2ecf20Sopenharmony_ci		for (i = 0; i < stats->map_capacity; ++i) {
5658c2ecf20Sopenharmony_ci			if (link_id_map & BIT(i)) {
5668c2ecf20Sopenharmony_ci				if (stats->link_map_cache[i]) {
5678c2ecf20Sopenharmony_ci					empty = false;
5688c2ecf20Sopenharmony_ci					break;
5698c2ecf20Sopenharmony_ci				}
5708c2ecf20Sopenharmony_ci			}
5718c2ecf20Sopenharmony_ci		}
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci	spin_unlock_bh(&stats->lock);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return empty;
5768c2ecf20Sopenharmony_ci}
577