[v1,3/7] mm/vmalloc.c: allow vread() to read out vm_map_ram areas

Message ID 20221204013046.154960-4-bhe@redhat.com
State New
Headers
Series mm/vmalloc.c: allow vread() to read out vm_map_ram areas |

Commit Message

Baoquan He Dec. 4, 2022, 1:30 a.m. UTC
  Currently, vread can read out vmalloc areas which is associated with
a vm_struct. While this doesn't work for areas created by vm_map_ram()
interface because it doesn't have an associated vm_struct. Then in vread(),
these areas will be skipped.

Here, add a new function vb_vread() to read out areas managed by
vmap_block specifically. Then recognize vm_map_ram areas via vmap->flags
and handle them respectively.

Signed-off-by: Baoquan He <bhe@redhat.com>
---
 mm/vmalloc.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 54 insertions(+), 7 deletions(-)
  

Comments

kernel test robot Dec. 4, 2022, 3:47 a.m. UTC | #1
Hi Baoquan,

I love your patch! Perhaps something to improve:

[auto build test WARNING on akpm-mm/mm-everything]

url:    https://github.com/intel-lab-lkp/linux/commits/Baoquan-He/mm-vmalloc-c-allow-vread-to-read-out-vm_map_ram-areas/20221204-093322
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link:    https://lore.kernel.org/r/20221204013046.154960-4-bhe%40redhat.com
patch subject: [PATCH v1 3/7] mm/vmalloc.c: allow vread() to read out vm_map_ram areas
config: arm-randconfig-r046-20221204
compiler: arm-linux-gnueabi-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/0bcc4ce1e46418b86eb569175879081116649727
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Baoquan-He/mm-vmalloc-c-allow-vread-to-read-out-vm_map_ram-areas/20221204-093322
        git checkout 0bcc4ce1e46418b86eb569175879081116649727
        # 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=arm SHELL=/bin/bash

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 >>):

   mm/vmalloc.c: In function 'vb_vread':
>> mm/vmalloc.c:3540:23: warning: variable 'offset' set but not used [-Wunused-but-set-variable]
    3540 |         unsigned long offset;
         |                       ^~~~~~


vim +/offset +3540 mm/vmalloc.c

  3535	
  3536	static void vb_vread(char *buf, char *addr, int count)
  3537	{
  3538		char *start;
  3539		struct vmap_block *vb;
> 3540		unsigned long offset;
  3541		unsigned int rs, re, n;
  3542	
  3543		offset = ((unsigned long)addr & (VMAP_BLOCK_SIZE - 1)) >> PAGE_SHIFT;
  3544		vb = xa_load(&vmap_blocks, addr_to_vb_idx((unsigned long)addr));
  3545	
  3546		spin_lock(&vb->lock);
  3547		if (bitmap_empty(vb->used_map, VMAP_BBMAP_BITS)) {
  3548			spin_unlock(&vb->lock);
  3549			memset(buf, 0, count);
  3550			return;
  3551		}
  3552		for_each_set_bitrange(rs, re, vb->used_map, VMAP_BBMAP_BITS) {
  3553			if (!count)
  3554				break;
  3555			start = vmap_block_vaddr(vb->va->va_start, rs);
  3556			if (addr < start) {
  3557				if (count == 0)
  3558					break;
  3559				*buf = '\0';
  3560				buf++;
  3561				addr++;
  3562				count--;
  3563			}
  3564			n = (re - rs + 1) << PAGE_SHIFT;
  3565			if (n > count)
  3566				n = count;
  3567			aligned_vread(buf, start, n);
  3568	
  3569			buf += n;
  3570			addr += n;
  3571			count -= n;
  3572		}
  3573		spin_unlock(&vb->lock);
  3574	}
  3575
  
Baoquan He Dec. 17, 2022, 1:14 a.m. UTC | #2
On 12/04/22 at 11:47am, kernel test robot wrote:
> Hi Baoquan,
> 
> I love your patch! Perhaps something to improve:
> 
> [auto build test WARNING on akpm-mm/mm-everything]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Baoquan-He/mm-vmalloc-c-allow-vread-to-read-out-vm_map_ram-areas/20221204-093322
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
> patch link:    https://lore.kernel.org/r/20221204013046.154960-4-bhe%40redhat.com
> patch subject: [PATCH v1 3/7] mm/vmalloc.c: allow vread() to read out vm_map_ram areas
> config: arm-randconfig-r046-20221204
> compiler: arm-linux-gnueabi-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/0bcc4ce1e46418b86eb569175879081116649727
>         git remote add linux-review https://github.com/intel-lab-lkp/linux
>         git fetch --no-tags linux-review Baoquan-He/mm-vmalloc-c-allow-vread-to-read-out-vm_map_ram-areas/20221204-093322
>         git checkout 0bcc4ce1e46418b86eb569175879081116649727
>         # 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=arm SHELL=/bin/bash
> 
> 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 >>):
> 
>    mm/vmalloc.c: In function 'vb_vread':
> >> mm/vmalloc.c:3540:23: warning: variable 'offset' set but not used [-Wunused-but-set-variable]
>     3540 |         unsigned long offset;
>          |                       ^~~~~~

