Skip to content

Commit 9f6b619

Browse files
minakuba-moo
authored andcommitted
net: support non paged skb frags
Make skb_frag_page() fail in the case where the frag is not backed by a page, and fix its relevant callers to handle this case. Signed-off-by: Mina Almasry <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Reviewed-by: Jakub Kicinski <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 0f92140 commit 9f6b619

File tree

8 files changed

+67
-10
lines changed

8 files changed

+67
-10
lines changed

include/linux/skbuff.h

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3524,21 +3524,58 @@ static inline void skb_frag_off_copy(skb_frag_t *fragto,
35243524
fragto->offset = fragfrom->offset;
35253525
}
35263526

3527+
/* Return: true if the skb_frag contains a net_iov. */
3528+
static inline bool skb_frag_is_net_iov(const skb_frag_t *frag)
3529+
{
3530+
return netmem_is_net_iov(frag->netmem);
3531+
}
3532+
3533+
/**
3534+
* skb_frag_net_iov - retrieve the net_iov referred to by fragment
3535+
* @frag: the fragment
3536+
*
3537+
* Return: the &struct net_iov associated with @frag. Returns NULL if this
3538+
* frag has no associated net_iov.
3539+
*/
3540+
static inline struct net_iov *skb_frag_net_iov(const skb_frag_t *frag)
3541+
{
3542+
if (!skb_frag_is_net_iov(frag))
3543+
return NULL;
3544+
3545+
return netmem_to_net_iov(frag->netmem);
3546+
}
3547+
35273548
/**
35283549
* skb_frag_page - retrieve the page referred to by a paged fragment
35293550
* @frag: the paged fragment
35303551
*
3531-
* Returns the &struct page associated with @frag.
3552+
* Return: the &struct page associated with @frag. Returns NULL if this frag
3553+
* has no associated page.
35323554
*/
35333555
static inline struct page *skb_frag_page(const skb_frag_t *frag)
35343556
{
3557+
if (skb_frag_is_net_iov(frag))
3558+
return NULL;
3559+
35353560
return netmem_to_page(frag->netmem);
35363561
}
35373562

3563+
/**
3564+
* skb_frag_netmem - retrieve the netmem referred to by a fragment
3565+
* @frag: the fragment
3566+
*
3567+
* Return: the &netmem_ref associated with @frag.
3568+
*/
3569+
static inline netmem_ref skb_frag_netmem(const skb_frag_t *frag)
3570+
{
3571+
return frag->netmem;
3572+
}
3573+
35383574
int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb,
35393575
unsigned int headroom);
35403576
int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb,
35413577
struct bpf_prog *prog);
3578+
35423579
/**
35433580
* skb_frag_address - gets the address of the data contained in a paged fragment
35443581
* @frag: the paged fragment buffer
@@ -3548,6 +3585,9 @@ int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb,
35483585
*/
35493586
static inline void *skb_frag_address(const skb_frag_t *frag)
35503587
{
3588+
if (!skb_frag_page(frag))
3589+
return NULL;
3590+
35513591
return page_address(skb_frag_page(frag)) + skb_frag_off(frag);
35523592
}
35533593

include/linux/skbuff_ref.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f)
3434

3535
bool napi_pp_put_page(netmem_ref netmem);
3636

37-
static inline void
38-
skb_page_unref(struct page *page, bool recycle)
37+
static inline void skb_page_unref(netmem_ref netmem, bool recycle)
3938
{
4039
#ifdef CONFIG_PAGE_POOL
41-
if (recycle && napi_pp_put_page(page_to_netmem(page)))
40+
if (recycle && napi_pp_put_page(netmem))
4241
return;
4342
#endif
44-
put_page(page);
43+
put_page(netmem_to_page(netmem));
4544
}
4645

4746
/**
@@ -54,7 +53,7 @@ skb_page_unref(struct page *page, bool recycle)
5453
*/
5554
static inline void __skb_frag_unref(skb_frag_t *frag, bool recycle)
5655
{
57-
skb_page_unref(skb_frag_page(frag), recycle);
56+
skb_page_unref(skb_frag_netmem(frag), recycle);
5857
}
5958

6059
/**

net/core/dev.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3434,8 +3434,9 @@ static int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
34343434
if (!(dev->features & NETIF_F_HIGHDMA)) {
34353435
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
34363436
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
3437+
struct page *page = skb_frag_page(frag);
34373438

3438-
if (PageHighMem(skb_frag_page(frag)))
3439+
if (page && PageHighMem(page))
34393440
return 1;
34403441
}
34413442
}

net/core/gro.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,8 @@ static inline void skb_gro_reset_offset(struct sk_buff *skb, u32 nhoff)
408408
pinfo = skb_shinfo(skb);
409409
frag0 = &pinfo->frags[0];
410410

411-
if (pinfo->nr_frags && !PageHighMem(skb_frag_page(frag0)) &&
411+
if (pinfo->nr_frags && skb_frag_page(frag0) &&
412+
!PageHighMem(skb_frag_page(frag0)) &&
412413
(!NET_IP_ALIGN || !((skb_frag_off(frag0) + nhoff) & 3))) {
413414
NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
414415
NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int,

net/core/skbuff.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,14 @@ void skb_dump(const char *level, const struct sk_buff *skb, bool full_pkt)
13711371
struct page *p;
13721372
u8 *vaddr;
13731373

1374+
if (skb_frag_is_net_iov(frag)) {
1375+
printk("%sskb frag %d: not readable\n", level, i);
1376+
len -= skb_frag_size(frag);
1377+
if (!len)
1378+
break;
1379+
continue;
1380+
}
1381+
13741382
skb_frag_foreach_page(frag, skb_frag_off(frag),
13751383
skb_frag_size(frag), p, p_off, p_len,
13761384
copied) {
@@ -3163,6 +3171,9 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
31633171
for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) {
31643172
const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];
31653173

3174+
if (WARN_ON_ONCE(!skb_frag_page(f)))
3175+
return false;
3176+
31663177
if (__splice_segment(skb_frag_page(f),
31673178
skb_frag_off(f), skb_frag_size(f),
31683179
offset, len, spd, false, sk, pipe))

net/ipv4/esp4.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
115115
*/
116116
if (req->src != req->dst)
117117
for (sg = sg_next(req->src); sg; sg = sg_next(sg))
118-
skb_page_unref(sg_page(sg), skb->pp_recycle);
118+
skb_page_unref(page_to_netmem(sg_page(sg)),
119+
skb->pp_recycle);
119120
}
120121

121122
#ifdef CONFIG_INET_ESPINTCP

net/ipv4/tcp.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,9 @@ static int tcp_zerocopy_receive(struct sock *sk,
21772177
break;
21782178
}
21792179
page = skb_frag_page(frags);
2180+
if (WARN_ON_ONCE(!page))
2181+
break;
2182+
21802183
prefetchw(page);
21812184
pages[pages_to_map++] = page;
21822185
length += PAGE_SIZE;

net/ipv6/esp6.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
132132
*/
133133
if (req->src != req->dst)
134134
for (sg = sg_next(req->src); sg; sg = sg_next(sg))
135-
skb_page_unref(sg_page(sg), skb->pp_recycle);
135+
skb_page_unref(page_to_netmem(sg_page(sg)),
136+
skb->pp_recycle);
136137
}
137138

138139
#ifdef CONFIG_INET6_ESPINTCP

0 commit comments

Comments
 (0)