Re: [PATCH v6 08/10] dax/kmem: extract hotplug/hotremove helper functions
Dave Jiang <[email protected]>
| Newsgroups | dev.linux.lists.nvdimm,dev.linux.lists.driver-core,org.kernel.vger.linux-cxl,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
On 6/30/26 2:18 PM, Gregory Price wrote: > 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. > > Detatching hotunplug/cleanup allows us to re-use the hotunplug code > without destroying the underlying resources. > > Signed-off-by: Gregory Price <[email protected]> Minor comment below Reviewed-by: Dave Jiang <[email protected]> > --- > drivers/dax/kmem.c | 327 +++++++++++++++++++++++++++++++-------------- > 1 file changed, 225 insertions(+), 102 deletions(-) > > diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c > index 0a184c0878dd..72dcccee41e1 100644 > --- a/drivers/dax/kmem.c > +++ b/drivers/dax/kmem.c > @@ -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() below. 'later' instead of 'below'? Comment reflects previous function flow that is no longer the case. DJ > + */ > + 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 online_type; > int adist = MEMTIER_DEFAULT_DAX_ADISTANCE; > @@ -133,73 +325,27 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) > goto err_reg_mgid; > data->mgid = rc; > > + dev_set_drvdata(dev, data); > + > + rc = dax_kmem_init_resources(dev_dax, data); > + if (rc < 0) > + goto err_resources; > + > /* Resolve system default at bind time in case it changed */ > online_type = dev_dax->online_type; > if (online_type == DAX_ONLINE_DEFAULT) > online_type = mhp_get_default_online_type(); > > - 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_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); > @@ -213,7 +359,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); > @@ -224,48 +370,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)