mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
dax/kmem: extract hotplug/hotremove helper functions
Refactor kmem _probe() _remove() by extracting init, cleanup, hotplug, and hot-remove logic into separate helper functions: - dax_kmem_init_resources: inits IO_RESOURCE w/ request_mem_region - dax_kmem_cleanup_resources: cleans up initialized IO_RESOURCE - dax_kmem_do_hotplug: handles memory region reservation and adding - dax_kmem_do_hotremove: handles memory removal and resource cleanup This is a pure refactoring with no functional change. The helpers will enable future extensions to support more granular control over memory hotplug operations. We need to split hotplug/hotunplug and init/cleanup in order to have the resources available for hot-add. Otherwise, when probe occurs, the dax devices are never added to sysfs because the resources are never registered. Detaching hotunplug/cleanup allows us to re-use the hotunplug code without destroying the underlying resources. Link: https://lore.kernel.org/20260712154505.3564379-9-gourry@gourry.net Signed-off-by: Gregory Price <gourry@gourry.net> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Cc: Alison Schofield <alison.schofield@intel.com> Cc: Danilo Krummrich <dakr@kernel.org> Cc: David Hildenbrand (Arm) <david@kernel.org> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Hannes Reinecke <hare@suse.de> Cc: Liam R. Howlett <liam@infradead.org> Cc: Lorenzo Stoakes <ljs@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Mike Rapoport <rppt@kernel.org> Cc: Oscar Salvador <osalvador@suse.de> Cc: Pankaj Gupta <pankaj.gupta@amd.com> Cc: "Rafael J. Wysocki" <rafael@kernel.org> Cc: Shuah Khan <shuah@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vishal Verma <vishal.l.verma@intel.com> Cc: Vlastimil Babka <vbabka@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
496a4cd658
commit
bb76d369b6
|
|
@ -63,14 +63,206 @@ static void kmem_put_memory_types(void)
|
|||
mt_put_memory_types(&kmem_memory_types);
|
||||
}
|
||||
|
||||
/**
|
||||
* dax_kmem_do_hotplug - hotplug memory for dax kmem device
|
||||
* @dev_dax: the dev_dax instance
|
||||
* @data: the dax_kmem_data structure with resource tracking
|
||||
*
|
||||
* Hotplugs all ranges in the dev_dax region as system memory.
|
||||
*
|
||||
* Returns the number of successfully mapped ranges, or negative error.
|
||||
*/
|
||||
static int dax_kmem_do_hotplug(struct dev_dax *dev_dax,
|
||||
struct dax_kmem_data *data,
|
||||
int online_type)
|
||||
{
|
||||
struct device *dev = &dev_dax->dev;
|
||||
int i, rc, onlined = 0;
|
||||
mhp_t mhp_flags;
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct range range;
|
||||
|
||||
rc = dax_kmem_range(dev_dax, i, &range);
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* init_resources() is best-effort: if a reservation conflict
|
||||
* occurs it keeps the range but leaves res[i]=NULL. For hotplug
|
||||
* on probe systems, this means kmem will partially online.
|
||||
*
|
||||
* We have to keep this behavior not to break those systems.
|
||||
* For those systems - atomicity only applies to valid ranges.
|
||||
*/
|
||||
if (!data->res[i])
|
||||
continue;
|
||||
|
||||
mhp_flags = MHP_NID_IS_MGID;
|
||||
if (dev_dax->memmap_on_memory)
|
||||
mhp_flags |= MHP_MEMMAP_ON_MEMORY;
|
||||
|
||||
/*
|
||||
* Ensure that future kexec'd kernels will not treat
|
||||
* this as RAM automatically.
|
||||
*/
|
||||
rc = __add_memory_driver_managed(data->mgid, range.start,
|
||||
range_len(&range), kmem_name, mhp_flags,
|
||||
online_type);
|
||||
|
||||
if (rc) {
|
||||
dev_warn(dev, "mapping%d: %#llx-%#llx memory add failed\n",
|
||||
i, range.start, range.end);
|
||||
/*
|
||||
* Release the reservation for the range that failed to
|
||||
* add so a later hotremove does not try to remove memory
|
||||
* that was never added.
|
||||
*/
|
||||
if (data->res[i]) {
|
||||
remove_resource(data->res[i]);
|
||||
kfree(data->res[i]);
|
||||
data->res[i] = NULL;
|
||||
}
|
||||
if (onlined)
|
||||
continue;
|
||||
return rc;
|
||||
}
|
||||
onlined++;
|
||||
}
|
||||
|
||||
return onlined;
|
||||
}
|
||||
|
||||
/**
|
||||
* dax_kmem_init_resources - create memory regions for dax kmem
|
||||
* @dev_dax: the dev_dax instance
|
||||
* @data: the dax_kmem_data structure with resource tracking
|
||||
*
|
||||
* Initializes all the resources for the DAX
|
||||
*
|
||||
* Returns the number of successfully mapped ranges, or negative error.
|
||||
*/
|
||||
static int dax_kmem_init_resources(struct dev_dax *dev_dax,
|
||||
struct dax_kmem_data *data)
|
||||
{
|
||||
struct device *dev = &dev_dax->dev;
|
||||
int i, rc, mapped = 0;
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct resource *res;
|
||||
struct range range;
|
||||
|
||||
rc = dax_kmem_range(dev_dax, i, &range);
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
/* Skip ranges already added */
|
||||
if (data->res[i])
|
||||
continue;
|
||||
|
||||
/* Region is permanently reserved if hotremove fails. */
|
||||
res = request_mem_region(range.start, range_len(&range),
|
||||
data->res_name);
|
||||
if (!res) {
|
||||
dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve region\n",
|
||||
i, range.start, range.end);
|
||||
/*
|
||||
* Once some memory has been onlined we can't
|
||||
* assume that it can be un-onlined safely.
|
||||
*/
|
||||
if (mapped)
|
||||
continue;
|
||||
return -EBUSY;
|
||||
}
|
||||
data->res[i] = res;
|
||||
/*
|
||||
* Set flags appropriate for System RAM. Leave ..._BUSY clear
|
||||
* so that add_memory() can add a child resource. Do not
|
||||
* inherit flags from the parent since it may set new flags
|
||||
* unknown to us that will break add_memory() later.
|
||||
*/
|
||||
res->flags = IORESOURCE_SYSTEM_RAM;
|
||||
mapped++;
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTREMOVE
|
||||
/**
|
||||
* dax_kmem_do_hotremove - hot-remove memory for dax kmem device
|
||||
* @dev_dax: the dev_dax instance
|
||||
* @data: the dax_kmem_data structure with resource tracking
|
||||
*
|
||||
* Removes all ranges in the dev_dax region.
|
||||
*
|
||||
* Returns the number of successfully removed ranges.
|
||||
*/
|
||||
static int dax_kmem_do_hotremove(struct dev_dax *dev_dax,
|
||||
struct dax_kmem_data *data)
|
||||
{
|
||||
struct device *dev = &dev_dax->dev;
|
||||
int i, success = 0;
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct range range;
|
||||
int rc;
|
||||
|
||||
rc = dax_kmem_range(dev_dax, i, &range);
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
/* range was never added during probe, count as removed */
|
||||
if (!data->res[i]) {
|
||||
success++;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = remove_memory(range.start, range_len(&range));
|
||||
if (rc == 0) {
|
||||
/* Release the resource for the successfully removed range */
|
||||
remove_resource(data->res[i]);
|
||||
kfree(data->res[i]);
|
||||
data->res[i] = NULL;
|
||||
success++;
|
||||
continue;
|
||||
}
|
||||
any_hotremove_failed = true;
|
||||
dev_err(dev, "mapping%d: %#llx-%#llx hotremove failed\n",
|
||||
i, range.start, range.end);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
#endif /* CONFIG_MEMORY_HOTREMOVE */
|
||||
|
||||
/**
|
||||
* dax_kmem_cleanup_resources - remove the dax memory resources
|
||||
* @dev_dax: the dev_dax instance
|
||||
* @data: the dax_kmem_data structure with resource tracking
|
||||
*
|
||||
* Removes all resources in the dev_dax region.
|
||||
*/
|
||||
static void dax_kmem_cleanup_resources(struct dev_dax *dev_dax,
|
||||
struct dax_kmem_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
if (!data->res[i])
|
||||
continue;
|
||||
remove_resource(data->res[i]);
|
||||
kfree(data->res[i]);
|
||||
data->res[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dev_dax_kmem_probe(struct dev_dax *dev_dax)
|
||||
{
|
||||
struct device *dev = &dev_dax->dev;
|
||||
unsigned long total_len = 0, orig_len = 0;
|
||||
struct dax_kmem_data *data;
|
||||
struct memory_dev_type *mtype;
|
||||
int i, rc, mapped = 0;
|
||||
mhp_t mhp_flags;
|
||||
int i, rc;
|
||||
int numa_node;
|
||||
int adist = MEMTIER_DEFAULT_DAX_ADISTANCE;
|
||||
int online_type = mhp_get_default_online_type();
|
||||
|
|
@ -133,68 +325,22 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax)
|
|||
goto err_reg_mgid;
|
||||
data->mgid = rc;
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct resource *res;
|
||||
struct range range;
|
||||
|
||||
rc = dax_kmem_range(dev_dax, i, &range);
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
/* Region is permanently reserved if hotremove fails. */
|
||||
res = request_mem_region(range.start, range_len(&range), data->res_name);
|
||||
if (!res) {
|
||||
dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve region\n",
|
||||
i, range.start, range.end);
|
||||
/*
|
||||
* Once some memory has been onlined we can't
|
||||
* assume that it can be un-onlined safely.
|
||||
*/
|
||||
if (mapped)
|
||||
continue;
|
||||
rc = -EBUSY;
|
||||
goto err_request_mem;
|
||||
}
|
||||
data->res[i] = res;
|
||||
|
||||
/*
|
||||
* Set flags appropriate for System RAM. Leave ..._BUSY clear
|
||||
* so that add_memory() can add a child resource. Do not
|
||||
* inherit flags from the parent since it may set new flags
|
||||
* unknown to us that will break add_memory() below.
|
||||
*/
|
||||
res->flags = IORESOURCE_SYSTEM_RAM;
|
||||
|
||||
mhp_flags = MHP_NID_IS_MGID;
|
||||
if (dev_dax->memmap_on_memory)
|
||||
mhp_flags |= MHP_MEMMAP_ON_MEMORY;
|
||||
|
||||
/*
|
||||
* Ensure that future kexec'd kernels will not treat
|
||||
* this as RAM automatically.
|
||||
*/
|
||||
rc = __add_memory_driver_managed(data->mgid, range.start,
|
||||
range_len(&range), kmem_name, mhp_flags,
|
||||
online_type);
|
||||
|
||||
if (rc) {
|
||||
dev_warn(dev, "mapping%d: %#llx-%#llx memory add failed\n",
|
||||
i, range.start, range.end);
|
||||
remove_resource(res);
|
||||
kfree(res);
|
||||
data->res[i] = NULL;
|
||||
if (mapped)
|
||||
continue;
|
||||
goto err_request_mem;
|
||||
}
|
||||
mapped++;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
rc = dax_kmem_init_resources(dev_dax, data);
|
||||
if (rc < 0)
|
||||
goto err_resources;
|
||||
|
||||
rc = dax_kmem_do_hotplug(dev_dax, data, online_type);
|
||||
if (rc < 0)
|
||||
goto err_hotplug;
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_mem:
|
||||
err_hotplug:
|
||||
dax_kmem_cleanup_resources(dev_dax, data);
|
||||
err_resources:
|
||||
dev_set_drvdata(dev, NULL);
|
||||
memory_group_unregister(data->mgid);
|
||||
err_reg_mgid:
|
||||
kfree(data->res_name);
|
||||
|
|
@ -208,7 +354,7 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax)
|
|||
#ifdef CONFIG_MEMORY_HOTREMOVE
|
||||
static void dev_dax_kmem_remove(struct dev_dax *dev_dax)
|
||||
{
|
||||
int i, success = 0;
|
||||
int success;
|
||||
int node = dev_dax->target_node;
|
||||
struct device *dev = &dev_dax->dev;
|
||||
struct dax_kmem_data *data = dev_get_drvdata(dev);
|
||||
|
|
@ -219,48 +365,25 @@ static void dev_dax_kmem_remove(struct dev_dax *dev_dax)
|
|||
* there is no way to hotremove this memory until reboot because device
|
||||
* unbind will succeed even if we return failure.
|
||||
*/
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct range range;
|
||||
int rc;
|
||||
|
||||
rc = dax_kmem_range(dev_dax, i, &range);
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
/* range was never added during probe */
|
||||
if (!data->res[i]) {
|
||||
success++;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = remove_memory(range.start, range_len(&range));
|
||||
if (rc == 0) {
|
||||
remove_resource(data->res[i]);
|
||||
kfree(data->res[i]);
|
||||
data->res[i] = NULL;
|
||||
success++;
|
||||
continue;
|
||||
}
|
||||
any_hotremove_failed = true;
|
||||
dev_err(dev,
|
||||
"mapping%d: %#llx-%#llx cannot be hotremoved until the next reboot\n",
|
||||
i, range.start, range.end);
|
||||
success = dax_kmem_do_hotremove(dev_dax, data);
|
||||
if (success < dev_dax->nr_range) {
|
||||
dev_err(dev, "Hotplug regions stuck online until reboot\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (success >= dev_dax->nr_range) {
|
||||
memory_group_unregister(data->mgid);
|
||||
kfree(data->res_name);
|
||||
kfree(data);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
/*
|
||||
* Clear the memtype association on successful unplug.
|
||||
* If not, we have memory blocks left which can be
|
||||
* offlined/onlined later. We need to keep memory_dev_type
|
||||
* for that. This implies this reference will be around
|
||||
* till next reboot.
|
||||
*/
|
||||
clear_node_memory_type(node, NULL);
|
||||
}
|
||||
dax_kmem_cleanup_resources(dev_dax, data);
|
||||
memory_group_unregister(data->mgid);
|
||||
kfree(data->res_name);
|
||||
kfree(data);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
/*
|
||||
* Clear the memtype association on successful unplug.
|
||||
* If not, we have memory blocks left which can be
|
||||
* offlined/onlined later. We need to keep memory_dev_type
|
||||
* for that. This implies this reference will be around
|
||||
* till next reboot.
|
||||
*/
|
||||
clear_node_memory_type(node, NULL);
|
||||
}
|
||||
#else
|
||||
static void dev_dax_kmem_remove(struct dev_dax *dev_dax)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user