These changes fix MM (soft-)dirty bit management in the procfs code & clean up the API.
Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmAtAgsRHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1gOnA/9GKJblgi88Qb23YwGKp0OfCMdLfx8FJa+ dq0AB0jgzc8v2J8IITSs/qo/8o25IE9IPTjTfItn0E0jxz7Y8J16urb/vyWX6O2s jb4riT5fIRCXvhv9DooxSQerZePaOJXbHYa2BBk8yqNJPGbd5kr0SUGn3BQnBQhR 0yAfqjrzBLMGzzSO+kK0nhGQH8BJZgYu94CHNnUZJtWcIb2ZC6lzZ7Lz0zi6ueRJ 81JblV4NCOC9uy9I9odOwESu2TIxT9afq1C/6COyrKYx3sWY6xPOGQTxYZe1LITE lb57/95qc7SOIj7Y3aL4YRSVRYRihEU31qlAltwP4fEnz49qdHJOR1HQmjKVG8xs Uaa6kCYFeTKmh4SRRr8ZR/hUkebrFUT+9+db6LmBs/i4Kt09T+ZurXC4jqmUHMFn 2nYCDH6RX153V1YwcHGkr4OWaUVWZwAZl+t0zIo7o7wQdkoAD75ydecW2R3nLMN7 p1ofGPXmT8Wh4en8LngBawO/4bBuunezh4L3vpz0/EU3viK5+DRsyNKf+d+Tti28 XCe7ID0GDGq7nIzSZxuyIxmAbWJxjI+7gWT2WUudrJxJ2rUUxPQms8GsQD54IMh5 UILv9GMBNuV8iA/2c3B52ff5iFl7kp+SxVS3MRC6zTudIV9VV6bb7WpFb8FLOhsH 3sEo0qDFab4= =qcXO -----END PGP SIGNATURE----- Merge tag 'core-mm-2021-02-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull tlb gather updates from Ingo Molnar: "Theses fix MM (soft-)dirty bit management in the procfs code & clean up the TLB gather API" * tag 'core-mm-2021-02-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/ldt: Use tlb_gather_mmu_fullmm() when freeing LDT page-tables tlb: arch: Remove empty __tlb_remove_tlb_entry() stubs tlb: mmu_gather: Remove start/end arguments from tlb_gather_mmu() tlb: mmu_gather: Introduce tlb_gather_mmu_fullmm() tlb: mmu_gather: Remove unused start/end arguments from tlb_finish_mmu() mm: proc: Invalidate TLB after clearing soft-dirty page state
This commit is contained in:
Коммит
7b15c27e2f
|
@ -23,7 +23,7 @@
|
|||
* unmapping a portion of the virtual address space, these hooks are called according to
|
||||
* the following template:
|
||||
*
|
||||
* tlb <- tlb_gather_mmu(mm, start, end); // start unmap for address space MM
|
||||
* tlb <- tlb_gather_mmu(mm); // start unmap for address space MM
|
||||
* {
|
||||
* for each vma that needs a shootdown do {
|
||||
* tlb_start_vma(tlb, vma);
|
||||
|
@ -36,7 +36,7 @@
|
|||
* tlb_end_vma(tlb, vma);
|
||||
* }
|
||||
* }
|
||||
* tlb_finish_mmu(tlb, start, end); // finish unmap for address space MM
|
||||
* tlb_finish_mmu(tlb); // finish unmap for address space MM
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
|
|
@ -24,7 +24,6 @@ void flush_tlb_pending(void);
|
|||
|
||||
#define tlb_start_vma(tlb, vma) do { } while (0)
|
||||
#define tlb_end_vma(tlb, vma) do { } while (0)
|
||||
#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)
|
||||
#define tlb_flush(tlb) flush_tlb_pending()
|
||||
|
||||
/*
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#define tlb_start_vma(tlb, vma) do { } while (0)
|
||||
#define tlb_end_vma(tlb, vma) do { } while (0)
|
||||
#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)
|
||||
|
||||
#define tlb_flush tlb_flush
|
||||
static inline void tlb_flush(struct mmu_gather *tlb);
|
||||
|
|
|
@ -398,9 +398,15 @@ static void free_ldt_pgtables(struct mm_struct *mm)
|
|||
if (!boot_cpu_has(X86_FEATURE_PTI))
|
||||
return;
|
||||
|
||||
tlb_gather_mmu(&tlb, mm, start, end);
|
||||
/*
|
||||
* Although free_pgd_range() is intended for freeing user
|
||||
* page-tables, it also works out for kernel mappings on x86.
|
||||
* We use tlb_gather_mmu_fullmm() to avoid confusing the
|
||||
* range-tracking logic in __tlb_adjust_range().
|
||||
*/
|
||||
tlb_gather_mmu_fullmm(&tlb, mm);
|
||||
free_pgd_range(&tlb, start, end, start, end);
|
||||
tlb_finish_mmu(&tlb, start, end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -708,7 +708,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
|
|||
return -ENOMEM;
|
||||
|
||||
lru_add_drain();
|
||||
tlb_gather_mmu(&tlb, mm, old_start, old_end);
|
||||
tlb_gather_mmu(&tlb, mm);
|
||||
if (new_end > old_start) {
|
||||
/*
|
||||
* when the old and new regions overlap clear from new_end.
|
||||
|
@ -725,7 +725,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
|
|||
free_pgd_range(&tlb, old_start, old_end, new_end,
|
||||
vma->vm_next ? vma->vm_next->vm_start : USER_PGTABLES_CEILING);
|
||||
}
|
||||
tlb_finish_mmu(&tlb, old_start, old_end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
|
||||
/*
|
||||
* Shrink the vma to just the new range. Always succeeds.
|
||||
|
|
|
@ -1210,7 +1210,6 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
|
|||
struct mm_struct *mm;
|
||||
struct vm_area_struct *vma;
|
||||
enum clear_refs_types type;
|
||||
struct mmu_gather tlb;
|
||||
int itype;
|
||||
int rv;
|
||||
|
||||
|
@ -1249,7 +1248,6 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
tlb_gather_mmu(&tlb, mm, 0, -1);
|
||||
if (type == CLEAR_REFS_SOFT_DIRTY) {
|
||||
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
||||
if (!(vma->vm_flags & VM_SOFTDIRTY))
|
||||
|
@ -1258,15 +1256,18 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
|
|||
vma_set_page_prot(vma);
|
||||
}
|
||||
|
||||
inc_tlb_flush_pending(mm);
|
||||
mmu_notifier_range_init(&range, MMU_NOTIFY_SOFT_DIRTY,
|
||||
0, NULL, mm, 0, -1UL);
|
||||
mmu_notifier_invalidate_range_start(&range);
|
||||
}
|
||||
walk_page_range(mm, 0, mm->highest_vm_end, &clear_refs_walk_ops,
|
||||
&cp);
|
||||
if (type == CLEAR_REFS_SOFT_DIRTY)
|
||||
if (type == CLEAR_REFS_SOFT_DIRTY) {
|
||||
mmu_notifier_invalidate_range_end(&range);
|
||||
tlb_finish_mmu(&tlb, 0, -1);
|
||||
flush_tlb_mm(mm);
|
||||
dec_tlb_flush_pending(mm);
|
||||
}
|
||||
out_unlock:
|
||||
mmap_write_unlock(mm);
|
||||
out_mm:
|
||||
|
|
|
@ -46,7 +46,9 @@
|
|||
*
|
||||
* The mmu_gather API consists of:
|
||||
*
|
||||
* - tlb_gather_mmu() / tlb_finish_mmu(); start and finish a mmu_gather
|
||||
* - tlb_gather_mmu() / tlb_gather_mmu_fullmm() / tlb_finish_mmu()
|
||||
*
|
||||
* start and finish a mmu_gather
|
||||
*
|
||||
* Finish in particular will issue a (final) TLB invalidate and free
|
||||
* all (remaining) queued pages.
|
||||
|
@ -91,7 +93,7 @@
|
|||
*
|
||||
* - mmu_gather::fullmm
|
||||
*
|
||||
* A flag set by tlb_gather_mmu() to indicate we're going to free
|
||||
* A flag set by tlb_gather_mmu_fullmm() to indicate we're going to free
|
||||
* the entire mm; this allows a number of optimizations.
|
||||
*
|
||||
* - We can ignore tlb_{start,end}_vma(); because we don't
|
||||
|
|
|
@ -588,10 +588,9 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm)
|
|||
}
|
||||
|
||||
struct mmu_gather;
|
||||
extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end);
|
||||
extern void tlb_finish_mmu(struct mmu_gather *tlb,
|
||||
unsigned long start, unsigned long end);
|
||||
extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm);
|
||||
extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm);
|
||||
extern void tlb_finish_mmu(struct mmu_gather *tlb);
|
||||
|
||||
static inline void init_tlb_flush_pending(struct mm_struct *mm)
|
||||
{
|
||||
|
|
18
mm/hugetlb.c
18
mm/hugetlb.c
|
@ -4008,25 +4008,11 @@ void __unmap_hugepage_range_final(struct mmu_gather *tlb,
|
|||
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end, struct page *ref_page)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
struct mmu_gather tlb;
|
||||
unsigned long tlb_start = start;
|
||||
unsigned long tlb_end = end;
|
||||
|
||||
/*
|
||||
* If shared PMDs were possibly used within this vma range, adjust
|
||||
* start/end for worst case tlb flushing.
|
||||
* Note that we can not be sure if PMDs are shared until we try to
|
||||
* unmap pages. However, we want to make sure TLB flushing covers
|
||||
* the largest possible range.
|
||||
*/
|
||||
adjust_range_if_pmd_sharing_possible(vma, &tlb_start, &tlb_end);
|
||||
|
||||
mm = vma->vm_mm;
|
||||
|
||||
tlb_gather_mmu(&tlb, mm, tlb_start, tlb_end);
|
||||
tlb_gather_mmu(&tlb, vma->vm_mm);
|
||||
__unmap_hugepage_range(&tlb, vma, start, end, ref_page);
|
||||
tlb_finish_mmu(&tlb, tlb_start, tlb_end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
12
mm/madvise.c
12
mm/madvise.c
|
@ -506,9 +506,9 @@ static long madvise_cold(struct vm_area_struct *vma,
|
|||
return -EINVAL;
|
||||
|
||||
lru_add_drain();
|
||||
tlb_gather_mmu(&tlb, mm, start_addr, end_addr);
|
||||
tlb_gather_mmu(&tlb, mm);
|
||||
madvise_cold_page_range(&tlb, vma, start_addr, end_addr);
|
||||
tlb_finish_mmu(&tlb, start_addr, end_addr);
|
||||
tlb_finish_mmu(&tlb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -558,9 +558,9 @@ static long madvise_pageout(struct vm_area_struct *vma,
|
|||
return 0;
|
||||
|
||||
lru_add_drain();
|
||||
tlb_gather_mmu(&tlb, mm, start_addr, end_addr);
|
||||
tlb_gather_mmu(&tlb, mm);
|
||||
madvise_pageout_page_range(&tlb, vma, start_addr, end_addr);
|
||||
tlb_finish_mmu(&tlb, start_addr, end_addr);
|
||||
tlb_finish_mmu(&tlb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -723,7 +723,7 @@ static int madvise_free_single_vma(struct vm_area_struct *vma,
|
|||
range.start, range.end);
|
||||
|
||||
lru_add_drain();
|
||||
tlb_gather_mmu(&tlb, mm, range.start, range.end);
|
||||
tlb_gather_mmu(&tlb, mm);
|
||||
update_hiwater_rss(mm);
|
||||
|
||||
mmu_notifier_invalidate_range_start(&range);
|
||||
|
@ -732,7 +732,7 @@ static int madvise_free_single_vma(struct vm_area_struct *vma,
|
|||
&madvise_free_walk_ops, &tlb);
|
||||
tlb_end_vma(&tlb, vma);
|
||||
mmu_notifier_invalidate_range_end(&range);
|
||||
tlb_finish_mmu(&tlb, range.start, range.end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1534,13 +1534,13 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long start,
|
|||
lru_add_drain();
|
||||
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, vma->vm_mm,
|
||||
start, start + size);
|
||||
tlb_gather_mmu(&tlb, vma->vm_mm, start, range.end);
|
||||
tlb_gather_mmu(&tlb, vma->vm_mm);
|
||||
update_hiwater_rss(vma->vm_mm);
|
||||
mmu_notifier_invalidate_range_start(&range);
|
||||
for ( ; vma && vma->vm_start < range.end; vma = vma->vm_next)
|
||||
unmap_single_vma(&tlb, vma, start, range.end, NULL);
|
||||
mmu_notifier_invalidate_range_end(&range);
|
||||
tlb_finish_mmu(&tlb, start, range.end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1561,12 +1561,12 @@ static void zap_page_range_single(struct vm_area_struct *vma, unsigned long addr
|
|||
lru_add_drain();
|
||||
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, vma->vm_mm,
|
||||
address, address + size);
|
||||
tlb_gather_mmu(&tlb, vma->vm_mm, address, range.end);
|
||||
tlb_gather_mmu(&tlb, vma->vm_mm);
|
||||
update_hiwater_rss(vma->vm_mm);
|
||||
mmu_notifier_invalidate_range_start(&range);
|
||||
unmap_single_vma(&tlb, vma, address, range.end, details);
|
||||
mmu_notifier_invalidate_range_end(&range);
|
||||
tlb_finish_mmu(&tlb, address, range.end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2671,12 +2671,12 @@ static void unmap_region(struct mm_struct *mm,
|
|||
struct mmu_gather tlb;
|
||||
|
||||
lru_add_drain();
|
||||
tlb_gather_mmu(&tlb, mm, start, end);
|
||||
tlb_gather_mmu(&tlb, mm);
|
||||
update_hiwater_rss(mm);
|
||||
unmap_vmas(&tlb, vma, start, end);
|
||||
free_pgtables(&tlb, vma, prev ? prev->vm_end : FIRST_USER_ADDRESS,
|
||||
next ? next->vm_start : USER_PGTABLES_CEILING);
|
||||
tlb_finish_mmu(&tlb, start, end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3214,12 +3214,12 @@ void exit_mmap(struct mm_struct *mm)
|
|||
|
||||
lru_add_drain();
|
||||
flush_cache_mm(mm);
|
||||
tlb_gather_mmu(&tlb, mm, 0, -1);
|
||||
tlb_gather_mmu_fullmm(&tlb, mm);
|
||||
/* update_hiwater_rss(mm) here? but nobody should be looking */
|
||||
/* Use -1 here to ensure all VMAs in the mm are unmapped */
|
||||
unmap_vmas(&tlb, vma, 0, -1);
|
||||
free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
|
||||
tlb_finish_mmu(&tlb, 0, -1);
|
||||
tlb_finish_mmu(&tlb);
|
||||
|
||||
/*
|
||||
* Walk the list again, actually closing and freeing it,
|
||||
|
|
|
@ -253,21 +253,17 @@ void tlb_flush_mmu(struct mmu_gather *tlb)
|
|||
* tlb_gather_mmu - initialize an mmu_gather structure for page-table tear-down
|
||||
* @tlb: the mmu_gather structure to initialize
|
||||
* @mm: the mm_struct of the target address space
|
||||
* @start: start of the region that will be removed from the page-table
|
||||
* @end: end of the region that will be removed from the page-table
|
||||
* @fullmm: @mm is without users and we're going to destroy the full address
|
||||
* space (exit/execve)
|
||||
*
|
||||
* Called to initialize an (on-stack) mmu_gather structure for page-table
|
||||
* tear-down from @mm. The @start and @end are set to 0 and -1
|
||||
* respectively when @mm is without users and we're going to destroy
|
||||
* the full address space (exit/execve).
|
||||
* tear-down from @mm.
|
||||
*/
|
||||
void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
static void __tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
|
||||
bool fullmm)
|
||||
{
|
||||
tlb->mm = mm;
|
||||
|
||||
/* Is it from 0 to ~0? */
|
||||
tlb->fullmm = !(start | (end+1));
|
||||
tlb->fullmm = fullmm;
|
||||
|
||||
#ifndef CONFIG_MMU_GATHER_NO_GATHER
|
||||
tlb->need_flush_all = 0;
|
||||
|
@ -287,17 +283,24 @@ void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
|
|||
inc_tlb_flush_pending(tlb->mm);
|
||||
}
|
||||
|
||||
void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm)
|
||||
{
|
||||
__tlb_gather_mmu(tlb, mm, false);
|
||||
}
|
||||
|
||||
void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm)
|
||||
{
|
||||
__tlb_gather_mmu(tlb, mm, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* tlb_finish_mmu - finish an mmu_gather structure
|
||||
* @tlb: the mmu_gather structure to finish
|
||||
* @start: start of the region that will be removed from the page-table
|
||||
* @end: end of the region that will be removed from the page-table
|
||||
*
|
||||
* Called at the end of the shootdown operation to free up any resources that
|
||||
* were required.
|
||||
*/
|
||||
void tlb_finish_mmu(struct mmu_gather *tlb,
|
||||
unsigned long start, unsigned long end)
|
||||
void tlb_finish_mmu(struct mmu_gather *tlb)
|
||||
{
|
||||
/*
|
||||
* If there are parallel threads are doing PTE changes on same range
|
||||
|
|
|
@ -546,15 +546,15 @@ bool __oom_reap_task_mm(struct mm_struct *mm)
|
|||
mmu_notifier_range_init(&range, MMU_NOTIFY_UNMAP, 0,
|
||||
vma, mm, vma->vm_start,
|
||||
vma->vm_end);
|
||||
tlb_gather_mmu(&tlb, mm, range.start, range.end);
|
||||
tlb_gather_mmu(&tlb, mm);
|
||||
if (mmu_notifier_invalidate_range_start_nonblock(&range)) {
|
||||
tlb_finish_mmu(&tlb, range.start, range.end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
ret = false;
|
||||
continue;
|
||||
}
|
||||
unmap_page_range(&tlb, vma, range.start, range.end, NULL);
|
||||
mmu_notifier_invalidate_range_end(&range);
|
||||
tlb_finish_mmu(&tlb, range.start, range.end);
|
||||
tlb_finish_mmu(&tlb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче