Commit ef233450 authored by Alistair Popple's avatar Alistair Popple Committed by Andrew Morton

mm: free device private pages have zero refcount

Since 27674ef6 ("mm: remove the extra ZONE_DEVICE struct page
refcount") device private pages have no longer had an extra reference
count when the page is in use.  However before handing them back to the
owning device driver we add an extra reference count such that free pages
have a reference count of one.

This makes it difficult to tell if a page is free or not because both free
and in use pages will have a non-zero refcount.  Instead we should return
pages to the drivers page allocator with a zero reference count.  Kernel
code can then safely use kernel functions such as get_page_unless_zero().

Link: https://lkml.kernel.org/r/cf70cf6f8c0bdb8aaebdbfb0d790aea4c683c3c6.1664366292.git-series.apopple@nvidia.comSigned-off-by: default avatarAlistair Popple <apopple@nvidia.com>
Acked-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: Lyude Paul <lyude@redhat.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Alex Sierra <alex.sierra@amd.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: "Huang, Ying" <ying.huang@intel.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Yang Shi <shy828301@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 16ce101d
...@@ -718,7 +718,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long gpa, struct kvm *kvm) ...@@ -718,7 +718,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long gpa, struct kvm *kvm)
dpage = pfn_to_page(uvmem_pfn); dpage = pfn_to_page(uvmem_pfn);
dpage->zone_device_data = pvt; dpage->zone_device_data = pvt;
lock_page(dpage); zone_device_page_init(dpage);
return dpage; return dpage;
out_clear: out_clear:
spin_lock(&kvmppc_uvmem_bitmap_lock); spin_lock(&kvmppc_uvmem_bitmap_lock);
......
...@@ -223,7 +223,7 @@ svm_migrate_get_vram_page(struct svm_range *prange, unsigned long pfn) ...@@ -223,7 +223,7 @@ svm_migrate_get_vram_page(struct svm_range *prange, unsigned long pfn)
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
svm_range_bo_ref(prange->svm_bo); svm_range_bo_ref(prange->svm_bo);
page->zone_device_data = prange->svm_bo; page->zone_device_data = prange->svm_bo;
lock_page(page); zone_device_page_init(page);
} }
static void static void
......
...@@ -326,7 +326,7 @@ nouveau_dmem_page_alloc_locked(struct nouveau_drm *drm) ...@@ -326,7 +326,7 @@ nouveau_dmem_page_alloc_locked(struct nouveau_drm *drm)
return NULL; return NULL;
} }
lock_page(page); zone_device_page_init(page);
return page; return page;
} }
......
...@@ -187,6 +187,7 @@ static inline bool folio_is_device_coherent(const struct folio *folio) ...@@ -187,6 +187,7 @@ static inline bool folio_is_device_coherent(const struct folio *folio)
} }
#ifdef CONFIG_ZONE_DEVICE #ifdef CONFIG_ZONE_DEVICE
void zone_device_page_init(struct page *page);
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);
void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap); void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap);
......
...@@ -627,8 +627,8 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice) ...@@ -627,8 +627,8 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice)
goto error; goto error;
} }
zone_device_page_init(dpage);
dpage->zone_device_data = rpage; dpage->zone_device_data = rpage;
lock_page(dpage);
return dpage; return dpage;
error: error:
......
...@@ -505,8 +505,17 @@ void free_zone_device_page(struct page *page) ...@@ -505,8 +505,17 @@ void free_zone_device_page(struct page *page)
/* /*
* Reset the page count to 1 to prepare for handing out the page again. * Reset the page count to 1 to prepare for handing out the page again.
*/ */
if (page->pgmap->type != MEMORY_DEVICE_PRIVATE &&
page->pgmap->type != MEMORY_DEVICE_COHERENT)
set_page_count(page, 1);
}
void zone_device_page_init(struct page *page)
{
set_page_count(page, 1); set_page_count(page, 1);
lock_page(page);
} }
EXPORT_SYMBOL_GPL(zone_device_page_init);
#ifdef CONFIG_FS_DAX #ifdef CONFIG_FS_DAX
bool __put_devmap_managed_page_refs(struct page *page, int refs) bool __put_devmap_managed_page_refs(struct page *page, int refs)
......
...@@ -6819,6 +6819,14 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, ...@@ -6819,6 +6819,14 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
set_pageblock_migratetype(page, MIGRATE_MOVABLE); set_pageblock_migratetype(page, MIGRATE_MOVABLE);
cond_resched(); cond_resched();
} }
/*
* ZONE_DEVICE pages are released directly to the driver page allocator
* which will set the page count to 1 when allocating the page.
*/
if (pgmap->type == MEMORY_DEVICE_PRIVATE ||
pgmap->type == MEMORY_DEVICE_COHERENT)
set_page_count(page, 0);
} }
/* /*
......
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