diff mbox series

[1/4] of: add struct page support to rmem

Message ID 20220711122459.13773-2-me@linux.beauty (mailing list archive)
State New, archived
Headers show
Series add struct page and Direct I/O support to reserved memory | expand

Commit Message

Li Chen July 11, 2022, 12:24 p.m. UTC
From: Li Chen <lchen@ambarella.com>

This commit add a new config OF_RESERVED_MEM_DIO_SUPPORT and
some utilities to enables consumers to build struct pages on rmem.

Signed-off-by: Li Chen <lchen@ambarella.com>
Change-Id: Iaba8874775c6d3a7096ac19575bb884db13351d1
---
 drivers/of/Kconfig              |   9 ++
 drivers/of/of_reserved_mem.c    | 218 +++++++++++++++++++++++++++++++-
 include/linux/of_reserved_mem.h |  11 ++
 3 files changed, 237 insertions(+), 1 deletion(-)

Comments

Arnd Bergmann July 11, 2022, 1:36 p.m. UTC | #1
On Mon, Jul 11, 2022 at 2:24 PM Li Chen <me@linux.beauty> wrote:

> +config OF_RESERVED_MEM_DIO_SUPPORT
> +       bool "add Direct I/O support to reserved_mem"
> +       depends on ZONE_DEVICE && ARCH_KEEP_MEMBLOCK
> +       help
> +          By default, reserved memory don't get struct page support, which
> +                means you cannot do Direct I/O from this region. This config takes
> +                uses of ZONE_DEVICE and treats rmem as hotplug mem to get struct
> +                page and DIO support.

This probably does not need to be user visible, it's enough to select it from
the drivers that need it.

> @@ -72,7 +72,6 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
>         rmem->size = size;
>
>         reserved_mem_count++;
> -       return;
>  }

This change is not wrong, but it does not belong into the same patch
as the rest, just drop it.

