Commit c4386bd8 authored by Joao Martins's avatar Joao Martins Committed by Linus Torvalds

mm/memremap: add ZONE_DEVICE support for compound pages

Add a new @vmemmap_shift property for struct dev_pagemap which specifies
that a devmap is composed of a set of compound pages of order
@vmemmap_shift, instead of base pages.  When a compound page devmap is
requested, all but the first page are initialised as tail pages instead
of order-0 pages.

For certain ZONE_DEVICE users like device-dax which have a fixed page
size, this creates an opportunity to optimize GUP and GUP-fast walkers,
treating it the same way as THP or hugetlb pages.

Additionally, commit 7118fc29 ("hugetlb: address ref count racing in
prep_compound_gigantic_page") removed set_page_count() because the
setting of page ref count to zero was redundant.  devmap pages don't
come from page allocator though and only head page refcount is used for
compound pages, hence initialize tail page count to zero.

Link: https://lkml.kernel.org/r/20211202204422.26777-5-joao.m.martins@oracle.comSigned-off-by: default avatarJoao Martins <joao.m.martins@oracle.com>
Reviewed-by: default avatarDan Williams <dan.j.williams@intel.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Jane Chu <jane.chu@oracle.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Muchun Song <songmuchun@bytedance.com>
Cc: Naoya Horiguchi <naoya.horiguchi@nec.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 46487e00
...@@ -99,6 +99,11 @@ struct dev_pagemap_ops { ...@@ -99,6 +99,11 @@ struct dev_pagemap_ops {
* @done: completion for @internal_ref * @done: completion for @internal_ref
* @type: memory type: see MEMORY_* in memory_hotplug.h * @type: memory type: see MEMORY_* in memory_hotplug.h
* @flags: PGMAP_* flags to specify defailed behavior * @flags: PGMAP_* flags to specify defailed behavior
* @vmemmap_shift: structural definition of how the vmemmap page metadata
* is populated, specifically the metadata page order.
* A zero value (default) uses base pages as the vmemmap metadata
* representation. A bigger value will set up compound struct pages
* of the requested order value.
* @ops: method table * @ops: method table
* @owner: an opaque pointer identifying the entity that manages this * @owner: an opaque pointer identifying the entity that manages this
* instance. Used by various helpers to make sure that no * instance. Used by various helpers to make sure that no
...@@ -114,6 +119,7 @@ struct dev_pagemap { ...@@ -114,6 +119,7 @@ struct dev_pagemap {
struct completion done; struct completion done;
enum memory_type type; enum memory_type type;
unsigned int flags; unsigned int flags;
unsigned long vmemmap_shift;
const struct dev_pagemap_ops *ops; const struct dev_pagemap_ops *ops;
void *owner; void *owner;
int nr_range; int nr_range;
...@@ -130,6 +136,11 @@ static inline struct vmem_altmap *pgmap_altmap(struct dev_pagemap *pgmap) ...@@ -130,6 +136,11 @@ static inline struct vmem_altmap *pgmap_altmap(struct dev_pagemap *pgmap)
return NULL; return NULL;
} }
static inline unsigned long pgmap_vmemmap_nr(struct dev_pagemap *pgmap)
{
return 1 << pgmap->vmemmap_shift;
}
#ifdef CONFIG_ZONE_DEVICE #ifdef CONFIG_ZONE_DEVICE
void *memremap_pages(struct dev_pagemap *pgmap, int nid); void *memremap_pages(struct dev_pagemap *pgmap, int nid);
void memunmap_pages(struct dev_pagemap *pgmap); void memunmap_pages(struct dev_pagemap *pgmap);
......
...@@ -102,15 +102,22 @@ static unsigned long pfn_end(struct dev_pagemap *pgmap, int range_id) ...@@ -102,15 +102,22 @@ static unsigned long pfn_end(struct dev_pagemap *pgmap, int range_id)
return (range->start + range_len(range)) >> PAGE_SHIFT; return (range->start + range_len(range)) >> PAGE_SHIFT;
} }
static unsigned long pfn_next(unsigned long pfn) static unsigned long pfn_next(struct dev_pagemap *pgmap, unsigned long pfn)
{ {
if (pfn % 1024 == 0) if (pfn % (1024 << pgmap->vmemmap_shift))
cond_resched(); cond_resched();
return pfn + 1; return pfn + pgmap_vmemmap_nr(pgmap);
}
static unsigned long pfn_len(struct dev_pagemap *pgmap, unsigned long range_id)
{
return (pfn_end(pgmap, range_id) -
pfn_first(pgmap, range_id)) >> pgmap->vmemmap_shift;
} }
#define for_each_device_pfn(pfn, map, i) \ #define for_each_device_pfn(pfn, map, i) \
for (pfn = pfn_first(map, i); pfn < pfn_end(map, i); pfn = pfn_next(pfn)) for (pfn = pfn_first(map, i); pfn < pfn_end(map, i); \
pfn = pfn_next(map, pfn))
static void dev_pagemap_kill(struct dev_pagemap *pgmap) static void dev_pagemap_kill(struct dev_pagemap *pgmap)
{ {
...@@ -295,8 +302,7 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params, ...@@ -295,8 +302,7 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,
memmap_init_zone_device(&NODE_DATA(nid)->node_zones[ZONE_DEVICE], memmap_init_zone_device(&NODE_DATA(nid)->node_zones[ZONE_DEVICE],
PHYS_PFN(range->start), PHYS_PFN(range->start),
PHYS_PFN(range_len(range)), pgmap); PHYS_PFN(range_len(range)), pgmap);
percpu_ref_get_many(pgmap->ref, pfn_end(pgmap, range_id) percpu_ref_get_many(pgmap->ref, pfn_len(pgmap, range_id));
- pfn_first(pgmap, range_id));
return 0; return 0;
err_add_memory: err_add_memory:
......
...@@ -6612,6 +6612,35 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, ...@@ -6612,6 +6612,35 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
} }
} }
static void __ref memmap_init_compound(struct page *head,
unsigned long head_pfn,
unsigned long zone_idx, int nid,
struct dev_pagemap *pgmap,
unsigned long nr_pages)
{
unsigned long pfn, end_pfn = head_pfn + nr_pages;
unsigned int order = pgmap->vmemmap_shift;
__SetPageHead(head);
for (pfn = head_pfn + 1; pfn < end_pfn; pfn++) {
struct page *page = pfn_to_page(pfn);
__init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
prep_compound_tail(head, pfn - head_pfn);
set_page_count(page, 0);
/*
* The first tail page stores compound_mapcount_ptr() and
* compound_order() and the second tail page stores
* compound_pincount_ptr(). Call prep_compound_head() after
* the first and second tail pages have been initialized to
* not have the data overwritten.
*/
if (pfn == head_pfn + 2)
prep_compound_head(head, order);
}
}
void __ref memmap_init_zone_device(struct zone *zone, void __ref memmap_init_zone_device(struct zone *zone,
unsigned long start_pfn, unsigned long start_pfn,
unsigned long nr_pages, unsigned long nr_pages,
...@@ -6620,6 +6649,7 @@ void __ref memmap_init_zone_device(struct zone *zone, ...@@ -6620,6 +6649,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
unsigned long pfn, end_pfn = start_pfn + nr_pages; unsigned long pfn, end_pfn = start_pfn + nr_pages;
struct pglist_data *pgdat = zone->zone_pgdat; struct pglist_data *pgdat = zone->zone_pgdat;
struct vmem_altmap *altmap = pgmap_altmap(pgmap); struct vmem_altmap *altmap = pgmap_altmap(pgmap);
unsigned int pfns_per_compound = pgmap_vmemmap_nr(pgmap);
unsigned long zone_idx = zone_idx(zone); unsigned long zone_idx = zone_idx(zone);
unsigned long start = jiffies; unsigned long start = jiffies;
int nid = pgdat->node_id; int nid = pgdat->node_id;
...@@ -6637,10 +6667,16 @@ void __ref memmap_init_zone_device(struct zone *zone, ...@@ -6637,10 +6667,16 @@ void __ref memmap_init_zone_device(struct zone *zone,
nr_pages = end_pfn - start_pfn; nr_pages = end_pfn - start_pfn;
} }
for (pfn = start_pfn; pfn < end_pfn; pfn++) { for (pfn = start_pfn; pfn < end_pfn; pfn += pfns_per_compound) {
struct page *page = pfn_to_page(pfn); struct page *page = pfn_to_page(pfn);
__init_zone_device_page(page, pfn, zone_idx, nid, pgmap); __init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
if (pfns_per_compound == 1)
continue;
memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
pfns_per_compound);
} }
pr_info("%s initialised %lu pages in %ums\n", __func__, pr_info("%s initialised %lu pages in %ums\n", __func__,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment