Merge tag 's390-6.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Alexander Gordeev:

 - Add machine variable capacity information to /proc/sysinfo.

 - Limit the waste of page tables and always align vmalloc area size and
   base address on segment boundary.

 - Fix a memory leak when an attempt to register interruption sub class
   (ISC) for the adjunct-processor (AP) guest failed.

 - Reset response code AP_RESPONSE_INVALID_GISA to understandable by
   guest AP_RESPONSE_INVALID_ADDRESS in response to a failed
   interruption sub class (ISC) registration attempt.

 - Improve reaction to adjunct-processor (AP)
   AP_RESPONSE_OTHERWISE_CHANGED response code when enabling interrupts
   on behalf of a guest.

 - Fix incorrect sysfs 'status' attribute of adjunct-processor (AP)
   queue device bound to the vfio_ap device driver when the mediated
   device is attached to a guest, but the queue device is not passed
   through.

 - Rework struct ap_card to hold the whole adjunct-processor (AP) card
   hardware information. As result, all the ugly bit checks are replaced
   by simple evaluations of the required bit fields.

 - Improve handling of some weird scenarios between service element (SE)
   host and SE guest with adjunct-processor (AP) pass-through support.

 - Change local_ctl_set_bit() and local_ctl_clear_bit() so they return
   the previous value of the to be changed control register. This is
   useful if a bit is only changed temporarily and the previous content
   needs to be restored.

 - The kernel starts with machine checks disabled and is expected to
   enable it once trap_init() is called. However the implementation
   allows machine checks early. Consistently enable it in trap_init()
   only.

 - local_mcck_disable() and local_mcck_enable() assume that machine
   checks are always enabled. Instead implement and use
   local_mcck_save() and local_mcck_restore() to disable machine checks
   and restore the previous state.

 - Modification of floating point control (FPC) register of a traced
   process using ptrace interface may lead to corruption of the FPC
   register of the tracing process. Fix this.

 - kvm_arch_vcpu_ioctl_set_fpu() allows to set the floating point
   control (FPC) register in vCPU, but may lead to corruption of the FPC
   register of the host process. Fix this.

 - Use READ_ONCE() to read a vCPU floating point register value from the
   memory mapped area. This avoids that, depending on code generation, a
   different value is tested for validity than the one that is used.

 - Get rid of test_fp_ctl(), since it is quite subtle to use it
   correctly. Instead copy a new floating point control register value
   into its save area and test the validity of the new value when
   loading it.

 - Remove superfluous save_fpu_regs() call.

 - Remove s390 support for ARCH_WANTS_DYNAMIC_TASK_STRUCT. All machines
   provide the vector facility since many years and the need to make the
   task structure size dependent on the vector facility does not exist.

 - Remove the "novx" kernel command line option, as the vector code runs
   without any problems since many years.

 - Add the vector facility to the z13 architecture level set (ALS). All
   hypervisors support the vector facility since many years. This allows
   compile time optimizations of the kernel.

 - Get rid of MACHINE_HAS_VX and replace it with cpu_has_vx(). As
   result, the compiled code will have less runtime checks and less
   code.

 - Convert pgste_get_lock() and pgste_set_unlock() ASM inlines to C.

 - Convert the struct subchannel spinlock from pointer to member.

* tag 's390-6.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (24 commits)
  Revert "s390: update defconfigs"
  s390/cio: make sch->lock spinlock pointer a member
  s390: update defconfigs
  s390/mm: convert pgste locking functions to C
  s390/fpu: get rid of MACHINE_HAS_VX
  s390/als: add vector facility to z13 architecture level set
  s390/fpu: remove "novx" option
  s390/fpu: remove ARCH_WANTS_DYNAMIC_TASK_STRUCT support
  KVM: s390: remove superfluous save_fpu_regs() call
  s390/fpu: get rid of test_fp_ctl()
  KVM: s390: use READ_ONCE() to read fpc register value
  KVM: s390: fix setting of fpc register
  s390/ptrace: handle setting of fpc register correctly
  s390/nmi: implement and use local_mcck_save() / local_mcck_restore()
  s390/nmi: consistently enable machine checks in trap_init()
  s390/ctlreg: return old register contents when changing bits
  s390/ap: handle outband SE bind state change
  s390/ap: store TAPQ hwinfo in struct ap_card
  s390/vfio-ap: fix sysfs status attribute for AP queue devices
  s390/vfio-ap: improve reaction to response code 07 from PQAP(AQIC) command
  ...
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 959adae..8f39f04 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -123,7 +123,6 @@
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
 	select ARCH_USE_SYM_ANNOTATIONS
-	select ARCH_WANTS_DYNAMIC_TASK_STRUCT
 	select ARCH_WANTS_NO_INSTR
 	select ARCH_WANT_DEFAULT_BPF_JIT
 	select ARCH_WANT_IPC_PARSE_VERSION
diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c
index 2ab4872..b24de9a 100644
--- a/arch/s390/boot/ipl_parm.c
+++ b/arch/s390/boot/ipl_parm.c
@@ -274,7 +274,7 @@ void parse_boot_command_line(void)
 			memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);
 
 		if (!strcmp(param, "vmalloc") && val) {
-			vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
+			vmalloc_size = round_up(memparse(val, NULL), _SEGMENT_SIZE);
 			vmalloc_size_set = 1;
 		}
 
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c
index 8104e0e..9cc76e6 100644
--- a/arch/s390/boot/startup.c
+++ b/arch/s390/boot/startup.c
@@ -255,7 +255,8 @@ static unsigned long setup_kernel_memory_layout(void)
 	VMALLOC_END = MODULES_VADDR;
 
 	/* allow vmalloc area to occupy up to about 1/2 of the rest virtual space left */
-	vmalloc_size = min(vmalloc_size, round_down(VMALLOC_END / 2, _REGION3_SIZE));
+	vsize = round_down(VMALLOC_END / 2, _SEGMENT_SIZE);
+	vmalloc_size = min(vmalloc_size, vsize);
 	VMALLOC_START = VMALLOC_END - vmalloc_size;
 
 	/* split remaining virtual space between 1:1 mapping & vmemmap array */
diff --git a/arch/s390/crypto/chacha-glue.c b/arch/s390/crypto/chacha-glue.c
index 5fae187..ed9959e 100644
--- a/arch/s390/crypto/chacha-glue.c
+++ b/arch/s390/crypto/chacha-glue.c
@@ -82,7 +82,7 @@ void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src,
 	 * it cannot handle a block of data or less, but otherwise
 	 * it can handle data of arbitrary size
 	 */
-	if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20 || !MACHINE_HAS_VX)
+	if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20 || !cpu_has_vx())
 		chacha_crypt_generic(state, dst, src, bytes, nrounds);
 	else
 		chacha20_crypt_s390(state, dst, src, bytes,
diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h
index 40c2b82..43ac4a6 100644
--- a/arch/s390/include/asm/ap.h
+++ b/arch/s390/include/asm/ap.h
@@ -88,7 +88,7 @@ static inline bool ap_instructions_available(void)
 }
 
 /* TAPQ register GR2 response struct */
