18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/pagewalk.h> 38c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 48c2ecf20Sopenharmony_ci#include <linux/bitops.h> 58c2ecf20Sopenharmony_ci#include <linux/mmu_notifier.h> 68c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 78c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/** 108c2ecf20Sopenharmony_ci * struct wp_walk - Private struct for pagetable walk callbacks 118c2ecf20Sopenharmony_ci * @range: Range for mmu notifiers 128c2ecf20Sopenharmony_ci * @tlbflush_start: Address of first modified pte 138c2ecf20Sopenharmony_ci * @tlbflush_end: Address of last modified pte + 1 148c2ecf20Sopenharmony_ci * @total: Total number of modified ptes 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_cistruct wp_walk { 178c2ecf20Sopenharmony_ci struct mmu_notifier_range range; 188c2ecf20Sopenharmony_ci unsigned long tlbflush_start; 198c2ecf20Sopenharmony_ci unsigned long tlbflush_end; 208c2ecf20Sopenharmony_ci unsigned long total; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * wp_pte - Write-protect a pte 258c2ecf20Sopenharmony_ci * @pte: Pointer to the pte 268c2ecf20Sopenharmony_ci * @addr: The virtual page address 278c2ecf20Sopenharmony_ci * @walk: pagetable walk callback argument 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * The function write-protects a pte and records the range in 308c2ecf20Sopenharmony_ci * virtual address space of touched ptes for efficient range TLB flushes. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistatic int wp_pte(pte_t *pte, unsigned long addr, unsigned long end, 338c2ecf20Sopenharmony_ci struct mm_walk *walk) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct wp_walk *wpwalk = walk->private; 368c2ecf20Sopenharmony_ci pte_t ptent = *pte; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (pte_write(ptent)) { 398c2ecf20Sopenharmony_ci pte_t old_pte = ptep_modify_prot_start(walk->vma, addr, pte); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ptent = pte_wrprotect(old_pte); 428c2ecf20Sopenharmony_ci ptep_modify_prot_commit(walk->vma, addr, pte, old_pte, ptent); 438c2ecf20Sopenharmony_ci wpwalk->total++; 448c2ecf20Sopenharmony_ci wpwalk->tlbflush_start = min(wpwalk->tlbflush_start, addr); 458c2ecf20Sopenharmony_ci wpwalk->tlbflush_end = max(wpwalk->tlbflush_end, 468c2ecf20Sopenharmony_ci addr + PAGE_SIZE); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return 0; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * struct clean_walk - Private struct for the clean_record_pte function. 548c2ecf20Sopenharmony_ci * @base: struct wp_walk we derive from 558c2ecf20Sopenharmony_ci * @bitmap_pgoff: Address_space Page offset of the first bit in @bitmap 568c2ecf20Sopenharmony_ci * @bitmap: Bitmap with one bit for each page offset in the address_space range 578c2ecf20Sopenharmony_ci * covered. 588c2ecf20Sopenharmony_ci * @start: Address_space page offset of first modified pte relative 598c2ecf20Sopenharmony_ci * to @bitmap_pgoff 608c2ecf20Sopenharmony_ci * @end: Address_space page offset of last modified pte relative 618c2ecf20Sopenharmony_ci * to @bitmap_pgoff 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistruct clean_walk { 648c2ecf20Sopenharmony_ci struct wp_walk base; 658c2ecf20Sopenharmony_ci pgoff_t bitmap_pgoff; 668c2ecf20Sopenharmony_ci unsigned long *bitmap; 678c2ecf20Sopenharmony_ci pgoff_t start; 688c2ecf20Sopenharmony_ci pgoff_t end; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define to_clean_walk(_wpwalk) container_of(_wpwalk, struct clean_walk, base) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/** 748c2ecf20Sopenharmony_ci * clean_record_pte - Clean a pte and record its address space offset in a 758c2ecf20Sopenharmony_ci * bitmap 768c2ecf20Sopenharmony_ci * @pte: Pointer to the pte 778c2ecf20Sopenharmony_ci * @addr: The virtual page address 788c2ecf20Sopenharmony_ci * @walk: pagetable walk callback argument 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * The function cleans a pte and records the range in 818c2ecf20Sopenharmony_ci * virtual address space of touched ptes for efficient TLB flushes. 828c2ecf20Sopenharmony_ci * It also records dirty ptes in a bitmap representing page offsets 838c2ecf20Sopenharmony_ci * in the address_space, as well as the first and last of the bits 848c2ecf20Sopenharmony_ci * touched. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic int clean_record_pte(pte_t *pte, unsigned long addr, 878c2ecf20Sopenharmony_ci unsigned long end, struct mm_walk *walk) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct wp_walk *wpwalk = walk->private; 908c2ecf20Sopenharmony_ci struct clean_walk *cwalk = to_clean_walk(wpwalk); 918c2ecf20Sopenharmony_ci pte_t ptent = *pte; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (pte_dirty(ptent)) { 948c2ecf20Sopenharmony_ci pgoff_t pgoff = ((addr - walk->vma->vm_start) >> PAGE_SHIFT) + 958c2ecf20Sopenharmony_ci walk->vma->vm_pgoff - cwalk->bitmap_pgoff; 968c2ecf20Sopenharmony_ci pte_t old_pte = ptep_modify_prot_start(walk->vma, addr, pte); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ptent = pte_mkclean(old_pte); 998c2ecf20Sopenharmony_ci ptep_modify_prot_commit(walk->vma, addr, pte, old_pte, ptent); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci wpwalk->total++; 1028c2ecf20Sopenharmony_ci wpwalk->tlbflush_start = min(wpwalk->tlbflush_start, addr); 1038c2ecf20Sopenharmony_ci wpwalk->tlbflush_end = max(wpwalk->tlbflush_end, 1048c2ecf20Sopenharmony_ci addr + PAGE_SIZE); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci __set_bit(pgoff, cwalk->bitmap); 1078c2ecf20Sopenharmony_ci cwalk->start = min(cwalk->start, pgoff); 1088c2ecf20Sopenharmony_ci cwalk->end = max(cwalk->end, pgoff + 1); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * wp_clean_pmd_entry - The pagewalk pmd callback. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * Dirty-tracking should take place on the PTE level, so 1188c2ecf20Sopenharmony_ci * WARN() if encountering a dirty huge pmd. 1198c2ecf20Sopenharmony_ci * Furthermore, never split huge pmds, since that currently 1208c2ecf20Sopenharmony_ci * causes dirty info loss. The pagefault handler should do 1218c2ecf20Sopenharmony_ci * that if needed. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_cistatic int wp_clean_pmd_entry(pmd_t *pmd, unsigned long addr, unsigned long end, 1248c2ecf20Sopenharmony_ci struct mm_walk *walk) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci pmd_t pmdval = pmd_read_atomic(pmd); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (!pmd_trans_unstable(&pmdval)) 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (pmd_none(pmdval)) { 1328c2ecf20Sopenharmony_ci walk->action = ACTION_AGAIN; 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Huge pmd, present or migrated */ 1378c2ecf20Sopenharmony_ci walk->action = ACTION_CONTINUE; 1388c2ecf20Sopenharmony_ci if (pmd_trans_huge(pmdval) || pmd_devmap(pmdval)) 1398c2ecf20Sopenharmony_ci WARN_ON(pmd_write(pmdval) || pmd_dirty(pmdval)); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * wp_clean_pud_entry - The pagewalk pud callback. 1468c2ecf20Sopenharmony_ci * 1478c2ecf20Sopenharmony_ci * Dirty-tracking should take place on the PTE level, so 1488c2ecf20Sopenharmony_ci * WARN() if encountering a dirty huge puds. 1498c2ecf20Sopenharmony_ci * Furthermore, never split huge puds, since that currently 1508c2ecf20Sopenharmony_ci * causes dirty info loss. The pagefault handler should do 1518c2ecf20Sopenharmony_ci * that if needed. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic int wp_clean_pud_entry(pud_t *pud, unsigned long addr, unsigned long end, 1548c2ecf20Sopenharmony_ci struct mm_walk *walk) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci pud_t pudval = READ_ONCE(*pud); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (!pud_trans_unstable(&pudval)) 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (pud_none(pudval)) { 1628c2ecf20Sopenharmony_ci walk->action = ACTION_AGAIN; 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Huge pud */ 1678c2ecf20Sopenharmony_ci walk->action = ACTION_CONTINUE; 1688c2ecf20Sopenharmony_ci if (pud_trans_huge(pudval) || pud_devmap(pudval)) 1698c2ecf20Sopenharmony_ci WARN_ON(pud_write(pudval) || pud_dirty(pudval)); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * wp_clean_pre_vma - The pagewalk pre_vma callback. 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * The pre_vma callback performs the cache flush, stages the tlb flush 1788c2ecf20Sopenharmony_ci * and calls the necessary mmu notifiers. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistatic int wp_clean_pre_vma(unsigned long start, unsigned long end, 1818c2ecf20Sopenharmony_ci struct mm_walk *walk) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct wp_walk *wpwalk = walk->private; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci wpwalk->tlbflush_start = end; 1868c2ecf20Sopenharmony_ci wpwalk->tlbflush_end = start; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mmu_notifier_range_init(&wpwalk->range, MMU_NOTIFY_PROTECTION_PAGE, 0, 1898c2ecf20Sopenharmony_ci walk->vma, walk->mm, start, end); 1908c2ecf20Sopenharmony_ci mmu_notifier_invalidate_range_start(&wpwalk->range); 1918c2ecf20Sopenharmony_ci flush_cache_range(walk->vma, start, end); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * We're not using tlb_gather_mmu() since typically 1958c2ecf20Sopenharmony_ci * only a small subrange of PTEs are affected, whereas 1968c2ecf20Sopenharmony_ci * tlb_gather_mmu() records the full range. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci inc_tlb_flush_pending(walk->mm); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * wp_clean_post_vma - The pagewalk post_vma callback. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * The post_vma callback performs the tlb flush and calls necessary mmu 2078c2ecf20Sopenharmony_ci * notifiers. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic void wp_clean_post_vma(struct mm_walk *walk) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct wp_walk *wpwalk = walk->private; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (mm_tlb_flush_nested(walk->mm)) 2148c2ecf20Sopenharmony_ci flush_tlb_range(walk->vma, wpwalk->range.start, 2158c2ecf20Sopenharmony_ci wpwalk->range.end); 2168c2ecf20Sopenharmony_ci else if (wpwalk->tlbflush_end > wpwalk->tlbflush_start) 2178c2ecf20Sopenharmony_ci flush_tlb_range(walk->vma, wpwalk->tlbflush_start, 2188c2ecf20Sopenharmony_ci wpwalk->tlbflush_end); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mmu_notifier_invalidate_range_end(&wpwalk->range); 2218c2ecf20Sopenharmony_ci dec_tlb_flush_pending(walk->mm); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* 2258c2ecf20Sopenharmony_ci * wp_clean_test_walk - The pagewalk test_walk callback. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Won't perform dirty-tracking on COW, read-only or HUGETLB vmas. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic int wp_clean_test_walk(unsigned long start, unsigned long end, 2308c2ecf20Sopenharmony_ci struct mm_walk *walk) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci unsigned long vm_flags = READ_ONCE(walk->vma->vm_flags); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Skip non-applicable VMAs */ 2358c2ecf20Sopenharmony_ci if ((vm_flags & (VM_SHARED | VM_MAYWRITE | VM_HUGETLB)) != 2368c2ecf20Sopenharmony_ci (VM_SHARED | VM_MAYWRITE)) 2378c2ecf20Sopenharmony_ci return 1; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct mm_walk_ops clean_walk_ops = { 2438c2ecf20Sopenharmony_ci .pte_entry = clean_record_pte, 2448c2ecf20Sopenharmony_ci .pmd_entry = wp_clean_pmd_entry, 2458c2ecf20Sopenharmony_ci .pud_entry = wp_clean_pud_entry, 2468c2ecf20Sopenharmony_ci .test_walk = wp_clean_test_walk, 2478c2ecf20Sopenharmony_ci .pre_vma = wp_clean_pre_vma, 2488c2ecf20Sopenharmony_ci .post_vma = wp_clean_post_vma 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic const struct mm_walk_ops wp_walk_ops = { 2528c2ecf20Sopenharmony_ci .pte_entry = wp_pte, 2538c2ecf20Sopenharmony_ci .pmd_entry = wp_clean_pmd_entry, 2548c2ecf20Sopenharmony_ci .pud_entry = wp_clean_pud_entry, 2558c2ecf20Sopenharmony_ci .test_walk = wp_clean_test_walk, 2568c2ecf20Sopenharmony_ci .pre_vma = wp_clean_pre_vma, 2578c2ecf20Sopenharmony_ci .post_vma = wp_clean_post_vma 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * wp_shared_mapping_range - Write-protect all ptes in an address space range 2628c2ecf20Sopenharmony_ci * @mapping: The address_space we want to write protect 2638c2ecf20Sopenharmony_ci * @first_index: The first page offset in the range 2648c2ecf20Sopenharmony_ci * @nr: Number of incremental page offsets to cover 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * Note: This function currently skips transhuge page-table entries, since 2678c2ecf20Sopenharmony_ci * it's intended for dirty-tracking on the PTE level. It will warn on 2688c2ecf20Sopenharmony_ci * encountering transhuge write-enabled entries, though, and can easily be 2698c2ecf20Sopenharmony_ci * extended to handle them as well. 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * Return: The number of ptes actually write-protected. Note that 2728c2ecf20Sopenharmony_ci * already write-protected ptes are not counted. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ciunsigned long wp_shared_mapping_range(struct address_space *mapping, 2758c2ecf20Sopenharmony_ci pgoff_t first_index, pgoff_t nr) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct wp_walk wpwalk = { .total = 0 }; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci i_mmap_lock_read(mapping); 2808c2ecf20Sopenharmony_ci WARN_ON(walk_page_mapping(mapping, first_index, nr, &wp_walk_ops, 2818c2ecf20Sopenharmony_ci &wpwalk)); 2828c2ecf20Sopenharmony_ci i_mmap_unlock_read(mapping); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return wpwalk.total; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wp_shared_mapping_range); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * clean_record_shared_mapping_range - Clean and record all ptes in an 2908c2ecf20Sopenharmony_ci * address space range 2918c2ecf20Sopenharmony_ci * @mapping: The address_space we want to clean 2928c2ecf20Sopenharmony_ci * @first_index: The first page offset in the range 2938c2ecf20Sopenharmony_ci * @nr: Number of incremental page offsets to cover 2948c2ecf20Sopenharmony_ci * @bitmap_pgoff: The page offset of the first bit in @bitmap 2958c2ecf20Sopenharmony_ci * @bitmap: Pointer to a bitmap of at least @nr bits. The bitmap needs to 2968c2ecf20Sopenharmony_ci * cover the whole range @first_index..@first_index + @nr. 2978c2ecf20Sopenharmony_ci * @start: Pointer to number of the first set bit in @bitmap. 2988c2ecf20Sopenharmony_ci * is modified as new bits are set by the function. 2998c2ecf20Sopenharmony_ci * @end: Pointer to the number of the last set bit in @bitmap. 3008c2ecf20Sopenharmony_ci * none set. The value is modified as new bits are set by the function. 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * Note: When this function returns there is no guarantee that a CPU has 3038c2ecf20Sopenharmony_ci * not already dirtied new ptes. However it will not clean any ptes not 3048c2ecf20Sopenharmony_ci * reported in the bitmap. The guarantees are as follows: 3058c2ecf20Sopenharmony_ci * a) All ptes dirty when the function starts executing will end up recorded 3068c2ecf20Sopenharmony_ci * in the bitmap. 3078c2ecf20Sopenharmony_ci * b) All ptes dirtied after that will either remain dirty, be recorded in the 3088c2ecf20Sopenharmony_ci * bitmap or both. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * If a caller needs to make sure all dirty ptes are picked up and none 3118c2ecf20Sopenharmony_ci * additional are added, it first needs to write-protect the address-space 3128c2ecf20Sopenharmony_ci * range and make sure new writers are blocked in page_mkwrite() or 3138c2ecf20Sopenharmony_ci * pfn_mkwrite(). And then after a TLB flush following the write-protection 3148c2ecf20Sopenharmony_ci * pick up all dirty bits. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * Note: This function currently skips transhuge page-table entries, since 3178c2ecf20Sopenharmony_ci * it's intended for dirty-tracking on the PTE level. It will warn on 3188c2ecf20Sopenharmony_ci * encountering transhuge dirty entries, though, and can easily be extended 3198c2ecf20Sopenharmony_ci * to handle them as well. 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * Return: The number of dirty ptes actually cleaned. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ciunsigned long clean_record_shared_mapping_range(struct address_space *mapping, 3248c2ecf20Sopenharmony_ci pgoff_t first_index, pgoff_t nr, 3258c2ecf20Sopenharmony_ci pgoff_t bitmap_pgoff, 3268c2ecf20Sopenharmony_ci unsigned long *bitmap, 3278c2ecf20Sopenharmony_ci pgoff_t *start, 3288c2ecf20Sopenharmony_ci pgoff_t *end) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci bool none_set = (*start >= *end); 3318c2ecf20Sopenharmony_ci struct clean_walk cwalk = { 3328c2ecf20Sopenharmony_ci .base = { .total = 0 }, 3338c2ecf20Sopenharmony_ci .bitmap_pgoff = bitmap_pgoff, 3348c2ecf20Sopenharmony_ci .bitmap = bitmap, 3358c2ecf20Sopenharmony_ci .start = none_set ? nr : *start, 3368c2ecf20Sopenharmony_ci .end = none_set ? 0 : *end, 3378c2ecf20Sopenharmony_ci }; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci i_mmap_lock_read(mapping); 3408c2ecf20Sopenharmony_ci WARN_ON(walk_page_mapping(mapping, first_index, nr, &clean_walk_ops, 3418c2ecf20Sopenharmony_ci &cwalk.base)); 3428c2ecf20Sopenharmony_ci i_mmap_unlock_read(mapping); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci *start = cwalk.start; 3458c2ecf20Sopenharmony_ci *end = cwalk.end; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return cwalk.base.total; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clean_record_shared_mapping_range); 350