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)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.