Skip to content

Commit

Permalink
net: Convert GRO SKB handling to list_head.
Browse files Browse the repository at this point in the history
Manage pending per-NAPI GRO packets via list_head.

Return an SKB pointer from the GRO receive handlers.  When GRO receive
handlers return non-NULL, it means that this SKB needs to be completed
at this time and removed from the NAPI queue.

Several operations are greatly simplified by this transformation,
especially timing out the oldest SKB in the list when gro_count
exceeds MAX_GRO_SKBS, and napi_gro_flush() which walks the queue
in reverse order.

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Jun 26, 2018
1 parent 9ff3b40 commit d4546c2
Show file tree
Hide file tree
Showing 24 changed files with 133 additions and 141 deletions.
11 changes: 6 additions & 5 deletions drivers/net/geneve.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,12 @@ static int geneve_hlen(struct genevehdr *gh)
return sizeof(*gh) + gh->opt_len * 4;
}

static struct sk_buff **geneve_gro_receive(struct sock *sk,
struct sk_buff **head,
struct sk_buff *skb)
static struct sk_buff *geneve_gro_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
{
struct sk_buff *p, **pp = NULL;
struct sk_buff *pp = NULL;
struct sk_buff *p;
struct genevehdr *gh, *gh2;
unsigned int hlen, gh_len, off_gnv;
const struct packet_offload *ptype;
Expand All @@ -449,7 +450,7 @@ static struct sk_buff **geneve_gro_receive(struct sock *sk,
goto out;
}

for (p = *head; p; p = p->next) {
list_for_each_entry(p, head, list) {
if (!NAPI_GRO_CB(p)->same_flow)
continue;

Expand Down
11 changes: 6 additions & 5 deletions drivers/net/vxlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,11 +568,12 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
return vh;
}

static struct sk_buff **vxlan_gro_receive(struct sock *sk,
struct sk_buff **head,
struct sk_buff *skb)
static struct sk_buff *vxlan_gro_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
{
struct sk_buff *p, **pp = NULL;
struct sk_buff *pp = NULL;
struct sk_buff *p;
struct vxlanhdr *vh, *vh2;
unsigned int hlen, off_vx;
int flush = 1;
Expand Down Expand Up @@ -607,7 +608,7 @@ static struct sk_buff **vxlan_gro_receive(struct sock *sk,

skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */

for (p = *head; p; p = p->next) {
list_for_each_entry(p, head, list) {
if (!NAPI_GRO_CB(p)->same_flow)
continue;

Expand Down
3 changes: 1 addition & 2 deletions include/linux/etherdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
unsigned int rxqs);
#define devm_alloc_etherdev(dev, sizeof_priv) devm_alloc_etherdev_mqs(dev, sizeof_priv, 1, 1)

struct sk_buff **eth_gro_receive(struct sk_buff **head,
struct sk_buff *skb);
struct sk_buff *eth_gro_receive(struct list_head *head, struct sk_buff *skb);
int eth_gro_complete(struct sk_buff *skb, int nhoff);

/* Reserved Ethernet Addresses per IEEE 802.1Q */
Expand Down
32 changes: 16 additions & 16 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ struct napi_struct {
int poll_owner;
#endif
struct net_device *dev;
struct sk_buff *gro_list;
struct list_head gro_list;
struct sk_buff *skb;
struct hrtimer timer;
struct list_head dev_list;
Expand Down Expand Up @@ -2255,10 +2255,10 @@ static inline int gro_recursion_inc_test(struct sk_buff *skb)
return ++NAPI_GRO_CB(skb)->recursion_counter == GRO_RECURSION_LIMIT;
}

typedef struct sk_buff **(*gro_receive_t)(struct sk_buff **, struct sk_buff *);
static inline struct sk_buff **call_gro_receive(gro_receive_t cb,
struct sk_buff **head,
struct sk_buff *skb)
typedef struct sk_buff *(*gro_receive_t)(struct list_head *, struct sk_buff *);
static inline struct sk_buff *call_gro_receive(gro_receive_t cb,
struct list_head *head,
struct sk_buff *skb)
{
if (unlikely(gro_recursion_inc_test(skb))) {
NAPI_GRO_CB(skb)->flush |= 1;
Expand All @@ -2268,12 +2268,12 @@ static inline struct sk_buff **call_gro_receive(gro_receive_t cb,
return cb(head, skb);
}

typedef struct sk_buff **(*gro_receive_sk_t)(struct sock *, struct sk_buff **,
struct sk_buff *);
static inline struct sk_buff **call_gro_receive_sk(gro_receive_sk_t cb,
struct sock *sk,
struct sk_buff **head,
struct sk_buff *skb)
typedef struct sk_buff *(*gro_receive_sk_t)(struct sock *, struct list_head *,
struct sk_buff *);
static inline struct sk_buff *call_gro_receive_sk(gro_receive_sk_t cb,
struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
{
if (unlikely(gro_recursion_inc_test(skb))) {
NAPI_GRO_CB(skb)->flush |= 1;
Expand All @@ -2299,8 +2299,8 @@ struct packet_type {
struct offload_callbacks {
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
netdev_features_t features);
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
struct sk_buff *(*gro_receive)(struct list_head *head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb, int nhoff);
};

Expand Down Expand Up @@ -2568,7 +2568,7 @@ struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
struct net_device *dev_get_by_napi_id(unsigned int napi_id);
int netdev_get_name(struct net *net, char *name, int ifindex);
int dev_restart(struct net_device *dev);
int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb);
int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);

static inline unsigned int skb_gro_offset(const struct sk_buff *skb)
{
Expand Down Expand Up @@ -2784,13 +2784,13 @@ static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
}

#ifdef CONFIG_XFRM_OFFLOAD
static inline void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff **pp, int flush)
static inline void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff *pp, int flush)
{
if (PTR_ERR(pp) != -EINPROGRESS)
NAPI_GRO_CB(skb)->flush |= flush;
}
#else
static inline void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff **pp, int flush)
static inline void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff *pp, int flush)
{
NAPI_GRO_CB(skb)->flush |= flush;
}
Expand Down
3 changes: 2 additions & 1 deletion include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,8 @@ struct sk_buff {
int ip_defrag_offset;
};
};
struct rb_node rbnode; /* used in netem & tcp stack */
struct rb_node rbnode; /* used in netem & tcp stack */
struct list_head list;
};
struct sock *sk;

Expand Down
4 changes: 2 additions & 2 deletions include/linux/udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ struct udp_sock {
void (*encap_destroy)(struct sock *sk);

/* GRO functions for UDP socket */
struct sk_buff ** (*gro_receive)(struct sock *sk,
struct sk_buff **head,
struct sk_buff * (*gro_receive)(struct sock *sk,
struct list_head *head,
struct sk_buff *skb);
int (*gro_complete)(struct sock *sk,
struct sk_buff *skb,
Expand Down
2 changes: 1 addition & 1 deletion include/net/inet_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family,
int inet_recv_error(struct sock *sk, struct msghdr *msg, int len,
int *addr_len);

struct sk_buff **inet_gro_receive(struct sk_buff **head, struct sk_buff *skb);
struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb);
int inet_gro_complete(struct sk_buff *skb, int nhoff);
struct sk_buff *inet_gso_segment(struct sk_buff *skb,
netdev_features_t features);
Expand Down
2 changes: 1 addition & 1 deletion include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1788,7 +1788,7 @@ void tcp_v4_destroy_sock(struct sock *sk);

struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
netdev_features_t features);
struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb);
struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
int tcp_gro_complete(struct sk_buff *skb);

void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr);
Expand Down
4 changes: 2 additions & 2 deletions include/net/udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ static inline void udp_csum_pull_header(struct sk_buff *skb)
typedef struct sock *(*udp_lookup_t)(struct sk_buff *skb, __be16 sport,
__be16 dport);

struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
struct udphdr *uh, udp_lookup_t lookup);
struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
struct udphdr *uh, udp_lookup_t lookup);
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);

struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
Expand Down
6 changes: 3 additions & 3 deletions include/net/udp_tunnel.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ static inline int udp_sock_create(struct net *net,

typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb);
typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk);
typedef struct sk_buff **(*udp_tunnel_gro_receive_t)(struct sock *sk,
struct sk_buff **head,
struct sk_buff *skb);
typedef struct sk_buff *(*udp_tunnel_gro_receive_t)(struct sock *sk,
struct list_head *head,
struct sk_buff *skb);
typedef int (*udp_tunnel_gro_complete_t)(struct sock *sk, struct sk_buff *skb,
int nhoff);

Expand Down
13 changes: 7 additions & 6 deletions net/8021q/vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,13 +647,14 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
return err;
}

static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
static struct sk_buff *vlan_gro_receive(struct list_head *head,
struct sk_buff *skb)
{
struct sk_buff *p, **pp = NULL;
struct vlan_hdr *vhdr;
unsigned int hlen, off_vlan;
const struct packet_offload *ptype;
unsigned int hlen, off_vlan;
struct sk_buff *pp = NULL;
struct vlan_hdr *vhdr;
struct sk_buff *p;
__be16 type;
int flush = 1;

Expand All @@ -675,7 +676,7 @@ static struct sk_buff **vlan_gro_receive(struct sk_buff **head,

flush = 0;

for (p = *head; p; p = p->next) {
list_for_each_entry(p, head, list) {
struct vlan_hdr *vhdr2;

if (!NAPI_GRO_CB(p)->same_flow)
Expand Down
68 changes: 28 additions & 40 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -4881,36 +4881,25 @@ static int napi_gro_complete(struct sk_buff *skb)
*/
void napi_gro_flush(struct napi_struct *napi, bool flush_old)
{
struct sk_buff *skb, *prev = NULL;

/* scan list and build reverse chain */
for (skb = napi->gro_list; skb != NULL; skb = skb->next) {
skb->prev = prev;
prev = skb;
}

for (skb = prev; skb; skb = prev) {
skb->next = NULL;
struct sk_buff *skb, *p;

list_for_each_entry_safe_reverse(skb, p, &napi->gro_list, list) {
if (flush_old && NAPI_GRO_CB(skb)->age == jiffies)
return;

prev = skb->prev;
list_del_init(&skb->list);
napi_gro_complete(skb);
napi->gro_count--;
}

napi->gro_list = NULL;
}
EXPORT_SYMBOL(napi_gro_flush);

static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff *p;
unsigned int maclen = skb->dev->hard_header_len;
u32 hash = skb_get_hash_raw(skb);
struct sk_buff *p;

for (p = napi->gro_list; p; p = p->next) {
list_for_each_entry(p, &napi->gro_list, list) {
unsigned long diffs;

NAPI_GRO_CB(p)->flush = 0;
Expand Down Expand Up @@ -4977,12 +4966,12 @@ static void gro_pull_from_frag0(struct sk_buff *skb, int grow)

static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff **pp = NULL;
struct list_head *head = &offload_base;
struct packet_offload *ptype;
__be16 type = skb->protocol;
struct list_head *head = &offload_base;
int same_flow;
struct sk_buff *pp = NULL;
enum gro_result ret;
int same_flow;
int grow;

if (netif_elide_gro(skb->dev))
Expand Down Expand Up @@ -5039,11 +5028,8 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE : GRO_MERGED;

if (pp) {
struct sk_buff *nskb = *pp;

*pp = nskb->next;
nskb->next = NULL;
napi_gro_complete(nskb);
list_del_init(&pp->list);
napi_gro_complete(pp);
napi->gro_count--;
}

Expand All @@ -5054,15 +5040,10 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
goto normal;

if (unlikely(napi->gro_count >= MAX_GRO_SKBS)) {
struct sk_buff *nskb = napi->gro_list;
struct sk_buff *nskb;

/* locate the end of the list to select the 'oldest' flow */
while (nskb->next) {
pp = &nskb->next;
nskb = *pp;
}
*pp = NULL;
nskb->next = NULL;
nskb = list_last_entry(&napi->gro_list, struct sk_buff, list);
list_del(&nskb->list);
napi_gro_complete(nskb);
} else {
napi->gro_count++;
Expand All @@ -5071,8 +5052,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
NAPI_GRO_CB(skb)->age = jiffies;
NAPI_GRO_CB(skb)->last = skb;
skb_shinfo(skb)->gso_size = skb_gro_len(skb);
skb->next = napi->gro_list;
napi->gro_list = skb;
list_add(&skb->list, &napi->gro_list);
ret = GRO_HELD;

pull:
Expand Down Expand Up @@ -5478,7 +5458,7 @@ bool napi_complete_done(struct napi_struct *n, int work_done)
NAPIF_STATE_IN_BUSY_POLL)))
return false;

if (n->gro_list) {
if (!list_empty(&n->gro_list)) {
unsigned long timeout = 0;

if (work_done)
Expand Down Expand Up @@ -5687,7 +5667,7 @@ static enum hrtimer_restart napi_watchdog(struct hrtimer *timer)
/* Note : we use a relaxed variant of napi_schedule_prep() not setting
* NAPI_STATE_MISSED, since we do not react to a device IRQ.
*/
if (napi->gro_list && !napi_disable_pending(napi) &&
if (!list_empty(&napi->gro_list) && !napi_disable_pending(napi) &&
!test_and_set_bit(NAPI_STATE_SCHED, &napi->state))
__napi_schedule_irqoff(napi);

Expand All @@ -5701,7 +5681,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
napi->timer.function = napi_watchdog;
napi->gro_count = 0;
napi->gro_list = NULL;
INIT_LIST_HEAD(&napi->gro_list);
napi->skb = NULL;
napi->poll = poll;
if (weight > NAPI_POLL_WEIGHT)
Expand Down Expand Up @@ -5734,6 +5714,14 @@ void napi_disable(struct napi_struct *n)
}
EXPORT_SYMBOL(napi_disable);

static void gro_list_free(struct list_head *head)
{
struct sk_buff *skb, *p;

list_for_each_entry_safe(skb, p, head, list)
kfree_skb(skb);
}

/* Must be called in process context */
void netif_napi_del(struct napi_struct *napi)
{
Expand All @@ -5743,8 +5731,8 @@ void netif_napi_del(struct napi_struct *napi)
list_del_init(&napi->dev_list);
napi_free_frags(napi);

kfree_skb_list(napi->gro_list);
napi->gro_list = NULL;
gro_list_free(&napi->gro_list);
INIT_LIST_HEAD(&napi->gro_list);
napi->gro_count = 0;
}
EXPORT_SYMBOL(netif_napi_del);
Expand Down Expand Up @@ -5787,7 +5775,7 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll)
goto out_unlock;
}

if (n->gro_list) {
if (!list_empty(&n->gro_list)) {
/* flush too old packets
* If HZ < 1000, flush all packets.
*/
Expand Down
Loading

0 comments on commit d4546c2

Please sign in to comment.