#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)
 #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)
 #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 */
 
 /*
 
                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
 
 #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
 }
 __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;
                /* 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)
                /* 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)
 
 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);
                }
                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;
        }
 
        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;
        }
                }
                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);
                        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);
 
        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;
        }