This series introduces a bulk order-0 page allocator with sunrpc and
the network page pool being the first users. The implementation is not
particularly efficient and the intention is to iron out what the semantics
of the API should have for users. Once the semantics are ironed out, it can
be made more efficient.
Improving the implementation requires fairly deep surgery in numerous
places. The lock scope would need to be significantly reduced, particularly
as vmstat, per-cpu and the buddy allocator have different locking protocol
that overall -- e.g. all partially depend on irqs being disabled at
various points. Secondly, the core of the allocator deals with single
pages where as both the bulk allocator and per-cpu allocator operate in
batches. All of that has to be reconciled with all the existing users and
their constraints (memory offline, CMA and cpusets being the trickiest).
Light testing passed, I'm relying on Chuck and Jesper to test the target
users more aggressively but both report performance improvements with the
initial RFC.
Patch 1 of this series is a cleanup to sunrpc, it could be merged
separately but is included here as a pre-requisite.
Patch 2 is the prototype bulk allocator
Patch 3 is the sunrpc user. Chuck also has a patch which further caches
pages but is not included in this series. It's not directly
related to the bulk allocator and as it caches pages, it might
have other concerns (e.g. does it need a shrinker?)
Patch 4 is a preparation patch only for the network user
Patch 5 converts the net page pool to the bulk allocator for order-0 pages.
include/linux/gfp.h | 13 +++++
mm/page_alloc.c | 113 +++++++++++++++++++++++++++++++++++++++++-
net/core/page_pool.c | 102 +++++++++++++++++++++++---------------
net/sunrpc/svc_xprt.c | 47 ++++++++++++------
4 files changed, 220 insertions(+), 55 deletions(-)
--
2.26.2
This patch adds a new page allocator interface via alloc_pages_bulk,
and __alloc_pages_bulk_nodemask. A caller requests a number of pages
to be allocated and added to a list. They can be freed in bulk using
free_pages_bulk().
The API is not guaranteed to return the requested number of pages and
may fail if the preferred allocation zone has limited free memory, the
cpuset changes during the allocation or page debugging decides to fail
an allocation. It's up to the caller to request more pages in batch
if necessary.
Note that this implementation is not very efficient and could be improved
but it would require refactoring. The intent is to make it available early
to determine what semantics are required by different callers. Once the
full semantics are nailed down, it can be refactored.
Signed-off-by: Mel Gorman <redacted>
---
include/linux/gfp.h | 13 +++++
mm/page_alloc.c | 113 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 124 insertions(+), 2 deletions(-)
@@ -4436,6 +4436,21 @@ static void wake_all_kswapds(unsigned int order, gfp_t gfp_mask,}}+/* Drop reference counts and free order-0 pages from a list. */+voidfree_pages_bulk(structlist_head*list)+{+structpage*page,*next;++list_for_each_entry_safe(page,next,list,lru){+trace_mm_page_free_batched(page);+if(put_page_testzero(page)){+list_del(&page->lru);+__free_pages_ok(page,0,FPI_NONE);+}+}+}+EXPORT_SYMBOL_GPL(free_pages_bulk);+staticinlineunsignedintgfp_to_alloc_flags(gfp_tgfp_mask){
@@ -4960,6 +4978,99 @@ static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,returntrue;}+/*+*Thisisabatchedversionofthepageallocatorthatattemptsto+*allocatenr_pagesquicklyfromthepreferredzoneandaddthemtolist.+*/+int__alloc_pages_bulk_nodemask(gfp_tgfp_mask,intpreferred_nid,+nodemask_t*nodemask,intnr_pages,+structlist_head*alloc_list)+{+structpage*page;+unsignedlongflags;+structzone*zone;+structzoneref*z;+structper_cpu_pages*pcp;+structlist_head*pcp_list;+structalloc_contextac;+gfp_talloc_mask;+unsignedintalloc_flags;+intalloced=0;++if(nr_pages==1)+gotofailed;++/* May set ALLOC_NOFRAGMENT, fragmentation will return 1 page. */+if(!prepare_alloc_pages(gfp_mask,0,preferred_nid,nodemask,&ac,&alloc_mask,&alloc_flags))+return0;+gfp_mask=alloc_mask;++/* Find an allowed local zone that meets the high watermark. */+for_each_zone_zonelist_nodemask(zone,z,ac.zonelist,ac.highest_zoneidx,ac.nodemask){+unsignedlongmark;++if(cpusets_enabled()&&(alloc_flags&ALLOC_CPUSET)&&+!__cpuset_zone_allowed(zone,gfp_mask)){+continue;+}++if(nr_online_nodes>1&&zone!=ac.preferred_zoneref->zone&&+zone_to_nid(zone)!=zone_to_nid(ac.preferred_zoneref->zone)){+gotofailed;+}++mark=wmark_pages(zone,alloc_flags&ALLOC_WMARK_MASK)+nr_pages;+if(zone_watermark_fast(zone,0,mark,+zonelist_zone_idx(ac.preferred_zoneref),+alloc_flags,gfp_mask)){+break;+}+}+if(!zone)+return0;++/* Attempt the batch allocation */+local_irq_save(flags);+pcp=&this_cpu_ptr(zone->pageset)->pcp;+pcp_list=&pcp->lists[ac.migratetype];++while(alloced<nr_pages){+page=__rmqueue_pcplist(zone,ac.migratetype,alloc_flags,+pcp,pcp_list);+if(!page)+break;++prep_new_page(page,0,gfp_mask,0);+list_add(&page->lru,alloc_list);+alloced++;+}++if(!alloced)+gotofailed_irq;++if(alloced){+__count_zid_vm_events(PGALLOC,zone_idx(zone),alloced);+zone_statistics(zone,zone);+}++local_irq_restore(flags);++returnalloced;++failed_irq:+local_irq_restore(flags);++failed:+page=__alloc_pages_nodemask(gfp_mask,0,preferred_nid,nodemask);+if(page){+alloced++;+list_add(&page->lru,alloc_list);+}++returnalloced;+}+EXPORT_SYMBOL_GPL(__alloc_pages_bulk_nodemask);+/**Thisisthe'heart'ofthezonedbuddyallocator.*/
@@ -4981,8 +5092,6 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,returnNULL;}-gfp_mask&=gfp_allowed_mask;-alloc_mask=gfp_mask;if(!prepare_alloc_pages(gfp_mask,order,preferred_nid,nodemask,&ac,&alloc_mask,&alloc_flags))returnNULL;
From: Chuck Lever <redacted>
Refactor:
I'm about to use the loop variable @i for something else.
As far as the "i++" is concerned, that is a post-increment. The
value of @i is not used subsequently, so the increment operator
is unnecessary and can be removed.
Also note that nfsd_read_actor() was renamed nfsd_splice_actor()
by commit cf8208d0eabd ("sendfile: convert nfsd to
splice_direct_to_actor()").
Signed-off-by: Chuck Lever <redacted>
Signed-off-by: Mel Gorman <redacted>
---
net/sunrpc/svc_xprt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -667,8 +667,8 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)}rqstp->rq_pages[i]=p;}-rqstp->rq_page_end=&rqstp->rq_pages[i];-rqstp->rq_pages[i++]=NULL;/* this might be seen in nfs_read_actor */+rqstp->rq_page_end=&rqstp->rq_pages[pages];+rqstp->rq_pages[pages]=NULL;/* this might be seen in nfsd_splice_actor() *//* Make arg->head point to first page and arg->pages point to rest */arg=&rqstp->rq_arg;
From: Jesper Dangaard Brouer <redacted>
In preparation for next patch, move the dma mapping into its own
function, as this will make it easier to follow the changes.
V2: make page_pool_dma_map return boolean (Ilias)
Signed-off-by: Jesper Dangaard Brouer <redacted>
Signed-off-by: Mel Gorman <redacted>
---
net/core/page_pool.c | 45 +++++++++++++++++++++++++-------------------
1 file changed, 26 insertions(+), 19 deletions(-)
@@ -180,14 +180,37 @@ static void page_pool_dma_sync_for_device(struct page_pool *pool,pool->p.dma_dir);}+staticboolpage_pool_dma_map(structpage_pool*pool,structpage*page)+{+dma_addr_tdma;++/* Setup DMA mapping: use 'struct page' area for storing DMA-addr+*sincedma_addr_tcanbeeither32or64bitsanddoesnotalwaysfit+*intopageprivatedata(i.e32bitcpuwith64bitDMAcaps)+*Thismappingiskeptforlifetimeofpage,untilleavingpool.+*/+dma=dma_map_page_attrs(pool->p.dev,page,0,+(PAGE_SIZE<<pool->p.order),+pool->p.dma_dir,DMA_ATTR_SKIP_CPU_SYNC);+if(dma_mapping_error(pool->p.dev,dma))+returnfalse;++page->dma_addr=dma;++if(pool->p.flags&PP_FLAG_DMA_SYNC_DEV)+page_pool_dma_sync_for_device(pool,page,pool->p.max_len);++returntrue;+}+/* slow path */noinlinestaticstructpage*__page_pool_alloc_pages_slow(structpage_pool*pool,gfp_t_gfp){+unsignedintpp_flags=pool->p.flags;structpage*page;gfp_tgfp=_gfp;-dma_addr_tdma;/* We could always set __GFP_COMP, and avoid this branch, as*prep_new_page()canhandleorder-0with__GFP_COMP.
@@ -211,30 +234,14 @@ static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool,if(!page)returnNULL;-if(!(pool->p.flags&PP_FLAG_DMA_MAP))-gotoskip_dma_map;--/* Setup DMA mapping: use 'struct page' area for storing DMA-addr-*sincedma_addr_tcanbeeither32or64bitsanddoesnotalwaysfit-*intopageprivatedata(i.e32bitcpuwith64bitDMAcaps)-*Thismappingiskeptforlifetimeofpage,untilleavingpool.-*/-dma=dma_map_page_attrs(pool->p.dev,page,0,-(PAGE_SIZE<<pool->p.order),-pool->p.dma_dir,DMA_ATTR_SKIP_CPU_SYNC);-if(dma_mapping_error(pool->p.dev,dma)){+if(pp_flags&PP_FLAG_DMA_MAP&&+unlikely(!page_pool_dma_map(pool,page))){put_page(page);returnNULL;}-page->dma_addr=dma;-if(pool->p.flags&PP_FLAG_DMA_SYNC_DEV)-page_pool_dma_sync_for_device(pool,page,pool->p.max_len);--skip_dma_map:/* 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);/* When page just alloc'ed is should/must have refcnt 1. */
From: Jesper Dangaard Brouer <redacted>
There are cases where the page_pool need to refill with pages from the
page allocator. Some workloads cause the page_pool to release pages
instead of recycling these pages.
For these workload it can improve performance to bulk alloc pages from
the page-allocator to refill the alloc cache.
For XDP-redirect workload with 100G mlx5 driver (that use page_pool)
redirecting xdp_frame packets into a veth, that does XDP_PASS to create
an SKB from the xdp_frame, which then cannot return the page to the
page_pool. In this case, we saw[1] an improvement of 18.8% from using
the alloc_pages_bulk API (3,677,958 pps -> 4,368,926 pps).
[1] https://github.com/xdp-project/xdp-project/blob/master/areas/mem/page_pool06_alloc_pages_bulk.org
Signed-off-by: Jesper Dangaard Brouer <redacted>
Signed-off-by: Mel Gorman <redacted>
---
net/core/page_pool.c | 63 ++++++++++++++++++++++++++++----------------
1 file changed, 40 insertions(+), 23 deletions(-)
@@ -208,44 +208,61 @@ noinlinestaticstructpage*__page_pool_alloc_pages_slow(structpage_pool*pool,gfp_t_gfp){+constintbulk=PP_ALLOC_CACHE_REFILL;+structpage*page,*next,*first_page;unsignedintpp_flags=pool->p.flags;-structpage*page;+unsignedintpp_order=pool->p.order;+intpp_nid=pool->p.nid;+LIST_HEAD(page_list);gfp_tgfp=_gfp;-/* We could always set __GFP_COMP, and avoid this branch, as-*prep_new_page()canhandleorder-0with__GFP_COMP.-*/-if(pool->p.order)+/* Don't support bulk alloc for high-order pages */+if(unlikely(pp_order)){gfp|=__GFP_COMP;+first_page=alloc_pages_node(pp_nid,gfp,pp_order);+if(unlikely(!first_page))+returnNULL;+gotoout;+}-/* FUTURE development:-*-*Currentslow-pathessentiallyfallsbacktosinglepage-*allocations,whichdoesn'timproveperformance.Thiscode-*needbulkallocationsupportfromthepageallocatorcode.-*/--/* Cache was empty, do real allocation */-#ifdef CONFIG_NUMA-page=alloc_pages_node(pool->p.nid,gfp,pool->p.order);-#else-page=alloc_pages(gfp,pool->p.order);-#endif-if(!page)+if(unlikely(!__alloc_pages_bulk_nodemask(gfp,pp_nid,NULL,+bulk,&page_list)))returnNULL;+/* First page is extracted and returned to caller */+first_page=list_first_entry(&page_list,structpage,lru);+list_del(&first_page->lru);++/* Remaining pages store in alloc.cache */+list_for_each_entry_safe(page,next,&page_list,lru){+list_del(&page->lru);+if(pp_flags&PP_FLAG_DMA_MAP&&+unlikely(!page_pool_dma_map(pool,page))){+put_page(page);+continue;+}+if(likely(pool->alloc.count<PP_ALLOC_CACHE_SIZE)){+pool->alloc.cache[pool->alloc.count++]=page;+pool->pages_state_hold_cnt++;+trace_page_pool_state_hold(pool,page,+pool->pages_state_hold_cnt);+}else{+put_page(page);+}+}+out:if(pp_flags&PP_FLAG_DMA_MAP&&-unlikely(!page_pool_dma_map(pool,page))){-put_page(page);+unlikely(!page_pool_dma_map(pool,first_page))){+put_page(first_page);returnNULL;}/* 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);+trace_page_pool_state_hold(pool,first_page,pool->pages_state_hold_cnt);/* When page just alloc'ed is should/must have refcnt 1. */-returnpage;+returnfirst_page;}/* For using page_pool replace: alloc_pages() API calls, but provide
From: Chuck Lever <redacted>
Reduce the rate at which nfsd threads hammer on the page allocator.
This improve throughput scalability by enabling the threads to run
more independently of each other.
Signed-off-by: Chuck Lever <redacted>
Signed-off-by: Mel Gorman <redacted>
---
net/sunrpc/svc_xprt.c | 43 +++++++++++++++++++++++++++++++------------
1 file changed, 31 insertions(+), 12 deletions(-)
@@ -642,11 +642,12 @@ static void svc_check_conn_limits(struct svc_serv *serv)staticintsvc_alloc_arg(structsvc_rqst*rqstp){structsvc_serv*serv=rqstp->rq_server;+unsignedlongneeded;structxdr_buf*arg;+structpage*page;intpages;inti;-/* now allocate needed pages. If we get a failure, sleep briefly */pages=(serv->sv_max_mesg+2*PAGE_SIZE)>>PAGE_SHIFT;if(pages>RPCSVC_MAXPAGES){pr_warn_once("svc: warning: pages=%u > RPCSVC_MAXPAGES=%lu\n",
@@ -654,19 +655,28 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)/* use as many pages as possible */pages=RPCSVC_MAXPAGES;}-for(i=0;i<pages;i++)-while(rqstp->rq_pages[i]==NULL){-structpage*p=alloc_page(GFP_KERNEL);-if(!p){-set_current_state(TASK_INTERRUPTIBLE);-if(signalled()||kthread_should_stop()){-set_current_state(TASK_RUNNING);-return-EINTR;-}-schedule_timeout(msecs_to_jiffies(500));++for(needed=0,i=0;i<pages;i++)+if(!rqstp->rq_pages[i])+needed++;+if(needed){+LIST_HEAD(list);++retry:+alloc_pages_bulk(GFP_KERNEL,needed,&list);+for(i=0;i<pages;i++){+if(!rqstp->rq_pages[i]){+page=list_first_entry_or_null(&list,+structpage,+lru);+if(unlikely(!page))+gotoempty_list;+list_del(&page->lru);+rqstp->rq_pages[i]=page;+needed--;}-rqstp->rq_pages[i]=p;}+}rqstp->rq_page_end=&rqstp->rq_pages[pages];rqstp->rq_pages[pages]=NULL;/* this might be seen in nfsd_splice_actor() */
@@ -681,6 +691,15 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)arg->len=(pages-1)*PAGE_SIZE;arg->tail[0].iov_len=0;return0;++empty_list:+set_current_state(TASK_INTERRUPTIBLE);+if(signalled()||kthread_should_stop()){+set_current_state(TASK_RUNNING);+return-EINTR;+}+schedule_timeout(msecs_to_jiffies(500));+gotoretry;}staticbool
Hi Mel,
Can you please CC me in future revisions. I almost missed that!
On Mon, Mar 01, 2021 at 04:11:59PM +0000, Mel Gorman wrote:
From: Jesper Dangaard Brouer <redacted>
In preparation for next patch, move the dma mapping into its own
function, as this will make it easier to follow the changes.
V2: make page_pool_dma_map return boolean (Ilias)
[...]
quoted hunk
static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool,
gfp_t _gfp)
{
+ unsigned int pp_flags = pool->p.flags;
struct page *page;
gfp_t gfp = _gfp;
- dma_addr_t dma;
/* We could always set __GFP_COMP, and avoid this branch, as
* prep_new_page() can handle order-0 with __GFP_COMP.
@@ -211,30 +234,14 @@ static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool, if (!page) return NULL;- if (!(pool->p.flags & PP_FLAG_DMA_MAP))- goto skip_dma_map;-- /* Setup DMA mapping: use 'struct page' area for storing DMA-addr- * since dma_addr_t can be either 32 or 64 bits and does not always fit- * into page private data (i.e 32bit cpu with 64bit DMA caps)- * This mapping is kept for lifetime of page, until leaving pool.- */- dma = dma_map_page_attrs(pool->p.dev, page, 0,- (PAGE_SIZE << pool->p.order),- pool->p.dma_dir, DMA_ATTR_SKIP_CPU_SYNC);- if (dma_mapping_error(pool->p.dev, dma)) {+ if (pp_flags & PP_FLAG_DMA_MAP &&
Nit pick but can we have if ((pp_flags & PP_FLAG_DMA_MAP) && ...
+ unlikely(!page_pool_dma_map(pool, page))) {
put_page(page);
return NULL;
}
- page->dma_addr = dma;
- if (pool->p.flags & PP_FLAG_DMA_SYNC_DEV)
- page_pool_dma_sync_for_device(pool, page, pool->p.max_len);
-
-skip_dma_map:
/* 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);
/* When page just alloc'ed is should/must have refcnt 1. */
--
2.26.2
On Mon, Mar 01, 2021 at 04:12:00PM +0000, Mel Gorman wrote:
quoted hunk
From: Jesper Dangaard Brouer <redacted>
There are cases where the page_pool need to refill with pages from the
page allocator. Some workloads cause the page_pool to release pages
instead of recycling these pages.
For these workload it can improve performance to bulk alloc pages from
the page-allocator to refill the alloc cache.
For XDP-redirect workload with 100G mlx5 driver (that use page_pool)
redirecting xdp_frame packets into a veth, that does XDP_PASS to create
an SKB from the xdp_frame, which then cannot return the page to the
page_pool. In this case, we saw[1] an improvement of 18.8% from using
the alloc_pages_bulk API (3,677,958 pps -> 4,368,926 pps).
[1] https://github.com/xdp-project/xdp-project/blob/master/areas/mem/page_pool06_alloc_pages_bulk.org
Signed-off-by: Jesper Dangaard Brouer <redacted>
Signed-off-by: Mel Gorman <redacted>
---
net/core/page_pool.c | 63 ++++++++++++++++++++++++++++----------------
1 file changed, 40 insertions(+), 23 deletions(-)
@@ -208,44 +208,61 @@ noinlinestaticstructpage*__page_pool_alloc_pages_slow(structpage_pool*pool,gfp_t_gfp){+constintbulk=PP_ALLOC_CACHE_REFILL;+structpage*page,*next,*first_page;unsignedintpp_flags=pool->p.flags;-structpage*page;+unsignedintpp_order=pool->p.order;+intpp_nid=pool->p.nid;+LIST_HEAD(page_list);gfp_tgfp=_gfp;-/* We could always set __GFP_COMP, and avoid this branch, as-*prep_new_page()canhandleorder-0with__GFP_COMP.-*/-if(pool->p.order)+/* Don't support bulk alloc for high-order pages */+if(unlikely(pp_order)){gfp|=__GFP_COMP;+first_page=alloc_pages_node(pp_nid,gfp,pp_order);+if(unlikely(!first_page))+returnNULL;+gotoout;+}-/* FUTURE development:-*-*Currentslow-pathessentiallyfallsbacktosinglepage-*allocations,whichdoesn'timproveperformance.Thiscode-*needbulkallocationsupportfromthepageallocatorcode.-*/--/* Cache was empty, do real allocation */-#ifdef CONFIG_NUMA-page=alloc_pages_node(pool->p.nid,gfp,pool->p.order);-#else-page=alloc_pages(gfp,pool->p.order);-#endif-if(!page)+if(unlikely(!__alloc_pages_bulk_nodemask(gfp,pp_nid,NULL,+bulk,&page_list)))returnNULL;+/* First page is extracted and returned to caller */+first_page=list_first_entry(&page_list,structpage,lru);+list_del(&first_page->lru);++/* Remaining pages store in alloc.cache */+list_for_each_entry_safe(page,next,&page_list,lru){+list_del(&page->lru);+if(pp_flags&PP_FLAG_DMA_MAP&&+unlikely(!page_pool_dma_map(pool,page))){+put_page(page);+continue;+}+if(likely(pool->alloc.count<PP_ALLOC_CACHE_SIZE)){+pool->alloc.cache[pool->alloc.count++]=page;+pool->pages_state_hold_cnt++;+trace_page_pool_state_hold(pool,page,+pool->pages_state_hold_cnt);+}else{+put_page(page);+}+}+out:if(pp_flags&PP_FLAG_DMA_MAP&&-unlikely(!page_pool_dma_map(pool,page))){-put_page(page);+unlikely(!page_pool_dma_map(pool,first_page))){+put_page(first_page);returnNULL;}/* 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);+trace_page_pool_state_hold(pool,first_page,pool->pages_state_hold_cnt);/* When page just alloc'ed is should/must have refcnt 1. */-returnpage;+returnfirst_page;}/* For using page_pool replace: alloc_pages() API calls, but provide
On Wed, 3 Mar 2021 09:18:25 +0000
Mel Gorman [off-list ref] wrote:
On Tue, Mar 02, 2021 at 08:49:06PM +0200, Ilias Apalodimas wrote:
quoted
On Mon, Mar 01, 2021 at 04:11:59PM +0000, Mel Gorman wrote:
quoted
From: Jesper Dangaard Brouer <redacted>
In preparation for next patch, move the dma mapping into its own
function, as this will make it easier to follow the changes.
V2: make page_pool_dma_map return boolean (Ilias)
[...]
quoted
@@ -211,30 +234,14 @@ static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool, if (!page) return NULL;- if (!(pool->p.flags & PP_FLAG_DMA_MAP))- goto skip_dma_map;-- /* Setup DMA mapping: use 'struct page' area for storing DMA-addr- * since dma_addr_t can be either 32 or 64 bits and does not always fit- * into page private data (i.e 32bit cpu with 64bit DMA caps)- * This mapping is kept for lifetime of page, until leaving pool.- */- dma = dma_map_page_attrs(pool->p.dev, page, 0,- (PAGE_SIZE << pool->p.order),- pool->p.dma_dir, DMA_ATTR_SKIP_CPU_SYNC);- if (dma_mapping_error(pool->p.dev, dma)) {+ if (pp_flags & PP_FLAG_DMA_MAP &&
Nit pick but can we have if ((pp_flags & PP_FLAG_DMA_MAP) && ...
Done.
Thanks for fixing this nitpick, and carrying the patch.
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
LinkedIn: http://www.linkedin.com/in/brouer
On Tue, Mar 02, 2021 at 08:49:06PM +0200, Ilias Apalodimas wrote:
Hi Mel,
Can you please CC me in future revisions. I almost missed that!
Will do.
On Mon, Mar 01, 2021 at 04:11:59PM +0000, Mel Gorman wrote:
quoted
From: Jesper Dangaard Brouer <redacted>
In preparation for next patch, move the dma mapping into its own
function, as this will make it easier to follow the changes.
V2: make page_pool_dma_map return boolean (Ilias)
[...]
quoted
@@ -211,30 +234,14 @@ static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool, if (!page) return NULL;- if (!(pool->p.flags & PP_FLAG_DMA_MAP))- goto skip_dma_map;-- /* Setup DMA mapping: use 'struct page' area for storing DMA-addr- * since dma_addr_t can be either 32 or 64 bits and does not always fit- * into page private data (i.e 32bit cpu with 64bit DMA caps)- * This mapping is kept for lifetime of page, until leaving pool.- */- dma = dma_map_page_attrs(pool->p.dev, page, 0,- (PAGE_SIZE << pool->p.order),- pool->p.dma_dir, DMA_ATTR_SKIP_CPU_SYNC);- if (dma_mapping_error(pool->p.dev, dma)) {+ if (pp_flags & PP_FLAG_DMA_MAP &&
Nit pick but can we have if ((pp_flags & PP_FLAG_DMA_MAP) && ...
From: Christoph Hellwig <hch@infradead.org> Date: 2021-03-09 17:14:06
Would vmalloc be another good user of this API?
+ /* May set ALLOC_NOFRAGMENT, fragmentation will return 1 page. */
+ if (!prepare_alloc_pages(gfp_mask, 0, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
This crazy long line is really hard to follow.
+ return 0;
+ gfp_mask = alloc_mask;
+
+ /* Find an allowed local zone that meets the high watermark. */
+ for_each_zone_zonelist_nodemask(zone, z, ac.zonelist, ac.highest_zoneidx, ac.nodemask) {
On Tue, Mar 09, 2021 at 05:12:30PM +0000, Christoph Hellwig wrote:
Would vmalloc be another good user of this API?
quoted
+ /* May set ALLOC_NOFRAGMENT, fragmentation will return 1 page. */
+ if (!prepare_alloc_pages(gfp_mask, 0, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
This crazy long line is really hard to follow.
It's not crazier than what is already in alloc_pages_nodemask to share
code.
quoted
+ return 0;
+ gfp_mask = alloc_mask;
+
+ /* Find an allowed local zone that meets the high watermark. */
+ for_each_zone_zonelist_nodemask(zone, z, ac.zonelist, ac.highest_zoneidx, ac.nodemask) {
Same here.
Similar to what happens in get_page_from_freelist with the
for_next_zone_zonelist_nodemask iterator.
Yes, but it's for coding style. MM has no hard coding style guidelines
around this but for sched, it's generally preferred that if the "if"
statement spans multiple lines then it should use {} even if the block
is one line long for clarity.
@@ -4436,6 +4436,21 @@ static void wake_all_kswapds(unsigned int
order, gfp_t gfp_mask,
}
}
...
+/*
+ * This is a batched version of the page allocator that
attempts to
+ * allocate nr_pages quickly from the preferred zone and add
them to list.
+ */
+int __alloc_pages_bulk_nodemask(gfp_t gfp_mask, int
preferred_nid,
+ nodemask_t *nodemask, int nr_pages,
+ struct list_head *alloc_list)
+{
+ struct page *page;
+ unsigned long flags;
+ struct zone *zone;
+ struct zoneref *z;
+ struct per_cpu_pages *pcp;
+ struct list_head *pcp_list;
+ struct alloc_context ac;
+ gfp_t alloc_mask;
+ unsigned int alloc_flags;
+ int alloced = 0;
Does alloced count the number of allocated pages ? Do you mind
renaming it to 'allocated' ?
+
+ if (nr_pages == 1)
+ goto failed;
+
+ /* May set ALLOC_NOFRAGMENT, fragmentation will return 1
page. */
+ if (!prepare_alloc_pages(gfp_mask, 0, preferred_nid,
nodemask, &ac, &alloc_mask, &alloc_flags))
+ return 0;
+ gfp_mask = alloc_mask;
+
+ /* Find an allowed local zone that meets the high
watermark. */
+ for_each_zone_zonelist_nodemask(zone, z, ac.zonelist,
ac.highest_zoneidx, ac.nodemask) {
+ unsigned long mark;
+
+ if (cpusets_enabled() && (alloc_flags &
ALLOC_CPUSET) &&
+ !__cpuset_zone_allowed(zone, gfp_mask)) {
+ continue;
+ }
+
+ if (nr_online_nodes > 1 && zone !=
ac.preferred_zoneref->zone &&
+ zone_to_nid(zone) !=
zone_to_nid(ac.preferred_zoneref->zone)) {
+ goto failed;
+ }
+
+ mark = wmark_pages(zone, alloc_flags &
ALLOC_WMARK_MASK) + nr_pages;
+ if (zone_watermark_fast(zone, 0, mark,
+
zonelist_zone_idx(ac.preferred_zoneref),
+ alloc_flags, gfp_mask)) {
+ break;
+ }
+ }
+ if (!zone)
+ return 0;
+
+ /* Attempt the batch allocation */
+ local_irq_save(flags);
+ pcp = &this_cpu_ptr(zone->pageset)->pcp;
+ pcp_list = &pcp->lists[ac.migratetype];
+
+ while (alloced < nr_pages) {
+ page = __rmqueue_pcplist(zone, ac.migratetype,
alloc_flags,
+
pcp, pcp_list);
Same indentation comment as before
quoted hunk
+ if (!page)
+ break;
+
+ prep_new_page(page, 0, gfp_mask, 0);
+ list_add(&page->lru, alloc_list);
+ alloced++;
+ }
+
+ if (!alloced)
+ goto failed_irq;
+
+ if (alloced) {
+ __count_zid_vm_events(PGALLOC, zone_idx(zone),
alloced);
+ zone_statistics(zone, zone);
+ }
+
+ local_irq_restore(flags);
+
+ return alloced;
+
+failed_irq:
+ local_irq_restore(flags);
+
+failed:
+ page = __alloc_pages_nodemask(gfp_mask, 0, preferred_nid,
nodemask);
+ if (page) {
+ alloced++;
+ list_add(&page->lru, alloc_list);
+ }
+
+ return alloced;
+}
+EXPORT_SYMBOL_GPL(__alloc_pages_bulk_nodemask);
+
/*
* This is the 'heart' of the zoned buddy allocator.
*/
@@ -4436,6 +4436,21 @@ static void wake_all_kswapds(unsigned int order,
gfp_t gfp_mask,
}
}
...
+/*
+ * This is a batched version of the page allocator that attempts to
+ * allocate nr_pages quickly from the preferred zone and add them to
list.
+ */
+int __alloc_pages_bulk_nodemask(gfp_t gfp_mask, int preferred_nid,
+ nodemask_t *nodemask, int nr_pages,
+ struct list_head *alloc_list)
+{
+ struct page *page;
+ unsigned long flags;
+ struct zone *zone;
+ struct zoneref *z;
+ struct per_cpu_pages *pcp;
+ struct list_head *pcp_list;
+ struct alloc_context ac;
+ gfp_t alloc_mask;
+ unsigned int alloc_flags;
+ int alloced = 0;
Does alloced count the number of allocated pages ?
Yes.
Do you mind renaming it to 'allocated' ?
I will if there is another version as I do not feel particularly strongly
about alloced vs allocated. alloc was to match the function name and I
don't think the change makes it much clearer.
Again, simple personal perference to avoid any possibility it's mixed
up with a later line. There has not been consistent code styling
enforcement of what indentation style should be used for a multi-line
within mm/page_alloc.c
--
Mel Gorman
SUSE Labs
Is the second line indentation intentional ? Why not align it to the first
argument (gfp_mask) ?
No particular reason. I usually pick this as it's visually clearer to me
that it's part of the same line when the multi-line is part of an if block.
quoted
quoted
+}
+
[...]
quoted
Same indentation comment as before
Again, simple personal perference to avoid any possibility it's mixed
up with a later line. There has not been consistent code styling
enforcement of what indentation style should be used for a multi-line
within mm/page_alloc.c
Hi Shay, it is might be surprising that indentation style actually
differs slightly in different parts of the kernel. I started in
networking area of the kernel, and I was also surprised when I started
working in MM area that the coding style differs. I can tell you that
the indentation style Mel choose is consistent with the code styling in
MM area. I usually respect that even-though I prefer the networking
style as I was "raised" with that style.
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
LinkedIn: http://www.linkedin.com/in/brouer