Skip to contentSkip to conversation

powerpc/kvm: Fix spinlock member access for PREEMPT_RT

RFC In review Vishal Chourasia 3 participants 5 messages Oct 10, 2024 – Oct 11, 2024

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 powerpc/kvm: Fix spinlock member access for PREEMPT_RT

Hi,

While building the kernel with CONFIG_PREEMPT_RT, I encountered several compilation errors in the PowerPC KVM code. The issues appear in book3s_hv_rm_mmu.c where it tries to access the 'rlock' member of struct spinlock, which doesn't exist in the RT configuration.

arch/powerpc/kvm/book3s_hv_rm_mmu.c:248:32: error: no member named 'rlock' in 'struct spinlock'

  248 |         arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
      |                         ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:164:45: note: expanded from macro 'arch_spin_lock'
164 | #define arch_spin_lock(l) queued_spin_lock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:263:36: error: no member named 'rlock' in 'struct spinlock'

  263 |                         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
      |                                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:277:34: error: no member named 'rlock' in 'struct spinlock'

  277 |         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
      |                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:938:32: error: no member named 'rlock' in 'struct spinlock'

  938 |         arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
      |                         ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:164:45: note: expanded from macro 'arch_spin_lock'
164 | #define arch_spin_lock(l) queued_spin_lock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:950:34: error: no member named 'rlock' in 'struct spinlock'

  950 |         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
      |                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:966:32: error: no member named 'rlock' in 'struct spinlock'

  966 |         arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
      |                         ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:164:45: note: expanded from macro 'arch_spin_lock'
164 | #define arch_spin_lock(l) queued_spin_lock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:981:34: error: no member named 'rlock' in 'struct spinlock'

  981 |         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
      |                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
7 errors generated.
make[4]: *** [scripts/Makefile.build:229: arch/powerpc/kvm/book3s_hv_rm_mmu.o] Error 1 make[3]: *** [scripts/Makefile.build:478: arch/powerpc/kvm] Error 2
make[3]: *** Waiting for unfinished jobs....
make[2]: *** [scripts/Makefile.build:478: arch/powerpc] Error 2
make[2]: *** Waiting for unfinished jobs....

Replace arch_spin_lock/unlock on kvm->mmu_lock.rlock.raw_lock with simple spin_lock/unlock on kvm->mmu_lock to fix build errors with CONFIG_PREEMPT_RT. The RT configuration changes the spinlock structure, removing the rlock member.

---
 arch/powerpc/kvm/book3s_hv_rm_mmu.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
arch/powerpc/kvm/book3s_hv_rm_mmu.c +7 -7
index 17cb75a127b04..abf1e6de85644 100644
@@ -245,7 +245,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
        /* Translate to host virtual address */
        hva = __gfn_to_hva_memslot(memslot, gfn);
 
-       arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_lock(&kvm->mmu_lock);
        ptep = find_kvm_host_pte(kvm, mmu_seq, hva, &hpage_shift);
        if (ptep) {
                pte_t pte;
@@ -260,7 +260,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
                 * to <= host page size, if host is using hugepage
                 */
                if (host_pte_size < psize) {
-                       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+                       spin_unlock(&kvm->mmu_lock);
                        return H_PARAMETER;
                }
                pte = kvmppc_read_update_linux_pte(ptep, writing);
@@ -274,7 +274,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
                        pa |= gpa & ~PAGE_MASK;
                }
        }
-       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_unlock(&kvm->mmu_lock);
 
        ptel &= HPTE_R_KEY | HPTE_R_PP0 | (psize-1);
        ptel |= pa;
