mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Dave Jiang <dave.jiang@intel.com>
To: Muchun Song <songmuchun@bytedance.com>,
	Dan Williams <djbw@kernel.org>,
	Vishal Verma <vishal.l.verma@intel.com>,
	Alison Schofield <alison.schofield@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	Joao Martins <joao.m.martins@oracle.com>,
	nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org,
	linux-kernel@vger.kernel.org, muchun.song@linux.dev
Subject: Re: [PATCH 1/2] dax/bus: fix Device DAX range alignment validation
Date: Mon, 21 Sep 2026 14:01:20 -0700	[thread overview]
Message-ID: <8a90ff33-97e4-4796-a8a6-f484760557ca@intel.com> (raw)
In-Reply-To: <20260915095621.3744167-2-songmuchun@bytedance.com>



On 9/15/26 2:56 AM, Muchun Song wrote:
> dev_dax->align describes the page size used by a Device DAX mapping.
> Both the start and size of every range must therefore be aligned to it;
> otherwise the starting PFN cannot represent a naturally aligned page of
> that size.
> 
> Only range sizes are currently validated. A dynamic device can therefore
> select a large-page alignment and allocate a range whose start is not
> naturally aligned to that page size. The device binds successfully, but a
> subsequent write to a userspace mapping may trigger a kernel panic.
> 
> The automatic resize path can also split a size-aligned request across
> arbitrary free gaps. When devices with different alignments fragment a
> region, this can extend a range by less than its alignment. A later
> allocation then fails, leaving the failed resize partially applied.
> 
> Validate both the start and size of allocated and adjusted ranges. Make
> the resize path account only for usable aligned space before changing any
> ranges, and skip gaps that cannot satisfy the device alignment. Initialize
> the device alignment before allocating its initial range so that all
> allocations use the same validation.
> 
> A mapping with an unaligned start is now rejected with -EINVAL, while a
> naturally aligned mapping still binds successfully.
> 
> Fixes: 6d82120f4156 ("device-dax: add an 'align' attribute")
> Assisted-by: LLM
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> ---
>  drivers/dax/bus.c | 143 +++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 109 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c
> index b809e1a264af..54e4bbc98218 100644
> --- a/drivers/dax/bus.c
> +++ b/drivers/dax/bus.c
> @@ -848,6 +848,44 @@ static int devm_register_dax_mapping(struct dev_dax *dev_dax, int range_id)
>  	return 0;
>  }
>  
> +static inline unsigned long dev_dax_min_align(struct dev_dax *dev_dax)
> +{
> +	return max_t(unsigned long, dev_dax->align, memremap_compat_align());
> +}
> +
> +static inline bool size_is_aligned(struct dev_dax *dev_dax, resource_size_t size)

Leave the name alloc_is_aligned() may generate less churn in this patch

DJ