Thanks.

The local variable 'offset' is needed, the handling in vb_vread() need
be improved to cover the case in which reading is started from dirty or
free regions. I will add below change to v2.


diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 78cae59170d8..6612914459cf 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3526,7 +3522,6 @@ static void vb_vread(char *buf, char *addr, int count)
 	unsigned long offset;
 	unsigned int rs, re, n;
 
-	offset = ((unsigned long)addr & (VMAP_BLOCK_SIZE - 1)) >> PAGE_SHIFT;
 	vb = xa_load(&vmap_blocks, addr_to_vb_idx((unsigned long)addr));
 
 	spin_lock(&vb->lock);
@@ -3547,16 +3542,22 @@ static void vb_vread(char *buf, char *addr, int count)
 			addr++;
 			count--;
 		}
-		n = (re - rs + 1) << PAGE_SHIFT;
+		/*it could start reading from the middle of used region*/
+		offset = offset_in_page(addr);
+		n = (re - rs + 1) << PAGE_SHIFT - offset;
 		if (n > count)
 			n = count;
-		aligned_vread(buf, start, n);
+		aligned_vread(buf, start+offset, n);
 
 		buf += n;
 		addr += n;
 		count -= n;
 	}
 	spin_unlock(&vb->lock);
+
+	/* zero-fill the left dirty or free regions */
+	if (count)
+		memset(buf, 0, count);
 }
 
 /**
  

Patch

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index d6f376060d83..e6b46da3e044 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3519,6 +3519,46 @@  static int aligned_vread(char *buf, char *addr, unsigned long count)
 	return copied;
 }
 
+static void vb_vread(char *buf, char *addr, int count)
+{
+	char *start;
+	struct vmap_block *vb;
+	unsigned long offset;
+	unsigned int rs, re, n;
+
+	offset = ((unsigned long)addr & (VMAP_BLOCK_SIZE - 1)) >> PAGE_SHIFT;
+	vb = xa_load(&vmap_blocks, addr_to_vb_idx((unsigned long)addr));
+
+	spin_lock(&vb->lock);
+	if (bitmap_empty(vb->used_map, VMAP_BBMAP_BITS)) {
+		spin_unlock(&vb->lock);
+		memset(buf, 0, count);
+		return;
+	}
+	for_each_set_bitrange(rs, re, vb->used_map, VMAP_BBMAP_BITS) {
+		if (!count)
+			break;
+		start = vmap_block_vaddr(vb->va->va_start, rs);
+		if (addr < start) {
+			if (count == 0)
+				break;
+			*buf = '\0';
+			buf++;
+			addr++;
+			count--;
+		}
+		n = (re - rs + 1) << PAGE_SHIFT;
+		if (n > count)
+			n = count;
+		aligned_vread(buf, start, n);
+
+		buf += n;
+		addr += n;
+		count -= n;
+	}
+	spin_unlock(&vb->lock);
+}
+
 /**
  * vread() - read vmalloc area in a safe way.
  * @buf:     buffer for reading data
@@ -3549,7 +3589,7 @@  long vread(char *buf, char *addr, unsigned long count)
 	struct vm_struct *vm;
 	char *vaddr, *buf_start = buf;
 	unsigned long buflen = count;
-	unsigned long n;
+	unsigned long n, size, flags;
 
 	addr = kasan_reset_tag(addr);
 
@@ -3570,12 +3610,16 @@  long vread(char *buf, char *addr, unsigned long count)
 		if (!count)
 			break;
 
-		if (!va->vm)
+		vm = va->vm;
+		flags = va->flags & VMAP_FLAGS_MASK;
+
+		if (!vm && !flags)
 			continue;
 
-		vm = va->vm;
-		vaddr = (char *) vm->addr;
-		if (addr >= vaddr + get_vm_area_size(vm))
+		vaddr = (char *) va->va_start;
+		size = flags ? va_size(va) : get_vm_area_size(vm);
+
+		if (addr >= vaddr + size)
 			continue;
 		while (addr < vaddr) {
 			if (count == 0)
@@ -3585,10 +3629,13 @@  long vread(char *buf, char *addr, unsigned long count)
 			addr++;
 			count--;
 		}
-		n = vaddr + get_vm_area_size(vm) - addr;
+		n = vaddr + size - addr;
 		if (n > count)
 			n = count;
-		if (!(vm->flags & VM_IOREMAP))
+
+		if ((flags & (VMAP_RAM|VMAP_BLOCK)) == (VMAP_RAM|VMAP_BLOCK))
+			vb_vread(buf, addr, n);
+		else if ((flags & VMAP_RAM) || !(vm->flags & VM_IOREMAP))
 			aligned_vread(buf, addr, n);
 		else /* IOREMAP area is treated as memory hole */
 			memset(buf, 0, n);