> +/**
> + * get_reserved_mem_from_dev() - get reserved_mem from a device node
> + * @dev: device pointer
> + *
> + * This function look for reserved_mem from given device.
> + *
> + * Returns a reserved_mem pointer, or NULL on error.
> + */
> +struct reserved_mem *get_reserved_mem_from_dev(struct device *dev)
> +{
> +       struct device_node *np = dev_of_node(dev);
> +       struct device_node *rmem_np;
> +       struct reserved_mem *rmem = NULL;
> +
> +       rmem_np = of_parse_phandle(np, "memory-region", 0);
> +       if (!rmem_np) {
> +               dev_err(dev, "failed to get memory region node\n");
> +               return ERR_PTR(-ENODEV);
> +       }
> +
> +       rmem = of_reserved_mem_lookup(rmem_np);
> +       if (!rmem) {
> +               dev_err(dev, "Failed to lookup reserved memory\n");
> +               return ERR_PTR(EINVAL);

This needs to be a negative error code rather than the positive EINVAL.
No need to initialize rmem=NULL first if you override it here.

> +       if (likely(reserved_mem_dio_in_region(pfn << PAGE_SHIFT, PAGE_SIZE, rmem) <
> +                  0))
> +               goto out;

It's not performance critical, so just drop the 'likely()' and put the
rest into one line.


> +       if (page) {
> +               *page = pfn_to_page(pfn);
> +               get_page(*page);
> +       }
> +
> +       ret = 0;
> +
> +out:
> +       pte_unmap(pte);
> +       return ret;
> +}

Should you perhaps return an error when 'page' is NULL?

> +#ifdef CONFIG_OF_RESERVED_MEM_DIO_SUPPORT
> +int reserved_mem_dio_mmap(struct file *file, struct vm_area_struct *vma, struct reserved_mem *rmem);
> +void *reserved_mem_memremap_pages(struct device *dev, struct reserved_mem *rmem);
> +#endif

The '#ifdef' check can be dropped here, declarations are normally
not hidden like this.

         Arnd
Li Chen July 11, 2022, 2:51 p.m. UTC | #2
Hi Arnd,

Thanks for your review!
 ---- On Mon, 11 Jul 2022 21:36:12 +0800  Arnd Bergmann <arnd@arndb.de> wrote --- 
 > On Mon, Jul 11, 2022 at 2:24 PM Li Chen <me@linux.beauty> wrote:
 > 
 > > +config OF_RESERVED_MEM_DIO_SUPPORT
 > > +       bool "add Direct I/O support to reserved_mem"
 > > +       depends on ZONE_DEVICE && ARCH_KEEP_MEMBLOCK
 > > +       help
 > > +          By default, reserved memory don't get struct page support, which
 > > +                means you cannot do Direct I/O from this region. This config takes
 > > +                uses of ZONE_DEVICE and treats rmem as hotplug mem to get struct
 > > +                page and DIO support.
 > 
 > This probably does not need to be user visible, it's enough to select it from
 > the drivers that need it.

When you say "user visible", do you mean the config can be dropped or something else like Kconfig type other than bool?

 > 
 > > @@ -72,7 +72,6 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
 > >         rmem->size = size;
 > >
 > >         reserved_mem_count++;
 > > -       return;
 > >  }
 > 
 > This change is not wrong, but it does not belong into the same patch
 > as the rest, just drop it.
 > 
 > > +/**
 > > + * get_reserved_mem_from_dev() - get reserved_mem from a device node
 > > + * @dev: device pointer
 > > + *
 > > + * This function look for reserved_mem from given device.
 > > + *
 > > + * Returns a reserved_mem pointer, or NULL on error.
 > > + */
 > > +struct reserved_mem *get_reserved_mem_from_dev(struct device *dev)
 > > +{
 > > +       struct device_node *np = dev_of_node(dev);
 > > +       struct device_node *rmem_np;
 > > +       struct reserved_mem *rmem = NULL;
 > > +
 > > +       rmem_np = of_parse_phandle(np, "memory-region", 0);
 > > +       if (!rmem_np) {
 > > +               dev_err(dev, "failed to get memory region node\n");
 > > +               return ERR_PTR(-ENODEV);
 > > +       }
 > > +
 > > +       rmem = of_reserved_mem_lookup(rmem_np);
 > > +       if (!rmem) {
 > > +               dev_err(dev, "Failed to lookup reserved memory\n");
 > > +               return ERR_PTR(EINVAL);
 > 
 > This needs to be a negative error code rather than the positive EINVAL.
 > No need to initialize rmem=NULL first if you override it here.
 > 
 > > +       if (likely(reserved_mem_dio_in_region(pfn << PAGE_SHIFT, PAGE_SIZE, rmem) <
 > > +                  0))
 > > +               goto out;
 > 
 > It's not performance critical, so just drop the 'likely()' and put the
 > rest into one line.
 > 
 > 
 > > +       if (page) {
 > > +               *page = pfn_to_page(pfn);
 > > +               get_page(*page);
 > > +       }
 > > +
 > > +       ret = 0;
 > > +
 > > +out:
 > > +       pte_unmap(pte);
 > > +       return ret;
 > > +}
 > 
 > Should you perhaps return an error when 'page' is NULL?
 > 
 > > +#ifdef CONFIG_OF_RESERVED_MEM_DIO_SUPPORT
 > > +int reserved_mem_dio_mmap(struct file *file, struct vm_area_struct *vma, struct reserved_mem *rmem);
 > > +void *reserved_mem_memremap_pages(struct device *dev, struct reserved_mem *rmem);
 > > +#endif
 > 
 > The '#ifdef' check can be dropped here, declarations are normally
 > not hidden like this.
 > 
 >          Arnd
 > 

These will be fixed in v2.

Regards,
Li
Arnd Bergmann July 11, 2022, 3:06 p.m. UTC | #3
On Mon, Jul 11, 2022 at 4:51 PM Li Chen <me@linux.beauty> wrote:
>  ---- On Mon, 11 Jul 2022 21:36:12 +0800  Arnd Bergmann <arnd@arndb.de> wrote ---
>  > On Mon, Jul 11, 2022 at 2:24 PM Li Chen <me@linux.beauty> wrote:
>  >
>  > > +config OF_RESERVED_MEM_DIO_SUPPORT
>  > > +       bool "add Direct I/O support to reserved_mem"
>  > > +       depends on ZONE_DEVICE && ARCH_KEEP_MEMBLOCK
>  > > +       help
>  > > +          By default, reserved memory don't get struct page support, which
>  > > +                means you cannot do Direct I/O from this region. This config takes
>  > > +                uses of ZONE_DEVICE and treats rmem as hotplug mem to get struct
>  > > +                page and DIO support.
>  >
>  > This probably does not need to be user visible, it's enough to select it from
>  > the drivers that need it.
>
> When you say "user visible", do you mean the config can be dropped or something else like Kconfig type other than bool?

I mean this can be a hidden option, which you can do by leaving out the
one-line description after the 'bool' keyword. The option will still
be selectable
in Kconfig files from other options, but not shown in 'make menuconfig'.

        Arnd
Li Chen July 12, 2022, 3:13 a.m. UTC | #4
Hi Arnd,
 ---- On Mon, 11 Jul 2022 23:06:50 +0800  Arnd Bergmann <arnd@arndb.de> wrote --- 
 > On Mon, Jul 11, 2022 at 4:51 PM Li Chen <me@linux.beauty> wrote:
 > >  ---- On Mon, 11 Jul 2022 21:36:12 +0800  Arnd Bergmann <arnd@arndb.de> wrote ---
 > >  > On Mon, Jul 11, 2022 at 2:24 PM Li Chen <me@linux.beauty> wrote:
 > >  >
 > >  > > +config OF_RESERVED_MEM_DIO_SUPPORT
 > >  > > +       bool "add Direct I/O support to reserved_mem"
 > >  > > +       depends on ZONE_DEVICE && ARCH_KEEP_MEMBLOCK
 > >  > > +       help
 > >  > > +          By default, reserved memory don't get struct page support, which
 > >  > > +                means you cannot do Direct I/O from this region. This config takes
 > >  > > +                uses of ZONE_DEVICE and treats rmem as hotplug mem to get struct
 > >  > > +                page and DIO support.
 > >  >
 > >  > This probably does not need to be user visible, it's enough to select it from
 > >  > the drivers that need it.
 > >
 > > When you say "user visible", do you mean the config can be dropped or something else like Kconfig type other than bool?
 > 
 > I mean this can be a hidden option, which you can do by leaving out the
 > one-line description after the 'bool' keyword. The option will still
 > be selectable
 > in Kconfig files from other options, but not shown in 'make menuconfig'.
 > 
 >         Arnd
 > 

Roger that. I will do it in v2.

Regards,
Li
kernel test robot July 16, 2022, 12:38 a.m. UTC | #5
Hi Li,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on robh/for-next]
[also build test WARNING on arm64/for-next/core arm-perf/for-next/perf linus/master v5.19-rc6 next-20220715]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Li-Chen/add-struct-page-and-Direct-I-O-support-to-reserved-memory/20220711-202957
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20220716/202207160854.nSdKYSY8-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 12.1.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
        # https://github.com/intel-lab-lkp/linux/commit/8b66b4b9614f1c7bb8b2d8fac17d5a2a73acf954
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Li-Chen/add-struct-page-and-Direct-I-O-support-to-reserved-memory/20220711-202957
        git checkout 8b66b4b9614f1c7bb8b2d8fac17d5a2a73acf954
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash drivers/mailbox/ drivers/of/

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

