[PATCH v5 4/6] pv-qspinlock: powerpc support pv-qspinlock
As we need let pv-qspinlock-kernel run on all environment which might have no powervm, we should runtime choose which qspinlock version to use. The default pv-qspinlock use native version. pv_lock initialization should be done in bootstage with irq disabled. And if there is PHYP, restore pv_lock_ops callbacks to pv version. There is also a hash table, we store cpu number into it and the key is lock. So everytime pv_wait can know who is the lock holder by searching the lock. Also store the lock in a per_cpu struct, and remove it when we own the lock. pv_wait need know which lock we are spinning on. Signed-off-by: Pan Xinhui--- arch/powerpc/include/asm/qspinlock.h | 15 +++ arch/powerpc/include/asm/qspinlock_paravirt.h | 38 +++ .../powerpc/include/asm/qspinlock_paravirt_types.h | 13 +++ arch/powerpc/kernel/paravirt.c | 121 + arch/powerpc/platforms/pseries/setup.c | 5 + 5 files changed, 192 insertions(+) create mode 100644 arch/powerpc/include/asm/qspinlock_paravirt.h create mode 100644 arch/powerpc/include/asm/qspinlock_paravirt_types.h create mode 100644 arch/powerpc/kernel/paravirt.c diff --git a/arch/powerpc/include/asm/qspinlock.h b/arch/powerpc/include/asm/qspinlock.h index fc83cd2..61a0d00 100644 --- a/arch/powerpc/include/asm/qspinlock.h +++ b/arch/powerpc/include/asm/qspinlock.h @@ -16,10 +16,25 @@ static inline void native_queued_spin_unlock(struct qspinlock *lock) smp_store_release(locked, 0); } +#ifdef CONFIG_PARAVIRT_SPINLOCKS + +#include + +static inline void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) +{ + pv_queued_spin_lock(lock, val); +} + +static inline void queued_spin_unlock(struct qspinlock *lock) +{ + pv_queued_spin_unlock(lock); +} +#else static inline void queued_spin_unlock(struct qspinlock *lock) { native_queued_spin_unlock(lock); } +#endif #include diff --git a/arch/powerpc/include/asm/qspinlock_paravirt.h b/arch/powerpc/include/asm/qspinlock_paravirt.h new file mode 100644 index 000..cd17a79 --- /dev/null +++ b/arch/powerpc/include/asm/qspinlock_paravirt.h @@ -0,0 +1,38 @@ +#ifndef CONFIG_PARAVIRT_SPINLOCKS +#error "do not include this file" +#endif + +#ifndef _ASM_QSPINLOCK_PARAVIRT_H +#define _ASM_QSPINLOCK_PARAVIRT_H + +#include + +extern void pv_lock_init(void); +extern void native_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +extern void __pv_init_lock_hash(void); +extern void __pv_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +extern void __pv_queued_spin_unlock(struct qspinlock *lock); + +static inline void pv_queued_spin_lock(struct qspinlock *lock, u32 val) +{ + CLEAR_IO_SYNC; + pv_lock_op.lock(lock, val); +} + +static inline void pv_queued_spin_unlock(struct qspinlock *lock) +{ + SYNC_IO; + pv_lock_op.unlock(lock); +} + +static inline void pv_wait(u8 *ptr, u8 val, int lockcpu) +{ + pv_lock_op.wait(ptr, val, lockcpu); +} + +static inline void pv_kick(int cpu) +{ + pv_lock_op.kick(cpu); +} + +#endif diff --git a/arch/powerpc/include/asm/qspinlock_paravirt_types.h b/arch/powerpc/include/asm/qspinlock_paravirt_types.h new file mode 100644 index 000..e1fdeb0 --- /dev/null +++ b/arch/powerpc/include/asm/qspinlock_paravirt_types.h @@ -0,0 +1,13 @@ +#ifndef _ASM_QSPINLOCK_PARAVIRT_TYPES_H +#define _ASM_QSPINLOCK_PARAVIRT_TYPES_H + +struct pv_lock_ops { + void (*lock)(struct qspinlock *lock, u32 val); + void (*unlock)(struct qspinlock *lock); + void (*wait)(u8 *ptr, u8 val, int cpu); + void (*kick)(int cpu); +}; + +extern struct pv_lock_ops pv_lock_op; + +#endif diff --git a/arch/powerpc/kernel/paravirt.c b/arch/powerpc/kernel/paravirt.c new file mode 100644 index 000..2e87fa6 --- /dev/null +++ b/arch/powerpc/kernel/paravirt.c @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#define NUM_LOCK_CPU_ENTRY_SHIFT 16 +#define NUM_LOCK_CPU_ENTRY (1 << NUM_LOCK_CPU_ENTRY_SHIFT) +#define NUM_LOCKS_PER_CPU 4 + +static u16 *hash_lock_cpu_ptr; + +struct locks_on_cpu { + void *l[NUM_LOCKS_PER_CPU]; + int count; +}; + +static DEFINE_PER_CPU(struct locks_on_cpu, node); + +static u16 *hash(void *l) +{ + int val = hash_ptr(l, NUM_LOCK_CPU_ENTRY_SHIFT); + + return _lock_cpu_ptr[val]; +} + +static void __init init_hash(void) +{ + int size = NUM_LOCK_CPU_ENTRY * sizeof(*hash_lock_cpu_ptr); + + hash_lock_cpu_ptr = memblock_virt_alloc(size, 0); + memset(hash_lock_cpu_ptr, 0, size); +} + +#define lock_get_holder(l) \ + ((int)*hash(l) - 1) + +#define lock_set_holder(l) \ +
[PATCH v5 4/6] pv-qspinlock: powerpc support pv-qspinlock
As we need let pv-qspinlock-kernel run on all environment which might have no powervm, we should runtime choose which qspinlock version to use. The default pv-qspinlock use native version. pv_lock initialization should be done in bootstage with irq disabled. And if there is PHYP, restore pv_lock_ops callbacks to pv version. There is also a hash table, we store cpu number into it and the key is lock. So everytime pv_wait can know who is the lock holder by searching the lock. Also store the lock in a per_cpu struct, and remove it when we own the lock. pv_wait need know which lock we are spinning on. Signed-off-by: Pan Xinhui--- arch/powerpc/include/asm/qspinlock.h | 15 +++ arch/powerpc/include/asm/qspinlock_paravirt.h | 38 +++ .../powerpc/include/asm/qspinlock_paravirt_types.h | 13 +++ arch/powerpc/kernel/paravirt.c | 121 + arch/powerpc/platforms/pseries/setup.c | 5 + 5 files changed, 192 insertions(+) create mode 100644 arch/powerpc/include/asm/qspinlock_paravirt.h create mode 100644 arch/powerpc/include/asm/qspinlock_paravirt_types.h create mode 100644 arch/powerpc/kernel/paravirt.c diff --git a/arch/powerpc/include/asm/qspinlock.h b/arch/powerpc/include/asm/qspinlock.h index fc83cd2..61a0d00 100644 --- a/arch/powerpc/include/asm/qspinlock.h +++ b/arch/powerpc/include/asm/qspinlock.h @@ -16,10 +16,25 @@ static inline void native_queued_spin_unlock(struct qspinlock *lock) smp_store_release(locked, 0); } +#ifdef CONFIG_PARAVIRT_SPINLOCKS + +#include + +static inline void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) +{ + pv_queued_spin_lock(lock, val); +} + +static inline void queued_spin_unlock(struct qspinlock *lock) +{ + pv_queued_spin_unlock(lock); +} +#else static inline void queued_spin_unlock(struct qspinlock *lock) { native_queued_spin_unlock(lock); } +#endif #include diff --git a/arch/powerpc/include/asm/qspinlock_paravirt.h b/arch/powerpc/include/asm/qspinlock_paravirt.h new file mode 100644 index 000..cd17a79 --- /dev/null +++ b/arch/powerpc/include/asm/qspinlock_paravirt.h @@ -0,0 +1,38 @@ +#ifndef CONFIG_PARAVIRT_SPINLOCKS +#error "do not include this file" +#endif + +#ifndef _ASM_QSPINLOCK_PARAVIRT_H +#define _ASM_QSPINLOCK_PARAVIRT_H + +#include + +extern void pv_lock_init(void); +extern void native_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +extern void __pv_init_lock_hash(void); +extern void __pv_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +extern void __pv_queued_spin_unlock(struct qspinlock *lock); + +static inline void pv_queued_spin_lock(struct qspinlock *lock, u32 val) +{ + CLEAR_IO_SYNC; + pv_lock_op.lock(lock, val); +} + +static inline void pv_queued_spin_unlock(struct qspinlock *lock) +{ + SYNC_IO; + pv_lock_op.unlock(lock); +} + +static inline void pv_wait(u8 *ptr, u8 val, int lockcpu) +{ + pv_lock_op.wait(ptr, val, lockcpu); +} + +static inline void pv_kick(int cpu) +{ + pv_lock_op.kick(cpu); +} + +#endif diff --git a/arch/powerpc/include/asm/qspinlock_paravirt_types.h b/arch/powerpc/include/asm/qspinlock_paravirt_types.h new file mode 100644 index 000..e1fdeb0 --- /dev/null +++ b/arch/powerpc/include/asm/qspinlock_paravirt_types.h @@ -0,0 +1,13 @@ +#ifndef _ASM_QSPINLOCK_PARAVIRT_TYPES_H +#define _ASM_QSPINLOCK_PARAVIRT_TYPES_H + +struct pv_lock_ops { + void (*lock)(struct qspinlock *lock, u32 val); + void (*unlock)(struct qspinlock *lock); + void (*wait)(u8 *ptr, u8 val, int cpu); + void (*kick)(int cpu); +}; + +extern struct pv_lock_ops pv_lock_op; + +#endif diff --git a/arch/powerpc/kernel/paravirt.c b/arch/powerpc/kernel/paravirt.c new file mode 100644 index 000..2e87fa6 --- /dev/null +++ b/arch/powerpc/kernel/paravirt.c @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#define NUM_LOCK_CPU_ENTRY_SHIFT 16 +#define NUM_LOCK_CPU_ENTRY (1 << NUM_LOCK_CPU_ENTRY_SHIFT) +#define NUM_LOCKS_PER_CPU 4 + +static u16 *hash_lock_cpu_ptr; + +struct locks_on_cpu { + void *l[NUM_LOCKS_PER_CPU]; + int count; +}; + +static DEFINE_PER_CPU(struct locks_on_cpu, node); + +static u16 *hash(void *l) +{ + int val = hash_ptr(l, NUM_LOCK_CPU_ENTRY_SHIFT); + + return _lock_cpu_ptr[val]; +} + +static void __init init_hash(void) +{ + int size = NUM_LOCK_CPU_ENTRY * sizeof(*hash_lock_cpu_ptr); + + hash_lock_cpu_ptr = memblock_virt_alloc(size, 0); + memset(hash_lock_cpu_ptr, 0, size); +} + +#define lock_get_holder(l) \ + ((int)*hash(l) - 1) + +#define lock_set_holder(l) \ +