Skip to content

Commit

Permalink
r8152: replace array with linking list for rx information
Browse files Browse the repository at this point in the history
The original method uses an array to store the rx information. The
new one uses a list to link each rx structure. Then, it is possible
to increase/decrease the number of rx structure dynamically.

Signed-off-by: Hayes Wang <hayeswang@realtek.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
  • Loading branch information
Hayes Wang authored and Jakub Kicinski committed Aug 14, 2019
1 parent ec5791c commit 252df8b
Showing 1 changed file with 125 additions and 57 deletions.
182 changes: 125 additions & 57 deletions drivers/net/usb/r8152.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/mdio.h>
#include <linux/usb/cdc.h>
#include <linux/suspend.h>
#include <linux/atomic.h>
#include <linux/acpi.h>

/* Information for net-next */
Expand Down Expand Up @@ -694,7 +695,7 @@ struct tx_desc {
struct r8152;

struct rx_agg {
struct list_head list;
struct list_head list, info_list;
struct urb *urb;
struct r8152 *context;
void *buffer;
Expand All @@ -719,7 +720,7 @@ struct r8152 {
struct net_device *netdev;
struct urb *intr_urb;
struct tx_agg tx_info[RTL8152_MAX_TX];
struct rx_agg rx_info[RTL8152_MAX_RX];
struct list_head rx_info;
struct list_head rx_done, tx_free;
struct sk_buff_head tx_queue, rx_queue;
spinlock_t rx_lock, tx_lock;
Expand All @@ -744,6 +745,8 @@ struct r8152 {
void (*autosuspend_en)(struct r8152 *tp, bool enable);
} rtl_ops;

atomic_t rx_count;

int intr_interval;
u32 saved_wolopts;
u32 msg_enable;
Expand Down Expand Up @@ -1468,18 +1471,81 @@ static inline void *tx_agg_align(void *data)
return (void *)ALIGN((uintptr_t)data, TX_ALIGN);
}

static void free_rx_agg(struct r8152 *tp, struct rx_agg *agg)
{
list_del(&agg->info_list);

usb_free_urb(agg->urb);
kfree(agg->buffer);
kfree(agg);

atomic_dec(&tp->rx_count);
}

static struct rx_agg *alloc_rx_agg(struct r8152 *tp, gfp_t mflags)
{
struct net_device *netdev = tp->netdev;
int node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1;
struct rx_agg *rx_agg;
unsigned long flags;
u8 *buf;

rx_agg = kmalloc_node(sizeof(*rx_agg), mflags, node);
if (!rx_agg)
return NULL;

buf = kmalloc_node(tp->rx_buf_sz, mflags, node);
if (!buf)
goto free_rx;

if (buf != rx_agg_align(buf)) {
kfree(buf);
buf = kmalloc_node(tp->rx_buf_sz + RX_ALIGN, mflags,
node);
if (!buf)
goto free_rx;
}

rx_agg->buffer = buf;
rx_agg->head = rx_agg_align(buf);

rx_agg->urb = usb_alloc_urb(0, mflags);
if (!rx_agg->urb)
goto free_buf;

rx_agg->context = tp;

INIT_LIST_HEAD(&rx_agg->list);
INIT_LIST_HEAD(&rx_agg->info_list);
spin_lock_irqsave(&tp->rx_lock, flags);
list_add_tail(&rx_agg->info_list, &tp->rx_info);
spin_unlock_irqrestore(&tp->rx_lock, flags);

atomic_inc(&tp->rx_count);

return rx_agg;

free_buf:
kfree(rx_agg->buffer);
free_rx:
kfree(rx_agg);
return NULL;
}

static void free_all_mem(struct r8152 *tp)
{
struct rx_agg *agg, *agg_next;
unsigned long flags;
int i;

for (i = 0; i < RTL8152_MAX_RX; i++) {
usb_free_urb(tp->rx_info[i].urb);
tp->rx_info[i].urb = NULL;
spin_lock_irqsave(&tp->rx_lock, flags);

kfree(tp->rx_info[i].buffer);
tp->rx_info[i].buffer = NULL;
tp->rx_info[i].head = NULL;
}
list_for_each_entry_safe(agg, agg_next, &tp->rx_info, info_list)
free_rx_agg(tp, agg);

spin_unlock_irqrestore(&tp->rx_lock, flags);

WARN_ON(atomic_read(&tp->rx_count));

for (i = 0; i < RTL8152_MAX_TX; i++) {
usb_free_urb(tp->tx_info[i].urb);
Expand All @@ -1503,46 +1569,28 @@ static int alloc_all_mem(struct r8152 *tp)
struct usb_interface *intf = tp->intf;
struct usb_host_interface *alt = intf->cur_altsetting;
struct usb_host_endpoint *ep_intr = alt->endpoint + 2;
struct urb *urb;
int node, i;
u8 *buf;

node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1;

spin_lock_init(&tp->rx_lock);
spin_lock_init(&tp->tx_lock);
INIT_LIST_HEAD(&tp->rx_info);
INIT_LIST_HEAD(&tp->tx_free);
INIT_LIST_HEAD(&tp->rx_done);
skb_queue_head_init(&tp->tx_queue);
skb_queue_head_init(&tp->rx_queue);
atomic_set(&tp->rx_count, 0);

for (i = 0; i < RTL8152_MAX_RX; i++) {
buf = kmalloc_node(tp->rx_buf_sz, GFP_KERNEL, node);
if (!buf)
if (!alloc_rx_agg(tp, GFP_KERNEL))
goto err1;

if (buf != rx_agg_align(buf)) {
kfree(buf);
buf = kmalloc_node(tp->rx_buf_sz + RX_ALIGN, GFP_KERNEL,
node);
if (!buf)
goto err1;
}

urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
kfree(buf);
goto err1;
}

INIT_LIST_HEAD(&tp->rx_info[i].list);
tp->rx_info[i].context = tp;
tp->rx_info[i].urb = urb;
tp->rx_info[i].buffer = buf;
tp->rx_info[i].head = rx_agg_align(buf);
}

for (i = 0; i < RTL8152_MAX_TX; i++) {
struct urb *urb;
u8 *buf;

buf = kmalloc_node(agg_buf_sz, GFP_KERNEL, node);
if (!buf)
goto err1;
Expand Down Expand Up @@ -2331,44 +2379,64 @@ static void rxdy_gated_en(struct r8152 *tp, bool enable)

static int rtl_start_rx(struct r8152 *tp)
{
int i, ret = 0;
struct rx_agg *agg, *agg_next;
struct list_head tmp_list;
unsigned long flags;
int ret = 0;

INIT_LIST_HEAD(&tp->rx_done);
for (i = 0; i < RTL8152_MAX_RX; i++) {
INIT_LIST_HEAD(&tp->rx_info[i].list);
ret = r8152_submit_rx(tp, &tp->rx_info[i], GFP_KERNEL);
if (ret)
break;
}
INIT_LIST_HEAD(&tmp_list);

if (ret && ++i < RTL8152_MAX_RX) {
struct list_head rx_queue;
unsigned long flags;
spin_lock_irqsave(&tp->rx_lock, flags);

INIT_LIST_HEAD(&rx_queue);
INIT_LIST_HEAD(&tp->rx_done);

do {
struct rx_agg *agg = &tp->rx_info[i++];
struct urb *urb = agg->urb;
list_splice_init(&tp->rx_info, &tmp_list);

urb->actual_length = 0;
list_add_tail(&agg->list, &rx_queue);
} while (i < RTL8152_MAX_RX);
spin_unlock_irqrestore(&tp->rx_lock, flags);

spin_lock_irqsave(&tp->rx_lock, flags);
list_splice_tail(&rx_queue, &tp->rx_done);
spin_unlock_irqrestore(&tp->rx_lock, flags);
list_for_each_entry_safe(agg, agg_next, &tmp_list, info_list) {
INIT_LIST_HEAD(&agg->list);

if (ret < 0)
list_add_tail(&agg->list, &tp->rx_done);
else
ret = r8152_submit_rx(tp, agg, GFP_KERNEL);
}

spin_lock_irqsave(&tp->rx_lock, flags);
WARN_ON(!list_empty(&tp->rx_info));
list_splice(&tmp_list, &tp->rx_info);
spin_unlock_irqrestore(&tp->rx_lock, flags);

return ret;
}

static int rtl_stop_rx(struct r8152 *tp)
{
int i;
struct rx_agg *agg, *agg_next;
struct list_head tmp_list;
unsigned long flags;

INIT_LIST_HEAD(&tmp_list);

/* The usb_kill_urb() couldn't be used in atomic.
* Therefore, move the list of rx_info to a tmp one.
* Then, list_for_each_entry_safe could be used without
* spin lock.
*/

spin_lock_irqsave(&tp->rx_lock, flags);
list_splice_init(&tp->rx_info, &tmp_list);
spin_unlock_irqrestore(&tp->rx_lock, flags);

list_for_each_entry_safe(agg, agg_next, &tmp_list, info_list)
usb_kill_urb(agg->urb);

for (i = 0; i < RTL8152_MAX_RX; i++)
usb_kill_urb(tp->rx_info[i].urb);
/* Move back the list of temp to the rx_info */
spin_lock_irqsave(&tp->rx_lock, flags);
WARN_ON(!list_empty(&tp->rx_info));
list_splice(&tmp_list, &tp->rx_info);
spin_unlock_irqrestore(&tp->rx_lock, flags);

while (!skb_queue_empty(&tp->rx_queue))
dev_kfree_skb(__skb_dequeue(&tp->rx_queue));
Expand Down

0 comments on commit 252df8b

Please sign in to comment.