| 1 | |
| 2 | #include <linux/skbuff.h>
|
| 3 | #include <linux/slab.h>
|
| 4 | #include <linux/netdevice.h>
|
| 5 | #include <net/gro_cells.h>
|
| 6 | #include <net/hotdata.h>
|
| 7 |
|
| 8 | struct gro_cell {
|
| 9 | struct sk_buff_head napi_skbs;
|
| 10 | struct napi_struct napi;
|
| 11 | local_lock_t bh_lock;
|
| 12 | };
|
| 13 |
|
| 14 | int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
|
| 15 | {
|
| 16 | struct net_device *dev = skb->dev;
|
| 17 | bool have_bh_lock = false;
|
| 18 | struct gro_cell *cell;
|
| 19 | int res;
|
| 20 |
|
| 21 | rcu_read_lock();
|
| 22 | if (unlikely(!(dev->flags & IFF_UP)))
|
| 23 | goto drop;
|
| 24 |
|
| 25 | skb_unset_transport_header(skb);
|
| 26 |
|
| 27 | if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
|
| 28 | res = netif_rx(skb);
|
| 29 | goto unlock;
|
| 30 | }
|
| 31 |
|
| 32 | local_lock_nested_bh(&gcells->cells->bh_lock);
|
| 33 | have_bh_lock = true;
|
| 34 | cell = this_cpu_ptr(gcells->cells);
|
| 35 |
|
| 36 | if (skb_queue_len(&cell->napi_skbs) > READ_ONCE(net_hotdata.max_backlog)) {
|
| 37 | drop:
|
| 38 | dev_core_stats_rx_dropped_inc(dev);
|
| 39 | kfree_skb(skb);
|
| 40 | res = NET_RX_DROP;
|
| 41 | goto unlock;
|
| 42 | }
|
| 43 |
|
| 44 | __skb_queue_tail(&cell->napi_skbs, skb);
|
| 45 | if (skb_queue_len(&cell->napi_skbs) == 1)
|
| 46 | napi_schedule(&cell->napi);
|
| 47 |
|
| 48 | res = NET_RX_SUCCESS;
|
| 49 |
|
| 50 | unlock:
|
| 51 | if (have_bh_lock)
|
| 52 | local_unlock_nested_bh(&gcells->cells->bh_lock);
|
| 53 | rcu_read_unlock();
|
| 54 | return res;
|
| 55 | }
|
| 56 | EXPORT_SYMBOL(gro_cells_receive);
|
| 57 |
|
| 58 |
|
| 59 | static int gro_cell_poll(struct napi_struct *napi, int budget)
|
| 60 | {
|
| 61 | struct gro_cell *cell = container_of(napi, struct gro_cell, napi);
|
| 62 | struct sk_buff *skb;
|
| 63 | int work_done = 0;
|
| 64 |
|
| 65 | while (work_done < budget) {
|
| 66 | __local_lock_nested_bh(&cell->bh_lock);
|
| 67 | skb = __skb_dequeue(&cell->napi_skbs);
|
| 68 | __local_unlock_nested_bh(&cell->bh_lock);
|
| 69 | if (!skb)
|
| 70 | break;
|
| 71 | napi_gro_receive(napi, skb);
|
| 72 | work_done++;
|
| 73 | }
|
| 74 |
|
| 75 | if (work_done < budget)
|
| 76 | napi_complete_done(napi, work_done);
|
| 77 | return work_done;
|
| 78 | }
|
| 79 |
|
| 80 | int gro_cells_init(struct gro_cells *gcells, struct net_device *dev)
|
| 81 | {
|
| 82 | int i;
|
| 83 |
|
| 84 | gcells->cells = alloc_percpu(struct gro_cell);
|
| 85 | if (!gcells->cells)
|
| 86 | return -ENOMEM;
|
| 87 |
|
| 88 | for_each_possible_cpu(i) {
|
| 89 | struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
|
| 90 |
|
| 91 | __skb_queue_head_init(&cell->napi_skbs);
|
| 92 | local_lock_init(&cell->bh_lock);
|
| 93 |
|
| 94 | set_bit(NAPI_STATE_NO_BUSY_POLL, &cell->napi.state);
|
| 95 |
|
| 96 | netif_napi_add(dev, &cell->napi, gro_cell_poll);
|
| 97 | napi_enable(&cell->napi);
|
| 98 | }
|
| 99 | return 0;
|
| 100 | }
|
| 101 | EXPORT_SYMBOL(gro_cells_init);
|
| 102 |
|
| 103 | struct percpu_free_defer {
|
| 104 | struct rcu_head rcu;
|
| 105 | void __percpu *ptr;
|
| 106 | };
|
| 107 |
|
| 108 | static void percpu_free_defer_callback(struct rcu_head *head)
|
| 109 | {
|
| 110 | struct percpu_free_defer *defer;
|
| 111 |
|
| 112 | defer = container_of(head, struct percpu_free_defer, rcu);
|
| 113 | free_percpu(defer->ptr);
|
| 114 | kfree(defer);
|
| 115 | }
|
| 116 |
|
| 117 | void gro_cells_destroy(struct gro_cells *gcells)
|
| 118 | {
|
| 119 | struct percpu_free_defer *defer;
|
| 120 | int i;
|
| 121 |
|
| 122 | if (!gcells->cells)
|
| 123 | return;
|
| 124 | for_each_possible_cpu(i) {
|
| 125 | struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
|
| 126 |
|
| 127 | napi_disable(&cell->napi);
|
| 128 | __netif_napi_del(&cell->napi);
|
| 129 | __skb_queue_purge(&cell->napi_skbs);
|
| 130 | }
|
| 131 | |
| 132 | * because netpoll could access dev->napi_list under rcu protection.
|
| 133 | * Try hard using call_rcu() instead of synchronize_rcu(),
|
| 134 | * because we might be called from cleanup_net(), and we
|
| 135 | * definitely do not want to block this critical task.
|
| 136 |
|
| 137 | defer = kmalloc_obj(*defer, GFP_KERNEL | __GFP_NOWARN);
|
| 138 | if (likely(defer)) {
|
| 139 | defer->ptr = gcells->cells;
|
| 140 | call_rcu(&defer->rcu, percpu_free_defer_callback);
|
| 141 | } else {
|
| 142 | |
| 143 | * would not be able to expedite this sync.
|
| 144 |
|
| 145 | synchronize_rcu_expedited();
|
| 146 | free_percpu(gcells->cells);
|
| 147 | }
|
| 148 | gcells->cells = NULL;
|
| 149 | }
|
| 150 | EXPORT_SYMBOL(gro_cells_destroy);
|