Skip to content

Commit edd8981

Browse files
urezkiakpm00
authored andcommitted
mm: vmalloc: avoid calling __find_vmap_area() twice in __vunmap()
Currently the __vunmap() path calls __find_vmap_area() twice. Once on entry to check that the area exists, then inside the remove_vm_area() function which also performs a new search for the VA. In order to improvie it from a performance point of view we split remove_vm_area() into two new parts: - find_unlink_vmap_area() that does a search and unlink from tree; - __remove_vm_area() that removes without searching. In this case there is no any functional change for remove_vm_area() whereas vm_remove_mappings(), where a second search happens, switches to the __remove_vm_area() variant where the already detached VA is passed as a parameter, so there is no need to find it again. Performance wise, i use test_vmalloc.sh with 32 threads doing alloc free on a 64-CPUs-x86_64-box: perf without this patch: - 31.41% 0.50% vmalloc_test/10 [kernel.vmlinux] [k] __vunmap - 30.92% __vunmap - 17.67% _raw_spin_lock native_queued_spin_lock_slowpath - 12.33% remove_vm_area - 11.79% free_vmap_area_noflush - 11.18% _raw_spin_lock native_queued_spin_lock_slowpath 0.76% free_unref_page perf with this patch: - 11.35% 0.13% vmalloc_test/14 [kernel.vmlinux] [k] __vunmap - 11.23% __vunmap - 8.28% find_unlink_vmap_area - 7.95% _raw_spin_lock 7.44% native_queued_spin_lock_slowpath - 1.93% free_vmap_area_noflush - 0.56% _raw_spin_lock 0.53% native_queued_spin_lock_slowpath 0.60% __vunmap_range_noflush __vunmap() consumes around ~20% less CPU cycles on this test. Also, switch from find_vmap_area() to find_unlink_vmap_area() to prevent a double access to the vmap_area_lock: one for finding area, second time is for unlinking from a tree. [[email protected]: switch to find_unlink_vmap_area() in vm_unmap_ram()] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Uladzislau Rezki (Sony) <[email protected]> Reported-by: Roman Gushchin <[email protected]> Reviewed-by: Lorenzo Stoakes <[email protected]> Cc: Baoquan He <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Nicholas Piggin <[email protected]> Cc: Oleksiy Avramchenko <[email protected]> Cc: Christoph Hellwig <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent b505417 commit edd8981

File tree

1 file changed

+48
-31
lines changed

1 file changed

+48
-31
lines changed

mm/vmalloc.c

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,19 +1815,18 @@ static void drain_vmap_area_work(struct work_struct *work)
18151815
}
18161816

18171817
/*
1818-
* Free a vmap area, caller ensuring that the area has been unmapped
1819-
* and flush_cache_vunmap had been called for the correct range
1820-
* previously.
1818+
* Free a vmap area, caller ensuring that the area has been unmapped,
1819+
* unlinked and flush_cache_vunmap had been called for the correct
1820+
* range previously.
18211821
*/
18221822
static void free_vmap_area_noflush(struct vmap_area *va)
18231823
{
18241824
unsigned long nr_lazy_max = lazy_max_pages();
18251825
unsigned long va_start = va->va_start;
18261826
unsigned long nr_lazy;
18271827

1828-
spin_lock(&vmap_area_lock);
1829-
unlink_va(va, &vmap_area_root);
1830-
spin_unlock(&vmap_area_lock);
1828+
if (WARN_ON_ONCE(!list_empty(&va->list)))
1829+
return;
18311830

18321831
nr_lazy = atomic_long_add_return((va->va_end - va->va_start) >>
18331832
PAGE_SHIFT, &vmap_lazy_nr);
@@ -1871,6 +1870,19 @@ struct vmap_area *find_vmap_area(unsigned long addr)
18711870
return va;
18721871
}
18731872

1873+
static struct vmap_area *find_unlink_vmap_area(unsigned long addr)
1874+
{
1875+
struct vmap_area *va;
1876+
1877+
spin_lock(&vmap_area_lock);
1878+
va = __find_vmap_area(addr, &vmap_area_root);
1879+
if (va)
1880+
unlink_va(va, &vmap_area_root);
1881+
spin_unlock(&vmap_area_lock);
1882+
1883+
return va;
1884+
}
1885+
18741886
/*** Per cpu kva allocator ***/
18751887

