s390/spinlock: add compare-and-delay to lock wait loops
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 14 Jan 2015 16:52:33 +0000 (17:52 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 23 Jan 2015 14:17:04 +0000 (15:17 +0100)
Add the compare-and-delay instruction to the spin-lock and rw-lock
retry loops. A CPU executing the compare-and-delay instruction stops
until the lock value has changed. This is done to make the locking
code for contended locks to behave better in regard to the multi-
hreading facility. A thread of a core executing a compare-and-delay
will allow the other threads of a core to get a larger share of the
core resources.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/setup.h
arch/s390/kernel/early.c
arch/s390/lib/spinlock.c

index 7736fdd725953c862f3df931723ed355d8b23f8b..b8d1e54b473308738167c1246b72734c5e0d71cc 100644 (file)
@@ -57,6 +57,7 @@ extern void detect_memory_memblock(void);
 #define MACHINE_FLAG_TE                (1UL << 15)
 #define MACHINE_FLAG_TLB_LC    (1UL << 17)
 #define MACHINE_FLAG_VX                (1UL << 18)
+#define MACHINE_FLAG_CAD       (1UL << 19)
 
 #define MACHINE_IS_VM          (S390_lowcore.machine_flags & MACHINE_FLAG_VM)
 #define MACHINE_IS_KVM         (S390_lowcore.machine_flags & MACHINE_FLAG_KVM)
@@ -80,6 +81,7 @@ extern void detect_memory_memblock(void);
 #define MACHINE_HAS_TE         (0)
 #define MACHINE_HAS_TLB_LC     (0)
 #define MACHINE_HAS_VX         (0)
+#define MACHINE_HAS_CAD                (0)
 #else /* CONFIG_64BIT */
 #define MACHINE_HAS_IEEE       (1)
 #define MACHINE_HAS_CSP                (1)
@@ -93,6 +95,7 @@ extern void detect_memory_memblock(void);
 #define MACHINE_HAS_TE         (S390_lowcore.machine_flags & MACHINE_FLAG_TE)
 #define MACHINE_HAS_TLB_LC     (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC)
 #define MACHINE_HAS_VX         (S390_lowcore.machine_flags & MACHINE_FLAG_VX)
+#define MACHINE_HAS_CAD                (S390_lowcore.machine_flags & MACHINE_FLAG_CAD)
 #endif /* CONFIG_64BIT */
 
 /*
index 302ac1f7f8e7693db6d547f544acfd84d1d4d357..70a3294509018e33c49d6b0a383c841d79d2f3af 100644 (file)
@@ -393,9 +393,27 @@ static __init void detect_machine_facilities(void)
                S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC;
        if (test_facility(129))
                S390_lowcore.machine_flags |= MACHINE_FLAG_VX;
+       if (test_facility(128))
+               S390_lowcore.machine_flags |= MACHINE_FLAG_CAD;
 #endif
 }
 
+static int __init nocad_setup(char *str)
+{
+       S390_lowcore.machine_flags &= ~MACHINE_FLAG_CAD;
+       return 0;
+}
+early_param("nocad", nocad_setup);
+
+static int __init cad_init(void)
+{
+       if (MACHINE_HAS_CAD)
+               /* Enable problem state CAD. */
+               __ctl_set_bit(2, 3);
+       return 0;
+}
+early_initcall(cad_init);
+
 static __init void rescue_initrd(void)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
index 034a35a3e9c11a0a5a9b7f2d73ba16cf9adbd314..d6c9991f77975e430f738bbbb802ae5e221ab46d 100644 (file)
 #include <linux/smp.h>
 #include <asm/io.h>
 
