From: Matteo Croce <redacted>
This is a respin of [1]
This patchset shows the plans for allowing page_pool to handle and
maintain DMA map/unmap of the pages it serves to the driver. For this
to work a return hook in the network core is introduced.
The overall purpose is to simplify drivers, by providing a page
allocation API that does recycling, such that each driver doesn't have
to reinvent its own recycling scheme. Using page_pool in a driver
does not require implementing XDP support, but it makes it trivially
easy to do so. Instead of allocating buffers specifically for SKBs
we now allocate a generic buffer and either wrap it on an SKB
(via build_skb) or create an XDP frame.
The recycling code leverages the XDP recycle APIs.
The Marvell mvpp2 and mvneta drivers are used in this patchset to
demonstrate how to use the API, and tested on a MacchiatoBIN
and EspressoBIN boards respectively.
Please let this going in on a future -rc1 so to allow enough time
to have wider tests.
Note that this series depends on the change "mm: fix struct page layout
on 32-bit systems"[2] which is not yet in master.
[1] https://lore.kernel.org/netdev/154413868810.21735.572808840657728172.stgit@firesoul/
[2] https://lore.kernel.org/linux-mm/20210510153211.1504886-1-willy@infradead.org/
Ilias Apalodimas (1):
page_pool: Allow drivers to hint on SKB recycling
Matteo Croce (3):
mm: add a signature in struct page
mvpp2: recycle buffers
mvneta: recycle buffers
drivers/net/ethernet/marvell/mvneta.c | 11 +++---
.../net/ethernet/marvell/mvpp2/mvpp2_main.c | 17 +++++-----
drivers/net/ethernet/marvell/sky2.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 2 +-
include/linux/mm_types.h | 1 +
include/linux/skbuff.h | 34 ++++++++++++++++---
include/net/page_pool.h | 11 ++++++
net/core/page_pool.c | 27 +++++++++++++++
net/core/skbuff.c | 20 +++++++++--
net/tls/tls_device.c | 2 +-
10 files changed, 105 insertions(+), 22 deletions(-)
--
2.31.1
@@ -221,6 +221,8 @@ static struct page *__page_pool_alloc_page_order(struct page_pool *pool,returnNULL;}+page->signature=PP_SIGNATURE;+/* Track how many pages are held 'in-flight' */pool->pages_state_hold_cnt++;trace_page_pool_state_hold(pool,page,pool->pages_state_hold_cnt);
@@ -341,6 +343,8 @@ void page_pool_release_page(struct page_pool *pool, struct page *page)DMA_ATTR_SKIP_CPU_SYNC);page_pool_set_dma_addr(page,0);skip_dma_unmap:+page->signature=0;+/* This may be the last page returned, releasing the pool, so*itisnotsafetoreferencepoolafterwards.*/
From: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Up to now several high speed NICs have custom mechanisms of recycling
the allocated memory they use for their payloads.
Our page_pool API already has recycling capabilities that are always
used when we are running in 'XDP mode'. So let's tweak the API and the
kernel network stack slightly and allow the recycling to happen even
during the standard operation.
The API doesn't take into account 'split page' policies used by those
drivers currently, but can be extended once we have users for that.
The idea is to be able to intercept the packet on skb_release_data().
If it's a buffer coming from our page_pool API recycle it back to the
pool for further usage or just release the packet entirely.
To achieve that we introduce a bit in struct sk_buff (pp_recycle:1) and
store the page_pool pointer in page->private. Storing the information in
page->private allows us to recycle both SKBs and their fragments.
The SKB bit is needed for a couple of reasons. First of all in an
effort to affect the free path as less as possible, reading a single bit,
is better that trying to derive identical information for the page stored
data. Moreover page->private is used by skb_copy_ubufs. We do have a
special mark in the page, that won't allow this to happen, but again
deciding without having to read the entire page is preferable.
The driver has to take care of the sync operations on it's own
during the buffer recycling since the buffer is, after opting-in to the
recycling, never unmapped.
Since the gain on the drivers depends on the architecture, we are not
enabling recycling by default if the page_pool API is used on a driver.
In order to enable recycling the driver must call skb_mark_for_recycle()
to store the information we need for recycling in page->private and
enabling the recycling bit, or page_pool_store_mem_info() for a fragment.
Since we added an extra argument on __skb_frag_unref() to handle
recycling, update the current users of the function with that.
Co-developed-by: Jesper Dangaard Brouer <redacted>
Co-developed-by: Matteo Croce <redacted>
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Jesper Dangaard Brouer <redacted>
Signed-off-by: Matteo Croce <redacted>
---
drivers/net/ethernet/marvell/sky2.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 2 +-
include/linux/skbuff.h | 34 ++++++++++++++++++----
include/net/page_pool.h | 9 ++++++
net/core/page_pool.c | 23 +++++++++++++++
net/core/skbuff.c | 20 +++++++++++--
net/tls/tls_device.c | 2 +-
7 files changed, 82 insertions(+), 10 deletions(-)
@@ -40,6 +40,9 @@#if IS_ENABLED(CONFIG_NF_CONNTRACK)#include<linux/netfilter/nf_conntrack_common.h>#endif+#if IS_BUILTIN(CONFIG_PAGE_POOL)+#include<net/page_pool.h>+#endif/* The interface for checksum offload between the stack and networking drivers*isasfollows...
@@ -253,4 +255,11 @@ static inline void page_pool_ring_unlock(struct page_pool *pool)spin_unlock_bh(&pool->ring.producer_lock);}+/* Store mem_info on struct page and use it while recycling skb frags */+staticinline+voidpage_pool_store_mem_info(structpage*page,structpage_pool*pp)+{+set_page_private(page,(unsignedlong)pp);+}+#endif /* _NET_PAGE_POOL_H */
@@ -626,3 +626,26 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid)}}EXPORT_SYMBOL(page_pool_update_nid);++boolpage_pool_return_skb_page(void*data)+{+structpage_pool*pp;+structpage*page;++page=virt_to_head_page(data);+if(unlikely(page->signature!=PP_SIGNATURE))+returnfalse;++pp=(structpage_pool*)page_private(page);++/* Driver set this to memory recycling info. Reset it on recycle.+*Thiswill*not*workforNICusingasplit-pagememorymodel.+*Thepagewillbereturnedtothepoolhereregardlessofthe+*'flipped'fragmentbeinginuseornot.+*/+set_page_private(page,0);+page_pool_put_full_page(pp,virt_to_head_page(data),false);++returntrue;+}+EXPORT_SYMBOL(page_pool_return_skb_page);
@@ -3495,7 +3504,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)fragto=&skb_shinfo(tgt)->frags[merge];skb_frag_size_add(fragto,skb_frag_size(fragfrom));-__skb_frag_unref(fragfrom);+__skb_frag_unref(fragfrom,skb->pp_recycle);}/* Reposition in the original skb */
@@ -5285,6 +5294,13 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,if(skb_cloned(to))returnfalse;+/* We can't coalesce skb that are allocated from slab and page_pool+*Therecyclemarkisontheskb,sothatmightenduptryingto+*recycleslaballocatedskb->head+*/+if(to->pp_recycle!=from->pp_recycle)+returnfalse;+if(len<=skb_tailroom(to)){if(len)BUG_ON(skb_copy_bits(from,0,skb_put(to,len),len));
From: Matteo Croce <redacted>
Use the new recycling API for page_pool.
In a drop rate test, the packet rate increased di 10%,
from 269 Kpps to 296 Kpps.
perf top on a stock system shows:
Overhead Shared Object Symbol
21.78% [kernel] [k] __pi___inval_dcache_area
21.66% [mvneta] [k] mvneta_rx_swbm
7.00% [kernel] [k] kmem_cache_alloc
6.05% [kernel] [k] eth_type_trans
4.44% [kernel] [k] kmem_cache_free.part.0
3.80% [kernel] [k] __netif_receive_skb_core
3.68% [kernel] [k] dev_gro_receive
3.65% [kernel] [k] get_page_from_freelist
3.43% [kernel] [k] page_pool_release_page
3.35% [kernel] [k] free_unref_page
And this is the same output with recycling enabled:
Overhead Shared Object Symbol
24.10% [kernel] [k] __pi___inval_dcache_area
23.02% [mvneta] [k] mvneta_rx_swbm
7.19% [kernel] [k] kmem_cache_alloc
6.50% [kernel] [k] eth_type_trans
4.93% [kernel] [k] __netif_receive_skb_core
4.77% [kernel] [k] kmem_cache_free.part.0
3.93% [kernel] [k] dev_gro_receive
3.03% [kernel] [k] build_skb
2.91% [kernel] [k] page_pool_put_page
2.85% [kernel] [k] __xdp_return
The test was done with mausezahn on the TX side with 64 byte raw
ethernet frames.
Signed-off-by: Matteo Croce <redacted>
---
drivers/net/ethernet/marvell/mvneta.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
From: Matthew Wilcox <willy@infradead.org> Date: 2021-05-11 13:45:56
On Tue, May 11, 2021 at 03:31:15PM +0200, Matteo Croce wrote:
quoted hunk
@@ -101,6 +101,7 @@ struct page { * 32-bit architectures. */ unsigned long dma_addr[2];+ unsigned long signature; }; struct { /* slab, slob and slub */ union {
No. Signature now aliases with page->mapping, which is going to go
badly wrong for drivers which map this page into userspace.
I had this as:
+ unsigned long pp_magic;
+ unsigned long xmi;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and pp_magic needs to be set to something with bits 0&1 clear and
clearly isn't a pointer. I went with POISON_POINTER_DELTA + 0x40.
Hi Matthew,
On Tue, May 11, 2021 at 02:45:32PM +0100, Matthew Wilcox wrote:
On Tue, May 11, 2021 at 03:31:15PM +0200, Matteo Croce wrote:
quoted
@@ -101,6 +101,7 @@ struct page { * 32-bit architectures. */ unsigned long dma_addr[2];+ unsigned long signature; }; struct { /* slab, slob and slub */ union {
No. Signature now aliases with page->mapping, which is going to go
badly wrong for drivers which map this page into userspace.
I had this as:
+ unsigned long pp_magic;
+ unsigned long xmi;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and pp_magic needs to be set to something with bits 0&1 clear and
clearly isn't a pointer. I went with POISON_POINTER_DELTA + 0x40.
Regardless to the changes required, there's another thing we'd like your
opinion on.
There was a change wrt to the previous patchset. We used to store the
struct xdp_mem_info into page->private. On the new version we store the
page_pool ptr address in page->private (there's an explanation why on the
mail thread, but the tl;dr is that we can get some more speed and keeping
xdp_mem_info is not that crucial). So since we can just store the page_pool
address directly, should we keep using page->private or it's better to
do:
+ unsigned long pp_magic;
+ unsigned long pp_ptr;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and use pp_ptr?
Thanks
/Ilias
From: Matthew Wilcox <willy@infradead.org> Date: 2021-05-11 14:19:38
On Tue, May 11, 2021 at 05:11:13PM +0300, Ilias Apalodimas wrote:
Hi Matthew,
On Tue, May 11, 2021 at 02:45:32PM +0100, Matthew Wilcox wrote:
quoted
On Tue, May 11, 2021 at 03:31:15PM +0200, Matteo Croce wrote:
quoted
@@ -101,6 +101,7 @@ struct page { * 32-bit architectures. */ unsigned long dma_addr[2];+ unsigned long signature; }; struct { /* slab, slob and slub */ union {
No. Signature now aliases with page->mapping, which is going to go
badly wrong for drivers which map this page into userspace.
I had this as:
+ unsigned long pp_magic;
+ unsigned long xmi;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and pp_magic needs to be set to something with bits 0&1 clear and
clearly isn't a pointer. I went with POISON_POINTER_DELTA + 0x40.
Regardless to the changes required, there's another thing we'd like your
opinion on.
There was a change wrt to the previous patchset. We used to store the
struct xdp_mem_info into page->private. On the new version we store the
page_pool ptr address in page->private (there's an explanation why on the
mail thread, but the tl;dr is that we can get some more speed and keeping
xdp_mem_info is not that crucial). So since we can just store the page_pool
address directly, should we keep using page->private or it's better to
do:
+ unsigned long pp_magic;
+ unsigned long pp_ptr;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and use pp_ptr?
I'd rather you didn't use page_private ... Any reason not to use:
unsigned long pp_magic;
struct page_pool *pp;
unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
?
On Tue, 11 May 2021 at 17:19, Matthew Wilcox [off-list ref] wrote:
On Tue, May 11, 2021 at 05:11:13PM +0300, Ilias Apalodimas wrote:
quoted
Hi Matthew,
On Tue, May 11, 2021 at 02:45:32PM +0100, Matthew Wilcox wrote:
quoted
On Tue, May 11, 2021 at 03:31:15PM +0200, Matteo Croce wrote:
quoted
@@ -101,6 +101,7 @@ struct page { * 32-bit architectures. */ unsigned long dma_addr[2];+ unsigned long signature; }; struct { /* slab, slob and slub */ union {
No. Signature now aliases with page->mapping, which is going to go
badly wrong for drivers which map this page into userspace.
I had this as:
+ unsigned long pp_magic;
+ unsigned long xmi;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and pp_magic needs to be set to something with bits 0&1 clear and
clearly isn't a pointer. I went with POISON_POINTER_DELTA + 0x40.
Regardless to the changes required, there's another thing we'd like your
opinion on.
There was a change wrt to the previous patchset. We used to store the
struct xdp_mem_info into page->private. On the new version we store the
page_pool ptr address in page->private (there's an explanation why on the
mail thread, but the tl;dr is that we can get some more speed and keeping
xdp_mem_info is not that crucial). So since we can just store the page_pool
address directly, should we keep using page->private or it's better to
do:
+ unsigned long pp_magic;
+ unsigned long pp_ptr;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
and use pp_ptr?
I'd rather you didn't use page_private ... Any reason not to use:
unsigned long pp_magic;
struct page_pool *pp;
unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
?
Nope not at all, either would work. we'll switch to that
From: Eric Dumazet <hidden> Date: 2021-05-11 15:24:57
On 5/11/21 3:31 PM, Matteo Croce wrote:
From: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Up to now several high speed NICs have custom mechanisms of recycling
the allocated memory they use for their payloads.
Our page_pool API already has recycling capabilities that are always
used when we are running in 'XDP mode'. So let's tweak the API and the
kernel network stack slightly and allow the recycling to happen even
during the standard operation.
The API doesn't take into account 'split page' policies used by those
drivers currently, but can be extended once we have users for that.
The idea is to be able to intercept the packet on skb_release_data().
If it's a buffer coming from our page_pool API recycle it back to the
pool for further usage or just release the packet entirely.
To achieve that we introduce a bit in struct sk_buff (pp_recycle:1) and
store the page_pool pointer in page->private. Storing the information in
page->private allows us to recycle both SKBs and their fragments.
The SKB bit is needed for a couple of reasons. First of all in an
effort to affect the free path as less as possible, reading a single bit,
is better that trying to derive identical information for the page stored
data. Moreover page->private is used by skb_copy_ubufs. We do have a
special mark in the page, that won't allow this to happen, but again
deciding without having to read the entire page is preferable.
The driver has to take care of the sync operations on it's own
during the buffer recycling since the buffer is, after opting-in to the
recycling, never unmapped.
Since the gain on the drivers depends on the architecture, we are not
enabling recycling by default if the page_pool API is used on a driver.
In order to enable recycling the driver must call skb_mark_for_recycle()
to store the information we need for recycling in page->private and
enabling the recycling bit, or page_pool_store_mem_info() for a fragment.
Since we added an extra argument on __skb_frag_unref() to handle
recycling, update the current users of the function with that.
This part could be done with a preliminary patch, only adding this
extra boolean, this would keep the 'complex' patch smaller.
@@ -40,6 +40,9 @@#if IS_ENABLED(CONFIG_NF_CONNTRACK)#include<linux/netfilter/nf_conntrack_common.h>#endif+#if IS_BUILTIN(CONFIG_PAGE_POOL)+#include<net/page_pool.h>+#endif/* The interface for checksum offload between the stack and networking drivers*isasfollows...
@@ -253,4 +255,11 @@ static inline void page_pool_ring_unlock(struct page_pool *pool)spin_unlock_bh(&pool->ring.producer_lock);}+/* Store mem_info on struct page and use it while recycling skb frags */+staticinline+voidpage_pool_store_mem_info(structpage*page,structpage_pool*pp)+{+set_page_private(page,(unsignedlong)pp);+}+#endif /* _NET_PAGE_POOL_H */
@@ -626,3 +626,26 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid)}}EXPORT_SYMBOL(page_pool_update_nid);++boolpage_pool_return_skb_page(void*data)+{+structpage_pool*pp;+structpage*page;++page=virt_to_head_page(data);+if(unlikely(page->signature!=PP_SIGNATURE))+returnfalse;++pp=(structpage_pool*)page_private(page);++/* Driver set this to memory recycling info. Reset it on recycle.+*Thiswill*not*workforNICusingasplit-pagememorymodel.+*Thepagewillbereturnedtothepoolhereregardlessofthe+*'flipped'fragmentbeinginuseornot.+*/+set_page_private(page,0);+page_pool_put_full_page(pp,virt_to_head_page(data),false);++returntrue;+}+EXPORT_SYMBOL(page_pool_return_skb_page);
Why IS_BUILTIN() ?
PAGE_POOL is either y or n
IS_ENABLED() would look better, since we use IS_BUILTIN() for the cases where a module might be used.
Or simply #ifdef CONFIG_PAGE_POOL
+ if (skb->pp_recycle && page_pool_return_skb_page(head))
This probably should be attempted only in the (skb->head_frag) case ?
Also this patch misses pskb_expand_head()
quoted hunk
+ return;
+#endif
+
if (skb->head_frag)
skb_free_frag(head);
else
@@ -664,7 +672,7 @@ static void skb_release_data(struct sk_buff *skb) skb_zcopy_clear(skb, true); for (i = 0; i < shinfo->nr_frags; i++)- __skb_frag_unref(&shinfo->frags[i]);+ __skb_frag_unref(&shinfo->frags[i], skb->pp_recycle); if (shinfo->frag_list) kfree_skb_list(shinfo->frag_list);
@@ -3495,7 +3504,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) fragto = &skb_shinfo(tgt)->frags[merge]; skb_frag_size_add(fragto, skb_frag_size(fragfrom));- __skb_frag_unref(fragfrom);+ __skb_frag_unref(fragfrom, skb->pp_recycle); } /* Reposition in the original skb */
@@ -5285,6 +5294,13 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, if (skb_cloned(to)) return false;+ /* We can't coalesce skb that are allocated from slab and page_pool+ * The recycle mark is on the skb, so that might end up trying to+ * recycle slab allocated skb->head+ */+ if (to->pp_recycle != from->pp_recycle)+ return false;+ if (len <= skb_tailroom(to)) { if (len) BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len));
PAGE_POOL is either y or n
IS_ENABLED() would look better, since we use IS_BUILTIN() for the cases where a module might be used.
Or simply #ifdef CONFIG_PAGE_POOL
quoted
+ if (skb->pp_recycle && page_pool_return_skb_page(head))
This probably should be attempted only in the (skb->head_frag) case ?
I think the extra check makes sense.
Also this patch misses pskb_expand_head()
I am not sure I am following. Misses what? pskb_expand_head() will either
call skb_release_data() or skb_free_head(), which would either recycle or
unmap the buffer for us (depending on the page refcnt)
[...]
Thanks
/Ilias
PAGE_POOL is either y or n
IS_ENABLED() would look better, since we use IS_BUILTIN() for the cases where a module might be used.
Or simply #ifdef CONFIG_PAGE_POOL
quoted
+ if (skb->pp_recycle && page_pool_return_skb_page(head))
This probably should be attempted only in the (skb->head_frag) case ?
I think the extra check makes sense.
What do you mean here ?
quoted
Also this patch misses pskb_expand_head()
I am not sure I am following. Misses what? pskb_expand_head() will either
call skb_release_data() or skb_free_head(), which would either recycle or
unmap the buffer for us (depending on the page refcnt)
pskb_expand_head() allocates a new skb->head, from slab.
We should clear skb->pp_recycle for consistency of the skb->head_frag
clearing we perform there.
But then, I now realize you use skb->pp_recycle bit for both skb->head
and fragments,
and rely on this PP_SIGNATURE thing (I note that patch 1 changelog
does not describe why a random page will _not_ have this signature by
bad luck)
Please document/describe which struct page fields are aliased with
page->signature ?
Thanks !
+ if (skb->pp_recycle && page_pool_return_skb_page(head))
This probably should be attempted only in the (skb->head_frag) case ?
I think the extra check makes sense.
What do you mean here ?
I thought you wanted an extra check in the if statement above. So move the
block under the existing if. Something like
if (skb->head_frag) {
#ifdef (CONFIG_PAGE_POOL)
if (skb->pp_recycle && page_pool_return_skb_page(head))
return;
#endif
skb_free_frag(head);
} else {
.....
quoted
quoted
Also this patch misses pskb_expand_head()
I am not sure I am following. Misses what? pskb_expand_head() will either
call skb_release_data() or skb_free_head(), which would either recycle or
unmap the buffer for us (depending on the page refcnt)
pskb_expand_head() allocates a new skb->head, from slab.
We should clear skb->pp_recycle for consistency of the skb->head_frag
clearing we perform there.
Ah right, good catch. I was mostly worried we are not freeing/unmapping
buffers and I completely missed that. I think nothing bad will happen even
if we don't, since the signature will eventually protect us, but it's
definitely the right thing to do.
But then, I now realize you use skb->pp_recycle bit for both skb->head
and fragments,
and rely on this PP_SIGNATURE thing (I note that patch 1 changelog
does not describe why a random page will _not_ have this signature by
bad luck)
Correct. I've tried to explain in the previous posting as well, but that's
the big difference compared to the initial RFC we sent a few years ago (the
ability to recycle frags as well).
Please document/describe which struct page fields are aliased with
page->signature ?
Sure, any preference on this? Right above page_pool_return_skb_page() ?
Keep in mind the current [1/4] patch is wrong, since it will overlap
pp_signature with mapping. So we'll have interesting results if a page
gets mapped to userspace :).
What Matthew proposed makes sense, we can add something along the lines of:
+ unsigned long pp_magic;
+ struct page_pool *pp;
+ unsigned long _pp_mapping_pad;
+ unsigned long dma_addr[2];
in struct page. In this case page->mapping aliases to pa->_pp_mapping_pad
The first word (that we'll now be using) is used for a pointer or a
compound_head. So as long as pp_magic doesn't resemble a pointer and has
bits 0/1 set to 0 we should be safe.
Thanks!
/Ilias
From: Matthew Wilcox <willy@infradead.org> Date: 2021-05-12 18:12:23
On Tue, May 11, 2021 at 05:25:36PM +0300, Ilias Apalodimas wrote:
Nope not at all, either would work. we'll switch to that
You'll need something like this because of the current use of
page->index to mean "pfmemalloc".
From ecd6d912056a21bbe55d997c01f96b0b8b9fbc31 Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Date: Fri, 16 Apr 2021 18:12:33 -0400
Subject: [PATCH] mm: Indicate pfmemalloc pages in compound_head
The net page_pool wants to use a magic value to identify page pool pages.
The best place to put it is in the first word where it can be clearly a
non-pointer value. That means shifting dma_addr up to alias with ->index,
which means we need to find another way to indicate page_is_pfmemalloc().
Since page_pool doesn't want to set its magic value on pages which are
pfmemalloc, we can use bit 1 of compound_head to indicate that the page
came from the memory reserves.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/mm.h | 12 +++++++-----
include/linux/mm_types.h | 7 +++----
2 files changed, 10 insertions(+), 9 deletions(-)
From: Eric Dumazet <edumazet@google.com> Date: 2021-05-12 18:38:09
On Wed, May 12, 2021 at 6:03 PM Matthew Wilcox [off-list ref] wrote:
quoted hunk
On Tue, May 11, 2021 at 05:25:36PM +0300, Ilias Apalodimas wrote:
quoted
Nope not at all, either would work. we'll switch to that
You'll need something like this because of the current use of
page->index to mean "pfmemalloc".
From ecd6d912056a21bbe55d997c01f96b0b8b9fbc31 Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Date: Fri, 16 Apr 2021 18:12:33 -0400
Subject: [PATCH] mm: Indicate pfmemalloc pages in compound_head
The net page_pool wants to use a magic value to identify page pool pages.
The best place to put it is in the first word where it can be clearly a
non-pointer value. That means shifting dma_addr up to alias with ->index,
which means we need to find another way to indicate page_is_pfmemalloc().
Since page_pool doesn't want to set its magic value on pages which are
pfmemalloc, we can use bit 1 of compound_head to indicate that the page
came from the memory reserves.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/mm.h | 12 +++++++-----
include/linux/mm_types.h | 7 +++----
2 files changed, 10 insertions(+), 9 deletions(-)
From: Matthew Wilcox <willy@infradead.org> Date: 2021-05-12 19:23:50
On Wed, May 12, 2021 at 06:09:21PM +0200, Eric Dumazet wrote:
On Wed, May 12, 2021 at 6:03 PM Matthew Wilcox [off-list ref] wrote:
quoted
On Tue, May 11, 2021 at 05:25:36PM +0300, Ilias Apalodimas wrote:
quoted
Nope not at all, either would work. we'll switch to that
You'll need something like this because of the current use of
page->index to mean "pfmemalloc".
From ecd6d912056a21bbe55d997c01f96b0b8b9fbc31 Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Date: Fri, 16 Apr 2021 18:12:33 -0400
Subject: [PATCH] mm: Indicate pfmemalloc pages in compound_head
The net page_pool wants to use a magic value to identify page pool pages.
The best place to put it is in the first word where it can be clearly a
non-pointer value. That means shifting dma_addr up to alias with ->index,
which means we need to find another way to indicate page_is_pfmemalloc().
Since page_pool doesn't want to set its magic value on pages which are
pfmemalloc, we can use bit 1 of compound_head to indicate that the page
came from the memory reserves.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/mm.h | 12 +++++++-----
include/linux/mm_types.h | 7 +++----
2 files changed, 10 insertions(+), 9 deletions(-)
On Wed, May 12, 2021 at 04:57:25PM +0100, Matthew Wilcox wrote:
On Tue, May 11, 2021 at 05:25:36PM +0300, Ilias Apalodimas wrote:
quoted
Nope not at all, either would work. we'll switch to that
You'll need something like this because of the current use of
page->index to mean "pfmemalloc".
Yes, I was somehow under the impression that was already merged.
We'll include it in the series, with your Co-developed-by tag.
Thanks
/Ilias
quoted hunk
From ecd6d912056a21bbe55d997c01f96b0b8b9fbc31 Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Date: Fri, 16 Apr 2021 18:12:33 -0400
Subject: [PATCH] mm: Indicate pfmemalloc pages in compound_head
The net page_pool wants to use a magic value to identify page pool pages.
The best place to put it is in the first word where it can be clearly a
non-pointer value. That means shifting dma_addr up to alias with ->index,
which means we need to find another way to indicate page_is_pfmemalloc().
Since page_pool doesn't want to set its magic value on pages which are
pfmemalloc, we can use bit 1 of compound_head to indicate that the page
came from the memory reserves.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/mm.h | 12 +++++++-----
include/linux/mm_types.h | 7 +++----
2 files changed, 10 insertions(+), 9 deletions(-)
From: Yunsheng Lin <hidden> Date: 2021-05-13 02:15:36
On 2021/5/12 23:57, Matthew Wilcox wrote:
quoted hunk
On Tue, May 11, 2021 at 05:25:36PM +0300, Ilias Apalodimas wrote:
quoted
Nope not at all, either would work. we'll switch to that
You'll need something like this because of the current use of
page->index to mean "pfmemalloc".
quoted
From ecd6d912056a21bbe55d997c01f96b0b8b9fbc31 Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Date: Fri, 16 Apr 2021 18:12:33 -0400
Subject: [PATCH] mm: Indicate pfmemalloc pages in compound_head
The net page_pool wants to use a magic value to identify page pool pages.
The best place to put it is in the first word where it can be clearly a
non-pointer value. That means shifting dma_addr up to alias with ->index,
which means we need to find another way to indicate page_is_pfmemalloc().
Since page_pool doesn't want to set its magic value on pages which are
pfmemalloc, we can use bit 1 of compound_head to indicate that the page
came from the memory reserves.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/mm.h | 12 +++++++-----
include/linux/mm_types.h | 7 +++----
2 files changed, 10 insertions(+), 9 deletions(-)
Is there any reason why not use "page->compound_head |= 2"? as
corresponding to the "page->compound_head & 2" in the above
page_is_pfmemalloc()?
Also, this may mean we need to make sure to pass head page or
base page to set_page_pfmemalloc() if using
"page->compound_head = 2", because it clears the bit 0 and head
page ptr for tail page too, right?
@@ -96,10 +96,9 @@ struct page {unsignedlongprivate;};struct{/* page_pool used by netstack */-/**-*@dma_addr:mightrequirea64-bitvalueon-*32-bitarchitectures.-*/+unsignedlongpp_magic;+structpage_pool*pp;+unsignedlong_pp_mapping_pad;unsignedlongdma_addr[2];
It seems the dma_addr[1] aliases with page->private, and
page_private() is used in skb_copy_ubufs()?
It seems we can avoid using page_private() in skb_copy_ubufs()
by using a dynamic allocated array to store the page ptr?
Is there any reason why not use "page->compound_head |= 2"? as
corresponding to the "page->compound_head & 2" in the above
page_is_pfmemalloc()?
Also, this may mean we need to make sure to pass head page or
base page to set_page_pfmemalloc() if using
"page->compound_head = 2", because it clears the bit 0 and head
page ptr for tail page too, right?
I think what you're missing here is that this page is freshly allocated.
This is information being passed from the page allocator to any user
who cares to look at it. By definition, it's set on the head/base page, and
there is nothing else present in the page->compound_head. Doing an OR
is more expensive than just setting it to 2.
I'm not really sure why set/clear page_pfmemalloc are defined in mm.h.
They should probably be in mm/page_alloc.c where nobody else would ever
think that they could or should be calling them.
quoted
struct { /* page_pool used by netstack */
- /**
- * @dma_addr: might require a 64-bit value on
- * 32-bit architectures.
- */
+ unsigned long pp_magic;
+ struct page_pool *pp;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
It seems the dma_addr[1] aliases with page->private, and
page_private() is used in skb_copy_ubufs()?
It seems we can avoid using page_private() in skb_copy_ubufs()
by using a dynamic allocated array to store the page ptr?
This is why I hate it when people use page_private() instead of
documenting what they're doing in struct page. There is no way to know
(as an outsider to networking) whether the page in skb_copy_ubufs()
comes from page_pool. I looked at it, and thought it didn't:
page = alloc_page(gfp_mask);
but if you say those pages can come from page_pool, I believe you.
Is there any reason why not use "page->compound_head |= 2"? as
corresponding to the "page->compound_head & 2" in the above
page_is_pfmemalloc()?
Also, this may mean we need to make sure to pass head page or
base page to set_page_pfmemalloc() if using
"page->compound_head = 2", because it clears the bit 0 and head
page ptr for tail page too, right?
I think what you're missing here is that this page is freshly allocated.
This is information being passed from the page allocator to any user
who cares to look at it. By definition, it's set on the head/base page, and
there is nothing else present in the page->compound_head. Doing an OR
is more expensive than just setting it to 2.
Thanks for clarifying.
I'm not really sure why set/clear page_pfmemalloc are defined in mm.h.
They should probably be in mm/page_alloc.c where nobody else would ever
think that they could or should be calling them.>
quoted
quoted
struct { /* page_pool used by netstack */
- /**
- * @dma_addr: might require a 64-bit value on
- * 32-bit architectures.
- */
+ unsigned long pp_magic;
+ struct page_pool *pp;
+ unsigned long _pp_mapping_pad;
unsigned long dma_addr[2];
It seems the dma_addr[1] aliases with page->private, and
page_private() is used in skb_copy_ubufs()?
It seems we can avoid using page_private() in skb_copy_ubufs()
by using a dynamic allocated array to store the page ptr?
This is why I hate it when people use page_private() instead of
documenting what they're doing in struct page. There is no way to know
(as an outsider to networking) whether the page in skb_copy_ubufs()
comes from page_pool. I looked at it, and thought it didn't:
page = alloc_page(gfp_mask);
but if you say those pages can come from page_pool, I believe you.
page_private() using in skb_copy_ubufs() does indeed seem ok here.
the page_private() is used on the page which is freshly allocated
from alloc_page().
Sorry for the confusion.