18761888
/*
@@ -2015,6 +2027,10 @@ static void free_vmap_block(struct vmap_block *vb)
20152027
tmp = xa_erase(&vmap_blocks, addr_to_vb_idx(vb->va->va_start));
20162028
BUG_ON(tmp != vb);
20172029

2030+
spin_lock(&vmap_area_lock);
2031+
unlink_va(vb->va, &vmap_area_root);
2032+
spin_unlock(&vmap_area_lock);
2033+
20182034
free_vmap_area_noflush(vb->va);
20192035
kfree_rcu(vb, rcu_head);
20202036
}
@@ -2236,7 +2252,7 @@ void vm_unmap_ram(const void *mem, unsigned int count)
22362252
return;
22372253
}
22382254

2239-
va = find_vmap_area(addr);
2255+
va = find_unlink_vmap_area(addr);
22402256
BUG_ON(!va);
22412257
debug_check_no_locks_freed((void *)va->va_start,
22422258
(va->va_end - va->va_start));
@@ -2591,6 +2607,20 @@ struct vm_struct *find_vm_area(const void *addr)
25912607
return va->vm;
25922608
}
25932609

2610+
static struct vm_struct *__remove_vm_area(struct vmap_area *va)
2611+
{
2612+
struct vm_struct *vm;
2613+
2614+
if (!va || !va->vm)
2615+
return NULL;
2616+
2617+
vm = va->vm;
2618+
kasan_free_module_shadow(vm);
2619+
free_unmap_vmap_area(va);
2620+
2621+
return vm;
2622+
}
2623+
25942624
/**
25952625
* remove_vm_area - find and remove a continuous kernel virtual area
25962626
* @addr: base address
@@ -2603,26 +2633,10 @@ struct vm_struct *find_vm_area(const void *addr)
26032633
*/
26042634
struct vm_struct *remove_vm_area(const void *addr)
26052635
{
2606-
struct vmap_area *va;
2607-
26082636
might_sleep();
26092637

2610-
spin_lock(&vmap_area_lock);
2611-
va = __find_vmap_area((unsigned long)addr, &vmap_area_root);
2612-
if (va && va->vm) {
2613-
struct vm_struct *vm = va->vm;
2614-
2615-
va->vm = NULL;
2616-
spin_unlock(&vmap_area_lock);
2617-
2618-
kasan_free_module_shadow(vm);
2619-
free_unmap_vmap_area(va);
2620-
2621-
return vm;
2622-
}
2623-
2624-
spin_unlock(&vmap_area_lock);
2625-
return NULL;
2638+
return __remove_vm_area(
2639+
find_unlink_vmap_area((unsigned long) addr));
26262640
}
26272641

26282642
static inline void set_area_direct_map(const struct vm_struct *area,
@@ -2636,16 +2650,17 @@ static inline void set_area_direct_map(const struct vm_struct *area,
26362650
set_direct_map(area->pages[i]);
26372651
}
26382652

2639-
/* Handle removing and resetting vm mappings related to the vm_struct. */
2640-
static void vm_remove_mappings(struct vm_struct *area, int deallocate_pages)
2653+
/* Handle removing and resetting vm mappings related to the VA's vm_struct. */
2654+
static void va_remove_mappings(struct vmap_area *va, int deallocate_pages)
26412655
{
2656+
struct vm_struct *area = va->vm;
26422657
unsigned long start = ULONG_MAX, end = 0;
26432658
unsigned int page_order = vm_area_page_order(area);
26442659
int flush_reset = area->flags & VM_FLUSH_RESET_PERMS;
26452660
int flush_dmap = 0;
26462661
int i;
26472662

2648-
remove_vm_area(area->addr);
2663+
__remove_vm_area(va);
26492664

26502665
/* If this is not VM_FLUSH_RESET_PERMS memory, no need for the below. */
26512666
if (!flush_reset)
@@ -2690,6 +2705,7 @@ static void vm_remove_mappings(struct vm_struct *area, int deallocate_pages)
26902705
static void __vunmap(const void *addr, int deallocate_pages)
26912706
{
26922707
struct vm_struct *area;
2708+
struct vmap_area *va;
26932709

26942710
if (!addr)
26952711
return;
@@ -2698,19 +2714,20 @@ static void __vunmap(const void *addr, int deallocate_pages)
26982714
addr))
26992715
return;
27002716

2701-
area = find_vm_area(addr);
2702-
if (unlikely(!area)) {
2717+
va = find_unlink_vmap_area((unsigned long)addr);
2718+
if (unlikely(!va)) {
27032719
WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",
27042720
addr);
27052721
return;
27062722
}
27072723

2724+
area = va->vm;
27082725
debug_check_no_locks_freed(area->addr, get_vm_area_size(area));
27092726
debug_check_no_obj_freed(area->addr, get_vm_area_size(area));
27102727

27112728
kasan_poison_vmalloc(area->addr, get_vm_area_size(area));
27122729

2713-
vm_remove_mappings(area, deallocate_pages);
2730+
va_remove_mappings(va, deallocate_pages);
27142731

27152732
if (deallocate_pages) {
27162733
int i;

0 commit comments

Comments
 (0)