-int spin_retry = 1000;
+int spin_retry = -1;
+
+static int __init spin_retry_init(void)
+{
+       if (spin_retry < 0)
+               spin_retry = MACHINE_HAS_CAD ? 10 : 1000;
+       return 0;
+}
+early_initcall(spin_retry_init);
 
 /**
  * spin_retry= parameter
@@ -24,6 +32,11 @@ static int __init spin_retry_setup(char *str)
 }
 __setup("spin_retry=", spin_retry_setup);
 
+static inline void _raw_compare_and_delay(unsigned int *lock, unsigned int old)
+{
+       asm(".insn rsy,0xeb0000000022,%0,0,%1" : : "d" (old), "Q" (*lock));
+}
+
 void arch_spin_lock_wait(arch_spinlock_t *lp)
 {
        unsigned int cpu = SPINLOCK_LOCKVAL;
@@ -46,6 +59,8 @@ void arch_spin_lock_wait(arch_spinlock_t *lp)
                /* Loop for a while on the lock value. */
                count = spin_retry;
                do {
+                       if (MACHINE_HAS_CAD)
+                               _raw_compare_and_delay(&lp->lock, owner);
                        owner = ACCESS_ONCE(lp->lock);
                } while (owner && count-- > 0);
                if (!owner)
@@ -84,6 +99,8 @@ void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags)
                /* Loop for a while on the lock value. */
                count = spin_retry;
                do {
+                       if (MACHINE_HAS_CAD)
+                               _raw_compare_and_delay(&lp->lock, owner);
                        owner = ACCESS_ONCE(lp->lock);
                } while (owner && count-- > 0);
                if (!owner)
@@ -100,11 +117,19 @@ EXPORT_SYMBOL(arch_spin_lock_wait_flags);
 
 int arch_spin_trylock_retry(arch_spinlock_t *lp)
 {
+       unsigned int cpu = SPINLOCK_LOCKVAL;
+       unsigned int owner;
        int count;
 
-       for (count = spin_retry; count > 0; count--)
-               if (arch_spin_trylock_once(lp))
-                       return 1;
+       for (count = spin_retry; count > 0; count--) {
+               owner = ACCESS_ONCE(lp->lock);
+               /* Try to get the lock if it is free. */
+               if (!owner) {
+                       if (_raw_compare_and_swap(&lp->lock, 0, cpu))
+                               return 1;
+               } else if (MACHINE_HAS_CAD)
+                       _raw_compare_and_delay(&lp->lock, owner);
+       }
        return 0;
 }
 EXPORT_SYMBOL(arch_spin_trylock_retry);
@@ -126,8 +151,11 @@ void _raw_read_lock_wait(arch_rwlock_t *rw)
                }
                old = ACCESS_ONCE(rw->lock);
                owner = ACCESS_ONCE(rw->owner);
-               if ((int) old < 0)
+               if ((int) old < 0) {
+                       if (MACHINE_HAS_CAD)
+                               _raw_compare_and_delay(&rw->lock, old);
                        continue;
+               }
                if (_raw_compare_and_swap(&rw->lock, old, old + 1))
                        return;
        }
@@ -141,8 +169,11 @@ int _raw_read_trylock_retry(arch_rwlock_t *rw)
 
        while (count-- > 0) {
                old = ACCESS_ONCE(rw->lock);
-               if ((int) old < 0)
+               if ((int) old < 0) {
+                       if (MACHINE_HAS_CAD)
+                               _raw_compare_and_delay(&rw->lock, old);
                        continue;
+               }
                if (_raw_compare_and_swap(&rw->lock, old, old + 1))
                        return 1;
        }
@@ -173,6 +204,8 @@ void _raw_write_lock_wait(arch_rwlock_t *rw, unsigned int prev)
                }
                if ((old & 0x7fffffff) == 0 && (int) prev >= 0)
                        break;
+               if (MACHINE_HAS_CAD)
+                       _raw_compare_and_delay(&rw->lock, old);
        }
 }
 EXPORT_SYMBOL(_raw_write_lock_wait);
@@ -201,6 +234,8 @@ void _raw_write_lock_wait(arch_rwlock_t *rw)
                        smp_rmb();
                if ((old & 0x7fffffff) == 0 && (int) prev >= 0)
                        break;
+               if (MACHINE_HAS_CAD)
+                       _raw_compare_and_delay(&rw->lock, old);
        }
 }
 EXPORT_SYMBOL(_raw_write_lock_wait);
@@ -214,8 +249,11 @@ int _raw_write_trylock_retry(arch_rwlock_t *rw)
 
        while (count-- > 0) {
                old = ACCESS_ONCE(rw->lock);
-               if (old)
+               if (old) {
+                       if (MACHINE_HAS_CAD)
+                               _raw_compare_and_delay(&rw->lock, old);
                        continue;
+               }
                if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000))
                        return 1;
        }