Commit 9f6b619e authored by Mina Almasry's avatar Mina Almasry Committed by Jakub Kicinski
Browse files

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: default avatarMina Almasry <almasrymina@google.com>
Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarJakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20240910171458.219195-8-almasrymina@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 0f921404
Loading
Loading
Loading
Loading
+41 −1
Original line number Diff line number Diff line
@@ -3524,21 +3524,58 @@ static inline void skb_frag_off_copy(skb_frag_t *fragto,
	fragto->offset = fragfrom->offset;
}

/* Return: true if the skb_frag contains a net_iov. */
static inline bool skb_frag_is_net_iov(const skb_frag_t *frag)
{
	return netmem_is_net_iov(frag->netmem);
}

/**
 * skb_frag_net_iov - retrieve the net_iov referred to by fragment
 * @frag: the fragment
 *
 * Return: the &struct net_iov associated with @frag. Returns NULL if this
 * frag has no associated net_iov.
 */
static inline struct net_iov *skb_frag_net_iov(const skb_frag_t *frag)
{
	if (!skb_frag_is_net_iov(frag))
		return NULL;

	return netmem_to_net_iov(frag->netmem);
}

/**
 * skb_frag_page - retrieve the page referred to by a paged fragment
 * @frag: the paged fragment
 *
 * Returns the &struct page associated with @frag.
 * Return: the &struct page associated with @frag. Returns NULL if this frag
 * has no associated page.
 */
static inline struct page *skb_frag_page(const skb_frag_t *frag)
{
	if (skb_frag_is_net_iov(frag))
		return NULL;

	return netmem_to_page(frag->netmem);
}

/**
 * skb_frag_netmem - retrieve the netmem referred to by a fragment
 * @frag: the fragment
 *
 * Return: the &netmem_ref associated with @frag.
 */
static inline netmem_ref skb_frag_netmem(const skb_frag_t *frag)
{
	return frag->netmem;
}

int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb,
		    unsigned int headroom);
int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb,
			 struct bpf_prog *prog);

/**
 * skb_frag_address - gets the address of the data contained in a paged fragment
 * @frag: the paged fragment buffer
@@ -3548,6 +3585,9 @@ int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb,
 */
static inline void *skb_frag_address(const skb_frag_t *frag)
{
	if (!skb_frag_page(frag))
		return NULL;

	return page_address(skb_frag_page(frag)) + skb_frag_off(frag);
}

+4 −5
Original line number Diff line number Diff line
@@ -34,14 +34,13 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f)

bool napi_pp_put_page(netmem_ref netmem);

static inline void
skb_page_unref(struct page *page, bool recycle)
static inline void skb_page_unref(netmem_ref netmem, bool recycle)
{
#ifdef CONFIG_PAGE_POOL
	if (recycle && napi_pp_put_page(page_to_netmem(page)))
	if (recycle && napi_pp_put_page(netmem))
		return;
#endif
	put_page(page);
	put_page(netmem_to_page(netmem));
}

/**
@@ -54,7 +53,7 @@ skb_page_unref(struct page *page, bool recycle)
 */
static inline void __skb_frag_unref(skb_frag_t *frag, bool recycle)
{
	skb_page_unref(skb_frag_page(frag), recycle);
	skb_page_unref(skb_frag_netmem(frag), recycle);
}

/**
+2 −1
Original line number Diff line number Diff line
@@ -3434,8 +3434,9 @@ static int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
	if (!(dev->features & NETIF_F_HIGHDMA)) {
		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
			struct page *page = skb_frag_page(frag);

			if (PageHighMem(skb_frag_page(frag)))
			if (page && PageHighMem(page))
				return 1;
		}
	}
+2 −1
Original line number Diff line number Diff line
@@ -408,7 +408,8 @@ static inline void skb_gro_reset_offset(struct sk_buff *skb, u32 nhoff)
	pinfo = skb_shinfo(skb);
	frag0 = &pinfo->frags[0];

	if (pinfo->nr_frags && !PageHighMem(skb_frag_page(frag0)) &&
	if (pinfo->nr_frags && skb_frag_page(frag0) &&
	    !PageHighMem(skb_frag_page(frag0)) &&
	    (!NET_IP_ALIGN || !((skb_frag_off(frag0) + nhoff) & 3))) {
		NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
		NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int,
+11 −0
Original line number Diff line number Diff line
@@ -1371,6 +1371,14 @@ void skb_dump(const char *level, const struct sk_buff *skb, bool full_pkt)
		struct page *p;
		u8 *vaddr;

		if (skb_frag_is_net_iov(frag)) {
			printk("%sskb frag %d: not readable\n", level, i);
			len -= skb_frag_size(frag);
			if (!len)
				break;
			continue;
		}

		skb_frag_foreach_page(frag, skb_frag_off(frag),
				      skb_frag_size(frag), p, p_off, p_len,
				      copied) {
@@ -3163,6 +3171,9 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
	for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) {
		const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];

		if (WARN_ON_ONCE(!skb_frag_page(f)))
			return false;

		if (__splice_segment(skb_frag_page(f),
				     skb_frag_off(f), skb_frag_size(f),
				     offset, len, spd, false, sk, pipe))
Loading