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