From: Yunsheng Lin <hidden> Date: 2021-09-30 08:09:40
Patch 1: disable dma mapping support for 32-bit arch with 64-bit
DMA.
Patch 2 & 3: pp page frag tracking support
The small packet drop test show no notiable performance degradation
when page pool is disabled.
V4:
1. Change error code to EOPNOTSUPP in patch 1.
2. Drop patch 2.
3. Use pp_frag_count to indicate if a pp page can be tracked,
to avoid breaking the mlx5 driver.
V3:
1. add patch 1/4/6/7.
2. use pp_magic to identify pp page uniquely too.
3. avoid unnecessary compound_head() calling.
V2: add patch 2, adjust the commit log accroding to the discussion
in V1, and fix a compiler error reported by kernel test robot.
Yunsheng Lin (3):
page_pool: disable dma mapping support for 32-bit arch with 64-bit DMA
page_pool: change BIAS_MAX to support incrementing
skbuff: keep track of pp page when pp_frag_count is used
include/linux/mm_types.h | 13 +------------
include/linux/skbuff.h | 30 ++++++++++++++++++++----------
include/net/page_pool.h | 36 ++++++++++++++++++++++++------------
net/core/page_pool.c | 29 +++++++++--------------------
net/core/skbuff.c | 10 ++++++++--
5 files changed, 62 insertions(+), 56 deletions(-)
--
2.33.0
From: Yunsheng Lin <hidden> Date: 2021-09-30 08:09:30
As the skb->pp_recycle and page->pp_magic may not be enough
to track if a frag page is from page pool after the calling
of __skb_frag_ref(), mostly because of a data race, see:
commit 2cc3aeb5eccc ("skbuff: Fix a potential race while
recycling page_pool packets").
There may be clone and expand head case that might lose the
track if a frag page is from page pool or not.
And not being able to keep track of pp page may cause problem
for the skb_split() case in tso_fragment() too:
Supposing a skb has 3 frag pages, all coming from a page pool,
and is split to skb1 and skb2:
skb1: first frag page + first half of second frag page
skb2: second half of second frag page + third frag page
How do we set the skb->pp_recycle of skb1 and skb2?
1. If we set both of them to 1, then we may have a similar
race as the above commit for second frag page.
2. If we set only one of them to 1, then we may have resource
leaking problem as both first frag page and third frag page
are indeed from page pool.
Increment the pp_frag_count of pp page frag in __skb_frag_ref(),
and only use page->pp_magic to indicate a pp page frag in
__skb_frag_unref() to keep track of pp page frag.
Similar handling is done for the head page of a skb too.
As we need the head page of a compound page to decide if it is
from page pool at first, so __page_frag_cache_drain() and
page_ref_inc() is used to avoid unnecessary compound_head()
calling.
Signed-off-by: Yunsheng Lin <redacted>
---
include/linux/skbuff.h | 30 ++++++++++++++++++++----------
include/net/page_pool.h | 24 +++++++++++++++++++++++-
net/core/page_pool.c | 17 ++---------------
net/core/skbuff.c | 10 ++++++++--
4 files changed, 53 insertions(+), 28 deletions(-)
@@ -231,6 +231,28 @@ static inline void page_pool_set_frag_count(struct page *page, long nr)atomic_long_set(&page->pp_frag_count,nr);}+staticinlinevoidpage_pool_atomic_inc_frag_count(structpage*page)+{+atomic_long_inc(&page->pp_frag_count);+}++staticinlineboolpage_pool_is_pp_page(structpage*page)+{+/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation+*inordertopreserveanyexistingbits,suchasbit0forthe+*headpageofcompoundpageandbit1forpfmemallocpage,so+*maskthosebitsforfreeingsidewhendoingbelowchecking,+*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()+*toavoidrecyclingthepfmemallocpage.+*/+return(page->pp_magic&~0x3UL)==PP_SIGNATURE;+}++staticinlineboolpage_pool_is_pp_page_frag(structpage*page)+{+return!!atomic_long_read(&page->pp_frag_count);+}+staticinlinelongpage_pool_atomic_sub_frag_count_return(structpage*page,longnr){
@@ -736,22 +737,10 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid)}EXPORT_SYMBOL(page_pool_update_nid);-boolpage_pool_return_skb_page(structpage*page)+voidpage_pool_return_skb_page(structpage*page){structpage_pool*pp;-page=compound_head(page);--/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation-*inordertopreserveanyexistingbits,suchasbit0forthe-*headpageofcompoundpageandbit1forpfmemallocpage,so-*maskthosebitsforfreeingsidewhendoingbelowchecking,-*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()-*toavoidrecyclingthepfmemallocpage.-*/-if(unlikely((page->pp_magic&~0x3UL)!=PP_SIGNATURE))-returnfalse;-pp=page->pp;/* Driver set this to memory recycling info. Reset it on recycle.
From: Yunsheng Lin <hidden> Date: 2021-09-30 08:09:36
As the 32-bit arch with 64-bit DMA seems to rare those days,
and page pool is carrying a lot of code and complexity for
systems that possibly don't exist when the pp page frag
tracking support is added.
So disable dma mapping support for such systems, if drivers
really want to work on such systems, they have to implement
their own DMA-mapping fallback tracking outside page_pool.
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Yunsheng Lin <redacted>
---
include/linux/mm_types.h | 13 +------------
include/net/page_pool.h | 12 +-----------
net/core/page_pool.c | 10 ++++++----
3 files changed, 8 insertions(+), 27 deletions(-)
@@ -49,6 +49,12 @@ static int page_pool_init(struct page_pool *pool,*whichistheXDP_TXuse-case.*/if(pool->p.flags&PP_FLAG_DMA_MAP){+/* DMA-mapping is not supported on 32-bit systems with+*64-bitDMAmapping.+*/+if(sizeof(dma_addr_t)>sizeof(unsignedlong))+return-EOPNOTSUPP;+if((pool->p.dma_dir!=DMA_FROM_DEVICE)&&(pool->p.dma_dir!=DMA_BIDIRECTIONAL))return-EINVAL;
@@ -69,10 +75,6 @@ static int page_pool_init(struct page_pool *pool,*/}-if(PAGE_POOL_DMA_USE_PP_FRAG_COUNT&&-pool->p.flags&PP_FLAG_PAGE_FRAG)-return-EINVAL;-if(ptr_ring_init(&pool->ring,ring_qsize,GFP_KERNEL)<0)return-ENOMEM;
From: Yunsheng Lin <hidden> Date: 2021-09-30 08:09:38
As the page->pp_frag_count need incrementing for pp page
frag tracking support, so change BIAS_MAX to (LONG_MAX / 2)
to avoid overflowing.
Signed-off-by: Yunsheng Lin <redacted>
---
net/core/page_pool.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
On Thu, Sep 30, 2021 at 04:07:47PM +0800, Yunsheng Lin wrote:
quoted hunk
As the skb->pp_recycle and page->pp_magic may not be enough
to track if a frag page is from page pool after the calling
of __skb_frag_ref(), mostly because of a data race, see:
commit 2cc3aeb5eccc ("skbuff: Fix a potential race while
recycling page_pool packets").
There may be clone and expand head case that might lose the
track if a frag page is from page pool or not.
And not being able to keep track of pp page may cause problem
for the skb_split() case in tso_fragment() too:
Supposing a skb has 3 frag pages, all coming from a page pool,
and is split to skb1 and skb2:
skb1: first frag page + first half of second frag page
skb2: second half of second frag page + third frag page
How do we set the skb->pp_recycle of skb1 and skb2?
1. If we set both of them to 1, then we may have a similar
race as the above commit for second frag page.
2. If we set only one of them to 1, then we may have resource
leaking problem as both first frag page and third frag page
are indeed from page pool.
Increment the pp_frag_count of pp page frag in __skb_frag_ref(),
and only use page->pp_magic to indicate a pp page frag in
__skb_frag_unref() to keep track of pp page frag.
Similar handling is done for the head page of a skb too.
As we need the head page of a compound page to decide if it is
from page pool at first, so __page_frag_cache_drain() and
page_ref_inc() is used to avoid unnecessary compound_head()
calling.
Signed-off-by: Yunsheng Lin <redacted>
---
include/linux/skbuff.h | 30 ++++++++++++++++++++----------
include/net/page_pool.h | 24 +++++++++++++++++++++++-
net/core/page_pool.c | 17 ++---------------
net/core/skbuff.c | 10 ++++++++--
4 files changed, 53 insertions(+), 28 deletions(-)
@@ -231,6 +231,28 @@ static inline void page_pool_set_frag_count(struct page *page, long nr)atomic_long_set(&page->pp_frag_count,nr);}+staticinlinevoidpage_pool_atomic_inc_frag_count(structpage*page)+{+atomic_long_inc(&page->pp_frag_count);+}++staticinlineboolpage_pool_is_pp_page(structpage*page)+{+/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation+*inordertopreserveanyexistingbits,suchasbit0forthe+*headpageofcompoundpageandbit1forpfmemallocpage,so+*maskthosebitsforfreeingsidewhendoingbelowchecking,+*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()+*toavoidrecyclingthepfmemallocpage.+*/+return(page->pp_magic&~0x3UL)==PP_SIGNATURE;+}++staticinlineboolpage_pool_is_pp_page_frag(structpage*page)+{+return!!atomic_long_read(&page->pp_frag_count);+}+staticinlinelongpage_pool_atomic_sub_frag_count_return(structpage*page,longnr){
@@ -736,22 +737,10 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid)}EXPORT_SYMBOL(page_pool_update_nid);-boolpage_pool_return_skb_page(structpage*page)+voidpage_pool_return_skb_page(structpage*page){structpage_pool*pp;-page=compound_head(page);--/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation-*inordertopreserveanyexistingbits,suchasbit0forthe-*headpageofcompoundpageandbit1forpfmemallocpage,so-*maskthosebitsforfreeingsidewhendoingbelowchecking,-*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()-*toavoidrecyclingthepfmemallocpage.-*/-if(unlikely((page->pp_magic&~0x3UL)!=PP_SIGNATURE))-returnfalse;-pp=page->pp;/* Driver set this to memory recycling info. Reset it on recycle.
Regardless of the comments above, providing some numbers on how the
patches affect performance (at least on hns3), would be good to have.
I'll try giving this another look. I still think having three indicators
to look at before recycling the page is not ideal.
Regards
/Ilias
From: Yunsheng Lin <hidden> Date: 2021-10-05 03:48:11
On 2021/10/4 13:50, Ilias Apalodimas wrote:
On Thu, Sep 30, 2021 at 04:07:47PM +0800, Yunsheng Lin wrote:
quoted
As the skb->pp_recycle and page->pp_magic may not be enough
to track if a frag page is from page pool after the calling
of __skb_frag_ref(), mostly because of a data race, see:
commit 2cc3aeb5eccc ("skbuff: Fix a potential race while
recycling page_pool packets").
There may be clone and expand head case that might lose the
track if a frag page is from page pool or not.
And not being able to keep track of pp page may cause problem
for the skb_split() case in tso_fragment() too:
Supposing a skb has 3 frag pages, all coming from a page pool,
and is split to skb1 and skb2:
skb1: first frag page + first half of second frag page
skb2: second half of second frag page + third frag page
How do we set the skb->pp_recycle of skb1 and skb2?
1. If we set both of them to 1, then we may have a similar
race as the above commit for second frag page.
2. If we set only one of them to 1, then we may have resource
leaking problem as both first frag page and third frag page
are indeed from page pool.
Increment the pp_frag_count of pp page frag in __skb_frag_ref(),
and only use page->pp_magic to indicate a pp page frag in
__skb_frag_unref() to keep track of pp page frag.
Similar handling is done for the head page of a skb too.
As we need the head page of a compound page to decide if it is
from page pool at first, so __page_frag_cache_drain() and
page_ref_inc() is used to avoid unnecessary compound_head()
calling.
Signed-off-by: Yunsheng Lin <redacted>
---
include/linux/skbuff.h | 30 ++++++++++++++++++++----------
include/net/page_pool.h | 24 +++++++++++++++++++++++-
net/core/page_pool.c | 17 ++---------------
net/core/skbuff.c | 10 ++++++++--
4 files changed, 53 insertions(+), 28 deletions(-)
Actually it is a VM_BUG_ON_PAGE(), and it is only turned into a
BUG() while CONFIG_DEBUG_VM is defined.
As there is already tracepoint in page_ref_inc(), I am not sure
VM_BUG_ON_PAGE() is really needed anymore, as there are a few other
place calling page_ref_inc() directly without the VM_BUG_ON_PAGE().
https://elixir.bootlin.com/linux/v5.15-rc4/source/include/linux/page_ref.h#L117
Same here, freeing the page is not the only thing put_page does.
I think the __page_frag_cache_drain() has the VM_BUG_ON_PAGE() as
put_page() does.
The one thing I am not sure about it is the devmap managed pages,
which is handled in put_page(), but is not handle in
__page_frag_cache_drain(). Is it possible that devmap managed pages
could be used in the network stack?
@@ -231,6 +231,28 @@ static inline void page_pool_set_frag_count(struct page *page, long nr)atomic_long_set(&page->pp_frag_count,nr);}+staticinlinevoidpage_pool_atomic_inc_frag_count(structpage*page)+{+atomic_long_inc(&page->pp_frag_count);+}++staticinlineboolpage_pool_is_pp_page(structpage*page)+{+/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation+*inordertopreserveanyexistingbits,suchasbit0forthe+*headpageofcompoundpageandbit1forpfmemallocpage,so+*maskthosebitsforfreeingsidewhendoingbelowchecking,+*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()+*toavoidrecyclingthepfmemallocpage.+*/+return(page->pp_magic&~0x3UL)==PP_SIGNATURE;+}++staticinlineboolpage_pool_is_pp_page_frag(structpage*page)+{+return!!atomic_long_read(&page->pp_frag_count);+}+staticinlinelongpage_pool_atomic_sub_frag_count_return(structpage*page,longnr){
@@ -736,22 +737,10 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid)}EXPORT_SYMBOL(page_pool_update_nid);-boolpage_pool_return_skb_page(structpage*page)+voidpage_pool_return_skb_page(structpage*page){structpage_pool*pp;-page=compound_head(page);--/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation-*inordertopreserveanyexistingbits,suchasbit0forthe-*headpageofcompoundpageandbit1forpfmemallocpage,so-*maskthosebitsforfreeingsidewhendoingbelowchecking,-*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()-*toavoidrecyclingthepfmemallocpage.-*/-if(unlikely((page->pp_magic&~0x3UL)!=PP_SIGNATURE))-returnfalse;-pp=page->pp;/* Driver set this to memory recycling info. Reset it on recycle.
Regardless of the comments above, providing some numbers on how the
patches affect performance (at least on hns3), would be good to have.
As mentioned in the cover letter:
"The small packet drop test show no notiable performance degradation
when page pool is disabled."
And no notiable performance degradation for the page pool enabled case
with hns3 too.
I'll try giving this another look. I still think having three indicators
to look at before recycling the page is not ideal.
All three indicators only need to be done when a page has PP_SIGNATURE set,
but do not want to be considered to be a pp page, which seems to be a rare
case?
If the mlx5 driver can change the way of using the page pool as it is now,
we can remove the addtional checking in the future, and just use the pp_magic
to indicate a pp page.
From: Yunsheng Lin <hidden> Date: 2021-10-05 03:52:10
On 2021/10/4 13:50, Ilias Apalodimas wrote:
On Thu, Sep 30, 2021 at 04:07:47PM +0800, Yunsheng Lin wrote:
quoted
As the skb->pp_recycle and page->pp_magic may not be enough
to track if a frag page is from page pool after the calling
of __skb_frag_ref(), mostly because of a data race, see:
commit 2cc3aeb5eccc ("skbuff: Fix a potential race while
recycling page_pool packets").
There may be clone and expand head case that might lose the
track if a frag page is from page pool or not.
And not being able to keep track of pp page may cause problem
for the skb_split() case in tso_fragment() too:
Supposing a skb has 3 frag pages, all coming from a page pool,
and is split to skb1 and skb2:
skb1: first frag page + first half of second frag page
skb2: second half of second frag page + third frag page
How do we set the skb->pp_recycle of skb1 and skb2?
1. If we set both of them to 1, then we may have a similar
race as the above commit for second frag page.
2. If we set only one of them to 1, then we may have resource
leaking problem as both first frag page and third frag page
are indeed from page pool.
Increment the pp_frag_count of pp page frag in __skb_frag_ref(),
and only use page->pp_magic to indicate a pp page frag in
__skb_frag_unref() to keep track of pp page frag.
Similar handling is done for the head page of a skb too.
As we need the head page of a compound page to decide if it is
from page pool at first, so __page_frag_cache_drain() and
page_ref_inc() is used to avoid unnecessary compound_head()
calling.
Signed-off-by: Yunsheng Lin <redacted>
---
include/linux/skbuff.h | 30 ++++++++++++++++++++----------
include/net/page_pool.h | 24 +++++++++++++++++++++++-
net/core/page_pool.c | 17 ++---------------
net/core/skbuff.c | 10 ++++++++--
4 files changed, 53 insertions(+), 28 deletions(-)
Actually it is a VM_BUG_ON_PAGE(), and it is only turned into a
BUG() while CONFIG_DEBUG_VM is defined.
As there is already tracepoint in page_ref_inc(), I am not sure
VM_BUG_ON_PAGE() is really needed anymore, as there are a few other
place calling page_ref_inc() directly without the VM_BUG_ON_PAGE().
https://elixir.bootlin.com/linux/v5.15-rc4/source/include/linux/page_ref.h#L117
Same here, freeing the page is not the only thing put_page does.
I think the __page_frag_cache_drain() has the VM_BUG_ON_PAGE() as
put_page() does.
The one thing I am not sure about it is the devmap managed pages,
which is handled in put_page(), but is not handle in
__page_frag_cache_drain(). Is it possible that devmap managed pages
could be used in the network stack?
@@ -231,6 +231,28 @@ static inline void page_pool_set_frag_count(struct page *page, long nr)atomic_long_set(&page->pp_frag_count,nr);}+staticinlinevoidpage_pool_atomic_inc_frag_count(structpage*page)+{+atomic_long_inc(&page->pp_frag_count);+}++staticinlineboolpage_pool_is_pp_page(structpage*page)+{+/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation+*inordertopreserveanyexistingbits,suchasbit0forthe+*headpageofcompoundpageandbit1forpfmemallocpage,so+*maskthosebitsforfreeingsidewhendoingbelowchecking,+*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()+*toavoidrecyclingthepfmemallocpage.+*/+return(page->pp_magic&~0x3UL)==PP_SIGNATURE;+}++staticinlineboolpage_pool_is_pp_page_frag(structpage*page)+{+return!!atomic_long_read(&page->pp_frag_count);+}+staticinlinelongpage_pool_atomic_sub_frag_count_return(structpage*page,longnr){
@@ -736,22 +737,10 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid)}EXPORT_SYMBOL(page_pool_update_nid);-boolpage_pool_return_skb_page(structpage*page)+voidpage_pool_return_skb_page(structpage*page){structpage_pool*pp;-page=compound_head(page);--/* page->pp_magic is OR'ed with PP_SIGNATURE after the allocation-*inordertopreserveanyexistingbits,suchasbit0forthe-*headpageofcompoundpageandbit1forpfmemallocpage,so-*maskthosebitsforfreeingsidewhendoingbelowchecking,-*andpage_is_pfmemalloc()ischeckedin__page_pool_put_page()-*toavoidrecyclingthepfmemallocpage.-*/-if(unlikely((page->pp_magic&~0x3UL)!=PP_SIGNATURE))-returnfalse;-pp=page->pp;/* Driver set this to memory recycling info. Reset it on recycle.
Regardless of the comments above, providing some numbers on how the
patches affect performance (at least on hns3), would be good to have.
As mentioned in the cover letter:
"The small packet drop test show no notiable performance degradation
when page pool is disabled."
And no notiable performance degradation for the page pool enabled case
with hns3 too.
I'll try giving this another look. I still think having three indicators
to look at before recycling the page is not ideal.
All three indicators only need to be done when a page has PP_SIGNATURE set,
but do not want to be considered to be a pp page, which seems to be a rare
case?
If the mlx5 driver can change the way of using the page pool as it is now,
we can remove the addtional checking in the future, and just use the pp_magic
to indicate a pp page.