Skip to content

Commit 06c8994

Browse files
Baoquan Heakpm00
Baoquan He
authored andcommitted
mm/vmalloc.c: allow vread() to read out vm_map_ram areas
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 are all skipped. Here, add a new function vmap_ram_vread() to read out vm_map_ram areas. The area created with vmap_ram_vread() interface directly can be handled like the other normal vmap areas with aligned_vread(). While areas which will be further subdivided and managed with vmap_block need carefully read out page-aligned small regions and zero fill holes. Link: https://lkml.kernel.org/r/[email protected] Reported-by: Stephen Brennan <[email protected]> Signed-off-by: Baoquan He <[email protected]> Reviewed-by: Lorenzo Stoakes <[email protected]> Tested-by: Stephen Brennan <[email protected]> Cc: Dan Carpenter <[email protected]> Cc: Uladzislau Rezki (Sony) <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 869176a commit 06c8994

File tree

1 file changed

+81
-7
lines changed

1 file changed

+81
-7
lines changed

mm/vmalloc.c

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3463,6 +3463,68 @@ static int aligned_vread(char *buf, char *addr, unsigned long count)
34633463
return copied;
34643464
}
34653465

3466+
static void vmap_ram_vread(char *buf, char *addr, int count, unsigned long flags)
3467+
{
3468+
char *start;
3469+
struct vmap_block *vb;
3470+
unsigned long offset;
3471+
unsigned int rs, re, n;
3472+
3473+
/*
3474+
* If it's area created by vm_map_ram() interface directly, but
3475+
* not further subdividing and delegating management to vmap_block,
3476+
* handle it here.
3477+
*/
3478+
if (!(flags & VMAP_BLOCK)) {
3479+
aligned_vread(buf, addr, count);
3480+
return;
3481+
}
3482+
3483+
/*
3484+
* Area is split into regions and tracked with vmap_block, read out
3485+
* each region and zero fill the hole between regions.
3486+
*/
3487+
vb = xa_load(&vmap_blocks, addr_to_vb_idx((unsigned long)addr));
3488+
if (!vb)
3489+
goto finished;
3490+
3491+
spin_lock(&vb->lock);
3492+
if (bitmap_empty(vb->used_map, VMAP_BBMAP_BITS)) {
3493+
spin_unlock(&vb->lock);
3494+
goto finished;
3495+
}
3496+
for_each_set_bitrange(rs, re, vb->used_map, VMAP_BBMAP_BITS) {
3497+
if (!count)
3498+
break;
3499+
start = vmap_block_vaddr(vb->va->va_start, rs);
3500+
while (addr < start) {
3501+
if (count == 0)
3502+
goto unlock;
3503+
*buf = '\0';
3504+
buf++;
3505+
addr++;
3506+
count--;
3507+
}
3508+
/*it could start reading from the middle of used region*/
3509+
offset = offset_in_page(addr);
3510+
n = ((re - rs + 1) << PAGE_SHIFT) - offset;
3511+
if (n > count)
3512+
n = count;
3513+
aligned_vread(buf, start+offset, n);
3514+
3515+
buf += n;
3516+
addr += n;
3517+
count -= n;
3518+
}
3519+
unlock:
3520+
spin_unlock(&vb->lock);
3521+
3522+
finished:
3523+
/* zero-fill the left dirty or free regions */
3524+
if (count)
3525+
memset(buf, 0, count);
3526+
}
3527+
34663528
/**
34673529
* vread() - read vmalloc area in a safe way.
34683530
* @buf: buffer for reading data
@@ -3493,7 +3555,7 @@ long vread(char *buf, char *addr, unsigned long count)
34933555
struct vm_struct *vm;
34943556
char *vaddr, *buf_start = buf;
34953557
unsigned long buflen = count;
3496-
unsigned long n;
3558+
unsigned long n, size, flags;
34973559

34983560
addr = kasan_reset_tag(addr);
34993561

@@ -3514,12 +3576,21 @@ long vread(char *buf, char *addr, unsigned long count)
35143576
if (!count)
35153577
break;
35163578

3517-
if (!va->vm)
3579+
vm = va->vm;
3580+
flags = va->flags & VMAP_FLAGS_MASK;
3581+
/*
3582+
* VMAP_BLOCK indicates a sub-type of vm_map_ram area, need
3583+
* be set together with VMAP_RAM.
3584+
*/
3585+
WARN_ON(flags == VMAP_BLOCK);
3586+
3587+
if (!vm && !flags)
35183588
continue;
35193589

3520-
vm = va->vm;
3521-
vaddr = (char *) vm->addr;
3522-
if (addr >= vaddr + get_vm_area_size(vm))
3590+
vaddr = (char *) va->va_start;
3591+
size = vm ? get_vm_area_size(vm) : va_size(va);
3592+
3593+
if (addr >= vaddr + size)
35233594
continue;
35243595
while (addr < vaddr) {
35253596
if (count == 0)
@@ -3529,10 +3600,13 @@ long vread(char *buf, char *addr, unsigned long count)
35293600
addr++;
35303601
count--;
35313602
}
3532-
n = vaddr + get_vm_area_size(vm) - addr;
3603+
n = vaddr + size - addr;
35333604
if (n > count)
35343605
n = count;
3535-
if (!(vm->flags & VM_IOREMAP))
3606+
3607+
if (flags & VMAP_RAM)
3608+
vmap_ram_vread(buf, addr, n, flags);
3609+
else if (!(vm->flags & VM_IOREMAP))
35363610
aligned_vread(buf, addr, n);
35373611
else /* IOREMAP area is treated as memory hole */
35383612
memset(buf, 0, n);

0 commit comments

Comments
 (0)