s390/bitops: optimize set_bit() for constant values

Since zEC12 we have the interlocked-access facility 2 which allows to
use the instructions ni/oi/xi to update a single byte in storage with
compare-and-swap semantics.
So change set_bit(), clear_bit() and change_bit() to generate such code
instead of a compare-and-swap loop (or using the load-and-* instruction
family), if possible.
This reduces the text segment by yet another 8KB (defconfig).

Alternatively the long displacement variants niy/oiy/xiy could have
been used, but the extended displacement field is usually not needed
and therefore would only increase the size of the text segment again.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/arch/s390/include/asm/bitops.h b/arch/s390/include/asm/bitops.h
index 6038349..16df62d 100644
--- a/arch/s390/include/asm/bitops.h
+++ b/arch/s390/include/asm/bitops.h
@@ -151,6 +151,18 @@
 	unsigned long *addr = __bitops_word(nr, ptr);
 	unsigned long mask;
 
+#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
+	if (__builtin_constant_p(nr)) {
+		unsigned char *caddr = __bitops_byte(nr, ptr);
+
+		asm volatile(
+			"oi	%0,%b1\n"
+			: "+Q" (*caddr)
+			: "i" (1 << (nr & 7))
+			: "cc");
+		return;
+	}
+#endif
 	mask = 1UL << (nr & (BITS_PER_LONG - 1));
 	__BITOPS_LOOP(addr, mask, __BITOPS_OR);
 }
@@ -160,6 +172,18 @@
 	unsigned long *addr = __bitops_word(nr, ptr);
 	unsigned long mask;
 
+#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
+	if (__builtin_constant_p(nr)) {
+		unsigned char *caddr = __bitops_byte(nr, ptr);
+
+		asm volatile(
+			"ni	%0,%b1\n"
+			: "+Q" (*caddr)
+			: "i" (~(1 << (nr & 7)))
+			: "cc");
+		return;
+	}
+#endif
 	mask = ~(1UL << (nr & (BITS_PER_LONG - 1)));
 	__BITOPS_LOOP(addr, mask, __BITOPS_AND);
 }
@@ -169,6 +193,18 @@
 	unsigned long *addr = __bitops_word(nr, ptr);
 	unsigned long mask;
 
+#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
+	if (__builtin_constant_p(nr)) {
+		unsigned char *caddr = __bitops_byte(nr, ptr);
+
+		asm volatile(
+			"xi	%0,%b1\n"
+			: "+Q" (*caddr)
+			: "i" (1 << (nr & 7))
+			: "cc");
+		return;
+	}
+#endif
 	mask = 1UL << (nr & (BITS_PER_LONG - 1));
 	__BITOPS_LOOP(addr, mask, __BITOPS_XOR);
 }