Merge tag 's390-7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux Pull s390 updates from Heiko Carstens: - Drop support for outdated 3590/3592 and 3480 tape devices, and limit support to virtualized 3490E types devices - Implement exception based WARN() and WARN_ONCE() similar to x86 - Slightly optimize preempt primitives like __preempt_count_add() and __preempt_count_dec_and_test() - A couple of small fixes and improvements * tag 's390-7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (35 commits) s390/tape: Consolidate tape config options and modules s390/cio: Fix device lifecycle handling in css_alloc_subchannel() s390/tape: Rename tape_34xx.c to tape_3490.c s390/tape: Cleanup sense data analysis and error handling s390/tape: Remove 3480 tape device type s390/tape: Remove unused command definitions s390/tape: Remove special block id handling s390/tape: Remove tape load display support s390/tape: Remove support for 3590/3592 models s390/kexec: Emit an error message when cmdline is too long s390/configs: Enable BLK_DEV_NULL_BLK as module s390: Document s390 stackprotector support s390/perf: Disable register readout on sampling events s390/Kconfig: Define non-zero ILLEGAL_POINTER_VALUE s390/bug: Prevent tail-call optimization s390/bug: Skip __WARN_trap() in call traces s390/bug: Implement WARN_ONCE() s390/bug: Implement __WARN_printf() s390/traps: Copy monitor code to pt_regs s390/bug: Introduce and use monitor code macro ...
diff --git a/Documentation/arch/s390/mm.rst b/Documentation/arch/s390/mm.rst index 084adad..1968115 100644 --- a/Documentation/arch/s390/mm.rst +++ b/Documentation/arch/s390/mm.rst
@@ -109,3 +109,7 @@ | KASAN shadow | KASAN untracked | | +------------------+ ASCE limit + | | + | CONFIG_ILLEGAL_POINTER_VALUE causes memory access fault + | | + +------------------+
diff --git a/Documentation/features/debug/stackprotector/arch-support.txt b/Documentation/features/debug/stackprotector/arch-support.txt index de8f43f..43e49c7 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 |
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0e5fad5..cda697a 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig
@@ -69,6 +69,12 @@ 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) @@ -85,6 +91,7 @@ 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 +301,14 @@ 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 @@ -704,6 +719,10 @@ 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/Makefile b/arch/s390/boot/Makefile index 490167f..a1e719a 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile
@@ -21,6 +21,7 @@ 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
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index f77067d..7f33434 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/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 0713914..7a91d30 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig
@@ -446,6 +446,7 @@ 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 c064e0c..3bb2aa8 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig
@@ -436,6 +436,7 @@ 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/include/asm/ap.h b/arch/s390/include/asm/ap.h index b24459f..3b95c65 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. */
diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h index f662eb4..7bd1801 100644 --- a/arch/s390/include/asm/asm-prototypes.h +++ b/arch/s390/include/asm/asm-prototypes.h
@@ -3,6 +3,7 @@ #include <linux/kvm_host.h> #include <linux/ftrace.h> +#include <asm/bug.h> #include <asm/fpu.h> #include <asm/nospec-branch.h> #include <asm-generic/asm-prototypes.h>
diff --git a/arch/s390/include/asm/asm.h b/arch/s390/include/asm/asm.h index e9062b01..510901c 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)
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index ee9221b..59017fd 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h
@@ -2,60 +2,127 @@ #ifndef _ASM_S390_BUG_H #define _ASM_S390_BUG_H -#include <linux/stringify.h> +#include <linux/compiler.h> +#include <linux/const.h> -#ifdef CONFIG_BUG +#define MONCODE_BUG _AC(0, U) +#define MONCODE_BUG_ARG _AC(1, U) -#ifndef CONFIG_DEBUG_BUGVERBOSE -#define _BUGVERBOSE_LOCATION(file, line) +#ifndef __ASSEMBLER__ +#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) + +#ifdef CONFIG_DEBUG_BUGVERBOSE +#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 __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(format, file, line) #endif -#ifndef CONFIG_GENERIC_BUG -#define __BUG_ENTRY(cond_str, flags) +#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED +#define WARN_CONDITION_STR(cond_str) cond_str #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: +#define WARN_CONDITION_STR(cond_str) "" #endif -#define ASM_BUG_FLAGS(cond_str, flags) \ - __BUG_ENTRY(cond_str, flags) \ - mc 0,0 +#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 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 %[monc](%%r0),0\n" \ + __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \ + "%[flgs]", "%[size]") \ + : \ + : [monc] "i" (MONCODE_BUG), \ + [frmt] "i" (WARN_CONDITION_STR(cond_str)), \ + [file] "i" (__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 __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); \ + /* prevent tail-call optimization */ \ + asm(""); \ +} while (0) + +#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 -#endif /* CONFIG_BUG */ +#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ +#endif /* __ASSEMBLER__ */ #include <asm-generic/bug.h>
diff --git a/arch/s390/include/asm/pci_io.h b/arch/s390/include/asm/pci_io.h index 43a5ea4..f3bef5b 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/include/asm/preempt.h b/arch/s390/include/asm/preempt.h index 6ccd033..6e5821b 100644 --- a/arch/s390/include/asm/preempt.h +++ b/arch/s390/include/asm/preempt.h
@@ -8,7 +8,10 @@ #include <asm/cmpxchg.h> #include <asm/march.h> -/* 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) @@ -68,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; } } @@ -87,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 } /*
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 962cf04..aaceb1d 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; @@ -214,16 +217,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 +243,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 +278,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 +296,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 +308,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; }
diff --git a/arch/s390/include/uapi/asm/tape390.h b/arch/s390/include/uapi/asm/tape390.h deleted file mode 100644 index 90266c6..0000000 --- a/arch/s390/include/uapi/asm/tape390.h +++ /dev/null
@@ -1,103 +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 <holzheu@de.ibm.com> - * - *************************************************************************/ - -#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; - -/* - * 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/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index b7f1553..4873fe9 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S
@@ -23,6 +23,7 @@ #include <asm/unistd.h> #include <asm/page.h> #include <asm/sigp.h> +#include <asm/bug.h> #include <asm/irq.h> #include <asm/fpu-insn.h> #include <asm/setup.h> @@ -173,6 +174,16 @@ 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/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c index a36d731..1bf59c3 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);
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 459af23..e8bd19a 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)
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 19687da..1b5c6fc 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c
@@ -23,6 +23,7 @@ #include <linux/cpu.h> #include <linux/entry-common.h> #include <linux/kmsan.h> +#include <linux/bug.h> #include <asm/asm-extable.h> #include <asm/irqflags.h> #include <asm/ptrace.h> @@ -220,11 +221,48 @@ 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) { + regs->psw.addr = regs->gprs[14]; + 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; @@ -258,11 +296,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"); } @@ -297,6 +336,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
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 57f3980..97bab20 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); @@ -397,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, @@ -405,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 = { @@ -1052,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);
diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index 0c196a5..61d240a 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile
@@ -23,6 +23,7 @@ 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
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 80c4e51..4d8f099 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig
@@ -106,37 +106,12 @@ 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_34XX - def_tristate m - prompt "Support for 3480/3490 tape hardware" - depends on S390_TAPE - help - Select this option if you want to access IBM 3480/3490 magnetic - 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. + 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 dcbd511..126a87c 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile
@@ -39,10 +39,8 @@ 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_3590) += tape_3590.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 3953b31..59541bd 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 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 */ }; @@ -155,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; }; @@ -192,7 +186,6 @@ struct tape_device { /* Device discipline information. */ struct tape_discipline * discipline; - void * discdata; /* Generic status flags */ long tape_generic_status; @@ -281,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 new file mode 100644 index 0000000..c4ea32a --- /dev/null +++ b/drivers/s390/char/tape_3490.c
@@ -0,0 +1,825 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * tape device discipline for 3490 tapes. + * + * Copyright IBM Corp. 2001, 2009 + * Author(s): Carsten Otte <cotte@de.ibm.com> + * Tuan Ngo-Anh <ngoanh@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> + */ + +#define pr_fmt(fmt) "tape_3490: " fmt + +#include <linux/export.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/bio.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define TAPE_DBF_AREA tape_3490_dbf + +#include "tape.h" +#include "tape_std.h" + +/* + * Pointer to debug area. + */ +debug_info_t *TAPE_DBF_AREA = NULL; +EXPORT_SYMBOL(TAPE_DBF_AREA); + +struct tape_3490_block_id { + unsigned int unused : 10; + unsigned int block : 22; +}; + +/* + * Medium sense for 3490 tapes. There is no 'real' medium sense call. + * So we just do a normal sense. + */ +static void __tape_3490_medium_sense(struct tape_request *request) +{ + struct tape_device *device = request->device; + unsigned char *sense; + + if (request->rc == 0) { + sense = request->cpdata; + + /* + * This isn't quite correct. But since INTERVENTION_REQUIRED + * means that the drive is 'neither ready nor on-line' it is + * only slightly inaccurate to say there is no tape loaded if + * the drive isn't online... + */ + if (sense[0] & SENSE_INTERVENTION_REQUIRED) + tape_med_state_set(device, MS_UNLOADED); + else + tape_med_state_set(device, MS_LOADED); + + if (sense[1] & SENSE_WRITE_PROTECT) + device->tape_generic_status |= GMT_WR_PROT(~0); + else + device->tape_generic_status &= ~GMT_WR_PROT(~0); + } else + DBF_EVENT(4, "tape_3490: medium sense failed with rc=%d\n", + request->rc); + tape_free_request(request); +} + +static int tape_3490_medium_sense(struct tape_device *device) +{ + struct tape_request *request; + int rc; + + request = tape_alloc_request(1, 32); + if (IS_ERR(request)) { + DBF_EXCEPTION(6, "MSEN fail\n"); + return PTR_ERR(request); + } + + request->op = TO_MSEN; + tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); + rc = tape_do_io_interruptible(device, request); + __tape_3490_medium_sense(request); + return rc; +} + +static void tape_3490_medium_sense_async(struct tape_device *device) +{ + struct tape_request *request; + + request = tape_alloc_request(1, 32); + if (IS_ERR(request)) { + DBF_EXCEPTION(6, "MSEN fail\n"); + return; + } + + request->op = TO_MSEN; + tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); + request->callback = (void *) __tape_3490_medium_sense; + request->callback_data = NULL; + tape_do_io_async(device, request); +} + +struct tape_3490_work { + struct tape_device *device; + enum tape_op op; + struct work_struct work; +}; + +/* + * These functions are currently used only to schedule a medium_sense for + * later execution. This is because we get an interrupt whenever a medium + * is inserted but cannot call tape_do_io* from an interrupt context. + * Maybe that's useful for other actions we want to start from the + * interrupt handler. + * 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). + */ +static void +tape_3490_work_handler(struct work_struct *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_3490_medium_sense_async(device); + break; + default: + DBF_EVENT(3, "T3490: internal error: unknown work\n"); + } + tape_put_device(device); + kfree(p); +} + +static int +tape_3490_schedule_work(struct tape_device *device, enum tape_op op) +{ + struct tape_3490_work *p; + + if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) + return -ENOMEM; + + INIT_WORK(&p->work, tape_3490_work_handler); + + p->device = tape_get_device(device); + p->op = op; + + schedule_work(&p->work); + return 0; +} + +/* + * Done Handler is called when dev stat = DEVICE-END (successful operation) + */ +static inline int +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_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); + return rc; +} + +static inline int +tape_3490_erp_succeeded(struct tape_request *request) +{ + DBF_EVENT(3, "Error Recovery successful for %s\n", + tape_op_verbose[request->op]); + return tape_3490_done(request); +} + +static inline int +tape_3490_erp_retry(struct tape_request *request) +{ + DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]); + return TAPE_IO_RETRY; +} + +/* + * This function is called, when no request is outstanding and we get an + * interrupt + */ +static int +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_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); + } + return TAPE_IO_SUCCESS; +} + +static int +tape_3490_erp_bug(struct tape_device *device, struct tape_request *request, + struct irb *irb, int no) +{ + if (request->op != TO_ASSIGN) { + dev_err(&device->cdev->dev, "An unexpected condition %d " + "occurred in tape error recovery\n", no); + tape_dump_sense_dbf(device, request, irb); + } + return tape_3490_erp_failed(request, -EIO); +} + +/* + * Handle data overrun between cu and drive. The channel speed might + * be too slow. + */ +static int +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_3490_erp_failed(request, -EIO); + } + return tape_3490_erp_bug(device, request, irb, -1); +} + +/* + * Handle record sequence error. + */ +static int +tape_3490_erp_sequence(struct tape_device *device, + struct tape_request *request, struct irb *irb) +{ + if (irb->ecw[3] == 0x41) { + /* + * cu detected incorrect block-id sequence on tape. + */ + dev_warn (&device->cdev->dev, "The block ID sequence on the " + "tape is incorrect\n"); + return tape_3490_erp_failed(request, -EIO); + } + /* + * Record sequence error bit is set, but erpa does not + * show record sequence error. + */ + return tape_3490_erp_bug(device, request, irb, -2); +} + +/* + * This function analyses the tape's sense-data in case of a unit-check. + * If possible, it tries to recover from the error. Else the user is + * informed about the problem. + */ +static int +tape_3490_unit_check(struct tape_device *device, struct tape_request *request, + struct irb *irb) +{ + int inhibit_cu_recovery; + __u8* sense; + + inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0; + sense = irb->ecw; + + if ( + sense[0] & SENSE_COMMAND_REJECT && + sense[1] & SENSE_WRITE_PROTECT + ) { + if ( + request->op == TO_DSE || + request->op == TO_WRI || + request->op == TO_WTM + ) { + /* medium is write protected */ + return tape_3490_erp_failed(request, -EACCES); + } else { + return tape_3490_erp_bug(device, request, irb, -3); + } + } + + /* + * Special cases for various tape-states when reaching + * end of recorded area + * + * FIXME: Maybe a special case of the special case: + * sense[0] == SENSE_EQUIPMENT_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE && + * sense[3] == 0x47 (Volume Fenced) + * + * This was caused by continued FSF or FSR after an + * 'End Of Data'. + */ + if (( + sense[0] == SENSE_DATA_CHECK || + sense[0] == SENSE_EQUIPMENT_CHECK || + sense[0] == (SENSE_EQUIPMENT_CHECK | SENSE_DEFERRED_UNIT_CHECK) + ) && ( + sense[1] == SENSE_DRIVE_ONLINE || + sense[1] == (SENSE_BEGINNING_OF_TAPE | SENSE_WRITE_MODE) + )) { + switch (request->op) { + /* + * sense[0] == SENSE_DATA_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE + * sense[3] == 0x36 (End Of Data) + * + * Further seeks might return a 'Volume Fenced'. + */ + case TO_FSF: + case TO_FSB: + /* Trying to seek beyond end of recorded area */ + return tape_3490_erp_failed(request, -ENOSPC); + case TO_BSB: + return tape_3490_erp_retry(request); + + /* + * sense[0] == SENSE_DATA_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE && + * sense[3] == 0x36 (End Of Data) + */ + case TO_LBL: + /* Block could not be located. */ + return tape_3490_erp_failed(request, -EIO); + + case TO_RFO: + /* Read beyond end of recorded area -> 0 bytes read */ + return tape_3490_erp_failed(request, 0); + + /* + * sense[0] == SENSE_EQUIPMENT_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE && + * sense[3] == 0x38 (Physical End Of Volume) + */ + case TO_WRI: + /* Writing at physical end of volume */ + return tape_3490_erp_failed(request, -ENOSPC); + default: + return tape_3490_erp_failed(request, 0); + } + } + + /* Sensing special bits */ + if (sense[0] & SENSE_BUS_OUT_CHECK) + return tape_3490_erp_retry(request); + + if (sense[0] & SENSE_DATA_CHECK) { + /* + * hardware failure, damaged tape or improper + * operating conditions + */ + switch (sense[3]) { + case 0x23: + /* a read data check occurred */ + if ((sense[2] & SENSE_TAPE_SYNC_MODE) || + inhibit_cu_recovery) + // data check is not permanent, may be + // recovered. We always use async-mode with + // cu-recovery, so this should *never* happen. + 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_3490_erp_failed(request, -EIO); + case 0x25: + // a write data check occurred + if ((sense[2] & SENSE_TAPE_SYNC_MODE) || + inhibit_cu_recovery) + // data check is not permanent, may be + // recovered. We always use async-mode with + // cu-recovery, so this should *never* happen. + 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_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_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_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_3490_erp_failed(request, -EIO); + } + } + + if (sense[0] & SENSE_OVERRUN) + return tape_3490_erp_overrun(device, request, irb); + + if (sense[1] & SENSE_RECORD_SEQUENCE_ERR) + return tape_3490_erp_sequence(device, request, irb); + + /* Sensing erpa codes */ + switch (sense[3]) { + case 0x00: + /* Unit check with erpa code 0. Report and ignore. */ + return TAPE_IO_SUCCESS; + case 0x27: + /* + * Command reject. May indicate illegal channel program or + * buffer over/underrun. Since all channel programs are + * issued by this driver and ought be correct, we assume a + * over/underrun situation and retry the channel program. + */ + 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_3490_erp_failed(request, -EIO); + case 0x2b: + /* + * Environmental data present. Indicates either unload + * completed ok or read buffered log command completed ok. + */ + if (request->op == TO_RUN) { + /* Rewind unload completed ok. */ + tape_med_state_set(device, MS_UNLOADED); + return tape_3490_erp_succeeded(request); + } + /* 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_3490_erp_failed(request, -EIO); + case 0x2d: + /* Data security erase failure. */ + if (request->op == TO_DSE) + return tape_3490_erp_failed(request, -EIO); + /* Data security erase failure, but no such command issued. */ + return tape_3490_erp_bug(device, request, irb, sense[3]); + case 0x2e: + /* + * Not capable. This indicates either that the drive fails + * reading the format id mark or that format specified + * is not supported by the drive. + */ + dev_warn (&device->cdev->dev, "The tape unit cannot process " + "the tape format\n"); + 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_3490_erp_failed(request, -EACCES); + case 0x35: + /* + * Drive equipment check. One of the following: + * - cu cannot recover from a drive detected error + * - a check code message is shown on drive display + * - the cartridge loader does not respond correctly + * - a failure occurs during an index, load, or unload cycle + */ + dev_warn (&device->cdev->dev, "An equipment check has occurred" + " on the tape unit\n"); + return tape_3490_erp_failed(request, -EIO); + case 0x36: + /* End of data. */ + return tape_3490_erp_failed(request, -EIO); + case 0x38: + /* + * Physical end of tape. A read/write operation reached + * the physical end of tape. + */ + if (request->op==TO_WRI || + request->op==TO_DSE || + request->op==TO_WTM) + return tape_3490_erp_failed(request, -ENOSPC); + return tape_3490_erp_failed(request, -EIO); + case 0x39: + /* Backward at Beginning of tape. */ + return tape_3490_erp_failed(request, -EIO); + case 0x42: + /* + * Degraded mode. A condition that can cause degraded + * performance is detected. + */ + dev_warn (&device->cdev->dev, "The tape subsystem is running " + "in degraded mode\n"); + return tape_3490_erp_retry(request); + case 0x43: + /* Drive not ready. */ + tape_med_state_set(device, MS_UNLOADED); + /* Some commands commands are successful even in this case */ + if (sense[1] & SENSE_DRIVE_ONLINE) { + switch(request->op) { + case TO_ASSIGN: + case TO_UNASSIGN: + case TO_DIS: + case TO_NOP: + return tape_3490_done(request); + break; + default: + break; + } + } + 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_3490_erp_bug(device, request, + irb, sense[3]); + 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_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_3490_erp_failed(request, -EIO); + case 0x48: + /* Log sense data and 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_3490_erp_retry(request); + case 0x4e: + /* + * 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_3490_erp_failed(request, -ENOBUFS); + case 0x50: + /* + * Read buffered log (Overflow). CU is running in extended + * buffered log mode, and a counter overflows. This should + * never happen, since we're never running in extended + * buffered log mode. + */ + return tape_3490_erp_retry(request); + case 0x51: + /* + * Read buffered log (EOV). EOF processing occurs while the + * CU is in extended buffered log mode. This should never + * happen, since we're never running in extended buffered + * log mode. + */ + 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_3490_erp_succeeded(request); + } + return tape_3490_erp_bug(device, request, irb, sense[3]); + case 0x53: + /* Global command intercept. */ + return tape_3490_erp_retry(request); + case 0x54: + /* Channel interface recovery (temporary). */ + 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_3490_erp_failed(request, -EIO); + case 0x56: + /* Channel protocol error. */ + dev_warn (&device->cdev->dev, "A channel protocol error " + "occurred\n"); + return tape_3490_erp_failed(request, -EIO); + case 0x57: + /* Global status intercept. */ + return tape_3490_erp_retry(request); + /* The following erpas should have been covered earlier. */ + case 0x23: /* Read data check. */ + case 0x25: /* Write data check. */ + case 0x28: /* Write id mark check. */ + case 0x31: /* Tape void. */ + case 0x40: /* Overrun error. */ + case 0x41: /* Record sequence error. */ + /* All other erpas are reserved for future use. */ + default: + return tape_3490_erp_bug(device, request, irb, sense[3]); + } +} + +/* + * 3490 interrupt handler + */ +static int +tape_3490_irq(struct tape_device *device, struct tape_request *request, + struct irb *irb) +{ + if (request == NULL) + 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_3490_erp_failed(request, -ENOSPC); + } + + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) + return tape_3490_unit_check(device, request, irb); + + if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { + /* + * A unit exception occurs on skipping over a tapemark block. + */ + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { + if (request->op == TO_BSB || request->op == TO_FSB) + request->rescnt++; + else + DBF_EVENT(5, "Unit Exception!\n"); + } + return tape_3490_done(request); + } + + DBF_EVENT(6, "xunknownirq\n"); + tape_dump_sense_dbf(device, request, irb); + return TAPE_IO_STOP; +} + +static int +tape_3490_setup_device(struct tape_device * device) +{ + int rc; + + DBF_EVENT(6, "3490 device setup\n"); + if ((rc = tape_std_assign(device)) == 0) { + if ((rc = tape_3490_medium_sense(device)) != 0) { + DBF_LH(3, "3490 medium sense returned %d\n", rc); + } + } + return rc; +} + +static void +tape_3490_cleanup_device(struct tape_device *device) +{ + tape_std_unassign(device); +} + + +/* + * MTTELL: Tell block. Return the number of block relative to current file. + */ +static int +tape_3490_mttell(struct tape_device *device, int mt_count) +{ + struct { + struct tape_3490_block_id cbid; + struct tape_3490_block_id dbid; + } __attribute__ ((packed)) block_id; + int rc; + + rc = tape_std_read_block_id(device, (__u64 *) &block_id); + if (rc) + return rc; + + return block_id.cbid.block; +} + +/* + * MTSEEK: seek to the specified block. + */ +static int +tape_3490_mtseek(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + struct tape_3490_block_id * bid; + + if (mt_count > 0x3fffff) { + DBF_EXCEPTION(6, "xsee parm\n"); + return -EINVAL; + } + request = tape_alloc_request(3, 4); + if (IS_ERR(request)) + return PTR_ERR(request); + + /* setup ccws */ + request->op = TO_LBL; + bid = (struct tape_3490_block_id *) request->cpdata; + bid->block = mt_count; + + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * List of 3490 tape commands. + */ +static tape_mtop_fn tape_3490_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_3490_mtseek, + [MTTELL] = tape_3490_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 3490. + */ +static struct tape_discipline tape_discipline_3490 = { + .owner = THIS_MODULE, + .setup_device = tape_3490_setup_device, + .cleanup_device = tape_3490_cleanup_device, + .process_eov = tape_std_process_eov, + .irq = tape_3490_irq, + .read_block = tape_std_read_block, + .write_block = tape_std_write_block, + .mtop_array = tape_3490_mtop +}; + +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_3490_online(struct ccw_device *cdev) +{ + return tape_generic_online( + dev_get_drvdata(&cdev->dev), + &tape_discipline_3490 + ); +} + +static struct ccw_driver tape_3490_driver = { + .driver = { + .name = "tape_3490", + .owner = THIS_MODULE, + }, + .ids = tape_3490_ids, + .probe = tape_generic_probe, + .remove = tape_generic_remove, + .set_online = tape_3490_online, + .set_offline = tape_generic_offline, + .int_class = IRQIO_TAP, +}; + +int tape_3490_init(void) +{ + int rc; + + 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, "3490 init\n"); + /* Register driver for 3490 tapes. */ + rc = ccw_driver_register(&tape_3490_driver); + if (rc) + DBF_EVENT(3, "3490 init failed\n"); + else + DBF_EVENT(3, "3490 registered\n"); + return rc; +} + +void tape_3490_exit(void) +{ + ccw_driver_unregister(&tape_3490_driver); + + debug_unregister(TAPE_DBF_AREA); +} + +MODULE_DEVICE_TABLE(ccw, tape_3490_ids);
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c deleted file mode 100644 index a13e0ac..0000000 --- a/drivers/s390/char/tape_34xx.c +++ /dev/null
@@ -1,1204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * tape device discipline for 3480/3490 tapes. - * - * Copyright IBM Corp. 2001, 2009 - * Author(s): Carsten Otte <cotte@de.ibm.com> - * Tuan Ngo-Anh <ngoanh@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> - */ - -#define pr_fmt(fmt) "tape_34xx: " fmt - -#include <linux/export.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/bio.h> -#include <linux/workqueue.h> -#include <linux/slab.h> - -#define TAPE_DBF_AREA tape_34xx_dbf - -#include "tape.h" -#include "tape_std.h" - -/* - * Pointer to debug area. - */ -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 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. - */ -static void __tape_34xx_medium_sense(struct tape_request *request) -{ - struct tape_device *device = request->device; - unsigned char *sense; - - if (request->rc == 0) { - sense = request->cpdata; - - /* - * This isn't quite correct. But since INTERVENTION_REQUIRED - * means that the drive is 'neither ready nor on-line' it is - * only slightly inaccurate to say there is no tape loaded if - * the drive isn't online... - */ - if (sense[0] & SENSE_INTERVENTION_REQUIRED) - tape_med_state_set(device, MS_UNLOADED); - else - tape_med_state_set(device, MS_LOADED); - - if (sense[1] & SENSE_WRITE_PROTECT) - device->tape_generic_status |= GMT_WR_PROT(~0); - else - device->tape_generic_status &= ~GMT_WR_PROT(~0); - } else - DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", - request->rc); - tape_free_request(request); -} - -static int tape_34xx_medium_sense(struct tape_device *device) -{ - struct tape_request *request; - int rc; - - request = tape_alloc_request(1, 32); - if (IS_ERR(request)) { - DBF_EXCEPTION(6, "MSEN fail\n"); - return PTR_ERR(request); - } - - 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); - return rc; -} - -static void tape_34xx_medium_sense_async(struct tape_device *device) -{ - struct tape_request *request; - - request = tape_alloc_request(1, 32); - if (IS_ERR(request)) { - DBF_EXCEPTION(6, "MSEN fail\n"); - return; - } - - request->op = TO_MSEN; - tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); - request->callback = (void *) __tape_34xx_medium_sense; - request->callback_data = NULL; - tape_do_io_async(device, request); -} - -struct tape_34xx_work { - struct tape_device *device; - enum tape_op op; - struct work_struct work; -}; - -/* - * These functions are currently used only to schedule a medium_sense for - * later execution. This is because we get an interrupt whenever a medium - * is inserted but cannot call tape_do_io* from an interrupt context. - * Maybe that's useful for other actions we want to start from the - * interrupt handler. - * 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). - */ -static void -tape_34xx_work_handler(struct work_struct *work) -{ - struct tape_34xx_work *p = - container_of(work, struct tape_34xx_work, work); - struct tape_device *device = p->device; - - switch(p->op) { - case TO_MSEN: - tape_34xx_medium_sense_async(device); - break; - default: - DBF_EVENT(3, "T34XX: 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) -{ - struct tape_34xx_work *p; - - if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) - return -ENOMEM; - - INIT_WORK(&p->work, tape_34xx_work_handler); - - p->device = tape_get_device(device); - p->op = op; - - schedule_work(&p->work); - return 0; -} - -/* - * Done Handler is called when dev stat = DEVICE-END (successful operation) - */ -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; -} - -static inline int -tape_34xx_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); - return rc; -} - -static inline int -tape_34xx_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); -} - -static inline int -tape_34xx_erp_retry(struct tape_request *request) -{ - DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]); - return TAPE_IO_RETRY; -} - -/* - * This function is called, when no request is outstanding and we get an - * interrupt - */ -static int -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); - tape_dump_sense_dbf(device, NULL, irb); - } - return TAPE_IO_SUCCESS; -} - -static int -tape_34xx_erp_bug(struct tape_device *device, struct tape_request *request, - struct irb *irb, int no) -{ - if (request->op != TO_ASSIGN) { - dev_err(&device->cdev->dev, "An unexpected condition %d " - "occurred in tape error recovery\n", no); - tape_dump_sense_dbf(device, request, irb); - } - return tape_34xx_erp_failed(request, -EIO); -} - -/* - * Handle data overrun between cu and drive. The channel speed might - * be too slow. - */ -static int -tape_34xx_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_34xx_erp_bug(device, request, irb, -1); -} - -/* - * Handle record sequence error. - */ -static int -tape_34xx_erp_sequence(struct tape_device *device, - struct tape_request *request, struct irb *irb) -{ - if (irb->ecw[3] == 0x41) { - /* - * cu detected incorrect block-id sequence on tape. - */ - dev_warn (&device->cdev->dev, "The block ID sequence on the " - "tape is incorrect\n"); - return tape_34xx_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); -} - -/* - * This function analyses the tape's sense-data in case of a unit-check. - * If possible, it tries to recover from the error. Else the user is - * informed about the problem. - */ -static int -tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, - struct irb *irb) -{ - int inhibit_cu_recovery; - __u8* sense; - - inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0; - sense = irb->ecw; - - if ( - sense[0] & SENSE_COMMAND_REJECT && - sense[1] & SENSE_WRITE_PROTECT - ) { - if ( - request->op == TO_DSE || - request->op == TO_WRI || - request->op == TO_WTM - ) { - /* medium is write protected */ - return tape_34xx_erp_failed(request, -EACCES); - } else { - return tape_34xx_erp_bug(device, request, irb, -3); - } - } - - /* - * Special cases for various tape-states when reaching - * end of recorded area - * - * FIXME: Maybe a special case of the special case: - * sense[0] == SENSE_EQUIPMENT_CHECK && - * sense[1] == SENSE_DRIVE_ONLINE && - * sense[3] == 0x47 (Volume Fenced) - * - * This was caused by continued FSF or FSR after an - * 'End Of Data'. - */ - if (( - sense[0] == SENSE_DATA_CHECK || - sense[0] == SENSE_EQUIPMENT_CHECK || - sense[0] == (SENSE_EQUIPMENT_CHECK | SENSE_DEFERRED_UNIT_CHECK) - ) && ( - sense[1] == SENSE_DRIVE_ONLINE || - sense[1] == (SENSE_BEGINNING_OF_TAPE | SENSE_WRITE_MODE) - )) { - switch (request->op) { - /* - * sense[0] == SENSE_DATA_CHECK && - * sense[1] == SENSE_DRIVE_ONLINE - * sense[3] == 0x36 (End Of Data) - * - * Further seeks might return a 'Volume Fenced'. - */ - case TO_FSF: - case TO_FSB: - /* Trying to seek beyond end of recorded area */ - return tape_34xx_erp_failed(request, -ENOSPC); - case TO_BSB: - return tape_34xx_erp_retry(request); - - /* - * sense[0] == SENSE_DATA_CHECK && - * sense[1] == SENSE_DRIVE_ONLINE && - * sense[3] == 0x36 (End Of Data) - */ - 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: - /* Read beyond end of recorded area -> 0 bytes read */ - return tape_34xx_erp_failed(request, 0); - - /* - * sense[0] == SENSE_EQUIPMENT_CHECK && - * sense[1] == SENSE_DRIVE_ONLINE && - * sense[3] == 0x38 (Physical End Of Volume) - */ - case TO_WRI: - /* Writing at physical end of volume */ - return tape_34xx_erp_failed(request, -ENOSPC); - default: - return tape_34xx_erp_failed(request, 0); - } - } - - /* Sensing special bits */ - if (sense[0] & SENSE_BUS_OUT_CHECK) - return tape_34xx_erp_retry(request); - - if (sense[0] & SENSE_DATA_CHECK) { - /* - * hardware failure, damaged tape or improper - * operating conditions - */ - switch (sense[3]) { - case 0x23: - /* a read data check occurred */ - if ((sense[2] & SENSE_TAPE_SYNC_MODE) || - inhibit_cu_recovery) - // 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, - 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); - case 0x25: - // a write data check occurred - if ((sense[2] & SENSE_TAPE_SYNC_MODE) || - inhibit_cu_recovery) - // 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, - 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); - 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); - 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); - 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); - 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); - } - } - - if (sense[0] & SENSE_OVERRUN) - return tape_34xx_erp_overrun(device, request, irb); - - if (sense[1] & SENSE_RECORD_SEQUENCE_ERR) - return tape_34xx_erp_sequence(device, request, irb); - - /* Sensing erpa codes */ - switch (sense[3]) { - 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 - * buffer over/underrun. Since all channel programs are - * 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); - 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); - 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 - * completed ok or read buffered log command completed ok. - */ - if (request->op == TO_RUN) { - /* Rewind unload completed ok. */ - tape_med_state_set(device, MS_UNLOADED); - return tape_34xx_erp_succeeded(request); - } - /* tape_34xx doesn't use read buffered log commands. */ - return tape_34xx_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); - case 0x2d: - /* Data security erase failure. */ - if (request->op == TO_DSE) - return tape_34xx_erp_failed(request, -EIO); - /* Data security erase failure, but no such command issued. */ - return tape_34xx_erp_bug(device, request, irb, sense[3]); - case 0x2e: - /* - * Not capable. This indicates either that the drive fails - * reading the format id mark or that format specified - * is not supported by the drive. - */ - dev_warn (&device->cdev->dev, "The tape unit cannot process " - "the tape format\n"); - return tape_34xx_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); - 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"); - tape_34xx_delete_sbid_from(device, 0); - 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: - * - cu cannot recover from a drive detected error - * - a check code message is shown on drive display - * - the cartridge loader does not respond correctly - * - a failure occurs during an index, load, or unload cycle - */ - dev_warn (&device->cdev->dev, "An equipment check has occurred" - " 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]); - 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 - * the physical end of tape. - */ - 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); - 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"); - tape_34xx_delete_sbid_from(device, 0); - return tape_34xx_erp_failed(request, -EIO); - case 0x42: - /* - * Degraded mode. A condition that can cause degraded - * performance is detected. - */ - dev_warn (&device->cdev->dev, "The tape subsystem is running " - "in degraded mode\n"); - 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) { - switch(request->op) { - case TO_ASSIGN: - case TO_UNASSIGN: - case TO_DIS: - case TO_NOP: - return tape_34xx_done(request); - break; - default: - break; - } - } - return tape_34xx_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, - irb, sense[3]); - return tape_34xx_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); - 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 " - "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. */ - 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: - 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]); - 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]); - case 0x50: - /* - * Read buffered log (Overflow). CU is running in extended - * buffered log mode, and a counter overflows. This should - * never happen, since we're never running in extended - * buffered log mode. - */ - return tape_34xx_erp_retry(request); - case 0x51: - /* - * Read buffered log (EOV). EOF processing occurs while the - * CU is in extended buffered log mode. This should never - * happen, since we're never running in extended buffered - * log mode. - */ - return tape_34xx_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); - tape_34xx_delete_sbid_from(device, 0); - return tape_34xx_erp_succeeded(request); - } - return tape_34xx_erp_bug(device, request, irb, sense[3]); - case 0x53: - /* Global command intercept. */ - return tape_34xx_erp_retry(request); - case 0x54: - /* Channel interface recovery (temporary). */ - return tape_34xx_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); - case 0x56: - /* Channel protocol error. */ - dev_warn (&device->cdev->dev, "A channel protocol error " - "occurred\n"); - return tape_34xx_erp_failed(request, -EIO); - case 0x57: - /* - * 3480: Attention intercept. - * 3490: 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 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" - " 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. */ - case 0x41: /* Record sequence error. */ - /* All other erpas are reserved for future use. */ - default: - return tape_34xx_erp_bug(device, request, irb, sense[3]); - } -} - -/* - * 3480/3490 interrupt handler - */ -static int -tape_34xx_irq(struct tape_device *device, struct tape_request *request, - struct irb *irb) -{ - if (request == NULL) - return tape_34xx_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); - } - - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) - return tape_34xx_unit_check(device, request, irb); - - if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { - /* - * A unit exception occurs on skipping over a tapemark block. - */ - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { - if (request->op == TO_BSB || request->op == TO_FSB) - request->rescnt++; - else - DBF_EVENT(5, "Unit Exception!\n"); - } - return tape_34xx_done(request); - } - - DBF_EVENT(6, "xunknownirq\n"); - tape_dump_sense_dbf(device, request, irb); - 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) -{ - 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; - - DBF_EVENT(6, "34xx 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); - } - } - discdata = kmalloc(sizeof(struct list_head), GFP_KERNEL); - if (discdata) { - INIT_LIST_HEAD(discdata); - device->discdata = discdata; - } - - return rc; -} - -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; - } -} - - -/* - * MTTELL: Tell block. Return the number of block relative to current file. - */ -static int -tape_34xx_mttell(struct tape_device *device, int mt_count) -{ - struct { - struct tape_34xx_block_id cbid; - struct tape_34xx_block_id dbid; - } __attribute__ ((packed)) block_id; - int rc; - - rc = tape_std_read_block_id(device, (__u64 *) &block_id); - if (rc) - return rc; - - tape_34xx_add_sbid(device, block_id.cbid); - return block_id.cbid.block; -} - -/* - * MTSEEK: seek to the specified block. - */ -static int -tape_34xx_mtseek(struct tape_device *device, int mt_count) -{ - struct tape_request *request; - struct tape_34xx_block_id * bid; - - if (mt_count > 0x3fffff) { - DBF_EXCEPTION(6, "xsee parm\n"); - return -EINVAL; - } - request = tape_alloc_request(3, 4); - if (IS_ERR(request)) - return PTR_ERR(request); - - /* 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); - tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); - - /* execute it */ - return tape_do_io_free(device, request); -} - -/* - * List of 3480/3490 magnetic tape commands. - */ -static tape_mtop_fn tape_34xx_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_34xx_mtseek, - [MTTELL] = tape_34xx_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 3480 and 3490. - */ -static struct tape_discipline tape_discipline_34xx = { - .owner = THIS_MODULE, - .setup_device = tape_34xx_setup_device, - .cleanup_device = tape_34xx_cleanup_device, - .process_eov = tape_std_process_eov, - .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 -}; - -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 */ }, -}; - -static int -tape_34xx_online(struct ccw_device *cdev) -{ - return tape_generic_online( - dev_get_drvdata(&cdev->dev), - &tape_discipline_34xx - ); -} - -static struct ccw_driver tape_34xx_driver = { - .driver = { - .name = "tape_34xx", - .owner = THIS_MODULE, - }, - .ids = tape_34xx_ids, - .probe = tape_generic_probe, - .remove = tape_generic_remove, - .set_online = tape_34xx_online, - .set_offline = tape_generic_offline, - .int_class = IRQIO_TAP, -}; - -static int -tape_34xx_init (void) -{ - int rc; - - TAPE_DBF_AREA = debug_register ( "tape_34xx", 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"); - /* Register driver for 3480/3490 tapes. */ - rc = ccw_driver_register(&tape_34xx_driver); - if (rc) - DBF_EVENT(3, "34xx init failed\n"); - else - DBF_EVENT(3, "34xx registered\n"); - return rc; -} - -static void -tape_34xx_exit(void) -{ - ccw_driver_unregister(&tape_34xx_driver); - - debug_unregister(TAPE_DBF_AREA); -} - -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_LICENSE("GPL"); - -module_init(tape_34xx_init); -module_exit(tape_34xx_exit);
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c deleted file mode 100644 index 0d80f43..0000000 --- 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 <shbader@de.ibm.com> - * Michael Holzheu <holzheu@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> - */ - -#define pr_fmt(fmt) "tape_3590: " fmt - -#include <linux/export.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/bio.h> -#include <asm/ebcdic.h> - -#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 b398d8a..0000000 --- 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 <shbader@de.ibm.com> - * Michael Holzheu <holzheu@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> - */ - -#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_char.c b/drivers/s390/char/tape_char.c index c5d3c30..879331e 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_class.c b/drivers/s390/char/tape_class.c index 6fa7b78..1ae9ad2 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 <shbader@de.ibm.com>"); -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 d25ac07..0bb6f0c 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 0250076..a62b650 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 */ @@ -74,9 +75,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) @@ -1312,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; } @@ -1325,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);
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 43a5586..96b7440 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c
@@ -22,7 +22,6 @@ #include <asm/types.h> #include <asm/idals.h> #include <asm/ebcdic.h> -#include <asm/tape390.h> #define TAPE_DBF_AREA tape_core_dbf @@ -119,36 +118,6 @@ tape_std_unassign (struct tape_device *device) } /* - * 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. */ int @@ -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 2cf9f72..2b67b0d 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 <asm/tape390.h> - /* * Biggest block size of 256K to handle. */ @@ -21,54 +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 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 */ -#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 @@ -105,7 +74,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. */ @@ -133,10 +101,7 @@ void tape_std_process_eov(struct tape_device *); /* S390 tape types */ enum s390_tape_type { - tape_3480, tape_3490, - tape_3590, - tape_3592, }; #endif // _TAPE_STD_H
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 4c85df7..ac24e01 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); }
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index a445494..19cd27e 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 7a3b99f..c796773 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)"); /*