From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-21 16:49:11
After commit 3226b158e67c ("net: avoid 32 x truesize under-estimation
for tiny skbs") we are observing 10-20% regressions in performance
tests with small packets. The perf trace points to high pressure on
the slab allocator.
This change tries to improve the allocation schema for small packets
using an idea originally suggested by Eric: a new per CPU page frag is
introduced and used in __napi_alloc_skb to cope with small allocation
requests.
To ensure that the above does not lead to excessive truesize
underestimation, the frag size for small allocation is inflated to 1K
and all the above is restricted to build with 4K page size.
Note that we need to update accordingly the run-time check introduced
with commit fd9ea57f4e95 ("net: add napi_get_frags_check() helper").
Alex suggested a smart page refcount schema to reduce the number
of atomic operations and deal properly with pfmemalloc pages.
Under small packet UDP flood, I measure a 15% peak tput increases.
Suggested-by: Eric Dumazet <redacted>
Suggested-by: Alexander H Duyck <redacted>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
@Eric, @Alex please let me know if you are comfortable with the
attribution
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 17 ------
net/core/skbuff.c | 115 +++++++++++++++++++++++++++++++++++++-
3 files changed, 113 insertions(+), 20 deletions(-)
@@ -134,8 +134,73 @@ static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr)#define NAPI_SKB_CACHE_BULK 16#define NAPI_SKB_CACHE_HALF (NAPI_SKB_CACHE_SIZE / 2)+/* the compiler doesn't like 'SKB_TRUESIZE(GRO_MAX_HEAD) > 512', but we+*canimplysuchconditioncheckingthedoublewordandMAX_HEADERsize+*/+#if PAGE_SIZE == SZ_4K && (defined(CONFIG_64BIT) || MAX_HEADER > 64)++#define NAPI_HAS_SMALL_PAGE_FRAG 1++/* specializzed page frag allocator using a single order 0 page+*andslicingitinto1Ksizedfragment.Constrainedtosystem+*with:+*-averylimitedamountof1Kfragmentsfittingasingle+*page-toavoidexcessivetruesizeunderestimation+*-reasonablyhightruesizevaluefornapi_get_frags()+*allocation-toavoidmemoryusageincreasedcompared+*tokalloc,see__napi_alloc_skb()+*+*/+structpage_frag_1k{+void*va;+u16offset;+boolpfmemalloc;+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp)+{+structpage*page;+intoffset;++if(likely(nc->va)){+offset=nc->offset-SZ_1K;+if(likely(offset>=0))+gotoout;++put_page(virt_to_page(nc->va));+}++page=alloc_pages_node(NUMA_NO_NODE,gfp,0);+if(!page){+nc->va=NULL;+returnNULL;+}++nc->va=page_address(page);+nc->pfmemalloc=page_is_pfmemalloc(page);+page_ref_add(page,PAGE_SIZE/SZ_1K);+offset=PAGE_SIZE-SZ_1K;++out:+nc->offset=offset;+returnnc->va+offset;+}+#else+#define NAPI_HAS_SMALL_PAGE_FRAG 0++structpage_frag_1k{+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp_mask)+{+returnNULL;+}++#endif+structnapi_alloc_cache{structpage_frag_cachepage;+structpage_frag_1kpage_small;unsignedintskb_count;void*skb_cache[NAPI_SKB_CACHE_SIZE];};
@@ -143,6 +208,23 @@ struct napi_alloc_cache {staticDEFINE_PER_CPU(structpage_frag_cache,netdev_alloc_cache);staticDEFINE_PER_CPU(structnapi_alloc_cache,napi_alloc_cache);+/* Double check that napi_get_frags() allocates skbs with+*skb->headbeingbackedbyslab,notapagefragment.+*Thisistomakesurebugfixedin3226b158e67c+*("net: avoid 32 x truesize under-estimation for tiny skbs")+*doesnotaccidentallycomeback.+*/+voidnapi_get_frags_check(structnapi_struct*napi)+{+structsk_buff*skb;++local_bh_disable();+skb=napi_get_frags(napi);+WARN_ON_ONCE(!NAPI_HAS_SMALL_PAGE_FRAG&&skb&&skb->head_frag);+napi_free_frags(napi);+local_bh_enable();+}+void*__napi_alloc_frag_align(unsignedintfragsz,unsignedintalign_mask){structnapi_alloc_cache*nc=this_cpu_ptr(&napi_alloc_cache);
@@ -561,15 +643,39 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len,{structnapi_alloc_cache*nc;structsk_buff*skb;+boolpfmemalloc;void*data;DEBUG_NET_WARN_ON_ONCE(!in_softirq());len+=NET_SKB_PAD+NET_IP_ALIGN;+/* When the small frag allocator is available, prefer it over kmalloc+*forsmallfragments+*/+if(NAPI_HAS_SMALL_PAGE_FRAG&&len<=SKB_WITH_OVERHEAD(1024)){+nc=this_cpu_ptr(&napi_alloc_cache);++if(sk_memalloc_socks())+gfp_mask|=__GFP_MEMALLOC;++/* we are artificially inflating the allocation size, but+*thatisnotasbadasitmaylooklike,as:+*-'len'lessthenGRO_MAX_HEADmakeslittlesense+*-larger'len'valuesleadtofragmentsizeabove512bytes+*asperNAPI_HAS_SMALL_PAGE_FRAGdefinition+*-kmallocwouldusethekmalloc-1kslabforsuchvalues+*/+len=SZ_1K;++data=page_frag_alloc_1k(&nc->page_small,gfp_mask);+pfmemalloc=nc->page_small.pfmemalloc;+gotocheck_data;+}+/* If requested length is either too small or too big,*weusekmalloc()forskb->headallocation.*/-if(len<=SKB_WITH_OVERHEAD(1024)||+if((!NAPI_HAS_SMALL_PAGE_FRAG&&len<=SKB_WITH_OVERHEAD(1024))||len>SKB_WITH_OVERHEAD(PAGE_SIZE)||(gfp_mask&(__GFP_DIRECT_RECLAIM|GFP_DMA))){skb=__alloc_skb(len,gfp_mask,SKB_ALLOC_RX|SKB_ALLOC_NAPI,
From: Eric Dumazet <edumazet@google.com> Date: 2022-09-21 17:19:16
On Wed, Sep 21, 2022 at 9:42 AM Paolo Abeni [off-list ref] wrote:
quoted hunk
After commit 3226b158e67c ("net: avoid 32 x truesize under-estimation
for tiny skbs") we are observing 10-20% regressions in performance
tests with small packets. The perf trace points to high pressure on
the slab allocator.
This change tries to improve the allocation schema for small packets
using an idea originally suggested by Eric: a new per CPU page frag is
introduced and used in __napi_alloc_skb to cope with small allocation
requests.
To ensure that the above does not lead to excessive truesize
underestimation, the frag size for small allocation is inflated to 1K
and all the above is restricted to build with 4K page size.
Note that we need to update accordingly the run-time check introduced
with commit fd9ea57f4e95 ("net: add napi_get_frags_check() helper").
Alex suggested a smart page refcount schema to reduce the number
of atomic operations and deal properly with pfmemalloc pages.
Under small packet UDP flood, I measure a 15% peak tput increases.
Suggested-by: Eric Dumazet <redacted>
Suggested-by: Alexander H Duyck <redacted>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
@Eric, @Alex please let me know if you are comfortable with the
attribution
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 17 ------
net/core/skbuff.c | 115 +++++++++++++++++++++++++++++++++++++-
3 files changed, 113 insertions(+), 20 deletions(-)
@@ -134,8 +134,73 @@ static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr)#define NAPI_SKB_CACHE_BULK 16#define NAPI_SKB_CACHE_HALF (NAPI_SKB_CACHE_SIZE / 2)+/* the compiler doesn't like 'SKB_TRUESIZE(GRO_MAX_HEAD) > 512', but we+*canimplysuchconditioncheckingthedoublewordandMAX_HEADERsize+*/+#if PAGE_SIZE == SZ_4K && (defined(CONFIG_64BIT) || MAX_HEADER > 64)++#define NAPI_HAS_SMALL_PAGE_FRAG 1++/* specializzed page frag allocator using a single order 0 page+*andslicingitinto1Ksizedfragment.Constrainedtosystem+*with:+*-averylimitedamountof1Kfragmentsfittingasingle+*page-toavoidexcessivetruesizeunderestimation+*-reasonablyhightruesizevaluefornapi_get_frags()+*allocation-toavoidmemoryusageincreasedcompared+*tokalloc,see__napi_alloc_skb()+*+*/+structpage_frag_1k{+void*va;+u16offset;+boolpfmemalloc;+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp)+{+structpage*page;+intoffset;++if(likely(nc->va)){+offset=nc->offset-SZ_1K;+if(likely(offset>=0))+gotoout;++put_page(virt_to_page(nc->va));
This probably can be removed, if the page_ref_add() later is adjusted by one ?
We know that for an exact chunk size of 1K, a 4K page is split in 4,
no matter what.
@@ -143,6 +208,23 @@ struct napi_alloc_cache { static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache); static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache);+/* Double check that napi_get_frags() allocates skbs with+ * skb->head being backed by slab, not a page fragment.+ * This is to make sure bug fixed in 3226b158e67c+ * ("net: avoid 32 x truesize under-estimation for tiny skbs")+ * does not accidentally come back.+ */+void napi_get_frags_check(struct napi_struct *napi)+{+ struct sk_buff *skb;++ local_bh_disable();+ skb = napi_get_frags(napi);+ WARN_ON_ONCE(!NAPI_HAS_SMALL_PAGE_FRAG && skb && skb->head_frag);+ napi_free_frags(napi);+ local_bh_enable();+}+ void *__napi_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) { struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
@@ -561,15 +643,39 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, { struct napi_alloc_cache *nc; struct sk_buff *skb;+ bool pfmemalloc; void *data; DEBUG_NET_WARN_ON_ONCE(!in_softirq()); len += NET_SKB_PAD + NET_IP_ALIGN;+ /* When the small frag allocator is available, prefer it over kmalloc+ * for small fragments+ */+ if (NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) {+ nc = this_cpu_ptr(&napi_alloc_cache);++ if (sk_memalloc_socks())+ gfp_mask |= __GFP_MEMALLOC;++ /* we are artificially inflating the allocation size, but+ * that is not as bad as it may look like, as:+ * - 'len' less then GRO_MAX_HEAD makes little sense+ * - larger 'len' values lead to fragment size above 512 bytes+ * as per NAPI_HAS_SMALL_PAGE_FRAG definition+ * - kmalloc would use the kmalloc-1k slab for such values+ */+ len = SZ_1K;++ data = page_frag_alloc_1k(&nc->page_small, gfp_mask);+ pfmemalloc = nc->page_small.pfmemalloc;+ goto check_data;+ }+ /* If requested length is either too small or too big, * we use kmalloc() for skb->head allocation. */- if (len <= SKB_WITH_OVERHEAD(1024) ||+ if ((!NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) || len > SKB_WITH_OVERHEAD(PAGE_SIZE) || (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) { skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX | SKB_ALLOC_NAPI,
@@ -587,6 +693,9 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, gfp_mask |= __GFP_MEMALLOC; data = page_frag_alloc(&nc->page, len, gfp_mask);+ pfmemalloc = nc->page.pfmemalloc;++check_data: if (unlikely(!data)) return NULL;
@@ -596,8 +705,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, return NULL; }- if (nc->page.pfmemalloc)- skb->pfmemalloc = 1;+ if (pfmemalloc)+ skb->pfmemalloc = pfmemalloc; skb->head_frag = 1; skb_success:--
From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-21 18:10:44
On Wed, 2022-09-21 at 10:18 -0700, Eric Dumazet wrote:
On Wed, Sep 21, 2022 at 9:42 AM Paolo Abeni [off-list ref] wrote:
quoted
After commit 3226b158e67c ("net: avoid 32 x truesize under-estimation
for tiny skbs") we are observing 10-20% regressions in performance
tests with small packets. The perf trace points to high pressure on
the slab allocator.
This change tries to improve the allocation schema for small packets
using an idea originally suggested by Eric: a new per CPU page frag is
introduced and used in __napi_alloc_skb to cope with small allocation
requests.
To ensure that the above does not lead to excessive truesize
underestimation, the frag size for small allocation is inflated to 1K
and all the above is restricted to build with 4K page size.
Note that we need to update accordingly the run-time check introduced
with commit fd9ea57f4e95 ("net: add napi_get_frags_check() helper").
Alex suggested a smart page refcount schema to reduce the number
of atomic operations and deal properly with pfmemalloc pages.
Under small packet UDP flood, I measure a 15% peak tput increases.
Suggested-by: Eric Dumazet <redacted>
Suggested-by: Alexander H Duyck <redacted>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
@Eric, @Alex please let me know if you are comfortable with the
attribution
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 17 ------
net/core/skbuff.c | 115 +++++++++++++++++++++++++++++++++++++-
3 files changed, 113 insertions(+), 20 deletions(-)
@@ -134,8 +134,73 @@ static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr)#define NAPI_SKB_CACHE_BULK 16#define NAPI_SKB_CACHE_HALF (NAPI_SKB_CACHE_SIZE / 2)+/* the compiler doesn't like 'SKB_TRUESIZE(GRO_MAX_HEAD) > 512', but we+*canimplysuchconditioncheckingthedoublewordandMAX_HEADERsize+*/+#if PAGE_SIZE == SZ_4K && (defined(CONFIG_64BIT) || MAX_HEADER > 64)++#define NAPI_HAS_SMALL_PAGE_FRAG 1++/* specializzed page frag allocator using a single order 0 page+*andslicingitinto1Ksizedfragment.Constrainedtosystem+*with:+*-averylimitedamountof1Kfragmentsfittingasingle+*page-toavoidexcessivetruesizeunderestimation+*-reasonablyhightruesizevaluefornapi_get_frags()+*allocation-toavoidmemoryusageincreasedcompared+*tokalloc,see__napi_alloc_skb()+*+*/+structpage_frag_1k{+void*va;+u16offset;+boolpfmemalloc;+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp)+{+structpage*page;+intoffset;++if(likely(nc->va)){+offset=nc->offset-SZ_1K;+if(likely(offset>=0))+gotoout;++put_page(virt_to_page(nc->va));
This probably can be removed, if the page_ref_add() later is adjusted by one ?
I think you are right. It looks like we never touch the page after the
last fragment is used. One less atomic operation :) And one less cold
cacheline accessed.
I read the above as you are somewhat ok with the overall size and
number of conditionals in this change, am I guessing too much?
Thanks!
Paolo
From: Alexander H Duyck <hidden> Date: 2022-09-21 18:12:22
On Wed, 2022-09-21 at 18:41 +0200, Paolo Abeni wrote:
quoted hunk
After commit 3226b158e67c ("net: avoid 32 x truesize under-estimation
for tiny skbs") we are observing 10-20% regressions in performance
tests with small packets. The perf trace points to high pressure on
the slab allocator.
This change tries to improve the allocation schema for small packets
using an idea originally suggested by Eric: a new per CPU page frag is
introduced and used in __napi_alloc_skb to cope with small allocation
requests.
To ensure that the above does not lead to excessive truesize
underestimation, the frag size for small allocation is inflated to 1K
and all the above is restricted to build with 4K page size.
Note that we need to update accordingly the run-time check introduced
with commit fd9ea57f4e95 ("net: add napi_get_frags_check() helper").
Alex suggested a smart page refcount schema to reduce the number
of atomic operations and deal properly with pfmemalloc pages.
Under small packet UDP flood, I measure a 15% peak tput increases.
Suggested-by: Eric Dumazet <redacted>
Suggested-by: Alexander H Duyck <redacted>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
@Eric, @Alex please let me know if you are comfortable with the
attribution
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 17 ------
net/core/skbuff.c | 115 +++++++++++++++++++++++++++++++++++++-
3 files changed, 113 insertions(+), 20 deletions(-)
@@ -134,8 +134,73 @@ static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr)#define NAPI_SKB_CACHE_BULK 16#define NAPI_SKB_CACHE_HALF (NAPI_SKB_CACHE_SIZE / 2)+/* the compiler doesn't like 'SKB_TRUESIZE(GRO_MAX_HEAD) > 512', but we+*canimplysuchconditioncheckingthedoublewordandMAX_HEADERsize+*/+#if PAGE_SIZE == SZ_4K && (defined(CONFIG_64BIT) || MAX_HEADER > 64)++#define NAPI_HAS_SMALL_PAGE_FRAG 1++/* specializzed page frag allocator using a single order 0 page+*andslicingitinto1Ksizedfragment.Constrainedtosystem+*with:+*-averylimitedamountof1Kfragmentsfittingasingle+*page-toavoidexcessivetruesizeunderestimation+*-reasonablyhightruesizevaluefornapi_get_frags()+*allocation-toavoidmemoryusageincreasedcompared+*tokalloc,see__napi_alloc_skb()+*+*/+structpage_frag_1k{+void*va;+u16offset;+boolpfmemalloc;+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp)+{+structpage*page;+intoffset;++if(likely(nc->va)){+offset=nc->offset-SZ_1K;+if(likely(offset>=0))+gotoout;++put_page(virt_to_page(nc->va));+}++page=alloc_pages_node(NUMA_NO_NODE,gfp,0);+if(!page){+nc->va=NULL;+returnNULL;+}++nc->va=page_address(page);+nc->pfmemalloc=page_is_pfmemalloc(page);+page_ref_add(page,PAGE_SIZE/SZ_1K);+offset=PAGE_SIZE-SZ_1K;++out:+nc->offset=offset;+returnnc->va+offset;
So you might be better off organizing this around the offset rather
than the virtual address. As long as offset is 0 you know the page
isn't there and has to be replaced.
offset = nc->offset - SZ_1K;
if (offset >= 0)
goto out;
page = alloc_pages_node(NUMA_NO_NODE, gfp, 0);
if (!page)
return NULL;
nc->va = page_address(page);
nc->pfmemalloc = page_is_pfmemalloc(page);
offset = PAGE_SIZE - SZ_1K;
page_ref_add(page, offset / SZ_1K);
out:
nc->offset = offset;
return nc->va + offset;
That will save you from having to call put_page and cleans it up so you
only have to perform 1 conditional check instead of 2 in the fast path.
@@ -143,6 +208,23 @@ struct napi_alloc_cache { static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache); static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache);+/* Double check that napi_get_frags() allocates skbs with+ * skb->head being backed by slab, not a page fragment.+ * This is to make sure bug fixed in 3226b158e67c+ * ("net: avoid 32 x truesize under-estimation for tiny skbs")+ * does not accidentally come back.+ */+void napi_get_frags_check(struct napi_struct *napi)+{+ struct sk_buff *skb;++ local_bh_disable();+ skb = napi_get_frags(napi);+ WARN_ON_ONCE(!NAPI_HAS_SMALL_PAGE_FRAG && skb && skb->head_frag);+ napi_free_frags(napi);+ local_bh_enable();+}+ void *__napi_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) { struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
@@ -561,15 +643,39 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, { struct napi_alloc_cache *nc; struct sk_buff *skb;+ bool pfmemalloc; void *data; DEBUG_NET_WARN_ON_ONCE(!in_softirq()); len += NET_SKB_PAD + NET_IP_ALIGN;+ /* When the small frag allocator is available, prefer it over kmalloc+ * for small fragments+ */+ if (NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) {+ nc = this_cpu_ptr(&napi_alloc_cache);++ if (sk_memalloc_socks())+ gfp_mask |= __GFP_MEMALLOC;++ /* we are artificially inflating the allocation size, but+ * that is not as bad as it may look like, as:+ * - 'len' less then GRO_MAX_HEAD makes little sense+ * - larger 'len' values lead to fragment size above 512 bytes+ * as per NAPI_HAS_SMALL_PAGE_FRAG definition+ * - kmalloc would use the kmalloc-1k slab for such values+ */+ len = SZ_1K;++ data = page_frag_alloc_1k(&nc->page_small, gfp_mask);+ pfmemalloc = nc->page_small.pfmemalloc;+ goto check_data;+ }+
It might be better to place this code further down as a branch rather
than having to duplicate things up here such as the __GFP_MEMALLOC
setting.
You could essentially just put the lines getting the napi_alloc_cache
and adding the shared info after the sk_memalloc_socks() check. Then it
could just be an if/else block either calling page_frag_alloc or your
page_frag_alloc_1k.
quoted hunk
/* If requested length is either too small or too big,
* we use kmalloc() for skb->head allocation.
*/
- if (len <= SKB_WITH_OVERHEAD(1024) ||
+ if ((!NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) ||
len > SKB_WITH_OVERHEAD(PAGE_SIZE) ||
(gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) {
skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX | SKB_ALLOC_NAPI,
@@ -587,6 +693,9 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, gfp_mask |= __GFP_MEMALLOC; data = page_frag_alloc(&nc->page, len, gfp_mask);+ pfmemalloc = nc->page.pfmemalloc;++check_data: if (unlikely(!data)) return NULL;
@@ -596,8 +705,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, return NULL; }- if (nc->page.pfmemalloc)- skb->pfmemalloc = 1;+ if (pfmemalloc)+ skb->pfmemalloc = pfmemalloc; skb->head_frag = 1; skb_success:
In regards to the pfmemalloc bits I wonder if it wouldn't be better to
just have them both using the page_frag_cache and just use a pointer to
that to populate the skb->pfmemalloc based on frag_cache->pfmemalloc at
the end?
From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-21 19:33:27
On Wed, 2022-09-21 at 11:11 -0700, Alexander H Duyck wrote:
On Wed, 2022-09-21 at 18:41 +0200, Paolo Abeni wrote:
quoted
After commit 3226b158e67c ("net: avoid 32 x truesize under-estimation
for tiny skbs") we are observing 10-20% regressions in performance
tests with small packets. The perf trace points to high pressure on
the slab allocator.
This change tries to improve the allocation schema for small packets
using an idea originally suggested by Eric: a new per CPU page frag is
introduced and used in __napi_alloc_skb to cope with small allocation
requests.
To ensure that the above does not lead to excessive truesize
underestimation, the frag size for small allocation is inflated to 1K
and all the above is restricted to build with 4K page size.
Note that we need to update accordingly the run-time check introduced
with commit fd9ea57f4e95 ("net: add napi_get_frags_check() helper").
Alex suggested a smart page refcount schema to reduce the number
of atomic operations and deal properly with pfmemalloc pages.
Under small packet UDP flood, I measure a 15% peak tput increases.
Suggested-by: Eric Dumazet <redacted>
Suggested-by: Alexander H Duyck <redacted>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
@Eric, @Alex please let me know if you are comfortable with the
attribution
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 17 ------
net/core/skbuff.c | 115 +++++++++++++++++++++++++++++++++++++-
3 files changed, 113 insertions(+), 20 deletions(-)
@@ -134,8 +134,73 @@ static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr)#define NAPI_SKB_CACHE_BULK 16#define NAPI_SKB_CACHE_HALF (NAPI_SKB_CACHE_SIZE / 2)+/* the compiler doesn't like 'SKB_TRUESIZE(GRO_MAX_HEAD) > 512', but we+*canimplysuchconditioncheckingthedoublewordandMAX_HEADERsize+*/+#if PAGE_SIZE == SZ_4K && (defined(CONFIG_64BIT) || MAX_HEADER > 64)++#define NAPI_HAS_SMALL_PAGE_FRAG 1++/* specializzed page frag allocator using a single order 0 page+*andslicingitinto1Ksizedfragment.Constrainedtosystem+*with:+*-averylimitedamountof1Kfragmentsfittingasingle+*page-toavoidexcessivetruesizeunderestimation+*-reasonablyhightruesizevaluefornapi_get_frags()+*allocation-toavoidmemoryusageincreasedcompared+*tokalloc,see__napi_alloc_skb()+*+*/+structpage_frag_1k{+void*va;+u16offset;+boolpfmemalloc;+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp)+{+structpage*page;+intoffset;++if(likely(nc->va)){+offset=nc->offset-SZ_1K;+if(likely(offset>=0))+gotoout;++put_page(virt_to_page(nc->va));+}++page=alloc_pages_node(NUMA_NO_NODE,gfp,0);+if(!page){+nc->va=NULL;+returnNULL;+}++nc->va=page_address(page);+nc->pfmemalloc=page_is_pfmemalloc(page);+page_ref_add(page,PAGE_SIZE/SZ_1K);+offset=PAGE_SIZE-SZ_1K;++out:+nc->offset=offset;+returnnc->va+offset;
So you might be better off organizing this around the offset rather
than the virtual address. As long as offset is 0 you know the page
isn't there and has to be replaced.
offset = nc->offset - SZ_1K;
if (offset >= 0)
goto out;
page = alloc_pages_node(NUMA_NO_NODE, gfp, 0);
if (!page)
return NULL;
nc->va = page_address(page);
nc->pfmemalloc = page_is_pfmemalloc(page);
offset = PAGE_SIZE - SZ_1K;
page_ref_add(page, offset / SZ_1K);
out:
nc->offset = offset;
return nc->va + offset;
That will save you from having to call put_page and cleans it up so you
only have to perform 1 conditional check instead of 2 in the fast path.
Nice! I'll use that in v2, with page_ref_add(page, offset / SZ_1K - 1);
or we will leak the page.
Rather than have this return NULL why not just point it at the
page_frag_alloc?
When NAPI_HAS_SMALL_PAGE_FRAG is 0, page_frag_alloc_1k() is never used.
the definition is there just to please the compiler. I preferred this
style to avoid more #ifdef in __napi_alloc_skb().
@@ -143,6 +208,23 @@ struct napi_alloc_cache { static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache); static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache);+/* Double check that napi_get_frags() allocates skbs with+ * skb->head being backed by slab, not a page fragment.+ * This is to make sure bug fixed in 3226b158e67c+ * ("net: avoid 32 x truesize under-estimation for tiny skbs")+ * does not accidentally come back.+ */+void napi_get_frags_check(struct napi_struct *napi)+{+ struct sk_buff *skb;++ local_bh_disable();+ skb = napi_get_frags(napi);+ WARN_ON_ONCE(!NAPI_HAS_SMALL_PAGE_FRAG && skb && skb->head_frag);+ napi_free_frags(napi);+ local_bh_enable();+}+ void *__napi_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) { struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
@@ -561,15 +643,39 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, { struct napi_alloc_cache *nc; struct sk_buff *skb;+ bool pfmemalloc; void *data; DEBUG_NET_WARN_ON_ONCE(!in_softirq()); len += NET_SKB_PAD + NET_IP_ALIGN;+ /* When the small frag allocator is available, prefer it over kmalloc+ * for small fragments+ */+ if (NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) {+ nc = this_cpu_ptr(&napi_alloc_cache);++ if (sk_memalloc_socks())+ gfp_mask |= __GFP_MEMALLOC;++ /* we are artificially inflating the allocation size, but+ * that is not as bad as it may look like, as:+ * - 'len' less then GRO_MAX_HEAD makes little sense+ * - larger 'len' values lead to fragment size above 512 bytes+ * as per NAPI_HAS_SMALL_PAGE_FRAG definition+ * - kmalloc would use the kmalloc-1k slab for such values+ */+ len = SZ_1K;++ data = page_frag_alloc_1k(&nc->page_small, gfp_mask);+ pfmemalloc = nc->page_small.pfmemalloc;+ goto check_data;+ }+
It might be better to place this code further down as a branch rather
than having to duplicate things up here such as the __GFP_MEMALLOC
setting.
You could essentially just put the lines getting the napi_alloc_cache
and adding the shared info after the sk_memalloc_socks() check. Then it
could just be an if/else block either calling page_frag_alloc or your
page_frag_alloc_1k.
I thought about that option, but I did not like it much because adds a
conditional in the fast-path for small-size allocation, and the
duplicate code is very little.
I can change the code that way, if you have strong opinion in that
regards.
quoted
/* If requested length is either too small or too big,
* we use kmalloc() for skb->head allocation.
*/
- if (len <= SKB_WITH_OVERHEAD(1024) ||
+ if ((!NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) ||
len > SKB_WITH_OVERHEAD(PAGE_SIZE) ||
(gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) {
skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX | SKB_ALLOC_NAPI,
@@ -587,6 +693,9 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, gfp_mask |= __GFP_MEMALLOC; data = page_frag_alloc(&nc->page, len, gfp_mask);+ pfmemalloc = nc->page.pfmemalloc;++check_data: if (unlikely(!data)) return NULL;
@@ -596,8 +705,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, return NULL; }- if (nc->page.pfmemalloc)- skb->pfmemalloc = 1;+ if (pfmemalloc)+ skb->pfmemalloc = pfmemalloc; skb->head_frag = 1; skb_success:
In regards to the pfmemalloc bits I wonder if it wouldn't be better to
just have them both using the page_frag_cache and just use a pointer to
that to populate the skb->pfmemalloc based on frag_cache->pfmemalloc at
the end?
Why? in the end we will still use an ancillary variable and the
napi_alloc_cache struct will be bigger (probaly not very relevant, but
for no gain at all).
Thanks!
Paolo
From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-21 20:21:39
On Wed, 2022-09-21 at 21:33 +0200, Paolo Abeni wrote:
On Wed, 2022-09-21 at 11:11 -0700, Alexander H Duyck wrote:
[...]
quoted
quoted
{
struct napi_alloc_cache *nc;
struct sk_buff *skb;
+ bool pfmemalloc;
void *data;
DEBUG_NET_WARN_ON_ONCE(!in_softirq());
len += NET_SKB_PAD + NET_IP_ALIGN;
+ /* When the small frag allocator is available, prefer it over kmalloc
+ * for small fragments
+ */
+ if (NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) {
+ nc = this_cpu_ptr(&napi_alloc_cache);
+
+ if (sk_memalloc_socks())
+ gfp_mask |= __GFP_MEMALLOC;
+
+ /* we are artificially inflating the allocation size, but
+ * that is not as bad as it may look like, as:
+ * - 'len' less then GRO_MAX_HEAD makes little sense
+ * - larger 'len' values lead to fragment size above 512 bytes
+ * as per NAPI_HAS_SMALL_PAGE_FRAG definition
+ * - kmalloc would use the kmalloc-1k slab for such values
+ */
+ len = SZ_1K;
+
+ data = page_frag_alloc_1k(&nc->page_small, gfp_mask);
+ pfmemalloc = nc->page_small.pfmemalloc;
+ goto check_data;
+ }
+
It might be better to place this code further down as a branch rather
than having to duplicate things up here such as the __GFP_MEMALLOC
setting.
You could essentially just put the lines getting the napi_alloc_cache
and adding the shared info after the sk_memalloc_socks() check. Then it
could just be an if/else block either calling page_frag_alloc or your
page_frag_alloc_1k.
I thought about that option, but I did not like it much because adds a
conditional in the fast-path for small-size allocation, and the
duplicate code is very little.
I can change the code that way, if you have strong opinion in that
regards.
Thinking again about the above, I now belive that what you suggest is
the right thing to do: my patch ignores the requested
__GFP_DIRECT_RECLAIM and GFP_DMA flags for small allocation - we always
need to fallback to kmalloc() when the caller ask for them.
TL;DR: I'll move the page_frag_alloc_1k() call below in v2.
Thanks!
Paolo
From: Alexander H Duyck <hidden> Date: 2022-09-21 20:23:36
On Wed, 2022-09-21 at 21:33 +0200, Paolo Abeni wrote:
On Wed, 2022-09-21 at 11:11 -0700, Alexander H Duyck wrote:
quoted
On Wed, 2022-09-21 at 18:41 +0200, Paolo Abeni wrote:
quoted
After commit 3226b158e67c ("net: avoid 32 x truesize under-estimation
for tiny skbs") we are observing 10-20% regressions in performance
tests with small packets. The perf trace points to high pressure on
the slab allocator.
This change tries to improve the allocation schema for small packets
using an idea originally suggested by Eric: a new per CPU page frag is
introduced and used in __napi_alloc_skb to cope with small allocation
requests.
To ensure that the above does not lead to excessive truesize
underestimation, the frag size for small allocation is inflated to 1K
and all the above is restricted to build with 4K page size.
Note that we need to update accordingly the run-time check introduced
with commit fd9ea57f4e95 ("net: add napi_get_frags_check() helper").
Alex suggested a smart page refcount schema to reduce the number
of atomic operations and deal properly with pfmemalloc pages.
Under small packet UDP flood, I measure a 15% peak tput increases.
Suggested-by: Eric Dumazet <redacted>
Suggested-by: Alexander H Duyck <redacted>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
@Eric, @Alex please let me know if you are comfortable with the
attribution
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 17 ------
net/core/skbuff.c | 115 +++++++++++++++++++++++++++++++++++++-
3 files changed, 113 insertions(+), 20 deletions(-)
@@ -134,8 +134,73 @@ static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr)#define NAPI_SKB_CACHE_BULK 16#define NAPI_SKB_CACHE_HALF (NAPI_SKB_CACHE_SIZE / 2)+/* the compiler doesn't like 'SKB_TRUESIZE(GRO_MAX_HEAD) > 512', but we+*canimplysuchconditioncheckingthedoublewordandMAX_HEADERsize+*/+#if PAGE_SIZE == SZ_4K && (defined(CONFIG_64BIT) || MAX_HEADER > 64)++#define NAPI_HAS_SMALL_PAGE_FRAG 1++/* specializzed page frag allocator using a single order 0 page+*andslicingitinto1Ksizedfragment.Constrainedtosystem+*with:+*-averylimitedamountof1Kfragmentsfittingasingle+*page-toavoidexcessivetruesizeunderestimation+*-reasonablyhightruesizevaluefornapi_get_frags()+*allocation-toavoidmemoryusageincreasedcompared+*tokalloc,see__napi_alloc_skb()+*+*/+structpage_frag_1k{+void*va;+u16offset;+boolpfmemalloc;+};++staticvoid*page_frag_alloc_1k(structpage_frag_1k*nc,gfp_tgfp)+{+structpage*page;+intoffset;++if(likely(nc->va)){+offset=nc->offset-SZ_1K;+if(likely(offset>=0))+gotoout;++put_page(virt_to_page(nc->va));+}++page=alloc_pages_node(NUMA_NO_NODE,gfp,0);+if(!page){+nc->va=NULL;+returnNULL;+}++nc->va=page_address(page);+nc->pfmemalloc=page_is_pfmemalloc(page);+page_ref_add(page,PAGE_SIZE/SZ_1K);+offset=PAGE_SIZE-SZ_1K;++out:+nc->offset=offset;+returnnc->va+offset;
So you might be better off organizing this around the offset rather
than the virtual address. As long as offset is 0 you know the page
isn't there and has to be replaced.
offset = nc->offset - SZ_1K;
if (offset >= 0)
goto out;
page = alloc_pages_node(NUMA_NO_NODE, gfp, 0);
if (!page)
return NULL;
nc->va = page_address(page);
nc->pfmemalloc = page_is_pfmemalloc(page);
offset = PAGE_SIZE - SZ_1K;
page_ref_add(page, offset / SZ_1K);
out:
nc->offset = offset;
return nc->va + offset;
That will save you from having to call put_page and cleans it up so you
only have to perform 1 conditional check instead of 2 in the fast path.
Nice! I'll use that in v2, with page_ref_add(page, offset / SZ_1K - 1);
or we will leak the page.
No, the offset already takes care of the -1 via the "- SZ_1K". What we
are adding is references for the unused offset.
Rather than have this return NULL why not just point it at the
page_frag_alloc?
When NAPI_HAS_SMALL_PAGE_FRAG is 0, page_frag_alloc_1k() is never used.
the definition is there just to please the compiler. I preferred this
style to avoid more #ifdef in __napi_alloc_skb().
@@ -143,6 +208,23 @@ struct napi_alloc_cache { static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache); static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache);+/* Double check that napi_get_frags() allocates skbs with+ * skb->head being backed by slab, not a page fragment.+ * This is to make sure bug fixed in 3226b158e67c+ * ("net: avoid 32 x truesize under-estimation for tiny skbs")+ * does not accidentally come back.+ */+void napi_get_frags_check(struct napi_struct *napi)+{+ struct sk_buff *skb;++ local_bh_disable();+ skb = napi_get_frags(napi);+ WARN_ON_ONCE(!NAPI_HAS_SMALL_PAGE_FRAG && skb && skb->head_frag);+ napi_free_frags(napi);+ local_bh_enable();+}+ void *__napi_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) { struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
@@ -561,15 +643,39 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, { struct napi_alloc_cache *nc; struct sk_buff *skb;+ bool pfmemalloc; void *data; DEBUG_NET_WARN_ON_ONCE(!in_softirq()); len += NET_SKB_PAD + NET_IP_ALIGN;+ /* When the small frag allocator is available, prefer it over kmalloc+ * for small fragments+ */+ if (NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) {+ nc = this_cpu_ptr(&napi_alloc_cache);++ if (sk_memalloc_socks())+ gfp_mask |= __GFP_MEMALLOC;++ /* we are artificially inflating the allocation size, but+ * that is not as bad as it may look like, as:+ * - 'len' less then GRO_MAX_HEAD makes little sense+ * - larger 'len' values lead to fragment size above 512 bytes+ * as per NAPI_HAS_SMALL_PAGE_FRAG definition+ * - kmalloc would use the kmalloc-1k slab for such values+ */+ len = SZ_1K;++ data = page_frag_alloc_1k(&nc->page_small, gfp_mask);+ pfmemalloc = nc->page_small.pfmemalloc;+ goto check_data;+ }+
It might be better to place this code further down as a branch rather
than having to duplicate things up here such as the __GFP_MEMALLOC
setting.
You could essentially just put the lines getting the napi_alloc_cache
and adding the shared info after the sk_memalloc_socks() check. Then it
could just be an if/else block either calling page_frag_alloc or your
page_frag_alloc_1k.
I thought about that option, but I did not like it much because adds a
conditional in the fast-path for small-size allocation, and the
duplicate code is very little.
I can change the code that way, if you have strong opinion in that
regards.
quoted
I see, so you are trying to optimize for the smaller packet size.
It occurs to me that I think you are missing the check for the gfp_mask
and the reclaim and DMA flags values as a result with your change. I
think we will need to perform that check before we can do the direct
page allocation based on size.
quoted
quoted
/* If requested length is either too small or too big,
* we use kmalloc() for skb->head allocation.
*/
- if (len <= SKB_WITH_OVERHEAD(1024) ||
+ if ((!NAPI_HAS_SMALL_PAGE_FRAG && len <= SKB_WITH_OVERHEAD(1024)) ||
len > SKB_WITH_OVERHEAD(PAGE_SIZE) ||
(gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) {
skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX | SKB_ALLOC_NAPI,
@@ -587,6 +693,9 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, gfp_mask |= __GFP_MEMALLOC; data = page_frag_alloc(&nc->page, len, gfp_mask);+ pfmemalloc = nc->page.pfmemalloc;++check_data: if (unlikely(!data)) return NULL;
@@ -596,8 +705,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, return NULL; }- if (nc->page.pfmemalloc)- skb->pfmemalloc = 1;+ if (pfmemalloc)+ skb->pfmemalloc = pfmemalloc; skb->head_frag = 1; skb_success:
In regards to the pfmemalloc bits I wonder if it wouldn't be better to
just have them both using the page_frag_cache and just use a pointer to
that to populate the skb->pfmemalloc based on frag_cache->pfmemalloc at
the end?
Why? in the end we will still use an ancillary variable and the
napi_alloc_cache struct will be bigger (probaly not very relevant, but
for no gain at all).
It was mostly just about reducing instructions. The thought is we could
get rid of the storage of the napi cache entirely since the only thing
used is the page member, so if we just passed that around instead it
would save us the trouble and not really be another variable. Basically
we would be passing a frag cache pointer instead of a napi_alloc_cache.
From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-21 20:52:16
On Wed, 2022-09-21 at 13:23 -0700, Alexander H Duyck wrote:
On Wed, 2022-09-21 at 21:33 +0200, Paolo Abeni wrote:
quoted
Nice! I'll use that in v2, with page_ref_add(page, offset / SZ_1K - 1);
or we will leak the page.
No, the offset already takes care of the -1 via the "- SZ_1K". What we
are adding is references for the unused offset.
You are right. For some reasons I keep reading PAGE_SIZE instead of
'offset'.
quoted
quoted
It occurs to me that I think you are missing the check for the gfp_mask
and the reclaim and DMA flags values as a result with your change. I
think we will need to perform that check before we can do the direct
page allocation based on size.
Yes, the gtp_mask checks are required (it just stuck me a few moments
ago ;). I will move the code as you originally suggested.
quoted
quoted
Why? in the end we will still use an ancillary variable and the
napi_alloc_cache struct will be bigger (probaly not very relevant, but
for no gain at all).
It was mostly just about reducing instructions. The thought is we could
get rid of the storage of the napi cache entirely since the only thing
used is the page member, so if we just passed that around instead it
would save us the trouble and not really be another variable. Basically
we would be passing a frag cache pointer instead of a napi_alloc_cache.
In that case we will still duplicate a bit of code -
this_cpu_ptr(&napi_alloc_cache) on both branches. gcc 11.3.1 here says
that the generated code is smaller without this change.
Cheers,
Paolo
From: Alexander Duyck <hidden> Date: 2022-09-21 21:44:40
On Wed, Sep 21, 2022 at 1:52 PM Paolo Abeni [off-list ref] wrote:
On Wed, 2022-09-21 at 13:23 -0700, Alexander H Duyck wrote:
quoted
On Wed, 2022-09-21 at 21:33 +0200, Paolo Abeni wrote:
quoted
Nice! I'll use that in v2, with page_ref_add(page, offset / SZ_1K - 1);
or we will leak the page.
No, the offset already takes care of the -1 via the "- SZ_1K". What we
are adding is references for the unused offset.
You are right. For some reasons I keep reading PAGE_SIZE instead of
'offset'.
quoted
quoted
quoted
It occurs to me that I think you are missing the check for the gfp_mask
and the reclaim and DMA flags values as a result with your change. I
think we will need to perform that check before we can do the direct
page allocation based on size.
Yes, the gtp_mask checks are required (it just stuck me a few moments
ago ;). I will move the code as you originally suggested.
quoted
quoted
quoted
Why? in the end we will still use an ancillary variable and the
napi_alloc_cache struct will be bigger (probaly not very relevant, but
for no gain at all).
It was mostly just about reducing instructions. The thought is we could
get rid of the storage of the napi cache entirely since the only thing
used is the page member, so if we just passed that around instead it
would save us the trouble and not really be another variable. Basically
we would be passing a frag cache pointer instead of a napi_alloc_cache.
In that case we will still duplicate a bit of code -
this_cpu_ptr(&napi_alloc_cache) on both branches. gcc 11.3.1 here says
that the generated code is smaller without this change.
Why do you need to duplicate it? I thought you would either be going
with nc->page or nc->page_small depending on the size so either way
you are accessing nc. Once you know you aren't going to be using the
slab cache you could basically fetch that and do the setting of
__GFP_MEMALLOC before you would even need to look at branching based
on the length. The branch on size would then assign the
page_frag_cache pointer, update the length, fetch the page frag, and
then resume the normal path.
From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-22 16:29:20
On Wed, 2022-09-21 at 14:44 -0700, Alexander Duyck wrote:
On Wed, Sep 21, 2022 at 1:52 PM Paolo Abeni [off-list ref] wrote:
quoted
In that case we will still duplicate a bit of code -
this_cpu_ptr(&napi_alloc_cache) on both branches. gcc 11.3.1 here says
that the generated code is smaller without this change.
Why do you need to duplicate it?
The goal was using a single local variable to track the napi cache and
the memory info. I thought ("was sure") that keeping two separate
variables ('nc' and 'page_frag' instead of 'nc' and 'pfmemalloc') would
produce the same amount of code. gcc says I'm wrong and you are right
;)
I'll use that in v2, thanks!
Paolo
From: Paolo Abeni <pabeni@redhat.com> Date: 2022-09-22 20:21:45
On Thu, 2022-09-22 at 18:29 +0200, Paolo Abeni wrote:
On Wed, 2022-09-21 at 14:44 -0700, Alexander Duyck wrote:
quoted
On Wed, Sep 21, 2022 at 1:52 PM Paolo Abeni [off-list ref] wrote:
quoted
In that case we will still duplicate a bit of code -
this_cpu_ptr(&napi_alloc_cache) on both branches. gcc 11.3.1 here says
that the generated code is smaller without this change.
Why do you need to duplicate it?
The goal was using a single local variable to track the napi cache and
the memory info. I thought ("was sure") that keeping two separate
variables ('nc' and 'page_frag' instead of 'nc' and 'pfmemalloc') would
produce the same amount of code. gcc says I'm wrong and you are right
;)
I'll use that in v2, thanks!
I'm sorry for being so noisy lately. I've to take back the above.
Before I measured the code size with for debug builds. With non debug
build the above schema does not reduce the instructions number.
I'll share the code after some more testing.
Cheers,
Paolo