> +{
> +	/*
> +	 * The minimum mapping granularity for a device instance is a
> +	 * single subsection, unless the arch says otherwise.
> +	 */
> +	return IS_ALIGNED(size, dev_dax_min_align(dev_dax));
> +}
> +
> +static inline bool range_is_aligned(struct dev_dax *dev_dax, u64 start,
> +				    resource_size_t size)
> +{
> +	return IS_ALIGNED(start | size, dev_dax_min_align(dev_dax));
> +}
> +
> +static resource_size_t
> +aligned_gap_size(struct dev_dax *dev_dax, resource_size_t *start,
> +		 resource_size_t end)
> +{
> +	resource_size_t aligned_start = ALIGN(*start, dev_dax_min_align(dev_dax));
> +	resource_size_t size;
> +
> +	if (aligned_start < *start || aligned_start > end)
> +		return 0;
> +
> +	size = ALIGN_DOWN(end - aligned_start + 1, dev_dax_min_align(dev_dax));
> +	if (!size)
> +		return 0;
> +
> +	*start = aligned_start;
> +	return size;
> +}
> +
>  static int alloc_dev_dax_range(struct dev_dax *dev_dax, u64 start,
>  		resource_size_t size)
>  {
> @@ -870,6 +908,9 @@ static int alloc_dev_dax_range(struct dev_dax *dev_dax, u64 start,
>  		return 0;
>  	}
>  
> +	if (!range_is_aligned(dev_dax, start, size))
> +		return -EINVAL;
> +
>  	alloc = __request_region(res, start, size, dev_name(dev), 0);
>  	if (!alloc)
>  		return -ENOMEM;
> @@ -923,6 +964,9 @@ static int adjust_dev_dax_range(struct dev_dax *dev_dax, struct resource *res, r
>  	if (dev_WARN_ONCE(dev, !size, "deletion is handled by dev_dax_shrink\n"))
>  		return -EINVAL;
>  
> +	if (!range_is_aligned(dev_dax, range->start, size))
> +		return -EINVAL;
> +
>  	rc = adjust_resource(res, range->start, size);
>  	if (rc)
>  		return rc;
> @@ -955,15 +999,6 @@ static ssize_t size_show(struct device *dev,
>  	return sysfs_emit(buf, "%llu\n", size);
>  }
>  
> -static bool alloc_is_aligned(struct dev_dax *dev_dax, resource_size_t size)
> -{
> -	/*
> -	 * The minimum mapping granularity for a device instance is a
> -	 * single subsection, unless the arch says otherwise.
> -	 */
> -	return IS_ALIGNED(size, max_t(unsigned long, dev_dax->align, memremap_compat_align()));
> -}
> -
>  static int dev_dax_shrink(struct dev_dax *dev_dax, resource_size_t size)
>  {
>  	resource_size_t to_shrink = dev_dax_size(dev_dax) - size;
> @@ -1030,30 +1065,55 @@ static bool adjust_ok(struct dev_dax *dev_dax, struct resource *res)
>  	return true;
>  }
>  
> +static resource_size_t
> +dax_region_aligned_avail_size(struct dax_region *dax_region,
> +			      struct dev_dax *dev_dax)
> +{
> +	struct resource *region_res = &dax_region->res;
> +	resource_size_t start = region_res->start;
> +	resource_size_t avail = 0;
> +	struct resource *res;
> +
> +	lockdep_assert_held_write(&dax_region_rwsem);
> +
> +	for_each_dax_region_resource(dax_region, res) {
> +		if (res->start > start) {
> +			resource_size_t gap_start = start;
> +
> +			avail += aligned_gap_size(dev_dax, &gap_start,
> +					res->start - 1);
> +		}
> +		start = res->end + 1;
> +	}
> +	if (start <= region_res->end)
> +		avail += aligned_gap_size(dev_dax, &start, region_res->end);
> +
> +	return avail;
> +}
> +
>  static ssize_t dev_dax_resize(struct dax_region *dax_region,
>  		struct dev_dax *dev_dax, resource_size_t size)
>  {
> -	resource_size_t avail = dax_region_avail_size(dax_region), to_alloc;
>  	resource_size_t dev_size = dev_dax_size(dev_dax);
>  	struct resource *region_res = &dax_region->res;
>  	struct device *dev = &dev_dax->dev;
>  	struct resource *res, *first;
> -	resource_size_t alloc = 0;
> +	resource_size_t alloc, to_alloc;
>  	int rc;
>  
>  	if (dev->driver)
>  		return -EBUSY;
>  	if (size == dev_size)
>  		return 0;
> -	if (size > dev_size && size - dev_size > avail)
> -		return -ENOSPC;
>  	if (size < dev_size)
>  		return dev_dax_shrink(dev_dax, size);
>  
>  	to_alloc = size - dev_size;
> -	if (dev_WARN_ONCE(dev, !alloc_is_aligned(dev_dax, to_alloc),
> -			"resize of %pa misaligned\n", &to_alloc))
> +	if (dev_WARN_ONCE(dev, !size_is_aligned(dev_dax, to_alloc),
> +			  "resize of %pa misaligned\n", &to_alloc))
>  		return -ENXIO;
> +	if (to_alloc > dax_region_aligned_avail_size(dax_region, dev_dax))
> +		return -ENOSPC;
>  
>  	/*
>  	 * Expand the device into the unused portion of the region. This
> @@ -1062,37 +1122,52 @@ static ssize_t dev_dax_resize(struct dax_region *dax_region,
>  	 */
>  retry:
>  	first = region_res->child;
> -	if (!first)
> -		return alloc_dev_dax_range(dev_dax, dax_region->res.start, to_alloc);
> +	if (!first) {
> +		resource_size_t start = region_res->start;
> +
> +		alloc = aligned_gap_size(dev_dax, &start, region_res->end);
> +		return alloc_dev_dax_range(dev_dax, start,
> +				min(alloc, to_alloc));
> +	}
>  
>  	rc = -ENOSPC;
>  	for (res = first; res; res = res->sibling) {
>  		struct resource *next = res->sibling;
> +		resource_size_t start, end;
>  
>  		/* space at the beginning of the region */
>  		if (res == first && res->start > dax_region->res.start) {
> -			alloc = min(res->start - dax_region->res.start, to_alloc);
> -			rc = alloc_dev_dax_range(dev_dax, dax_region->res.start, alloc);
> -			break;
> +			start = dax_region->res.start;
> +			end = res->start - 1;
> +			alloc = min(aligned_gap_size(dev_dax, &start, end), to_alloc);
> +			if (alloc) {
> +				rc = alloc_dev_dax_range(dev_dax, start, alloc);
> +				break;
> +			}
>  		}
>  
> -		alloc = 0;
>  		/* space between allocations */
> -		if (next && next->start > res->end + 1)
> -			alloc = min(next->start - (res->end + 1), to_alloc);
> -
> -		/* space at the end of the region */
> -		if (!alloc && !next && res->end < region_res->end)
> -			alloc = min(region_res->end - res->end, to_alloc);
> +		if (next) {
> +			if (next->start <= res->end + 1)
> +				continue;
> +			end = next->start - 1;
> +		} else {
> +			/* space at the end of the region */
> +			if (res->end >= region_res->end)
> +				continue;
> +			end = region_res->end;
> +		}
>  
> +		start = res->end + 1;
> +		alloc = min(aligned_gap_size(dev_dax, &start, end), to_alloc);
>  		if (!alloc)
>  			continue;
>  
> -		if (adjust_ok(dev_dax, res)) {
> +		if (start == res->end + 1 && adjust_ok(dev_dax, res)) {
>  			rc = adjust_dev_dax_range(dev_dax, res, resource_size(res) + alloc);
>  			break;
>  		}
> -		rc = alloc_dev_dax_range(dev_dax, res->end + 1, alloc);
> +		rc = alloc_dev_dax_range(dev_dax, start, alloc);
>  		break;
>  	}
>  	if (rc)
> @@ -1115,7 +1190,7 @@ static ssize_t size_store(struct device *dev, struct device_attribute *attr,
>  	if (rc)
>  		return rc;
>  
> -	if (!alloc_is_aligned(dev_dax, val)) {
> +	if (!size_is_aligned(dev_dax, val)) {
>  		dev_dbg(dev, "%s: size: %lld misaligned\n", __func__, val);
>  		return -EINVAL;
>  	}
> @@ -1201,7 +1276,7 @@ static ssize_t mapping_store(struct device *dev, struct device_attribute *attr,
>  	}
>  
>  	to_alloc = range_len(&r);
> -	if (alloc_is_aligned(dev_dax, to_alloc))
> +	if (size_is_aligned(dev_dax, to_alloc))
>  		rc = alloc_dev_dax_range(dev_dax, r.start, to_alloc);
>  	up_write(&dax_dev_rwsem);
>  	up_write(&dax_region_rwsem);
> @@ -1224,9 +1299,9 @@ static ssize_t dev_dax_validate_align(struct dev_dax *dev_dax)
>  	int i;
>  
>  	for (i = 0; i < dev_dax->nr_range; i++) {
> -		size_t len = range_len(&dev_dax->ranges[i].range);
> +		struct range *range = &dev_dax->ranges[i].range;
>  
> -		if (!alloc_is_aligned(dev_dax, len)) {
> +		if (!range_is_aligned(dev_dax, range->start, range_len(range))) {
>  			dev_dbg(dev, "%s: align %u invalid for range %d\n",
>  				__func__, dev_dax->align, i);
>  			return -EINVAL;
> @@ -1464,6 +1539,7 @@ static struct dev_dax *__devm_create_dev_dax(struct dev_dax_data *data)
>  		return ERR_PTR(-ENOMEM);
>  
>  	dev_dax->region = dax_region;
> +	dev_dax->align = dax_region->align;
>  	if (is_static(dax_region)) {
>  		if (dev_WARN_ONCE(parent, data->id < 0,
>  				"dynamic id specified to static region\n")) {
> @@ -1522,7 +1598,6 @@ static struct dev_dax *__devm_create_dev_dax(struct dev_dax_data *data)
>  
>  	dev_dax->dax_dev = dax_dev;
>  	dev_dax->target_node = dax_region->target_node;
> -	dev_dax->align = dax_region->align;
>  	ida_init(&dev_dax->ida);
>  
>  	dev_dax->memmap_on_memory = data->memmap_on_memory;


  reply	other threads:[~2026-09-21 21:01 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-15  9:56 [PATCH 0/2] dax: Fix Device DAX range allocation validation Muchun Song
2026-09-15  9:56 ` [PATCH 1/2] dax/bus: fix Device DAX range alignment validation Muchun Song
2026-09-21 21:01   ` Dave Jiang [this message]
2026-09-22  2:21     ` Muchun Song
2026-09-15  9:56 ` [PATCH 2/2] dax/bus: fix mapping attribute error reporting Muchun Song
2026-09-21 19:23   ` Dave Jiang
2026-09-22  2:27     ` Muchun Song
2026-09-18 17:02 ` [PATCH 0/2] dax: Fix Device DAX range allocation validation Dave Jiang
2026-09-19  5:03   ` Muchun Song
2026-09-21 18:58     ` Dave Jiang
2026-09-22  2:25       ` Muchun Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8a90ff33-97e4-4796-a8a6-f484760557ca@intel.com \
    --to=dave.jiang@intel.com \
    --cc=akpm@linux-foundation.org \
    --cc=alison.schofield@intel.com \
    --cc=djbw@kernel.org \
    --cc=joao.m.martins@oracle.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=muchun.song@linux.dev \
    --cc=nvdimm@lists.linux.dev \
    --cc=songmuchun@bytedance.com \
    --cc=vishal.l.verma@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®