From 5ba35a6c13fff0929c34aba6b7602dacbe68686c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 12 Dec 2025 16:43:58 +0100 Subject: [PATCH 01/35] s390/boot: Add -Wno-default-const-init-unsafe to KBUILD_CFLAGS Add -Wno-default-const-init-unsafe to boot KBUILD_CFLAGS, similar to scripts/Makefile.extrawarn, since clang generates warnings for the dummy variable in typecheck(): CC arch/s390/boot/version.o arch/s390/include/asm/ptrace.h:221:9: warning: default initialization of an object of type 'typeof (regs->psw)' (aka 'const psw_t') leaves the object uninitialized [-Wdefault-const-init-var-unsafe] 221 | return psw_bits(regs->psw).pstate; | ^ arch/s390/include/asm/ptrace.h:98:2: note: expanded from macro 'psw_bits' 98 | typecheck(psw_t, __psw); \ | ^ include/linux/typecheck.h:11:12: note: expanded from macro 'typecheck' 11 | typeof(x) __dummy2; \ | ^ Signed-off-by: Heiko Carstens --- arch/s390/boot/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 490167faba7a..a1e719a79d38 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -21,6 +21,7 @@ KBUILD_AFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_AFLAGS_DECOMPRESSOR)) KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_CFLAGS_DECOMPRESSOR)) KBUILD_AFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS KBUILD_CFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS +KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe) CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char From b4780fe4ddf04b51127a33d705f4a2e224df00fa Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 12 Dec 2025 16:47:07 +0100 Subject: [PATCH 02/35] s390/purgatory: Add -Wno-default-const-init-unsafe to KBUILD_CFLAGS Add -Wno-default-const-init-unsafe to purgatory KBUILD_CFLAGS, similar to scripts/Makefile.extrawarn, since clang generates warnings for the dummy variable in typecheck(): CC arch/s390/purgatory/purgatory.o arch/s390/include/asm/ptrace.h:221:9: warning: default initialization of an object of type 'typeof (regs->psw)' (aka 'const psw_t') leaves the object uninitialized [-Wdefault-const-init-var-unsafe] 221 | return psw_bits(regs->psw).pstate; | ^ arch/s390/include/asm/ptrace.h:98:2: note: expanded from macro 'psw_bits' 98 | typecheck(psw_t, __psw); \ | ^ include/linux/typecheck.h:11:12: note: expanded from macro 'typecheck' 11 | typeof(x) __dummy2; \ | ^ Signed-off-by: Heiko Carstens --- arch/s390/purgatory/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index 0c196a5b194a..61d240a37633 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -23,6 +23,7 @@ KBUILD_CFLAGS += -D__DISABLE_EXPORTS KBUILD_CFLAGS += $(CLANG_FLAGS) KBUILD_CFLAGS += $(if $(CONFIG_CC_IS_CLANG),-Wno-microsoft-anon-tag) KBUILD_CFLAGS += $(call cc-option,-fno-PIE) +KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe) KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS)) KBUILD_AFLAGS += -D__DISABLE_EXPORTS From afa8fa52a42c310e418994b3255597efde31b343 Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Thu, 11 Dec 2025 13:19:31 +0100 Subject: [PATCH 03/35] s390/ptrace: Convert function macros to inline functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the function macros user_mode(), instruction_pointer(), and user_stack_pointer() to inline functions, to align their definition with x86 and arm64. Use const qualifier on struct pt_regs pointer parameters to prevent compiler warnings: arch/s390/kernel/stacktrace.c: In function ‘arch_stack_walk_user_common’: arch/s390/kernel/stacktrace.c:114:34: warning: passing argument 1 of ‘instruction_pointer’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] ... arch/s390/kernel/stacktrace.c:117:48: warning: passing argument 1 of ‘user_stack_pointer’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] ... While at it add const qualifier to all struct pt_regs pointer parameters that are accessed read-only and use __always_inline instead of inline to harmonize the helper functions. [hca@linux.ibm.com: Use psw_bits() helper] Reviewed-by: Heiko Carstens Signed-off-by: Jens Remus Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ptrace.h | 37 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 962cf042c66d..e6ec0ccf3d73 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -214,16 +214,23 @@ void update_cr_regs(struct task_struct *task); #define arch_has_single_step() (1) #define arch_has_block_step() (1) -#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) -#define instruction_pointer(regs) ((regs)->psw.addr) -#define user_stack_pointer(regs)((regs)->gprs[15]) #define profile_pc(regs) instruction_pointer(regs) -static inline long regs_return_value(struct pt_regs *regs) +static __always_inline bool user_mode(const struct pt_regs *regs) +{ + return psw_bits(regs->psw).pstate; +} + +static inline long regs_return_value(const struct pt_regs *regs) { return regs->gprs[2]; } +static __always_inline unsigned long instruction_pointer(const struct pt_regs *regs) +{ + return regs->psw.addr; +} + static inline void instruction_pointer_set(struct pt_regs *regs, unsigned long val) { @@ -233,19 +240,26 @@ static inline void instruction_pointer_set(struct pt_regs *regs, int regs_query_register_offset(const char *name); const char *regs_query_register_name(unsigned int offset); -static __always_inline unsigned long kernel_stack_pointer(struct pt_regs *regs) +static __always_inline unsigned long kernel_stack_pointer(const struct pt_regs *regs) { return regs->gprs[15]; } -static __always_inline unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset) +static __always_inline unsigned long user_stack_pointer(const struct pt_regs *regs) +{ + return regs->gprs[15]; +} + +static __always_inline unsigned long regs_get_register(const struct pt_regs *regs, + unsigned int offset) { if (offset >= NUM_GPRS) return 0; return regs->gprs[offset]; } -static __always_inline int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) +static __always_inline int regs_within_kernel_stack(const struct pt_regs *regs, + unsigned long addr) { unsigned long ksp = kernel_stack_pointer(regs); @@ -261,7 +275,8 @@ static __always_inline int regs_within_kernel_stack(struct pt_regs *regs, unsign * is specifined by @regs. If the @n th entry is NOT in the kernel stack, * this returns 0. */ -static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) +static __always_inline unsigned long regs_get_kernel_stack_nth(const struct pt_regs *regs, + unsigned int n) { unsigned long addr; @@ -278,8 +293,8 @@ static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *r * * regs_get_kernel_argument() returns @n th argument of the function call. */ -static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs, - unsigned int n) +static __always_inline unsigned long regs_get_kernel_argument(const struct pt_regs *regs, + unsigned int n) { unsigned int argoffset = STACK_FRAME_OVERHEAD / sizeof(long); @@ -290,7 +305,7 @@ static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs, return regs_get_kernel_stack_nth(regs, argoffset + n); } -static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc) +static __always_inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc) { regs->gprs[2] = rc; } From 71f9bc6f7c25294d79132345f94561469a03c245 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 11 Dec 2025 11:34:41 +0100 Subject: [PATCH 04/35] s390/ap/zcrypt: Revisit module param permissions Revisit and rework module parameter permissions for AP bus and zcrypt device drivers. In general all sysfs permissions for AP bus and zcrypt parameters should be 0444 so that user space tools like lszcrypt can read the current value of module parameters. Some exceptions are only for some internal tweak parameters like ap_msg_pool_min_items and zcrypt_mempool_threshold which should only be readable by an administrator. Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Signed-off-by: Heiko Carstens --- drivers/s390/crypto/ap_bus.c | 12 ++++++------ drivers/s390/crypto/zcrypt_api.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index a445494fd2be..19cd27e9a3f3 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -52,24 +52,24 @@ MODULE_LICENSE("GPL"); int ap_domain_index = -1; /* Adjunct Processor Domain Index */ static DEFINE_SPINLOCK(ap_domain_lock); -module_param_named(domain, ap_domain_index, int, 0440); +module_param_named(domain, ap_domain_index, int, 0444); MODULE_PARM_DESC(domain, "domain index for ap devices"); EXPORT_SYMBOL(ap_domain_index); static int ap_thread_flag; -module_param_named(poll_thread, ap_thread_flag, int, 0440); +module_param_named(poll_thread, ap_thread_flag, int, 0444); MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off)."); static char *apm_str; -module_param_named(apmask, apm_str, charp, 0440); +module_param_named(apmask, apm_str, charp, 0444); MODULE_PARM_DESC(apmask, "AP bus adapter mask."); static char *aqm_str; -module_param_named(aqmask, aqm_str, charp, 0440); +module_param_named(aqmask, aqm_str, charp, 0444); MODULE_PARM_DESC(aqmask, "AP bus domain mask."); static int ap_useirq = 1; -module_param_named(useirq, ap_useirq, int, 0440); +module_param_named(useirq, ap_useirq, int, 0444); MODULE_PARM_DESC(useirq, "Use interrupt if available, default is 1 (on)."); atomic_t ap_max_msg_size = ATOMIC_INIT(AP_DEFAULT_MAX_MSG_SIZE); @@ -130,7 +130,7 @@ debug_info_t *ap_dbf_info; */ static mempool_t *ap_msg_pool; static unsigned int ap_msg_pool_min_items = 8; -module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0440); +module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0400); MODULE_PARM_DESC(msgpool_min_items, "AP message pool minimal items"); /* diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 7a3b99f065f2..c796773fbce8 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -50,7 +50,7 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \ MODULE_LICENSE("GPL"); unsigned int zcrypt_mempool_threshold = 5; -module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0440); +module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0400); MODULE_PARM_DESC(mempool_threshold, "CCA and EP11 request/reply mempool minimal items (min: 1)"); /* From eb2606bba1cbac610701d73af25cbff0f3c5b09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 22 Dec 2025 09:28:17 +0100 Subject: [PATCH 05/35] s390: Implement ARCH_HAS_CC_CAN_LINK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic CC_CAN_LINK detection relies on -m32/-m64 compiler flags. Some s390 toolchains use -m31 instead but that is not supported in the kernel. Make the logic easier to understand and allow the simplification of the generic CC_CAN_LINK by using a tailored implementation. Signed-off-by: Thomas Weißschuh Signed-off-by: Heiko Carstens --- arch/s390/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0e5fad5f06ca..cd856318ffe5 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -85,6 +85,7 @@ config S390 select ARCH_ENABLE_MEMORY_HOTREMOVE select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE + select ARCH_HAS_CC_CAN_LINK select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL @@ -294,6 +295,14 @@ config PGTABLE_LEVELS source "kernel/livepatch/Kconfig" +config ARCH_CC_CAN_LINK + bool + default $(cc_can_link_user,-m64) + +config ARCH_USERFLAGS + string + default "-m64" + config ARCH_SUPPORTS_KEXEC def_bool y From 12ea976f955cefb06eeae4c9e5eb48d08038ccb2 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 30 Dec 2025 16:42:39 +0100 Subject: [PATCH 06/35] s390/ap: Fix typo in function name reference Add missing s into ap_intructions_available. Signed-off-by: Julia Lawall Reviewed-by: Jimmy Brisson Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index b24459f692fa..3b95c6531a67 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -78,7 +78,7 @@ union ap_queue_status_reg { }; /** - * ap_intructions_available() - Test if AP instructions are available. + * ap_instructions_available() - Test if AP instructions are available. * * Returns true if the AP instructions are installed, otherwise false. */ From 6cce3609a1e0dedeef9b4bfdc87d0d4692f691d7 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 13 Jan 2026 20:43:57 +0100 Subject: [PATCH 07/35] s390/preempt: Optimize preempt_count() Provide an inline assembly using alternatives to avoid the need of a base register when reading preempt_count() from lowcore. Use the LLGT instruction, which reads only the least significant 31 bits of preempt_count. This masks out the encoded PREEMPT_NEED_RESCHED bit. Generated code is changed from 000000000046e5d0 : 46e5d0: c0 04 00 00 00 00 jgnop 46e5d0 46e5d6: a7 39 00 00 lghi %r3,0 46e5da: 58 10 33 a8 l %r1,936(%r3) 46e5de: c0 1b 00 ff ff 00 nilf %r1,16776960 46e5e4: a7 74 00 11 jne 46e606 to something like this: 000000000046e5d0 : 46e5d0: c0 04 00 00 00 00 jgnop 46e5d0 46e5d6: e3 10 03 a8 00 17 llgt %r1,936 46e5dc: ec 41 28 b7 00 55 risbgz %r4,%r1,40,55 46e5e2: a7 74 00 0f jne 46e600 Overall savings are only 82 bytes according to bloat-o-meter. This is because of different inlining decisions, and there aren't many preempt_count() users in the kernel. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/preempt.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h index 6ccd033acfe5..6d6a28bee4e7 100644 --- a/arch/s390/include/asm/preempt.h +++ b/arch/s390/include/asm/preempt.h @@ -8,7 +8,10 @@ #include #include -/* We use the MSB mostly because its available */ +/* + * Use MSB so it is possible to read preempt_count with LLGT which + * reads the least significant 31 bits with a single instruction. + */ #define PREEMPT_NEED_RESCHED 0x80000000 /* @@ -23,7 +26,20 @@ */ static __always_inline int preempt_count(void) { - return READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED; + unsigned long lc_preempt, count; + + BUILD_BUG_ON(sizeof_field(struct lowcore, preempt_count) != sizeof(int)); + lc_preempt = offsetof(struct lowcore, preempt_count); + /* READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED */ + asm_inline( + ALTERNATIVE("llgt %[count],%[offzero](%%r0)\n", + "llgt %[count],%[offalt](%%r0)\n", + ALT_FEATURE(MFEATURE_LOWCORE)) + : [count] "=d" (count) + : [offzero] "i" (lc_preempt), + [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS), + "m" (((struct lowcore *)0)->preempt_count)); + return count; } static __always_inline void preempt_count_set(int pc) From 23ba7d31633da6b0706b4154e4eb74cdfab710ef Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 13 Jan 2026 20:43:58 +0100 Subject: [PATCH 08/35] s390/preempt: Optimize __preemp_count_add()/__preempt_count_sub() Provide an inline assembly using alternatives to avoid the need of a base register due to relocatable lowcore when adding or subtracting small constants from preempt_count. Main user is preempt_disable(), which subtracts one from preempt_count. With this the generated code changes from 10012c: a7 b9 00 00 lghi %r11,0 100130: eb 01 b3 a8 00 6a asi 936(%r11),1 to something like this: 10012c: eb 01 03 a8 00 6a asi 936,1 Kernel image size is reduced by 13kb (bloat-o-meter -t, defconfig, gcc15). Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/preempt.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h index 6d6a28bee4e7..ef6ea3163c27 100644 --- a/arch/s390/include/asm/preempt.h +++ b/arch/s390/include/asm/preempt.h @@ -84,7 +84,17 @@ static __always_inline void __preempt_count_add(int val) */ if (!IS_ENABLED(CONFIG_PROFILE_ALL_BRANCHES)) { if (__builtin_constant_p(val) && (val >= -128) && (val <= 127)) { - __atomic_add_const(val, &get_lowcore()->preempt_count); + unsigned long lc_preempt; + + lc_preempt = offsetof(struct lowcore, preempt_count); + asm_inline( + ALTERNATIVE("asi %[offzero](%%r0),%[val]\n", + "asi %[offalt](%%r0),%[val]\n", + ALT_FEATURE(MFEATURE_LOWCORE)) + : "+m" (((struct lowcore *)0)->preempt_count) + : [offzero] "i" (lc_preempt), [val] "i" (val), + [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS) + : "cc"); return; } } From 05405b8fd284189278636a0392976cbec3bb6d19 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 13 Jan 2026 20:43:59 +0100 Subject: [PATCH 09/35] s390/asm: Let __HAVE_ASM_FLAG_OUTPUTS__ define 1 With the empty define __is_enabled(__HAVE_ASM_FLAG_OUTPUTS__) evaluates to false. Therefore let __HAVE_ASM_FLAG_OUTPUTS__ define 1 if it is defined. This allows to make use of __is_defined(__HAVE_ASM_FLAG_OUTPUTS__) like expected. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/asm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/asm.h b/arch/s390/include/asm/asm.h index e9062b01e2a2..510901c2a5f9 100644 --- a/arch/s390/include/asm/asm.h +++ b/arch/s390/include/asm/asm.h @@ -30,7 +30,7 @@ */ #if defined(__GCC_ASM_FLAG_OUTPUTS__) && !(IS_ENABLED(CONFIG_CC_ASM_FLAG_OUTPUT_BROKEN)) -#define __HAVE_ASM_FLAG_OUTPUTS__ +#define __HAVE_ASM_FLAG_OUTPUTS__ 1 #define CC_IPM(sym) #define CC_OUT(sym, var) "=@cc" (var) From 48b4790f054994d4df6d1025ec9267b19618f0ec Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 13 Jan 2026 20:44:00 +0100 Subject: [PATCH 10/35] s390/preempt: Optimize __preempt_count_dec_and_test() Provide an inline assembly using alternatives to avoid the need of a base register due to relocatable lowcore when adding or subtracting small constants from preempt_count. Main user is preempt_enable(), which subtracts one from preempt_count and tests if the result is zero. With this the generated code changes from 1000b8: a7 19 00 00 lghi %r1,0 1000bc: eb ff 13 a8 00 6e alsi 936(%r1),-1 1000c2: a7 54 00 05 jnhe 1000cc <__rcu_read_unlock+0x14> to something like this: 1000b8: eb ff 03 a8 00 6e alsi 936,-1 1000be: a7 54 00 05 jnhe 1000c8 <__rcu_read_unlock+0x10> Kernel image size is reduced by 45kb (bloat-o-meter -t, defconfig, gcc15). Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/preempt.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h index ef6ea3163c27..6e5821bb047e 100644 --- a/arch/s390/include/asm/preempt.h +++ b/arch/s390/include/asm/preempt.h @@ -113,7 +113,22 @@ static __always_inline void __preempt_count_sub(int val) */ static __always_inline bool __preempt_count_dec_and_test(void) { +#ifdef __HAVE_ASM_FLAG_OUTPUTS__ + unsigned long lc_preempt; + int cc; + + lc_preempt = offsetof(struct lowcore, preempt_count); + asm_inline( + ALTERNATIVE("alsi %[offzero](%%r0),%[val]\n", + "alsi %[offalt](%%r0),%[val]\n", + ALT_FEATURE(MFEATURE_LOWCORE)) + : "=@cc" (cc), "+m" (((struct lowcore *)0)->preempt_count) + : [offzero] "i" (lc_preempt), [val] "i" (-1), + [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS)); + return (cc == 0) || (cc == 2); +#else return __atomic_add_const_and_test(-1, &get_lowcore()->preempt_count); +#endif } /* From 84d875e69818bed600edccb09be4a64b84a34a54 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Thu, 8 Jan 2026 16:45:53 +0100 Subject: [PATCH 11/35] s390/pci: Handle futile config accesses of disabled devices directly On s390 PCI busses and slots with multiple functions may have holes because PCI functions are passed-through by the hypervisor on a per function basis and some functions may be in standby or reserved. This fact is indicated by returning true from the hypervisor_isolated_pci_functions() helper and triggers common code to scan all possible devfn values. Via pci_scan_single_device() this in turn causes config reads for the device and vendor IDs, even for PCI functions which are in standby and thereofore disabled. So far these futile config reads, as well as potentially writes, which can never succeed were handled by the PCI load/store instructions themselves. This works as the platform just returns an error for a disabled and thus not usable function handle. It does cause spamming of error logs and additional overhead though. Instead check if the used function handle is enabled in zpci_cfg_load() and zpci_cfg_write() and if not enable directly return -ENODEV. Also refactor zpci_cfg_load() and zpci_cfg_store() slightly to accommodate the new logic while meeting modern kernel style guidelines. Cc: stable@vger.kernel.org Fixes: a50297cf8235 ("s390/pci: separate zbus creation from scanning") Signed-off-by: Niklas Schnelle Reviewed-by: Benjamin Block Reviewed-by: Farhan Ali Signed-off-by: Heiko Carstens --- arch/s390/pci/pci.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 57f3980b98a9..7f44b0644a20 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -231,24 +231,33 @@ int zpci_fmb_disable_device(struct zpci_dev *zdev) static int zpci_cfg_load(struct zpci_dev *zdev, int offset, u32 *val, u8 len) { u64 req = ZPCI_CREATE_REQ(zdev->fh, ZPCI_PCIAS_CFGSPC, len); + int rc = -ENODEV; u64 data; - int rc; + + if (!zdev_enabled(zdev)) + goto out_err; rc = __zpci_load(&data, req, offset); - if (!rc) { - data = le64_to_cpu((__force __le64) data); - data >>= (8 - len) * 8; - *val = (u32) data; - } else - *val = 0xffffffff; + if (rc) + goto out_err; + data = le64_to_cpu((__force __le64)data); + data >>= (8 - len) * 8; + *val = (u32)data; + return 0; + +out_err: + PCI_SET_ERROR_RESPONSE(val); return rc; } static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len) { u64 req = ZPCI_CREATE_REQ(zdev->fh, ZPCI_PCIAS_CFGSPC, len); + int rc = -ENODEV; u64 data = val; - int rc; + + if (!zdev_enabled(zdev)) + return rc; data <<= (8 - len) * 8; data = (__force u64) cpu_to_le64(data); From 88303fb68cc2e8b975f1505c84f215a934f6c2ad Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Thu, 8 Jan 2026 16:45:54 +0100 Subject: [PATCH 12/35] s390/pci: Use PCIBIOS return values in pci_read()/pci_write() While pci_read() and pci_write() have returned errno values since their inception with commit cd24834130ac ("s390/pci: base support") other config accessors in particular pci_generic_config_read() as well as pci_generic_config_write() return specific error values which are then converted to errno by pcibios_err_to_errno(). Since latter does handle the case where the error value already looks like an errno the previous behavior is unlikely to cause actual issues. Still, for consistency and in case any caller explicitly checks error values align pci_read() and pci_write() with the generic accessors. Reviewed-by: Benjamin Block Signed-off-by: Niklas Schnelle Reviewed-by: Farhan Ali Signed-off-by: Heiko Carstens --- arch/s390/pci/pci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 7f44b0644a20..0d952e8fdc8a 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -406,7 +406,9 @@ static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, { struct zpci_dev *zdev = zdev_from_bus(bus, devfn); - return (zdev) ? zpci_cfg_load(zdev, where, val, size) : -ENODEV; + if (!zdev || zpci_cfg_load(zdev, where, val, size)) + return PCIBIOS_DEVICE_NOT_FOUND; + return PCIBIOS_SUCCESSFUL; } static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, @@ -414,7 +416,9 @@ static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, { struct zpci_dev *zdev = zdev_from_bus(bus, devfn); - return (zdev) ? zpci_cfg_store(zdev, where, val, size) : -ENODEV; + if (!zdev || zpci_cfg_store(zdev, where, val, size)) + return PCIBIOS_DEVICE_NOT_FOUND; + return PCIBIOS_SUCCESSFUL; } static struct pci_ops pci_root_ops = { From e5f3e67de587b8e876ca04d5bd021d751fe9c4d2 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:36 +0100 Subject: [PATCH 13/35] s390: Add CC_HAS_ASM_IMMEDIATE_STRINGS Upcoming changes to s390 specific inline assemblies require the usage of strings as immediate input operands. This works only with gcc-9 and newer compilers. With gcc-8 this leads to a compile error: void bar(void) { asm volatile("" :: "i" ("foo")); } Results in: In function 'bar': warning: asm operand 0 probably doesn't match constraints asm volatile("" :: "i" ("foo")); ^~~ error: impossible constraint in 'asm' Provide a CC_HAS_ASM_IMMEDIATE_STRINGS config option which allows to tell if the compiler supports strings as immediate input operands. Based on that conditional code can be provided. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index cd856318ffe5..fcce8c5db703 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -69,6 +69,12 @@ config CC_HAS_ASM_AOR_FORMAT_FLAGS Clang versions before 19.1.0 do not support A, O, and R inline assembly format flags. +config CC_HAS_ASM_IMMEDIATE_STRINGS + def_bool !(CC_IS_GCC && GCC_VERSION < 90000) + help + GCC versions before 9.0.0 cannot handle strings as immediate + input operands in inline assemblies. + config CC_HAS_STACKPROTECTOR_GLOBAL def_bool $(cc-option, -mstack-protector-guard=global -mstack-protector-guard-record) From e3abd056ffc9d6397766817eadbd4297632aceaf Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:37 +0100 Subject: [PATCH 14/35] s390/bug: Convert to inline assembly with input operands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite the bug inline assembly so it uses input operands again instead of pure macro replacements. This more or less reverts the conversion done when 'cond_str' support was added [1]. Reason for this is that the upcoming __WARN_printf() implementation requires an inline assembly with an output operand. At the same time input strings (format specifier and condition string) may contain the special '%' character. As soon as an inline assembly is specified to have input/output operands the '%' has a special meaning: e.g. converting the existing #define __BUG_FLAGS(cond_str, flags) \ asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags))); to #define __BUG_FLAGS(cond_str, flags) \ asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags))::); will result in a compile error as soon as 'cond_str' contains a '%' character: net/core/neighbour.c: In function ‘neigh_table_init’: ././include/linux/compiler_types.h:546:20: error: invalid 'asm': invalid %-code ... net/core/neighbour.c:1838:17: note: in expansion of macro ‘WARN_ON’ 1838 | WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); | ^~~~~~~ Convert the code, use immediate operands, and also add comments similar to x86 which are emitted to the generated assembly file, which makes debugging much easier. Note: since gcc-8 does not support strings as immediate input operands, guard the new implementation with CC_HAS_ASM_IMMEDIATE_STRINGS and fallback to the generic non-exception based warning implementation for incompatible compilers. [1] 6584ff203aec ("bugs/s390: Use 'cond_str' in __EMIT_BUG()") Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/bug.h | 75 ++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index ee9221bb5d18..73f65d91da50 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -2,60 +2,49 @@ #ifndef _ASM_S390_BUG_H #define _ASM_S390_BUG_H -#include +#include -#ifdef CONFIG_BUG +#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) -#ifndef CONFIG_DEBUG_BUGVERBOSE -#define _BUGVERBOSE_LOCATION(file, line) +#ifdef CONFIG_DEBUG_BUGVERBOSE +#define __BUG_ENTRY_VERBOSE(file, line) \ + " .long " file " - . # bug_entry::file\n" \ + " .short " line " # bug_entry::line\n" #else -#define __BUGVERBOSE_LOCATION(file, line) \ - .pushsection .rodata.str, "aMS", @progbits, 1; \ - .align 2; \ - 10002: .ascii file "\0"; \ - .popsection; \ - \ - .long 10002b - .; \ - .short line; -#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line) +#define __BUG_ENTRY_VERBOSE(file, line) #endif -#ifndef CONFIG_GENERIC_BUG -#define __BUG_ENTRY(cond_str, flags) -#else -#define __BUG_ENTRY(cond_str, flags) \ - .pushsection __bug_table, "aw"; \ - .align 4; \ - 10000: .long 10001f - .; \ - _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \ - .short flags; \ - .popsection; \ - 10001: -#endif - -#define ASM_BUG_FLAGS(cond_str, flags) \ - __BUG_ENTRY(cond_str, flags) \ - mc 0,0 - -#define ASM_BUG() ASM_BUG_FLAGS("", 0) - -#define __BUG_FLAGS(cond_str, flags) \ - asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags))); - -#define __WARN_FLAGS(cond_str, flags) \ -do { \ - __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \ +#define __BUG_ASM(cond_str, flags) \ +do { \ + asm_inline volatile("\n" \ + "0: mc 0,0\n" \ + " .section __bug_table,\"aw\"\n" \ + "1: .long 0b - . # bug_entry::bug_addr\n" \ + __BUG_ENTRY_VERBOSE("%[file]", "%[line]") \ + " .short %[flgs] # bug_entry::flags\n" \ + " .org 1b+%[size]\n" \ + " .previous" \ + : \ + : [file] "i" (WARN_CONDITION_STR(cond_str) __FILE__), \ + [line] "i" (__LINE__), \ + [flgs] "i" (flags), \ + [size] "i" (sizeof(struct bug_entry))); \ } while (0) -#define BUG() \ -do { \ - __BUG_FLAGS("", 0); \ - unreachable(); \ +#define BUG() \ +do { \ + __BUG_ASM("", 0); \ + unreachable(); \ +} while (0) + +#define __WARN_FLAGS(cond_str, flags) \ +do { \ + __BUG_ASM(cond_str, BUGFLAG_WARNING | (flags)); \ } while (0) #define HAVE_ARCH_BUG -#endif /* CONFIG_BUG */ +#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ #include From 2b71b8ab971889edba278b71f1d3ff82cd42e175 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:38 +0100 Subject: [PATCH 15/35] s390/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED This is just the s390 variant of commit 4f1b701f24be ("x86/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED"). Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/bug.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index 73f65d91da50..7e0498f22f2a 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -7,11 +7,18 @@ #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) #ifdef CONFIG_DEBUG_BUGVERBOSE -#define __BUG_ENTRY_VERBOSE(file, line) \ +#define __BUG_ENTRY_VERBOSE(format, file, line) \ + " .long " format " - . # bug_entry::format\n" \ " .long " file " - . # bug_entry::file\n" \ " .short " line " # bug_entry::line\n" #else -#define __BUG_ENTRY_VERBOSE(file, line) +#define __BUG_ENTRY_VERBOSE(format, file, line) +#endif + +#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED +#define WARN_CONDITION_STR(cond_str) cond_str +#else +#define WARN_CONDITION_STR(cond_str) "" #endif #define __BUG_ASM(cond_str, flags) \ @@ -20,12 +27,13 @@ do { \ "0: mc 0,0\n" \ " .section __bug_table,\"aw\"\n" \ "1: .long 0b - . # bug_entry::bug_addr\n" \ - __BUG_ENTRY_VERBOSE("%[file]", "%[line]") \ + __BUG_ENTRY_VERBOSE("%[frmt]", "%[file]", "%[line]") \ " .short %[flgs] # bug_entry::flags\n" \ " .org 1b+%[size]\n" \ " .previous" \ : \ - : [file] "i" (WARN_CONDITION_STR(cond_str) __FILE__), \ + : [frmt] "i" (WARN_CONDITION_STR(cond_str)), \ + [file] "i" (__FILE__), \ [line] "i" (__LINE__), \ [flgs] "i" (flags), \ [size] "i" (sizeof(struct bug_entry))); \ @@ -43,6 +51,7 @@ do { \ } while (0) #define HAVE_ARCH_BUG +#define HAVE_ARCH_BUG_FORMAT #endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ From 8cbfd13601af7d71bb86c2ea686489a6f139c0ba Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:39 +0100 Subject: [PATCH 16/35] s390/bug: Introduce and use monitor code macro The first operand address of the monitor call (mc) instruction is the monitor code. Currently the monitor code is ignored, but this will change. Therefore add and use MONCODE_BUG instead of a hardcoded zero. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/bug.h | 10 ++++++++-- arch/s390/kernel/traps.c | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index 7e0498f22f2a..faa556226c00 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -3,7 +3,11 @@ #define _ASM_S390_BUG_H #include +#include +#define MONCODE_BUG _AC(0, U) + +#ifndef __ASSEMBLER__ #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) #ifdef CONFIG_DEBUG_BUGVERBOSE @@ -24,7 +28,7 @@ #define __BUG_ASM(cond_str, flags) \ do { \ asm_inline volatile("\n" \ - "0: mc 0,0\n" \ + "0: mc %[monc](%%r0),0\n" \ " .section __bug_table,\"aw\"\n" \ "1: .long 0b - . # bug_entry::bug_addr\n" \ __BUG_ENTRY_VERBOSE("%[frmt]", "%[file]", "%[line]") \ @@ -32,7 +36,8 @@ do { \ " .org 1b+%[size]\n" \ " .previous" \ : \ - : [frmt] "i" (WARN_CONDITION_STR(cond_str)), \ + : [monc] "i" (MONCODE_BUG), \ + [frmt] "i" (WARN_CONDITION_STR(cond_str)), \ [file] "i" (__FILE__), \ [line] "i" (__LINE__), \ [flgs] "i" (flags), \ @@ -54,6 +59,7 @@ do { \ #define HAVE_ARCH_BUG_FORMAT #endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ +#endif /* __ASSEMBLER__ */ #include diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 19687dab32f7..de63e98e724b 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -258,11 +258,12 @@ static void __init test_monitor_call(void) if (!IS_ENABLED(CONFIG_BUG)) return; asm_inline volatile( - " mc 0,0\n" + " mc %[monc](%%r0),0\n" "0: lhi %[val],0\n" "1:\n" EX_TABLE(0b, 1b) - : [val] "+d" (val)); + : [val] "+d" (val) + : [monc] "i" (MONCODE_BUG)); if (!val) panic("Monitor call doesn't work!\n"); } From ee44f4e7ebb56f1a2a3aaed8b01ea052fc225680 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:40 +0100 Subject: [PATCH 17/35] s390/traps: Copy monitor code to pt_regs In case of a monitor call program check the CPU stores the monitor code to lowcore. Let the program check handler copy it to the pt_regs structure so it can be used by the monitor call exception handler. Instead of increasing the pt_regs size add a union which contains both orig_gpr2 and monitor_code, since orig_gpr2 is not used in case of a program check. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ptrace.h | 5 ++++- arch/s390/kernel/traps.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index e6ec0ccf3d73..aaceb1d9110a 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -120,7 +120,10 @@ struct pt_regs { unsigned long gprs[NUM_GPRS]; }; }; - unsigned long orig_gpr2; + union { + unsigned long orig_gpr2; + unsigned long monitor_code; + }; union { struct { unsigned int int_code; diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index de63e98e724b..b2d6d7cc3b17 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -298,6 +298,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs) teid.val = lc->trans_exc_code; regs->int_code = lc->pgm_int_code; regs->int_parm_long = teid.val; + regs->monitor_code = lc->monitor_code; /* * In case of a guest fault, short-circuit the fault handler and return. * This way the sie64a() function will return 0; fault address and From 04dabb4261c387318affbdb22c15c31138a989f5 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:41 +0100 Subject: [PATCH 18/35] s390/bug: Implement __WARN_printf() This is the s390 variant of commit 5b472b6e5bd9 ("x86_64/bug: Implement __WARN_printf()"). See the x86 commit for the general idea; there are only implementation details which are different. With the new exception based __WARN_printf() implementation the generated code for a simple WARN() is simplified. For example: void foo(int a) { WARN(a, "bar"); } Before this change the generated code looks like this: 0000000000000210 : 210: c0 04 00 00 00 00 jgnop 210 216: ec 26 00 06 00 7c cgijne %r2,0,222 21c: c0 f4 00 00 00 00 jg 21c 21e: R_390_PC32DBL __s390_indirect_jump_r14+0x2 222: eb ef f0 88 00 24 stmg %r14,%r15,136(%r15) 228: b9 04 00 ef lgr %r14,%r15 22c: e3 f0 ff e8 ff 71 lay %r15,-24(%r15) 232: e3 e0 f0 98 00 24 stg %r14,152(%r15) 238: c0 20 00 00 00 00 larl %r2,238 23a: R_390_PC32DBL .LC48+0x2 23e: c0 e5 00 00 00 00 brasl %r14,23e 240: R_390_PLT32DBL __warn_printk+0x2 244: af 00 00 00 mc 0,0 248: eb ef f0 a0 00 04 lmg %r14,%r15,160(%r15) 24e: c0 f4 00 00 00 00 jg 24e 250: R_390_PC32DBL __s390_indirect_jump_r14+0x2 With this change the generated code looks like this: 0000000000000210 : 210: c0 04 00 00 00 00 jgnop 210 216: ec 26 00 06 00 7c cgijne %r2,0,222 21c: c0 f4 00 00 00 00 jg 21c 21e: R_390_PC32DBL __s390_indirect_jump_r14+0x2 222: c0 20 00 00 00 00 larl %r2,222 224: R_390_PC32DBL __bug_table+0x2 228: c0 f4 00 00 00 00 jg 228 22a: R_390_PLT32DBL __WARN_trap+0x2 Downside is that the call trace now starts at __WARN_trap(): ------------[ cut here ]------------ bar WARNING: arch/s390/kernel/setup.c:1017 at 0x0, CPU#0: swapper/0/0 ... Krnl PSW : 0704c00180000000 000003ffe0f6a3b4 (__WARN_trap+0x4/0x10) ... Krnl Code: 000003ffe0f6a3ac: 0707 bcr 0,%r7 000003ffe0f6a3ae: 0707 bcr 0,%r7 *000003ffe0f6a3b0: af000001 mc 1,0 >000003ffe0f6a3b4: 07fe bcr 15,%r14 000003ffe0f6a3b6: 47000700 bc 0,1792 000003ffe0f6a3ba: 0707 bcr 0,%r7 000003ffe0f6a3bc: 0707 bcr 0,%r7 000003ffe0f6a3be: 0707 bcr 0,%r7 Call Trace: [<000003ffe0f6a3b4>] __WARN_trap+0x4/0x10 ([<000003ffe185a54c>] start_kernel+0x53c/0x5d8) [<000003ffe010002e>] startup_continue+0x2e/0x40 Which isn't too helpful. This can be addressed by just skipping __WARN_trap(), which will be addressed in a later patch. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/asm-prototypes.h | 1 + arch/s390/include/asm/bug.h | 62 +++++++++++++++++++++++--- arch/s390/kernel/entry.S | 11 +++++ arch/s390/kernel/traps.c | 38 +++++++++++++++- 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h index f662eb4b9246..7bd1801cf241 100644 --- a/arch/s390/include/asm/asm-prototypes.h +++ b/arch/s390/include/asm/asm-prototypes.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index faa556226c00..1559873b19b0 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -6,6 +6,7 @@ #include #define MONCODE_BUG _AC(0, U) +#define MONCODE_BUG_ARG _AC(1, U) #ifndef __ASSEMBLER__ #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) @@ -25,16 +26,20 @@ #define WARN_CONDITION_STR(cond_str) "" #endif +#define __BUG_ENTRY(format, file, line, flags, size) \ + " .section __bug_table,\"aw\"\n" \ + "1: .long 0b - . # bug_entry::bug_addr\n" \ + __BUG_ENTRY_VERBOSE(format, file, line) \ + " .short "flags" # bug_entry::flags\n" \ + " .org 1b+"size"\n" \ + " .previous" + #define __BUG_ASM(cond_str, flags) \ do { \ asm_inline volatile("\n" \ "0: mc %[monc](%%r0),0\n" \ - " .section __bug_table,\"aw\"\n" \ - "1: .long 0b - . # bug_entry::bug_addr\n" \ - __BUG_ENTRY_VERBOSE("%[frmt]", "%[file]", "%[line]") \ - " .short %[flgs] # bug_entry::flags\n" \ - " .org 1b+%[size]\n" \ - " .previous" \ + __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \ + "%[flgs]", "%[size]") \ : \ : [monc] "i" (MONCODE_BUG), \ [frmt] "i" (WARN_CONDITION_STR(cond_str)), \ @@ -55,8 +60,53 @@ do { \ __BUG_ASM(cond_str, BUGFLAG_WARNING | (flags)); \ } while (0) +#define __WARN_bug_entry(flags, format) \ +({ \ + struct bug_entry *bug; \ + \ + asm_inline volatile("\n" \ + "0: larl %[bug],1f\n" \ + __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \ + "%[flgs]", "%[size]") \ + : [bug] "=d" (bug) \ + : [frmt] "i" (format), \ + [file] "i" (__FILE__), \ + [line] "i" (__LINE__), \ + [flgs] "i" (flags), \ + [size] "i" (sizeof(struct bug_entry))); \ + bug; \ +}) + +/* + * Variable Argument List (va_list) as defined in ELF Application + * Binary Interface s390x Supplement documentation. + */ +struct arch_va_list { + long __gpr; + long __fpr; + void *__overflow_arg_area; + void *__reg_save_area; +}; + +struct bug_entry; +struct pt_regs; + +void *__warn_args(struct arch_va_list *args, struct pt_regs *regs); +void __WARN_trap(struct bug_entry *bug, ...); + +#define __WARN_print_arg(flags, format, arg...) \ +do { \ + int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS; \ + \ + __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \ +} while (0) + +#define __WARN_printf(taint, fmt, arg...) \ + __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg) + #define HAVE_ARCH_BUG #define HAVE_ARCH_BUG_FORMAT +#define HAVE_ARCH_BUG_FORMAT_ARGS #endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ #endif /* __ASSEMBLER__ */ diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index b7f1553d9ee5..4873fe9d891b 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +174,16 @@ SYM_FUNC_START(__switch_to_asm) BR_EX %r14 SYM_FUNC_END(__switch_to_asm) +#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) + +SYM_FUNC_START(__WARN_trap) + mc MONCODE_BUG_ARG(%r0),0 + BR_EX %r14 +SYM_FUNC_END(__WARN_trap) +EXPORT_SYMBOL(__WARN_trap) + +#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ + #if IS_ENABLED(CONFIG_KVM) /* * __sie64a calling convention: diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index b2d6d7cc3b17..2972f526cd81 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -220,11 +221,46 @@ static void space_switch_exception(struct pt_regs *regs) do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); } +#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) + +void *__warn_args(struct arch_va_list *args, struct pt_regs *regs) +{ + struct stack_frame *stack_frame; + + /* + * Generate va_list from pt_regs. See ELF Application Binary Interface + * s390x Supplement documentation for details. + * + * - __overflow_arg_area needs to point to the parameter area, which + * is right above the standard stack frame (160 bytes) + * + * - __reg_save_area needs to point to a register save area where + * general registers (%r2 - %r6) can be found at offset 16. Which + * means that the gprs save area of pt_regs can be used + * + * - __gpr must be set to one, since the first parameter has been + * processed (pointer to bug_entry) + */ + stack_frame = (struct stack_frame *)regs->gprs[15]; + args->__overflow_arg_area = stack_frame + 1; + args->__reg_save_area = regs->gprs; + args->__gpr = 1; + return args; +} + +#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ + static void monitor_event_exception(struct pt_regs *regs) { + enum bug_trap_type btt; + if (user_mode(regs)) return; - switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) { + if (regs->monitor_code == MONCODE_BUG_ARG) + btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs); + else + btt = report_bug(regs->psw.addr - (regs->int_code >> 16), regs); + switch (btt) { case BUG_TRAP_TYPE_NONE: fixup_exception(regs); break; From 940cfea4270436edf515bf07d0a778eed6cec16d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:42 +0100 Subject: [PATCH 19/35] s390/bug: Implement WARN_ONCE() This is the s390 variant of commit 11bb4944f014 ("x86/bug: Implement WARN_ONCE()"). Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/bug.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index 1559873b19b0..1fbcbdbc595f 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -104,6 +104,17 @@ do { \ #define __WARN_printf(taint, fmt, arg...) \ __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg) +#define WARN_ONCE(cond, format, arg...) \ +({ \ + int __ret_warn_on = !!(cond); \ + \ + if (unlikely(__ret_warn_on)) { \ + __WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\ + format, ## arg); \ + } \ + __ret_warn_on; \ +}) + #define HAVE_ARCH_BUG #define HAVE_ARCH_BUG_FORMAT #define HAVE_ARCH_BUG_FORMAT_ARGS From 79996065cfa258de95c123ca9ed93754ab60d8c8 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:43 +0100 Subject: [PATCH 20/35] s390/bug: Skip __WARN_trap() in call traces In order to avoid rather pointless warning disassemblies of __WARN_trap() set the PSW address to the return address of the function which called __WARN_trap(). This is the address to which __WARN_trap() would return in any case. The result is a disassembly of the function which called __WARN_trap(), which is much more helpful. Before: WARNING: arch/s390/kernel/setup.c:1017 at foobar+0x2c/0x20, CPU#0: swapper/0/0 ... Krnl PSW : 0704c00180000000 000003ffe0f675f4 (__WARN_trap+0x4/0x10) ... Krnl Code: 000003ffe0f675ec: 0707 bcr 0,%r7 000003ffe0f675ee: 0707 bcr 0,%r7 *000003ffe0f675f0: af000001 mc 1,0 >000003ffe0f675f4: 07fe bcr 15,%r14 000003ffe0f675f6: 47000700 bc 0,1792 000003ffe0f675fa: 0707 bcr 0,%r7 000003ffe0f675fc: 0707 bcr 0,%r7 000003ffe0f675fe: 0707 bcr 0,%r7 Call Trace: [<000003ffe0f675f4>] __WARN_trap+0x4/0x10 [<000003ffe185bc2e>] arch_cpu_finalize_init+0x26/0x60 [<000003ffe185654c>] start_kernel+0x53c/0x5d8 [<000003ffe010002e>] startup_continue+0x2e/0x40 Afterwards: WARNING: arch/s390/kernel/setup.c:1017 at foobar+0x12/0x30, CPU#0: swapper/0/0 ... Krnl PSW : 0704c00180000000 000003ffe185bc2e (arch_cpu_finalize_init+0x26/0x60) ... Krnl Code: 000003ffe185bc1c: e3f0ff98ff71 lay %r15,-104(%r15) 000003ffe185bc22: e3e0f0980024 stg %r14,152(%r15) *000003ffe185bc28: c0e5ff45ed94 brasl %r14,000003ffe0119750 >000003ffe185bc2e: c0e5ffa052b9 brasl %r14,000003ffe0c661a0 000003ffe185bc34: c020fffe86d6 larl %r2,000003ffe182c9e0 000003ffe185bc3a: e548f0a80006 mvghi 168(%r15),6 000003ffe185bc40: e548f0a00005 mvghi 160(%r15),5 000003ffe185bc46: a7690004 lghi %r6,4 Call Trace: [<000003ffe185bc2e>] arch_cpu_finalize_init+0x26/0x60 [<000003ffe185654c>] start_kernel+0x53c/0x5d8 [<000003ffe010002e>] startup_continue+0x2e/0x40 Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/kernel/traps.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 2972f526cd81..1b5c6fc431cc 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -256,10 +256,12 @@ static void monitor_event_exception(struct pt_regs *regs) if (user_mode(regs)) return; - if (regs->monitor_code == MONCODE_BUG_ARG) + if (regs->monitor_code == MONCODE_BUG_ARG) { + regs->psw.addr = regs->gprs[14]; btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs); - else + } else { btt = report_bug(regs->psw.addr - (regs->int_code >> 16), regs); + } switch (btt) { case BUG_TRAP_TYPE_NONE: fixup_exception(regs); From 9f9d68c308cb63de67d35171925ce3875d076d4f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Jan 2026 16:31:44 +0100 Subject: [PATCH 21/35] s390/bug: Prevent tail-call optimization For the exception based __WARN_trap() implementation it is technically not necessary to prevent tail-call optimization, however it may be confusing to see warning messages like: WARNING: arch/s390/kernel/setup.c:1017 at foobar+0x2c/0x50, CPU#0: swapper/0/0 together with a disassembly of a different function caused by tail-call optimization for the __WARN_trap() call. Prevent that by adding an empty asm statement. This generates slightly worse code, but should hopefully avoid confusion. With this the output looks like: WARNING: arch/s390/kernel/setup.c:1017 at foobar+0x2c/0x50, CPU#0: swapper/0/0 ... Krnl PSW : 0704c00180000000 000003ffe0119788 (foobar+0x38/0x50) ... Krnl Code: 000003ffe0119776: e3e0f0980024 stg %r14,152(%r15) 000003ffe011977c: c02000b8992a larl %r2,000003ffe182c9d0 *000003ffe0119782: c0e5007270b7 brasl %r14,000003ffe0f678f0 >000003ffe0119788: ebeff0a00004 lmg %r14,%r15,160(%r15) 000003ffe011978e: 07fe bcr 15,%r14 000003ffe0119790: 47000700 bc 0,1792 000003ffe0119794: 0707 bcr 0,%r7 000003ffe0119796: 0707 bcr 0,%r7 Call Trace: [<000003ffe0119788>] foobar+0x38/0x50 [<000003ffe185bc2e>] arch_cpu_finalize_init+0x26/0x60 [<000003ffe185654c>] start_kernel+0x53c/0x5d8 [<000003ffe010002e>] startup_continue+0x2e/0x40 A better solution would be to replace or patch the branch instruction to __WARN_trap() with the monitor call instruction, similar to what is done for x86 [1]. However s390 does not support static_cond_calls(). Therefore use the simple approach for the time being. [1] commit 860238af7a33 ("x86_64/bug: Inline the UD1") Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/bug.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index 1fbcbdbc595f..59017fd3d935 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -99,6 +99,8 @@ do { \ int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS; \ \ __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \ + /* prevent tail-call optimization */ \ + asm(""); \ } while (0) #define __WARN_printf(taint, fmt, arg...) \ From 0d453ba04044bb1b0df366d4a0a9098481f14621 Mon Sep 17 00:00:00 2001 From: Gerd Bayer Date: Fri, 16 Jan 2026 16:02:42 +0100 Subject: [PATCH 22/35] s390/Kconfig: Define non-zero ILLEGAL_POINTER_VALUE Define CONFIG_ILLEGAL_POINTER_VALUE to the eye-catching non-zero value of 0xdead000000000000, consistent with other architectures. Assert at compile-time that the poison pointers that include/linux/poison.h defines based on this illegal pointer are beyond the largest useful virtual addresses. Also, assert at compile-time that the range of poison pointers per include/linux/poison.h (currently a range of less than 0x10000 addresses) does not overlap with the range used for address handles for s390's non-MIO PCI instructions. This enables s390 to track the DMA mappings by the network stack's page_pool that was introduced with [0]. Other functional changes are not intended. Other archictectures have introduced this for various other reasons with commit 5c178472af24 ("riscv: define ILLEGAL_POINTER_VALUE for 64bit") commit f6853eb561fb ("powerpc/64: Define ILLEGAL_POINTER_VALUE for 64-bit") commit bf0c4e047324 ("arm64: kconfig: Move LIST_POISON to a safe value") commit a29815a333c6 ("core, x86: make LIST_POISON less deadly") [0] https://lore.kernel.org/all/20250409-page-pool-track-dma-v9-0-6a9ef2e0cba8@redhat.com/ Reviewed-by: Niklas Schnelle Signed-off-by: Gerd Bayer Acked-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- Documentation/arch/s390/mm.rst | 4 ++++ arch/s390/Kconfig | 4 ++++ arch/s390/boot/startup.c | 1 + arch/s390/include/asm/pci_io.h | 1 + arch/s390/pci/pci.c | 2 ++ 5 files changed, 12 insertions(+) diff --git a/Documentation/arch/s390/mm.rst b/Documentation/arch/s390/mm.rst index 084adad5eef9..19681157c6f2 100644 --- a/Documentation/arch/s390/mm.rst +++ b/Documentation/arch/s390/mm.rst @@ -109,3 +109,7 @@ Virtual memory layout | KASAN shadow | KASAN untracked | | +------------------+ ASCE limit + | | + | CONFIG_ILLEGAL_POINTER_VALUE causes memory access fault + | | + +------------------+ diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index fcce8c5db703..cda697a03abf 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -719,6 +719,10 @@ config ARCH_SPARSEMEM_ENABLE config ARCH_SPARSEMEM_DEFAULT def_bool y +config ILLEGAL_POINTER_VALUE + hex + default 0xdead000000000000 + config MAX_PHYSMEM_BITS int "Maximum size of supported physical memory in bits (42-53)" range 42 53 diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index f77067dfc2a8..7f3343493ab9 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -336,6 +336,7 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size) BUILD_BUG_ON(!IS_ALIGNED(TEXT_OFFSET, THREAD_SIZE)); BUILD_BUG_ON(!IS_ALIGNED(__NO_KASLR_START_KERNEL, THREAD_SIZE)); BUILD_BUG_ON(__NO_KASLR_END_KERNEL > _REGION1_SIZE); + BUILD_BUG_ON(CONFIG_ILLEGAL_POINTER_VALUE < _REGION1_SIZE); vsize = get_vmem_size(ident_map_size, vmemmap_size, vmalloc_size, _REGION3_SIZE); boot_debug("vmem size estimated: 0x%016lx\n", vsize); if (IS_ENABLED(CONFIG_KASAN) || __NO_KASLR_END_KERNEL > _REGION2_SIZE || diff --git a/arch/s390/include/asm/pci_io.h b/arch/s390/include/asm/pci_io.h index 43a5ea4ee20f..f3bef5bc7223 100644 --- a/arch/s390/include/asm/pci_io.h +++ b/arch/s390/include/asm/pci_io.h @@ -18,6 +18,7 @@ #define ZPCI_IOMAP_SHIFT 48 #define ZPCI_IOMAP_ADDR_SHIFT 62 #define ZPCI_IOMAP_ADDR_BASE (1UL << ZPCI_IOMAP_ADDR_SHIFT) +#define ZPCI_IOMAP_ADDR_MAX ((1UL << (ZPCI_IOMAP_ADDR_SHIFT + 1)) - 1) #define ZPCI_IOMAP_ADDR_OFF_MASK ((1UL << ZPCI_IOMAP_SHIFT) - 1) #define ZPCI_IOMAP_MAX_ENTRIES \ (1UL << (ZPCI_IOMAP_ADDR_SHIFT - ZPCI_IOMAP_SHIFT)) diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 0d952e8fdc8a..97bab20bc163 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -1065,6 +1065,8 @@ static int zpci_mem_init(void) { BUILD_BUG_ON(!is_power_of_2(__alignof__(struct zpci_fmb)) || __alignof__(struct zpci_fmb) < sizeof(struct zpci_fmb)); + BUILD_BUG_ON((CONFIG_ILLEGAL_POINTER_VALUE + 0x10000 > ZPCI_IOMAP_ADDR_BASE) && + (CONFIG_ILLEGAL_POINTER_VALUE <= ZPCI_IOMAP_ADDR_MAX)); zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb), __alignof__(struct zpci_fmb), 0, NULL); From b2c04fc1239062b39ddfdd8731ee1a10810dfb74 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 23 Jan 2026 10:14:12 +0100 Subject: [PATCH 23/35] s390/perf: Disable register readout on sampling events Running commands # ./perf record -IR0,R1 -a sleep 1 extracts and displays register value of general purpose register r1 and r0. However the value displayed of any register is random and does not reflect the register value recorded at the time of the sample interrupt. The sampling device driver on s390 creates a very large buffer for the hardware to store the samples. Only when that large buffer gets full an interrupt is generated and many hundreds of sample entries are processed and copied to the kernel ring buffer and eventually get copied to the perf tool. It is during the copy to the kernel ring buffer that each sample is processed (on s390) and at that time the register values are extracted. This is not the original goal, the register values should be read when the samples are created not when the samples are copied to the kernel ring buffer. Prevent this event from being installed in the first place and return -EOPNOTSUPP. This is already the case for PERF_SAMPLE_REGS_USER. Signed-off-by: Thomas Richter Reviewed-by: Jan Polensky Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_sf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 459af23a47a5..e8bd19ac82c7 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -841,7 +841,7 @@ static bool is_callchain_event(struct perf_event *event) u64 sample_type = event->attr.sample_type; return sample_type & (PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | - PERF_SAMPLE_STACK_USER); + PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_STACK_USER); } static int cpumsf_pmu_event_init(struct perf_event *event) From cab7d81de304bd33ac41d2f0fb0d1721b08b7f88 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 27 Jan 2026 14:59:49 +0100 Subject: [PATCH 24/35] s390: Document s390 stackprotector support Recently [1] s390 got stackprotector support. Document this. [1] commit f5730d44e05e ("s390: Add stackprotector support") Reviewed-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- Documentation/features/debug/stackprotector/arch-support.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/features/debug/stackprotector/arch-support.txt b/Documentation/features/debug/stackprotector/arch-support.txt index de8f43f2e5d6..43e49c71612e 100644 --- a/Documentation/features/debug/stackprotector/arch-support.txt +++ b/Documentation/features/debug/stackprotector/arch-support.txt @@ -21,7 +21,7 @@ | parisc: | TODO | | powerpc: | ok | | riscv: | ok | - | s390: | TODO | + | s390: | ok | | sh: | ok | | sparc: | TODO | | um: | TODO | From b8555fbc16311346ccf332010898c6307ec145c3 Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Mon, 2 Feb 2026 15:40:47 +0100 Subject: [PATCH 25/35] s390/configs: Enable BLK_DEV_NULL_BLK as module Enable BLK_DEV_NULL_BLK as module in defconfig and debug_defconfig, so the Null Test Block Device Driver can be easily used for testing purposes. Signed-off-by: Halil Pasic Signed-off-by: Heiko Carstens --- arch/s390/configs/debug_defconfig | 1 + arch/s390/configs/defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 0713914b25b4..7a91d300c549 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -446,6 +446,7 @@ CONFIG_BLK_DEV_RAM_SIZE=32768 CONFIG_VIRTIO_BLK=y CONFIG_BLK_DEV_RBD=m CONFIG_BLK_DEV_NVME=m +CONFIG_BLK_DEV_NULL_BLK=m CONFIG_ENCLOSURE_SERVICES=m CONFIG_GENWQE=m CONFIG_RAID_ATTRS=m diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index c064e0cacc98..3bb2aa8ecd13 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -436,6 +436,7 @@ CONFIG_BLK_DEV_RAM_SIZE=32768 CONFIG_VIRTIO_BLK=y CONFIG_BLK_DEV_RBD=m CONFIG_BLK_DEV_NVME=m +CONFIG_BLK_DEV_NULL_BLK=m CONFIG_ENCLOSURE_SERVICES=m CONFIG_GENWQE=m CONFIG_RAID_ATTRS=m From f8a9c11000e52a8e59f15e49edaf5a2857705f9a Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 3 Feb 2026 10:18:03 +0100 Subject: [PATCH 26/35] s390/kexec: Emit an error message when cmdline is too long Currently, if the command line passed to kexec_file_load() exceeds the supported limit of the kernel being kexec'd, -EINVAL is returned to userspace, which is consistent across architectures. Since -EINVAL is not specific to this case, the kexec tool cannot provide a specific reason for the failure. Many architectures emit an error message in this case. Add a similar error message, including the effective limit, since the command line length is configurable. Acked-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/kernel/machine_kexec_file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c index a36d7311c668..1bf59c3f0e2b 100644 --- a/arch/s390/kernel/machine_kexec_file.c +++ b/arch/s390/kernel/machine_kexec_file.c @@ -270,8 +270,10 @@ void *kexec_file_add_components(struct kimage *image, if (image->kernel_buf_len < minsize + max_command_line_size) goto out; - if (image->cmdline_buf_len >= max_command_line_size) + if (image->cmdline_buf_len >= max_command_line_size) { + pr_err("Kernel command line exceeds supported limit of %lu", max_command_line_size); goto out; + } memcpy(data.parm->command_line, image->cmdline_buf, image->cmdline_buf_len); From c03b6ef74c2b48a0f544f27c7354d5200ab6569c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:39 +0100 Subject: [PATCH 27/35] s390/tape: Remove support for 3590/3592 models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Physical 3590/3592 tape models are not supported anymore for a very long time. The Virtual Tape Server (VTS) emulates and presents only 3490E models to the host. This is the only supported model and storage server. Remove the entire code base for 3590/3592 models as it can be considered dead code for quite some time already. Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/tape390.h | 64 - drivers/s390/char/Kconfig | 9 - drivers/s390/char/Makefile | 1 - drivers/s390/char/tape.h | 6 +- drivers/s390/char/tape_3590.c | 1612 -------------------------- drivers/s390/char/tape_3590.h | 175 --- drivers/s390/char/tape_core.c | 4 +- drivers/s390/char/tape_std.h | 2 - 8 files changed, 2 insertions(+), 1871 deletions(-) delete mode 100644 drivers/s390/char/tape_3590.c delete mode 100644 drivers/s390/char/tape_3590.h diff --git a/arch/s390/include/uapi/asm/tape390.h b/arch/s390/include/uapi/asm/tape390.h index 90266c696486..880065cfa039 100644 --- a/arch/s390/include/uapi/asm/tape390.h +++ b/arch/s390/include/uapi/asm/tape390.h @@ -36,68 +36,4 @@ typedef struct display_struct { char message2[8]; } display_struct; -/* - * Tape encryption support - */ - -struct tape390_crypt_info { - char capability; - char status; - char medium_status; -} __attribute__ ((packed)); - - -/* Macros for "capable" field */ -#define TAPE390_CRYPT_SUPPORTED_MASK 0x01 -#define TAPE390_CRYPT_SUPPORTED(x) \ - ((x.capability & TAPE390_CRYPT_SUPPORTED_MASK)) - -/* Macros for "status" field */ -#define TAPE390_CRYPT_ON_MASK 0x01 -#define TAPE390_CRYPT_ON(x) (((x.status) & TAPE390_CRYPT_ON_MASK)) - -/* Macros for "medium status" field */ -#define TAPE390_MEDIUM_LOADED_MASK 0x01 -#define TAPE390_MEDIUM_ENCRYPTED_MASK 0x02 -#define TAPE390_MEDIUM_ENCRYPTED(x) \ - (((x.medium_status) & TAPE390_MEDIUM_ENCRYPTED_MASK)) -#define TAPE390_MEDIUM_LOADED(x) \ - (((x.medium_status) & TAPE390_MEDIUM_LOADED_MASK)) - -/* - * The TAPE390_CRYPT_SET ioctl is used to switch on/off encryption. - * The "encryption_capable" and "tape_status" fields are ignored for this ioctl! - */ -#define TAPE390_CRYPT_SET _IOW('d', 2, struct tape390_crypt_info) - -/* - * The TAPE390_CRYPT_QUERY ioctl is used to query the encryption state. - */ -#define TAPE390_CRYPT_QUERY _IOR('d', 3, struct tape390_crypt_info) - -/* Values for "kekl1/2_type" and "kekl1/2_type_on_tape" fields */ -#define TAPE390_KEKL_TYPE_NONE 0 -#define TAPE390_KEKL_TYPE_LABEL 1 -#define TAPE390_KEKL_TYPE_HASH 2 - -struct tape390_kekl { - unsigned char type; - unsigned char type_on_tape; - char label[65]; -} __attribute__ ((packed)); - -struct tape390_kekl_pair { - struct tape390_kekl kekl[2]; -} __attribute__ ((packed)); - -/* - * The TAPE390_KEKL_SET ioctl is used to set Key Encrypting Key labels. - */ -#define TAPE390_KEKL_SET _IOW('d', 4, struct tape390_kekl_pair) - -/* - * The TAPE390_KEKL_QUERY ioctl is used to query Key Encrypting Key labels. - */ -#define TAPE390_KEKL_QUERY _IOR('d', 5, struct tape390_kekl_pair) - #endif diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 80c4e5101c97..d57083209c95 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -130,15 +130,6 @@ config S390_TAPE_34XX tape subsystems and 100% compatibles. It is safe to say "Y" here. -config S390_TAPE_3590 - def_tristate m - prompt "Support for 3590 tape hardware" - depends on S390_TAPE - help - Select this option if you want to access IBM 3590 magnetic - tape subsystems and 100% compatibles. - It is safe to say "Y" here. - config VMLOGRDR def_tristate m prompt "Support for the z/VM recording system services (VM only)" diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index dcbd51152ee3..60b9911653f3 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -42,7 +42,6 @@ tape-$(CONFIG_PROC_FS) += tape_proc.o tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y) obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o -obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o obj-$(CONFIG_S390_VMUR) += vmur.o diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 3953b31b0c55..b65a18f3dc94 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * tape device driver for 3480/3490E/3590 tapes. + * tape device driver for 3480/3490E tapes. * * S390 and zSeries version * Copyright IBM Corp. 2001, 2009 @@ -98,10 +98,6 @@ enum tape_op { TO_DIS, /* Tape display */ TO_ASSIGN, /* Assign tape to channel path */ TO_UNASSIGN, /* Unassign tape from channel path */ - TO_CRYPT_ON, /* Enable encrpytion */ - TO_CRYPT_OFF, /* Disable encrpytion */ - TO_KEKL_SET, /* Set KEK label */ - TO_KEKL_QUERY, /* Query KEK label */ TO_RDC, /* Read device characteristics */ TO_SIZE, /* #entries in tape_op_t */ }; diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c deleted file mode 100644 index 0d80f43b175d..000000000000 --- a/drivers/s390/char/tape_3590.c +++ /dev/null @@ -1,1612 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * tape device discipline for 3590 tapes. - * - * Copyright IBM Corp. 2001, 2009 - * Author(s): Stefan Bader - * Michael Holzheu - * Martin Schwidefsky - */ - -#define pr_fmt(fmt) "tape_3590: " fmt - -#include -#include -#include -#include -#include -#include - -#define TAPE_DBF_AREA tape_3590_dbf -#define BUFSIZE 512 /* size of buffers for dynamic generated messages */ - -#include "tape.h" -#include "tape_std.h" -#include "tape_3590.h" - -static struct workqueue_struct *tape_3590_wq; - -/* - * Pointer to debug area. - */ -debug_info_t *TAPE_DBF_AREA = NULL; -EXPORT_SYMBOL(TAPE_DBF_AREA); - -/******************************************************************* - * Error Recovery functions: - * - Read Opposite: implemented - * - Read Device (buffered) log: BRA - * - Read Library log: BRA - * - Swap Devices: BRA - * - Long Busy: implemented - * - Special Intercept: BRA - * - Read Alternate: implemented - *******************************************************************/ - -static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = { - [0x00] = "", - [0x10] = "Lost Sense", - [0x11] = "Assigned Elsewhere", - [0x12] = "Allegiance Reset", - [0x13] = "Shared Access Violation", - [0x20] = "Command Reject", - [0x21] = "Configuration Error", - [0x22] = "Protection Exception", - [0x23] = "Write Protect", - [0x24] = "Write Length", - [0x25] = "Read-Only Format", - [0x31] = "Beginning of Partition", - [0x33] = "End of Partition", - [0x34] = "End of Data", - [0x35] = "Block not found", - [0x40] = "Device Intervention", - [0x41] = "Loader Intervention", - [0x42] = "Library Intervention", - [0x50] = "Write Error", - [0x51] = "Erase Error", - [0x52] = "Formatting Error", - [0x53] = "Read Error", - [0x54] = "Unsupported Format", - [0x55] = "No Formatting", - [0x56] = "Positioning lost", - [0x57] = "Read Length", - [0x60] = "Unsupported Medium", - [0x61] = "Medium Length Error", - [0x62] = "Medium removed", - [0x64] = "Load Check", - [0x65] = "Unload Check", - [0x70] = "Equipment Check", - [0x71] = "Bus out Check", - [0x72] = "Protocol Error", - [0x73] = "Interface Error", - [0x74] = "Overrun", - [0x75] = "Halt Signal", - [0x90] = "Device fenced", - [0x91] = "Device Path fenced", - [0xa0] = "Volume misplaced", - [0xa1] = "Volume inaccessible", - [0xa2] = "Volume in input", - [0xa3] = "Volume ejected", - [0xa4] = "All categories reserved", - [0xa5] = "Duplicate Volume", - [0xa6] = "Library Manager Offline", - [0xa7] = "Library Output Station full", - [0xa8] = "Vision System non-operational", - [0xa9] = "Library Manager Equipment Check", - [0xaa] = "Library Equipment Check", - [0xab] = "All Library Cells full", - [0xac] = "No Cleaner Volumes in Library", - [0xad] = "I/O Station door open", - [0xae] = "Subsystem environmental alert", -}; - -static int crypt_supported(struct tape_device *device) -{ - return TAPE390_CRYPT_SUPPORTED(TAPE_3590_CRYPT_INFO(device)); -} - -static int crypt_enabled(struct tape_device *device) -{ - return TAPE390_CRYPT_ON(TAPE_3590_CRYPT_INFO(device)); -} - -static void ext_to_int_kekl(struct tape390_kekl *in, - struct tape3592_kekl *out) -{ - int len; - - memset(out, 0, sizeof(*out)); - if (in->type == TAPE390_KEKL_TYPE_HASH) - out->flags |= 0x40; - if (in->type_on_tape == TAPE390_KEKL_TYPE_HASH) - out->flags |= 0x80; - len = min(sizeof(out->label), strlen(in->label)); - memcpy(out->label, in->label, len); - memset(out->label + len, ' ', sizeof(out->label) - len); - ASCEBC(out->label, sizeof(out->label)); -} - -static void int_to_ext_kekl(struct tape3592_kekl *in, - struct tape390_kekl *out) -{ - memset(out, 0, sizeof(*out)); - if(in->flags & 0x40) - out->type = TAPE390_KEKL_TYPE_HASH; - else - out->type = TAPE390_KEKL_TYPE_LABEL; - if(in->flags & 0x80) - out->type_on_tape = TAPE390_KEKL_TYPE_HASH; - else - out->type_on_tape = TAPE390_KEKL_TYPE_LABEL; - memcpy(out->label, in->label, sizeof(in->label)); - EBCASC(out->label, sizeof(in->label)); - strim(out->label); -} - -static void int_to_ext_kekl_pair(struct tape3592_kekl_pair *in, - struct tape390_kekl_pair *out) -{ - if (in->count == 0) { - out->kekl[0].type = TAPE390_KEKL_TYPE_NONE; - out->kekl[0].type_on_tape = TAPE390_KEKL_TYPE_NONE; - out->kekl[1].type = TAPE390_KEKL_TYPE_NONE; - out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE; - } else if (in->count == 1) { - int_to_ext_kekl(&in->kekl[0], &out->kekl[0]); - out->kekl[1].type = TAPE390_KEKL_TYPE_NONE; - out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE; - } else if (in->count == 2) { - int_to_ext_kekl(&in->kekl[0], &out->kekl[0]); - int_to_ext_kekl(&in->kekl[1], &out->kekl[1]); - } else { - printk("Invalid KEKL number: %d\n", in->count); - BUG(); - } -} - -static int check_ext_kekl(struct tape390_kekl *kekl) -{ - if (kekl->type == TAPE390_KEKL_TYPE_NONE) - goto invalid; - if (kekl->type > TAPE390_KEKL_TYPE_HASH) - goto invalid; - if (kekl->type_on_tape == TAPE390_KEKL_TYPE_NONE) - goto invalid; - if (kekl->type_on_tape > TAPE390_KEKL_TYPE_HASH) - goto invalid; - if ((kekl->type == TAPE390_KEKL_TYPE_HASH) && - (kekl->type_on_tape == TAPE390_KEKL_TYPE_LABEL)) - goto invalid; - - return 0; -invalid: - return -EINVAL; -} - -static int check_ext_kekl_pair(struct tape390_kekl_pair *kekls) -{ - if (check_ext_kekl(&kekls->kekl[0])) - goto invalid; - if (check_ext_kekl(&kekls->kekl[1])) - goto invalid; - - return 0; -invalid: - return -EINVAL; -} - -/* - * Query KEKLs - */ -static int tape_3592_kekl_query(struct tape_device *device, - struct tape390_kekl_pair *ext_kekls) -{ - struct tape_request *request; - struct tape3592_kekl_query_order *order; - struct tape3592_kekl_query_data *int_kekls; - int rc; - - DBF_EVENT(6, "tape3592_kekl_query\n"); - int_kekls = kmalloc(sizeof(*int_kekls), GFP_KERNEL|GFP_DMA); - if (!int_kekls) - return -ENOMEM; - request = tape_alloc_request(2, sizeof(*order)); - if (IS_ERR(request)) { - rc = PTR_ERR(request); - goto fail_malloc; - } - order = request->cpdata; - memset(order,0,sizeof(*order)); - order->code = 0xe2; - order->max_count = 2; - request->op = TO_KEKL_QUERY; - tape_ccw_cc(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order); - tape_ccw_end(request->cpaddr + 1, READ_SS_DATA, sizeof(*int_kekls), - int_kekls); - rc = tape_do_io(device, request); - if (rc) - goto fail_request; - int_to_ext_kekl_pair(&int_kekls->kekls, ext_kekls); - - rc = 0; -fail_request: - tape_free_request(request); -fail_malloc: - kfree(int_kekls); - return rc; -} - -/* - * IOCTL: Query KEKLs - */ -static int tape_3592_ioctl_kekl_query(struct tape_device *device, - unsigned long arg) -{ - int rc; - struct tape390_kekl_pair *ext_kekls; - - DBF_EVENT(6, "tape_3592_ioctl_kekl_query\n"); - if (!crypt_supported(device)) - return -ENOSYS; - if (!crypt_enabled(device)) - return -EUNATCH; - ext_kekls = kmalloc(sizeof(*ext_kekls), GFP_KERNEL); - if (!ext_kekls) - return -ENOMEM; - rc = tape_3592_kekl_query(device, ext_kekls); - if (rc != 0) - goto fail; - if (copy_to_user((char __user *) arg, ext_kekls, sizeof(*ext_kekls))) { - rc = -EFAULT; - goto fail; - } - rc = 0; -fail: - kfree(ext_kekls); - return rc; -} - -static int tape_3590_mttell(struct tape_device *device, int mt_count); - -/* - * Set KEKLs - */ -static int tape_3592_kekl_set(struct tape_device *device, - struct tape390_kekl_pair *ext_kekls) -{ - struct tape_request *request; - struct tape3592_kekl_set_order *order; - - DBF_EVENT(6, "tape3592_kekl_set\n"); - if (check_ext_kekl_pair(ext_kekls)) { - DBF_EVENT(6, "invalid kekls\n"); - return -EINVAL; - } - if (tape_3590_mttell(device, 0) != 0) - return -EBADSLT; - request = tape_alloc_request(1, sizeof(*order)); - if (IS_ERR(request)) - return PTR_ERR(request); - order = request->cpdata; - memset(order, 0, sizeof(*order)); - order->code = 0xe3; - order->kekls.count = 2; - ext_to_int_kekl(&ext_kekls->kekl[0], &order->kekls.kekl[0]); - ext_to_int_kekl(&ext_kekls->kekl[1], &order->kekls.kekl[1]); - request->op = TO_KEKL_SET; - tape_ccw_end(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order); - - return tape_do_io_free(device, request); -} - -/* - * IOCTL: Set KEKLs - */ -static int tape_3592_ioctl_kekl_set(struct tape_device *device, - unsigned long arg) -{ - int rc; - struct tape390_kekl_pair *ext_kekls; - - DBF_EVENT(6, "tape_3592_ioctl_kekl_set\n"); - if (!crypt_supported(device)) - return -ENOSYS; - if (!crypt_enabled(device)) - return -EUNATCH; - ext_kekls = memdup_user((char __user *)arg, sizeof(*ext_kekls)); - if (IS_ERR(ext_kekls)) - return PTR_ERR(ext_kekls); - rc = tape_3592_kekl_set(device, ext_kekls); - kfree(ext_kekls); - return rc; -} - -/* - * Enable encryption - */ -static struct tape_request *__tape_3592_enable_crypt(struct tape_device *device) -{ - struct tape_request *request; - char *data; - - DBF_EVENT(6, "tape_3592_enable_crypt\n"); - if (!crypt_supported(device)) - return ERR_PTR(-ENOSYS); - request = tape_alloc_request(2, 72); - if (IS_ERR(request)) - return request; - data = request->cpdata; - memset(data,0,72); - - data[0] = 0x05; - data[36 + 0] = 0x03; - data[36 + 1] = 0x03; - data[36 + 4] = 0x40; - data[36 + 6] = 0x01; - data[36 + 14] = 0x2f; - data[36 + 18] = 0xc3; - data[36 + 35] = 0x72; - request->op = TO_CRYPT_ON; - tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data); - tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36); - return request; -} - -static int tape_3592_enable_crypt(struct tape_device *device) -{ - struct tape_request *request; - - request = __tape_3592_enable_crypt(device); - if (IS_ERR(request)) - return PTR_ERR(request); - return tape_do_io_free(device, request); -} - -static void tape_3592_enable_crypt_async(struct tape_device *device) -{ - struct tape_request *request; - - request = __tape_3592_enable_crypt(device); - if (!IS_ERR(request)) - tape_do_io_async_free(device, request); -} - -/* - * Disable encryption - */ -static struct tape_request *__tape_3592_disable_crypt(struct tape_device *device) -{ - struct tape_request *request; - char *data; - - DBF_EVENT(6, "tape_3592_disable_crypt\n"); - if (!crypt_supported(device)) - return ERR_PTR(-ENOSYS); - request = tape_alloc_request(2, 72); - if (IS_ERR(request)) - return request; - data = request->cpdata; - memset(data,0,72); - - data[0] = 0x05; - data[36 + 0] = 0x03; - data[36 + 1] = 0x03; - data[36 + 35] = 0x32; - - request->op = TO_CRYPT_OFF; - tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data); - tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36); - - return request; -} - -static int tape_3592_disable_crypt(struct tape_device *device) -{ - struct tape_request *request; - - request = __tape_3592_disable_crypt(device); - if (IS_ERR(request)) - return PTR_ERR(request); - return tape_do_io_free(device, request); -} - -static void tape_3592_disable_crypt_async(struct tape_device *device) -{ - struct tape_request *request; - - request = __tape_3592_disable_crypt(device); - if (!IS_ERR(request)) - tape_do_io_async_free(device, request); -} - -/* - * IOCTL: Set encryption status - */ -static int tape_3592_ioctl_crypt_set(struct tape_device *device, - unsigned long arg) -{ - struct tape390_crypt_info info; - - DBF_EVENT(6, "tape_3592_ioctl_crypt_set\n"); - if (!crypt_supported(device)) - return -ENOSYS; - if (copy_from_user(&info, (char __user *)arg, sizeof(info))) - return -EFAULT; - if (info.status & ~TAPE390_CRYPT_ON_MASK) - return -EINVAL; - if (info.status & TAPE390_CRYPT_ON_MASK) - return tape_3592_enable_crypt(device); - else - return tape_3592_disable_crypt(device); -} - -static int tape_3590_sense_medium(struct tape_device *device); - -/* - * IOCTL: Query enryption status - */ -static int tape_3592_ioctl_crypt_query(struct tape_device *device, - unsigned long arg) -{ - DBF_EVENT(6, "tape_3592_ioctl_crypt_query\n"); - if (!crypt_supported(device)) - return -ENOSYS; - tape_3590_sense_medium(device); - if (copy_to_user((char __user *) arg, &TAPE_3590_CRYPT_INFO(device), - sizeof(TAPE_3590_CRYPT_INFO(device)))) - return -EFAULT; - else - return 0; -} - -/* - * 3590 IOCTL Overload - */ -static int -tape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TAPE390_DISPLAY: { - struct display_struct disp; - - if (copy_from_user(&disp, (char __user *) arg, sizeof(disp))) - return -EFAULT; - - return tape_std_display(device, &disp); - } - case TAPE390_KEKL_SET: - return tape_3592_ioctl_kekl_set(device, arg); - case TAPE390_KEKL_QUERY: - return tape_3592_ioctl_kekl_query(device, arg); - case TAPE390_CRYPT_SET: - return tape_3592_ioctl_crypt_set(device, arg); - case TAPE390_CRYPT_QUERY: - return tape_3592_ioctl_crypt_query(device, arg); - default: - return -EINVAL; /* no additional ioctls */ - } -} - -/* - * SENSE Medium: Get Sense data about medium state - */ -static int tape_3590_sense_medium(struct tape_device *device) -{ - struct tape_request *request; - - request = tape_alloc_request(1, 128); - if (IS_ERR(request)) - return PTR_ERR(request); - request->op = TO_MSEN; - tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata); - return tape_do_io_free(device, request); -} - -static void tape_3590_sense_medium_async(struct tape_device *device) -{ - struct tape_request *request; - - request = tape_alloc_request(1, 128); - if (IS_ERR(request)) - return; - request->op = TO_MSEN; - tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata); - tape_do_io_async_free(device, request); -} - -/* - * MTTELL: Tell block. Return the number of block relative to current file. - */ -static int -tape_3590_mttell(struct tape_device *device, int mt_count) -{ - __u64 block_id; - int rc; - - rc = tape_std_read_block_id(device, &block_id); - if (rc) - return rc; - return block_id >> 32; -} - -/* - * MTSEEK: seek to the specified block. - */ -static int -tape_3590_mtseek(struct tape_device *device, int count) -{ - struct tape_request *request; - - DBF_EVENT(6, "xsee id: %x\n", count); - request = tape_alloc_request(3, 4); - if (IS_ERR(request)) - return PTR_ERR(request); - request->op = TO_LBL; - tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); - *(__u32 *) request->cpdata = count; - tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); - tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); - return tape_do_io_free(device, request); -} - -/* - * Read Attention Msg - * This should be done after an interrupt with attention bit (0x80) - * in device state. - * - * After a "read attention message" request there are two possible - * results: - * - * 1. A unit check is presented, when attention sense is present (e.g. when - * a medium has been unloaded). The attention sense comes then - * together with the unit check. The recovery action is either "retry" - * (in case there is an attention message pending) or "permanent error". - * - * 2. The attention msg is written to the "read subsystem data" buffer. - * In this case we probably should print it to the console. - */ -static void tape_3590_read_attmsg_async(struct tape_device *device) -{ - struct tape_request *request; - char *buf; - - request = tape_alloc_request(3, 4096); - if (IS_ERR(request)) - return; - request->op = TO_READ_ATTMSG; - buf = request->cpdata; - buf[0] = PREP_RD_SS_DATA; - buf[6] = RD_ATTMSG; /* read att msg */ - tape_ccw_cc(request->cpaddr, PERFORM_SS_FUNC, 12, buf); - tape_ccw_cc(request->cpaddr + 1, READ_SS_DATA, 4096 - 12, buf + 12); - tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); - tape_do_io_async_free(device, request); -} - -/* - * These functions are used to schedule follow-up actions from within an - * interrupt context (like unsolicited interrupts). - * Note: the work handler is called by the system work queue. The tape - * commands started by the handler need to be asynchrounous, otherwise - * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq). - */ -struct work_handler_data { - struct tape_device *device; - enum tape_op op; - struct work_struct work; -}; - -static void -tape_3590_work_handler(struct work_struct *work) -{ - struct work_handler_data *p = - container_of(work, struct work_handler_data, work); - - switch (p->op) { - case TO_MSEN: - tape_3590_sense_medium_async(p->device); - break; - case TO_READ_ATTMSG: - tape_3590_read_attmsg_async(p->device); - break; - case TO_CRYPT_ON: - tape_3592_enable_crypt_async(p->device); - break; - case TO_CRYPT_OFF: - tape_3592_disable_crypt_async(p->device); - break; - default: - DBF_EVENT(3, "T3590: work handler undefined for " - "operation 0x%02x\n", p->op); - } - tape_put_device(p->device); - kfree(p); -} - -static int -tape_3590_schedule_work(struct tape_device *device, enum tape_op op) -{ - struct work_handler_data *p; - - if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) - return -ENOMEM; - - INIT_WORK(&p->work, tape_3590_work_handler); - - p->device = tape_get_device(device); - p->op = op; - - queue_work(tape_3590_wq, &p->work); - return 0; -} - -static void tape_3590_med_state_set(struct tape_device *device, - struct tape_3590_med_sense *sense) -{ - struct tape390_crypt_info *c_info; - - c_info = &TAPE_3590_CRYPT_INFO(device); - - DBF_EVENT(6, "medium state: %x:%x\n", sense->macst, sense->masst); - switch (sense->macst) { - case 0x04: - case 0x05: - case 0x06: - tape_med_state_set(device, MS_UNLOADED); - TAPE_3590_CRYPT_INFO(device).medium_status = 0; - return; - case 0x08: - case 0x09: - tape_med_state_set(device, MS_LOADED); - break; - default: - tape_med_state_set(device, MS_UNKNOWN); - return; - } - c_info->medium_status |= TAPE390_MEDIUM_LOADED_MASK; - if (sense->flags & MSENSE_CRYPT_MASK) { - DBF_EVENT(6, "Medium is encrypted (%04x)\n", sense->flags); - c_info->medium_status |= TAPE390_MEDIUM_ENCRYPTED_MASK; - } else { - DBF_EVENT(6, "Medium is not encrypted %04x\n", sense->flags); - c_info->medium_status &= ~TAPE390_MEDIUM_ENCRYPTED_MASK; - } -} - -/* - * The done handler is called at device/channel end and wakes up the sleeping - * process - */ -static int -tape_3590_done(struct tape_device *device, struct tape_request *request) -{ - - DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); - - switch (request->op) { - case TO_BSB: - case TO_BSF: - case TO_DSE: - case TO_FSB: - case TO_FSF: - case TO_LBL: - case TO_RFO: - case TO_RBA: - case TO_REW: - case TO_WRI: - case TO_WTM: - case TO_BLOCK: - case TO_LOAD: - tape_med_state_set(device, MS_LOADED); - break; - case TO_RUN: - tape_med_state_set(device, MS_UNLOADED); - tape_3590_schedule_work(device, TO_CRYPT_OFF); - break; - case TO_MSEN: - tape_3590_med_state_set(device, request->cpdata); - break; - case TO_CRYPT_ON: - TAPE_3590_CRYPT_INFO(device).status - |= TAPE390_CRYPT_ON_MASK; - *(device->modeset_byte) |= 0x03; - break; - case TO_CRYPT_OFF: - TAPE_3590_CRYPT_INFO(device).status - &= ~TAPE390_CRYPT_ON_MASK; - *(device->modeset_byte) &= ~0x03; - break; - case TO_RBI: /* RBI seems to succeed even without medium loaded. */ - case TO_NOP: /* Same to NOP. */ - case TO_READ_CONFIG: - case TO_READ_ATTMSG: - case TO_DIS: - case TO_ASSIGN: - case TO_UNASSIGN: - case TO_SIZE: - case TO_KEKL_SET: - case TO_KEKL_QUERY: - case TO_RDC: - break; - } - return TAPE_IO_SUCCESS; -} - -/* - * This function is called, when error recovery was successful - */ -static inline int -tape_3590_erp_succeeded(struct tape_device *device, struct tape_request *request) -{ - DBF_EVENT(3, "Error Recovery successful for %s\n", - tape_op_verbose[request->op]); - return tape_3590_done(device, request); -} - -/* - * This function is called, when error recovery was not successful - */ -static inline int -tape_3590_erp_failed(struct tape_device *device, struct tape_request *request, - struct irb *irb, int rc) -{ - DBF_EVENT(3, "Error Recovery failed for %s\n", - tape_op_verbose[request->op]); - tape_dump_sense_dbf(device, request, irb); - return rc; -} - -/* - * Error Recovery do retry - */ -static inline int -tape_3590_erp_retry(struct tape_device *device, struct tape_request *request, - struct irb *irb) -{ - DBF_EVENT(2, "Retry: %s\n", tape_op_verbose[request->op]); - tape_dump_sense_dbf(device, request, irb); - return TAPE_IO_RETRY; -} - -/* - * Handle unsolicited interrupts - */ -static int -tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb) -{ - if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END) - /* Probably result of halt ssch */ - return TAPE_IO_PENDING; - else if (irb->scsw.cmd.dstat == 0x85) - /* Device Ready */ - DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id); - else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { - tape_3590_schedule_work(device, TO_READ_ATTMSG); - } else { - DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); - tape_dump_sense_dbf(device, NULL, irb); - } - /* check medium state */ - tape_3590_schedule_work(device, TO_MSEN); - return TAPE_IO_SUCCESS; -} - -/* - * Basic Recovery routine - */ -static int -tape_3590_erp_basic(struct tape_device *device, struct tape_request *request, - struct irb *irb, int rc) -{ - struct tape_3590_sense *sense; - - sense = (struct tape_3590_sense *) irb->ecw; - - switch (sense->bra) { - case SENSE_BRA_PER: - return tape_3590_erp_failed(device, request, irb, rc); - case SENSE_BRA_CONT: - return tape_3590_erp_succeeded(device, request); - case SENSE_BRA_RE: - return tape_3590_erp_retry(device, request, irb); - case SENSE_BRA_DRE: - return tape_3590_erp_failed(device, request, irb, rc); - default: - BUG(); - return TAPE_IO_STOP; - } -} - -/* - * RDL: Read Device (buffered) log - */ -static int -tape_3590_erp_read_buf_log(struct tape_device *device, - struct tape_request *request, struct irb *irb) -{ - /* - * We just do the basic error recovery at the moment (retry). - * Perhaps in the future, we read the log and dump it somewhere... - */ - return tape_3590_erp_basic(device, request, irb, -EIO); -} - -/* - * SWAP: Swap Devices - */ -static int -tape_3590_erp_swap(struct tape_device *device, struct tape_request *request, - struct irb *irb) -{ - /* - * This error recovery should swap the tapes - * if the original has a problem. The operation - * should proceed with the new tape... this - * should probably be done in user space! - */ - dev_warn (&device->cdev->dev, "The tape medium must be loaded into a " - "different tape unit\n"); - return tape_3590_erp_basic(device, request, irb, -EIO); -} - -/* - * LBY: Long Busy - */ -static int -tape_3590_erp_long_busy(struct tape_device *device, - struct tape_request *request, struct irb *irb) -{ - DBF_EVENT(6, "Device is busy\n"); - return TAPE_IO_LONG_BUSY; -} - -/* - * SPI: Special Intercept - */ -static int -tape_3590_erp_special_interrupt(struct tape_device *device, - struct tape_request *request, struct irb *irb) -{ - return tape_3590_erp_basic(device, request, irb, -EIO); -} - -/* - * Print an MIM (Media Information Message) (message code f0) - */ -static void -tape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb) -{ - struct tape_3590_sense *sense; - char *exception, *service; - - exception = kmalloc(BUFSIZE, GFP_ATOMIC); - service = kmalloc(BUFSIZE, GFP_ATOMIC); - - if (!exception || !service) - goto out_nomem; - - sense = (struct tape_3590_sense *) irb->ecw; - /* Exception Message */ - switch (sense->fmt.f70.emc) { - case 0x02: - snprintf(exception, BUFSIZE, "Data degraded"); - break; - case 0x03: - snprintf(exception, BUFSIZE, "Data degraded in partition %i", - sense->fmt.f70.mp); - break; - case 0x04: - snprintf(exception, BUFSIZE, "Medium degraded"); - break; - case 0x05: - snprintf(exception, BUFSIZE, "Medium degraded in partition %i", - sense->fmt.f70.mp); - break; - case 0x06: - snprintf(exception, BUFSIZE, "Block 0 Error"); - break; - case 0x07: - snprintf(exception, BUFSIZE, "Medium Exception 0x%02x", - sense->fmt.f70.md); - break; - default: - snprintf(exception, BUFSIZE, "0x%02x", - sense->fmt.f70.emc); - break; - } - /* Service Message */ - switch (sense->fmt.f70.smc) { - case 0x02: - snprintf(service, BUFSIZE, "Reference Media maintenance " - "procedure %i", sense->fmt.f70.md); - break; - default: - snprintf(service, BUFSIZE, "0x%02x", - sense->fmt.f70.smc); - break; - } - - dev_warn (&device->cdev->dev, "Tape media information: exception %s, " - "service %s\n", exception, service); - -out_nomem: - kfree(exception); - kfree(service); -} - -/* - * Print an I/O Subsystem Service Information Message (message code f1) - */ -static void -tape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb) -{ - struct tape_3590_sense *sense; - char *exception, *service; - - exception = kmalloc(BUFSIZE, GFP_ATOMIC); - service = kmalloc(BUFSIZE, GFP_ATOMIC); - - if (!exception || !service) - goto out_nomem; - - sense = (struct tape_3590_sense *) irb->ecw; - /* Exception Message */ - switch (sense->fmt.f71.emc) { - case 0x01: - snprintf(exception, BUFSIZE, "Effect of failure is unknown"); - break; - case 0x02: - snprintf(exception, BUFSIZE, "CU Exception - no performance " - "impact"); - break; - case 0x03: - snprintf(exception, BUFSIZE, "CU Exception on channel " - "interface 0x%02x", sense->fmt.f71.md[0]); - break; - case 0x04: - snprintf(exception, BUFSIZE, "CU Exception on device path " - "0x%02x", sense->fmt.f71.md[0]); - break; - case 0x05: - snprintf(exception, BUFSIZE, "CU Exception on library path " - "0x%02x", sense->fmt.f71.md[0]); - break; - case 0x06: - snprintf(exception, BUFSIZE, "CU Exception on node 0x%02x", - sense->fmt.f71.md[0]); - break; - case 0x07: - snprintf(exception, BUFSIZE, "CU Exception on partition " - "0x%02x", sense->fmt.f71.md[0]); - break; - default: - snprintf(exception, BUFSIZE, "0x%02x", - sense->fmt.f71.emc); - } - /* Service Message */ - switch (sense->fmt.f71.smc) { - case 0x01: - snprintf(service, BUFSIZE, "Repair impact is unknown"); - break; - case 0x02: - snprintf(service, BUFSIZE, "Repair will not impact cu " - "performance"); - break; - case 0x03: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable node " - "0x%x on CU", sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable " - "nodes (0x%x-0x%x) on CU", sense->fmt.f71.md[1], - sense->fmt.f71.md[2]); - break; - case 0x04: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable " - "channel path 0x%x on CU", - sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable channel" - " paths (0x%x-0x%x) on CU", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x05: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable device" - " path 0x%x on CU", sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable device" - " paths (0x%x-0x%x) on CU", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x06: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable " - "library path 0x%x on CU", - sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable " - "library paths (0x%x-0x%x) on CU", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x07: - snprintf(service, BUFSIZE, "Repair will disable access to CU"); - break; - default: - snprintf(service, BUFSIZE, "0x%02x", - sense->fmt.f71.smc); - } - - dev_warn (&device->cdev->dev, "I/O subsystem information: exception" - " %s, service %s\n", exception, service); -out_nomem: - kfree(exception); - kfree(service); -} - -/* - * Print an Device Subsystem Service Information Message (message code f2) - */ -static void -tape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb) -{ - struct tape_3590_sense *sense; - char *exception, *service; - - exception = kmalloc(BUFSIZE, GFP_ATOMIC); - service = kmalloc(BUFSIZE, GFP_ATOMIC); - - if (!exception || !service) - goto out_nomem; - - sense = (struct tape_3590_sense *) irb->ecw; - /* Exception Message */ - switch (sense->fmt.f71.emc) { - case 0x01: - snprintf(exception, BUFSIZE, "Effect of failure is unknown"); - break; - case 0x02: - snprintf(exception, BUFSIZE, "DV Exception - no performance" - " impact"); - break; - case 0x03: - snprintf(exception, BUFSIZE, "DV Exception on channel " - "interface 0x%02x", sense->fmt.f71.md[0]); - break; - case 0x04: - snprintf(exception, BUFSIZE, "DV Exception on loader 0x%02x", - sense->fmt.f71.md[0]); - break; - case 0x05: - snprintf(exception, BUFSIZE, "DV Exception on message display" - " 0x%02x", sense->fmt.f71.md[0]); - break; - case 0x06: - snprintf(exception, BUFSIZE, "DV Exception in tape path"); - break; - case 0x07: - snprintf(exception, BUFSIZE, "DV Exception in drive"); - break; - default: - snprintf(exception, BUFSIZE, "0x%02x", - sense->fmt.f71.emc); - } - /* Service Message */ - switch (sense->fmt.f71.smc) { - case 0x01: - snprintf(service, BUFSIZE, "Repair impact is unknown"); - break; - case 0x02: - snprintf(service, BUFSIZE, "Repair will not impact device " - "performance"); - break; - case 0x03: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable " - "channel path 0x%x on DV", - sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable " - "channel path (0x%x-0x%x) on DV", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x04: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable " - "interface 0x%x on DV", sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable " - "interfaces (0x%x-0x%x) on DV", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x05: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable loader" - " 0x%x on DV", sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable loader" - " (0x%x-0x%x) on DV", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x07: - snprintf(service, BUFSIZE, "Repair will disable access to DV"); - break; - case 0x08: - if (sense->fmt.f71.mdf == 0) - snprintf(service, BUFSIZE, "Repair will disable " - "message display 0x%x on DV", - sense->fmt.f71.md[1]); - else - snprintf(service, BUFSIZE, "Repair will disable " - "message displays (0x%x-0x%x) on DV", - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); - break; - case 0x09: - snprintf(service, BUFSIZE, "Clean DV"); - break; - default: - snprintf(service, BUFSIZE, "0x%02x", - sense->fmt.f71.smc); - } - - dev_warn (&device->cdev->dev, "Device subsystem information: exception" - " %s, service %s\n", exception, service); -out_nomem: - kfree(exception); - kfree(service); -} - -/* - * Print standard ERA Message - */ -static void -tape_3590_print_era_msg(struct tape_device *device, struct irb *irb) -{ - struct tape_3590_sense *sense; - - sense = (struct tape_3590_sense *) irb->ecw; - if (sense->mc == 0) - return; - if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) { - if (tape_3590_msg[sense->mc] != NULL) - dev_warn (&device->cdev->dev, "The tape unit has " - "issued sense message %s\n", - tape_3590_msg[sense->mc]); - else - dev_warn (&device->cdev->dev, "The tape unit has " - "issued an unknown sense message code 0x%x\n", - sense->mc); - return; - } - if (sense->mc == 0xf0) { - /* Standard Media Information Message */ - dev_warn (&device->cdev->dev, "MIM SEV=%i, MC=%02x, ES=%x/%x, " - "RC=%02x-%04x-%02x\n", sense->fmt.f70.sev, sense->mc, - sense->fmt.f70.emc, sense->fmt.f70.smc, - sense->fmt.f70.refcode, sense->fmt.f70.mid, - sense->fmt.f70.fid); - tape_3590_print_mim_msg_f0(device, irb); - return; - } - if (sense->mc == 0xf1) { - /* Standard I/O Subsystem Service Information Message */ - dev_warn (&device->cdev->dev, "IOSIM SEV=%i, DEVTYPE=3590/%02x," - " MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n", - sense->fmt.f71.sev, device->cdev->id.dev_model, - sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc, - sense->fmt.f71.refcode1, sense->fmt.f71.refcode2, - sense->fmt.f71.refcode3); - tape_3590_print_io_sim_msg_f1(device, irb); - return; - } - if (sense->mc == 0xf2) { - /* Standard Device Service Information Message */ - dev_warn (&device->cdev->dev, "DEVSIM SEV=%i, DEVTYPE=3590/%02x" - ", MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n", - sense->fmt.f71.sev, device->cdev->id.dev_model, - sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc, - sense->fmt.f71.refcode1, sense->fmt.f71.refcode2, - sense->fmt.f71.refcode3); - tape_3590_print_dev_sim_msg_f2(device, irb); - return; - } - if (sense->mc == 0xf3) { - /* Standard Library Service Information Message */ - return; - } - dev_warn (&device->cdev->dev, "The tape unit has issued an unknown " - "sense message code %x\n", sense->mc); -} - -static int tape_3590_crypt_error(struct tape_device *device, - struct tape_request *request, struct irb *irb) -{ - u8 cu_rc; - u16 ekm_rc2; - char *sense; - - sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data; - cu_rc = sense[0]; - ekm_rc2 = *((u16*) &sense[10]); - if ((cu_rc == 0) && (ekm_rc2 == 0xee31)) - /* key not defined on EKM */ - return tape_3590_erp_basic(device, request, irb, -EKEYREJECTED); - if ((cu_rc == 1) || (cu_rc == 2)) - /* No connection to EKM */ - return tape_3590_erp_basic(device, request, irb, -ENOTCONN); - - dev_err (&device->cdev->dev, "The tape unit failed to obtain the " - "encryption key from EKM\n"); - - return tape_3590_erp_basic(device, request, irb, -ENOKEY); -} - -/* - * 3590 error Recovery routine: - * If possible, it tries to recover from the error. If this is not possible, - * inform the user about the problem. - */ -static int -tape_3590_unit_check(struct tape_device *device, struct tape_request *request, - struct irb *irb) -{ - struct tape_3590_sense *sense; - - sense = (struct tape_3590_sense *) irb->ecw; - - DBF_EVENT(6, "Unit Check: RQC = %x\n", sense->rc_rqc); - - /* - * First check all RC-QRCs where we want to do something special - * - "break": basic error recovery is done - * - "goto out:": just print error message if available - */ - switch (sense->rc_rqc) { - - case 0x1110: - tape_3590_print_era_msg(device, irb); - return tape_3590_erp_read_buf_log(device, request, irb); - - case 0x2230: - case 0x2231: - tape_3590_print_era_msg(device, irb); - return tape_3590_erp_special_interrupt(device, request, irb); - case 0x2240: - return tape_3590_crypt_error(device, request, irb); - - case 0x3010: - DBF_EVENT(2, "(%08x): Backward at Beginning of Partition\n", - device->cdev_id); - return tape_3590_erp_basic(device, request, irb, -ENOSPC); - case 0x3012: - DBF_EVENT(2, "(%08x): Forward at End of Partition\n", - device->cdev_id); - return tape_3590_erp_basic(device, request, irb, -ENOSPC); - case 0x3020: - DBF_EVENT(2, "(%08x): End of Data Mark\n", device->cdev_id); - return tape_3590_erp_basic(device, request, irb, -ENOSPC); - - case 0x3122: - DBF_EVENT(2, "(%08x): Rewind Unload initiated\n", - device->cdev_id); - return tape_3590_erp_basic(device, request, irb, -EIO); - case 0x3123: - DBF_EVENT(2, "(%08x): Rewind Unload complete\n", - device->cdev_id); - tape_med_state_set(device, MS_UNLOADED); - tape_3590_schedule_work(device, TO_CRYPT_OFF); - return tape_3590_erp_basic(device, request, irb, 0); - - case 0x4010: - /* - * print additional msg since default msg - * "device intervention" is not very meaningfull - */ - tape_med_state_set(device, MS_UNLOADED); - tape_3590_schedule_work(device, TO_CRYPT_OFF); - return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM); - case 0x4012: /* Device Long Busy */ - /* XXX: Also use long busy handling here? */ - DBF_EVENT(6, "(%08x): LONG BUSY\n", device->cdev_id); - tape_3590_print_era_msg(device, irb); - return tape_3590_erp_basic(device, request, irb, -EBUSY); - case 0x4014: - DBF_EVENT(6, "(%08x): Crypto LONG BUSY\n", device->cdev_id); - return tape_3590_erp_long_busy(device, request, irb); - - case 0x5010: - if (sense->rac == 0xd0) { - /* Swap */ - tape_3590_print_era_msg(device, irb); - return tape_3590_erp_swap(device, request, irb); - } - return tape_3590_erp_basic(device, request, irb, -EIO); - case 0x5020: - case 0x5021: - case 0x5022: - case 0x5040: - case 0x5041: - case 0x5042: - tape_3590_print_era_msg(device, irb); - return tape_3590_erp_swap(device, request, irb); - - case 0x5110: - case 0x5111: - return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE); - - case 0x5120: - case 0x1120: - tape_med_state_set(device, MS_UNLOADED); - tape_3590_schedule_work(device, TO_CRYPT_OFF); - return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM); - - case 0x6020: - return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE); - - case 0x8011: - return tape_3590_erp_basic(device, request, irb, -EPERM); - case 0x8013: - dev_warn (&device->cdev->dev, "A different host has privileged" - " access to the tape unit\n"); - return tape_3590_erp_basic(device, request, irb, -EPERM); - default: - return tape_3590_erp_basic(device, request, irb, -EIO); - } -} - -/* - * 3590 interrupt handler: - */ -static int -tape_3590_irq(struct tape_device *device, struct tape_request *request, - struct irb *irb) -{ - if (request == NULL) - return tape_3590_unsolicited_irq(device, irb); - - if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) && - (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) && - (request->op == TO_WRI)) { - /* Write at end of volume */ - DBF_EVENT(2, "End of volume\n"); - return tape_3590_erp_failed(device, request, irb, -ENOSPC); - } - - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) - return tape_3590_unit_check(device, request, irb); - - if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { - if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) { - if (request->op == TO_FSB || request->op == TO_BSB) - request->rescnt++; - else - DBF_EVENT(5, "Unit Exception!\n"); - } - - return tape_3590_done(device, request); - } - - if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) { - DBF_EVENT(2, "channel end\n"); - return TAPE_IO_PENDING; - } - - if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { - DBF_EVENT(2, "Unit Attention when busy..\n"); - return TAPE_IO_PENDING; - } - - DBF_EVENT(6, "xunknownirq\n"); - tape_dump_sense_dbf(device, request, irb); - return TAPE_IO_STOP; -} - - -static int tape_3590_read_dev_chars(struct tape_device *device, - struct tape_3590_rdc_data *rdc_data) -{ - int rc; - struct tape_request *request; - - request = tape_alloc_request(1, sizeof(*rdc_data)); - if (IS_ERR(request)) - return PTR_ERR(request); - request->op = TO_RDC; - tape_ccw_end(request->cpaddr, CCW_CMD_RDC, sizeof(*rdc_data), - request->cpdata); - rc = tape_do_io(device, request); - if (rc == 0) - memcpy(rdc_data, request->cpdata, sizeof(*rdc_data)); - tape_free_request(request); - return rc; -} - -/* - * Setup device function - */ -static int -tape_3590_setup_device(struct tape_device *device) -{ - int rc; - struct tape_3590_disc_data *data; - struct tape_3590_rdc_data *rdc_data; - - DBF_EVENT(6, "3590 device setup\n"); - data = kzalloc(sizeof(struct tape_3590_disc_data), GFP_KERNEL | GFP_DMA); - if (data == NULL) - return -ENOMEM; - data->read_back_op = READ_PREVIOUS; - device->discdata = data; - - rdc_data = kmalloc(sizeof(*rdc_data), GFP_KERNEL | GFP_DMA); - if (!rdc_data) { - rc = -ENOMEM; - goto fail_kmalloc; - } - rc = tape_3590_read_dev_chars(device, rdc_data); - if (rc) { - DBF_LH(3, "Read device characteristics failed!\n"); - goto fail_rdc_data; - } - rc = tape_std_assign(device); - if (rc) - goto fail_rdc_data; - if (rdc_data->data[31] == 0x13) { - data->crypt_info.capability |= TAPE390_CRYPT_SUPPORTED_MASK; - tape_3592_disable_crypt(device); - } else { - DBF_EVENT(6, "Device has NO crypto support\n"); - } - /* Try to find out if medium is loaded */ - rc = tape_3590_sense_medium(device); - if (rc) { - DBF_LH(3, "3590 medium sense returned %d\n", rc); - goto fail_rdc_data; - } - return 0; - -fail_rdc_data: - kfree(rdc_data); -fail_kmalloc: - kfree(data); - return rc; -} - -/* - * Cleanup device function - */ -static void -tape_3590_cleanup_device(struct tape_device *device) -{ - flush_workqueue(tape_3590_wq); - tape_std_unassign(device); - - kfree(device->discdata); - device->discdata = NULL; -} - -/* - * List of 3590 magnetic tape commands. - */ -static tape_mtop_fn tape_3590_mtop[TAPE_NR_MTOPS] = { - [MTRESET] = tape_std_mtreset, - [MTFSF] = tape_std_mtfsf, - [MTBSF] = tape_std_mtbsf, - [MTFSR] = tape_std_mtfsr, - [MTBSR] = tape_std_mtbsr, - [MTWEOF] = tape_std_mtweof, - [MTREW] = tape_std_mtrew, - [MTOFFL] = tape_std_mtoffl, - [MTNOP] = tape_std_mtnop, - [MTRETEN] = tape_std_mtreten, - [MTBSFM] = tape_std_mtbsfm, - [MTFSFM] = tape_std_mtfsfm, - [MTEOM] = tape_std_mteom, - [MTERASE] = tape_std_mterase, - [MTRAS1] = NULL, - [MTRAS2] = NULL, - [MTRAS3] = NULL, - [MTSETBLK] = tape_std_mtsetblk, - [MTSETDENSITY] = NULL, - [MTSEEK] = tape_3590_mtseek, - [MTTELL] = tape_3590_mttell, - [MTSETDRVBUFFER] = NULL, - [MTFSS] = NULL, - [MTBSS] = NULL, - [MTWSM] = NULL, - [MTLOCK] = NULL, - [MTUNLOCK] = NULL, - [MTLOAD] = tape_std_mtload, - [MTUNLOAD] = tape_std_mtunload, - [MTCOMPRESSION] = tape_std_mtcompression, - [MTSETPART] = NULL, - [MTMKPART] = NULL -}; - -/* - * Tape discipline structure for 3590. - */ -static struct tape_discipline tape_discipline_3590 = { - .owner = THIS_MODULE, - .setup_device = tape_3590_setup_device, - .cleanup_device = tape_3590_cleanup_device, - .process_eov = tape_std_process_eov, - .irq = tape_3590_irq, - .read_block = tape_std_read_block, - .write_block = tape_std_write_block, - .ioctl_fn = tape_3590_ioctl, - .mtop_array = tape_3590_mtop -}; - -static struct ccw_device_id tape_3590_ids[] = { - {CCW_DEVICE_DEVTYPE(0x3590, 0, 0x3590, 0), .driver_info = tape_3590}, - {CCW_DEVICE_DEVTYPE(0x3592, 0, 0x3592, 0), .driver_info = tape_3592}, - { /* end of list */ } -}; - -static int -tape_3590_online(struct ccw_device *cdev) -{ - return tape_generic_online(dev_get_drvdata(&cdev->dev), - &tape_discipline_3590); -} - -static struct ccw_driver tape_3590_driver = { - .driver = { - .name = "tape_3590", - .owner = THIS_MODULE, - }, - .ids = tape_3590_ids, - .probe = tape_generic_probe, - .remove = tape_generic_remove, - .set_offline = tape_generic_offline, - .set_online = tape_3590_online, - .int_class = IRQIO_TAP, -}; - -/* - * Setup discipline structure. - */ -static int -tape_3590_init(void) -{ - int rc; - - TAPE_DBF_AREA = debug_register("tape_3590", 2, 2, 4 * sizeof(long)); - debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view); -#ifdef DBF_LIKE_HELL - debug_set_level(TAPE_DBF_AREA, 6); -#endif - - DBF_EVENT(3, "3590 init\n"); - - tape_3590_wq = alloc_workqueue("tape_3590", WQ_PERCPU, 0); - if (!tape_3590_wq) - return -ENOMEM; - - /* Register driver for 3590 tapes. */ - rc = ccw_driver_register(&tape_3590_driver); - if (rc) { - destroy_workqueue(tape_3590_wq); - DBF_EVENT(3, "3590 init failed\n"); - } else - DBF_EVENT(3, "3590 registered\n"); - return rc; -} - -static void -tape_3590_exit(void) -{ - ccw_driver_unregister(&tape_3590_driver); - destroy_workqueue(tape_3590_wq); - debug_unregister(TAPE_DBF_AREA); -} - -MODULE_DEVICE_TABLE(ccw, tape_3590_ids); -MODULE_AUTHOR("(C) 2001,2006 IBM Corporation"); -MODULE_DESCRIPTION("Linux on zSeries channel attached 3590 tape device driver"); -MODULE_LICENSE("GPL"); - -module_init(tape_3590_init); -module_exit(tape_3590_exit); diff --git a/drivers/s390/char/tape_3590.h b/drivers/s390/char/tape_3590.h deleted file mode 100644 index b398d8a3ed3c..000000000000 --- a/drivers/s390/char/tape_3590.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * tape device discipline for 3590 tapes. - * - * Copyright IBM Corp. 2001, 2006 - * Author(s): Stefan Bader - * Michael Holzheu - * Martin Schwidefsky - */ - -#ifndef _TAPE_3590_H -#define _TAPE_3590_H - -#define MEDIUM_SENSE 0xc2 -#define READ_PREVIOUS 0x0a -#define MODE_SENSE 0xcf -#define PERFORM_SS_FUNC 0x77 -#define READ_SS_DATA 0x3e - -#define PREP_RD_SS_DATA 0x18 -#define RD_ATTMSG 0x3 - -#define SENSE_BRA_PER 0 -#define SENSE_BRA_CONT 1 -#define SENSE_BRA_RE 2 -#define SENSE_BRA_DRE 3 - -#define SENSE_FMT_LIBRARY 0x23 -#define SENSE_FMT_UNSOLICITED 0x40 -#define SENSE_FMT_COMMAND_REJ 0x41 -#define SENSE_FMT_COMMAND_EXEC0 0x50 -#define SENSE_FMT_COMMAND_EXEC1 0x51 -#define SENSE_FMT_EVENT0 0x60 -#define SENSE_FMT_EVENT1 0x61 -#define SENSE_FMT_MIM 0x70 -#define SENSE_FMT_SIM 0x71 - -#define MSENSE_UNASSOCIATED 0x00 -#define MSENSE_ASSOCIATED_MOUNT 0x01 -#define MSENSE_ASSOCIATED_UMOUNT 0x02 -#define MSENSE_CRYPT_MASK 0x00000010 - -#define TAPE_3590_MAX_MSG 0xb0 - -/* Datatypes */ - -struct tape_3590_disc_data { - struct tape390_crypt_info crypt_info; - int read_back_op; -}; - -#define TAPE_3590_CRYPT_INFO(device) \ - ((struct tape_3590_disc_data*)(device->discdata))->crypt_info -#define TAPE_3590_READ_BACK_OP(device) \ - ((struct tape_3590_disc_data*)(device->discdata))->read_back_op - -struct tape_3590_sense { - - unsigned int command_rej:1; - unsigned int interv_req:1; - unsigned int bus_out_check:1; - unsigned int eq_check:1; - unsigned int data_check:1; - unsigned int overrun:1; - unsigned int def_unit_check:1; - unsigned int assgnd_elsew:1; - - unsigned int locate_fail:1; - unsigned int inst_online:1; - unsigned int reserved:1; - unsigned int blk_seq_err:1; - unsigned int begin_part:1; - unsigned int wr_mode:1; - unsigned int wr_prot:1; - unsigned int not_cap:1; - - unsigned int bra:2; - unsigned int lc:3; - unsigned int vlf_active:1; - unsigned int stm:1; - unsigned int med_pos:1; - - unsigned int rac:8; - - unsigned int rc_rqc:16; - - unsigned int mc:8; - - unsigned int sense_fmt:8; - - union { - struct { - unsigned int emc:4; - unsigned int smc:4; - unsigned int sev:2; - unsigned int reserved:6; - unsigned int md:8; - unsigned int refcode:8; - unsigned int mid:16; - unsigned int mp:16; - unsigned char volid[6]; - unsigned int fid:8; - } f70; - struct { - unsigned int emc:4; - unsigned int smc:4; - unsigned int sev:2; - unsigned int reserved1:5; - unsigned int mdf:1; - unsigned char md[3]; - unsigned int simid:8; - unsigned int uid:16; - unsigned int refcode1:16; - unsigned int refcode2:16; - unsigned int refcode3:16; - unsigned int reserved2:8; - } f71; - unsigned char data[14]; - } fmt; - unsigned char pad[10]; - -} __attribute__ ((packed)); - -struct tape_3590_med_sense { - unsigned int macst:4; - unsigned int masst:4; - char pad1[7]; - unsigned int flags; - char pad2[116]; -} __attribute__ ((packed)); - -struct tape_3590_rdc_data { - char data[64]; -} __attribute__ ((packed)); - -/* Datastructures for 3592 encryption support */ - -struct tape3592_kekl { - __u8 flags; - char label[64]; -} __attribute__ ((packed)); - -struct tape3592_kekl_pair { - __u8 count; - struct tape3592_kekl kekl[2]; -} __attribute__ ((packed)); - -struct tape3592_kekl_query_data { - __u16 len; - __u8 fmt; - __u8 mc; - __u32 id; - __u8 flags; - struct tape3592_kekl_pair kekls; - char reserved[116]; -} __attribute__ ((packed)); - -struct tape3592_kekl_query_order { - __u8 code; - __u8 flags; - char reserved1[2]; - __u8 max_count; - char reserved2[35]; -} __attribute__ ((packed)); - -struct tape3592_kekl_set_order { - __u8 code; - __u8 flags; - char reserved1[2]; - __u8 op; - struct tape3592_kekl_pair kekls; - char reserved2[120]; -} __attribute__ ((packed)); - -#endif /* _TAPE_3590_H */ diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 0250076a7d9f..7cf7167dcf56 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -74,9 +74,7 @@ const char *tape_op_verbose[TO_SIZE] = [TO_LOAD] = "LOA", [TO_READ_CONFIG] = "RCF", [TO_READ_ATTMSG] = "RAT", [TO_DIS] = "DIS", [TO_ASSIGN] = "ASS", - [TO_UNASSIGN] = "UAS", [TO_CRYPT_ON] = "CON", - [TO_CRYPT_OFF] = "COF", [TO_KEKL_SET] = "KLS", - [TO_KEKL_QUERY] = "KLQ",[TO_RDC] = "RDC", + [TO_UNASSIGN] = "UAS", [TO_RDC] = "RDC", }; static int devid_to_int(struct ccw_dev_id *dev_id) diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index 2cf9f725b3b3..5fef8b28309c 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -135,8 +135,6 @@ void tape_std_process_eov(struct tape_device *); enum s390_tape_type { tape_3480, tape_3490, - tape_3590, - tape_3592, }; #endif // _TAPE_STD_H From effcf3df282ba66e60718cefd08c6a3ed57d9dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:40 +0100 Subject: [PATCH 28/35] s390/tape: Remove tape load display support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LOAD_DISPLAY (LDD) X'9F' is still accepted by the Virtual Tape Server (VTS) but does not perform any action. Remove all functions and definitions related to this command. The tape_34xx_ioctl() function is also removed as it was mainly used to handle additional ioctl functionality. LOAD_DISPLAY was the only left case. All other ioctls are handled in tapechar_ioctl(). With LOAD_DISPLAY, the remaining definitions in asm/tape390.h are gone. Delete the file. Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/tape390.h | 39 ---------------------------- drivers/s390/char/tape.h | 2 -- drivers/s390/char/tape_34xx.c | 18 ------------- drivers/s390/char/tape_char.c | 5 +--- drivers/s390/char/tape_std.c | 32 ----------------------- drivers/s390/char/tape_std.h | 4 --- 6 files changed, 1 insertion(+), 99 deletions(-) delete mode 100644 arch/s390/include/uapi/asm/tape390.h diff --git a/arch/s390/include/uapi/asm/tape390.h b/arch/s390/include/uapi/asm/tape390.h deleted file mode 100644 index 880065cfa039..000000000000 --- a/arch/s390/include/uapi/asm/tape390.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/************************************************************************* - * - * enables user programs to display messages and control encryption - * on s390 tape devices - * - * Copyright IBM Corp. 2001, 2006 - * Author(s): Michael Holzheu - * - *************************************************************************/ - -#ifndef _TAPE390_H -#define _TAPE390_H - -#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct) - -/* - * The TAPE390_DISPLAY ioctl calls the Load Display command - * which transfers 17 bytes of data from the channel to the subsystem: - * - 1 format control byte, and - * - two 8-byte messages - * - * Format control byte: - * 0-2: New Message Overlay - * 3: Alternate Messages - * 4: Blink Message - * 5: Display Low/High Message - * 6: Reserved - * 7: Automatic Load Request - * - */ - -typedef struct display_struct { - char cntrl; - char message1[8]; - char message2[8]; -} display_struct; - -#endif diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index b65a18f3dc94..0dbec870e9ae 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -151,8 +151,6 @@ struct tape_discipline { struct tape_request *(*read_block)(struct tape_device *); struct tape_request *(*write_block)(struct tape_device *); void (*process_eov)(struct tape_device*); - /* ioctl function for additional ioctls. */ - int (*ioctl_fn)(struct tape_device *, unsigned int, unsigned long); /* Array of tape commands with TAPE_NR_MTOPS entries */ tape_mtop_fn *mtop_array; }; diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index a13e0ac1a4e2..b555829f741c 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -835,23 +835,6 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, return TAPE_IO_STOP; } -/* - * ioctl_overload - */ -static int -tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg) -{ - if (cmd == TAPE390_DISPLAY) { - struct display_struct disp; - - if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)) != 0) - return -EFAULT; - - return tape_std_display(device, &disp); - } else - return -EINVAL; -} - static inline void tape_34xx_append_new_sbid(struct tape_34xx_block_id bid, struct list_head *l) { @@ -1134,7 +1117,6 @@ static struct tape_discipline tape_discipline_34xx = { .irq = tape_34xx_irq, .read_block = tape_std_read_block, .write_block = tape_std_write_block, - .ioctl_fn = tape_34xx_ioctl, .mtop_array = tape_34xx_mtop }; diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index c5d3c303c15c..879331e2f283 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -412,10 +412,7 @@ __tapechar_ioctl(struct tape_device *device, return put_user_mtget(data, &get); } - /* Try the discipline ioctl function. */ - if (device->discipline->ioctl_fn == NULL) - return -EINVAL; - return device->discipline->ioctl_fn(device, no, (unsigned long)data); + return -EINVAL; } static long diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 43a5586685ff..96b7440126d2 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -22,7 +22,6 @@ #include #include #include -#include #define TAPE_DBF_AREA tape_core_dbf @@ -118,36 +117,6 @@ tape_std_unassign (struct tape_device *device) return rc; } -/* - * TAPE390_DISPLAY: Show a string on the tape display. - */ -int -tape_std_display(struct tape_device *device, struct display_struct *disp) -{ - struct tape_request *request; - int rc; - - request = tape_alloc_request(2, 17); - if (IS_ERR(request)) { - DBF_EVENT(3, "TAPE: load display failed\n"); - return PTR_ERR(request); - } - request->op = TO_DIS; - - *(unsigned char *) request->cpdata = disp->cntrl; - DBF_EVENT(5, "TAPE: display cntrl=%04x\n", disp->cntrl); - memcpy(((unsigned char *) request->cpdata) + 1, disp->message1, 8); - memcpy(((unsigned char *) request->cpdata) + 9, disp->message2, 8); - ASCEBC(((unsigned char*) request->cpdata) + 1, 16); - - tape_ccw_cc(request->cpaddr, LOAD_DISPLAY, 17, request->cpdata); - tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); - - rc = tape_do_io_interruptible(device, request); - tape_free_request(request); - return rc; -} - /* * Read block id. */ @@ -696,7 +665,6 @@ tape_std_process_eov(struct tape_device *device) EXPORT_SYMBOL(tape_std_assign); EXPORT_SYMBOL(tape_std_unassign); -EXPORT_SYMBOL(tape_std_display); EXPORT_SYMBOL(tape_std_read_block_id); EXPORT_SYMBOL(tape_std_mtload); EXPORT_SYMBOL(tape_std_mtsetblk); diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index 5fef8b28309c..580241866d9e 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -11,8 +11,6 @@ #ifndef _TAPE_STD_H #define _TAPE_STD_H -#include - /* * Biggest block size of 256K to handle. */ @@ -41,7 +39,6 @@ #define ASSIGN 0xB7 /* 3420 REJECT,3480 OK */ #define CONTROL_ACCESS 0xE3 /* Set high speed */ #define DIAG_MODE_SET 0x0B /* 3420 NOP, 3480 REJECT */ -#define LOAD_DISPLAY 0x9F /* 3420 REJECT,3480 OK */ #define LOCATE 0x4F /* 3420 REJ, 3480 NOP */ #define LOOP_WRITE_TO_READ 0x8B /* 3480 REJECT */ #define MODE_SET_DB 0xDB /* 3420 REJECT,3480 OK */ @@ -105,7 +102,6 @@ struct tape_request *tape_std_write_block(struct tape_device *); int tape_std_assign(struct tape_device *); int tape_std_unassign(struct tape_device *); int tape_std_read_block_id(struct tape_device *device, __u64 *id); -int tape_std_display(struct tape_device *, struct display_struct *disp); int tape_std_terminate_write(struct tape_device *); /* Standard magnetic tape commands. */ From 28da74c2943972168976d58ef5a1dc2b5493e890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:41 +0100 Subject: [PATCH 29/35] s390/tape: Remove special block id handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For real 3490 tape models a logical block was composed of a direction bit (wrap), a segment number, a format mode, and a logical block number. This is represented in a 4byte identifier. The Virtual Tape Server (VTS) emulates 3490 tape devices and uses a stripped block id format where bit 0-9 of the 4byte identifier are always 0. Bit 10-31 represent the logical block number. All tapes use the 3480-2 XF format, which was defined via TAPE34XX_FMT_3480_2_XF but never used. There is also no special handling required anymore as this is the only format being used. Since VTS doesn't require any special handling of block ids and format, corresponding code is removed. Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- drivers/s390/char/tape.h | 1 - drivers/s390/char/tape_34xx.c | 203 +--------------------------------- 2 files changed, 2 insertions(+), 202 deletions(-) diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 0dbec870e9ae..aca9723a3e20 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -186,7 +186,6 @@ struct tape_device { /* Device discipline information. */ struct tape_discipline * discipline; - void * discdata; /* Generic status flags */ long tape_generic_status; diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index b555829f741c..1573f7427c04 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -28,27 +28,11 @@ debug_info_t *TAPE_DBF_AREA = NULL; EXPORT_SYMBOL(TAPE_DBF_AREA); -#define TAPE34XX_FMT_3480 0 -#define TAPE34XX_FMT_3480_2_XF 1 -#define TAPE34XX_FMT_3480_XF 2 - struct tape_34xx_block_id { - unsigned int wrap : 1; - unsigned int segment : 7; - unsigned int format : 2; + unsigned int unused : 10; unsigned int block : 22; }; -/* - * A list of block ID's is used to faster seek blocks. - */ -struct tape_34xx_sbid { - struct list_head list; - struct tape_34xx_block_id bid; -}; - -static void tape_34xx_delete_sbid_from(struct tape_device *, int); - /* * Medium sense for 34xx tapes. There is no 'real' medium sense call. * So we just do a normal sense. @@ -175,19 +159,6 @@ static inline int tape_34xx_done(struct tape_request *request) { DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); - - switch (request->op) { - case TO_DSE: - case TO_RUN: - case TO_WRI: - case TO_WTM: - case TO_ASSIGN: - case TO_UNASSIGN: - tape_34xx_delete_sbid_from(request->device, 0); - break; - default: - ; - } return TAPE_IO_SUCCESS; } @@ -224,7 +195,6 @@ tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) if (irb->scsw.cmd.dstat == 0x85) { /* READY */ /* A medium was inserted in the drive. */ DBF_EVENT(6, "xuud med\n"); - tape_34xx_delete_sbid_from(device, 0); tape_34xx_schedule_work(device, TO_MSEN); } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); @@ -356,7 +326,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ case TO_LBL: /* Block could not be located. */ - tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case TO_RFO: @@ -543,7 +512,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ dev_warn (&device->cdev->dev, "The tape unit failed to load" " the cartridge\n"); - tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case 0x34: /* @@ -601,7 +569,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, /* Manual rewind or unload. This causes an I/O error. */ dev_warn (&device->cdev->dev, "The tape medium has been " "rewound or unloaded manually\n"); - tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case 0x42: /* @@ -613,7 +580,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, return tape_34xx_erp_retry(request); case 0x43: /* Drive not ready. */ - tape_34xx_delete_sbid_from(device, 0); tape_med_state_set(device, MS_UNLOADED); /* Some commands commands are successful even in this case */ if (sense[1] & SENSE_DRIVE_ONLINE) { @@ -653,7 +619,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, /* Volume fenced. CU reports volume integrity is lost. */ dev_warn (&device->cdev->dev, "The control unit has fenced " "access to the tape volume\n"); - tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case 0x48: /* Log sense data and retry request. */ @@ -725,7 +690,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, /* End of Volume complete. Rewind unload completed ok. */ if (request->op == TO_RUN) { tape_med_state_set(device, MS_UNLOADED); - tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_succeeded(request); } return tape_34xx_erp_bug(device, request, irb, sense[3]); @@ -835,157 +799,10 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, return TAPE_IO_STOP; } -static inline void -tape_34xx_append_new_sbid(struct tape_34xx_block_id bid, struct list_head *l) -{ - struct tape_34xx_sbid * new_sbid; - - new_sbid = kmalloc(sizeof(*new_sbid), GFP_ATOMIC); - if (!new_sbid) - return; - - new_sbid->bid = bid; - list_add(&new_sbid->list, l); -} - -/* - * Build up the search block ID list. The block ID consists of a logical - * block number and a hardware specific part. The hardware specific part - * helps the tape drive to speed up searching for a specific block. - */ -static void -tape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid) -{ - struct list_head * sbid_list; - struct tape_34xx_sbid * sbid; - struct list_head * l; - - /* - * immediately return if there is no list at all or the block to add - * is located in segment 1 of wrap 0 because this position is used - * if no hardware position data is supplied. - */ - sbid_list = (struct list_head *) device->discdata; - if (!sbid_list || (bid.segment < 2 && bid.wrap == 0)) - return; - - /* - * Search the position where to insert the new entry. Hardware - * acceleration uses only the segment and wrap number. So we - * need only one entry for a specific wrap/segment combination. - * If there is a block with a lower number but the same hard- - * ware position data we just update the block number in the - * existing entry. - */ - list_for_each(l, sbid_list) { - sbid = list_entry(l, struct tape_34xx_sbid, list); - - if ( - (sbid->bid.segment == bid.segment) && - (sbid->bid.wrap == bid.wrap) - ) { - if (bid.block < sbid->bid.block) - sbid->bid = bid; - else return; - break; - } - - /* Sort in according to logical block number. */ - if (bid.block < sbid->bid.block) { - tape_34xx_append_new_sbid(bid, l->prev); - break; - } - } - /* List empty or new block bigger than last entry. */ - if (l == sbid_list) - tape_34xx_append_new_sbid(bid, l->prev); - - DBF_LH(4, "Current list is:\n"); - list_for_each(l, sbid_list) { - sbid = list_entry(l, struct tape_34xx_sbid, list); - DBF_LH(4, "%d:%03d@%05d\n", - sbid->bid.wrap, - sbid->bid.segment, - sbid->bid.block - ); - } -} - -/* - * Delete all entries from the search block ID list that belong to tape blocks - * equal or higher than the given number. - */ -static void -tape_34xx_delete_sbid_from(struct tape_device *device, int from) -{ - struct list_head * sbid_list; - struct tape_34xx_sbid * sbid; - struct list_head * l; - struct list_head * n; - - sbid_list = (struct list_head *) device->discdata; - if (!sbid_list) - return; - - list_for_each_safe(l, n, sbid_list) { - sbid = list_entry(l, struct tape_34xx_sbid, list); - if (sbid->bid.block >= from) { - DBF_LH(4, "Delete sbid %d:%03d@%05d\n", - sbid->bid.wrap, - sbid->bid.segment, - sbid->bid.block - ); - list_del(l); - kfree(sbid); - } - } -} - -/* - * Merge hardware position data into a block id. - */ -static void -tape_34xx_merge_sbid( - struct tape_device * device, - struct tape_34xx_block_id * bid -) { - struct tape_34xx_sbid * sbid; - struct tape_34xx_sbid * sbid_to_use; - struct list_head * sbid_list; - struct list_head * l; - - sbid_list = (struct list_head *) device->discdata; - bid->wrap = 0; - bid->segment = 1; - - if (!sbid_list || list_empty(sbid_list)) - return; - - sbid_to_use = NULL; - list_for_each(l, sbid_list) { - sbid = list_entry(l, struct tape_34xx_sbid, list); - - if (sbid->bid.block >= bid->block) - break; - sbid_to_use = sbid; - } - if (sbid_to_use) { - bid->wrap = sbid_to_use->bid.wrap; - bid->segment = sbid_to_use->bid.segment; - DBF_LH(4, "Use %d:%03d@%05d for %05d\n", - sbid_to_use->bid.wrap, - sbid_to_use->bid.segment, - sbid_to_use->bid.block, - bid->block - ); - } -} - static int tape_34xx_setup_device(struct tape_device * device) { - int rc; - struct list_head * discdata; + int rc; DBF_EVENT(6, "34xx device setup\n"); if ((rc = tape_std_assign(device)) == 0) { @@ -993,12 +810,6 @@ tape_34xx_setup_device(struct tape_device * device) DBF_LH(3, "34xx medium sense returned %d\n", rc); } } - discdata = kmalloc(sizeof(struct list_head), GFP_KERNEL); - if (discdata) { - INIT_LIST_HEAD(discdata); - device->discdata = discdata; - } - return rc; } @@ -1006,12 +817,6 @@ static void tape_34xx_cleanup_device(struct tape_device *device) { tape_std_unassign(device); - - if (device->discdata) { - tape_34xx_delete_sbid_from(device, 0); - kfree(device->discdata); - device->discdata = NULL; - } } @@ -1031,7 +836,6 @@ tape_34xx_mttell(struct tape_device *device, int mt_count) if (rc) return rc; - tape_34xx_add_sbid(device, block_id.cbid); return block_id.cbid.block; } @@ -1055,10 +859,7 @@ tape_34xx_mtseek(struct tape_device *device, int mt_count) /* setup ccws */ request->op = TO_LBL; bid = (struct tape_34xx_block_id *) request->cpdata; - bid->format = (*device->modeset_byte & 0x08) ? - TAPE34XX_FMT_3480_XF : TAPE34XX_FMT_3480; bid->block = mt_count; - tape_34xx_merge_sbid(device, bid); tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); From 516fbb4852457d3d3f3623a91bd5b2d95c4f070b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:42 +0100 Subject: [PATCH 30/35] s390/tape: Remove unused command definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quite a few command definitions are either not used or don't exist for 3490 tape devices on Virtual Tape Servers (VTS). Cleanup the list, which makes it easier to understand what commands are actually implemented by the driver. The lists below outline the exact reason for the removal. Description for existing commands are adapted to reflect the removal of support for old device types. The following commands don't exist in VTS for 3490 devices and are unused: INVALID_00 0x00 DIAG_MODE_SET 0x0B FORCE_STREAM_CNT 0xEB LOOP_WRITE_TO_READ 0x8B MODE_SET_C3 0xC3 MODE_SET_CB 0xCB MODE_SET_D3 0xD3 NEW_MODE_SET 0xEB RELEASE 0xD4 REQ_TRK_IN_ERROR 0x1B RESERVE 0xF4 SET_DIAGNOSE 0x4B The following command definitions are not used: CONTROL_ACCESS 0xE3 PERF_SUBSYS_FUNC 0x77 READ_BACKWARD 0x0C READ_BUFFER 0x12 READ_BUFF_LOG 0x24 READ_CONFIG_DATA 0xFA READ_DEV_CHAR 0x64 READ_MESSAGE_ID 0x4E READ_SUBSYS_DATA 0x3E SENSE_GROUP_ID 0x34 SENSE_ID 0xE4 SET_GROUP_ID 0xAF SET_INTERFACE_ID 0x73 SET_TAPE_WRITE_IMMED 0xC3 SUSPEND 0x5B SYNC 0x43 Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- drivers/s390/char/tape_std.h | 38 +++++------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index 580241866d9e..c2da50a6a133 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -19,53 +19,25 @@ /* * The CCW commands for the Tape type of command. */ -#define INVALID_00 0x00 /* Invalid cmd */ #define BACKSPACEBLOCK 0x27 /* Back Space block */ #define BACKSPACEFILE 0x2f /* Back Space file */ #define DATA_SEC_ERASE 0x97 /* Data security erase */ #define ERASE_GAP 0x17 /* Erase Gap */ #define FORSPACEBLOCK 0x37 /* Forward space block */ #define FORSPACEFILE 0x3F /* Forward Space file */ -#define FORCE_STREAM_CNT 0xEB /* Forced streaming count # */ #define NOP 0x03 /* No operation */ #define READ_FORWARD 0x02 /* Read forward */ #define REWIND 0x07 /* Rewind */ #define REWIND_UNLOAD 0x0F /* Rewind and Unload */ #define SENSE 0x04 /* Sense */ -#define NEW_MODE_SET 0xEB /* Guess it is Mode set */ #define WRITE_CMD 0x01 /* Write */ #define WRITETAPEMARK 0x1F /* Write Tape Mark */ -#define ASSIGN 0xB7 /* 3420 REJECT,3480 OK */ -#define CONTROL_ACCESS 0xE3 /* Set high speed */ -#define DIAG_MODE_SET 0x0B /* 3420 NOP, 3480 REJECT */ -#define LOCATE 0x4F /* 3420 REJ, 3480 NOP */ -#define LOOP_WRITE_TO_READ 0x8B /* 3480 REJECT */ -#define MODE_SET_DB 0xDB /* 3420 REJECT,3480 OK */ -#define MODE_SET_C3 0xC3 /* for 3420 */ -#define MODE_SET_CB 0xCB /* for 3420 */ -#define MODE_SET_D3 0xD3 /* for 3420 */ -#define READ_BACKWARD 0x0C /* */ -#define READ_BLOCK_ID 0x22 /* 3420 REJECT,3480 OK */ -#define READ_BUFFER 0x12 /* 3420 REJECT,3480 OK */ -#define READ_BUFF_LOG 0x24 /* 3420 REJECT,3480 OK */ -#define RELEASE 0xD4 /* 3420 NOP, 3480 REJECT */ -#define REQ_TRK_IN_ERROR 0x1B /* 3420 NOP, 3480 REJECT */ -#define RESERVE 0xF4 /* 3420 NOP, 3480 REJECT */ -#define SENSE_GROUP_ID 0x34 /* 3420 REJECT,3480 OK */ -#define SENSE_ID 0xE4 /* 3420 REJECT,3480 OK */ -#define READ_DEV_CHAR 0x64 /* Read device characteristics */ -#define SET_DIAGNOSE 0x4B /* 3420 NOP, 3480 REJECT */ -#define SET_GROUP_ID 0xAF /* 3420 REJECT,3480 OK */ -#define SET_TAPE_WRITE_IMMED 0xC3 /* for 3480 */ -#define SUSPEND 0x5B /* 3420 REJ, 3480 NOP */ -#define SYNC 0x43 /* Synchronize (flush buffer) */ -#define UNASSIGN 0xC7 /* 3420 REJECT,3480 OK */ -#define PERF_SUBSYS_FUNC 0x77 /* 3490 CMD */ -#define READ_CONFIG_DATA 0xFA /* 3490 CMD */ -#define READ_MESSAGE_ID 0x4E /* 3490 CMD */ -#define READ_SUBSYS_DATA 0x3E /* 3490 CMD */ -#define SET_INTERFACE_ID 0x73 /* 3490 CMD */ +#define ASSIGN 0xB7 /* Assign */ +#define LOCATE 0x4F /* Locate Block */ +#define MODE_SET_DB 0xDB /* Mode Set */ +#define READ_BLOCK_ID 0x22 /* Read Block ID */ +#define UNASSIGN 0xC7 /* Unassign */ #define SENSE_COMMAND_REJECT 0x80 #define SENSE_INTERVENTION_REQUIRED 0x40 From 4b6852d764b7794e1b624dc71771b008716cde34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:43 +0100 Subject: [PATCH 31/35] s390/tape: Remove 3480 tape device type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only supported device type by the Virtual Tape Server is 3490. The 3480 device type was an old physical tape model and doesn't exist anymore. Remove 3480 from the list and any mention of it. Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- drivers/s390/char/Kconfig | 4 +- drivers/s390/char/tape.h | 2 +- drivers/s390/char/tape_34xx.c | 80 ++++++++++------------------------- drivers/s390/char/tape_std.h | 1 - 4 files changed, 26 insertions(+), 61 deletions(-) diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index d57083209c95..fc1cf818b9a0 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -123,10 +123,10 @@ comment "S/390 tape hardware support" config S390_TAPE_34XX def_tristate m - prompt "Support for 3480/3490 tape hardware" + prompt "Support for 3490 tape hardware" depends on S390_TAPE help - Select this option if you want to access IBM 3480/3490 magnetic + Select this option if you want to access IBM 3490 magnetic tape subsystems and 100% compatibles. It is safe to say "Y" here. diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index aca9723a3e20..bfbfb3b48aca 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * tape device driver for 3480/3490E tapes. + * tape device driver for 3490E tapes. * * S390 and zSeries version * Copyright IBM Corp. 2001, 2009 diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 1573f7427c04..944aa03f4e85 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * tape device discipline for 3480/3490 tapes. + * tape device discipline for 3490 tapes. * * Copyright IBM Corp. 2001, 2009 * Author(s): Carsten Otte @@ -398,13 +398,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, dev_warn (&device->cdev->dev, "The tape contains an " "incorrect block ID sequence\n"); return tape_34xx_erp_failed(request, -EIO); - default: - /* all data checks for 3480 should result in one of - * the above erpa-codes. For 3490, other data-check - * conditions do exist. */ - if (device->cdev->id.driver_info == tape_3480) - return tape_34xx_erp_bug(device, request, - irb, -6); } } @@ -535,11 +528,8 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, " on the tape unit\n"); return tape_34xx_erp_failed(request, -EIO); case 0x36: - if (device->cdev->id.driver_info == tape_3490) - /* End of data. */ - return tape_34xx_erp_failed(request, -EIO); - /* This erpa is reserved for 3480 */ - return tape_34xx_erp_bug(device, request, irb, sense[3]); + /* End of data. */ + return tape_34xx_erp_failed(request, -EIO); case 0x37: /* * Tape length error. The tape is shorter than reported in @@ -648,28 +638,21 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ return tape_34xx_erp_retry(request); case 0x4d: - if (device->cdev->id.driver_info == tape_3490) - /* - * Resetting event received. Since the driver does - * not support resetting event recovery (which has to - * be handled by the I/O Layer), retry our command. - */ - return tape_34xx_erp_retry(request); - /* This erpa is reserved for 3480. */ - return tape_34xx_erp_bug(device, request, irb, sense[3]); + /* + * Resetting event received. Since the driver does + * not support resetting event recovery (which has to + * be handled by the I/O Layer), retry our command. + */ + return tape_34xx_erp_retry(request); case 0x4e: - if (device->cdev->id.driver_info == tape_3490) { - /* - * Maximum block size exceeded. This indicates, that - * the block to be written is larger than allowed for - * buffered mode. - */ - dev_warn (&device->cdev->dev, "The maximum block size" - " for buffered mode is exceeded\n"); - return tape_34xx_erp_failed(request, -ENOBUFS); - } - /* This erpa is reserved for 3480. */ - return tape_34xx_erp_bug(device, request, irb, sense[3]); + /* + * Maximum block size exceeded. This indicates, that + * the block to be written is larger than allowed for + * buffered mode. + */ + dev_warn (&device->cdev->dev, + "The maximum block size for buffered mode is exceeded\n"); + return tape_34xx_erp_failed(request, -ENOBUFS); case 0x50: /* * Read buffered log (Overflow). CU is running in extended @@ -710,10 +693,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, "occurred\n"); return tape_34xx_erp_failed(request, -EIO); case 0x57: - /* - * 3480: Attention intercept. - * 3490: Global status intercept. - */ + /* Global status intercept. */ return tape_34xx_erp_retry(request); case 0x5a: /* @@ -723,19 +703,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, dev_warn (&device->cdev->dev, "The tape unit does not support " "the tape length\n"); return tape_34xx_erp_failed(request, -EIO); - case 0x5b: - /* Format 3480 XF incompatible */ - if (sense[1] & SENSE_BEGINNING_OF_TAPE) - /* The tape will get overwritten. */ - return tape_34xx_erp_retry(request); - dev_warn (&device->cdev->dev, "The tape unit does not support" - " format 3480 XF\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x5c: - /* Format 3480-2 XF incompatible */ - dev_warn (&device->cdev->dev, "The tape unit does not support tape " - "format 3480-2 XF\n"); - return tape_34xx_erp_failed(request, -EIO); case 0x5d: /* Tape length violation. */ dev_warn (&device->cdev->dev, "The tape unit does not support" @@ -762,7 +729,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, } /* - * 3480/3490 interrupt handler + * 3490 interrupt handler */ static int tape_34xx_irq(struct tape_device *device, struct tape_request *request, @@ -870,7 +837,7 @@ tape_34xx_mtseek(struct tape_device *device, int mt_count) } /* - * List of 3480/3490 magnetic tape commands. + * List of 3490 tape commands. */ static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { [MTRESET] = tape_std_mtreset, @@ -908,7 +875,7 @@ static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { }; /* - * Tape discipline structure for 3480 and 3490. + * Tape discipline structure for 3490. */ static struct tape_discipline tape_discipline_34xx = { .owner = THIS_MODULE, @@ -922,7 +889,6 @@ static struct tape_discipline tape_discipline_34xx = { }; static struct ccw_device_id tape_34xx_ids[] = { - { CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), .driver_info = tape_3480}, { CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490}, { /* end of list */ }, }; @@ -961,7 +927,7 @@ tape_34xx_init (void) #endif DBF_EVENT(3, "34xx init\n"); - /* Register driver for 3480/3490 tapes. */ + /* Register driver for 3490 tapes. */ rc = ccw_driver_register(&tape_34xx_driver); if (rc) DBF_EVENT(3, "34xx init failed\n"); @@ -980,7 +946,7 @@ tape_34xx_exit(void) MODULE_DEVICE_TABLE(ccw, tape_34xx_ids); MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH"); -MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape device driver"); +MODULE_DESCRIPTION("Linux on zSeries channel attached 3490 tape device driver"); MODULE_LICENSE("GPL"); module_init(tape_34xx_init); diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index c2da50a6a133..2b67b0dc9ddd 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -101,7 +101,6 @@ void tape_std_process_eov(struct tape_device *); /* S390 tape types */ enum s390_tape_type { - tape_3480, tape_3490, }; From 13391069bdc2a1df83a51dc5c4bf12ada1c6bab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:44 +0100 Subject: [PATCH 32/35] s390/tape: Cleanup sense data analysis and error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quite a few Error Recovery Action (ERA) codes and sense data entries are not relevant anymore for the Virtual Tape Server (VTS) and are not being used by VTS. Most of them were relevant for actual physical errors when a tape cartridge got stuck or a tape didn't rewind properly for example. Remove these codes from the sense data analysis as it's dead code anyway. Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- drivers/s390/char/tape_34xx.c | 120 ---------------------------------- 1 file changed, 120 deletions(-) diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 944aa03f4e85..6e710eca8feb 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -412,28 +412,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case 0x00: /* Unit check with erpa code 0. Report and ignore. */ return TAPE_IO_SUCCESS; - case 0x21: - /* - * Data streaming not operational. CU will switch to - * interlock mode. Reissue the command. - */ - return tape_34xx_erp_retry(request); - case 0x22: - /* - * Path equipment check. Might be drive adapter error, buffer - * error on the lower interface, internal path not usable, - * or error during cartridge load. - */ - dev_warn (&device->cdev->dev, "A path equipment check occurred" - " for the tape device\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x24: - /* - * Load display check. Load display was command was issued, - * but the drive is displaying a drive check message. Can - * be threated as "device end". - */ - return tape_34xx_erp_succeeded(request); case 0x27: /* * Command reject. May indicate illegal channel program or @@ -449,12 +427,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * subsystem func is issued and the CU is not on-line. */ return tape_34xx_erp_failed(request, -EIO); - case 0x2a: - /* - * Unsolicited environmental data. An internal counter - * overflows, we can ignore this and reissue the cmd. - */ - return tape_34xx_erp_retry(request); case 0x2b: /* * Environmental data present. Indicates either unload @@ -493,29 +465,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, dev_warn (&device->cdev->dev, "The tape medium is write-" "protected\n"); return tape_34xx_erp_failed(request, -EACCES); - case 0x32: - // Tension loss. We cannot recover this, it's an I/O error. - dev_warn (&device->cdev->dev, "The tape does not have the " - "required tape tension\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x33: - /* - * Load Failure. The cartridge was not inserted correctly or - * the tape is not threaded correctly. - */ - dev_warn (&device->cdev->dev, "The tape unit failed to load" - " the cartridge\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x34: - /* - * Unload failure. The drive cannot maintain tape tension - * and control tape movement during an unload operation. - */ - dev_warn (&device->cdev->dev, "Automatic unloading of the tape" - " cartridge failed\n"); - if (request->op == TO_RUN) - return tape_34xx_erp_failed(request, -EIO); - return tape_34xx_erp_bug(device, request, irb, sense[3]); case 0x35: /* * Drive equipment check. One of the following: @@ -530,14 +479,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case 0x36: /* End of data. */ return tape_34xx_erp_failed(request, -EIO); - case 0x37: - /* - * Tape length error. The tape is shorter than reported in - * the beginning-of-tape data. - */ - dev_warn (&device->cdev->dev, "The tape information states an" - " incorrect length\n"); - return tape_34xx_erp_failed(request, -EIO); case 0x38: /* * Physical end of tape. A read/write operation reached @@ -551,15 +492,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case 0x39: /* Backward at Beginning of tape. */ return tape_34xx_erp_failed(request, -EIO); - case 0x3a: - /* Drive switched to not ready. */ - dev_warn (&device->cdev->dev, "The tape unit is not ready\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x3b: - /* Manual rewind or unload. This causes an I/O error. */ - dev_warn (&device->cdev->dev, "The tape medium has been " - "rewound or unloaded manually\n"); - return tape_34xx_erp_failed(request, -EIO); case 0x42: /* * Degraded mode. A condition that can cause degraded @@ -597,14 +529,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, dev_warn (&device->cdev->dev, "The tape unit is already " "assigned\n"); return tape_34xx_erp_failed(request, -EIO); - case 0x46: - /* - * Drive not on-line. Drive may be switched offline, - * the power supply may be switched off or - * the drive address may not be set correctly. - */ - dev_warn (&device->cdev->dev, "The tape unit is not online\n"); - return tape_34xx_erp_failed(request, -EIO); case 0x47: /* Volume fenced. CU reports volume integrity is lost. */ dev_warn (&device->cdev->dev, "The control unit has fenced " @@ -613,30 +537,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case 0x48: /* Log sense data and retry request. */ return tape_34xx_erp_retry(request); - case 0x49: - /* Bus out check. A parity check error on the bus was found. */ - dev_warn (&device->cdev->dev, "A parity error occurred on the " - "tape bus\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x4a: - /* Control unit erp failed. */ - dev_warn (&device->cdev->dev, "I/O error recovery failed on " - "the tape control unit\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x4b: - /* - * CU and drive incompatible. The drive requests micro-program - * patches, which are not available on the CU. - */ - dev_warn (&device->cdev->dev, "The tape unit requires a " - "firmware update\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x4c: - /* - * Recovered Check-One failure. Cu develops a hardware error, - * but is able to recover. - */ - return tape_34xx_erp_retry(request); case 0x4d: /* * Resetting event received. Since the driver does @@ -695,29 +595,9 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case 0x57: /* Global status intercept. */ return tape_34xx_erp_retry(request); - case 0x5a: - /* - * Tape length incompatible. The tape inserted is too long, - * which could cause damage to the tape or the drive. - */ - dev_warn (&device->cdev->dev, "The tape unit does not support " - "the tape length\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x5d: - /* Tape length violation. */ - dev_warn (&device->cdev->dev, "The tape unit does not support" - " the current tape length\n"); - return tape_34xx_erp_failed(request, -EMEDIUMTYPE); - case 0x5e: - /* Compaction algorithm incompatible. */ - dev_warn (&device->cdev->dev, "The tape unit does not support" - " the compaction algorithm\n"); - return tape_34xx_erp_failed(request, -EMEDIUMTYPE); - /* The following erpas should have been covered earlier. */ case 0x23: /* Read data check. */ case 0x25: /* Write data check. */ - case 0x26: /* Data check (read opposite). */ case 0x28: /* Write id mark check. */ case 0x31: /* Tape void. */ case 0x40: /* Overrun error. */ From 9872dae6102eb4d8c3cde83ded9df0d60f4e67d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 2 Feb 2026 14:48:45 +0100 Subject: [PATCH 33/35] s390/tape: Rename tape_34xx.c to tape_3490.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver now exclusively supports 3490 tape devices, given support for 3480 tape devices has been removed. Update the device driver name, its source file name, and change any occurrences of "34xx/34XX" to "3490" in the source code and comments. Signed-off-by: Jan Höppner Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens --- drivers/s390/char/Kconfig | 2 +- drivers/s390/char/Makefile | 2 +- .../s390/char/{tape_34xx.c => tape_3490.c} | 260 +++++++++--------- 3 files changed, 132 insertions(+), 132 deletions(-) rename drivers/s390/char/{tape_34xx.c => tape_3490.c} (74%) diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index fc1cf818b9a0..1139d260ea93 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -121,7 +121,7 @@ config S390_TAPE comment "S/390 tape hardware support" depends on S390_TAPE -config S390_TAPE_34XX +config S390_TAPE_3490 def_tristate m prompt "Support for 3490 tape hardware" depends on S390_TAPE diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 60b9911653f3..8256fac51c13 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -41,7 +41,7 @@ obj-$(CONFIG_VMCP) += vmcp.o tape-$(CONFIG_PROC_FS) += tape_proc.o tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y) obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o -obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o +obj-$(CONFIG_S390_TAPE_3490) += tape_3490.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o obj-$(CONFIG_S390_VMUR) += vmur.o diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_3490.c similarity index 74% rename from drivers/s390/char/tape_34xx.c rename to drivers/s390/char/tape_3490.c index 6e710eca8feb..83898f92ad0f 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_3490.c @@ -8,7 +8,7 @@ * Martin Schwidefsky */ -#define pr_fmt(fmt) "tape_34xx: " fmt +#define pr_fmt(fmt) "tape_3490: " fmt #include #include @@ -17,7 +17,7 @@ #include #include -#define TAPE_DBF_AREA tape_34xx_dbf +#define TAPE_DBF_AREA tape_3490_dbf #include "tape.h" #include "tape_std.h" @@ -28,16 +28,16 @@ debug_info_t *TAPE_DBF_AREA = NULL; EXPORT_SYMBOL(TAPE_DBF_AREA); -struct tape_34xx_block_id { +struct tape_3490_block_id { unsigned int unused : 10; unsigned int block : 22; }; /* - * Medium sense for 34xx tapes. There is no 'real' medium sense call. + * Medium sense for 3490 tapes. There is no 'real' medium sense call. * So we just do a normal sense. */ -static void __tape_34xx_medium_sense(struct tape_request *request) +static void __tape_3490_medium_sense(struct tape_request *request) { struct tape_device *device = request->device; unsigned char *sense; @@ -61,12 +61,12 @@ static void __tape_34xx_medium_sense(struct tape_request *request) else device->tape_generic_status &= ~GMT_WR_PROT(~0); } else - DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", + DBF_EVENT(4, "tape_3490: medium sense failed with rc=%d\n", request->rc); tape_free_request(request); } -static int tape_34xx_medium_sense(struct tape_device *device) +static int tape_3490_medium_sense(struct tape_device *device) { struct tape_request *request; int rc; @@ -80,11 +80,11 @@ static int tape_34xx_medium_sense(struct tape_device *device) request->op = TO_MSEN; tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); rc = tape_do_io_interruptible(device, request); - __tape_34xx_medium_sense(request); + __tape_3490_medium_sense(request); return rc; } -static void tape_34xx_medium_sense_async(struct tape_device *device) +static void tape_3490_medium_sense_async(struct tape_device *device) { struct tape_request *request; @@ -96,12 +96,12 @@ static void tape_34xx_medium_sense_async(struct tape_device *device) request->op = TO_MSEN; tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); - request->callback = (void *) __tape_34xx_medium_sense; + request->callback = (void *) __tape_3490_medium_sense; request->callback_data = NULL; tape_do_io_async(device, request); } -struct tape_34xx_work { +struct tape_3490_work { struct tape_device *device; enum tape_op op; struct work_struct work; @@ -118,32 +118,32 @@ struct tape_34xx_work { * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq). */ static void -tape_34xx_work_handler(struct work_struct *work) +tape_3490_work_handler(struct work_struct *work) { - struct tape_34xx_work *p = - container_of(work, struct tape_34xx_work, work); + struct tape_3490_work *p = + container_of(work, struct tape_3490_work, work); struct tape_device *device = p->device; switch(p->op) { case TO_MSEN: - tape_34xx_medium_sense_async(device); + tape_3490_medium_sense_async(device); break; default: - DBF_EVENT(3, "T34XX: internal error: unknown work\n"); + DBF_EVENT(3, "T3490: internal error: unknown work\n"); } tape_put_device(device); kfree(p); } static int -tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) +tape_3490_schedule_work(struct tape_device *device, enum tape_op op) { - struct tape_34xx_work *p; + struct tape_3490_work *p; if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) return -ENOMEM; - INIT_WORK(&p->work, tape_34xx_work_handler); + INIT_WORK(&p->work, tape_3490_work_handler); p->device = tape_get_device(device); p->op = op; @@ -156,14 +156,14 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) * Done Handler is called when dev stat = DEVICE-END (successful operation) */ static inline int -tape_34xx_done(struct tape_request *request) +tape_3490_done(struct tape_request *request) { DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); return TAPE_IO_SUCCESS; } static inline int -tape_34xx_erp_failed(struct tape_request *request, int rc) +tape_3490_erp_failed(struct tape_request *request, int rc) { DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n", tape_op_verbose[request->op], rc); @@ -171,15 +171,15 @@ tape_34xx_erp_failed(struct tape_request *request, int rc) } static inline int -tape_34xx_erp_succeeded(struct tape_request *request) +tape_3490_erp_succeeded(struct tape_request *request) { DBF_EVENT(3, "Error Recovery successful for %s\n", tape_op_verbose[request->op]); - return tape_34xx_done(request); + return tape_3490_done(request); } static inline int -tape_34xx_erp_retry(struct tape_request *request) +tape_3490_erp_retry(struct tape_request *request) { DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]); return TAPE_IO_RETRY; @@ -190,12 +190,12 @@ tape_34xx_erp_retry(struct tape_request *request) * interrupt */ static int -tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) +tape_3490_unsolicited_irq(struct tape_device *device, struct irb *irb) { if (irb->scsw.cmd.dstat == 0x85) { /* READY */ /* A medium was inserted in the drive. */ DBF_EVENT(6, "xuud med\n"); - tape_34xx_schedule_work(device, TO_MSEN); + tape_3490_schedule_work(device, TO_MSEN); } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); tape_dump_sense_dbf(device, NULL, irb); @@ -204,7 +204,7 @@ tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) } static int -tape_34xx_erp_bug(struct tape_device *device, struct tape_request *request, +tape_3490_erp_bug(struct tape_device *device, struct tape_request *request, struct irb *irb, int no) { if (request->op != TO_ASSIGN) { @@ -212,7 +212,7 @@ tape_34xx_erp_bug(struct tape_device *device, struct tape_request *request, "occurred in tape error recovery\n", no); tape_dump_sense_dbf(device, request, irb); } - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); } /* @@ -220,22 +220,22 @@ tape_34xx_erp_bug(struct tape_device *device, struct tape_request *request, * be too slow. */ static int -tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request, +tape_3490_erp_overrun(struct tape_device *device, struct tape_request *request, struct irb *irb) { if (irb->ecw[3] == 0x40) { dev_warn (&device->cdev->dev, "A data overrun occurred between" " the control unit and tape unit\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); } - return tape_34xx_erp_bug(device, request, irb, -1); + return tape_3490_erp_bug(device, request, irb, -1); } /* * Handle record sequence error. */ static int -tape_34xx_erp_sequence(struct tape_device *device, +tape_3490_erp_sequence(struct tape_device *device, struct tape_request *request, struct irb *irb) { if (irb->ecw[3] == 0x41) { @@ -244,13 +244,13 @@ tape_34xx_erp_sequence(struct tape_device *device, */ dev_warn (&device->cdev->dev, "The block ID sequence on the " "tape is incorrect\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); } /* * Record sequence error bit is set, but erpa does not * show record sequence error. */ - return tape_34xx_erp_bug(device, request, irb, -2); + return tape_3490_erp_bug(device, request, irb, -2); } /* @@ -259,7 +259,7 @@ tape_34xx_erp_sequence(struct tape_device *device, * informed about the problem. */ static int -tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, +tape_3490_unit_check(struct tape_device *device, struct tape_request *request, struct irb *irb) { int inhibit_cu_recovery; @@ -278,9 +278,9 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, request->op == TO_WTM ) { /* medium is write protected */ - return tape_34xx_erp_failed(request, -EACCES); + return tape_3490_erp_failed(request, -EACCES); } else { - return tape_34xx_erp_bug(device, request, irb, -3); + return tape_3490_erp_bug(device, request, irb, -3); } } @@ -315,9 +315,9 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case TO_FSF: case TO_FSB: /* Trying to seek beyond end of recorded area */ - return tape_34xx_erp_failed(request, -ENOSPC); + return tape_3490_erp_failed(request, -ENOSPC); case TO_BSB: - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); /* * sense[0] == SENSE_DATA_CHECK && @@ -326,11 +326,11 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ case TO_LBL: /* Block could not be located. */ - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case TO_RFO: /* Read beyond end of recorded area -> 0 bytes read */ - return tape_34xx_erp_failed(request, 0); + return tape_3490_erp_failed(request, 0); /* * sense[0] == SENSE_EQUIPMENT_CHECK && @@ -339,15 +339,15 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ case TO_WRI: /* Writing at physical end of volume */ - return tape_34xx_erp_failed(request, -ENOSPC); + return tape_3490_erp_failed(request, -ENOSPC); default: - return tape_34xx_erp_failed(request, 0); + return tape_3490_erp_failed(request, 0); } } /* Sensing special bits */ if (sense[0] & SENSE_BUS_OUT_CHECK) - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); if (sense[0] & SENSE_DATA_CHECK) { /* @@ -362,13 +362,13 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, // data check is not permanent, may be // recovered. We always use async-mode with // cu-recovery, so this should *never* happen. - return tape_34xx_erp_bug(device, request, + return tape_3490_erp_bug(device, request, irb, -4); /* data check is permanent, CU recovery has failed */ dev_warn (&device->cdev->dev, "A read error occurred " "that cannot be recovered\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x25: // a write data check occurred if ((sense[2] & SENSE_TAPE_SYNC_MODE) || @@ -376,36 +376,36 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, // data check is not permanent, may be // recovered. We always use async-mode with // cu-recovery, so this should *never* happen. - return tape_34xx_erp_bug(device, request, + return tape_3490_erp_bug(device, request, irb, -5); // data check is permanent, cu-recovery has failed dev_warn (&device->cdev->dev, "A write error on the " "tape cannot be recovered\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x28: /* ID-Mark at tape start couldn't be written */ dev_warn (&device->cdev->dev, "Writing the ID-mark " "failed\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x31: /* Tape void. Tried to read beyond end of device. */ dev_warn (&device->cdev->dev, "Reading the tape beyond" " the end of the recorded area failed\n"); - return tape_34xx_erp_failed(request, -ENOSPC); + return tape_3490_erp_failed(request, -ENOSPC); case 0x41: /* Record sequence error. */ dev_warn (&device->cdev->dev, "The tape contains an " "incorrect block ID sequence\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); } } if (sense[0] & SENSE_OVERRUN) - return tape_34xx_erp_overrun(device, request, irb); + return tape_3490_erp_overrun(device, request, irb); if (sense[1] & SENSE_RECORD_SEQUENCE_ERR) - return tape_34xx_erp_sequence(device, request, irb); + return tape_3490_erp_sequence(device, request, irb); /* Sensing erpa codes */ switch (sense[3]) { @@ -419,14 +419,14 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * issued by this driver and ought be correct, we assume a * over/underrun situation and retry the channel program. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x29: /* * Function incompatible. Either the tape is idrc compressed * but the hardware isn't capable to do idrc, or a perform * subsystem func is issued and the CU is not on-line. */ - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x2b: /* * Environmental data present. Indicates either unload @@ -435,22 +435,22 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, if (request->op == TO_RUN) { /* Rewind unload completed ok. */ tape_med_state_set(device, MS_UNLOADED); - return tape_34xx_erp_succeeded(request); + return tape_3490_erp_succeeded(request); } - /* tape_34xx doesn't use read buffered log commands. */ - return tape_34xx_erp_bug(device, request, irb, sense[3]); + /* tape_3490 doesn't use read buffered log commands. */ + return tape_3490_erp_bug(device, request, irb, sense[3]); case 0x2c: /* * Permanent equipment check. CU has tried recovery, but * did not succeed. */ - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x2d: /* Data security erase failure. */ if (request->op == TO_DSE) - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); /* Data security erase failure, but no such command issued. */ - return tape_34xx_erp_bug(device, request, irb, sense[3]); + return tape_3490_erp_bug(device, request, irb, sense[3]); case 0x2e: /* * Not capable. This indicates either that the drive fails @@ -459,12 +459,12 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ dev_warn (&device->cdev->dev, "The tape unit cannot process " "the tape format\n"); - return tape_34xx_erp_failed(request, -EMEDIUMTYPE); + return tape_3490_erp_failed(request, -EMEDIUMTYPE); case 0x30: /* The medium is write protected. */ dev_warn (&device->cdev->dev, "The tape medium is write-" "protected\n"); - return tape_34xx_erp_failed(request, -EACCES); + return tape_3490_erp_failed(request, -EACCES); case 0x35: /* * Drive equipment check. One of the following: @@ -475,10 +475,10 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ dev_warn (&device->cdev->dev, "An equipment check has occurred" " on the tape unit\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x36: /* End of data. */ - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x38: /* * Physical end of tape. A read/write operation reached @@ -487,11 +487,11 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, if (request->op==TO_WRI || request->op==TO_DSE || request->op==TO_WTM) - return tape_34xx_erp_failed(request, -ENOSPC); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -ENOSPC); + return tape_3490_erp_failed(request, -EIO); case 0x39: /* Backward at Beginning of tape. */ - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x42: /* * Degraded mode. A condition that can cause degraded @@ -499,7 +499,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ dev_warn (&device->cdev->dev, "The tape subsystem is running " "in degraded mode\n"); - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x43: /* Drive not ready. */ tape_med_state_set(device, MS_UNLOADED); @@ -510,40 +510,40 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case TO_UNASSIGN: case TO_DIS: case TO_NOP: - return tape_34xx_done(request); + return tape_3490_done(request); break; default: break; } } - return tape_34xx_erp_failed(request, -ENOMEDIUM); + return tape_3490_erp_failed(request, -ENOMEDIUM); case 0x44: /* Locate Block unsuccessful. */ if (request->op != TO_BLOCK && request->op != TO_LBL) /* No locate block was issued. */ - return tape_34xx_erp_bug(device, request, + return tape_3490_erp_bug(device, request, irb, sense[3]); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x45: /* The drive is assigned to a different channel path. */ dev_warn (&device->cdev->dev, "The tape unit is already " "assigned\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x47: /* Volume fenced. CU reports volume integrity is lost. */ dev_warn (&device->cdev->dev, "The control unit has fenced " "access to the tape volume\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x48: /* Log sense data and retry request. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x4d: /* * Resetting event received. Since the driver does * not support resetting event recovery (which has to * be handled by the I/O Layer), retry our command. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x4e: /* * Maximum block size exceeded. This indicates, that @@ -552,7 +552,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, */ dev_warn (&device->cdev->dev, "The maximum block size for buffered mode is exceeded\n"); - return tape_34xx_erp_failed(request, -ENOBUFS); + return tape_3490_erp_failed(request, -ENOBUFS); case 0x50: /* * Read buffered log (Overflow). CU is running in extended @@ -560,7 +560,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * never happen, since we're never running in extended * buffered log mode. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x51: /* * Read buffered log (EOV). EOF processing occurs while the @@ -568,33 +568,33 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * happen, since we're never running in extended buffered * log mode. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x52: /* End of Volume complete. Rewind unload completed ok. */ if (request->op == TO_RUN) { tape_med_state_set(device, MS_UNLOADED); - return tape_34xx_erp_succeeded(request); + return tape_3490_erp_succeeded(request); } - return tape_34xx_erp_bug(device, request, irb, sense[3]); + return tape_3490_erp_bug(device, request, irb, sense[3]); case 0x53: /* Global command intercept. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x54: /* Channel interface recovery (temporary). */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); case 0x55: /* Channel interface recovery (permanent). */ dev_warn (&device->cdev->dev, "A channel interface error cannot be" " recovered\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x56: /* Channel protocol error. */ dev_warn (&device->cdev->dev, "A channel protocol error " "occurred\n"); - return tape_34xx_erp_failed(request, -EIO); + return tape_3490_erp_failed(request, -EIO); case 0x57: /* Global status intercept. */ - return tape_34xx_erp_retry(request); + return tape_3490_erp_retry(request); /* The following erpas should have been covered earlier. */ case 0x23: /* Read data check. */ case 0x25: /* Write data check. */ @@ -604,7 +604,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, case 0x41: /* Record sequence error. */ /* All other erpas are reserved for future use. */ default: - return tape_34xx_erp_bug(device, request, irb, sense[3]); + return tape_3490_erp_bug(device, request, irb, sense[3]); } } @@ -612,21 +612,21 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * 3490 interrupt handler */ static int -tape_34xx_irq(struct tape_device *device, struct tape_request *request, +tape_3490_irq(struct tape_device *device, struct tape_request *request, struct irb *irb) { if (request == NULL) - return tape_34xx_unsolicited_irq(device, irb); + return tape_3490_unsolicited_irq(device, irb); if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) && (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) { /* Write at end of volume */ - return tape_34xx_erp_failed(request, -ENOSPC); + return tape_3490_erp_failed(request, -ENOSPC); } if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) - return tape_34xx_unit_check(device, request, irb); + return tape_3490_unit_check(device, request, irb); if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { /* @@ -638,7 +638,7 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, else DBF_EVENT(5, "Unit Exception!\n"); } - return tape_34xx_done(request); + return tape_3490_done(request); } DBF_EVENT(6, "xunknownirq\n"); @@ -647,21 +647,21 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, } static int -tape_34xx_setup_device(struct tape_device * device) +tape_3490_setup_device(struct tape_device * device) { int rc; - DBF_EVENT(6, "34xx device setup\n"); + DBF_EVENT(6, "3490 device setup\n"); if ((rc = tape_std_assign(device)) == 0) { - if ((rc = tape_34xx_medium_sense(device)) != 0) { - DBF_LH(3, "34xx medium sense returned %d\n", rc); + if ((rc = tape_3490_medium_sense(device)) != 0) { + DBF_LH(3, "3490 medium sense returned %d\n", rc); } } return rc; } static void -tape_34xx_cleanup_device(struct tape_device *device) +tape_3490_cleanup_device(struct tape_device *device) { tape_std_unassign(device); } @@ -671,11 +671,11 @@ tape_34xx_cleanup_device(struct tape_device *device) * MTTELL: Tell block. Return the number of block relative to current file. */ static int -tape_34xx_mttell(struct tape_device *device, int mt_count) +tape_3490_mttell(struct tape_device *device, int mt_count) { struct { - struct tape_34xx_block_id cbid; - struct tape_34xx_block_id dbid; + struct tape_3490_block_id cbid; + struct tape_3490_block_id dbid; } __attribute__ ((packed)) block_id; int rc; @@ -690,10 +690,10 @@ tape_34xx_mttell(struct tape_device *device, int mt_count) * MTSEEK: seek to the specified block. */ static int -tape_34xx_mtseek(struct tape_device *device, int mt_count) +tape_3490_mtseek(struct tape_device *device, int mt_count) { struct tape_request *request; - struct tape_34xx_block_id * bid; + struct tape_3490_block_id * bid; if (mt_count > 0x3fffff) { DBF_EXCEPTION(6, "xsee parm\n"); @@ -705,7 +705,7 @@ tape_34xx_mtseek(struct tape_device *device, int mt_count) /* setup ccws */ request->op = TO_LBL; - bid = (struct tape_34xx_block_id *) request->cpdata; + bid = (struct tape_3490_block_id *) request->cpdata; bid->block = mt_count; tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); @@ -719,7 +719,7 @@ tape_34xx_mtseek(struct tape_device *device, int mt_count) /* * List of 3490 tape commands. */ -static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { +static tape_mtop_fn tape_3490_mtop[TAPE_NR_MTOPS] = { [MTRESET] = tape_std_mtreset, [MTFSF] = tape_std_mtfsf, [MTBSF] = tape_std_mtbsf, @@ -739,8 +739,8 @@ static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { [MTRAS3] = NULL, [MTSETBLK] = tape_std_mtsetblk, [MTSETDENSITY] = NULL, - [MTSEEK] = tape_34xx_mtseek, - [MTTELL] = tape_34xx_mttell, + [MTSEEK] = tape_3490_mtseek, + [MTTELL] = tape_3490_mttell, [MTSETDRVBUFFER] = NULL, [MTFSS] = NULL, [MTBSS] = NULL, @@ -757,77 +757,77 @@ static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { /* * Tape discipline structure for 3490. */ -static struct tape_discipline tape_discipline_34xx = { +static struct tape_discipline tape_discipline_3490 = { .owner = THIS_MODULE, - .setup_device = tape_34xx_setup_device, - .cleanup_device = tape_34xx_cleanup_device, + .setup_device = tape_3490_setup_device, + .cleanup_device = tape_3490_cleanup_device, .process_eov = tape_std_process_eov, - .irq = tape_34xx_irq, + .irq = tape_3490_irq, .read_block = tape_std_read_block, .write_block = tape_std_write_block, - .mtop_array = tape_34xx_mtop + .mtop_array = tape_3490_mtop }; -static struct ccw_device_id tape_34xx_ids[] = { +static struct ccw_device_id tape_3490_ids[] = { { CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490}, { /* end of list */ }, }; static int -tape_34xx_online(struct ccw_device *cdev) +tape_3490_online(struct ccw_device *cdev) { return tape_generic_online( dev_get_drvdata(&cdev->dev), - &tape_discipline_34xx + &tape_discipline_3490 ); } -static struct ccw_driver tape_34xx_driver = { +static struct ccw_driver tape_3490_driver = { .driver = { - .name = "tape_34xx", + .name = "tape_3490", .owner = THIS_MODULE, }, - .ids = tape_34xx_ids, + .ids = tape_3490_ids, .probe = tape_generic_probe, .remove = tape_generic_remove, - .set_online = tape_34xx_online, + .set_online = tape_3490_online, .set_offline = tape_generic_offline, .int_class = IRQIO_TAP, }; static int -tape_34xx_init (void) +tape_3490_init (void) { int rc; - TAPE_DBF_AREA = debug_register ( "tape_34xx", 2, 2, 4*sizeof(long)); + TAPE_DBF_AREA = debug_register ( "tape_3490", 2, 2, 4*sizeof(long)); debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view); #ifdef DBF_LIKE_HELL debug_set_level(TAPE_DBF_AREA, 6); #endif - DBF_EVENT(3, "34xx init\n"); + DBF_EVENT(3, "3490 init\n"); /* Register driver for 3490 tapes. */ - rc = ccw_driver_register(&tape_34xx_driver); + rc = ccw_driver_register(&tape_3490_driver); if (rc) - DBF_EVENT(3, "34xx init failed\n"); + DBF_EVENT(3, "3490 init failed\n"); else - DBF_EVENT(3, "34xx registered\n"); + DBF_EVENT(3, "3490 registered\n"); return rc; } static void -tape_34xx_exit(void) +tape_3490_exit(void) { - ccw_driver_unregister(&tape_34xx_driver); + ccw_driver_unregister(&tape_3490_driver); debug_unregister(TAPE_DBF_AREA); } -MODULE_DEVICE_TABLE(ccw, tape_34xx_ids); +MODULE_DEVICE_TABLE(ccw, tape_3490_ids); MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH"); MODULE_DESCRIPTION("Linux on zSeries channel attached 3490 tape device driver"); MODULE_LICENSE("GPL"); -module_init(tape_34xx_init); -module_exit(tape_34xx_exit); +module_init(tape_3490_init); +module_exit(tape_3490_exit); From f65c75b0b9b5a390bc3beadcde0a6fbc3ad118f7 Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Fri, 30 Jan 2026 21:47:59 +0100 Subject: [PATCH 34/35] s390/cio: Fix device lifecycle handling in css_alloc_subchannel() `css_alloc_subchannel()` calls `device_initialize()` before setting up the DMA masks. If `dma_set_coherent_mask()` or `dma_set_mask()` fails, the error path frees the subchannel structure directly, bypassing the device model reference counting. Once `device_initialize()` has been called, the embedded struct device must be released via `put_device()`, allowing the release callback to free the container structure. Fix the error path by dropping the initial device reference with `put_device()` instead of calling `kfree()` directly. This ensures correct device lifetime handling and avoids potential use-after-free or double-free issues. Fixes: e5dcf0025d7af ("s390/css: move subchannel lock allocation") Signed-off-by: Salah Triki Reviewed-by: Vineeth Vijayan Signed-off-by: Heiko Carstens --- drivers/s390/cio/css.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 4c85df7a548e..ac24e019020e 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -235,7 +235,7 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid, return sch; err: - kfree(sch); + put_device(&sch->dev); return ERR_PTR(ret); } From 5ae76830c76cf38708399245606e4e07a33fe51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Wed, 4 Feb 2026 16:30:40 +0100 Subject: [PATCH 35/35] s390/tape: Consolidate tape config options and modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tape device driver only supports 3490E devices on Virtual Tape Server (VTS). There is no point in keeping separated options and modules for different device types and general hardware support. Consolidate the tape config option into a single option, which enables the complete tape support with one singular module called 'tape_s390'. The corresponding module entry points are adapted and consolidate in tape_init() and tape_exit() respectively in tape_core.c The current module author and descriptions of the individual tape modules are outdated and haven't been changed for quite some time. Change it to a more generic description that is in line with the corresponding supported s390 architecture for the single tape module. Signed-off-by: Jan Höppner Signed-off-by: Heiko Carstens --- drivers/s390/char/Kconfig | 24 ++++-------------------- drivers/s390/char/Makefile | 5 ++--- drivers/s390/char/tape.h | 4 ++++ drivers/s390/char/tape_3490.c | 12 ++---------- drivers/s390/char/tape_class.c | 15 ++------------- drivers/s390/char/tape_class.h | 2 ++ drivers/s390/char/tape_core.c | 10 +++++++--- 7 files changed, 23 insertions(+), 49 deletions(-) diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 1139d260ea93..4d8f09910a46 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -106,28 +106,12 @@ config S390_UV_UAPI config S390_TAPE def_tristate m - prompt "S/390 tape device support" + prompt "Support for 3490E tape on VTS" depends on CCW help - Select this option if you want to access channel-attached tape - devices on IBM S/390 or zSeries. - If you select this option you will also want to select at - least one of the tape interface options and one of the tape - hardware options in order to access a tape device. - This option is also available as a module. The module will be - called tape390 and include all selected interfaces and - hardware drivers. - -comment "S/390 tape hardware support" - depends on S390_TAPE - -config S390_TAPE_3490 - def_tristate m - prompt "Support for 3490 tape hardware" - depends on S390_TAPE - help - Select this option if you want to access IBM 3490 magnetic - tape subsystems and 100% compatibles. + Select this option if you want to access channel-attached IBM 3490E + tape devices on VTS, such as IBM TS7700. + This option is also available as a module. It is safe to say "Y" here. config VMLOGRDR diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 8256fac51c13..126a87c3c6f8 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -39,9 +39,8 @@ obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o obj-$(CONFIG_VMCP) += vmcp.o tape-$(CONFIG_PROC_FS) += tape_proc.o -tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y) -obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o -obj-$(CONFIG_S390_TAPE_3490) += tape_3490.o +tape_s390-objs := tape_3490.o tape_char.o tape_class.o tape_core.o tape_std.o $(tape-y) +obj-$(CONFIG_S390_TAPE) += tape_s390.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o obj-$(CONFIG_S390_VMUR) += vmur.o diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index bfbfb3b48aca..59541bd88d51 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -274,6 +274,10 @@ extern void tapechar_exit(void); extern int tapechar_setup_device(struct tape_device *); extern void tapechar_cleanup_device(struct tape_device *); +/* Externals from tape_3490.c */ +extern int tape_3490_init(void); +extern void tape_3490_exit(void); + /* tape initialisation functions */ #ifdef CONFIG_PROC_FS extern void tape_proc_init (void); diff --git a/drivers/s390/char/tape_3490.c b/drivers/s390/char/tape_3490.c index 83898f92ad0f..c4ea32af1b65 100644 --- a/drivers/s390/char/tape_3490.c +++ b/drivers/s390/char/tape_3490.c @@ -795,8 +795,7 @@ static struct ccw_driver tape_3490_driver = { .int_class = IRQIO_TAP, }; -static int -tape_3490_init (void) +int tape_3490_init(void) { int rc; @@ -816,8 +815,7 @@ tape_3490_init (void) return rc; } -static void -tape_3490_exit(void) +void tape_3490_exit(void) { ccw_driver_unregister(&tape_3490_driver); @@ -825,9 +823,3 @@ tape_3490_exit(void) } MODULE_DEVICE_TABLE(ccw, tape_3490_ids); -MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH"); -MODULE_DESCRIPTION("Linux on zSeries channel attached 3490 tape device driver"); -MODULE_LICENSE("GPL"); - -module_init(tape_3490_init); -module_exit(tape_3490_exit); diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index 6fa7b7824856..1ae9ad219c08 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -15,13 +15,6 @@ #include "tape_class.h" -MODULE_AUTHOR("Stefan Bader "); -MODULE_DESCRIPTION( - "Copyright IBM Corp. 2004 All Rights Reserved.\n" - "tape_class.c" -); -MODULE_LICENSE("GPL"); - static const struct class tape_class = { .name = "tape390", }; @@ -116,16 +109,12 @@ void unregister_tape_dev(struct device *device, struct tape_class_device *tcd) } EXPORT_SYMBOL(unregister_tape_dev); - -static int __init tape_init(void) +int tape_class_init(void) { return class_register(&tape_class); } -static void __exit tape_exit(void) +void tape_class_exit(void) { class_unregister(&tape_class); } - -postcore_initcall(tape_init); -module_exit(tape_exit); diff --git a/drivers/s390/char/tape_class.h b/drivers/s390/char/tape_class.h index d25ac075b1ad..0bb6f0ca45b6 100644 --- a/drivers/s390/char/tape_class.h +++ b/drivers/s390/char/tape_class.h @@ -55,5 +55,7 @@ struct tape_class_device *register_tape_dev( char * node_name ); void unregister_tape_dev(struct device *device, struct tape_class_device *tcd); +int tape_class_init(void); +void tape_class_exit(void); #endif /* __TAPE_CLASS_H__ */ diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 7cf7167dcf56..a62b650ea3c2 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -28,6 +28,7 @@ #include "tape.h" #include "tape_std.h" +#include "tape_class.h" #define LONG_BUSY_TIMEOUT 180 /* seconds */ @@ -1310,7 +1311,9 @@ tape_init (void) #endif DBF_EVENT(3, "tape init\n"); tape_proc_init(); + tape_class_init(); tapechar_init (); + tape_3490_init(); return 0; } @@ -1323,14 +1326,15 @@ tape_exit(void) DBF_EVENT(6, "tape exit\n"); /* Get rid of the frontends */ + tape_3490_exit(); tapechar_exit(); + tape_class_exit(); tape_proc_cleanup(); debug_unregister (TAPE_DBF_AREA); } -MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and " - "Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)"); -MODULE_DESCRIPTION("Linux on zSeries channel attached tape device driver"); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("s390 channel-attached tape device driver"); MODULE_LICENSE("GPL"); module_init(tape_init);