The track_pte_set() is used to track the setting of the PTE page table
entry, and the percpu_ref of the PTE page table page will be incremented
when the entry changes from pte_none() to !pte_none().
The track_pte_clear() is used to track the clearing of the PTE page
table entry, and the percpu_ref of the PTE page table page will be
decremented when the entry changes from !pte_none() to pte_none().
In this way, the usage of the PTE page table page can be tracked by
its percpu_ref.
Signed-off-by: Qi Zheng <[email protected]>
---
include/linux/pte_ref.h | 14 ++++++++++++++
mm/pte_ref.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/include/linux/pte_ref.h b/include/linux/pte_ref.h
index 379c3b45a6ab..6ab740e1b989 100644
--- a/include/linux/pte_ref.h
+++ b/include/linux/pte_ref.h
@@ -18,6 +18,10 @@ void __pte_put(pgtable_t page);
void pte_put(pte_t *ptep);
void try_to_free_user_pte(struct mm_struct *mm, pmd_t *pmd, unsigned long addr,
bool switch_back);
+void track_pte_set(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
+ pte_t pte);
+void track_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
+ pte_t pte);
#else /* !CONFIG_FREE_USER_PTE */
@@ -54,6 +58,16 @@ static inline void try_to_free_user_pte(struct mm_struct *mm, pmd_t *pmd,
{
}
+static inline void track_pte_set(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pte)
+{
+}
+
+static inline void track_pte_clear(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pte)
+{
+}
+
#endif /* CONFIG_FREE_USER_PTE */
#endif /* _LINUX_PTE_REF_H */
diff --git a/mm/pte_ref.c b/mm/pte_ref.c
index bf9629272c71..e92510deda0b 100644
--- a/mm/pte_ref.c
+++ b/mm/pte_ref.c
@@ -197,4 +197,34 @@ void try_to_free_user_pte(struct mm_struct *mm, pmd_t *pmd, unsigned long addr,
}
}
+void track_pte_set(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
+ pte_t pte)
+{
+ pgtable_t page;
+
+ if (&init_mm == mm || pte_huge(pte))
+ return;
+
+ page = pte_to_page(ptep);
+ BUG_ON(percpu_ref_is_zero(page->pte_ref));
+ if (pte_none(*ptep) && !pte_none(pte))
+ percpu_ref_get(page->pte_ref);
+}
+EXPORT_SYMBOL(track_pte_set);
+
+void track_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
+ pte_t pte)
+{
+ pgtable_t page;
+
+ if (&init_mm == mm || pte_huge(pte))
+ return;
+
+ page = pte_to_page(ptep);
+ BUG_ON(percpu_ref_is_zero(page->pte_ref));
+ if (!pte_none(pte))
+ percpu_ref_put(page->pte_ref);
+}
+EXPORT_SYMBOL(track_pte_clear);
+
#endif /* CONFIG_FREE_USER_PTE */
--
2.20.1