-struct ap_tapq_gr2 {
+struct ap_tapq_hwinfo {
 	union {
 		unsigned long value;
 		struct {
@@ -96,11 +96,13 @@ struct ap_tapq_gr2 {
 			unsigned int apinfo : 32; /* ap type, ... */
 		};
 		struct {
-			unsigned int s	   :  1; /* APSC */
-			unsigned int m	   :  1; /* AP4KM */
-			unsigned int c	   :  1; /* AP4KC */
-			unsigned int mode  :  3;
-			unsigned int n	   :  1; /* APXA */
+			unsigned int apsc  :  1; /* APSC */
+			unsigned int mex4k :  1; /* AP4KM */
+			unsigned int crt4k :  1; /* AP4KC */
+			unsigned int cca   :  1; /* D */
+			unsigned int accel :  1; /* A */
+			unsigned int ep11  :  1; /* X */
+			unsigned int apxa  :  1; /* APXA */
 			unsigned int	   :  1;
 			unsigned int class :  8;
 			unsigned int bs	   :  2; /* SE bind/assoc */
@@ -126,11 +128,12 @@ struct ap_tapq_gr2 {
 /**
  * ap_tapq(): Test adjunct processor queue.
  * @qid: The AP queue number
- * @info: Pointer to queue descriptor
+ * @info: Pointer to tapq hwinfo struct
  *
  * Returns AP queue status structure.
  */
-static inline struct ap_queue_status ap_tapq(ap_qid_t qid, struct ap_tapq_gr2 *info)
+static inline struct ap_queue_status ap_tapq(ap_qid_t qid,
+					     struct ap_tapq_hwinfo *info)
 {
 	union ap_queue_status_reg reg1;
 	unsigned long reg2;
@@ -158,7 +161,7 @@ static inline struct ap_queue_status ap_tapq(ap_qid_t qid, struct ap_tapq_gr2 *i
  * Returns AP queue status structure.
  */
 static inline struct ap_queue_status ap_test_queue(ap_qid_t qid, int tbit,
-						   struct ap_tapq_gr2 *info)
+						   struct ap_tapq_hwinfo *info)
 {
 	if (tbit)
 		qid |= 1UL << 23; /* set T bit*/
diff --git a/arch/s390/include/asm/ctlreg.h b/arch/s390/include/asm/ctlreg.h
index 6d4b85f..72a9556 100644
--- a/arch/s390/include/asm/ctlreg.h
+++ b/arch/s390/include/asm/ctlreg.h
@@ -141,22 +141,26 @@ static __always_inline void local_ctl_store(unsigned int cr, struct ctlreg *reg)
 		: [cr] "i" (cr));
 }
 
-static __always_inline void local_ctl_set_bit(unsigned int cr, unsigned int bit)
+static __always_inline struct ctlreg local_ctl_set_bit(unsigned int cr, unsigned int bit)
 {
-	struct ctlreg reg;
+	struct ctlreg new, old;
 
-	local_ctl_store(cr, &reg);
-	reg.val |= 1UL << bit;
-	local_ctl_load(cr, &reg);
+	local_ctl_store(cr, &old);
+	new = old;
+	new.val |= 1UL << bit;
+	local_ctl_load(cr, &new);
+	return old;
 }
 
-static __always_inline void local_ctl_clear_bit(unsigned int cr, unsigned int bit)
+static __always_inline struct ctlreg local_ctl_clear_bit(unsigned int cr, unsigned int bit)
 {
-	struct ctlreg reg;
+	struct ctlreg new, old;
 
-	local_ctl_store(cr, &reg);
-	reg.val &= ~(1UL << bit);
-	local_ctl_load(cr, &reg);
+	local_ctl_store(cr, &old);
+	new = old;
+	new.val &= ~(1UL << bit);
+	local_ctl_load(cr, &new);
+	return old;
 }
 
 struct lowcore;
diff --git a/arch/s390/include/asm/fpu/api.h b/arch/s390/include/asm/fpu/api.h
index 9acf48e..d6ca8bc 100644
--- a/arch/s390/include/asm/fpu/api.h
+++ b/arch/s390/include/asm/fpu/api.h
@@ -46,26 +46,33 @@
 
 #include <linux/preempt.h>
 #include <asm/asm-extable.h>
+#include <asm/fpu/internal.h>
 
 void save_fpu_regs(void);
 void load_fpu_regs(void);
 void __load_fpu_regs(void);
 
-static inline int test_fp_ctl(u32 fpc)
+/**
+ * sfpc_safe - Set floating point control register safely.
+ * @fpc: new value for floating point control register
+ *
+ * Set floating point control register. This may lead to an exception,
+ * since a saved value may have been modified by user space (ptrace,
+ * signal return, kvm registers) to an invalid value. In such a case
+ * set the floating point control register to zero.
+ */
+static inline void sfpc_safe(u32 fpc)
 {
-	u32 orig_fpc;
-	int rc;
-
-	asm volatile(
-		"	efpc    %1\n"
-		"	sfpc	%2\n"
-		"0:	sfpc	%1\n"
-		"	la	%0,0\n"
-		"1:\n"
-		EX_TABLE(0b,1b)
-		: "=d" (rc), "=&d" (orig_fpc)
-		: "d" (fpc), "0" (-EINVAL));
-	return rc;
+	asm volatile("\n"
+		"0:	sfpc	%[fpc]\n"
+		"1:	nopr	%%r7\n"
+		".pushsection .fixup, \"ax\"\n"
+		"2:	lghi	%[fpc],0\n"
+		"	jg	0b\n"
+		".popsection\n"
+		EX_TABLE(1b, 2b)
+		: [fpc] "+d" (fpc)
+		: : "memory");
 }
 
 #define KERNEL_FPC		1
diff --git a/arch/s390/include/asm/fpu/internal.h b/arch/s390/include/asm/fpu/internal.h
index 8634581..d511c4c 100644
--- a/arch/s390/include/asm/fpu/internal.h
+++ b/arch/s390/include/asm/fpu/internal.h
@@ -10,8 +10,14 @@
 #define _ASM_S390_FPU_INTERNAL_H
 
 #include <linux/string.h>
+#include <asm/facility.h>
 #include <asm/fpu/types.h>
 
+static inline bool cpu_has_vx(void)
+{
+	return likely(test_facility(129));
+}
+
 static inline void save_vx_regs(__vector128 *vxrs)
 {
 	asm volatile(
@@ -41,7 +47,7 @@ static inline void fpregs_store(_s390_fp_regs *fpregs, struct fpu *fpu)
 {
 	fpregs->pad = 0;
 	fpregs->fpc = fpu->fpc;
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		convert_vx_to_fp((freg_t *)&fpregs->fprs, fpu->vxrs);
 	else
 		memcpy((freg_t *)&fpregs->fprs, fpu->fprs,
@@ -51,7 +57,7 @@ static inline void fpregs_store(_s390_fp_regs *fpregs, struct fpu *fpu)
 static inline void fpregs_load(_s390_fp_regs *fpregs, struct fpu *fpu)
 {
 	fpu->fpc = fpregs->fpc;
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		convert_fp_to_vx(fpu->vxrs, (freg_t *)&fpregs->fprs);
 	else
 		memcpy(fpu->fprs, (freg_t *)&fpregs->fprs,
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index c15eadb..c0b6e74 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -184,11 +184,7 @@ struct thread_struct {
 	struct gs_cb *gs_cb;			/* Current guarded storage cb */
 	struct gs_cb *gs_bc_cb;			/* Broadcast guarded storage cb */
 	struct pgm_tdb trap_tdb;		/* Transaction abort diagnose block */
-	/*
-	 * Warning: 'fpu' is dynamically-sized. It *MUST* be at
-	 * the end.
-	 */
-	struct fpu fpu;			/* FP and VX register save area */
+	struct fpu fpu;				/* FP and VX register save area */
 };
 
 /* Flag to disable transactions. */
@@ -331,14 +327,36 @@ static inline unsigned long __extract_psw(void)
 	return (((unsigned long) reg1) << 32) | ((unsigned long) reg2);
 }
 
-static inline void local_mcck_enable(void)
+static inline unsigned long __local_mcck_save(void)
 {
-	__load_psw_mask(__extract_psw() | PSW_MASK_MCHECK);
+	unsigned long mask = __extract_psw();
+
+	__load_psw_mask(mask & ~PSW_MASK_MCHECK);
+	return mask & PSW_MASK_MCHECK;
+}
+
+#define local_mcck_save(mflags)			\
+do {						\
+	typecheck(unsigned long, mflags);	\
+	mflags = __local_mcck_save();		\
+} while (0)
+
+static inline void local_mcck_restore(unsigned long mflags)
+{
+	unsigned long mask = __extract_psw();
+
+	mask &= ~PSW_MASK_MCHECK;
+	__load_psw_mask(mask | mflags);
 }
 
 static inline void local_mcck_disable(void)
 {
-	__load_psw_mask(__extract_psw() & ~PSW_MASK_MCHECK);
+	__local_mcck_save();
+}
+
+static inline void local_mcck_enable(void)
+{
+	__load_psw_mask(__extract_psw() | PSW_MASK_MCHECK);
 }
 
 /*
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h
index df31643..03bcaa8 100644
--- a/arch/s390/include/asm/setup.h
+++ b/arch/s390/include/asm/setup.h
@@ -28,7 +28,6 @@
 #define MACHINE_FLAG_TOPOLOGY	BIT(10)
 #define MACHINE_FLAG_TE		BIT(11)
 #define MACHINE_FLAG_TLB_LC	BIT(12)
-#define MACHINE_FLAG_VX		BIT(13)
 #define MACHINE_FLAG_TLB_GUEST	BIT(14)
 #define MACHINE_FLAG_NX		BIT(15)
 #define MACHINE_FLAG_GS		BIT(16)
@@ -90,7 +89,6 @@ extern unsigned long mio_wb_bit_mask;
 #define MACHINE_HAS_TOPOLOGY	(S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY)
 #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_TLB_GUEST	(S390_lowcore.machine_flags & MACHINE_FLAG_TLB_GUEST)
 #define MACHINE_HAS_NX		(S390_lowcore.machine_flags & MACHINE_FLAG_NX)
 #define MACHINE_HAS_GS		(S390_lowcore.machine_flags & MACHINE_FLAG_GS)
diff --git a/arch/s390/include/asm/sysinfo.h b/arch/s390/include/asm/sysinfo.h
index ab1c631..edca5a7 100644
--- a/arch/s390/include/asm/sysinfo.h
+++ b/arch/s390/include/asm/sysinfo.h
@@ -40,6 +40,10 @@ struct sysinfo_1_1_1 {
 	unsigned int ncr;
 	unsigned int npr;
 	unsigned int ntr;
+	char reserved_3[4];
+	char model_var_cap[16];
+	unsigned int model_var_cap_rating;
+	unsigned int nvr;
 };
 
 struct sysinfo_1_2_1 {
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
index cecedd0..f8fc6c25 100644
--- a/arch/s390/kernel/compat_signal.c
+++ b/arch/s390/kernel/compat_signal.c
@@ -29,6 +29,7 @@
 #include <asm/lowcore.h>
 #include <asm/switch_to.h>
 #include <asm/vdso.h>
+#include <asm/fpu/api.h>
 #include "compat_linux.h"
 #include "compat_ptrace.h"
 #include "entry.h"
@@ -98,10 +99,6 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
 	if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_MASK_RI))
 		return -EINVAL;
 
-	/* Test the floating-point-control word. */
-	if (test_fp_ctl(user_sregs.fpregs.fpc))
-		return -EINVAL;
-
 	/* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
 	regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) |
 		(__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 |
@@ -137,7 +134,7 @@ static int save_sigregs_ext32(struct pt_regs *regs,
 		return -EFAULT;
 
 	/* Save vector registers to signal stack */
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		for (i = 0; i < __NUM_VXRS_LOW; i++)
 			vxrs[i] = current->thread.fpu.vxrs[i].low;
 		if (__copy_to_user(&sregs_ext->vxrs_low, vxrs,
@@ -165,7 +162,7 @@ static int restore_sigregs_ext32(struct pt_regs *regs,
 		*(__u32 *)&regs->gprs[i] = gprs_high[i];
 
 	/* Restore vector registers from signal stack */
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		if (__copy_from_user(vxrs, &sregs_ext->vxrs_low,
 				     sizeof(sregs_ext->vxrs_low)) ||
 		    __copy_from_user(current->thread.fpu.vxrs + __NUM_VXRS_LOW,
@@ -265,7 +262,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set,
 	 * the machine supports it
 	 */
 	frame_size = sizeof(*frame) - sizeof(frame->sregs_ext.__reserved);
-	if (!MACHINE_HAS_VX)
+	if (!cpu_has_vx())
 		frame_size -= sizeof(frame->sregs_ext.vxrs_low) +
 			      sizeof(frame->sregs_ext.vxrs_high);
 	frame = get_sigframe(&ksig->ka, regs, frame_size);
@@ -348,11 +345,12 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set,
 	 * the machine supports it
 	 */
 	uc_flags = UC_GPRS_HIGH;
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		uc_flags |= UC_VXRS;
-	} else
+	} else {
 		frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) +
 			      sizeof(frame->uc.uc_mcontext_ext.vxrs_high);
+	}
 	frame = get_sigframe(&ksig->ka, regs, frame_size);
 	if (frame == (void __user *) -1UL)
 		return -EFAULT;
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index 514fead..5c46c265 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -22,6 +22,7 @@
 #include <asm/ipl.h>
 #include <asm/sclp.h>
 #include <asm/maccess.h>
+#include <asm/fpu/api.h>
 
 #define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
 #define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
@@ -319,7 +320,7 @@ static void *fill_cpu_elf_notes(void *ptr, int cpu, struct save_area *sa)
 	ptr = nt_init(ptr, NT_S390_TODPREG, &sa->todpreg, sizeof(sa->todpreg));
 	ptr = nt_init(ptr, NT_S390_CTRS, &sa->ctrs, sizeof(sa->ctrs));
 	ptr = nt_init(ptr, NT_S390_PREFIX, &sa->prefix, sizeof(sa->prefix));
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		ptr = nt_init(ptr, NT_S390_VXRS_HIGH,
 			      &sa->vxrs_high, sizeof(sa->vxrs_high));
 		ptr = nt_init(ptr, NT_S390_VXRS_LOW,
@@ -343,7 +344,7 @@ static size_t get_cpu_elf_notes_size(void)
 	size +=  nt_size(NT_S390_TODPREG, sizeof(sa->todpreg));
 	size +=  nt_size(NT_S390_CTRS, sizeof(sa->ctrs));
 	size +=  nt_size(NT_S390_PREFIX, sizeof(sa->prefix));
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		size += nt_size(NT_S390_VXRS_HIGH, sizeof(sa->vxrs_high));
 		size += nt_size(NT_S390_VXRS_LOW, sizeof(sa->vxrs_low));
 	}
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index eb43e59..2345ea3 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -229,10 +229,8 @@ static __init void detect_machine_facilities(void)
 	}
 	if (test_facility(51))
 		S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC;
-	if (test_facility(129)) {
-		S390_lowcore.machine_flags |= MACHINE_FLAG_VX;
+	if (test_facility(129))
 		system_ctl_set_bit(0, CR0_VECTOR_BIT);
-	}
 	if (test_facility(130))
 		S390_lowcore.machine_flags |= MACHINE_FLAG_NX;
 	if (test_facility(133))
@@ -271,14 +269,6 @@ static inline void setup_access_registers(void)
 	restore_access_regs(acrs);
 }
 
-static int __init disable_vector_extension(char *str)
-{
-	S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
-	system_ctl_clear_bit(0, CR0_VECTOR_BIT);
-	return 0;
-}
-early_param("novx", disable_vector_extension);
-
 char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
 static void __init setup_boot_command_line(void)
 {
diff --git a/arch/s390/kernel/fpu.c b/arch/s390/kernel/fpu.c
index 4666b29..9e7c15f 100644
--- a/arch/s390/kernel/fpu.c
+++ b/arch/s390/kernel/fpu.c
@@ -24,7 +24,7 @@ void __kernel_fpu_begin(struct kernel_fpu *state, u32 flags)
 		/* Save floating point control */
 		asm volatile("stfpc %0" : "=Q" (state->fpc));
 
-	if (!MACHINE_HAS_VX) {
+	if (!cpu_has_vx()) {
 		if (flags & KERNEL_VXR_V0V7) {
 			/* Save floating-point registers */
 			asm volatile("std 0,%0" : "=Q" (state->fprs[0]));
@@ -106,7 +106,7 @@ void __kernel_fpu_end(struct kernel_fpu *state, u32 flags)
 		/* Restore floating-point controls */
 		asm volatile("lfpc %0" : : "Q" (state->fpc));
 
-	if (!MACHINE_HAS_VX) {
+	if (!cpu_has_vx()) {
 		if (flags & KERNEL_VXR_V0V7) {
 			/* Restore floating-point registers */
 			asm volatile("ld 0,%0" : : "Q" (state->fprs[0]));
@@ -177,11 +177,11 @@ EXPORT_SYMBOL(__kernel_fpu_end);
 
 void __load_fpu_regs(void)
 {
-	struct fpu *state = &current->thread.fpu;
 	unsigned long *regs = current->thread.fpu.regs;
+	struct fpu *state = &current->thread.fpu;
 
-	asm volatile("lfpc %0" : : "Q" (state->fpc));
-	if (likely(MACHINE_HAS_VX)) {
+	sfpc_safe(state->fpc);
+	if (likely(cpu_has_vx())) {
 		asm volatile("lgr	1,%0\n"
 			     "VLM	0,15,0,1\n"
 			     "VLM	16,31,256,1\n"
@@ -232,7 +232,7 @@ void save_fpu_regs(void)
 	regs = current->thread.fpu.regs;
 
 	asm volatile("stfpc %0" : "=Q" (state->fpc));
-	if (likely(MACHINE_HAS_VX)) {
+	if (likely(cpu_has_vx())) {
 		asm volatile("lgr	1,%0\n"
 			     "VSTM	0,15,0,1\n"
 			     "VSTM	16,31,256,1\n"
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c
index bb0d4d6..aa22ffc 100644
--- a/arch/s390/kernel/machine_kexec.c
+++ b/arch/s390/kernel/machine_kexec.c
@@ -91,7 +91,7 @@ static noinline void __machine_kdump(void *image)
 	}
 	/* Store status of the boot CPU */
 	mcesa = __va(S390_lowcore.mcesad & MCESA_ORIGIN_MASK);
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		save_vx_regs((__vector128 *) mcesa->vector_save_area);
 	if (MACHINE_HAS_GS) {
 		local_ctl_store(2, &cr2_old.reg);
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c
index 0daf0f1..9ad44c2 100644
--- a/arch/s390/kernel/nmi.c
+++ b/arch/s390/kernel/nmi.c
@@ -32,6 +32,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/pai.h>
 #include <asm/vx-insn.h>
+#include <asm/fpu/api.h>
 
 struct mcck_struct {
 	unsigned int kill_task : 1;
@@ -45,7 +46,7 @@ static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
 
 static inline int nmi_needs_mcesa(void)
 {
-	return MACHINE_HAS_VX || MACHINE_HAS_GS;
+	return cpu_has_vx() || MACHINE_HAS_GS;
 }
 
 /*
@@ -159,16 +160,17 @@ NOKPROBE_SYMBOL(s390_handle_damage);
 void s390_handle_mcck(void)
 {
 	struct mcck_struct mcck;
+	unsigned long mflags;
 
 	/*
 	 * Disable machine checks and get the current state of accumulated
 	 * machine checks. Afterwards delete the old state and enable machine
 	 * checks again.
 	 */
-	local_mcck_disable();
+	local_mcck_save(mflags);
 	mcck = *this_cpu_ptr(&cpu_mcck);
 	memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
-	local_mcck_enable();
+	local_mcck_restore(mflags);
 
 	if (mcck.channel_report)
 		crw_handle_channel_report();
@@ -234,7 +236,7 @@ static int notrace s390_validate_registers(union mci mci)
 	}
 
 	mcesa = __va(S390_lowcore.mcesad & MCESA_ORIGIN_MASK);
-	if (!MACHINE_HAS_VX) {
+	if (!cpu_has_vx()) {
 		/* Validate floating point registers */
 		asm volatile(
 			"	ld	0,0(%0)\n"
diff --git a/arch/s390/kernel/perf_regs.c b/arch/s390/kernel/perf_regs.c
index 6e9e5d5..3d93656 100644
--- a/arch/s390/kernel/perf_regs.c
+++ b/arch/s390/kernel/perf_regs.c
@@ -20,8 +20,10 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
 			return 0;
 
 		idx -= PERF_REG_S390_FP0;
-		fp = MACHINE_HAS_VX ? *(freg_t *)(current->thread.fpu.vxrs + idx)
-				    : current->thread.fpu.fprs[idx];
+		if (cpu_has_vx())
+			fp = *(freg_t *)(current->thread.fpu.vxrs + idx);
+		else
+			fp = current->thread.fpu.fprs[idx];
 		return fp.ui;
 	}
 
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 2580004..4e3b366 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -89,7 +89,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 	 */
 	save_fpu_regs();
 
-	memcpy(dst, src, arch_task_struct_size);
+	*dst = *src;
 	dst->thread.fpu.regs = dst->thread.fpu.fprs;
 
 	/*
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 0a999c8..65c1464 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -201,11 +201,8 @@ static int __init setup_hwcaps(void)
 	if (MACHINE_HAS_TE)
 		elf_hwcap |= HWCAP_TE;
 
-	/*
-	 * Vector extension can be disabled with the "novx" parameter.
-	 * Use MACHINE_HAS_VX instead of facility bit 129.
-	 */
-	if (MACHINE_HAS_VX) {
+	/* vector */
+	if (test_facility(129)) {
 		elf_hwcap |= HWCAP_VXRS;
 		if (test_facility(134))
 			elf_hwcap |= HWCAP_VXRS_BCD;
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 0464034..2e6754b 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -30,6 +30,7 @@
 #include <asm/switch_to.h>
 #include <asm/runtime_instr.h>
 #include <asm/facility.h>
+#include <asm/fpu/api.h>
 
 #include "entry.h"
 
@@ -254,7 +255,7 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
 		 * or the child->thread.fpu.vxrs array
 		 */
 		offset = addr - offsetof(struct user, regs.fp_regs.fprs);
-		if (MACHINE_HAS_VX)
+		if (cpu_has_vx())
 			tmp = *(addr_t *)
 			       ((addr_t) child->thread.fpu.vxrs + 2*offset);
 		else
@@ -392,8 +393,7 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
 		/*
 		 * floating point control reg. is in the thread structure
 		 */
-		if ((unsigned int) data != 0 ||
-		    test_fp_ctl(data >> (BITS_PER_LONG - 32)))
+		if ((unsigned int)data != 0)
 			return -EINVAL;
 		child->thread.fpu.fpc = data >> (BITS_PER_LONG - 32);
 
@@ -403,7 +403,7 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
 		 * or the child->thread.fpu.vxrs array
 		 */
 		offset = addr - offsetof(struct user, regs.fp_regs.fprs);
-		if (MACHINE_HAS_VX)
+		if (cpu_has_vx())
 			*(addr_t *)((addr_t)
 				child->thread.fpu.vxrs + 2*offset) = data;
 		else
@@ -630,7 +630,7 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
 		 * or the child->thread.fpu.vxrs array
 		 */
 		offset = addr - offsetof(struct compat_user, regs.fp_regs.fprs);
-		if (MACHINE_HAS_VX)
+		if (cpu_has_vx())
 			tmp = *(__u32 *)
 			       ((addr_t) child->thread.fpu.vxrs + 2*offset);
 		else
@@ -748,8 +748,6 @@ static int __poke_user_compat(struct task_struct *child,
 		/*
 		 * floating point control reg. is in the thread structure
 		 */
-		if (test_fp_ctl(tmp))
-			return -EINVAL;
 		child->thread.fpu.fpc = data;
 
 	} else if (addr < offsetof(struct compat_user, regs.fp_regs) + sizeof(s390_fp_regs)) {
@@ -758,7 +756,7 @@ static int __poke_user_compat(struct task_struct *child,
 		 * or the child->thread.fpu.vxrs array
 		 */
 		offset = addr - offsetof(struct compat_user, regs.fp_regs.fprs);
-		if (MACHINE_HAS_VX)
+		if (cpu_has_vx())
 			*(__u32 *)((addr_t)
 				child->thread.fpu.vxrs + 2*offset) = tmp;
 		else
@@ -914,7 +912,7 @@ static int s390_fpregs_set(struct task_struct *target,
 	if (target == current)
 		save_fpu_regs();
 
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		convert_vx_to_fp(fprs, target->thread.fpu.vxrs);
 	else
 		memcpy(&fprs, target->thread.fpu.fprs, sizeof(fprs));
@@ -926,7 +924,7 @@ static int s390_fpregs_set(struct task_struct *target,
 					0, offsetof(s390_fp_regs, fprs));
 		if (rc)
 			return rc;
-		if (ufpc[1] != 0 || test_fp_ctl(ufpc[0]))
+		if (ufpc[1] != 0)
 			return -EINVAL;
 		target->thread.fpu.fpc = ufpc[0];
 	}
@@ -937,7 +935,7 @@ static int s390_fpregs_set(struct task_struct *target,
 	if (rc)
 		return rc;
 
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		convert_fp_to_vx(target->thread.fpu.vxrs, fprs);
 	else
 		memcpy(target->thread.fpu.fprs, &fprs, sizeof(fprs));
@@ -988,7 +986,7 @@ static int s390_vxrs_low_get(struct task_struct *target,
 	__u64 vxrs[__NUM_VXRS_LOW];
 	int i;
 
-	if (!MACHINE_HAS_VX)
+	if (!cpu_has_vx())
 		return -ENODEV;
 	if (target == current)
 		save_fpu_regs();
@@ -1005,7 +1003,7 @@ static int s390_vxrs_low_set(struct task_struct *target,
 	__u64 vxrs[__NUM_VXRS_LOW];
 	int i, rc;
 
-	if (!MACHINE_HAS_VX)
+	if (!cpu_has_vx())
 		return -ENODEV;
 	if (target == current)
 		save_fpu_regs();
@@ -1025,7 +1023,7 @@ static int s390_vxrs_high_get(struct task_struct *target,
 			      const struct user_regset *regset,
 			      struct membuf to)
 {
-	if (!MACHINE_HAS_VX)
+	if (!cpu_has_vx())
 		return -ENODEV;
 	if (target == current)
 		save_fpu_regs();
@@ -1040,7 +1038,7 @@ static int s390_vxrs_high_set(struct task_struct *target,
 {
 	int rc;
 
-	if (!MACHINE_HAS_VX)
+	if (!cpu_has_vx())
 		return -ENODEV;
 	if (target == current)
 		save_fpu_regs();
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 5701356..d1f3b56 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -408,15 +408,15 @@ static void __init setup_lowcore(void)
 
 	lc->restart_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_DAT;
 	lc->restart_psw.addr = __pa(restart_int_handler);
-	lc->external_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK;
+	lc->external_new_psw.mask = PSW_KERNEL_BITS;
 	lc->external_new_psw.addr = (unsigned long) ext_int_handler;
-	lc->svc_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK;
+	lc->svc_new_psw.mask = PSW_KERNEL_BITS;
 	lc->svc_new_psw.addr = (unsigned long) system_call;
-	lc->program_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK;
+	lc->program_new_psw.mask = PSW_KERNEL_BITS;
 	lc->program_new_psw.addr = (unsigned long) pgm_check_handler;
 	lc->mcck_new_psw.mask = PSW_KERNEL_BITS;
 	lc->mcck_new_psw.addr = (unsigned long) mcck_int_handler;
-	lc->io_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK;
+	lc->io_new_psw.mask = PSW_KERNEL_BITS;
 	lc->io_new_psw.addr = (unsigned long) io_int_handler;
 	lc->clock_comparator = clock_comparator_max;
 	lc->current_task = (unsigned long)&init_task;
@@ -820,22 +820,6 @@ static void __init setup_randomness(void)
 }
 
 /*
- * Find the correct size for the task_struct. This depends on
- * the size of the struct fpu at the end of the thread_struct
- * which is embedded in the task_struct.
- */
-static void __init setup_task_size(void)
-{
-	int task_size = sizeof(struct task_struct);
-
-	if (!MACHINE_HAS_VX) {
-		task_size -= sizeof(__vector128) * __NUM_VXRS;
-		task_size += sizeof(freg_t) * __NUM_FPRS;
-	}
-	arch_task_struct_size = task_size;
-}
-
-/*
  * Issue diagnose 318 to set the control program name and
  * version codes.
  */
@@ -927,7 +911,6 @@ void __init setup_arch(char **cmdline_p)
 
 	os_info_init();
 	setup_ipl();
-	setup_task_size();
 	setup_control_program_code();
 
 	/* Do some memory reservations *before* memory is added to memblock */
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index 48e4680..43e9661 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -150,10 +150,6 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
 	if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_MASK_RI))
 		return -EINVAL;
 
-	/* Test the floating-point-control word. */
-	if (test_fp_ctl(user_sregs.fpregs.fpc))
-		return -EINVAL;
-
 	/* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
 	regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) |
 		(user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
@@ -183,7 +179,7 @@ static int save_sigregs_ext(struct pt_regs *regs,
 	int i;
 
 	/* Save vector registers to signal stack */
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		for (i = 0; i < __NUM_VXRS_LOW; i++)
 			vxrs[i] = current->thread.fpu.vxrs[i].low;
 		if (__copy_to_user(&sregs_ext->vxrs_low, vxrs,
@@ -203,7 +199,7 @@ static int restore_sigregs_ext(struct pt_regs *regs,
 	int i;
 
 	/* Restore vector registers from signal stack */
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		if (__copy_from_user(vxrs, &sregs_ext->vxrs_low,
 				     sizeof(sregs_ext->vxrs_low)) ||
 		    __copy_from_user(current->thread.fpu.vxrs + __NUM_VXRS_LOW,
@@ -301,7 +297,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
 	 * included in the signal frame on a 31-bit system.
 	 */
 	frame_size = sizeof(*frame) - sizeof(frame->sregs_ext);
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		frame_size += sizeof(frame->sregs_ext);
 	frame = get_sigframe(ka, regs, frame_size);
 	if (frame == (void __user *) -1UL)
@@ -378,7 +374,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
 	 * included in the signal frame on a 31-bit system.
 	 */
 	uc_flags = 0;
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		frame_size += sizeof(_sigregs_ext);
 		uc_flags |= UC_VXRS;
 	}
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index f7fcfff..c39d9f0 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -582,7 +582,7 @@ int smp_store_status(int cpu)
 	if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_STATUS_AT_ADDRESS,
 			      pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
 		return -EIO;
-	if (!MACHINE_HAS_VX && !MACHINE_HAS_GS)
+	if (!cpu_has_vx() && !MACHINE_HAS_GS)
 		return 0;
 	pa = lc->mcesad & MCESA_ORIGIN_MASK;
 	if (MACHINE_HAS_GS)
@@ -638,7 +638,7 @@ void __init smp_save_dump_ipl_cpu(void)
 	copy_oldmem_kernel(regs, __LC_FPREGS_SAVE_AREA, 512);
 	save_area_add_regs(sa, regs);
 	memblock_free(regs, 512);
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		save_area_add_vxrs(sa, boot_cpu_vector_save_area);
 }
 
@@ -671,7 +671,7 @@ void __init smp_save_dump_secondary_cpus(void)
 			panic("could not allocate memory for save area\n");
 		__pcpu_sigp_relax(addr, SIGP_STORE_STATUS_AT_ADDRESS, __pa(page));
 		save_area_add_regs(sa, page);
-		if (MACHINE_HAS_VX) {
+		if (cpu_has_vx()) {
 			__pcpu_sigp_relax(addr, SIGP_STORE_ADDITIONAL_STATUS, __pa(page));
 			save_area_add_vxrs(sa, page);
 		}
diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c
index b5e3643..f6f8f49 100644
--- a/arch/s390/kernel/sysinfo.c
+++ b/arch/s390/kernel/sysinfo.c
@@ -81,10 +81,12 @@ static bool convert_ext_name(unsigned char encoding, char *name, size_t len)
 
 static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
 {
+	bool has_var_cap;
 	int i;
 
 	if (stsi(info, 1, 1, 1))
 		return;
+	has_var_cap = !!info->model_var_cap[0];
 	EBCASC(info->manufacturer, sizeof(info->manufacturer));
 	EBCASC(info->type, sizeof(info->type));
 	EBCASC(info->model, sizeof(info->model));
@@ -93,6 +95,8 @@ static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
 	EBCASC(info->model_capacity, sizeof(info->model_capacity));
 	EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
 	EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
+	if (has_var_cap)
+		EBCASC(info->model_var_cap, sizeof(info->model_var_cap));
 	seq_printf(m, "Manufacturer:         %-16.16s\n", info->manufacturer);
 	seq_printf(m, "Type:                 %-4.4s\n", info->type);
 	if (info->lic)
@@ -120,12 +124,18 @@ static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
 		seq_printf(m, "Model Temp. Capacity: %-16.16s %08u\n",
 			   info->model_temp_cap,
 			   info->model_temp_cap_rating);
+	if (has_var_cap && info->model_var_cap_rating)
+		seq_printf(m, "Model Var. Capacity:  %-16.16s %08u\n",
+			   info->model_var_cap,
+			   info->model_var_cap_rating);
 	if (info->ncr)
 		seq_printf(m, "Nominal Cap. Rating:  %08u\n", info->ncr);
 	if (info->npr)
 		seq_printf(m, "Nominal Perm. Rating: %08u\n", info->npr);
 	if (info->ntr)
 		seq_printf(m, "Nominal Temp. Rating: %08u\n", info->ntr);
+	if (has_var_cap && info->nvr)
+		seq_printf(m, "Nominal Var. Rating:  %08u\n", info->nvr);
 	if (info->cai) {
 		seq_printf(m, "Capacity Adj. Ind.:   %d\n", info->cai);
 		seq_printf(m, "Capacity Ch. Reason:  %d\n", info->ccr);
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index cc3e3a0..46dac45 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -195,7 +195,7 @@ static void vector_exception(struct pt_regs *regs)
 {
 	int si_code, vic;
 
-	if (!MACHINE_HAS_VX) {
+	if (!cpu_has_vx()) {
 		do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation");
 		return;
 	}
@@ -288,6 +288,17 @@ static void __init test_monitor_call(void)
 
 void __init trap_init(void)
 {
+	unsigned long flags;
+	struct ctlreg cr0;
+
+	local_irq_save(flags);
+	cr0 = local_ctl_clear_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT);
+	psw_bits(S390_lowcore.external_new_psw).mcheck = 1;
+	psw_bits(S390_lowcore.program_new_psw).mcheck = 1;
+	psw_bits(S390_lowcore.svc_new_psw).mcheck = 1;
+	psw_bits(S390_lowcore.io_new_psw).mcheck = 1;
+	local_ctl_load(0, &cr0);
+	local_irq_restore(flags);
 	local_mcck_enable();
 	test_monitor_call();
 }
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index 2ae201e..e32ef44 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -52,6 +52,7 @@
 		SOFTIRQENTRY_TEXT
 		FTRACE_HOTPATCH_TRAMPOLINES_TEXT
 		*(.text.*_indirect_*)
+		*(.fixup)
 		*(.gnu.warning)
 		. = ALIGN(PAGE_SIZE);
 		_etext = .;		/* End of text section */
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index efaebba..fc4007c 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -639,7 +639,7 @@ static int __write_machine_check(struct kvm_vcpu *vcpu,
 	rc |= put_guest_lc(vcpu, mci.val, (u64 __user *) __LC_MCCK_CODE);
 
 	/* Register-save areas */
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		convert_vx_to_fp(fprs, (__vector128 *) vcpu->run->s.regs.vrs);
 		rc |= write_guest_lc(vcpu, __LC_FPREGS_SAVE_AREA, fprs, 128);
 	} else {
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 7aa0e66..acc81ca 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -618,7 +618,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 		r = MACHINE_HAS_ESOP;
 		break;
 	case KVM_CAP_S390_VECTOR_REGISTERS:
-		r = MACHINE_HAS_VX;
+		r = test_facility(129);
 		break;
 	case KVM_CAP_S390_RI:
 		r = test_facility(64);
@@ -767,7 +767,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
 		mutex_lock(&kvm->lock);
 		if (kvm->created_vcpus) {
 			r = -EBUSY;
-		} else if (MACHINE_HAS_VX) {
+		} else if (cpu_has_vx()) {
 			set_kvm_facility(kvm->arch.model.fac_mask, 129);
 			set_kvm_facility(kvm->arch.model.fac_list, 129);
 			if (test_facility(134)) {
@@ -3962,9 +3962,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 	if (test_kvm_facility(vcpu->kvm, 156))
 		vcpu->run->kvm_valid_regs |= KVM_SYNC_ETOKEN;
 	/* fprs can be synchronized via vrs, even if the guest has no vx. With
-	 * MACHINE_HAS_VX, (load|store)_fpu_regs() will work with vrs format.
+	 * cpu_has_vx(), (load|store)_fpu_regs() will work with vrs format.
 	 */
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		vcpu->run->kvm_valid_regs |= KVM_SYNC_VRS;
 	else
 		vcpu->run->kvm_valid_regs |= KVM_SYNC_FPRS;
@@ -4316,18 +4316,13 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 
 	vcpu_load(vcpu);
 
-	if (test_fp_ctl(fpu->fpc)) {
-		ret = -EINVAL;
-		goto out;
-	}
 	vcpu->run->s.regs.fpc = fpu->fpc;
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		convert_fp_to_vx((__vector128 *) vcpu->run->s.regs.vrs,
 				 (freg_t *) fpu->fprs);
 	else
 		memcpy(vcpu->run->s.regs.fprs, &fpu->fprs, sizeof(fpu->fprs));
 
-out:
 	vcpu_put(vcpu);
 	return ret;
 }
@@ -4336,9 +4331,7 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
 	vcpu_load(vcpu);
 
-	/* make sure we have the latest values */
-	save_fpu_regs();
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		convert_vx_to_fp((freg_t *) fpu->fprs,
 				 (__vector128 *) vcpu->run->s.regs.vrs);
 	else
@@ -4963,14 +4956,11 @@ static void sync_regs(struct kvm_vcpu *vcpu)
 	save_fpu_regs();
 	vcpu->arch.host_fpregs.fpc = current->thread.fpu.fpc;
 	vcpu->arch.host_fpregs.regs = current->thread.fpu.regs;
-	if (MACHINE_HAS_VX)
+	if (cpu_has_vx())
 		current->thread.fpu.regs = vcpu->run->s.regs.vrs;
 	else
 		current->thread.fpu.regs = vcpu->run->s.regs.fprs;
 	current->thread.fpu.fpc = vcpu->run->s.regs.fpc;
-	if (test_fp_ctl(current->thread.fpu.fpc))
-		/* User space provided an invalid FPC, let's clear it */
-		current->thread.fpu.fpc = 0;
 
 	/* Sync fmt2 only data */
 	if (likely(!kvm_s390_pv_cpu_is_protected(vcpu))) {
@@ -5145,7 +5135,7 @@ int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa)
 		gpa -= __LC_FPREGS_SAVE_AREA;
 
 	/* manually convert vector registers if necessary */
-	if (MACHINE_HAS_VX) {
+	if (cpu_has_vx()) {
 		convert_vx_to_fp(fprs, (__vector128 *) vcpu->run->s.regs.vrs);
 		rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA,
 				     fprs, 128);
diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c
index 7231bf9..2848e3f 100644
--- a/arch/s390/lib/test_unwind.c
+++ b/arch/s390/lib/test_unwind.c
@@ -350,15 +350,15 @@ static noinline int unwindme_func3(struct unwindme *u)
 /* This function must appear in the backtrace. */
 static noinline int unwindme_func2(struct unwindme *u)
 {
-	unsigned long flags;
+	unsigned long flags, mflags;
 	int rc;
 
 	if (u->flags & UWM_SWITCH_STACK) {
 		local_irq_save(flags);
-		local_mcck_disable();
+		local_mcck_save(mflags);
 		rc = call_on_stack(1, S390_lowcore.nodat_stack,
 				   int, unwindme_func3, struct unwindme *, u);
-		local_mcck_enable();
+		local_mcck_restore(mflags);
 		local_irq_restore(flags);
 		return rc;
 	} else {
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 5cb9294..9942292 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -125,32 +125,23 @@ static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
 
 static inline pgste_t pgste_get_lock(pte_t *ptep)
 {
-	unsigned long new = 0;
+	unsigned long value = 0;
 #ifdef CONFIG_PGSTE
-	unsigned long old;
+	unsigned long *ptr = (unsigned long *)(ptep + PTRS_PER_PTE);
 
-	asm(
-		"	lg	%0,%2\n"
-		"0:	lgr	%1,%0\n"
-		"	nihh	%0,0xff7f\n"	/* clear PCL bit in old */
-		"	oihh	%1,0x0080\n"	/* set PCL bit in new */
-		"	csg	%0,%1,%2\n"
-		"	jl	0b\n"
-		: "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE])
-		: "Q" (ptep[PTRS_PER_PTE]) : "cc", "memory");
+	do {
+		value = __atomic64_or_barrier(PGSTE_PCL_BIT, ptr);
+	} while (value & PGSTE_PCL_BIT);
+	value |= PGSTE_PCL_BIT;
 #endif
-	return __pgste(new);
+	return __pgste(value);
 }
 
 static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
 {
 #ifdef CONFIG_PGSTE
-	asm(
-		"	nihh	%1,0xff7f\n"	/* clear PCL bit */
-		"	stg	%1,%0\n"
-		: "=Q" (ptep[PTRS_PER_PTE])
-		: "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE])
-		: "cc", "memory");
+	barrier();
+	WRITE_ONCE(*(unsigned long *)(ptep + PTRS_PER_PTE), pgste_val(pgste) & ~PGSTE_PCL_BIT);
 #endif
 }
 
diff --git a/arch/s390/tools/gen_facilities.c b/arch/s390/tools/gen_facilities.c
index cb0aff5..68580cbe 100644
--- a/arch/s390/tools/gen_facilities.c
+++ b/arch/s390/tools/gen_facilities.c
@@ -46,6 +46,7 @@ static struct facility_def facility_defs[] = {
 #endif
 #ifdef CONFIG_HAVE_MARCH_Z13_FEATURES
 			53, /* load-and-zero-rightmost-byte, etc. */
+			129, /* vector */
 #endif
 #ifdef CONFIG_HAVE_MARCH_Z14_FEATURES
 			58, /* miscellaneous-instruction-extension 2 */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index f8b04ce..64ed55c 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -219,16 +219,16 @@ EXPORT_SYMBOL_GPL(chsc_sadc);
 
 static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
 {
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	if (sch->driver && sch->driver->chp_event)
 		if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
 			goto out_unreg;
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	return 0;
 
 out_unreg:
 	sch->lpm = 0;
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	css_schedule_eval(sch->schid);
 	return 0;
 }
@@ -258,10 +258,10 @@ void chsc_chp_offline(struct chp_id chpid)
 
 static int __s390_process_res_acc(struct subchannel *sch, void *data)
 {
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	if (sch->driver && sch->driver->chp_event)
 		sch->driver->chp_event(sch, data, CHP_ONLINE);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 
 	return 0;
 }
@@ -292,10 +292,10 @@ static void s390_process_res_acc(struct chp_link *link)
 
 static int process_fces_event(struct subchannel *sch, void *data)
 {
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	if (sch->driver && sch->driver->chp_event)
 		sch->driver->chp_event(sch, data, CHP_FCES_EVENT);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	return 0;
 }
 
@@ -769,11 +769,11 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
 
 	memset(&link, 0, sizeof(struct chp_link));
 	link.chpid = chpid;
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 	if (sch->driver && sch->driver->chp_event)
 		sch->driver->chp_event(sch, &link,
 				       on ? CHP_VARY_ON : CHP_VARY_OFF);
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 }
 
 static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index 180ab89..902237d 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -211,10 +211,10 @@ static int chsc_async(struct chsc_async_area *chsc_area,
 
 	chsc_area->header.key = PAGE_DEFAULT_KEY >> 4;
 	while ((sch = chsc_get_next_subchannel(sch))) {
-		spin_lock(sch->lock);
+		spin_lock(&sch->lock);
 		private = dev_get_drvdata(&sch->dev);
 		if (private->request) {
-			spin_unlock(sch->lock);
+			spin_unlock(&sch->lock);
 			ret = -EBUSY;
 			continue;
 		}
@@ -239,7 +239,7 @@ static int chsc_async(struct chsc_async_area *chsc_area,
 		default:
 			ret = -ENODEV;
 		}
-		spin_unlock(sch->lock);
+		spin_unlock(&sch->lock);
 		CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n",
 			 sch->schid.ssid, sch->schid.sch_no, cc);
 		if (ret == -EINPROGRESS)
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 6127add..a5736b7 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -546,7 +546,7 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy)
 		return IRQ_HANDLED;
 	}
 	sch = phys_to_virt(tpi_info->intparm);
-	spin_lock(sch->lock);
+	spin_lock(&sch->lock);
 	/* Store interrupt response block to lowcore. */
 	if (tsch(tpi_info->schid, irb) == 0) {
 		/* Keep subchannel information word up to date. */
@@ -558,7 +558,7 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy)
 			inc_irq_stat(IRQIO_CIO);
 	} else
 		inc_irq_stat(IRQIO_CIO);
-	spin_unlock(sch->lock);
+	spin_unlock(&sch->lock);
 
 	return IRQ_HANDLED;
 }
@@ -663,7 +663,7 @@ struct subchannel *cio_probe_console(void)
 	if (IS_ERR(sch))
 		return sch;
 
-	lockdep_set_class(sch->lock, &console_sch_key);
+	lockdep_set_class(&sch->lock, &console_sch_key);
 	isc_register(CONSOLE_ISC);
 	sch->config.isc = CONSOLE_ISC;
 	sch->config.intparm = (u32)virt_to_phys(sch);
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index fa8df50..a9057a5 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -83,7 +83,7 @@ enum sch_todo {
 /* subchannel data structure used by I/O subroutines */
 struct subchannel {
 	struct subchannel_id schid;
-	spinlock_t *lock;	/* subchannel lock */
+	spinlock_t lock;	/* subchannel lock */
 	struct mutex reg_mutex;
 	enum {
 		SUBCHANNEL_TYPE_IO = 0,
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 3ff46fc..28a88ed 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -148,16 +148,10 @@ int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
 
 static void css_sch_todo(struct work_struct *work);
 
-static int css_sch_create_locks(struct subchannel *sch)
+static void css_sch_create_locks(struct subchannel *sch)
 {
-	sch->lock = kmalloc(sizeof(*sch->lock), GFP_KERNEL);
-	if (!sch->lock)
-		return -ENOMEM;
-
-	spin_lock_init(sch->lock);
+	spin_lock_init(&sch->lock);
 	mutex_init(&sch->reg_mutex);
-
-	return 0;
 }
 
 static void css_subchannel_release(struct device *dev)
@@ -167,7 +161,6 @@ static void css_subchannel_release(struct device *dev)
 	sch->config.intparm = 0;
 	cio_commit_config(sch);
 	kfree(sch->driver_override);
-	kfree(sch->lock);
 	kfree(sch);
 }
 
@@ -219,9 +212,7 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid,
 	sch->schib = *schib;
 	sch->st = schib->pmcw.st;
 
-	ret = css_sch_create_locks(sch);
-	if (ret)
-		goto err;
+	css_sch_create_locks(sch);
 
 	INIT_WORK(&sch->todo_work, css_sch_todo);
 	sch->dev.release = &css_subchannel_release;
@@ -233,19 +224,17 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid,
 	 */
 	ret = dma_set_coherent_mask(&sch->dev, DMA_BIT_MASK(31));
 	if (ret)
-		goto err_lock;
+		goto err;
 	/*
 	 * But we don't have such restrictions imposed on the stuff that
 	 * is handled by the streaming API.
 	 */
 	ret = dma_set_mask(&sch->dev, DMA_BIT_MASK(64));
 	if (ret)
-		goto err_lock;
+		goto err;
 
 	return sch;
 
-err_lock:
-	kfree(sch->lock);
 err:
 	kfree(sch);
 	return ERR_PTR(ret);
@@ -604,12 +593,12 @@ static void css_sch_todo(struct work_struct *work)
 
 	sch = container_of(work, struct subchannel, todo_work);
 	/* Find out todo. */
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	todo = sch->todo;
 	CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid,
 		      sch->schid.sch_no, todo);
 	sch->todo = SCH_TODO_NOTHING;
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	/* Perform todo. */
 	switch (todo) {
 	case SCH_TODO_NOTHING:
@@ -617,9 +606,9 @@ static void css_sch_todo(struct work_struct *work)
 	case SCH_TODO_EVAL:
 		ret = css_evaluate_known_subchannel(sch, 1);
 		if (ret == -EAGAIN) {
-			spin_lock_irq(sch->lock);
+			spin_lock_irq(&sch->lock);
 			css_sched_sch_todo(sch, todo);
-			spin_unlock_irq(sch->lock);
+			spin_unlock_irq(&sch->lock);
 		}
 		break;
 	case SCH_TODO_UNREG:
@@ -1028,12 +1017,7 @@ static int __init setup_css(int nr)
 	css->pseudo_subchannel->dev.parent = &css->device;
 	css->pseudo_subchannel->dev.release = css_subchannel_release;
 	mutex_init(&css->pseudo_subchannel->reg_mutex);
-	ret = css_sch_create_locks(css->pseudo_subchannel);
-	if (ret) {
-		kfree(css->pseudo_subchannel);
-		device_unregister(&css->device);
-		goto out_err;
-	}
+	css_sch_create_locks(css->pseudo_subchannel);
 
 	dev_set_name(&css->pseudo_subchannel->dev, "defunct");
 	ret = device_register(&css->pseudo_subchannel->dev);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 4ca5adc..0cfb179 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -748,7 +748,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
 	mutex_init(&cdev->reg_mutex);
 
 	atomic_set(&priv->onoff, 0);
-	cdev->ccwlock = sch->lock;
+	cdev->ccwlock = &sch->lock;
 	cdev->dev.parent = &sch->dev;
 	cdev->dev.release = ccw_device_release;
 	cdev->dev.bus = &ccw_bus_type;
@@ -764,9 +764,9 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
 		goto out_put;
 	}
 	priv->flags.initialized = 1;
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	sch_set_cdev(sch, cdev);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	return 0;
 
 out_put:
@@ -851,9 +851,9 @@ static void io_subchannel_register(struct ccw_device *cdev)
 		CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
 			      cdev->private->dev_id.ssid,
 			      cdev->private->dev_id.devno, ret);
-		spin_lock_irqsave(sch->lock, flags);
+		spin_lock_irqsave(&sch->lock, flags);
 		sch_set_cdev(sch, NULL);
-		spin_unlock_irqrestore(sch->lock, flags);
+		spin_unlock_irqrestore(&sch->lock, flags);
 		mutex_unlock(&cdev->reg_mutex);
 		/* Release initial device reference. */
 		put_device(&cdev->dev);
@@ -904,9 +904,9 @@ static void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 	atomic_inc(&ccw_device_init_count);
 
 	/* Start async. device sensing. */
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	ccw_device_recognition(cdev);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 }
 
 static int ccw_device_move_to_sch(struct ccw_device *cdev,
@@ -921,12 +921,12 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
 		return -ENODEV;
 
 	if (!sch_is_pseudo_sch(old_sch)) {
-		spin_lock_irq(old_sch->lock);
+		spin_lock_irq(&old_sch->lock);
 		old_enabled = old_sch->schib.pmcw.ena;
 		rc = 0;
 		if (old_enabled)
 			rc = cio_disable_subchannel(old_sch);
-		spin_unlock_irq(old_sch->lock);
+		spin_unlock_irq(&old_sch->lock);
 		if (rc == -EBUSY) {
 			/* Release child reference for new parent. */
 			put_device(&sch->dev);
@@ -944,9 +944,9 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
 			      sch->schib.pmcw.dev, rc);
 		if (old_enabled) {
 			/* Try to re-enable the old subchannel. */
-			spin_lock_irq(old_sch->lock);
+			spin_lock_irq(&old_sch->lock);
 			cio_enable_subchannel(old_sch, (u32)virt_to_phys(old_sch));
-			spin_unlock_irq(old_sch->lock);
+			spin_unlock_irq(&old_sch->lock);
 		}
 		/* Release child reference for new parent. */
 		put_device(&sch->dev);
@@ -954,19 +954,19 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
 	}
 	/* Clean up old subchannel. */
 	if (!sch_is_pseudo_sch(old_sch)) {
-		spin_lock_irq(old_sch->lock);
+		spin_lock_irq(&old_sch->lock);
 		sch_set_cdev(old_sch, NULL);
-		spin_unlock_irq(old_sch->lock);
+		spin_unlock_irq(&old_sch->lock);
 		css_schedule_eval(old_sch->schid);
 	}
 	/* Release child reference for old parent. */
 	put_device(&old_sch->dev);
 	/* Initialize new subchannel. */
-	spin_lock_irq(sch->lock);
-	cdev->ccwlock = sch->lock;
+	spin_lock_irq(&sch->lock);
+	cdev->ccwlock = &sch->lock;
 	if (!sch_is_pseudo_sch(sch))
 		sch_set_cdev(sch, cdev);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	if (!sch_is_pseudo_sch(sch))
 		css_update_ssd_info(sch);
 	return 0;
@@ -1077,9 +1077,9 @@ static int io_subchannel_probe(struct subchannel *sch)
 	return 0;
 
 out_schedule:
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	css_sched_sch_todo(sch, SCH_TODO_UNREG);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	return 0;
 }
 
@@ -1093,10 +1093,10 @@ static void io_subchannel_remove(struct subchannel *sch)
 		goto out_free;
 
 	ccw_device_unregister(cdev);
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	sch_set_cdev(sch, NULL);
 	set_io_private(sch, NULL);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 out_free:
 	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
 			  io_priv->dma_area, io_priv->dma_area_dma);
@@ -1203,7 +1203,7 @@ static void io_subchannel_quiesce(struct subchannel *sch)
 	struct ccw_device *cdev;
 	int ret;
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	cdev = sch_get_cdev(sch);
 	if (cio_is_console(sch->schid))
 		goto out_unlock;
@@ -1220,15 +1220,15 @@ static void io_subchannel_quiesce(struct subchannel *sch)
 		ret = ccw_device_cancel_halt_clear(cdev);
 		if (ret == -EBUSY) {
 			ccw_device_set_timeout(cdev, HZ/10);
-			spin_unlock_irq(sch->lock);
+			spin_unlock_irq(&sch->lock);
 			wait_event(cdev->private->wait_q,
 				   cdev->private->state != DEV_STATE_QUIESCE);
-			spin_lock_irq(sch->lock);
+			spin_lock_irq(&sch->lock);
 		}
 		ret = cio_disable_subchannel(sch);
 	}
 out_unlock:
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 }
 
 static void io_subchannel_shutdown(struct subchannel *sch)
@@ -1439,7 +1439,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
 	enum io_sch_action action;
 	int rc = -EAGAIN;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 	if (!device_is_registered(&sch->dev))
 		goto out_unlock;
 	if (work_pending(&sch->todo_work))
@@ -1492,7 +1492,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
 	default:
 		break;
 	}
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 	/* All other actions require process context. */
 	if (!process)
 		goto out;
@@ -1507,9 +1507,9 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
 		break;
 	case IO_SCH_UNREG_CDEV:
 	case IO_SCH_UNREG_ATTACH:
-		spin_lock_irqsave(sch->lock, flags);
+		spin_lock_irqsave(&sch->lock, flags);
 		sch_set_cdev(sch, NULL);
-		spin_unlock_irqrestore(sch->lock, flags);
+		spin_unlock_irqrestore(&sch->lock, flags);
 		/* Unregister ccw device. */
 		ccw_device_unregister(cdev);
 		break;
@@ -1538,9 +1538,9 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
 			put_device(&cdev->dev);
 			goto out;
 		}
-		spin_lock_irqsave(sch->lock, flags);
+		spin_lock_irqsave(&sch->lock, flags);
 		ccw_device_trigger_reprobe(cdev);
-		spin_unlock_irqrestore(sch->lock, flags);
+		spin_unlock_irqrestore(&sch->lock, flags);
 		/* Release reference from get_ccwdev_by_dev_id() */
 		put_device(&cdev->dev);
 		break;
@@ -1550,7 +1550,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
 	return 0;
 
 out_unlock:
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 out:
 	return rc;
 }
@@ -1846,9 +1846,9 @@ static void ccw_device_todo(struct work_struct *work)
 			css_schedule_eval(sch->schid);
 		fallthrough;
 	case CDEV_TODO_UNREG:
-		spin_lock_irq(sch->lock);
+		spin_lock_irq(&sch->lock);
 		sch_set_cdev(sch, NULL);
-		spin_unlock_irq(sch->lock);
+		spin_unlock_irq(&sch->lock);
 		ccw_device_unregister(cdev);
 		break;
 	default:
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 3862961..ad90045 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -698,29 +698,29 @@ int ccw_device_stlck(struct ccw_device *cdev)
 		return -ENOMEM;
 	init_completion(&data.done);
 	data.rc = -EIO;
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	rc = cio_enable_subchannel(sch, (u32)virt_to_phys(sch));
 	if (rc)
 		goto out_unlock;
 	/* Perform operation. */
 	cdev->private->state = DEV_STATE_STEAL_LOCK;
 	ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	/* Wait for operation to finish. */
 	if (wait_for_completion_interruptible(&data.done)) {
 		/* Got a signal. */
-		spin_lock_irq(sch->lock);
+		spin_lock_irq(&sch->lock);
 		ccw_request_cancel(cdev);
-		spin_unlock_irq(sch->lock);
+		spin_unlock_irq(&sch->lock);
 		wait_for_completion(&data.done);
 	}
 	rc = data.rc;
 	/* Check results. */
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	cio_disable_subchannel(sch);
 	cdev->private->state = DEV_STATE_BOXED;
 out_unlock:
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	kfree(buffer);
 
 	return rc;
diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c
index 826364d..1caedf9 100644
--- a/drivers/s390/cio/eadm_sch.c
+++ b/drivers/s390/cio/eadm_sch.c
@@ -101,12 +101,12 @@ static void eadm_subchannel_timeout(struct timer_list *t)
 	struct eadm_private *private = from_timer(private, t, timer);
 	struct subchannel *sch = private->sch;
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	EADM_LOG(1, "timeout");
 	EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid));
 	if (eadm_subchannel_clear(sch))
 		EADM_LOG(0, "clear failed");
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 }
 
 static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
@@ -163,16 +163,16 @@ static struct subchannel *eadm_get_idle_sch(void)
 	spin_lock_irqsave(&list_lock, flags);
 	list_for_each_entry(private, &eadm_list, head) {
 		sch = private->sch;
-		spin_lock(sch->lock);
+		spin_lock(&sch->lock);
 		if (private->state == EADM_IDLE) {
 			private->state = EADM_BUSY;
 			list_move_tail(&private->head, &eadm_list);
-			spin_unlock(sch->lock);
+			spin_unlock(&sch->lock);
 			spin_unlock_irqrestore(&list_lock, flags);
 
 			return sch;
 		}
-		spin_unlock(sch->lock);
+		spin_unlock(&sch->lock);
 	}
 	spin_unlock_irqrestore(&list_lock, flags);
 
@@ -190,7 +190,7 @@ int eadm_start_aob(struct aob *aob)
 	if (!sch)
 		return -EBUSY;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 	eadm_subchannel_set_timeout(sch, EADM_TIMEOUT);
 	ret = eadm_subchannel_start(sch, aob);
 	if (!ret)
@@ -203,7 +203,7 @@ int eadm_start_aob(struct aob *aob)
 	css_sched_sch_todo(sch, SCH_TODO_EVAL);
 
 out_unlock:
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 
 	return ret;
 }
@@ -221,7 +221,7 @@ static int eadm_subchannel_probe(struct subchannel *sch)
 	INIT_LIST_HEAD(&private->head);
 	timer_setup(&private->timer, eadm_subchannel_timeout, 0);
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	set_eadm_private(sch, private);
 	private->state = EADM_IDLE;
 	private->sch = sch;
@@ -229,11 +229,11 @@ static int eadm_subchannel_probe(struct subchannel *sch)
 	ret = cio_enable_subchannel(sch, (u32)virt_to_phys(sch));
 	if (ret) {
 		set_eadm_private(sch, NULL);
-		spin_unlock_irq(sch->lock);
+		spin_unlock_irq(&sch->lock);
 		kfree(private);
 		goto out;
 	}
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 
 	spin_lock_irq(&list_lock);
 	list_add(&private->head, &eadm_list);
@@ -248,7 +248,7 @@ static void eadm_quiesce(struct subchannel *sch)
 	DECLARE_COMPLETION_ONSTACK(completion);
 	int ret;
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	if (private->state != EADM_BUSY)
 		goto disable;
 
@@ -256,11 +256,11 @@ static void eadm_quiesce(struct subchannel *sch)
 		goto disable;
 
 	private->completion = &completion;
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 
 	wait_for_completion_io(&completion);
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	private->completion = NULL;
 
 disable:
@@ -269,7 +269,7 @@ static void eadm_quiesce(struct subchannel *sch)
 		ret = cio_disable_subchannel(sch);
 	} while (ret == -EBUSY);
 
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 }
 
 static void eadm_subchannel_remove(struct subchannel *sch)
@@ -282,9 +282,9 @@ static void eadm_subchannel_remove(struct subchannel *sch)
 
 	eadm_quiesce(sch);
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	set_eadm_private(sch, NULL);
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 
 	kfree(private);
 }
@@ -309,7 +309,7 @@ static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
 	struct eadm_private *private;
 	unsigned long flags;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 	if (!device_is_registered(&sch->dev))
 		goto out_unlock;
 
@@ -325,7 +325,7 @@ static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
 		private->state = EADM_IDLE;
 
 out_unlock:
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 
 	return 0;
 }
diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
index bfb35cf..8ad4903 100644
--- a/drivers/s390/cio/vfio_ccw_drv.c
+++ b/drivers/s390/cio/vfio_ccw_drv.c
@@ -65,14 +65,14 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
 		 * cancel/halt/clear completion.
 		 */
 		private->completion = &completion;
-		spin_unlock_irq(sch->lock);
+		spin_unlock_irq(&sch->lock);
 
 		if (ret == -EBUSY)
 			wait_for_completion_timeout(&completion, 3*HZ);
 
 		private->completion = NULL;
 		flush_workqueue(vfio_ccw_work_q);
-		spin_lock_irq(sch->lock);
+		spin_lock_irq(&sch->lock);
 		ret = cio_disable_subchannel(sch);
 	} while (ret == -EBUSY);
 
@@ -249,7 +249,7 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process)
 	unsigned long flags;
 	int rc = -EAGAIN;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 	if (!device_is_registered(&sch->dev))
 		goto out_unlock;
 
@@ -264,7 +264,7 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process)
 	}
 
 out_unlock:
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 
 	return rc;
 }
diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c
index 757b731..09877b4 100644
--- a/drivers/s390/cio/vfio_ccw_fsm.c
+++ b/drivers/s390/cio/vfio_ccw_fsm.c
@@ -25,7 +25,7 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
 	unsigned long flags;
 	int ret;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 
 	orb = cp_get_orb(&private->cp, sch);
 	if (!orb) {
@@ -72,7 +72,7 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
 		ret = ccode;
 	}
 out:
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 	return ret;
 }
 
@@ -83,7 +83,7 @@ static int fsm_do_halt(struct vfio_ccw_private *private)
 	int ccode;
 	int ret;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 
 	VFIO_CCW_TRACE_EVENT(2, "haltIO");
 	VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
@@ -111,7 +111,7 @@ static int fsm_do_halt(struct vfio_ccw_private *private)
 	default:
 		ret = ccode;
 	}
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 	return ret;
 }
 
@@ -122,7 +122,7 @@ static int fsm_do_clear(struct vfio_ccw_private *private)
 	int ccode;
 	int ret;
 
-	spin_lock_irqsave(sch->lock, flags);
+	spin_lock_irqsave(&sch->lock, flags);
 
 	VFIO_CCW_TRACE_EVENT(2, "clearIO");
 	VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
@@ -147,7 +147,7 @@ static int fsm_do_clear(struct vfio_ccw_private *private)
 	default:
 		ret = ccode;
 	}
-	spin_unlock_irqrestore(sch->lock, flags);
+	spin_unlock_irqrestore(&sch->lock, flags);
 	return ret;
 }
 
@@ -376,18 +376,18 @@ static void fsm_open(struct vfio_ccw_private *private,
 	struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
 	int ret;
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 	sch->isc = VFIO_CCW_ISC;
 	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
 	if (ret)
 		goto err_unlock;
 
 	private->state = VFIO_CCW_STATE_IDLE;
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	return;
 
 err_unlock:
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
 }
 
@@ -397,7 +397,7 @@ static void fsm_close(struct vfio_ccw_private *private,
 	struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
 	int ret;
 
-	spin_lock_irq(sch->lock);
+	spin_lock_irq(&sch->lock);
 
 	if (!sch->schib.pmcw.ena)
 		goto err_unlock;
@@ -409,12 +409,12 @@ static void fsm_close(struct vfio_ccw_private *private,
 		goto err_unlock;
 
 	private->state = VFIO_CCW_STATE_STANDBY;
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	cp_free(&private->cp);
 	return;
 
 err_unlock:
-	spin_unlock_irq(sch->lock);
+	spin_unlock_irq(&sch->lock);
 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
 }
 
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 5dd3315..f46dd6a 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -357,13 +357,12 @@ EXPORT_SYMBOL(ap_test_config_ctrl_domain);
  *	   -1 invalid APQN, TAPQ error or AP queue status which
  *	      indicates there is no APQN.
  */
-static int ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
-			 int *q_depth, int *q_ml, bool *q_decfg, bool *q_cstop)
+static int ap_queue_info(ap_qid_t qid, struct ap_tapq_hwinfo *hwinfo,
+			 bool *decfg, bool *cstop)
 {
 	struct ap_queue_status status;
-	struct ap_tapq_gr2 tapq_info;
 
-	tapq_info.value = 0;
+	hwinfo->value = 0;
 
 	/* make sure we don't run into a specifiation exception */
 	if (AP_QID_CARD(qid) > ap_max_adapter_id ||
@@ -371,7 +370,7 @@ static int ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
 		return -1;
 
 	/* call TAPQ on this APQN */
-	status = ap_test_queue(qid, ap_apft_available(), &tapq_info);
+	status = ap_test_queue(qid, ap_apft_available(), hwinfo);
 
 	switch (status.response_code) {
 	case AP_RESPONSE_NORMAL:
@@ -389,15 +388,11 @@ static int ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
 	}
 
 	/* There should be at least one of the mode bits set */
-	if (WARN_ON_ONCE(!tapq_info.value))
+	if (WARN_ON_ONCE(!hwinfo->value))
 		return 0;
 
-	*q_type = tapq_info.at;
-	*q_fac = tapq_info.fac;
-	*q_depth = tapq_info.qd;
-	*q_ml = tapq_info.ml;
-	*q_decfg = status.response_code == AP_RESPONSE_DECONFIGURED;
-	*q_cstop = status.response_code == AP_RESPONSE_CHECKSTOPPED;
+	*decfg = status.response_code == AP_RESPONSE_DECONFIGURED;
+	*cstop = status.response_code == AP_RESPONSE_CHECKSTOPPED;
 
 	return 1;
 }
@@ -642,11 +637,11 @@ static int ap_uevent(const struct device *dev, struct kobj_uevent_env *env)
 			return rc;
 
 		/* Add MODE=<accel|cca|ep11> */
-		if (ap_test_bit(&ac->functions, AP_FUNC_ACCEL))
+		if (ac->hwinfo.accel)
 			rc = add_uevent_var(env, "MODE=accel");
-		else if (ap_test_bit(&ac->functions, AP_FUNC_COPRO))
+		else if (ac->hwinfo.cca)
 			rc = add_uevent_var(env, "MODE=cca");
-		else if (ap_test_bit(&ac->functions, AP_FUNC_EP11))
+		else if (ac->hwinfo.ep11)
 			rc = add_uevent_var(env, "MODE=ep11");
 		if (rc)
 			return rc;
@@ -654,11 +649,11 @@ static int ap_uevent(const struct device *dev, struct kobj_uevent_env *env)
 		struct ap_queue *aq = to_ap_queue(&ap_dev->device);
 
 		/* Add MODE=<accel|cca|ep11> */
-		if (ap_test_bit(&aq->card->functions, AP_FUNC_ACCEL))
+		if (aq->card->hwinfo.accel)
 			rc = add_uevent_var(env, "MODE=accel");
-		else if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO))
+		else if (aq->card->hwinfo.cca)
 			rc = add_uevent_var(env, "MODE=cca");
-		else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11))
+		else if (aq->card->hwinfo.ep11)
 			rc = add_uevent_var(env, "MODE=ep11");
 		if (rc)
 			return rc;
@@ -1799,12 +1794,12 @@ static inline void ap_scan_rm_card_dev_and_queue_devs(struct ap_card *ac)
  */
 static inline void ap_scan_domains(struct ap_card *ac)
 {
-	int rc, dom, depth, type, ml;
+	struct ap_tapq_hwinfo hwinfo;
 	bool decfg, chkstop;
 	struct ap_queue *aq;
 	struct device *dev;
-	unsigned int func;
 	ap_qid_t qid;
+	int rc, dom;
 
 	/*
 	 * Go through the configuration for the domains and compare them
@@ -1827,8 +1822,7 @@ static inline void ap_scan_domains(struct ap_card *ac)
 			goto put_dev_and_continue;
 		}
 		/* domain is valid, get info from this APQN */
-		rc = ap_queue_info(qid, &type, &func, &depth,
-				   &ml, &decfg, &chkstop);
+		rc = ap_queue_info(qid, &hwinfo, &decfg, &chkstop);
 		switch (rc) {
 		case -1:
 			if (dev) {
@@ -1853,6 +1847,7 @@ static inline void ap_scan_domains(struct ap_card *ac)
 			aq->card = ac;
 			aq->config = !decfg;
 			aq->chkstop = chkstop;
+			aq->se_bstate = hwinfo.bs;
 			dev = &aq->ap_dev.device;
 			dev->bus = &ap_bus_type;
 			dev->parent = &ac->ap_dev.device;
@@ -1882,6 +1877,8 @@ static inline void ap_scan_domains(struct ap_card *ac)
 		}
 		/* handle state changes on already existing queue device */
 		spin_lock_bh(&aq->lock);
+		/* SE bind state */
+		aq->se_bstate = hwinfo.bs;
 		/* checkstop state */
 		if (chkstop && !aq->chkstop) {
 			/* checkstop on */
@@ -1955,11 +1952,11 @@ static inline void ap_scan_domains(struct ap_card *ac)
  */
 static inline void ap_scan_adapter(int ap)
 {
-	int rc, dom, depth, type, comp_type, ml;
+	struct ap_tapq_hwinfo hwinfo;
+	int rc, dom, comp_type;
 	bool decfg, chkstop;
 	struct ap_card *ac;
 	struct device *dev;
-	unsigned int func;
 	ap_qid_t qid;
 
 	/* Is there currently a card device for this adapter ? */
@@ -1989,8 +1986,7 @@ static inline void ap_scan_adapter(int ap)
 	for (dom = 0; dom <= ap_max_domain_id; dom++)
 		if (ap_test_config_usage_domain(dom)) {
 			qid = AP_MKQID(ap, dom);
-			if (ap_queue_info(qid, &type, &func, &depth,
-					  &ml, &decfg, &chkstop) > 0)
+			if (ap_queue_info(qid, &hwinfo, &decfg, &chkstop) > 0)
 				break;
 		}
 	if (dom > ap_max_domain_id) {
@@ -2006,7 +2002,7 @@ static inline void ap_scan_adapter(int ap)
 		}
 		return;
 	}
-	if (!type) {
+	if (!hwinfo.at) {
 		/* No apdater type info available, an unusable adapter */
 		if (ac) {
 			AP_DBF_INFO("%s(%d) no valid type (0) info, rm card and queue devs\n",
@@ -2019,18 +2015,18 @@ static inline void ap_scan_adapter(int ap)
 		}
 		return;
 	}
+	hwinfo.value &= TAPQ_CARD_HWINFO_MASK; /* filter card specific hwinfo */
 	if (ac) {
 		/* Check APQN against existing card device for changes */
-		if (ac->raw_hwtype != type) {
+		if (ac->hwinfo.at != hwinfo.at) {
 			AP_DBF_INFO("%s(%d) hwtype %d changed, rm card and queue devs\n",
-				    __func__, ap, type);
+				    __func__, ap, hwinfo.at);
 			ap_scan_rm_card_dev_and_queue_devs(ac);
 			put_device(dev);
 			ac = NULL;
-		} else if ((ac->functions & TAPQ_CARD_FUNC_CMP_MASK) !=
-			   (func & TAPQ_CARD_FUNC_CMP_MASK)) {
+		} else if (ac->hwinfo.fac != hwinfo.fac) {
 			AP_DBF_INFO("%s(%d) functions 0x%08x changed, rm card and queue devs\n",
-				    __func__, ap, func);
+				    __func__, ap, hwinfo.fac);
 			ap_scan_rm_card_dev_and_queue_devs(ac);
 			put_device(dev);
 			ac = NULL;
@@ -2064,13 +2060,13 @@ static inline void ap_scan_adapter(int ap)
 
 	if (!ac) {
 		/* Build a new card device */
-		comp_type = ap_get_compatible_type(qid, type, func);
+		comp_type = ap_get_compatible_type(qid, hwinfo.at, hwinfo.fac);
 		if (!comp_type) {
 			AP_DBF_WARN("%s(%d) type %d, can't get compatibility type\n",
-				    __func__, ap, type);
+				    __func__, ap, hwinfo.at);
 			return;
 		}
-		ac = ap_card_create(ap, depth, type, comp_type, func, ml);
+		ac = ap_card_create(ap, hwinfo, comp_type);
 		if (!ac) {
 			AP_DBF_WARN("%s(%d) ap_card_create() failed\n",
 				    __func__, ap);
@@ -2101,13 +2097,13 @@ static inline void ap_scan_adapter(int ap)
 		get_device(dev);
 		if (decfg)
 			AP_DBF_INFO("%s(%d) new (decfg) card dev type=%d func=0x%08x created\n",
-				    __func__, ap, type, func);
+				    __func__, ap, hwinfo.at, hwinfo.fac);
 		else if (chkstop)
 			AP_DBF_INFO("%s(%d) new (chkstop) card dev type=%d func=0x%08x created\n",
-				    __func__, ap, type, func);
+				    __func__, ap, hwinfo.at, hwinfo.fac);
 		else
 			AP_DBF_INFO("%s(%d) new card dev type=%d func=0x%08x created\n",
-				    __func__, ap, type, func);
+				    __func__, ap, hwinfo.at, hwinfo.fac);
 	}
 
 	/* Verify the domains and the queue devices for this card */
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index b0771ca..9881483 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -76,16 +76,6 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
 #define AP_DEVICE_TYPE_CEX8	14
 
 /*
- * Known function facilities
- */
-#define AP_FUNC_MEX4K 1
-#define AP_FUNC_CRT4K 2
-#define AP_FUNC_COPRO 3
-#define AP_FUNC_ACCEL 4
-#define AP_FUNC_EP11  5
-#define AP_FUNC_APXA  6
-
-/*
  * AP queue state machine states
  */
 enum ap_sm_state {
@@ -182,9 +172,7 @@ struct ap_device {
 
 struct ap_card {
 	struct ap_device ap_dev;
-	int raw_hwtype;			/* AP raw hardware type. */
-	unsigned int functions;		/* TAPQ GR2 upper 32 facility bits */
-	int queue_depth;		/* AP queue depth.*/
+	struct ap_tapq_hwinfo hwinfo;	/* TAPQ GR2 content */
 	int id;				/* AP card number. */
 	unsigned int maxmsgsize;	/* AP msg limit for this card */
 	bool config;			/* configured state */
@@ -192,7 +180,7 @@ struct ap_card {
 	atomic64_t total_request_count;	/* # requests ever for this AP device.*/
 };
 
-#define TAPQ_CARD_FUNC_CMP_MASK 0xFFFF0000
+#define TAPQ_CARD_HWINFO_MASK 0xFEFF0000FFFF0F0FUL
 #define ASSOC_IDX_INVALID 0x10000
 
 #define to_ap_card(x) container_of((x), struct ap_card, ap_dev.device)
@@ -206,7 +194,7 @@ struct ap_queue {
 	bool config;			/* configured state */
 	bool chkstop;			/* checkstop state */
 	ap_qid_t qid;			/* AP queue id. */
-	bool se_bound;			/* SE bound state */
+	unsigned int se_bstate;		/* SE bind state (BS) */
 	unsigned int assoc_idx;		/* SE association index */
 	int queue_count;		/* # messages currently on AP queue. */
 	int pendingq_count;		/* # requests on pendingq list. */
@@ -290,8 +278,8 @@ void ap_queue_remove(struct ap_queue *aq);
 void ap_queue_init_state(struct ap_queue *aq);
 void _ap_queue_init_state(struct ap_queue *aq);
 
-struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
-			       int comp_type, unsigned int functions, int ml);
+struct ap_card *ap_card_create(int id, struct ap_tapq_hwinfo info,
+			       int comp_type);
 
 #define APMASKSIZE (BITS_TO_LONGS(AP_DEVICES) * sizeof(unsigned long))
 #define AQMASKSIZE (BITS_TO_LONGS(AP_DOMAINS) * sizeof(unsigned long))
diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c
index b2bd477..ce953cb 100644
--- a/drivers/s390/crypto/ap_card.c
+++ b/drivers/s390/crypto/ap_card.c
@@ -34,7 +34,7 @@ static ssize_t raw_hwtype_show(struct device *dev,
 {
 	struct ap_card *ac = to_ap_card(dev);
 
-	return sysfs_emit(buf, "%d\n", ac->raw_hwtype);
+	return sysfs_emit(buf, "%d\n", ac->hwinfo.at);
 }
 
 static DEVICE_ATTR_RO(raw_hwtype);
@@ -44,7 +44,7 @@ static ssize_t depth_show(struct device *dev, struct device_attribute *attr,
 {
 	struct ap_card *ac = to_ap_card(dev);
 
-	return sysfs_emit(buf, "%d\n", ac->queue_depth);
+	return sysfs_emit(buf, "%d\n", ac->hwinfo.qd);
 }
 
 static DEVICE_ATTR_RO(depth);
@@ -54,7 +54,7 @@ static ssize_t ap_functions_show(struct device *dev,
 {
 	struct ap_card *ac = to_ap_card(dev);
 
-	return sysfs_emit(buf, "0x%08X\n", ac->functions);
+	return sysfs_emit(buf, "0x%08X\n", ac->hwinfo.fac);
 }
 
 static DEVICE_ATTR_RO(ap_functions);
@@ -229,8 +229,8 @@ static void ap_card_device_release(struct device *dev)
 	kfree(ac);
 }
 
-struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
-			       int comp_type, unsigned int functions, int ml)
+struct ap_card *ap_card_create(int id, struct ap_tapq_hwinfo hwinfo,
+			       int comp_type)
 {
 	struct ap_card *ac;
 
@@ -240,12 +240,10 @@ struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
 	ac->ap_dev.device.release = ap_card_device_release;
 	ac->ap_dev.device.type = &ap_card_type;
 	ac->ap_dev.device_type = comp_type;
-	ac->raw_hwtype = raw_type;
-	ac->queue_depth = queue_depth;
-	ac->functions = functions;
+	ac->hwinfo = hwinfo;
 	ac->id = id;
-	ac->maxmsgsize = ml > 0 ?
-		ml * AP_TAPQ_ML_FIELD_CHUNK_SIZE : AP_DEFAULT_MAX_MSG_SIZE;
+	ac->maxmsgsize = hwinfo.ml > 0 ?
+		hwinfo.ml * AP_TAPQ_ML_FIELD_CHUNK_SIZE : AP_DEFAULT_MAX_MSG_SIZE;
 
 	return ac;
 }
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
index 3934a0c..6825954 100644
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -24,13 +24,12 @@ static void __ap_flush_queue(struct ap_queue *aq);
 
 static inline bool ap_q_supports_bind(struct ap_queue *aq)
 {
-	return ap_test_bit(&aq->card->functions, AP_FUNC_EP11) ||
-		ap_test_bit(&aq->card->functions, AP_FUNC_ACCEL);
+	return aq->card->hwinfo.ep11 || aq->card->hwinfo.accel;
 }
 
 static inline bool ap_q_supports_assoc(struct ap_queue *aq)
 {
-	return ap_test_bit(&aq->card->functions, AP_FUNC_EP11);
+	return aq->card->hwinfo.ep11;
 }
 
 static inline bool ap_q_needs_bind(struct ap_queue *aq)
@@ -257,7 +256,7 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq)
 		list_move_tail(&ap_msg->list, &aq->pendingq);
 		aq->requestq_count--;
 		aq->pendingq_count++;
-		if (aq->queue_count < aq->card->queue_depth) {
+		if (aq->queue_count < aq->card->hwinfo.qd) {
 			aq->sm_state = AP_SM_STATE_WORKING;
 			return AP_SM_WAIT_AGAIN;
 		}
@@ -318,7 +317,6 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq)
 	case AP_RESPONSE_RESET_IN_PROGRESS:
 		aq->sm_state = AP_SM_STATE_RESET_WAIT;
 		aq->rapq_fbit = 0;
-		aq->se_bound = false;
 		return AP_SM_WAIT_LOW_TIMEOUT;
 	default:
 		aq->dev_state = AP_DEV_STATE_ERROR;
@@ -339,17 +337,15 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq)
 static enum ap_sm_wait ap_sm_reset_wait(struct ap_queue *aq)
 {
 	struct ap_queue_status status;
+	struct ap_tapq_hwinfo hwinfo;
 	void *lsi_ptr;
 
-	if (aq->queue_count > 0 && aq->reply)
-		/* Try to read a completed message and get the status */
-		status = ap_sm_recv(aq);
-	else
-		/* Get the status with TAPQ */
-		status = ap_tapq(aq->qid, NULL);
+	/* Get the status with TAPQ */
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
 
 	switch (status.response_code) {
 	case AP_RESPONSE_NORMAL:
+		aq->se_bstate = hwinfo.bs;
 		lsi_ptr = ap_airq_ptr();
 		if (lsi_ptr && ap_queue_enable_irq(aq, lsi_ptr) == 0)
 			aq->sm_state = AP_SM_STATE_SETIRQ_WAIT;
@@ -421,9 +417,9 @@ static enum ap_sm_wait ap_sm_setirq_wait(struct ap_queue *aq)
 static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq)
 {
 	struct ap_queue_status status;
-	struct ap_tapq_gr2 info;
+	struct ap_tapq_hwinfo hwinfo;
 
-	status = ap_test_queue(aq->qid, 1, &info);
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
 	/* handle asynchronous error on this queue */
 	if (status.async && status.response_code) {
 		aq->dev_state = AP_DEV_STATE_ERROR;
@@ -442,8 +438,11 @@ static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq)
 		return AP_SM_WAIT_NONE;
 	}
 
+	/* update queue's SE bind state */
+	aq->se_bstate = hwinfo.bs;
+
 	/* check bs bits */
-	switch (info.bs) {
+	switch (hwinfo.bs) {
 	case AP_BS_Q_USABLE:
 		/* association is through */
 		aq->sm_state = AP_SM_STATE_IDLE;
@@ -460,7 +459,7 @@ static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq)
 		aq->dev_state = AP_DEV_STATE_ERROR;
 		aq->last_err_rc = status.response_code;
 		AP_DBF_WARN("%s bs 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",
-			    __func__, info.bs,
+			    __func__, hwinfo.bs,
 			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
 		return AP_SM_WAIT_NONE;
 	}
@@ -687,9 +686,9 @@ static ssize_t ap_functions_show(struct device *dev,
 {
 	struct ap_queue *aq = to_ap_queue(dev);
 	struct ap_queue_status status;
-	struct ap_tapq_gr2 info;
+	struct ap_tapq_hwinfo hwinfo;
 
-	status = ap_test_queue(aq->qid, 1, &info);
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
 	if (status.response_code > AP_RESPONSE_BUSY) {
 		AP_DBF_DBG("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
 			   __func__, status.response_code,
@@ -697,7 +696,7 @@ static ssize_t ap_functions_show(struct device *dev,
 		return -EIO;
 	}
 
-	return sysfs_emit(buf, "0x%08X\n", info.fac);
+	return sysfs_emit(buf, "0x%08X\n", hwinfo.fac);
 }
 
 static DEVICE_ATTR_RO(ap_functions);
@@ -840,19 +839,25 @@ static ssize_t se_bind_show(struct device *dev,
 {
 	struct ap_queue *aq = to_ap_queue(dev);
 	struct ap_queue_status status;
-	struct ap_tapq_gr2 info;
+	struct ap_tapq_hwinfo hwinfo;
 
 	if (!ap_q_supports_bind(aq))
 		return sysfs_emit(buf, "-\n");
 
-	status = ap_test_queue(aq->qid, 1, &info);
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
 	if (status.response_code > AP_RESPONSE_BUSY) {
 		AP_DBF_DBG("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
 			   __func__, status.response_code,
 			   AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
 		return -EIO;
 	}
-	switch (info.bs) {
+
+	/* update queue's SE bind state */
+	spin_lock_bh(&aq->lock);
+	aq->se_bstate = hwinfo.bs;
+	spin_unlock_bh(&aq->lock);
+
+	switch (hwinfo.bs) {
 	case AP_BS_Q_USABLE:
 	case AP_BS_Q_USABLE_NO_SECURE_KEY:
 		return sysfs_emit(buf, "bound\n");
@@ -867,6 +872,7 @@ static ssize_t se_bind_store(struct device *dev,
 {
 	struct ap_queue *aq = to_ap_queue(dev);
 	struct ap_queue_status status;
+	struct ap_tapq_hwinfo hwinfo;
 	bool value;
 	int rc;
 
@@ -878,39 +884,80 @@ static ssize_t se_bind_store(struct device *dev,
 	if (rc)
 		return rc;
 
-	if (value) {
-		/* bind, do BAPQ */
-		spin_lock_bh(&aq->lock);
-		if (aq->sm_state < AP_SM_STATE_IDLE) {
-			spin_unlock_bh(&aq->lock);
-			return -EBUSY;
-		}
-		status = ap_bapq(aq->qid);
-		spin_unlock_bh(&aq->lock);
-		if (!status.response_code) {
-			aq->se_bound = true;
-			AP_DBF_INFO("%s bapq(0x%02x.%04x) success\n", __func__,
-				    AP_QID_CARD(aq->qid),
-				    AP_QID_QUEUE(aq->qid));
-		} else {
-			AP_DBF_WARN("%s RC 0x%02x on bapq(0x%02x.%04x)\n",
-				    __func__, status.response_code,
-				    AP_QID_CARD(aq->qid),
-				    AP_QID_QUEUE(aq->qid));
-			return -EIO;
-		}
-	} else {
-		/* unbind, set F bit arg and trigger RAPQ */
+	if (!value) {
+		/* Unbind. Set F bit arg and trigger RAPQ */
 		spin_lock_bh(&aq->lock);
 		__ap_flush_queue(aq);
 		aq->rapq_fbit = 1;
-		aq->assoc_idx = ASSOC_IDX_INVALID;
-		aq->sm_state = AP_SM_STATE_RESET_START;
-		ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL));
-		spin_unlock_bh(&aq->lock);
+		_ap_queue_init_state(aq);
+		rc = count;
+		goto out;
 	}
 
-	return count;
+	/* Bind. Check current SE bind state */
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
+	if (status.response_code) {
+		AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
+			    __func__, status.response_code,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		return -EIO;
+	}
+
+	/* Update BS state */
+	spin_lock_bh(&aq->lock);
+	aq->se_bstate = hwinfo.bs;
+	if (hwinfo.bs != AP_BS_Q_AVAIL_FOR_BINDING) {
+		AP_DBF_WARN("%s bind attempt with bs %d on queue 0x%02x.%04x\n",
+			    __func__, hwinfo.bs,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Check SM state */
+	if (aq->sm_state < AP_SM_STATE_IDLE) {
+		rc = -EBUSY;
+		goto out;
+	}
+
+	/* invoke BAPQ */
+	status = ap_bapq(aq->qid);
+	if (status.response_code) {
+		AP_DBF_WARN("%s RC 0x%02x on bapq(0x%02x.%04x)\n",
+			    __func__, status.response_code,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		rc = -EIO;
+		goto out;
+	}
+	aq->assoc_idx = ASSOC_IDX_INVALID;
+
+	/* verify SE bind state */
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
+	if (status.response_code) {
+		AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
+			    __func__, status.response_code,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		rc = -EIO;
+		goto out;
+	}
+	aq->se_bstate = hwinfo.bs;
+	if (!(hwinfo.bs == AP_BS_Q_USABLE ||
+	      hwinfo.bs == AP_BS_Q_USABLE_NO_SECURE_KEY)) {
+		AP_DBF_WARN("%s BAPQ success, but bs shows %d on queue 0x%02x.%04x\n",
+			    __func__, hwinfo.bs,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		rc = -EIO;
+		goto out;
+	}
+
+	/* SE bind was successful */
+	AP_DBF_INFO("%s bapq(0x%02x.%04x) success\n", __func__,
+		    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+	rc = count;
+
+out:
+	spin_unlock_bh(&aq->lock);
+	return rc;
 }
 
 static DEVICE_ATTR_RW(se_bind);
@@ -920,12 +967,12 @@ static ssize_t se_associate_show(struct device *dev,
 {
 	struct ap_queue *aq = to_ap_queue(dev);
 	struct ap_queue_status status;
-	struct ap_tapq_gr2 info;
+	struct ap_tapq_hwinfo hwinfo;
 
 	if (!ap_q_supports_assoc(aq))
 		return sysfs_emit(buf, "-\n");
 
-	status = ap_test_queue(aq->qid, 1, &info);
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
 	if (status.response_code > AP_RESPONSE_BUSY) {
 		AP_DBF_DBG("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
 			   __func__, status.response_code,
@@ -933,7 +980,12 @@ static ssize_t se_associate_show(struct device *dev,
 		return -EIO;
 	}
 
-	switch (info.bs) {
+	/* update queue's SE bind state */
+	spin_lock_bh(&aq->lock);
+	aq->se_bstate = hwinfo.bs;
+	spin_unlock_bh(&aq->lock);
+
+	switch (hwinfo.bs) {
 	case AP_BS_Q_USABLE:
 		if (aq->assoc_idx == ASSOC_IDX_INVALID) {
 			AP_DBF_WARN("%s AP_BS_Q_USABLE but invalid assoc_idx\n", __func__);
@@ -955,6 +1007,7 @@ static ssize_t se_associate_store(struct device *dev,
 {
 	struct ap_queue *aq = to_ap_queue(dev);
 	struct ap_queue_status status;
+	struct ap_tapq_hwinfo hwinfo;
 	unsigned int value;
 	int rc;
 
@@ -968,18 +1021,28 @@ static ssize_t se_associate_store(struct device *dev,
 	if (value >= ASSOC_IDX_INVALID)
 		return -EINVAL;
 
+	/* check current SE bind state */
+	status = ap_test_queue(aq->qid, 1, &hwinfo);
+	if (status.response_code) {
+		AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
+			    __func__, status.response_code,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		return -EIO;
+	}
 	spin_lock_bh(&aq->lock);
-
-	/* sm should be in idle state */
-	if (aq->sm_state != AP_SM_STATE_IDLE) {
-		spin_unlock_bh(&aq->lock);
-		return -EBUSY;
+	aq->se_bstate = hwinfo.bs;
+	if (hwinfo.bs != AP_BS_Q_USABLE_NO_SECURE_KEY) {
+		AP_DBF_WARN("%s association attempt with bs %d on queue 0x%02x.%04x\n",
+			    __func__, hwinfo.bs,
+			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+		rc = -EINVAL;
+		goto out;
 	}
 
-	/* already associated or association pending ? */
-	if (aq->assoc_idx != ASSOC_IDX_INVALID) {
-		spin_unlock_bh(&aq->lock);
-		return -EINVAL;
+	/* check SM state */
+	if (aq->sm_state != AP_SM_STATE_IDLE) {
+		rc = -EBUSY;
+		goto out;
 	}
 
 	/* trigger the asynchronous association request */
@@ -990,17 +1053,20 @@ static ssize_t se_associate_store(struct device *dev,
 		aq->sm_state = AP_SM_STATE_ASSOC_WAIT;
 		aq->assoc_idx = value;
 		ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL));
-		spin_unlock_bh(&aq->lock);
 		break;
 	default:
-		spin_unlock_bh(&aq->lock);
 		AP_DBF_WARN("%s RC 0x%02x on aapq(0x%02x.%04x)\n",
 			    __func__, status.response_code,
 			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
-		return -EIO;
+		rc = -EIO;
+		goto out;
 	}
 
-	return count;
+	rc = count;
+
+out:
+	spin_unlock_bh(&aq->lock);
+	return rc;
 }
 
 static DEVICE_ATTR_RW(se_associate);
@@ -1123,7 +1189,9 @@ bool ap_queue_usable(struct ap_queue *aq)
 	}
 
 	/* SE guest's queues additionally need to be bound */
-	if (ap_q_needs_bind(aq) && !aq->se_bound)
+	if (ap_q_needs_bind(aq) &&
+	    !(aq->se_bstate == AP_BS_Q_USABLE ||
+	      aq->se_bstate == AP_BS_Q_USABLE_NO_SECURE_KEY))
 		rc = false;
 
 unlock_and_out:
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 542b5be..acb710d 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -393,8 +393,8 @@ static int ensure_nib_shared(unsigned long addr, struct gmap *gmap)
  * Register the guest ISC to GIB interface and retrieve the
  * host ISC to issue the host side PQAP/AQIC
  *
- * Response.status may be set to AP_RESPONSE_INVALID_ADDRESS in case the
- * vfio_pin_pages failed.
+ * status.response_code may be set to AP_RESPONSE_INVALID_ADDRESS in case the
+ * vfio_pin_pages or kvm_s390_gisc_register failed.
  *
  * Otherwise return the ap_queue_status returned by the ap_aqic(),
  * all retry handling will be done by the guest.
@@ -457,7 +457,8 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
 		VFIO_AP_DBF_WARN("%s: gisc registration failed: nisc=%d, isc=%d, apqn=%#04x\n",
 				 __func__, nisc, isc, q->apqn);
 
-		status.response_code = AP_RESPONSE_INVALID_GISA;
+		vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1);
+		status.response_code = AP_RESPONSE_INVALID_ADDRESS;
 		return status;
 	}
 
@@ -475,8 +476,11 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
 		break;
 	case AP_RESPONSE_OTHERWISE_CHANGED:
 		/* We could not modify IRQ settings: clear new configuration */
+		ret = kvm_s390_gisc_unregister(kvm, isc);
+		if (ret)
+			VFIO_AP_DBF_WARN("%s: kvm_s390_gisc_unregister: rc=%d isc=%d, apqn=%#04x\n",
+					 __func__, ret, isc, q->apqn);
 		vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1);
-		kvm_s390_gisc_unregister(kvm, isc);
 		break;
 	default:
 		pr_warn("%s: apqn %04x: response: %02x\n", __func__, q->apqn,
@@ -1976,6 +1980,7 @@ static ssize_t status_show(struct device *dev,
 {
 	ssize_t nchars = 0;
 	struct vfio_ap_queue *q;
+	unsigned long apid, apqi;
 	struct ap_matrix_mdev *matrix_mdev;
 	struct ap_device *apdev = to_ap_dev(dev);
 
@@ -1983,8 +1988,21 @@ static ssize_t status_show(struct device *dev,
 	q = dev_get_drvdata(&apdev->device);
 	matrix_mdev = vfio_ap_mdev_for_queue(q);
 
+	/* If the queue is assigned to the matrix mediated device, then
+	 * determine whether it is passed through to a guest; otherwise,
+	 * indicate that it is unassigned.
+	 */
 	if (matrix_mdev) {
-		if (matrix_mdev->kvm)
+		apid = AP_QID_CARD(q->apqn);
+		apqi = AP_QID_QUEUE(q->apqn);
+		/*
+		 * If the queue is passed through to the guest, then indicate
+		 * that it is in use; otherwise, indicate that it is
+		 * merely assigned to a matrix mediated device.
+		 */
+		if (matrix_mdev->kvm &&
+		    test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) &&
+		    test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm))
 			nchars = scnprintf(buf, PAGE_SIZE, "%s\n",
 					   AP_QUEUE_IN_USE);
 		else
@@ -2297,7 +2315,7 @@ static void vfio_ap_filter_apid_by_qtype(unsigned long *apm, unsigned long *aqm)
 	bool apid_cleared;
 	struct ap_queue_status status;
 	unsigned long apid, apqi;
-	struct ap_tapq_gr2 info;
+	struct ap_tapq_hwinfo info;
 
 	for_each_set_bit_inv(apid, apm, AP_DEVICES) {
 		apid_cleared = false;
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index dcd6c729..74200f5 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -673,7 +673,7 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms,
 	for_each_zcrypt_card(zc) {
 		/* Check for usable accelerator or CCA card */
 		if (!zc->online || !zc->card->config || zc->card->chkstop ||
-		    !(zc->card->functions & 0x18000000))
+		    !(zc->card->hwinfo.accel || zc->card->hwinfo.cca))
 			continue;
 		/* Check for size limits */
 		if (zc->min_mod_size > mex->inputdatalength ||
@@ -778,7 +778,7 @@ static long zcrypt_rsa_crt(struct ap_perms *perms,
 	for_each_zcrypt_card(zc) {
 		/* Check for usable accelerator or CCA card */
 		if (!zc->online || !zc->card->config || zc->card->chkstop ||
-		    !(zc->card->functions & 0x18000000))
+		    !(zc->card->hwinfo.accel || zc->card->hwinfo.cca))
 			continue;
 		/* Check for size limits */
 		if (zc->min_mod_size > crt->inputdatalength ||
@@ -893,7 +893,7 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
 	for_each_zcrypt_card(zc) {
 		/* Check for usable CCA card */
 		if (!zc->online || !zc->card->config || zc->card->chkstop ||
-		    !(zc->card->functions & 0x10000000))
+		    !zc->card->hwinfo.cca)
 			continue;
 		/* Check for user selected CCA card */
 		if (xcrb->user_defined != AUTOSELECT &&
@@ -1064,7 +1064,7 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms,
 	for_each_zcrypt_card(zc) {
 		/* Check for usable EP11 card */
 		if (!zc->online || !zc->card->config || zc->card->chkstop ||
-		    !(zc->card->functions & 0x04000000))
+		    !zc->card->hwinfo.ep11)
 			continue;
 		/* Check for user selected EP11 card */
 		if (targets &&
@@ -1177,7 +1177,7 @@ static long zcrypt_rng(char *buffer)
 	for_each_zcrypt_card(zc) {
 		/* Check for usable CCA card */
 		if (!zc->online || !zc->card->config || zc->card->chkstop ||
-		    !(zc->card->functions & 0x10000000))
+		    !zc->card->hwinfo.cca)
 			continue;
 		/* get weight index of the card device	*/
 		wgt = zc->speed_rating[func_code];
@@ -1238,7 +1238,7 @@ static void zcrypt_device_status_mask(struct zcrypt_device_status *devstatus)
 			queue = AP_QID_QUEUE(zq->queue->qid);
 			stat = &devstatus[card * AP_DOMAINS + queue];
 			stat->hwtype = zc->card->ap_dev.device_type;
-			stat->functions = zc->card->functions >> 26;
+			stat->functions = zc->card->hwinfo.fac >> 26;
 			stat->qid = zq->queue->qid;
 			stat->online = zq->online ? 0x01 : 0x00;
 		}
@@ -1263,7 +1263,7 @@ void zcrypt_device_status_mask_ext(struct zcrypt_device_status_ext *devstatus)
 			queue = AP_QID_QUEUE(zq->queue->qid);
 			stat = &devstatus[card * AP_DOMAINS + queue];
 			stat->hwtype = zc->card->ap_dev.device_type;
-			stat->functions = zc->card->functions >> 26;
+			stat->functions = zc->card->hwinfo.fac >> 26;
 			stat->qid = zq->queue->qid;
 			stat->online = zq->online ? 0x01 : 0x00;
 		}
@@ -1286,7 +1286,7 @@ int zcrypt_device_status_ext(int card, int queue,
 			if (card == AP_QID_CARD(zq->queue->qid) &&
 			    queue == AP_QID_QUEUE(zq->queue->qid)) {
 				devstat->hwtype = zc->card->ap_dev.device_type;
-				devstat->functions = zc->card->functions >> 26;
+				devstat->functions = zc->card->hwinfo.fac >> 26;
 				devstat->qid = zq->queue->qid;
 				devstat->online = zq->online ? 0x01 : 0x00;
 				spin_unlock(&zcrypt_list_lock);
diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c
index 5c4532a..64df7d2 100644
--- a/drivers/s390/crypto/zcrypt_cex4.c
+++ b/drivers/s390/crypto/zcrypt_cex4.c
@@ -477,7 +477,7 @@ static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
 		return -ENOMEM;
 	zc->card = ac;
 	dev_set_drvdata(&ap_dev->device, zc);
-	if (ap_test_bit(&ac->functions, AP_FUNC_ACCEL)) {
+	if (ac->hwinfo.accel) {
 		if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
 			zc->type_string = "CEX4A";
 			zc->user_space_type = ZCRYPT_CEX4;
@@ -506,8 +506,7 @@ static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
 			zc->user_space_type = ZCRYPT_CEX6;
 		}
 		zc->min_mod_size = CEX4A_MIN_MOD_SIZE;
-		if (ap_test_bit(&ac->functions, AP_FUNC_MEX4K) &&
-		    ap_test_bit(&ac->functions, AP_FUNC_CRT4K)) {
+		if (ac->hwinfo.mex4k && ac->hwinfo.crt4k) {
 			zc->max_mod_size = CEX4A_MAX_MOD_SIZE_4K;
 			zc->max_exp_bit_length =
 				CEX4A_MAX_MOD_SIZE_4K;
@@ -516,7 +515,7 @@ static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
 			zc->max_exp_bit_length =
 				CEX4A_MAX_MOD_SIZE_2K;
 		}
-	} else if (ap_test_bit(&ac->functions, AP_FUNC_COPRO)) {
+	} else if (ac->hwinfo.cca) {
 		if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
 			zc->type_string = "CEX4C";
 			zc->speed_rating = CEX4C_SPEED_IDX;
@@ -556,7 +555,7 @@ static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
 		zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
 		zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
 		zc->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
-	} else if (ap_test_bit(&ac->functions, AP_FUNC_EP11)) {
+	} else if (ac->hwinfo.ep11) {
 		if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
 			zc->type_string = "CEX4P";
 			zc->user_space_type = ZCRYPT_CEX4;
@@ -599,14 +598,14 @@ static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
 		return rc;
 	}
 
-	if (ap_test_bit(&ac->functions, AP_FUNC_COPRO)) {
+	if (ac->hwinfo.cca) {
 		rc = sysfs_create_group(&ap_dev->device.kobj,
 					&cca_card_attr_grp);
 		if (rc) {
 			zcrypt_card_unregister(zc);
 			zcrypt_card_free(zc);
 		}
-	} else if (ap_test_bit(&ac->functions, AP_FUNC_EP11)) {
+	} else if (ac->hwinfo.ep11) {
 		rc = sysfs_create_group(&ap_dev->device.kobj,
 					&ep11_card_attr_grp);
 		if (rc) {
@@ -627,9 +626,9 @@ static void zcrypt_cex4_card_remove(struct ap_device *ap_dev)
 	struct zcrypt_card *zc = dev_get_drvdata(&ap_dev->device);
 	struct ap_card *ac = to_ap_card(&ap_dev->device);
 
-	if (ap_test_bit(&ac->functions, AP_FUNC_COPRO))
+	if (ac->hwinfo.cca)
 		sysfs_remove_group(&ap_dev->device.kobj, &cca_card_attr_grp);
-	else if (ap_test_bit(&ac->functions, AP_FUNC_EP11))
+	else if (ac->hwinfo.ep11)
 		sysfs_remove_group(&ap_dev->device.kobj, &ep11_card_attr_grp);
 
 	zcrypt_card_unregister(zc);
@@ -654,19 +653,19 @@ static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev)
 	struct zcrypt_queue *zq;
 	int rc;
 
-	if (ap_test_bit(&aq->card->functions, AP_FUNC_ACCEL)) {
+	if (aq->card->hwinfo.accel) {
 		zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
 		if (!zq)
 			return -ENOMEM;
 		zq->ops = zcrypt_msgtype(MSGTYPE50_NAME,
 					 MSGTYPE50_VARIANT_DEFAULT);
-	} else if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO)) {
+	} else if (aq->card->hwinfo.cca) {
 		zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
 		if (!zq)
 			return -ENOMEM;
 		zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
 					 MSGTYPE06_VARIANT_DEFAULT);
-	} else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11)) {
+	} else if (aq->card->hwinfo.ep11) {
 		zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
 		if (!zq)
 			return -ENOMEM;
@@ -689,14 +688,14 @@ static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev)
 		return rc;
 	}
 
-	if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO)) {
+	if (aq->card->hwinfo.cca) {
 		rc = sysfs_create_group(&ap_dev->device.kobj,
 					&cca_queue_attr_grp);
 		if (rc) {
 			zcrypt_queue_unregister(zq);
 			zcrypt_queue_free(zq);
 		}
-	} else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11)) {
+	} else if (aq->card->hwinfo.ep11) {
 		rc = sysfs_create_group(&ap_dev->device.kobj,
 					&ep11_queue_attr_grp);
 		if (rc) {
@@ -717,9 +716,9 @@ static void zcrypt_cex4_queue_remove(struct ap_device *ap_dev)
 	struct zcrypt_queue *zq = dev_get_drvdata(&ap_dev->device);
 	struct ap_queue *aq = to_ap_queue(&ap_dev->device);
 
-	if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO))
+	if (aq->card->hwinfo.cca)
 		sysfs_remove_group(&ap_dev->device.kobj, &cca_queue_attr_grp);
-	else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11))
+	else if (aq->card->hwinfo.ep11)
 		sysfs_remove_group(&ap_dev->device.kobj, &ep11_queue_attr_grp);
 
 	zcrypt_queue_unregister(zq);
diff --git a/lib/raid6/s390vx.uc b/lib/raid6/s390vx.uc
index b25dfc9..cd0b9e9 100644
--- a/lib/raid6/s390vx.uc
+++ b/lib/raid6/s390vx.uc
@@ -158,7 +158,7 @@
 
 static int raid6_s390vx$#_valid(void)
 {
-	return MACHINE_HAS_VX;
+	return cpu_has_vx();
 }
 
 const struct raid6_calls raid6_s390vx$# = {