@@ -795,16 +795,26 @@ static struct kvm_lpage_info *lpage_info_slot(gfn_t gfn,
795795 return & slot -> arch .lpage_info [level - 2 ][idx ];
796796}
797797
798+ /*
799+ * The most significant bit in disallow_lpage tracks whether or not memory
800+ * attributes are mixed, i.e. not identical for all gfns at the current level.
801+ * The lower order bits are used to refcount other cases where a hugepage is
802+ * disallowed, e.g. if KVM has shadow a page table at the gfn.
803+ */
804+ #define KVM_LPAGE_MIXED_FLAG BIT(31)
805+
798806static void update_gfn_disallow_lpage_count (const struct kvm_memory_slot * slot ,
799807 gfn_t gfn , int count )
800808{
801809 struct kvm_lpage_info * linfo ;
802- int i ;
810+ int old , i ;
803811
804812 for (i = PG_LEVEL_2M ; i <= KVM_MAX_HUGEPAGE_LEVEL ; ++ i ) {
805813 linfo = lpage_info_slot (gfn , slot , i );
814+
815+ old = linfo -> disallow_lpage ;
806816 linfo -> disallow_lpage += count ;
807- WARN_ON_ONCE (linfo -> disallow_lpage < 0 );
817+ WARN_ON_ONCE (( old ^ linfo -> disallow_lpage ) & KVM_LPAGE_MIXED_FLAG );
808818 }
809819}
810820
@@ -7176,3 +7186,143 @@ void kvm_mmu_pre_destroy_vm(struct kvm *kvm)
71767186 if (kvm -> arch .nx_huge_page_recovery_thread )
71777187 kthread_stop (kvm -> arch .nx_huge_page_recovery_thread );
71787188}
7189+
7190+ #ifdef CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES
7191+ static bool hugepage_test_mixed (struct kvm_memory_slot * slot , gfn_t gfn ,
7192+ int level )
7193+ {
7194+ return lpage_info_slot (gfn , slot , level )-> disallow_lpage & KVM_LPAGE_MIXED_FLAG ;
7195+ }
7196+
7197+ static void hugepage_clear_mixed (struct kvm_memory_slot * slot , gfn_t gfn ,
7198+ int level )
7199+ {
7200+ lpage_info_slot (gfn , slot , level )-> disallow_lpage &= ~KVM_LPAGE_MIXED_FLAG ;
7201+ }
7202+
7203+ static void hugepage_set_mixed (struct kvm_memory_slot * slot , gfn_t gfn ,
7204+ int level )
7205+ {
7206+ lpage_info_slot (gfn , slot , level )-> disallow_lpage |= KVM_LPAGE_MIXED_FLAG ;
7207+ }
7208+
7209+ static bool hugepage_has_attrs (struct kvm * kvm , struct kvm_memory_slot * slot ,
7210+ gfn_t gfn , int level , unsigned long attrs )
7211+ {
7212+ const unsigned long start = gfn ;
7213+ const unsigned long end = start + KVM_PAGES_PER_HPAGE (level );
7214+
7215+ if (level == PG_LEVEL_2M )
7216+ return kvm_range_has_memory_attributes (kvm , start , end , attrs );
7217+
7218+ for (gfn = start ; gfn < end ; gfn += KVM_PAGES_PER_HPAGE (level - 1 )) {
7219+ if (hugepage_test_mixed (slot , gfn , level - 1 ) ||
7220+ attrs != kvm_get_memory_attributes (kvm , gfn ))
7221+ return false;
7222+ }
7223+ return true;
7224+ }
7225+
7226+ bool kvm_arch_post_set_memory_attributes (struct kvm * kvm ,
7227+ struct kvm_gfn_range * range )
7228+ {
7229+ unsigned long attrs = range -> arg .attributes ;
7230+ struct kvm_memory_slot * slot = range -> slot ;
7231+ int level ;
7232+
7233+ lockdep_assert_held_write (& kvm -> mmu_lock );
7234+ lockdep_assert_held (& kvm -> slots_lock );
7235+
7236+ /*
7237+ * Calculate which ranges can be mapped with hugepages even if the slot
7238+ * can't map memory PRIVATE. KVM mustn't create a SHARED hugepage over
7239+ * a range that has PRIVATE GFNs, and conversely converting a range to
7240+ * SHARED may now allow hugepages.
7241+ */
7242+ if (WARN_ON_ONCE (!kvm_arch_has_private_mem (kvm )))
7243+ return false;
7244+
7245+ /*
7246+ * The sequence matters here: upper levels consume the result of lower
7247+ * level's scanning.
7248+ */
7249+ for (level = PG_LEVEL_2M ; level <= KVM_MAX_HUGEPAGE_LEVEL ; level ++ ) {
7250+ gfn_t nr_pages = KVM_PAGES_PER_HPAGE (level );
7251+ gfn_t gfn = gfn_round_for_level (range -> start , level );
7252+
7253+ /* Process the head page if it straddles the range. */
7254+ if (gfn != range -> start || gfn + nr_pages > range -> end ) {
7255+ /*
7256+ * Skip mixed tracking if the aligned gfn isn't covered
7257+ * by the memslot, KVM can't use a hugepage due to the
7258+ * misaligned address regardless of memory attributes.
7259+ */
7260+ if (gfn >= slot -> base_gfn ) {
7261+ if (hugepage_has_attrs (kvm , slot , gfn , level , attrs ))
7262+ hugepage_clear_mixed (slot , gfn , level );
7263+ else
7264+ hugepage_set_mixed (slot , gfn , level );
7265+ }
7266+ gfn += nr_pages ;
7267+ }
7268+
7269+ /*
7270+ * Pages entirely covered by the range are guaranteed to have
7271+ * only the attributes which were just set.
7272+ */
7273+ for ( ; gfn + nr_pages <= range -> end ; gfn += nr_pages )
7274+ hugepage_clear_mixed (slot , gfn , level );
7275+
7276+ /*
7277+ * Process the last tail page if it straddles the range and is
7278+ * contained by the memslot. Like the head page, KVM can't
7279+ * create a hugepage if the slot size is misaligned.
7280+ */
7281+ if (gfn < range -> end &&
7282+ (gfn + nr_pages ) <= (slot -> base_gfn + slot -> npages )) {
7283+ if (hugepage_has_attrs (kvm , slot , gfn , level , attrs ))
7284+ hugepage_clear_mixed (slot , gfn , level );
7285+ else
7286+ hugepage_set_mixed (slot , gfn , level );
7287+ }
7288+ }
7289+ return false;
7290+ }
7291+
7292+ void kvm_mmu_init_memslot_memory_attributes (struct kvm * kvm ,
7293+ struct kvm_memory_slot * slot )
7294+ {
7295+ int level ;
7296+
7297+ if (!kvm_arch_has_private_mem (kvm ))
7298+ return ;
7299+
7300+ for (level = PG_LEVEL_2M ; level <= KVM_MAX_HUGEPAGE_LEVEL ; level ++ ) {
7301+ /*
7302+ * Don't bother tracking mixed attributes for pages that can't
7303+ * be huge due to alignment, i.e. process only pages that are
7304+ * entirely contained by the memslot.
7305+ */
7306+ gfn_t end = gfn_round_for_level (slot -> base_gfn + slot -> npages , level );
7307+ gfn_t start = gfn_round_for_level (slot -> base_gfn , level );
7308+ gfn_t nr_pages = KVM_PAGES_PER_HPAGE (level );
7309+ gfn_t gfn ;
7310+
7311+ if (start < slot -> base_gfn )
7312+ start += nr_pages ;
7313+
7314+ /*
7315+ * Unlike setting attributes, every potential hugepage needs to
7316+ * be manually checked as the attributes may already be mixed.
7317+ */
7318+ for (gfn = start ; gfn < end ; gfn += nr_pages ) {
7319+ unsigned long attrs = kvm_get_memory_attributes (kvm , gfn );
7320+
7321+ if (hugepage_has_attrs (kvm , slot , gfn , level , attrs ))
7322+ hugepage_clear_mixed (slot , gfn , level );
7323+ else
7324+ hugepage_set_mixed (slot , gfn , level );
7325+ }
7326+ }
7327+ }
7328+ #endif
0 commit comments