162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci#include <net/gro.h> 362306a36Sopenharmony_ci#include <net/dst_metadata.h> 462306a36Sopenharmony_ci#include <net/busy_poll.h> 562306a36Sopenharmony_ci#include <trace/events/net.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define MAX_GRO_SKBS 8 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* This should be increased if a protocol with a bigger head is added. */ 1062306a36Sopenharmony_ci#define GRO_MAX_HEAD (MAX_HEADER + 128) 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(offload_lock); 1362306a36Sopenharmony_cistruct list_head offload_base __read_mostly = LIST_HEAD_INIT(offload_base); 1462306a36Sopenharmony_ci/* Maximum number of GRO_NORMAL skbs to batch up for list-RX */ 1562306a36Sopenharmony_ciint gro_normal_batch __read_mostly = 8; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * dev_add_offload - register offload handlers 1962306a36Sopenharmony_ci * @po: protocol offload declaration 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Add protocol offload handlers to the networking stack. The passed 2262306a36Sopenharmony_ci * &proto_offload is linked into kernel lists and may not be freed until 2362306a36Sopenharmony_ci * it has been removed from the kernel lists. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * This call does not sleep therefore it can not 2662306a36Sopenharmony_ci * guarantee all CPU's that are in middle of receiving packets 2762306a36Sopenharmony_ci * will see the new offload handlers (until the next received packet). 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_civoid dev_add_offload(struct packet_offload *po) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct packet_offload *elem; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci spin_lock(&offload_lock); 3462306a36Sopenharmony_ci list_for_each_entry(elem, &offload_base, list) { 3562306a36Sopenharmony_ci if (po->priority < elem->priority) 3662306a36Sopenharmony_ci break; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci list_add_rcu(&po->list, elem->list.prev); 3962306a36Sopenharmony_ci spin_unlock(&offload_lock); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ciEXPORT_SYMBOL(dev_add_offload); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * __dev_remove_offload - remove offload handler 4562306a36Sopenharmony_ci * @po: packet offload declaration 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Remove a protocol offload handler that was previously added to the 4862306a36Sopenharmony_ci * kernel offload handlers by dev_add_offload(). The passed &offload_type 4962306a36Sopenharmony_ci * is removed from the kernel lists and can be freed or reused once this 5062306a36Sopenharmony_ci * function returns. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * The packet type might still be in use by receivers 5362306a36Sopenharmony_ci * and must not be freed until after all the CPU's have gone 5462306a36Sopenharmony_ci * through a quiescent state. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistatic void __dev_remove_offload(struct packet_offload *po) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct list_head *head = &offload_base; 5962306a36Sopenharmony_ci struct packet_offload *po1; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci spin_lock(&offload_lock); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci list_for_each_entry(po1, head, list) { 6462306a36Sopenharmony_ci if (po == po1) { 6562306a36Sopenharmony_ci list_del_rcu(&po->list); 6662306a36Sopenharmony_ci goto out; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci pr_warn("dev_remove_offload: %p not found\n", po); 7162306a36Sopenharmony_ciout: 7262306a36Sopenharmony_ci spin_unlock(&offload_lock); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * dev_remove_offload - remove packet offload handler 7762306a36Sopenharmony_ci * @po: packet offload declaration 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Remove a packet offload handler that was previously added to the kernel 8062306a36Sopenharmony_ci * offload handlers by dev_add_offload(). The passed &offload_type is 8162306a36Sopenharmony_ci * removed from the kernel lists and can be freed or reused once this 8262306a36Sopenharmony_ci * function returns. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * This call sleeps to guarantee that no CPU is looking at the packet 8562306a36Sopenharmony_ci * type after return. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_civoid dev_remove_offload(struct packet_offload *po) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci __dev_remove_offload(po); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci synchronize_net(); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ciEXPORT_SYMBOL(dev_remove_offload); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciint skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct skb_shared_info *pinfo, *skbinfo = skb_shinfo(skb); 9962306a36Sopenharmony_ci unsigned int offset = skb_gro_offset(skb); 10062306a36Sopenharmony_ci unsigned int headlen = skb_headlen(skb); 10162306a36Sopenharmony_ci unsigned int len = skb_gro_len(skb); 10262306a36Sopenharmony_ci unsigned int delta_truesize; 10362306a36Sopenharmony_ci unsigned int gro_max_size; 10462306a36Sopenharmony_ci unsigned int new_truesize; 10562306a36Sopenharmony_ci struct sk_buff *lp; 10662306a36Sopenharmony_ci int segs; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Do not splice page pool based packets w/ non-page pool 10962306a36Sopenharmony_ci * packets. This can result in reference count issues as page 11062306a36Sopenharmony_ci * pool pages will not decrement the reference count and will 11162306a36Sopenharmony_ci * instead be immediately returned to the pool or have frag 11262306a36Sopenharmony_ci * count decremented. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if (p->pp_recycle != skb->pp_recycle) 11562306a36Sopenharmony_ci return -ETOOMANYREFS; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* pairs with WRITE_ONCE() in netif_set_gro(_ipv4)_max_size() */ 11862306a36Sopenharmony_ci gro_max_size = p->protocol == htons(ETH_P_IPV6) ? 11962306a36Sopenharmony_ci READ_ONCE(p->dev->gro_max_size) : 12062306a36Sopenharmony_ci READ_ONCE(p->dev->gro_ipv4_max_size); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (unlikely(p->len + len >= gro_max_size || NAPI_GRO_CB(skb)->flush)) 12362306a36Sopenharmony_ci return -E2BIG; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (unlikely(p->len + len >= GRO_LEGACY_MAX_SIZE)) { 12662306a36Sopenharmony_ci if (NAPI_GRO_CB(skb)->proto != IPPROTO_TCP || 12762306a36Sopenharmony_ci (p->protocol == htons(ETH_P_IPV6) && 12862306a36Sopenharmony_ci skb_headroom(p) < sizeof(struct hop_jumbo_hdr)) || 12962306a36Sopenharmony_ci p->encapsulation) 13062306a36Sopenharmony_ci return -E2BIG; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci segs = NAPI_GRO_CB(skb)->count; 13462306a36Sopenharmony_ci lp = NAPI_GRO_CB(p)->last; 13562306a36Sopenharmony_ci pinfo = skb_shinfo(lp); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (headlen <= offset) { 13862306a36Sopenharmony_ci skb_frag_t *frag; 13962306a36Sopenharmony_ci skb_frag_t *frag2; 14062306a36Sopenharmony_ci int i = skbinfo->nr_frags; 14162306a36Sopenharmony_ci int nr_frags = pinfo->nr_frags + i; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (nr_frags > MAX_SKB_FRAGS) 14462306a36Sopenharmony_ci goto merge; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci offset -= headlen; 14762306a36Sopenharmony_ci pinfo->nr_frags = nr_frags; 14862306a36Sopenharmony_ci skbinfo->nr_frags = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci frag = pinfo->frags + nr_frags; 15162306a36Sopenharmony_ci frag2 = skbinfo->frags + i; 15262306a36Sopenharmony_ci do { 15362306a36Sopenharmony_ci *--frag = *--frag2; 15462306a36Sopenharmony_ci } while (--i); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci skb_frag_off_add(frag, offset); 15762306a36Sopenharmony_ci skb_frag_size_sub(frag, offset); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* all fragments truesize : remove (head size + sk_buff) */ 16062306a36Sopenharmony_ci new_truesize = SKB_TRUESIZE(skb_end_offset(skb)); 16162306a36Sopenharmony_ci delta_truesize = skb->truesize - new_truesize; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci skb->truesize = new_truesize; 16462306a36Sopenharmony_ci skb->len -= skb->data_len; 16562306a36Sopenharmony_ci skb->data_len = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE; 16862306a36Sopenharmony_ci goto done; 16962306a36Sopenharmony_ci } else if (skb->head_frag) { 17062306a36Sopenharmony_ci int nr_frags = pinfo->nr_frags; 17162306a36Sopenharmony_ci skb_frag_t *frag = pinfo->frags + nr_frags; 17262306a36Sopenharmony_ci struct page *page = virt_to_head_page(skb->head); 17362306a36Sopenharmony_ci unsigned int first_size = headlen - offset; 17462306a36Sopenharmony_ci unsigned int first_offset; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS) 17762306a36Sopenharmony_ci goto merge; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci first_offset = skb->data - 18062306a36Sopenharmony_ci (unsigned char *)page_address(page) + 18162306a36Sopenharmony_ci offset; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci pinfo->nr_frags = nr_frags + 1 + skbinfo->nr_frags; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci skb_frag_fill_page_desc(frag, page, first_offset, first_size); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags); 18862306a36Sopenharmony_ci /* We dont need to clear skbinfo->nr_frags here */ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci new_truesize = SKB_DATA_ALIGN(sizeof(struct sk_buff)); 19162306a36Sopenharmony_ci delta_truesize = skb->truesize - new_truesize; 19262306a36Sopenharmony_ci skb->truesize = new_truesize; 19362306a36Sopenharmony_ci NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD; 19462306a36Sopenharmony_ci goto done; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cimerge: 19862306a36Sopenharmony_ci /* sk owenrship - if any - completely transferred to the aggregated packet */ 19962306a36Sopenharmony_ci skb->destructor = NULL; 20062306a36Sopenharmony_ci delta_truesize = skb->truesize; 20162306a36Sopenharmony_ci if (offset > headlen) { 20262306a36Sopenharmony_ci unsigned int eat = offset - headlen; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci skb_frag_off_add(&skbinfo->frags[0], eat); 20562306a36Sopenharmony_ci skb_frag_size_sub(&skbinfo->frags[0], eat); 20662306a36Sopenharmony_ci skb->data_len -= eat; 20762306a36Sopenharmony_ci skb->len -= eat; 20862306a36Sopenharmony_ci offset = headlen; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci __skb_pull(skb, offset); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (NAPI_GRO_CB(p)->last == p) 21462306a36Sopenharmony_ci skb_shinfo(p)->frag_list = skb; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci NAPI_GRO_CB(p)->last->next = skb; 21762306a36Sopenharmony_ci NAPI_GRO_CB(p)->last = skb; 21862306a36Sopenharmony_ci __skb_header_release(skb); 21962306a36Sopenharmony_ci lp = p; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cidone: 22262306a36Sopenharmony_ci NAPI_GRO_CB(p)->count += segs; 22362306a36Sopenharmony_ci p->data_len += len; 22462306a36Sopenharmony_ci p->truesize += delta_truesize; 22562306a36Sopenharmony_ci p->len += len; 22662306a36Sopenharmony_ci if (lp != p) { 22762306a36Sopenharmony_ci lp->data_len += len; 22862306a36Sopenharmony_ci lp->truesize += delta_truesize; 22962306a36Sopenharmony_ci lp->len += len; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci NAPI_GRO_CB(skb)->same_flow = 1; 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct packet_offload *ptype; 23962306a36Sopenharmony_ci __be16 type = skb->protocol; 24062306a36Sopenharmony_ci struct list_head *head = &offload_base; 24162306a36Sopenharmony_ci int err = -ENOENT; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct napi_gro_cb) > sizeof(skb->cb)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (NAPI_GRO_CB(skb)->count == 1) { 24662306a36Sopenharmony_ci skb_shinfo(skb)->gso_size = 0; 24762306a36Sopenharmony_ci goto out; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci rcu_read_lock(); 25162306a36Sopenharmony_ci list_for_each_entry_rcu(ptype, head, list) { 25262306a36Sopenharmony_ci if (ptype->type != type || !ptype->callbacks.gro_complete) 25362306a36Sopenharmony_ci continue; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete, 25662306a36Sopenharmony_ci ipv6_gro_complete, inet_gro_complete, 25762306a36Sopenharmony_ci skb, 0); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci rcu_read_unlock(); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (err) { 26362306a36Sopenharmony_ci WARN_ON(&ptype->list == head); 26462306a36Sopenharmony_ci kfree_skb(skb); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciout: 26962306a36Sopenharmony_ci gro_normal_one(napi, skb, NAPI_GRO_CB(skb)->count); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void __napi_gro_flush_chain(struct napi_struct *napi, u32 index, 27362306a36Sopenharmony_ci bool flush_old) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct list_head *head = &napi->gro_hash[index].list; 27662306a36Sopenharmony_ci struct sk_buff *skb, *p; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci list_for_each_entry_safe_reverse(skb, p, head, list) { 27962306a36Sopenharmony_ci if (flush_old && NAPI_GRO_CB(skb)->age == jiffies) 28062306a36Sopenharmony_ci return; 28162306a36Sopenharmony_ci skb_list_del_init(skb); 28262306a36Sopenharmony_ci napi_gro_complete(napi, skb); 28362306a36Sopenharmony_ci napi->gro_hash[index].count--; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!napi->gro_hash[index].count) 28762306a36Sopenharmony_ci __clear_bit(index, &napi->gro_bitmask); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* napi->gro_hash[].list contains packets ordered by age. 29162306a36Sopenharmony_ci * youngest packets at the head of it. 29262306a36Sopenharmony_ci * Complete skbs in reverse order to reduce latencies. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_civoid napi_gro_flush(struct napi_struct *napi, bool flush_old) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned long bitmask = napi->gro_bitmask; 29762306a36Sopenharmony_ci unsigned int i, base = ~0U; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci while ((i = ffs(bitmask)) != 0) { 30062306a36Sopenharmony_ci bitmask >>= i; 30162306a36Sopenharmony_ci base += i; 30262306a36Sopenharmony_ci __napi_gro_flush_chain(napi, base, flush_old); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ciEXPORT_SYMBOL(napi_gro_flush); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic unsigned long gro_list_prepare_tc_ext(const struct sk_buff *skb, 30862306a36Sopenharmony_ci const struct sk_buff *p, 30962306a36Sopenharmony_ci unsigned long diffs) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 31262306a36Sopenharmony_ci struct tc_skb_ext *skb_ext; 31362306a36Sopenharmony_ci struct tc_skb_ext *p_ext; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci skb_ext = skb_ext_find(skb, TC_SKB_EXT); 31662306a36Sopenharmony_ci p_ext = skb_ext_find(p, TC_SKB_EXT); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci diffs |= (!!p_ext) ^ (!!skb_ext); 31962306a36Sopenharmony_ci if (!diffs && unlikely(skb_ext)) 32062306a36Sopenharmony_ci diffs |= p_ext->chain ^ skb_ext->chain; 32162306a36Sopenharmony_ci#endif 32262306a36Sopenharmony_ci return diffs; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void gro_list_prepare(const struct list_head *head, 32662306a36Sopenharmony_ci const struct sk_buff *skb) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci unsigned int maclen = skb->dev->hard_header_len; 32962306a36Sopenharmony_ci u32 hash = skb_get_hash_raw(skb); 33062306a36Sopenharmony_ci struct sk_buff *p; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci list_for_each_entry(p, head, list) { 33362306a36Sopenharmony_ci unsigned long diffs; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci NAPI_GRO_CB(p)->flush = 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (hash != skb_get_hash_raw(p)) { 33862306a36Sopenharmony_ci NAPI_GRO_CB(p)->same_flow = 0; 33962306a36Sopenharmony_ci continue; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev; 34362306a36Sopenharmony_ci diffs |= p->vlan_all ^ skb->vlan_all; 34462306a36Sopenharmony_ci diffs |= skb_metadata_differs(p, skb); 34562306a36Sopenharmony_ci if (maclen == ETH_HLEN) 34662306a36Sopenharmony_ci diffs |= compare_ether_header(skb_mac_header(p), 34762306a36Sopenharmony_ci skb_mac_header(skb)); 34862306a36Sopenharmony_ci else if (!diffs) 34962306a36Sopenharmony_ci diffs = memcmp(skb_mac_header(p), 35062306a36Sopenharmony_ci skb_mac_header(skb), 35162306a36Sopenharmony_ci maclen); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* in most common scenarions 'slow_gro' is 0 35462306a36Sopenharmony_ci * otherwise we are already on some slower paths 35562306a36Sopenharmony_ci * either skip all the infrequent tests altogether or 35662306a36Sopenharmony_ci * avoid trying too hard to skip each of them individually 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci if (!diffs && unlikely(skb->slow_gro | p->slow_gro)) { 35962306a36Sopenharmony_ci diffs |= p->sk != skb->sk; 36062306a36Sopenharmony_ci diffs |= skb_metadata_dst_cmp(p, skb); 36162306a36Sopenharmony_ci diffs |= skb_get_nfct(p) ^ skb_get_nfct(skb); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci diffs |= gro_list_prepare_tc_ext(skb, p, diffs); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci NAPI_GRO_CB(p)->same_flow = !diffs; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic inline void skb_gro_reset_offset(struct sk_buff *skb, u32 nhoff) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci const struct skb_shared_info *pinfo = skb_shinfo(skb); 37362306a36Sopenharmony_ci const skb_frag_t *frag0 = &pinfo->frags[0]; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci NAPI_GRO_CB(skb)->data_offset = 0; 37662306a36Sopenharmony_ci NAPI_GRO_CB(skb)->frag0 = NULL; 37762306a36Sopenharmony_ci NAPI_GRO_CB(skb)->frag0_len = 0; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!skb_headlen(skb) && pinfo->nr_frags && 38062306a36Sopenharmony_ci !PageHighMem(skb_frag_page(frag0)) && 38162306a36Sopenharmony_ci (!NET_IP_ALIGN || !((skb_frag_off(frag0) + nhoff) & 3))) { 38262306a36Sopenharmony_ci NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0); 38362306a36Sopenharmony_ci NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int, 38462306a36Sopenharmony_ci skb_frag_size(frag0), 38562306a36Sopenharmony_ci skb->end - skb->tail); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void gro_pull_from_frag0(struct sk_buff *skb, int grow) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct skb_shared_info *pinfo = skb_shinfo(skb); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci BUG_ON(skb->end - skb->tail < grow); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci memcpy(skb_tail_pointer(skb), NAPI_GRO_CB(skb)->frag0, grow); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci skb->data_len -= grow; 39862306a36Sopenharmony_ci skb->tail += grow; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci skb_frag_off_add(&pinfo->frags[0], grow); 40162306a36Sopenharmony_ci skb_frag_size_sub(&pinfo->frags[0], grow); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (unlikely(!skb_frag_size(&pinfo->frags[0]))) { 40462306a36Sopenharmony_ci skb_frag_unref(skb, 0); 40562306a36Sopenharmony_ci memmove(pinfo->frags, pinfo->frags + 1, 40662306a36Sopenharmony_ci --pinfo->nr_frags * sizeof(pinfo->frags[0])); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void gro_try_pull_from_frag0(struct sk_buff *skb) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci int grow = skb_gro_offset(skb) - skb_headlen(skb); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (grow > 0) 41562306a36Sopenharmony_ci gro_pull_from_frag0(skb, grow); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void gro_flush_oldest(struct napi_struct *napi, struct list_head *head) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct sk_buff *oldest; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci oldest = list_last_entry(head, struct sk_buff, list); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* We are called with head length >= MAX_GRO_SKBS, so this is 42562306a36Sopenharmony_ci * impossible. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci if (WARN_ON_ONCE(!oldest)) 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Do not adjust napi->gro_hash[].count, caller is adding a new 43162306a36Sopenharmony_ci * SKB to the chain. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ci skb_list_del_init(oldest); 43462306a36Sopenharmony_ci napi_gro_complete(napi, oldest); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci u32 bucket = skb_get_hash_raw(skb) & (GRO_HASH_BUCKETS - 1); 44062306a36Sopenharmony_ci struct gro_list *gro_list = &napi->gro_hash[bucket]; 44162306a36Sopenharmony_ci struct list_head *head = &offload_base; 44262306a36Sopenharmony_ci struct packet_offload *ptype; 44362306a36Sopenharmony_ci __be16 type = skb->protocol; 44462306a36Sopenharmony_ci struct sk_buff *pp = NULL; 44562306a36Sopenharmony_ci enum gro_result ret; 44662306a36Sopenharmony_ci int same_flow; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (netif_elide_gro(skb->dev)) 44962306a36Sopenharmony_ci goto normal; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci gro_list_prepare(&gro_list->list, skb); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci rcu_read_lock(); 45462306a36Sopenharmony_ci list_for_each_entry_rcu(ptype, head, list) { 45562306a36Sopenharmony_ci if (ptype->type == type && ptype->callbacks.gro_receive) 45662306a36Sopenharmony_ci goto found_ptype; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci rcu_read_unlock(); 45962306a36Sopenharmony_ci goto normal; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cifound_ptype: 46262306a36Sopenharmony_ci skb_set_network_header(skb, skb_gro_offset(skb)); 46362306a36Sopenharmony_ci skb_reset_mac_len(skb); 46462306a36Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct napi_gro_cb, zeroed) != sizeof(u32)); 46562306a36Sopenharmony_ci BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct napi_gro_cb, zeroed), 46662306a36Sopenharmony_ci sizeof(u32))); /* Avoid slow unaligned acc */ 46762306a36Sopenharmony_ci *(u32 *)&NAPI_GRO_CB(skb)->zeroed = 0; 46862306a36Sopenharmony_ci NAPI_GRO_CB(skb)->flush = skb_has_frag_list(skb); 46962306a36Sopenharmony_ci NAPI_GRO_CB(skb)->is_atomic = 1; 47062306a36Sopenharmony_ci NAPI_GRO_CB(skb)->count = 1; 47162306a36Sopenharmony_ci if (unlikely(skb_is_gso(skb))) { 47262306a36Sopenharmony_ci NAPI_GRO_CB(skb)->count = skb_shinfo(skb)->gso_segs; 47362306a36Sopenharmony_ci /* Only support TCP and non DODGY users. */ 47462306a36Sopenharmony_ci if (!skb_is_gso_tcp(skb) || 47562306a36Sopenharmony_ci (skb_shinfo(skb)->gso_type & SKB_GSO_DODGY)) 47662306a36Sopenharmony_ci NAPI_GRO_CB(skb)->flush = 1; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Setup for GRO checksum validation */ 48062306a36Sopenharmony_ci switch (skb->ip_summed) { 48162306a36Sopenharmony_ci case CHECKSUM_COMPLETE: 48262306a36Sopenharmony_ci NAPI_GRO_CB(skb)->csum = skb->csum; 48362306a36Sopenharmony_ci NAPI_GRO_CB(skb)->csum_valid = 1; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case CHECKSUM_UNNECESSARY: 48662306a36Sopenharmony_ci NAPI_GRO_CB(skb)->csum_cnt = skb->csum_level + 1; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci pp = INDIRECT_CALL_INET(ptype->callbacks.gro_receive, 49162306a36Sopenharmony_ci ipv6_gro_receive, inet_gro_receive, 49262306a36Sopenharmony_ci &gro_list->list, skb); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci rcu_read_unlock(); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (PTR_ERR(pp) == -EINPROGRESS) { 49762306a36Sopenharmony_ci ret = GRO_CONSUMED; 49862306a36Sopenharmony_ci goto ok; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci same_flow = NAPI_GRO_CB(skb)->same_flow; 50262306a36Sopenharmony_ci ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE : GRO_MERGED; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (pp) { 50562306a36Sopenharmony_ci skb_list_del_init(pp); 50662306a36Sopenharmony_ci napi_gro_complete(napi, pp); 50762306a36Sopenharmony_ci gro_list->count--; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (same_flow) 51162306a36Sopenharmony_ci goto ok; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (NAPI_GRO_CB(skb)->flush) 51462306a36Sopenharmony_ci goto normal; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (unlikely(gro_list->count >= MAX_GRO_SKBS)) 51762306a36Sopenharmony_ci gro_flush_oldest(napi, &gro_list->list); 51862306a36Sopenharmony_ci else 51962306a36Sopenharmony_ci gro_list->count++; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* Must be called before setting NAPI_GRO_CB(skb)->{age|last} */ 52262306a36Sopenharmony_ci gro_try_pull_from_frag0(skb); 52362306a36Sopenharmony_ci NAPI_GRO_CB(skb)->age = jiffies; 52462306a36Sopenharmony_ci NAPI_GRO_CB(skb)->last = skb; 52562306a36Sopenharmony_ci if (!skb_is_gso(skb)) 52662306a36Sopenharmony_ci skb_shinfo(skb)->gso_size = skb_gro_len(skb); 52762306a36Sopenharmony_ci list_add(&skb->list, &gro_list->list); 52862306a36Sopenharmony_ci ret = GRO_HELD; 52962306a36Sopenharmony_ciok: 53062306a36Sopenharmony_ci if (gro_list->count) { 53162306a36Sopenharmony_ci if (!test_bit(bucket, &napi->gro_bitmask)) 53262306a36Sopenharmony_ci __set_bit(bucket, &napi->gro_bitmask); 53362306a36Sopenharmony_ci } else if (test_bit(bucket, &napi->gro_bitmask)) { 53462306a36Sopenharmony_ci __clear_bit(bucket, &napi->gro_bitmask); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cinormal: 54062306a36Sopenharmony_ci ret = GRO_NORMAL; 54162306a36Sopenharmony_ci gro_try_pull_from_frag0(skb); 54262306a36Sopenharmony_ci goto ok; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistruct packet_offload *gro_find_receive_by_type(__be16 type) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct list_head *offload_head = &offload_base; 54862306a36Sopenharmony_ci struct packet_offload *ptype; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci list_for_each_entry_rcu(ptype, offload_head, list) { 55162306a36Sopenharmony_ci if (ptype->type != type || !ptype->callbacks.gro_receive) 55262306a36Sopenharmony_ci continue; 55362306a36Sopenharmony_ci return ptype; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci return NULL; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ciEXPORT_SYMBOL(gro_find_receive_by_type); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistruct packet_offload *gro_find_complete_by_type(__be16 type) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct list_head *offload_head = &offload_base; 56262306a36Sopenharmony_ci struct packet_offload *ptype; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci list_for_each_entry_rcu(ptype, offload_head, list) { 56562306a36Sopenharmony_ci if (ptype->type != type || !ptype->callbacks.gro_complete) 56662306a36Sopenharmony_ci continue; 56762306a36Sopenharmony_ci return ptype; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci return NULL; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ciEXPORT_SYMBOL(gro_find_complete_by_type); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic gro_result_t napi_skb_finish(struct napi_struct *napi, 57462306a36Sopenharmony_ci struct sk_buff *skb, 57562306a36Sopenharmony_ci gro_result_t ret) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci switch (ret) { 57862306a36Sopenharmony_ci case GRO_NORMAL: 57962306a36Sopenharmony_ci gro_normal_one(napi, skb, 1); 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci case GRO_MERGED_FREE: 58362306a36Sopenharmony_ci if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) 58462306a36Sopenharmony_ci napi_skb_free_stolen_head(skb); 58562306a36Sopenharmony_ci else if (skb->fclone != SKB_FCLONE_UNAVAILABLE) 58662306a36Sopenharmony_ci __kfree_skb(skb); 58762306a36Sopenharmony_ci else 58862306a36Sopenharmony_ci __napi_kfree_skb(skb, SKB_CONSUMED); 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci case GRO_HELD: 59262306a36Sopenharmony_ci case GRO_MERGED: 59362306a36Sopenharmony_ci case GRO_CONSUMED: 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cigro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci gro_result_t ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci skb_mark_napi_id(skb, napi); 60562306a36Sopenharmony_ci trace_napi_gro_receive_entry(skb); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci skb_gro_reset_offset(skb, 0); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ret = napi_skb_finish(napi, skb, dev_gro_receive(napi, skb)); 61062306a36Sopenharmony_ci trace_napi_gro_receive_exit(ret); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return ret; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ciEXPORT_SYMBOL(napi_gro_receive); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci if (unlikely(skb->pfmemalloc)) { 61962306a36Sopenharmony_ci consume_skb(skb); 62062306a36Sopenharmony_ci return; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci __skb_pull(skb, skb_headlen(skb)); 62362306a36Sopenharmony_ci /* restore the reserve we had after netdev_alloc_skb_ip_align() */ 62462306a36Sopenharmony_ci skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN - skb_headroom(skb)); 62562306a36Sopenharmony_ci __vlan_hwaccel_clear_tag(skb); 62662306a36Sopenharmony_ci skb->dev = napi->dev; 62762306a36Sopenharmony_ci skb->skb_iif = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* eth_type_trans() assumes pkt_type is PACKET_HOST */ 63062306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci skb->encapsulation = 0; 63362306a36Sopenharmony_ci skb_shinfo(skb)->gso_type = 0; 63462306a36Sopenharmony_ci skb_shinfo(skb)->gso_size = 0; 63562306a36Sopenharmony_ci if (unlikely(skb->slow_gro)) { 63662306a36Sopenharmony_ci skb_orphan(skb); 63762306a36Sopenharmony_ci skb_ext_reset(skb); 63862306a36Sopenharmony_ci nf_reset_ct(skb); 63962306a36Sopenharmony_ci skb->slow_gro = 0; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci napi->skb = skb; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistruct sk_buff *napi_get_frags(struct napi_struct *napi) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct sk_buff *skb = napi->skb; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (!skb) { 65062306a36Sopenharmony_ci skb = napi_alloc_skb(napi, GRO_MAX_HEAD); 65162306a36Sopenharmony_ci if (skb) { 65262306a36Sopenharmony_ci napi->skb = skb; 65362306a36Sopenharmony_ci skb_mark_napi_id(skb, napi); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci return skb; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ciEXPORT_SYMBOL(napi_get_frags); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic gro_result_t napi_frags_finish(struct napi_struct *napi, 66162306a36Sopenharmony_ci struct sk_buff *skb, 66262306a36Sopenharmony_ci gro_result_t ret) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci switch (ret) { 66562306a36Sopenharmony_ci case GRO_NORMAL: 66662306a36Sopenharmony_ci case GRO_HELD: 66762306a36Sopenharmony_ci __skb_push(skb, ETH_HLEN); 66862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, skb->dev); 66962306a36Sopenharmony_ci if (ret == GRO_NORMAL) 67062306a36Sopenharmony_ci gro_normal_one(napi, skb, 1); 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci case GRO_MERGED_FREE: 67462306a36Sopenharmony_ci if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) 67562306a36Sopenharmony_ci napi_skb_free_stolen_head(skb); 67662306a36Sopenharmony_ci else 67762306a36Sopenharmony_ci napi_reuse_skb(napi, skb); 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci case GRO_MERGED: 68162306a36Sopenharmony_ci case GRO_CONSUMED: 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/* Upper GRO stack assumes network header starts at gro_offset=0 68962306a36Sopenharmony_ci * Drivers could call both napi_gro_frags() and napi_gro_receive() 69062306a36Sopenharmony_ci * We copy ethernet header into skb->data to have a common layout. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_cistatic struct sk_buff *napi_frags_skb(struct napi_struct *napi) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct sk_buff *skb = napi->skb; 69562306a36Sopenharmony_ci const struct ethhdr *eth; 69662306a36Sopenharmony_ci unsigned int hlen = sizeof(*eth); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci napi->skb = NULL; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci skb_reset_mac_header(skb); 70162306a36Sopenharmony_ci skb_gro_reset_offset(skb, hlen); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (unlikely(skb_gro_header_hard(skb, hlen))) { 70462306a36Sopenharmony_ci eth = skb_gro_header_slow(skb, hlen, 0); 70562306a36Sopenharmony_ci if (unlikely(!eth)) { 70662306a36Sopenharmony_ci net_warn_ratelimited("%s: dropping impossible skb from %s\n", 70762306a36Sopenharmony_ci __func__, napi->dev->name); 70862306a36Sopenharmony_ci napi_reuse_skb(napi, skb); 70962306a36Sopenharmony_ci return NULL; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci eth = (const struct ethhdr *)skb->data; 71362306a36Sopenharmony_ci gro_pull_from_frag0(skb, hlen); 71462306a36Sopenharmony_ci NAPI_GRO_CB(skb)->frag0 += hlen; 71562306a36Sopenharmony_ci NAPI_GRO_CB(skb)->frag0_len -= hlen; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci __skb_pull(skb, hlen); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * This works because the only protocols we care about don't require 72162306a36Sopenharmony_ci * special handling. 72262306a36Sopenharmony_ci * We'll fix it up properly in napi_frags_finish() 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_ci skb->protocol = eth->h_proto; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return skb; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cigro_result_t napi_gro_frags(struct napi_struct *napi) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci gro_result_t ret; 73262306a36Sopenharmony_ci struct sk_buff *skb = napi_frags_skb(napi); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci trace_napi_gro_frags_entry(skb); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci ret = napi_frags_finish(napi, skb, dev_gro_receive(napi, skb)); 73762306a36Sopenharmony_ci trace_napi_gro_frags_exit(ret); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return ret; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ciEXPORT_SYMBOL(napi_gro_frags); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci/* Compute the checksum from gro_offset and return the folded value 74462306a36Sopenharmony_ci * after adding in any pseudo checksum. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci__sum16 __skb_gro_checksum_complete(struct sk_buff *skb) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci __wsum wsum; 74962306a36Sopenharmony_ci __sum16 sum; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci wsum = skb_checksum(skb, skb_gro_offset(skb), skb_gro_len(skb), 0); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* NAPI_GRO_CB(skb)->csum holds pseudo checksum */ 75462306a36Sopenharmony_ci sum = csum_fold(csum_add(NAPI_GRO_CB(skb)->csum, wsum)); 75562306a36Sopenharmony_ci /* See comments in __skb_checksum_complete(). */ 75662306a36Sopenharmony_ci if (likely(!sum)) { 75762306a36Sopenharmony_ci if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && 75862306a36Sopenharmony_ci !skb->csum_complete_sw) 75962306a36Sopenharmony_ci netdev_rx_csum_fault(skb->dev, skb); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci NAPI_GRO_CB(skb)->csum = wsum; 76362306a36Sopenharmony_ci NAPI_GRO_CB(skb)->csum_valid = 1; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return sum; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL(__skb_gro_checksum_complete); 768