@@ -521,6 +521,9 @@ void gmap_unlink(struct mm_struct *mm, unsigned long *table,
521521 rcu_read_unlock ();
522522}
523523
524+ static void gmap_pmdp_xchg (struct gmap * gmap , pmd_t * old , pmd_t new ,
525+ unsigned long gaddr );
526+
524527/**
525528 * gmap_link - set up shadow page tables to connect a host to a guest address
526529 * @gmap: pointer to guest mapping meta data structure
@@ -541,6 +544,7 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr)
541544 p4d_t * p4d ;
542545 pud_t * pud ;
543546 pmd_t * pmd ;
547+ u64 unprot ;
544548 int rc ;
545549
546550 BUG_ON (gmap_is_shadow (gmap ));
@@ -598,12 +602,19 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr)
598602 vmaddr >> PMD_SHIFT , table );
599603 if (!rc ) {
600604 if (pmd_large (* pmd )) {
601- * table = pmd_val (* pmd ) &
602- _SEGMENT_ENTRY_HARDWARE_BITS_LARGE ;
605+ * table = (pmd_val (* pmd ) &
606+ _SEGMENT_ENTRY_HARDWARE_BITS_LARGE )
607+ | _SEGMENT_ENTRY_GMAP_UC ;
603608 } else
604609 * table = pmd_val (* pmd ) &
605610 _SEGMENT_ENTRY_HARDWARE_BITS ;
606611 }
612+ } else if (* table & _SEGMENT_ENTRY_PROTECT &&
613+ !(pmd_val (* pmd ) & _SEGMENT_ENTRY_PROTECT )) {
614+ unprot = (u64 )* table ;
615+ unprot &= ~_SEGMENT_ENTRY_PROTECT ;
616+ unprot |= _SEGMENT_ENTRY_GMAP_UC ;
617+ gmap_pmdp_xchg (gmap , (pmd_t * )table , __pmd (unprot ), gaddr );
607618 }
608619 spin_unlock (& gmap -> guest_table_lock );
609620 spin_unlock (ptl );
@@ -930,11 +941,23 @@ static int gmap_protect_pmd(struct gmap *gmap, unsigned long gaddr,
930941{
931942 int pmd_i = pmd_val (* pmdp ) & _SEGMENT_ENTRY_INVALID ;
932943 int pmd_p = pmd_val (* pmdp ) & _SEGMENT_ENTRY_PROTECT ;
944+ pmd_t new = * pmdp ;
933945
934946 /* Fixup needed */
935947 if ((pmd_i && (prot != PROT_NONE )) || (pmd_p && (prot == PROT_WRITE )))
936948 return - EAGAIN ;
937949
950+ if (prot == PROT_NONE && !pmd_i ) {
951+ pmd_val (new ) |= _SEGMENT_ENTRY_INVALID ;
952+ gmap_pmdp_xchg (gmap , pmdp , new , gaddr );
953+ }
954+
955+ if (prot == PROT_READ && !pmd_p ) {
956+ pmd_val (new ) &= ~_SEGMENT_ENTRY_INVALID ;
957+ pmd_val (new ) |= _SEGMENT_ENTRY_PROTECT ;
958+ gmap_pmdp_xchg (gmap , pmdp , new , gaddr );
959+ }
960+
938961 if (bits & GMAP_NOTIFY_MPROT )
939962 pmd_val (* pmdp ) |= _SEGMENT_ENTRY_GMAP_IN ;
940963
@@ -2228,6 +2251,32 @@ static void pmdp_notify_gmap(struct gmap *gmap, pmd_t *pmdp,
22282251 gmap_call_notifier (gmap , gaddr , gaddr + HPAGE_SIZE - 1 );
22292252}
22302253
2254+ /**
2255+ * gmap_pmdp_xchg - exchange a gmap pmd with another
2256+ * @gmap: pointer to the guest address space structure
2257+ * @pmdp: pointer to the pmd entry
2258+ * @new: replacement entry
2259+ * @gaddr: the affected guest address
2260+ *
2261+ * This function is assumed to be called with the guest_table_lock
2262+ * held.
2263+ */
2264+ static void gmap_pmdp_xchg (struct gmap * gmap , pmd_t * pmdp , pmd_t new ,
2265+ unsigned long gaddr )
2266+ {
2267+ gaddr &= HPAGE_MASK ;
2268+ pmdp_notify_gmap (gmap , pmdp , gaddr );
2269+ pmd_val (new ) &= ~_SEGMENT_ENTRY_GMAP_IN ;
2270+ if (MACHINE_HAS_TLB_GUEST )
2271+ __pmdp_idte (gaddr , (pmd_t * )pmdp , IDTE_GUEST_ASCE , gmap -> asce ,
2272+ IDTE_GLOBAL );
2273+ else if (MACHINE_HAS_IDTE )
2274+ __pmdp_idte (gaddr , (pmd_t * )pmdp , 0 , 0 , IDTE_GLOBAL );
2275+ else
2276+ __pmdp_csp (pmdp );
2277+ * pmdp = new ;
2278+ }
2279+
22312280static void gmap_pmdp_clear (struct mm_struct * mm , unsigned long vmaddr ,
22322281 int purge )
22332282{
@@ -2243,7 +2292,8 @@ static void gmap_pmdp_clear(struct mm_struct *mm, unsigned long vmaddr,
22432292 if (pmdp ) {
22442293 gaddr = __gmap_segment_gaddr ((unsigned long * )pmdp );
22452294 pmdp_notify_gmap (gmap , pmdp , gaddr );
2246- WARN_ON (pmd_val (* pmdp ) & ~_SEGMENT_ENTRY_HARDWARE_BITS_LARGE );
2295+ WARN_ON (pmd_val (* pmdp ) & ~(_SEGMENT_ENTRY_HARDWARE_BITS_LARGE |
2296+ _SEGMENT_ENTRY_GMAP_UC ));
22472297 if (purge )
22482298 __pmdp_csp (pmdp );
22492299 pmd_val (* pmdp ) = _SEGMENT_ENTRY_EMPTY ;
@@ -2296,7 +2346,8 @@ void gmap_pmdp_idte_local(struct mm_struct *mm, unsigned long vmaddr)
22962346 pmdp = (pmd_t * )entry ;
22972347 gaddr = __gmap_segment_gaddr (entry );
22982348 pmdp_notify_gmap (gmap , pmdp , gaddr );
2299- WARN_ON (* entry & ~_SEGMENT_ENTRY_HARDWARE_BITS_LARGE );
2349+ WARN_ON (* entry & ~(_SEGMENT_ENTRY_HARDWARE_BITS_LARGE |
2350+ _SEGMENT_ENTRY_GMAP_UC ));
23002351 if (MACHINE_HAS_TLB_GUEST )
23012352 __pmdp_idte (gaddr , pmdp , IDTE_GUEST_ASCE ,
23022353 gmap -> asce , IDTE_LOCAL );
@@ -2330,7 +2381,8 @@ void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr)
23302381 pmdp = (pmd_t * )entry ;
23312382 gaddr = __gmap_segment_gaddr (entry );
23322383 pmdp_notify_gmap (gmap , pmdp , gaddr );
2333- WARN_ON (* entry & ~_SEGMENT_ENTRY_HARDWARE_BITS_LARGE );
2384+ WARN_ON (* entry & ~(_SEGMENT_ENTRY_HARDWARE_BITS_LARGE |
2385+ _SEGMENT_ENTRY_GMAP_UC ));
23342386 if (MACHINE_HAS_TLB_GUEST )
23352387 __pmdp_idte (gaddr , pmdp , IDTE_GUEST_ASCE ,
23362388 gmap -> asce , IDTE_GLOBAL );
@@ -2346,6 +2398,71 @@ void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr)
23462398}
23472399EXPORT_SYMBOL_GPL (gmap_pmdp_idte_global );
23482400
2401+ /**
2402+ * gmap_test_and_clear_dirty_pmd - test and reset segment dirty status
2403+ * @gmap: pointer to guest address space
2404+ * @pmdp: pointer to the pmd to be tested
2405+ * @gaddr: virtual address in the guest address space
2406+ *
2407+ * This function is assumed to be called with the guest_table_lock
2408+ * held.
2409+ */
2410+ bool gmap_test_and_clear_dirty_pmd (struct gmap * gmap , pmd_t * pmdp ,
2411+ unsigned long gaddr )
2412+ {
2413+ if (pmd_val (* pmdp ) & _SEGMENT_ENTRY_INVALID )
2414+ return false;
2415+
2416+ /* Already protected memory, which did not change is clean */
2417+ if (pmd_val (* pmdp ) & _SEGMENT_ENTRY_PROTECT &&
2418+ !(pmd_val (* pmdp ) & _SEGMENT_ENTRY_GMAP_UC ))
2419+ return false;
2420+
2421+ /* Clear UC indication and reset protection */
2422+ pmd_val (* pmdp ) &= ~_SEGMENT_ENTRY_GMAP_UC ;
2423+ gmap_protect_pmd (gmap , gaddr , pmdp , PROT_READ , 0 );
2424+ return true;
2425+ }
2426+
2427+ /**
2428+ * gmap_sync_dirty_log_pmd - set bitmap based on dirty status of segment
2429+ * @gmap: pointer to guest address space
2430+ * @bitmap: dirty bitmap for this pmd
2431+ * @gaddr: virtual address in the guest address space
2432+ * @vmaddr: virtual address in the host address space
2433+ *
2434+ * This function is assumed to be called with the guest_table_lock
2435+ * held.
2436+ */
2437+ void gmap_sync_dirty_log_pmd (struct gmap * gmap , unsigned long bitmap [4 ],
2438+ unsigned long gaddr , unsigned long vmaddr )
2439+ {
2440+ int i ;
2441+ pmd_t * pmdp ;
2442+ pte_t * ptep ;
2443+ spinlock_t * ptl ;
2444+
2445+ pmdp = gmap_pmd_op_walk (gmap , gaddr );
2446+ if (!pmdp )
2447+ return ;
2448+
2449+ if (pmd_large (* pmdp )) {
2450+ if (gmap_test_and_clear_dirty_pmd (gmap , pmdp , gaddr ))
2451+ bitmap_fill (bitmap , _PAGE_ENTRIES );
2452+ } else {
2453+ for (i = 0 ; i < _PAGE_ENTRIES ; i ++ , vmaddr += PAGE_SIZE ) {
2454+ ptep = pte_alloc_map_lock (gmap -> mm , pmdp , vmaddr , & ptl );
2455+ if (!ptep )
2456+ continue ;
2457+ if (ptep_test_and_clear_uc (gmap -> mm , vmaddr , ptep ))
2458+ set_bit (i , bitmap );
2459+ spin_unlock (ptl );
2460+ }
2461+ }
2462+ gmap_pmd_op_end (gmap , pmdp );
2463+ }
2464+ EXPORT_SYMBOL_GPL (gmap_sync_dirty_log_pmd );
2465+
23492466static inline void thp_split_mm (struct mm_struct * mm )
23502467{
23512468#ifdef CONFIG_TRANSPARENT_HUGEPAGE
0 commit comments