@@ -935,7 +935,7 @@ static long kvmppc_do_h_page_init_zero(struct kvm_vcpu *vcpu,
        mmu_seq = kvm->mmu_invalidate_seq;
        smp_rmb();
 
-       arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_lock(&kvm->mmu_lock);
 
        ret = kvmppc_get_hpa(vcpu, mmu_seq, dest, 1, &pa, &memslot);
        if (ret != H_SUCCESS)
@@ -947,7 +947,7 @@ static long kvmppc_do_h_page_init_zero(struct kvm_vcpu *vcpu,
        kvmppc_update_dirty_map(memslot, dest >> PAGE_SHIFT, PAGE_SIZE);
 
 out_unlock:
-       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_unlock(&kvm->mmu_lock);
        return ret;
 }
 
@@ -963,7 +963,7 @@ static long kvmppc_do_h_page_init_copy(struct kvm_vcpu *vcpu,
        mmu_seq = kvm->mmu_invalidate_seq;
        smp_rmb();
 
-       arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_lock(&kvm->mmu_lock);
        ret = kvmppc_get_hpa(vcpu, mmu_seq, dest, 1, &dest_pa, &dest_memslot);
        if (ret != H_SUCCESS)
                goto out_unlock;
@@ -978,7 +978,7 @@ static long kvmppc_do_h_page_init_copy(struct kvm_vcpu *vcpu,
        kvmppc_update_dirty_map(dest_memslot, dest >> PAGE_SHIFT, PAGE_SIZE);
 
 out_unlock:
-       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_unlock(&kvm->mmu_lock);
        return ret;
 }
 

--
2.46.2

maintainer
#1 lore raw
Quoted 6 lines · Vishal Chourasia

Hi,

While building the kernel with CONFIG_PREEMPT_RT, I encountered several compilation errors in the PowerPC KVM code. The issues appear in book3s_hv_rm_mmu.c where it tries to access the 'rlock' member of struct spinlock, which doesn't exist in the RT configuration.

How was this tested? I suspect that putting to sleep a task that is running in real mode is a huge no-no. The actual solution would have to be to split mmu_lock into a spin_lock and a raw_spin_lock, but that's a huge amount of work probably. I'd just add a "depends on !PPC || !KVM_BOOK3S_64_HV" or something like that, to prevent enabling KVM-HV on PREEMPT_RT kernels.

Thanks,

Paolo

Quoted 130 lines

arch/powerpc/kvm/book3s_hv_rm_mmu.c:248:32: error: no member named 'rlock' in 'struct spinlock'

   248 |         arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
       |                         ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:164:45: note: expanded from macro 'arch_spin_lock'
164 | #define arch_spin_lock(l) queued_spin_lock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:263:36: error: no member named 'rlock' in 'struct spinlock'

   263 |                         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
       |                                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:277:34: error: no member named 'rlock' in 'struct spinlock'

   277 |         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
       |                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:938:32: error: no member named 'rlock' in 'struct spinlock'

   938 |         arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
       |                         ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:164:45: note: expanded from macro 'arch_spin_lock'
164 | #define arch_spin_lock(l) queued_spin_lock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:950:34: error: no member named 'rlock' in 'struct spinlock'

   950 |         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
       |                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:966:32: error: no member named 'rlock' in 'struct spinlock'

   966 |         arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
       |                         ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:164:45: note: expanded from macro 'arch_spin_lock'
164 | #define arch_spin_lock(l) queued_spin_lock(l)
| ^
arch/powerpc/kvm/book3s_hv_rm_mmu.c:981:34: error: no member named 'rlock' in 'struct spinlock'

   981 |         arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
       |                           ~~~~~~~~~~~~~ ^

./arch/powerpc/include/asm/qspinlock.h:166:49: note: expanded from macro 'arch_spin_unlock'
166 | #define arch_spin_unlock(l) queued_spin_unlock(l)
| ^
7 errors generated.
make[4]: *** [scripts/Makefile.build:229: arch/powerpc/kvm/book3s_hv_rm_mmu.o] Error 1 make[3]: *** [scripts/Makefile.build:478: arch/powerpc/kvm] Error 2
make[3]: *** Waiting for unfinished jobs....
make[2]: *** [scripts/Makefile.build:478: arch/powerpc] Error 2
make[2]: *** Waiting for unfinished jobs....

Replace arch_spin_lock/unlock on kvm->mmu_lock.rlock.raw_lock with simple spin_lock/unlock on kvm->mmu_lock to fix build errors with CONFIG_PREEMPT_RT. The RT configuration changes the spinlock structure, removing the rlock member.

---
  arch/powerpc/kvm/book3s_hv_rm_mmu.c | 14 +++++++-------
  1 file changed, 7 insertions(+), 7 deletions(-)
arch/powerpc/kvm/book3s_hv_rm_mmu.c +7 -8
index 17cb75a127b04..abf1e6de85644 100644
@@ -245,7 +245,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
         /* Translate to host virtual address */
         hva = __gfn_to_hva_memslot(memslot, gfn);
 
-       arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_lock(&kvm->mmu_lock);
         ptep = find_kvm_host_pte(kvm, mmu_seq, hva, &hpage_shift);
         if (ptep) {
                 pte_t pte;
@@ -260,7 +260,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
                  * to <= host page size, if host is using hugepage
                  */
                 if (host_pte_size < psize) {
-                       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+                       spin_unlock(&kvm->mmu_lock);
                         return H_PARAMETER;
                 }
                 pte = kvmppc_read_update_linux_pte(ptep, writing);
@@ -274,7 +274,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
                         pa |= gpa & ~PAGE_MASK;
                 }
         }
-       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_unlock(&kvm->mmu_lock);
 
         ptel &= HPTE_R_KEY | HPTE_R_PP0 | (psize-1);
         ptel |= pa;
@@ -935,7 +935,7 @@ static long kvmppc_do_h_page_init_zero(struct kvm_vcpu *vcpu,
         mmu_seq = kvm->mmu_invalidate_seq;
         smp_rmb();
 
-       arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_lock(&kvm->mmu_lock);
 
         ret = kvmppc_get_hpa(vcpu, mmu_seq, dest, 1, &pa, &memslot);
         if (ret != H_SUCCESS)
@@ -947,7 +947,7 @@ static long kvmppc_do_h_page_init_zero(struct kvm_vcpu *vcpu,
         kvmppc_update_dirty_map(memslot, dest >> PAGE_SHIFT, PAGE_SIZE);
 
  out_unlock:
-       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_unlock(&kvm->mmu_lock);
         return ret;
  }
 
@@ -963,7 +963,7 @@ static long kvmppc_do_h_page_init_copy(struct kvm_vcpu *vcpu,
         mmu_seq = kvm->mmu_invalidate_seq;
         smp_rmb();
 
-       arch_spin_lock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_lock(&kvm->mmu_lock);
         ret = kvmppc_get_hpa(vcpu, mmu_seq, dest, 1, &dest_pa, &dest_memslot);
         if (ret != H_SUCCESS)
                 goto out_unlock;
@@ -978,7 +978,7 @@ static long kvmppc_do_h_page_init_copy(struct kvm_vcpu *vcpu,
         kvmppc_update_dirty_map(dest_memslot, dest >> PAGE_SHIFT, PAGE_SIZE);
 
  out_unlock:
-       arch_spin_unlock(&kvm->mmu_lock.rlock.raw_lock);
+       spin_unlock(&kvm->mmu_lock);
         return ret;
  }
 
--

2.46.2

#2 lore raw
Quoted 10 lines · Paolo Bonzini
Quoted 6 lines · Vishal Chourasia

Hi,

While building the kernel with CONFIG_PREEMPT_RT, I encountered several compilation errors in the PowerPC KVM code. The issues appear in book3s_hv_rm_mmu.c where it tries to access the 'rlock' member of struct spinlock, which doesn't exist in the RT configuration.

How was this tested? I suspect that putting to sleep a task that is running in real mode is a huge no-no.

Yeah.

Even without preempt, spin_lock() can end up in debug/tracing code that will blow up in real mode.

Vishal, if you look at the history of that file you'll see eg:

87013f9c602c ("powerpc/kvm/book3s: switch from raw_spin_*lock to arch_spin_lock.")

Quoted 5 lines

The actual solution would have to
be to split mmu_lock into a spin_lock and a raw_spin_lock, but that's a huge amount of work probably. I'd just add a "depends on !PPC || !KVM_BOOK3S_64_HV" or something like that, to prevent enabling KVM-HV on PREEMPT_RT kernels.

Yeah that should work to get something building.

The bulk (or all?) of that file is not used for Radix guests, only for hash page table MMU guests.

So I think it should be possible to hide that code behind a new CONFIG option that controls support for HPT guests. And then that option could be incompatible with PREEMPT_RT. But that will require unstitching some of the connections between that code and the other ppc KVM code.

cheers

#3 lore raw
In reply to Paolo Bonzini
Quoted 8 lines · Paolo Bonzini
Quoted 6 lines · Vishal Chourasia

Hi,

While building the kernel with CONFIG_PREEMPT_RT, I encountered several compilation errors in the PowerPC KVM code. The issues appear in book3s_hv_rm_mmu.c where it tries to access the 'rlock' member of struct spinlock, which doesn't exist in the RT configuration.

Quoted 5 lines

How was this tested? I suspect that putting to sleep a task that is running in real mode is a huge no-no. The actual solution would have to be to split mmu_lock into a spin_lock and a raw_spin_lock, but that's a huge amount of work probably. I'd just add a "depends on !PPC || !KVM_BOOK3S_64_HV" or something like that, to prevent enabling KVM-HV on PREEMPT_RT kernels.

Hi Paolo,

This is a build time error, I didn't boot the kernel.

I used pseries_le_defconfig with some other configs enabled. I was trying to see if the kernel would compile with ARCH_SUPPORTS_RT and CONFIG_PREEMPT_RT enabled.

arch/powerpc/Kconfig +1 -0
index 8094a01974cca..568dc856f0dfa 100644
@@ -168,6 +168,7 @@ config PPC
        select ARCH_STACKWALK
        select ARCH_SUPPORTS_ATOMIC_RMW
        select ARCH_SUPPORTS_DEBUG_PAGEALLOC    if PPC_BOOK3S || PPC_8xx
+       select ARCH_SUPPORTS_RT                 if !PPC || !KVM_BOOK3S_64_HV
        select ARCH_USE_BUILTIN_BSWAP
        select ARCH_USE_CMPXCHG_LOCKREF         if PPC64
        select ARCH_USE_MEMTEST

I tried rebuilding with the above diff as per your suggestion though it works when KVM_BOOK3S_64_HV is set to N, but for pseries_le_defconfig, it's set to M, by default, which then requires setting it to N explicitly.

Will something like below be a better solution? This will set KVM_BOOK3S_64_HV to N if ARCH_SUPPORTS_RT is set.

arch/powerpc/kvm/Kconfig +1 -1
index dbfdc126bf144..33e0d50b08b14 100644
@@ -80,7 +80,7 @@ config KVM_BOOK3S_64
 
 config KVM_BOOK3S_64_HV
        tristate "KVM for POWER7 and later using hypervisor mode in host"
-       depends on KVM_BOOK3S_64 && PPC_POWERNV
+       depends on KVM_BOOK3S_64 && PPC_POWERNV && !ARCH_SUPPORTS_RT
        select KVM_BOOK3S_HV_POSSIBLE
        select KVM_GENERIC_MMU_NOTIFIER
        select CMA

Thanks

maintainer
#4 lore raw
Quoted 12 lines · Vishal Chourasia
arch/powerpc/Kconfig +1 -0
index 8094a01974cca..568dc856f0dfa 100644
@@ -168,6 +168,7 @@ config PPC
        select ARCH_STACKWALK
        select ARCH_SUPPORTS_ATOMIC_RMW
        select ARCH_SUPPORTS_DEBUG_PAGEALLOC    if PPC_BOOK3S || PPC_8xx
+       select ARCH_SUPPORTS_RT                 if !PPC || !KVM_BOOK3S_64_HV
        select ARCH_USE_BUILTIN_BSWAP
        select ARCH_USE_CMPXCHG_LOCKREF         if PPC64
        select ARCH_USE_MEMTEST
Quoted 4 lines

I tried rebuilding with the above diff as per your suggestion though it works when KVM_BOOK3S_64_HV is set to N, but for pseries_le_defconfig, it's set to M, by default, which then requires setting it to N explicitly.

Yes, that was intentional (the "!PPC ||" part is not necessary since you placed this in "config PPC"). I understand however that it's hard to discover that you need KVM_BOOK3S_64_HV=n in order to build an RT kernel.

Quoted 8 lines

Will something like below be a better solution? This will set KVM_BOOK3S_64_HV to N if ARCH_SUPPORTS_RT is set.

arch/powerpc/kvm/Kconfig +0 -0
index dbfdc126bf144..33e0d50b08b14 100644

arch/powerpc/kvm/Kconfig config KVM_BOOK3S_64 · line 86

Quoted 8 lines
arch/powerpc/kvm/Kconfig +1 -1
@@ -80,7 +80,7 @@ config KVM_BOOK3S_64
 
 config KVM_BOOK3S_64_HV
        tristate "KVM for POWER7 and later using hypervisor mode in host"
-       depends on KVM_BOOK3S_64 && PPC_POWERNV
+       depends on KVM_BOOK3S_64 && PPC_POWERNV && !ARCH_SUPPORTS_RT
        select KVM_BOOK3S_HV_POSSIBLE
        select KVM_GENERIC_MMU_NOTIFIER
        select CMA

No, that would make it completely impossible to build with KVM enabled.

Paolo