/* * The memremap() and memremap_pages() interfaces are alternately used * to map persistent memory namespaces. These interfaces place different * constraints on the alignment and size of the mapping (namespace). * memremap() can map individual PAGE_SIZE pages. memremap_pages() can * only map subsections (2MB), and at least one architecture (PowerPC) * the minimum mapping granularity of memremap_pages() is 16MB. * * The role of memremap_compat_align() is to communicate the minimum * arch supported alignment of a namespace such that it can freely * switch modes without violating the arch constraint. Namely, do not * allow a namespace to be PAGE_SIZE aligned since that namespace may be * reconfigured into a mode that requires SUBSECTION_SIZE alignment.
*/ #ifndef CONFIG_ARCH_HAS_MEMREMAP_COMPAT_ALIGN unsignedlong memremap_compat_align(void)
{ return SUBSECTION_SIZE;
}
EXPORT_SYMBOL_GPL(memremap_compat_align); #endif
void memunmap_pages(struct dev_pagemap *pgmap)
{ int i;
percpu_ref_kill(&pgmap->ref); if (pgmap->type != MEMORY_DEVICE_PRIVATE &&
pgmap->type != MEMORY_DEVICE_COHERENT) for (i = 0; i < pgmap->nr_range; i++)
percpu_ref_put_many(&pgmap->ref, pfn_len(pgmap, i));
wait_for_completion(&pgmap->done);
for (i = 0; i < pgmap->nr_range; i++)
pageunmap_range(pgmap, i);
percpu_ref_exit(&pgmap->ref);
WARN_ONCE(pgmap->altmap.alloc, "failed to free all reserved pages\n");
}
EXPORT_SYMBOL_GPL(memunmap_pages);
/* * For device private memory we call add_pages() as we only need to * allocate and initialize struct page for the device memory. More- * over the device memory is un-accessible thus we do not want to * create a linear mapping for the memory like arch_add_memory() * would do. * * For all other device memory types, which are accessible by * the CPU, we do want the linear mapping and thus use * arch_add_memory().
*/ if (is_private) {
error = add_pages(nid, PHYS_PFN(range->start),
PHYS_PFN(range_len(range)), params);
} else {
error = kasan_add_zero_shadow(__va(range->start), range_len(range)); if (error) {
mem_hotplug_done(); goto err_kasan;
}
zone = &NODE_DATA(nid)->node_zones[ZONE_DEVICE];
move_pfn_range_to_zone(zone, PHYS_PFN(range->start),
PHYS_PFN(range_len(range)), params->altmap,
MIGRATE_MOVABLE, false);
}
mem_hotplug_done(); if (error) goto err_add_memory;
/* * Initialization of the pages has been deferred until now in order * to allow us to do the work while not holding the hotplug lock.
*/
memmap_init_zone_device(&NODE_DATA(nid)->node_zones[ZONE_DEVICE],
PHYS_PFN(range->start),
PHYS_PFN(range_len(range)), pgmap); if (pgmap->type != MEMORY_DEVICE_PRIVATE &&
pgmap->type != MEMORY_DEVICE_COHERENT)
percpu_ref_get_many(&pgmap->ref, pfn_len(pgmap, range_id)); return 0;
/* * Not device managed version of devm_memremap_pages, undone by * memunmap_pages(). Please use devm_memremap_pages if you have a struct * device available.
*/ void *memremap_pages(struct dev_pagemap *pgmap, int nid)
{ struct mhp_params params = {
.altmap = pgmap_altmap(pgmap),
.pgmap = pgmap,
.pgprot = PAGE_KERNEL,
}; constint nr_range = pgmap->nr_range; int error, i;
if (WARN_ONCE(!nr_range, "nr_range must be specified\n")) return ERR_PTR(-EINVAL);
switch (pgmap->type) { case MEMORY_DEVICE_PRIVATE: if (!IS_ENABLED(CONFIG_DEVICE_PRIVATE)) {
WARN(1, "Device private memory not supported\n"); return ERR_PTR(-EINVAL);
} if (!pgmap->ops || !pgmap->ops->migrate_to_ram) {
WARN(1, "Missing migrate_to_ram method\n"); return ERR_PTR(-EINVAL);
} if (!pgmap->ops->page_free) {
WARN(1, "Missing page_free method\n"); return ERR_PTR(-EINVAL);
} if (!pgmap->owner) {
WARN(1, "Missing owner\n"); return ERR_PTR(-EINVAL);
} break; case MEMORY_DEVICE_COHERENT: if (!pgmap->ops->page_free) {
WARN(1, "Missing page_free method\n"); return ERR_PTR(-EINVAL);
} if (!pgmap->owner) {
WARN(1, "Missing owner\n"); return ERR_PTR(-EINVAL);
} break; case MEMORY_DEVICE_FS_DAX:
params.pgprot = pgprot_decrypted(params.pgprot); break; case MEMORY_DEVICE_GENERIC: break; case MEMORY_DEVICE_PCI_P2PDMA:
params.pgprot = pgprot_noncached(params.pgprot); break; default:
WARN(1, "Invalid pgmap type %d\n", pgmap->type); break;
}
/* * Clear the pgmap nr_range as it will be incremented for each * successfully processed range. This communicates how many * regions to unwind in the abort case.
*/
pgmap->nr_range = 0;
error = 0; for (i = 0; i < nr_range; i++) {
error = pagemap_range(pgmap, ¶ms, i, nid); if (error) break;
pgmap->nr_range++;
}
if (i < nr_range) {
memunmap_pages(pgmap);
pgmap->nr_range = nr_range; return ERR_PTR(error);
}
/** * devm_memremap_pages - remap and provide memmap backing for the given resource * @dev: hosting device for @res * @pgmap: pointer to a struct dev_pagemap * * Notes: * 1/ At a minimum the range and type members of @pgmap must be initialized * by the caller before passing it to this function * * 2/ The altmap field may optionally be initialized, in which case * PGMAP_ALTMAP_VALID must be set in pgmap->flags. * * 3/ The ref field may optionally be provided, in which pgmap->ref must be * 'live' on entry and will be killed and reaped at * devm_memremap_pages_release() time, or if this routine fails. * * 4/ range is expected to be a host memory range that could feasibly be * treated as a "System RAM" range, i.e. not a device mmio range, but * this is not enforced.
*/ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
{ int error; void *ret;
ret = memremap_pages(pgmap, dev_to_node(dev)); if (IS_ERR(ret)) return ret;
/** * get_dev_pagemap() - take a new live reference on the dev_pagemap for @pfn * @pfn: page frame number to lookup page_map * @pgmap: optional known pgmap that already has a reference * * If @pgmap is non-NULL and covers @pfn it will be returned as-is. If @pgmap * is non-NULL but does not cover @pfn the reference to it will be released.
*/ struct dev_pagemap *get_dev_pagemap(unsignedlong pfn, struct dev_pagemap *pgmap)
{
resource_size_t phys = PFN_PHYS(pfn);
/* * In the cached case we're already holding a live reference.
*/ if (pgmap) { if (phys >= pgmap->range.start && phys <= pgmap->range.end) return pgmap;
put_dev_pagemap(pgmap);
}
/* fall back to slow path lookup */
rcu_read_lock();
pgmap = xa_load(&pgmap_array, PHYS_PFN(phys)); if (pgmap && !percpu_ref_tryget_live_rcu(&pgmap->ref))
pgmap = NULL;
rcu_read_unlock();
/* * Note: we don't expect anonymous compound pages yet. Once supported * and we could PTE-map them similar to THP, we'd have to clear * PG_anon_exclusive on all tail pages.
*/ if (folio_test_anon(folio)) {
VM_BUG_ON_FOLIO(folio_test_large(folio), folio);
__ClearPageAnonExclusive(folio_page(folio, 0));
}
/* * When a device managed page is freed, the folio->mapping field * may still contain a (stale) mapping value. For example, the * lower bits of folio->mapping may still identify the folio as an * anonymous folio. Ultimately, this entire field is just stale * and wrong, and it will cause errors if not cleared. * * For other types of ZONE_DEVICE pages, migration is either * handled differently or not done at all, so there is no need * to clear folio->mapping. * * FS DAX pages clear the mapping when the folio->share count hits * zero which indicating the page has been removed from the file * system mapping.
*/ if (pgmap->type != MEMORY_DEVICE_FS_DAX &&
pgmap->type != MEMORY_DEVICE_GENERIC)
folio->mapping = NULL;
switch (pgmap->type) { case MEMORY_DEVICE_PRIVATE: case MEMORY_DEVICE_COHERENT: if (WARN_ON_ONCE(!pgmap->ops || !pgmap->ops->page_free)) break;
pgmap->ops->page_free(folio_page(folio, 0));
put_dev_pagemap(pgmap); break;
case MEMORY_DEVICE_GENERIC: /* * Reset the refcount to 1 to prepare for handing out the page * again.
*/
folio_set_count(folio, 1); break;
case MEMORY_DEVICE_FS_DAX:
wake_up_var(&folio->page); break;
case MEMORY_DEVICE_PCI_P2PDMA: if (WARN_ON_ONCE(!pgmap->ops || !pgmap->ops->page_free)) break;
pgmap->ops->page_free(folio_page(folio, 0)); break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.