Re: Excessive TLB flush ranges

Baoquan He
2023-05-16T10:05:40Z

On 05/16/23 at 11:03am, Thomas Gleixner wrote:
......
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -1724,7 +1724,8 @@ static void purge_fragmented_blocks_allc
> */
> static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
> {
> - unsigned long resched_threshold;
> + unsigned long resched_threshold, num_entries = 0, num_alias_entries = 0;
> + struct vmap_area alias_va = { .va_start = start, .va_end = end };

Note that the start and end passed in are not only direct map which is
alias of va. It is the range which has done merging on direct map range
and all dirty range of vbq in _vm_unmap_aliases(). We may need to append
below draft code on your patch to at least flush the direct map range
separately.

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 7672c2422f0c..beaaa2f983d3 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -1722,13 +1722,15 @@ static void purge_fragmented_blocks_allcpus(void);
/*
* Purges all lazily-freed vmap areas.
*/
-static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
+static bool __purge_vmap_area_lazy(struct *range)
{
unsigned long resched_threshold, num_entries = 0, num_alias_entries = 0;
- struct vmap_area alias_va = { .va_start = start, .va_end = end };
+ struct vmap_area alias_va = { .va_start = range[0].start, .va_end = range[0].end };
+ struct vmap_area dirty_va = { .va_start = range[1].start, .va_end = range[1].end };
unsigned int num_purged_areas = 0;
struct list_head local_purge_list;
struct vmap_area *va, *n_va;
+ unsigned long start = ULONG_MAX, end = 0;

lockdep_assert_held(&vmap_purge_lock);

@@ -1737,6 +1739,10 @@ static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
list_replace_init(&purge_vmap_area_list, &local_purge_list);
spin_unlock(&purge_vmap_area_lock);

+ start = alias_va.va_start;
+ end = alias_va.va_end;
+ start = min(start, dirty_va.va_start);
+ end = min(start, dirty_va.va_end);
start = min(start, list_first_entry(&local_purge_list, struct vmap_area, list)->va_start);
end = max(end, list_last_entry(&local_purge_list, struct vmap_area, list)->va_end);

@@ -1752,6 +1758,10 @@ static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
list_add(&alias_va.list, &local_purge_list);
}

+ if (dirty_va.va_end > dirty_va.va_start) {
+ num_alias_entries = (dirty_va.va_end - dirty_va.va_start) >> PAGE_SHIFT;
+ list_add(&dirty_va.list, &local_purge_list);
+ }
flush_tlb_kernel_vas(&local_purge_list, num_entries + num_alias_entries);

if (num_alias_entries)
@@ -2236,15 +2246,18 @@ static void vb_free(unsigned long addr, unsigned long size)
spin_unlock(&vb->lock);
}

-static void _vm_unmap_aliases(unsigned long start, unsigned long end, int flush)
+static void _vm_unmap_aliases(unsigned long dm_start, unsigned long dm_end, int flush)
{
int cpu;
+ struct range range[2];
+ unsigned long start = ULONG_MAX, end = 0;

if (unlikely(!vmap_initialized))
return;

might_sleep();

+ range[0] = {dm_start, dm_end};
for_each_possible_cpu(cpu) {
struct vmap_block_queue *vbq = &per_cpu(vmap_block_queue, cpu);
struct vmap_block *vb;
@@ -2269,6 +2282,7 @@ static void _vm_unmap_aliases(unsigned long start, unsigned long end, int flush)
rcu_read_unlock();
}

+ range[1] = {start, end};
mutex_lock(&vmap_purge_lock);
purge_fragmented_blocks_allcpus();
if (!__purge_vmap_area_lazy(start, end) && flush)