diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h
index 7736fdd725953c862f3df931723ed355d8b23f8b..b8d1e54b473308738167c1246b72734c5e0d71cc 100644
--- a/arch/s390/include/asm/setup.h
+++ b/arch/s390/include/asm/setup.h
@@ -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 */
 
 /*
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 302ac1f7f8e7693db6d547f544acfd84d1d4d357..70a3294509018e33c49d6b0a383c841d79d2f3af 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -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
diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c
index 034a35a3e9c11a0a5a9b7f2d73ba16cf9adbd314..d6c9991f77975e430f738bbbb802ae5e221ab46d 100644
--- a/arch/s390/lib/spinlock.c
+++ b/arch/s390/lib/spinlock.c
@@ -12,7 +12,15 @@
 #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;
 	}