diff mbox series

[v3,21/23] device-dax: Add an 'align' attribute

Message ID 159625241660.3040297.3801913809845542130.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive)
State Superseded
Headers show
Series device-dax: Support sub-dividing soft-reserved ranges | expand

Commit Message

Dan Williams Aug. 1, 2020, 3:26 a.m. UTC
From: Joao Martins <joao.m.martins@oracle.com>

Introduce a device align attribute. While doing so,
rename the region align attribute to be more explicitly
named as so, but keep it named as @align to retain the API
for tools like daxctl.

Changes on align may not always be valid, when say certain
mappings were created with 2M and then we switch to 1G. So, we
validate all ranges against the new value being attempted,
post resizing.

Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Link: https://lore.kernel.org/r/20200716172913.19658-3-joao.m.martins@oracle.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/dax/bus.c |  102 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 92 insertions(+), 10 deletions(-)

Comments

kernel test robot Aug. 1, 2020, 6:14 a.m. UTC | #1
Hi Dan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on 01830e6c042e8eb6eb202e05d7df8057135b4c26]

url:    https://github.com/0day-ci/linux/commits/Dan-Williams/device-dax-Support-sub-dividing-soft-reserved-ranges/20200801-114823
base:    01830e6c042e8eb6eb202e05d7df8057135b4c26
config: m68k-randconfig-r034-20200731 (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

                    from include/linux/irqflags.h:16,
                    from arch/m68k/include/asm/atomic.h:6,
                    from include/linux/atomic.h:7,
                    from include/linux/percpu-refcount.h:53,
                    from include/linux/memremap.h:6,
                    from drivers/dax/bus.c:3:
   drivers/dax/bus.c: In function 'dev_dax_shrink':
   include/linux/kernel.h:850:29: warning: comparison of distinct pointer types lacks a cast
     850 |   (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
         |                             ^~
   include/linux/kernel.h:864:4: note: in expansion of macro '__typecheck'
     864 |   (__typecheck(x, y) && __no_side_effects(x, y))
         |    ^~~~~~~~~~~
   include/linux/kernel.h:874:24: note: in expansion of macro '__safe_cmp'
     874 |  __builtin_choose_expr(__safe_cmp(x, y), \
         |                        ^~~~~~~~~~
   include/linux/kernel.h:883:19: note: in expansion of macro '__careful_cmp'
     883 | #define min(x, y) __careful_cmp(x, y, <)
         |                   ^~~~~~~~~~~~~
   drivers/dax/bus.c:881:12: note: in expansion of macro 'min'
     881 |   shrink = min(to_shrink, range_len(range));
         |            ^~~
   In file included from include/linux/printk.h:405,
                    from include/linux/kernel.h:15,
                    from include/linux/list.h:9,
                    from include/linux/preempt.h:11,
                    from arch/m68k/include/asm/irqflags.h:6,
                    from include/linux/irqflags.h:16,
                    from arch/m68k/include/asm/atomic.h:6,
                    from include/linux/atomic.h:7,
                    from include/linux/percpu-refcount.h:53,
                    from include/linux/memremap.h:6,
                    from drivers/dax/bus.c:3:
   drivers/dax/bus.c: In function 'dev_dax_validate_align':
   drivers/dax/bus.c:1062:16: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
    1062 |   dev_dbg(dev, "%s: align %u invalid for size %llu\n",
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:125:15: note: in definition of macro '__dynamic_func_call'
     125 |   func(&id, ##__VA_ARGS__);  \
         |               ^~~~~~~~~~~
   include/linux/dynamic_debug.h:157:2: note: in expansion of macro '_dynamic_func_call'
     157 |  _dynamic_func_call(fmt,__dynamic_dev_dbg,   \
         |  ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:115:2: note: in expansion of macro 'dynamic_dev_dbg'
     115 |  dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |  ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:115:23: note: in expansion of macro 'dev_fmt'
     115 |  dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                       ^~~~~~~
   drivers/dax/bus.c:1062:3: note: in expansion of macro 'dev_dbg'
    1062 |   dev_dbg(dev, "%s: align %u invalid for size %llu\n",
         |   ^~~~~~~
   drivers/dax/bus.c:1062:50: note: format string is defined here
    1062 |   dev_dbg(dev, "%s: align %u invalid for size %llu\n",
         |                                               ~~~^
         |                                                  |
         |                                                  long long unsigned int
         |                                               %u
   In file included from include/linux/printk.h:405,
                    from include/linux/kernel.h:15,
                    from include/linux/list.h:9,
                    from include/linux/preempt.h:11,
                    from arch/m68k/include/asm/irqflags.h:6,
                    from include/linux/irqflags.h:16,
                    from arch/m68k/include/asm/atomic.h:6,
                    from include/linux/atomic.h:7,
                    from include/linux/percpu-refcount.h:53,
                    from include/linux/memremap.h:6,
                    from drivers/dax/bus.c:3:
   drivers/dax/bus.c:1071:17: warning: format '%ld' expects argument of type 'long int', but argument 6 has type 'ssize_t' {aka 'int'} [-Wformat=]
    1071 |    dev_dbg(dev, "%s: align %u invalid for range %ld\n",
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:125:15: note: in definition of macro '__dynamic_func_call'
     125 |   func(&id, ##__VA_ARGS__);  \
         |               ^~~~~~~~~~~
   include/linux/dynamic_debug.h:157:2: note: in expansion of macro '_dynamic_func_call'
     157 |  _dynamic_func_call(fmt,__dynamic_dev_dbg,   \
         |  ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:115:2: note: in expansion of macro 'dynamic_dev_dbg'
     115 |  dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |  ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:115:23: note: in expansion of macro 'dev_fmt'
     115 |  dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                       ^~~~~~~
   drivers/dax/bus.c:1071:4: note: in expansion of macro 'dev_dbg'
    1071 |    dev_dbg(dev, "%s: align %u invalid for range %ld\n",
         |    ^~~~~~~
   drivers/dax/bus.c:1071:51: note: format string is defined here
    1071 |    dev_dbg(dev, "%s: align %u invalid for range %ld\n",
         |                                                 ~~^
         |                                                   |
         |                                                   long int
         |                                                 %d
   In file included from include/asm-generic/pgtable-nopud.h:7,
                    from arch/m68k/include/asm/pgtable_no.h:5,
                    from arch/m68k/include/asm/pgtable.h:3,
                    from include/linux/pgtable.h:6,
                    from include/linux/mm.h:33,
                    from include/linux/dax.h:6,
                    from drivers/dax/bus.c:8:
>> include/asm-generic/pgtable-nop4d.h:11:20: error: 'PGDIR_SHIFT' undeclared (first use in this function); did you mean 'PUD_SHIFT'?
      11 | #define P4D_SHIFT  PGDIR_SHIFT
         |                    ^~~~~~~~~~~
   include/asm-generic/pgtable-nopud.h:18:19: note: in expansion of macro 'P4D_SHIFT'
      18 | #define PUD_SHIFT P4D_SHIFT
         |                   ^~~~~~~~~
   include/asm-generic/pgtable-nopud.h:20:28: note: in expansion of macro 'PUD_SHIFT'
      20 | #define PUD_SIZE   (1UL << PUD_SHIFT)
         |                            ^~~~~~~~~
   drivers/dax/bus.c:1078:7: note: in expansion of macro 'PUD_SIZE'
    1078 |  case PUD_SIZE:
         |       ^~~~~~~~
   include/asm-generic/pgtable-nop4d.h:11:20: note: each undeclared identifier is reported only once for each function it appears in
      11 | #define P4D_SHIFT  PGDIR_SHIFT
         |                    ^~~~~~~~~~~
   include/asm-generic/pgtable-nopud.h:18:19: note: in expansion of macro 'P4D_SHIFT'
      18 | #define PUD_SHIFT P4D_SHIFT
         |                   ^~~~~~~~~
   include/asm-generic/pgtable-nopud.h:20:28: note: in expansion of macro 'PUD_SHIFT'
      20 | #define PUD_SIZE   (1UL << PUD_SHIFT)
         |                            ^~~~~~~~~
   drivers/dax/bus.c:1078:7: note: in expansion of macro 'PUD_SIZE'
    1078 |  case PUD_SIZE:
         |       ^~~~~~~~
>> drivers/dax/bus.c:1082:7: error: 'PMD_SIZE' undeclared (first use in this function); did you mean 'P4D_SIZE'?
    1082 |  case PMD_SIZE:
         |       ^~~~~~~~
         |       P4D_SIZE

vim +1082 drivers/dax/bus.c

  1050	
  1051	static ssize_t dev_dax_validate_align(struct dev_dax *dev_dax)
  1052	{
  1053		resource_size_t dev_size = dev_dax_size(dev_dax);
  1054		struct device *dev = &dev_dax->dev;
  1055		ssize_t rc, i;
  1056	
  1057		if (dev->driver)
  1058			return -EBUSY;
  1059	
  1060		rc = -EINVAL;
  1061		if (dev_size > 0 && !alloc_is_aligned(dev_dax, dev_size)) {
  1062			dev_dbg(dev, "%s: align %u invalid for size %llu\n",
  1063				__func__, dev_dax->align, dev_size);
  1064			return rc;
  1065		}
  1066	
  1067		for (i = 0; i < dev_dax->nr_range; i++) {
  1068			size_t len = range_len(&dev_dax->ranges[i].range);
  1069	
  1070			if (!alloc_is_aligned(dev_dax, len)) {
  1071				dev_dbg(dev, "%s: align %u invalid for range %ld\n",
  1072					__func__, dev_dax->align, i);
  1073				return rc;
  1074			}
  1075		}
  1076	
  1077		switch (dev_dax->align) {
  1078		case PUD_SIZE:
  1079			if (!IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
  1080				break;
  1081			fallthrough;
> 1082		case PMD_SIZE:
  1083			if (!has_transparent_hugepage())
  1084				break;
  1085			fallthrough;
  1086		case PAGE_SIZE:
  1087			rc = 0;
  1088			break;
  1089		}
  1090	
  1091		return rc;
  1092	}
  1093	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot Aug. 1, 2020, 6:18 a.m. UTC | #2
Hi Dan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on 01830e6c042e8eb6eb202e05d7df8057135b4c26]

url:    https://github.com/0day-ci/linux/commits/Dan-Williams/device-dax-Support-sub-dividing-soft-reserved-ranges/20200801-114823
base:    01830e6c042e8eb6eb202e05d7df8057135b4c26
config: arm-randconfig-r006-20200731 (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/dax/bus.c: In function 'do_id_store':
   drivers/dax/bus.c:94:27: warning: suggest braces around empty body in an 'else' statement [-Wempty-body]
      94 |    /* nothing to remove */;
         |                           ^
   drivers/dax/bus.c:99:29: warning: suggest braces around empty body in an 'else' statement [-Wempty-body]
      99 |   /* dax_id already added */;
         |                             ^
   In file included from include/linux/percpu-refcount.h:54,
                    from include/linux/memremap.h:6,
                    from drivers/dax/bus.c:3:
   drivers/dax/bus.c: In function 'dev_dax_shrink':
   include/linux/kernel.h:850:29: warning: comparison of distinct pointer types lacks a cast
     850 |   (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
         |                             ^~
   include/linux/kernel.h:864:4: note: in expansion of macro '__typecheck'
     864 |   (__typecheck(x, y) && __no_side_effects(x, y))
         |    ^~~~~~~~~~~
   include/linux/kernel.h:874:24: note: in expansion of macro '__safe_cmp'
     874 |  __builtin_choose_expr(__safe_cmp(x, y), \
         |                        ^~~~~~~~~~
   include/linux/kernel.h:883:19: note: in expansion of macro '__careful_cmp'
     883 | #define min(x, y) __careful_cmp(x, y, <)
         |                   ^~~~~~~~~~~~~
   drivers/dax/bus.c:881:12: note: in expansion of macro 'min'
     881 |   shrink = min(to_shrink, range_len(range));
         |            ^~~
   In file included from include/linux/device.h:15,
                    from drivers/dax/bus.c:4:
   drivers/dax/bus.c: In function 'dev_dax_validate_align':
   drivers/dax/bus.c:1062:16: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
    1062 |   dev_dbg(dev, "%s: align %u invalid for size %llu\n",
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:19:22: note: in definition of macro 'dev_fmt'
      19 | #define dev_fmt(fmt) fmt
         |                      ^~~
   drivers/dax/bus.c:1062:3: note: in expansion of macro 'dev_dbg'
    1062 |   dev_dbg(dev, "%s: align %u invalid for size %llu\n",
         |   ^~~~~~~
   drivers/dax/bus.c:1062:50: note: format string is defined here
    1062 |   dev_dbg(dev, "%s: align %u invalid for size %llu\n",
         |                                               ~~~^
         |                                                  |
         |                                                  long long unsigned int
         |                                               %u
   In file included from include/linux/device.h:15,
                    from drivers/dax/bus.c:4:
   drivers/dax/bus.c:1071:17: warning: format '%ld' expects argument of type 'long int', but argument 6 has type 'ssize_t' {aka 'int'} [-Wformat=]
    1071 |    dev_dbg(dev, "%s: align %u invalid for range %ld\n",
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:19:22: note: in definition of macro 'dev_fmt'
      19 | #define dev_fmt(fmt) fmt
         |                      ^~~
   drivers/dax/bus.c:1071:4: note: in expansion of macro 'dev_dbg'
    1071 |    dev_dbg(dev, "%s: align %u invalid for range %ld\n",
         |    ^~~~~~~
   drivers/dax/bus.c:1071:51: note: format string is defined here
    1071 |    dev_dbg(dev, "%s: align %u invalid for range %ld\n",
         |                                                 ~~^
         |                                                   |
         |                                                   long int
         |                                                 %d
>> drivers/dax/bus.c:1082:7: error: 'PMD_SIZE' undeclared (first use in this function); did you mean 'PUD_SIZE'?
    1082 |  case PMD_SIZE:
         |       ^~~~~~~~
         |       PUD_SIZE
   drivers/dax/bus.c:1082:7: note: each undeclared identifier is reported only once for each function it appears in
   In file included from include/linux/compiler_types.h:65,
                    from <command-line>:
   include/linux/compiler_attributes.h:214:41: warning: attribute 'fallthrough' not preceding a case label or default label
     214 | # define fallthrough                    __attribute__((__fallthrough__))
         |                                         ^~~~~~~~~~~~~
   drivers/dax/bus.c:1081:3: note: in expansion of macro 'fallthrough'
    1081 |   fallthrough;
         |   ^~~~~~~~~~~

vim +1082 drivers/dax/bus.c

  1050	
  1051	static ssize_t dev_dax_validate_align(struct dev_dax *dev_dax)
  1052	{
  1053		resource_size_t dev_size = dev_dax_size(dev_dax);
  1054		struct device *dev = &dev_dax->dev;
  1055		ssize_t rc, i;
  1056	
  1057		if (dev->driver)
  1058			return -EBUSY;
  1059	
  1060		rc = -EINVAL;
  1061		if (dev_size > 0 && !alloc_is_aligned(dev_dax, dev_size)) {
  1062			dev_dbg(dev, "%s: align %u invalid for size %llu\n",
  1063				__func__, dev_dax->align, dev_size);
  1064			return rc;
  1065		}
  1066	
  1067		for (i = 0; i < dev_dax->nr_range; i++) {
  1068			size_t len = range_len(&dev_dax->ranges[i].range);
  1069	
  1070			if (!alloc_is_aligned(dev_dax, len)) {
  1071				dev_dbg(dev, "%s: align %u invalid for range %ld\n",
  1072					__func__, dev_dax->align, i);
  1073				return rc;
  1074			}
  1075		}
  1076	
  1077		switch (dev_dax->align) {
  1078		case PUD_SIZE:
  1079			if (!IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
  1080				break;
  1081			fallthrough;
> 1082		case PMD_SIZE:
  1083			if (!has_transparent_hugepage())
  1084				break;
  1085			fallthrough;
  1086		case PAGE_SIZE:
  1087			rc = 0;
  1088			break;
  1089		}
  1090	
  1091		return rc;
  1092	}
  1093	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c
index feca1413481c..7a9439132573 100644
--- a/drivers/dax/bus.c
+++ b/drivers/dax/bus.c
@@ -230,14 +230,15 @@  static ssize_t region_size_show(struct device *dev,
 static struct device_attribute dev_attr_region_size = __ATTR(size, 0444,
 		region_size_show, NULL);
 
-static ssize_t align_show(struct device *dev,
+static ssize_t region_align_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	struct dax_region *dax_region = dev_get_drvdata(dev);
 
 	return sprintf(buf, "%u\n", dax_region->align);
 }
-static DEVICE_ATTR_RO(align);
+static struct device_attribute dev_attr_region_align =
+		__ATTR(align, 0400, region_align_show, NULL);
 
 #define for_each_dax_region_resource(dax_region, res) \
 	for (res = (dax_region)->res.child; res; res = res->sibling)
@@ -488,7 +489,7 @@  static umode_t dax_region_visible(struct kobject *kobj, struct attribute *a,
 static struct attribute *dax_region_attributes[] = {
 	&dev_attr_available_size.attr,
 	&dev_attr_region_size.attr,
-	&dev_attr_align.attr,
+	&dev_attr_region_align.attr,
 	&dev_attr_create.attr,
 	&dev_attr_seed.attr,
 	&dev_attr_delete.attr,
@@ -855,15 +856,13 @@  static ssize_t size_show(struct device *dev,
 	return sprintf(buf, "%llu\n", size);
 }
 
-static bool alloc_is_aligned(struct dax_region *dax_region,
-		resource_size_t 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, dax_region->align,
-				memremap_compat_align()));
+	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)
@@ -958,7 +957,7 @@  static ssize_t dev_dax_resize(struct dax_region *dax_region,
 		return dev_dax_shrink(dev_dax, size);
 
 	to_alloc = size - dev_size;
-	if (dev_WARN_ONCE(dev, !alloc_is_aligned(dax_region, to_alloc),
+	if (dev_WARN_ONCE(dev, !alloc_is_aligned(dev_dax, to_alloc),
 			"resize of %pa misaligned\n", &to_alloc))
 		return -ENXIO;
 
@@ -1022,7 +1021,7 @@  static ssize_t size_store(struct device *dev, struct device_attribute *attr,
 	if (rc)
 		return rc;
 
-	if (!alloc_is_aligned(dax_region, val)) {
+	if (!alloc_is_aligned(dev_dax, val)) {
 		dev_dbg(dev, "%s: size: %lld misaligned\n", __func__, val);
 		return -EINVAL;
 	}
@@ -1041,6 +1040,87 @@  static ssize_t size_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(size);
 
+static ssize_t align_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct dev_dax *dev_dax = to_dev_dax(dev);
+
+	return sprintf(buf, "%d\n", dev_dax->align);
+}
+
+static ssize_t dev_dax_validate_align(struct dev_dax *dev_dax)
+{
+	resource_size_t dev_size = dev_dax_size(dev_dax);
+	struct device *dev = &dev_dax->dev;
+	ssize_t rc, i;
+
+	if (dev->driver)
+		return -EBUSY;
+
+	rc = -EINVAL;
+	if (dev_size > 0 && !alloc_is_aligned(dev_dax, dev_size)) {
+		dev_dbg(dev, "%s: align %u invalid for size %llu\n",
+			__func__, dev_dax->align, dev_size);
+		return rc;
+	}
+
+	for (i = 0; i < dev_dax->nr_range; i++) {
+		size_t len = range_len(&dev_dax->ranges[i].range);
+
+		if (!alloc_is_aligned(dev_dax, len)) {
+			dev_dbg(dev, "%s: align %u invalid for range %ld\n",
+				__func__, dev_dax->align, i);
+			return rc;
+		}
+	}
+
+	switch (dev_dax->align) {
+	case PUD_SIZE:
+		if (!IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
+			break;
+		fallthrough;
+	case PMD_SIZE:
+		if (!has_transparent_hugepage())
+			break;
+		fallthrough;
+	case PAGE_SIZE:
+		rc = 0;
+		break;
+	}
+
+	return rc;
+}
+
+static ssize_t align_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct dev_dax *dev_dax = to_dev_dax(dev);
+	struct dax_region *dax_region = dev_dax->region;
+	unsigned long long val, align_save;
+	ssize_t rc;
+
+	rc = kstrtoull(buf, 0, &val);
+	if (rc)
+		return -ENXIO;
+
+	device_lock(dax_region->dev);
+	if (!dax_region->dev->driver) {
+		device_unlock(dax_region->dev);
+		return -ENXIO;
+	}
+
+	device_lock(dev);
+	align_save = dev_dax->align;
+	dev_dax->align = val;
+	rc = dev_dax_validate_align(dev_dax);
+	if (rc)
+		dev_dax->align = align_save;
+	device_unlock(dev);
+	device_unlock(dax_region->dev);
+	return rc == 0 ? len : rc;
+}
+static DEVICE_ATTR_RW(align);
+
 static int dev_dax_target_node(struct dev_dax *dev_dax)
 {
 	struct dax_region *dax_region = dev_dax->region;
@@ -1101,7 +1181,8 @@  static umode_t dev_dax_visible(struct kobject *kobj, struct attribute *a, int n)
 		return 0;
 	if (a == &dev_attr_numa_node.attr && !IS_ENABLED(CONFIG_NUMA))
 		return 0;
-	if (a == &dev_attr_size.attr && is_static(dax_region))
+	if ((a == &dev_attr_align.attr ||
+	     a == &dev_attr_size.attr) && is_static(dax_region))
 		return 0444;
 	return a->mode;
 }
@@ -1110,6 +1191,7 @@  static struct attribute *dev_dax_attributes[] = {
 	&dev_attr_modalias.attr,
 	&dev_attr_size.attr,
 	&dev_attr_target_node.attr,
+	&dev_attr_align.attr,
 	&dev_attr_resource.attr,
 	&dev_attr_numa_node.attr,
 	NULL,