All warnings (new ones prefixed by >>):

   drivers/of/of_reserved_mem.c: In function 'reserved_mem_memremap_pages':
   drivers/of/of_reserved_mem.c:633:50: error: invalid application of 'sizeof' to incomplete type 'struct dev_pagemap'
     633 |         pgmap_rmem_dio = devm_kzalloc(dev, sizeof(*pgmap_rmem_dio), GFP_KERNEL);
         |                                                  ^
   drivers/of/of_reserved_mem.c:635:23: error: invalid use of undefined type 'struct dev_pagemap'
     635 |         pgmap_rmem_dio->range.start = rmem->base;
         |                       ^~
   drivers/of/of_reserved_mem.c:636:23: error: invalid use of undefined type 'struct dev_pagemap'
     636 |         pgmap_rmem_dio->range.end = rmem->base + rmem->size - 1;
         |                       ^~
   drivers/of/of_reserved_mem.c:637:23: error: invalid use of undefined type 'struct dev_pagemap'
     637 |         pgmap_rmem_dio->nr_range = 1;
         |                       ^~
   drivers/of/of_reserved_mem.c:638:23: error: invalid use of undefined type 'struct dev_pagemap'
     638 |         pgmap_rmem_dio->type = MEMORY_DEVICE_GENERIC;
         |                       ^~
   drivers/of/of_reserved_mem.c:638:32: error: 'MEMORY_DEVICE_GENERIC' undeclared (first use in this function)
     638 |         pgmap_rmem_dio->type = MEMORY_DEVICE_GENERIC;
         |                                ^~~~~~~~~~~~~~~~~~~~~
   drivers/of/of_reserved_mem.c:638:32: note: each undeclared identifier is reported only once for each function it appears in
   In file included from include/linux/printk.h:584,
                    from include/linux/kernel.h:29,
                    from include/linux/cpumask.h:10,
                    from include/linux/smp.h:13,
                    from include/linux/lockdep.h:14,
                    from include/linux/mutex.h:17,
                    from include/linux/kernfs.h:11,
                    from include/linux/sysfs.h:16,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:17,
                    from drivers/of/of_reserved_mem.c:15:
   drivers/of/of_reserved_mem.c:641:42: error: invalid use of undefined type 'struct dev_pagemap'
     641 |                  __func__, pgmap_rmem_dio->range.start, pgmap_rmem_dio->range.end);
         |                                          ^~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:162:9: note: in expansion of macro '_dynamic_func_call'
     162 |         _dynamic_func_call(fmt, __dynamic_pr_debug,             \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/printk.h:599:9: note: in expansion of macro 'dynamic_pr_debug'
     599 |         dynamic_pr_debug(fmt, ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~~
   drivers/of/of_reserved_mem.c:640:9: note: in expansion of macro 'pr_debug'
     640 |         pr_debug("%s, will do devm_memremap_pages, start from %llx, to %llx\n",
         |         ^~~~~~~~
   drivers/of/of_reserved_mem.c:641:71: error: invalid use of undefined type 'struct dev_pagemap'
     641 |                  __func__, pgmap_rmem_dio->range.start, pgmap_rmem_dio->range.end);
         |                                                                       ^~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:162:9: note: in expansion of macro '_dynamic_func_call'
     162 |         _dynamic_func_call(fmt, __dynamic_pr_debug,             \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/printk.h:599:9: note: in expansion of macro 'dynamic_pr_debug'
     599 |         dynamic_pr_debug(fmt, ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~~
   drivers/of/of_reserved_mem.c:640:9: note: in expansion of macro 'pr_debug'
     640 |         pr_debug("%s, will do devm_memremap_pages, start from %llx, to %llx\n",
         |         ^~~~~~~~
   drivers/of/of_reserved_mem.c:643:17: error: implicit declaration of function 'devm_memremap_pages'; did you mean 'devm_free_pages'? [-Werror=implicit-function-declaration]
     643 |         vaddr = devm_memremap_pages(dev, pgmap_rmem_dio);
         |                 ^~~~~~~~~~~~~~~~~~~
         |                 devm_free_pages
>> drivers/of/of_reserved_mem.c:643:15: warning: assignment to 'void *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     643 |         vaddr = devm_memremap_pages(dev, pgmap_rmem_dio);
         |               ^
   cc1: some warnings being treated as errors


vim +643 drivers/of/of_reserved_mem.c

   610	
   611	/**
   612	 * reserved_mem_memremap_pages() - build struct pages for reserved mem
   613	 * @dev: device pointer
   614	 * @rmem: reserved memory region from dts, which can be get by
   615	 *        get_reserved_mem_from_dev(dev)
   616	 *
   617	 * Returns: 0 on success or a negative error-code on failure.
   618	 */
   619	void *reserved_mem_memremap_pages(struct device *dev, struct reserved_mem *rmem)
   620	{
   621		struct dev_pagemap *pgmap_rmem_dio;
   622		void *vaddr;
   623		struct page **pages;
   624		int i;
   625		unsigned long offset = 0;
   626		struct page *page;
   627	
   628		rmem->nr_pages = DIV_ROUND_UP(rmem->size, PAGE_SIZE);
   629		pages = kvmalloc_array(rmem->nr_pages, sizeof(*pages), GFP_KERNEL);
   630		if (!pages)
   631			return ERR_PTR(-ENOMEM);
   632	
   633		pgmap_rmem_dio = devm_kzalloc(dev, sizeof(*pgmap_rmem_dio), GFP_KERNEL);
   634	
   635		pgmap_rmem_dio->range.start = rmem->base;
   636		pgmap_rmem_dio->range.end = rmem->base + rmem->size - 1;
   637		pgmap_rmem_dio->nr_range = 1;
   638		pgmap_rmem_dio->type = MEMORY_DEVICE_GENERIC;
   639	
   640		pr_debug("%s, will do devm_memremap_pages, start from %llx, to %llx\n",
   641			 __func__, pgmap_rmem_dio->range.start, pgmap_rmem_dio->range.end);
   642	
 > 643		vaddr = devm_memremap_pages(dev, pgmap_rmem_dio);
Dan Carpenter July 18, 2022, 1:21 p.m. UTC | #6
Hi Li,

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Li-Chen/add-struct-page-and-Direct-I-O-support-to-reserved-memory/20220711-202957
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: nios2-randconfig-m031-20220717 (https://download.01.org/0day-ci/archive/20220718/202207181758.YyEzUSPl-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 12.1.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
drivers/of/of_reserved_mem.c:472 get_reserved_mem_from_dev() error: passing non negative 22 to ERR_PTR

vim +472 drivers/of/of_reserved_mem.c

8b66b4b9614f1c7 Li Chen 2022-07-11  457  struct reserved_mem *get_reserved_mem_from_dev(struct device *dev)
8b66b4b9614f1c7 Li Chen 2022-07-11  458  {
8b66b4b9614f1c7 Li Chen 2022-07-11  459  	struct device_node *np = dev_of_node(dev);
8b66b4b9614f1c7 Li Chen 2022-07-11  460  	struct device_node *rmem_np;
8b66b4b9614f1c7 Li Chen 2022-07-11  461  	struct reserved_mem *rmem = NULL;
8b66b4b9614f1c7 Li Chen 2022-07-11  462  
8b66b4b9614f1c7 Li Chen 2022-07-11  463  	rmem_np = of_parse_phandle(np, "memory-region", 0);
8b66b4b9614f1c7 Li Chen 2022-07-11  464  	if (!rmem_np) {
8b66b4b9614f1c7 Li Chen 2022-07-11  465  		dev_err(dev, "failed to get memory region node\n");
8b66b4b9614f1c7 Li Chen 2022-07-11  466  		return ERR_PTR(-ENODEV);
8b66b4b9614f1c7 Li Chen 2022-07-11  467  	}
8b66b4b9614f1c7 Li Chen 2022-07-11  468  
8b66b4b9614f1c7 Li Chen 2022-07-11  469  	rmem = of_reserved_mem_lookup(rmem_np);
8b66b4b9614f1c7 Li Chen 2022-07-11  470  	if (!rmem) {
8b66b4b9614f1c7 Li Chen 2022-07-11  471  		dev_err(dev, "Failed to lookup reserved memory\n");
8b66b4b9614f1c7 Li Chen 2022-07-11 @472  		return ERR_PTR(EINVAL);

Missing - character on -EINVAL.

8b66b4b9614f1c7 Li Chen 2022-07-11  473  	}
8b66b4b9614f1c7 Li Chen 2022-07-11  474  	return rmem;
8b66b4b9614f1c7 Li Chen 2022-07-11  475  }
diff mbox series

Patch

diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 80b5fd44ab1c..0297c03328d8 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -73,6 +73,15 @@  config OF_IRQ
 config OF_RESERVED_MEM
 	def_bool OF_EARLY_FLATTREE
 
+config OF_RESERVED_MEM_DIO_SUPPORT
+	bool "add Direct I/O support to reserved_mem"
+	depends on ZONE_DEVICE && ARCH_KEEP_MEMBLOCK
+	help
+	   By default, reserved memory don't get struct page support, which
+		 means you cannot do Direct I/O from this region. This config takes
+		 uses of ZONE_DEVICE and treats rmem as hotplug mem to get struct
+		 page and DIO support.
+
 config OF_RESOLVE
 	bool
 
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 9da8835ba5a5..f4974da6df98 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -72,7 +72,6 @@  void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
 	rmem->size = size;
 
 	reserved_mem_count++;
-	return;
 }
 
 /*
@@ -447,3 +446,220 @@  struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
 	return NULL;
 }
 EXPORT_SYMBOL_GPL(of_reserved_mem_lookup);
+
+/**
+ * get_reserved_mem_from_dev() - get reserved_mem from a device node
+ * @dev: device pointer
+ *
+ * This function look for reserved_mem from given device.
+ *
+ * Returns a reserved_mem pointer, or NULL on error.
+ */
+struct reserved_mem *get_reserved_mem_from_dev(struct device *dev)
+{
+	struct device_node *np = dev_of_node(dev);
+	struct device_node *rmem_np;
+	struct reserved_mem *rmem = NULL;
+
+	rmem_np = of_parse_phandle(np, "memory-region", 0);
+	if (!rmem_np) {
+		dev_err(dev, "failed to get memory region node\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	rmem = of_reserved_mem_lookup(rmem_np);
+	if (!rmem) {
+		dev_err(dev, "Failed to lookup reserved memory\n");
+		return ERR_PTR(EINVAL);
+	}
+	return rmem;
+}
+EXPORT_SYMBOL_GPL(get_reserved_mem_from_dev);
+
+#ifdef CONFIG_OF_RESERVED_MEM_DIO_SUPPORT
+
+static int reserved_mem_dio_in_region(unsigned long addr,
+				      unsigned long size,
+				      const struct reserved_mem *rmem)
+{
+	if ((rmem && (addr >= rmem->base) &&
+	    ((addr + size) <= (rmem->base + rmem->size))))
+		return 0;
+
+	return -EINVAL;
+}
+
+static int reserved_mem_dio_get_page(struct mm_struct *mm,
+				     unsigned long start,
+				     struct page **page,
+				     const struct reserved_mem *rmem)
+{
+	unsigned long vaddr = start & PAGE_MASK, pfn;
+	int ret = -EFAULT;
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+	p4d_t *p4d;
+
+	pgd = pgd_offset(mm, vaddr);
+	if (pgd_none(*pgd))
+		return ret;
+
+	p4d = p4d_offset(pgd, vaddr);
+	if (p4d_none(*p4d))
+		return ret;
+
+	pud = pud_offset(p4d, vaddr);
+	if (pud_none(*pud))
+		return ret;
+
+	pmd = pmd_offset(pud, vaddr);
+	if (pmd_none(*pmd))
+		return ret;
+
+	pte = pte_offset_map(pmd, vaddr);
+	if (pte_none(*pte))
+		goto out;
+
+	pfn = pte_pfn(*pte);
+	if (!pfn_valid(pfn))
+		goto out;
+
+	if (likely(reserved_mem_dio_in_region(pfn << PAGE_SHIFT, PAGE_SIZE, rmem) <
+		   0))
+		goto out;
+
+	if (page) {
+		*page = pfn_to_page(pfn);
+		get_page(*page);
+	}
+
+	ret = 0;
+
+out:
+	pte_unmap(pte);
+	return ret;
+}
+
+static struct page *reserved_mem_dio_find_special_page(struct vm_area_struct *vma,
+						       unsigned long addr)
+{
+	struct page *page = NULL;
+
+	reserved_mem_dio_get_page(vma->vm_mm, addr, &page,
+				  (struct reserved_mem *)vma->vm_private_data);
+	return page;
+}
+
+static const struct vm_operations_struct rmem_dio_vmops = {
+	.find_special_page = reserved_mem_dio_find_special_page,
+};
+
+/**
+ * reserved_mem_dio_mmap() - mmap helper function to map given rmem to userspace
+ *					   with struct pages support
+ * @file: file pointing to address space structure to wait for
+ * @vma:  the vm area in which the mapping is added
+ * @rmem: reserved memory region from dts, which can be obtained from
+ *        get_reserved_mem_from_dev(dev)
+ *
+ * Returns: 0 on success or a negative error-code on failure.
+ */
+int reserved_mem_dio_mmap(struct file *file, struct vm_area_struct *vma, struct reserved_mem *rmem)
+{
+	int ret = 0;
+	unsigned long nr_pages;
+
+	if (!rmem) {
+		pr_err("%s: failed to get rmem from private data\n", __func__);
+		return -ENOMEM;
+	}
+	if (!rmem->pages) {
+		pr_err("%s: failed to get struct pages from reserved mem\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (!rmem->nr_pages) {
+		pr_err("%s: error: rmem nr_pages is 0\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (vma->vm_end - vma->vm_start > rmem->size)
+		return -EINVAL;
+
+	vma->vm_private_data = rmem;
+
+	/* duplicitate nr_pages in that vm_insert_pages can change nr_pages */
+	nr_pages = rmem->nr_pages;
+
+	/*
+	 * use vm_insert_pages instead of add remap_pfn_range variant
+	 * because vm_insert_pages will invoke rmap functions to inc _mapcount,
+	 * while latter don't do it. When unmap,
+	 * kernel will warn if page's _mapcount is <= -1.
+	 */
+	ret = vm_insert_pages(vma, vma->vm_start, rmem->pages, &nr_pages);
+	if (ret < 0)
+		pr_err("%s vm_insert_pages fail, error is %d\n", __func__, ret);
+
+	vma->vm_ops = &rmem_dio_vmops;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(reserved_mem_dio_mmap);
+
+/**
+ * reserved_mem_memremap_pages() - build struct pages for reserved mem
+ * @dev: device pointer
+ * @rmem: reserved memory region from dts, which can be get by
+ *        get_reserved_mem_from_dev(dev)
+ *
+ * Returns: 0 on success or a negative error-code on failure.
+ */
+void *reserved_mem_memremap_pages(struct device *dev, struct reserved_mem *rmem)
+{
+	struct dev_pagemap *pgmap_rmem_dio;
+	void *vaddr;
+	struct page **pages;
+	int i;
+	unsigned long offset = 0;
+	struct page *page;
+
+	rmem->nr_pages = DIV_ROUND_UP(rmem->size, PAGE_SIZE);
+	pages = kvmalloc_array(rmem->nr_pages, sizeof(*pages), GFP_KERNEL);
+	if (!pages)
+		return ERR_PTR(-ENOMEM);
+
+	pgmap_rmem_dio = devm_kzalloc(dev, sizeof(*pgmap_rmem_dio), GFP_KERNEL);
+
+	pgmap_rmem_dio->range.start = rmem->base;
+	pgmap_rmem_dio->range.end = rmem->base + rmem->size - 1;
+	pgmap_rmem_dio->nr_range = 1;
+	pgmap_rmem_dio->type = MEMORY_DEVICE_GENERIC;
+
+	pr_debug("%s, will do devm_memremap_pages, start from %llx, to %llx\n",
+		 __func__, pgmap_rmem_dio->range.start, pgmap_rmem_dio->range.end);
+
+	vaddr = devm_memremap_pages(dev, pgmap_rmem_dio);
+
+	if (IS_ERR_OR_NULL(vaddr)) {
+		dev_err(dev, "%s %d: %ld", __func__, __LINE__, PTR_ERR(vaddr));
+		return vaddr;
+	}
+
+	rmem->pages = pages;
+
+	for (i = 0; i < rmem->nr_pages; offset += PAGE_SIZE) {
+		page = virt_to_page((unsigned long)vaddr + offset);
+		if (!page) {
+			pr_err("%s: virt_to_page fail\n", __func__);
+			return ERR_PTR(-ENOMEM);
+		}
+		pages[i++] = page;
+	}
+
+	return vaddr;
+}
+EXPORT_SYMBOL_GPL(reserved_mem_memremap_pages);
+#endif
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 4de2a24cadc9..0aa1ef883060 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -16,6 +16,10 @@  struct reserved_mem {
 	phys_addr_t			base;
 	phys_addr_t			size;
 	void				*priv;
+#ifdef CONFIG_OF_RESERVED_MEM_DIO_SUPPORT
+	struct page                     **pages; /* point to array of struct pages of this region */
+	unsigned long                   nr_pages; /* number of struct page* */
+#endif
 };
 
 struct reserved_mem_ops {
@@ -81,4 +85,11 @@  static inline int of_reserved_mem_device_init(struct device *dev)
 	return of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0);
 }
 
+struct reserved_mem *get_reserved_mem_from_dev(struct device *dev);
+
+#ifdef CONFIG_OF_RESERVED_MEM_DIO_SUPPORT
+int reserved_mem_dio_mmap(struct file *file, struct vm_area_struct *vma, struct reserved_mem *rmem);
+void *reserved_mem_memremap_pages(struct device *dev, struct reserved_mem *rmem);
+#endif
+
 #endif /* __OF_RESERVED_MEM_H */