0001
0002 #include <linux/pagewalk.h>
0003 #include <linux/hugetlb.h>
0004 #include <linux/bitops.h>
0005 #include <linux/mmu_notifier.h>
0006 #include <linux/mm_inline.h>
0007 #include <asm/cacheflush.h>
0008 #include <asm/tlbflush.h>
0009
0010
0011
0012
0013
0014
0015
0016
0017 struct wp_walk {
0018 struct mmu_notifier_range range;
0019 unsigned long tlbflush_start;
0020 unsigned long tlbflush_end;
0021 unsigned long total;
0022 };
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 static int wp_pte(pte_t *pte, unsigned long addr, unsigned long end,
0035 struct mm_walk *walk)
0036 {
0037 struct wp_walk *wpwalk = walk->private;
0038 pte_t ptent = *pte;
0039
0040 if (pte_write(ptent)) {
0041 pte_t old_pte = ptep_modify_prot_start(walk->vma, addr, pte);
0042
0043 ptent = pte_wrprotect(old_pte);
0044 ptep_modify_prot_commit(walk->vma, addr, pte, old_pte, ptent);
0045 wpwalk->total++;
0046 wpwalk->tlbflush_start = min(wpwalk->tlbflush_start, addr);
0047 wpwalk->tlbflush_end = max(wpwalk->tlbflush_end,
0048 addr + PAGE_SIZE);
0049 }
0050
0051 return 0;
0052 }
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065 struct clean_walk {
0066 struct wp_walk base;
0067 pgoff_t bitmap_pgoff;
0068 unsigned long *bitmap;
0069 pgoff_t start;
0070 pgoff_t end;
0071 };
0072
0073 #define to_clean_walk(_wpwalk) container_of(_wpwalk, struct clean_walk, base)
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089 static int clean_record_pte(pte_t *pte, unsigned long addr,
0090 unsigned long end, struct mm_walk *walk)
0091 {
0092 struct wp_walk *wpwalk = walk->private;
0093 struct clean_walk *cwalk = to_clean_walk(wpwalk);
0094 pte_t ptent = *pte;
0095
0096 if (pte_dirty(ptent)) {
0097 pgoff_t pgoff = ((addr - walk->vma->vm_start) >> PAGE_SHIFT) +
0098 walk->vma->vm_pgoff - cwalk->bitmap_pgoff;
0099 pte_t old_pte = ptep_modify_prot_start(walk->vma, addr, pte);
0100
0101 ptent = pte_mkclean(old_pte);
0102 ptep_modify_prot_commit(walk->vma, addr, pte, old_pte, ptent);
0103
0104 wpwalk->total++;
0105 wpwalk->tlbflush_start = min(wpwalk->tlbflush_start, addr);
0106 wpwalk->tlbflush_end = max(wpwalk->tlbflush_end,
0107 addr + PAGE_SIZE);
0108
0109 __set_bit(pgoff, cwalk->bitmap);
0110 cwalk->start = min(cwalk->start, pgoff);
0111 cwalk->end = max(cwalk->end, pgoff + 1);
0112 }
0113
0114 return 0;
0115 }
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126 static int wp_clean_pmd_entry(pmd_t *pmd, unsigned long addr, unsigned long end,
0127 struct mm_walk *walk)
0128 {
0129 pmd_t pmdval = pmd_read_atomic(pmd);
0130
0131 if (!pmd_trans_unstable(&pmdval))
0132 return 0;
0133
0134 if (pmd_none(pmdval)) {
0135 walk->action = ACTION_AGAIN;
0136 return 0;
0137 }
0138
0139
0140 walk->action = ACTION_CONTINUE;
0141 if (pmd_trans_huge(pmdval) || pmd_devmap(pmdval))
0142 WARN_ON(pmd_write(pmdval) || pmd_dirty(pmdval));
0143
0144 return 0;
0145 }
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156 static int wp_clean_pud_entry(pud_t *pud, unsigned long addr, unsigned long end,
0157 struct mm_walk *walk)
0158 {
0159 pud_t pudval = READ_ONCE(*pud);
0160
0161 if (!pud_trans_unstable(&pudval))
0162 return 0;
0163
0164 if (pud_none(pudval)) {
0165 walk->action = ACTION_AGAIN;
0166 return 0;
0167 }
0168
0169 #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
0170
0171 walk->action = ACTION_CONTINUE;
0172 if (pud_trans_huge(pudval) || pud_devmap(pudval))
0173 WARN_ON(pud_write(pudval) || pud_dirty(pudval));
0174 #endif
0175
0176 return 0;
0177 }
0178
0179
0180
0181
0182
0183
0184
0185 static int wp_clean_pre_vma(unsigned long start, unsigned long end,
0186 struct mm_walk *walk)
0187 {
0188 struct wp_walk *wpwalk = walk->private;
0189
0190 wpwalk->tlbflush_start = end;
0191 wpwalk->tlbflush_end = start;
0192
0193 mmu_notifier_range_init(&wpwalk->range, MMU_NOTIFY_PROTECTION_PAGE, 0,
0194 walk->vma, walk->mm, start, end);
0195 mmu_notifier_invalidate_range_start(&wpwalk->range);
0196 flush_cache_range(walk->vma, start, end);
0197
0198
0199
0200
0201
0202
0203 inc_tlb_flush_pending(walk->mm);
0204
0205 return 0;
0206 }
0207
0208
0209
0210
0211
0212
0213
0214 static void wp_clean_post_vma(struct mm_walk *walk)
0215 {
0216 struct wp_walk *wpwalk = walk->private;
0217
0218 if (mm_tlb_flush_nested(walk->mm))
0219 flush_tlb_range(walk->vma, wpwalk->range.start,
0220 wpwalk->range.end);
0221 else if (wpwalk->tlbflush_end > wpwalk->tlbflush_start)
0222 flush_tlb_range(walk->vma, wpwalk->tlbflush_start,
0223 wpwalk->tlbflush_end);
0224
0225 mmu_notifier_invalidate_range_end(&wpwalk->range);
0226 dec_tlb_flush_pending(walk->mm);
0227 }
0228
0229
0230
0231
0232
0233
0234 static int wp_clean_test_walk(unsigned long start, unsigned long end,
0235 struct mm_walk *walk)
0236 {
0237 unsigned long vm_flags = READ_ONCE(walk->vma->vm_flags);
0238
0239
0240 if ((vm_flags & (VM_SHARED | VM_MAYWRITE | VM_HUGETLB)) !=
0241 (VM_SHARED | VM_MAYWRITE))
0242 return 1;
0243
0244 return 0;
0245 }
0246
0247 static const struct mm_walk_ops clean_walk_ops = {
0248 .pte_entry = clean_record_pte,
0249 .pmd_entry = wp_clean_pmd_entry,
0250 .pud_entry = wp_clean_pud_entry,
0251 .test_walk = wp_clean_test_walk,
0252 .pre_vma = wp_clean_pre_vma,
0253 .post_vma = wp_clean_post_vma
0254 };
0255
0256 static const struct mm_walk_ops wp_walk_ops = {
0257 .pte_entry = wp_pte,
0258 .pmd_entry = wp_clean_pmd_entry,
0259 .pud_entry = wp_clean_pud_entry,
0260 .test_walk = wp_clean_test_walk,
0261 .pre_vma = wp_clean_pre_vma,
0262 .post_vma = wp_clean_post_vma
0263 };
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278
0279 unsigned long wp_shared_mapping_range(struct address_space *mapping,
0280 pgoff_t first_index, pgoff_t nr)
0281 {
0282 struct wp_walk wpwalk = { .total = 0 };
0283
0284 i_mmap_lock_read(mapping);
0285 WARN_ON(walk_page_mapping(mapping, first_index, nr, &wp_walk_ops,
0286 &wpwalk));
0287 i_mmap_unlock_read(mapping);
0288
0289 return wpwalk.total;
0290 }
0291 EXPORT_SYMBOL_GPL(wp_shared_mapping_range);
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328 unsigned long clean_record_shared_mapping_range(struct address_space *mapping,
0329 pgoff_t first_index, pgoff_t nr,
0330 pgoff_t bitmap_pgoff,
0331 unsigned long *bitmap,
0332 pgoff_t *start,
0333 pgoff_t *end)
0334 {
0335 bool none_set = (*start >= *end);
0336 struct clean_walk cwalk = {
0337 .base = { .total = 0 },
0338 .bitmap_pgoff = bitmap_pgoff,
0339 .bitmap = bitmap,
0340 .start = none_set ? nr : *start,
0341 .end = none_set ? 0 : *end,
0342 };
0343
0344 i_mmap_lock_read(mapping);
0345 WARN_ON(walk_page_mapping(mapping, first_index, nr, &clean_walk_ops,
0346 &cwalk.base));
0347 i_mmap_unlock_read(mapping);
0348
0349 *start = cwalk.start;
0350 *end = cwalk.end;
0351
0352 return cwalk.base.total;
0353 }
0354 EXPORT_SYMBOL_GPL(clean_record_shared_mapping_range);