286 lines
7.0 KiB
C
286 lines
7.0 KiB
C
/*
|
|
* xr806/queue.c
|
|
*
|
|
* Copyright (c) 2022
|
|
* Allwinner Technology Co., Ltd. <www.allwinner.com>
|
|
* laumy <liumingyuan@allwinner.com>
|
|
*
|
|
* Transmit Queue implementation for Xr806 drivers
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include "os_intf.h"
|
|
#include "queue.h"
|
|
#include "debug.h"
|
|
|
|
int xradio_queue_init(struct xradio_queue *queue, uint8_t queue_id, size_t capacity,
|
|
unsigned long ttl, xr_free_skb_t cb)
|
|
{
|
|
int i = 0;
|
|
|
|
memset(queue, 0, sizeof(*queue));
|
|
queue->capacity = capacity;
|
|
queue->queue_id = queue_id;
|
|
queue->ttl = ttl;
|
|
queue->free_skb = cb;
|
|
|
|
INIT_LIST_HEAD(&queue->queue);
|
|
INIT_LIST_HEAD(&queue->pending);
|
|
INIT_LIST_HEAD(&queue->free_pool);
|
|
|
|
xradio_k_spin_lock_init(&queue->lock);
|
|
|
|
queue->pool = xradio_k_zmalloc(sizeof(struct xradio_queue_item) * capacity);
|
|
if (!queue->pool)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < capacity; ++i)
|
|
list_add_tail(&queue->pool[i].head, &queue->free_pool);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xradio_queue_deinit(struct xradio_queue *queue)
|
|
{
|
|
// TODO - xradio_queue_clear(queue, XRWL_ALL_IFS);
|
|
INIT_LIST_HEAD(&queue->free_pool);
|
|
xradio_k_free(queue->pool);
|
|
queue->pool = NULL;
|
|
queue->capacity = 0;
|
|
}
|
|
|
|
int xradio_queue_put(struct xradio_queue *queue, struct sk_buff *skb, u16 seq)
|
|
{
|
|
int ret = -1;
|
|
|
|
xradio_k_spin_lock_bh(&queue->lock);
|
|
|
|
ret = list_empty(&queue->free_pool);
|
|
|
|
if (!ret) {
|
|
struct xradio_queue_item *item =
|
|
list_first_entry(&queue->free_pool, struct xradio_queue_item, head);
|
|
|
|
if (!item) {
|
|
txrx_printk(XRADIO_DBG_ERROR, "push queue,find first entry error\n");
|
|
ret = -ENOMEM; // TODO,check
|
|
goto end;
|
|
}
|
|
|
|
item->skb = skb;
|
|
|
|
item->seq_number = seq;
|
|
|
|
item->queue_timestamp = jiffies;
|
|
|
|
list_move_tail(&item->head, &queue->queue);
|
|
|
|
++item->queue_index;
|
|
|
|
++queue->num_queued;
|
|
|
|
item->queue_index = queue->num_queued;
|
|
|
|
/*Stop enqueuing if the 80% watermark is reached*/
|
|
if (queue->num_queued + queue->num_pending >=
|
|
queue->capacity * 4 / 5) {
|
|
txrx_printk(XRADIO_DBG_MSG, "num_queued:%d,num_pending:%d, cap:%d\n",
|
|
queue->num_queued, queue->num_pending, queue->capacity);
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
end:
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct sk_buff *xradio_queue_get(struct xradio_queue *queue)
|
|
{
|
|
struct xradio_queue_item *item = NULL;
|
|
struct sk_buff *skb = NULL;
|
|
|
|
xradio_k_spin_lock_bh(&queue->lock);
|
|
|
|
if (!list_empty(&queue->queue)) {
|
|
item = list_first_entry(&queue->queue, struct xradio_queue_item, head);
|
|
|
|
if (!item) {
|
|
txrx_printk(XRADIO_DBG_ERROR, "pop queue,find fist entry error\n");
|
|
xradio_queue_debug_info(queue);
|
|
goto end;
|
|
}
|
|
|
|
skb = item->skb;
|
|
|
|
list_move_tail(&item->head, &queue->pending);
|
|
|
|
++queue->num_pending;
|
|
|
|
--queue->num_queued;
|
|
|
|
item->xmit_timestamp = jiffies;
|
|
}
|
|
end:
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
return skb;
|
|
}
|
|
|
|
int xradio_queue_remove(struct xradio_queue *queue, u16 seq_number)
|
|
{
|
|
int ret = -ENOENT;
|
|
struct xradio_queue_item *item;
|
|
|
|
xradio_k_spin_lock_bh(&queue->lock);
|
|
|
|
list_for_each_entry(item, &queue->pending, head) {
|
|
if (item->seq_number == seq_number) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ret) {
|
|
if (queue->free_skb) {
|
|
queue->free_skb(item->skb, __func__);
|
|
item->skb = NULL;
|
|
}
|
|
list_move(&item->head, &queue->free_pool);
|
|
--queue->num_pending;
|
|
txrx_printk(XRADIO_DBG_MSG, "Remove item seq_number%d\n", item->seq_number);
|
|
} else {
|
|
txrx_printk(XRADIO_DBG_ERROR, "item is not found,seq:%d\n", seq_number);
|
|
xradio_queue_debug_info(queue);
|
|
}
|
|
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
return ret;
|
|
}
|
|
|
|
int xradio_queue_multi_remove(struct xradio_queue *queue, u16 seq_number)
|
|
{
|
|
int ret = -ENOENT;
|
|
struct xradio_queue_item *item, *tmp_item;
|
|
|
|
xradio_k_spin_lock_bh(&queue->lock);
|
|
|
|
list_for_each_entry_safe(item, tmp_item, &queue->pending, head) {
|
|
if ((item->seq_number % 255) <= seq_number) {
|
|
if (queue->free_skb) {
|
|
queue->free_skb(item->skb, __func__);
|
|
item->skb = NULL;
|
|
}
|
|
list_move(&item->head, &queue->free_pool);
|
|
--queue->num_pending;
|
|
txrx_printk(XRADIO_DBG_MSG, "Remove item seq_number%d\n", item->seq_number);
|
|
}
|
|
}
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
return ret;
|
|
}
|
|
|
|
int xradio_queue_requeue(struct xradio_queue *queue, uint16_t seq_number)
|
|
{
|
|
struct xradio_queue_item *item;
|
|
int ret = -1;
|
|
|
|
xradio_k_spin_lock_bh(&queue->lock);
|
|
|
|
list_for_each_entry(item, &queue->pending, head) {
|
|
if (item->seq_number == seq_number) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ret) {
|
|
list_move(&item->head, &queue->queue);
|
|
--queue->num_pending;
|
|
txrx_printk(XRADIO_DBG_MSG, "Requeue item seq_number%d\n", item->seq_number);
|
|
}
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
return 0;
|
|
}
|
|
|
|
int xradio_queue_requeue_all(struct xradio_queue *queue, uint16_t seq_number)
|
|
{
|
|
struct xradio_queue_item *item, *tmp_item;
|
|
struct list_head tmp;
|
|
int ret = 0;
|
|
|
|
xradio_k_spin_lock_bh(&queue->lock);
|
|
xradio_queue_debug_info(queue);
|
|
|
|
INIT_LIST_HEAD(&tmp);
|
|
|
|
list_for_each_entry_safe(item, tmp_item, &queue->pending, head) {
|
|
if (item->seq_number < seq_number) {
|
|
if (queue->free_skb) {
|
|
queue->free_skb(item->skb, __func__);
|
|
item->skb = NULL;
|
|
}
|
|
list_move(&item->head, &queue->free_pool);
|
|
--queue->num_pending;
|
|
txrx_printk(XRADIO_DBG_MSG, "Remove item seq_number%d\n", item->seq_number);
|
|
} else {
|
|
list_move(&item->head, &tmp);
|
|
--queue->num_pending;
|
|
ret++;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
list_for_each_entry_safe(item, tmp_item, &tmp, head) {
|
|
item->queue_timestamp = jiffies;
|
|
list_move(&item->head, &queue->queue);
|
|
++queue->num_queued;
|
|
|
|
txrx_printk(XRADIO_DBG_MSG, "Requeue item seq_number%d\n",
|
|
item->seq_number);
|
|
}
|
|
}
|
|
xradio_queue_debug_info(queue);
|
|
xradio_k_spin_unlock_bh(&queue->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xradio_queue_get_pending_num(struct xradio_queue *queue)
|
|
{
|
|
return queue->num_pending;
|
|
}
|
|
|
|
int xradio_queue_get_queue_num(struct xradio_queue *queue)
|
|
{
|
|
return queue->num_pending + queue->num_queued;
|
|
}
|
|
|
|
void xradio_queue_debug_info(struct xradio_queue *queue)
|
|
{
|
|
struct xradio_queue_item *item = NULL;
|
|
int free_pool_cnt = 0;
|
|
|
|
queue_printk(XRADIO_DBG_ALWY, "Queue id :%d\n", queue->queue_id);
|
|
queue_printk(XRADIO_DBG_ALWY, " Capacity :%ld\n", queue->capacity);
|
|
queue_printk(XRADIO_DBG_ALWY, " Queued :%ld\n", queue->num_queued);
|
|
queue_printk(XRADIO_DBG_ALWY, " Pending :%ld\n", queue->num_pending);
|
|
queue_printk(XRADIO_DBG_ALWY, "queued list info\n");
|
|
list_for_each_entry(item, &queue->queue, head) {
|
|
queue_printk(XRADIO_DBG_ALWY, " queued--->seq:%d ,id:%d \n", item->seq_number,
|
|
item->queue_index);
|
|
}
|
|
queue_printk(XRADIO_DBG_ALWY, "pending list info\n");
|
|
list_for_each_entry(item, &queue->pending, head) {
|
|
queue_printk(XRADIO_DBG_ALWY, " pending--->seq:%d ,id:%d skb:%pK\n",
|
|
item->seq_number, item->queue_index, item->skb);
|
|
}
|
|
list_for_each_entry(item, &queue->free_pool, head) { free_pool_cnt++; }
|
|
queue_printk(XRADIO_DBG_ALWY, "pending list node cnt:%d\n", free_pool_cnt);
|
|
}
|