| // SPDX-License-Identifier: GPL-2.0 | 
 | #include <linux/hugetlb.h> | 
 | #include <asm-generic/tlb.h> | 
 | #include <asm/pgalloc.h> | 
 |  | 
 | #include "internal.h" | 
 |  | 
 | bool reclaim_pt_is_enabled(unsigned long start, unsigned long end, | 
 | 			   struct zap_details *details) | 
 | { | 
 | 	return details && details->reclaim_pt && (end - start >= PMD_SIZE); | 
 | } | 
 |  | 
 | bool try_get_and_clear_pmd(struct mm_struct *mm, pmd_t *pmd, pmd_t *pmdval) | 
 | { | 
 | 	spinlock_t *pml = pmd_lockptr(mm, pmd); | 
 |  | 
 | 	if (!spin_trylock(pml)) | 
 | 		return false; | 
 |  | 
 | 	*pmdval = pmdp_get_lockless(pmd); | 
 | 	pmd_clear(pmd); | 
 | 	spin_unlock(pml); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | void free_pte(struct mm_struct *mm, unsigned long addr, struct mmu_gather *tlb, | 
 | 	      pmd_t pmdval) | 
 | { | 
 | 	pte_free_tlb(tlb, pmd_pgtable(pmdval), addr); | 
 | 	mm_dec_nr_ptes(mm); | 
 | } | 
 |  | 
 | void try_to_free_pte(struct mm_struct *mm, pmd_t *pmd, unsigned long addr, | 
 | 		     struct mmu_gather *tlb) | 
 | { | 
 | 	pmd_t pmdval; | 
 | 	spinlock_t *pml, *ptl = NULL; | 
 | 	pte_t *start_pte, *pte; | 
 | 	int i; | 
 |  | 
 | 	pml = pmd_lock(mm, pmd); | 
 | 	start_pte = pte_offset_map_rw_nolock(mm, pmd, addr, &pmdval, &ptl); | 
 | 	if (!start_pte) | 
 | 		goto out_ptl; | 
 | 	if (ptl != pml) | 
 | 		spin_lock_nested(ptl, SINGLE_DEPTH_NESTING); | 
 |  | 
 | 	/* Check if it is empty PTE page */ | 
 | 	for (i = 0, pte = start_pte; i < PTRS_PER_PTE; i++, pte++) { | 
 | 		if (!pte_none(ptep_get(pte))) | 
 | 			goto out_ptl; | 
 | 	} | 
 | 	pte_unmap(start_pte); | 
 |  | 
 | 	pmd_clear(pmd); | 
 |  | 
 | 	if (ptl != pml) | 
 | 		spin_unlock(ptl); | 
 | 	spin_unlock(pml); | 
 |  | 
 | 	free_pte(mm, addr, tlb, pmdval); | 
 |  | 
 | 	return; | 
 | out_ptl: | 
 | 	if (start_pte) | 
 | 		pte_unmap_unlock(start_pte, ptl); | 
 | 	if (ptl != pml) | 
 | 		spin_unlock(pml); | 
 | } |