Unlike the mm of a task, an mshare host mm is not updated on context switch. In particular this means that mm_cpumask is never updated which results in TLB flushes for updates to mshare PTEs only being done on the local CPU. To ensure entries are flushed for non-local TLBs, set up an mmu notifier on the mshare mm and use the .arch_invalidate_secondary_tlbs callback to flush all TLBs. arch_invalidate_secondary_tlbs guarantees that TLB entries will be flushed before pages are freed when unmapping pages in an mshare region. Signed-off-by: Anthony Yznaga --- mm/mshare.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/mm/mshare.c b/mm/mshare.c index e0dc42602f7f..be7cae739225 100644 --- a/mm/mshare.c +++ b/mm/mshare.c @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include const unsigned long mshare_align = P4D_SIZE; const unsigned long mshare_base = mshare_align; @@ -30,6 +32,7 @@ struct mshare_data { unsigned long start; unsigned long size; unsigned long flags; + struct mmu_notifier mn; }; static inline bool mshare_is_initialized(struct mshare_data *m_data) @@ -37,6 +40,16 @@ static inline bool mshare_is_initialized(struct mshare_data *m_data) return test_bit(MSHARE_INITIALIZED, &m_data->flags); } +static void mshare_invalidate_tlbs(struct mmu_notifier *mn, struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + flush_tlb_all(); +} + +static const struct mmu_notifier_ops mshare_mmu_ops = { + .arch_invalidate_secondary_tlbs = mshare_invalidate_tlbs, +}; + static int mshare_vm_op_split(struct vm_area_struct *vma, unsigned long addr) { return -EINVAL; @@ -238,6 +251,10 @@ msharefs_fill_mm(struct inode *inode) goto err_free; m_data->mm = mm; m_data->start = mshare_base; + m_data->mn.ops = &mshare_mmu_ops; + ret = mmu_notifier_register(&m_data->mn, mm); + if (ret) + goto err_free; refcount_set(&m_data->ref, 1); inode->i_private = m_data; -- 2.47.1