summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 20:17:03 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 20:17:03 -0800
commitc48953d81972bfe16a9e3551883992aa6efe541a (patch)
treef59a8a296e4b5e6239267465a46c83bfbbf22346
parent861ea34546dcde8600878d5e7f746795f22fc818 (diff)
parent5ae76830c76cf38708399245606e4e07a33fe51c (diff)
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 ...
-rw-r--r--Documentation/arch/s390/mm.rst4
-rw-r--r--Documentation/features/debug/stackprotector/arch-support.txt2
-rw-r--r--arch/s390/Kconfig19
-rw-r--r--arch/s390/boot/Makefile1
-rw-r--r--arch/s390/boot/startup.c1
-rw-r--r--arch/s390/configs/debug_defconfig1
-rw-r--r--arch/s390/configs/defconfig1
-rw-r--r--arch/s390/include/asm/ap.h2
-rw-r--r--arch/s390/include/asm/asm-prototypes.h1
-rw-r--r--arch/s390/include/asm/asm.h2
-rw-r--r--arch/s390/include/asm/bug.h141
-rw-r--r--arch/s390/include/asm/pci_io.h1
-rw-r--r--arch/s390/include/asm/preempt.h47
-rw-r--r--arch/s390/include/asm/ptrace.h42
-rw-r--r--arch/s390/include/uapi/asm/tape390.h103
-rw-r--r--arch/s390/kernel/entry.S11
-rw-r--r--arch/s390/kernel/machine_kexec_file.c4
-rw-r--r--arch/s390/kernel/perf_cpum_sf.c2
-rw-r--r--arch/s390/kernel/traps.c46
-rw-r--r--arch/s390/pci/pci.c35
-rw-r--r--arch/s390/purgatory/Makefile1
-rw-r--r--drivers/s390/char/Kconfig33
-rw-r--r--drivers/s390/char/Makefile6
-rw-r--r--drivers/s390/char/tape.h13
-rw-r--r--drivers/s390/char/tape_3490.c825
-rw-r--r--drivers/s390/char/tape_34xx.c1204
-rw-r--r--drivers/s390/char/tape_3590.c1612
-rw-r--r--drivers/s390/char/tape_3590.h175
-rw-r--r--drivers/s390/char/tape_char.c5
-rw-r--r--drivers/s390/char/tape_class.c15
-rw-r--r--drivers/s390/char/tape_class.h2
-rw-r--r--drivers/s390/char/tape_core.c14
-rw-r--r--drivers/s390/char/tape_std.c32
-rw-r--r--drivers/s390/char/tape_std.h45
-rw-r--r--drivers/s390/cio/css.c2
-rw-r--r--drivers/s390/crypto/ap_bus.c12
-rw-r--r--drivers/s390/crypto/zcrypt_api.c2
37 files changed, 1156 insertions, 3308 deletions
diff --git a/Documentation/arch/s390/mm.rst b/Documentation/arch/s390/mm.rst
index 084adad5eef9..19681157c6f2 100644
--- a/Documentation/arch/s390/mm.rst
+++ b/Documentation/arch/s390/mm.rst
@@ -109,3 +109,7 @@ Virtual memory layout
| KASAN shadow | KASAN untracked
| |
+------------------+ ASCE limit
+ | |
+ | CONFIG_ILLEGAL_POINTER_VALUE causes memory access fault
+ | |
+ +------------------+
diff --git a/Documentation/features/debug/stackprotector/arch-support.txt b/Documentation/features/debug/stackprotector/arch-support.txt
index de8f43f2e5d6..43e49c71612e 100644
--- a/Documentation/features/debug/stackprotector/arch-support.txt
+++ b/Documentation/features/debug/stackprotector/arch-support.txt
@@ -21,7 +21,7 @@
| parisc: | TODO |
| powerpc: | ok |
| riscv: | ok |
- | s390: | TODO |
+ | s390: | ok |
| sh: | ok |
| sparc: | TODO |
| um: | TODO |
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0e5fad5f06ca..cda697a03abf 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -69,6 +69,12 @@ config CC_HAS_ASM_AOR_FORMAT_FLAGS
Clang versions before 19.1.0 do not support A,
O, and R inline assembly format flags.
+config CC_HAS_ASM_IMMEDIATE_STRINGS
+ def_bool !(CC_IS_GCC && GCC_VERSION < 90000)
+ help
+ GCC versions before 9.0.0 cannot handle strings as immediate
+ input operands in inline assemblies.
+
config CC_HAS_STACKPROTECTOR_GLOBAL
def_bool $(cc-option, -mstack-protector-guard=global -mstack-protector-guard-record)
@@ -85,6 +91,7 @@ config S390
select ARCH_ENABLE_MEMORY_HOTREMOVE
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
+ select ARCH_HAS_CC_CAN_LINK
select ARCH_HAS_CPU_FINALIZE_INIT
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL
@@ -294,6 +301,14 @@ config PGTABLE_LEVELS
source "kernel/livepatch/Kconfig"
+config ARCH_CC_CAN_LINK
+ bool
+ default $(cc_can_link_user,-m64)
+
+config ARCH_USERFLAGS
+ string
+ default "-m64"
+
config ARCH_SUPPORTS_KEXEC
def_bool y
@@ -704,6 +719,10 @@ config ARCH_SPARSEMEM_ENABLE
config ARCH_SPARSEMEM_DEFAULT
def_bool y
+config ILLEGAL_POINTER_VALUE
+ hex
+ default 0xdead000000000000
+
config MAX_PHYSMEM_BITS
int "Maximum size of supported physical memory in bits (42-53)"
range 42 53
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile
index 490167faba7a..a1e719a79d38 100644
--- a/arch/s390/boot/Makefile
+++ b/arch/s390/boot/Makefile
@@ -21,6 +21,7 @@ KBUILD_AFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_AFLAGS_DECOMPRESSOR))
KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_CFLAGS_DECOMPRESSOR))
KBUILD_AFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS
KBUILD_CFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS
+KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe)
CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c
index f77067dfc2a8..7f3343493ab9 100644
--- a/arch/s390/boot/startup.c
+++ b/arch/s390/boot/startup.c
@@ -336,6 +336,7 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size)
BUILD_BUG_ON(!IS_ALIGNED(TEXT_OFFSET, THREAD_SIZE));
BUILD_BUG_ON(!IS_ALIGNED(__NO_KASLR_START_KERNEL, THREAD_SIZE));
BUILD_BUG_ON(__NO_KASLR_END_KERNEL > _REGION1_SIZE);
+ BUILD_BUG_ON(CONFIG_ILLEGAL_POINTER_VALUE < _REGION1_SIZE);
vsize = get_vmem_size(ident_map_size, vmemmap_size, vmalloc_size, _REGION3_SIZE);
boot_debug("vmem size estimated: 0x%016lx\n", vsize);
if (IS_ENABLED(CONFIG_KASAN) || __NO_KASLR_END_KERNEL > _REGION2_SIZE ||
diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig
index 0713914b25b4..7a91d300c549 100644
--- a/arch/s390/configs/debug_defconfig
+++ b/arch/s390/configs/debug_defconfig
@@ -446,6 +446,7 @@ CONFIG_BLK_DEV_RAM_SIZE=32768
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_RBD=m
CONFIG_BLK_DEV_NVME=m
+CONFIG_BLK_DEV_NULL_BLK=m
CONFIG_ENCLOSURE_SERVICES=m
CONFIG_GENWQE=m
CONFIG_RAID_ATTRS=m
diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig
index c064e0cacc98..3bb2aa8ecd13 100644
--- a/arch/s390/configs/defconfig
+++ b/arch/s390/configs/defconfig
@@ -436,6 +436,7 @@ CONFIG_BLK_DEV_RAM_SIZE=32768
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_RBD=m
CONFIG_BLK_DEV_NVME=m
+CONFIG_BLK_DEV_NULL_BLK=m
CONFIG_ENCLOSURE_SERVICES=m
CONFIG_GENWQE=m
CONFIG_RAID_ATTRS=m
diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h
index b24459f692fa..3b95c6531a67 100644
--- a/arch/s390/include/asm/ap.h
+++ b/arch/s390/include/asm/ap.h
@@ -78,7 +78,7 @@ union ap_queue_status_reg {
};
/**
- * ap_intructions_available() - Test if AP instructions are available.
+ * ap_instructions_available() - Test if AP instructions are available.
*
* Returns true if the AP instructions are installed, otherwise false.
*/
diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h
index f662eb4b9246..7bd1801cf241 100644
--- a/arch/s390/include/asm/asm-prototypes.h
+++ b/arch/s390/include/asm/asm-prototypes.h
@@ -3,6 +3,7 @@
#include <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 e9062b01e2a2..510901c2a5f9 100644
--- a/arch/s390/include/asm/asm.h
+++ b/arch/s390/include/asm/asm.h
@@ -30,7 +30,7 @@
*/
#if defined(__GCC_ASM_FLAG_OUTPUTS__) && !(IS_ENABLED(CONFIG_CC_ASM_FLAG_OUTPUT_BROKEN))
-#define __HAVE_ASM_FLAG_OUTPUTS__
+#define __HAVE_ASM_FLAG_OUTPUTS__ 1
#define CC_IPM(sym)
#define CC_OUT(sym, var) "=@cc" (var)
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index ee9221bb5d18..59017fd3d935 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_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_FLAGS(cond_str, flags) \
- asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags)));
+#define BUG() \
+do { \
+ __BUG_ASM("", 0); \
+ unreachable(); \
+} while (0)
-#define __WARN_FLAGS(cond_str, flags) \
-do { \
- __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \
+#define __WARN_FLAGS(cond_str, flags) \
+do { \
+ __BUG_ASM(cond_str, BUGFLAG_WARNING | (flags)); \
} while (0)
-#define BUG() \
-do { \
- __BUG_FLAGS("", 0); \
- unreachable(); \
+#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 43a5ea4ee20f..f3bef5bc7223 100644
--- a/arch/s390/include/asm/pci_io.h
+++ b/arch/s390/include/asm/pci_io.h
@@ -18,6 +18,7 @@
#define ZPCI_IOMAP_SHIFT 48
#define ZPCI_IOMAP_ADDR_SHIFT 62
#define ZPCI_IOMAP_ADDR_BASE (1UL << ZPCI_IOMAP_ADDR_SHIFT)
+#define ZPCI_IOMAP_ADDR_MAX ((1UL << (ZPCI_IOMAP_ADDR_SHIFT + 1)) - 1)
#define ZPCI_IOMAP_ADDR_OFF_MASK ((1UL << ZPCI_IOMAP_SHIFT) - 1)
#define ZPCI_IOMAP_MAX_ENTRIES \
(1UL << (ZPCI_IOMAP_ADDR_SHIFT - ZPCI_IOMAP_SHIFT))
diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h
index 6ccd033acfe5..6e5821bb047e 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 962cf042c66d..aaceb1d9110a 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -120,7 +120,10 @@ struct pt_regs {
unsigned long gprs[NUM_GPRS];
};
};
- unsigned long orig_gpr2;
+ union {
+ unsigned long orig_gpr2;
+ unsigned long monitor_code;
+ };
union {
struct {
unsigned int int_code;
@@ -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 user_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 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 90266c696486..000000000000
--- 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 b7f1553d9ee5..4873fe9d891b 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 @@ SYM_FUNC_START(__switch_to_asm)
BR_EX %r14
SYM_FUNC_END(__switch_to_asm)
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+SYM_FUNC_START(__WARN_trap)
+ mc MONCODE_BUG_ARG(%r0),0
+ BR_EX %r14
+SYM_FUNC_END(__WARN_trap)
+EXPORT_SYMBOL(__WARN_trap)
+
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+
#if IS_ENABLED(CONFIG_KVM)
/*
* __sie64a calling convention:
diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c
index a36d7311c668..1bf59c3f0e2b 100644
--- a/arch/s390/kernel/machine_kexec_file.c
+++ b/arch/s390/kernel/machine_kexec_file.c
@@ -270,8 +270,10 @@ void *kexec_file_add_components(struct kimage *image,
if (image->kernel_buf_len < minsize + max_command_line_size)
goto out;
- if (image->cmdline_buf_len >= max_command_line_size)
+ if (image->cmdline_buf_len >= max_command_line_size) {
+ pr_err("Kernel command line exceeds supported limit of %lu", max_command_line_size);
goto out;
+ }
memcpy(data.parm->command_line, image->cmdline_buf,
image->cmdline_buf_len);
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index 459af23a47a5..e8bd19ac82c7 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -841,7 +841,7 @@ static bool is_callchain_event(struct perf_event *event)
u64 sample_type = event->attr.sample_type;
return sample_type & (PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER |
- PERF_SAMPLE_STACK_USER);
+ PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_STACK_USER);
}
static int cpumsf_pmu_event_init(struct perf_event *event)
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 19687dab32f7..1b5c6fc431cc 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 57f3980b98a9..97bab20bc163 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 0c196a5b194a..61d240a37633 100644
--- a/arch/s390/purgatory/Makefile
+++ b/arch/s390/purgatory/Makefile
@@ -23,6 +23,7 @@ KBUILD_CFLAGS += -D__DISABLE_EXPORTS
KBUILD_CFLAGS += $(CLANG_FLAGS)
KBUILD_CFLAGS += $(if $(CONFIG_CC_IS_CLANG),-Wno-microsoft-anon-tag)
KBUILD_CFLAGS += $(call cc-option,-fno-PIE)
+KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe)
KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS))
KBUILD_AFLAGS += -D__DISABLE_EXPORTS
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index 80c4e5101c97..4d8f09910a46 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -106,37 +106,12 @@ config S390_UV_UAPI
config S390_TAPE
def_tristate m
- prompt "S/390 tape device support"
+ prompt "Support for 3490E tape on VTS"
depends on CCW
help
- Select this option if you want to access channel-attached tape
- devices on IBM S/390 or zSeries.
- If you select this option you will also want to select at
- least one of the tape interface options and one of the tape
- hardware options in order to access a tape device.
- This option is also available as a module. The module will be
- called tape390 and include all selected interfaces and
- hardware drivers.
-
-comment "S/390 tape hardware support"
- depends on S390_TAPE
-
-config S390_TAPE_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 dcbd51152ee3..126a87c3c6f8 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -39,10 +39,8 @@ obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
obj-$(CONFIG_VMCP) += vmcp.o
tape-$(CONFIG_PROC_FS) += tape_proc.o
-tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
-obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
-obj-$(CONFIG_S390_TAPE_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 3953b31b0c55..59541bd88d51 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 000000000000..c4ea32af1b65
--- /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 a13e0ac1a4e2..000000000000
--- 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 0d80f43b175d..000000000000
--- a/drivers/s390/char/tape_3590.c
+++ /dev/null
@@ -1,1612 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * tape device discipline for 3590 tapes.
- *
- * Copyright IBM Corp. 2001, 2009
- * Author(s): Stefan Bader <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 b398d8a3ed3c..000000000000
--- a/drivers/s390/char/tape_3590.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * tape device discipline for 3590 tapes.
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Stefan Bader <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 c5d3c303c15c..879331e2f283 100644
--- a/drivers/s390/char/tape_char.c
+++ b/drivers/s390/char/tape_char.c
@@ -412,10 +412,7 @@ __tapechar_ioctl(struct tape_device *device,
return put_user_mtget(data, &get);
}
- /* Try the discipline ioctl function. */
- if (device->discipline->ioctl_fn == NULL)
- return -EINVAL;
- return device->discipline->ioctl_fn(device, no, (unsigned long)data);
+ return -EINVAL;
}
static long
diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c
index 6fa7b7824856..1ae9ad219c08 100644
--- a/drivers/s390/char/tape_class.c
+++ b/drivers/s390/char/tape_class.c
@@ -15,13 +15,6 @@
#include "tape_class.h"
-MODULE_AUTHOR("Stefan Bader <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 d25ac075b1ad..0bb6f0ca45b6 100644
--- a/drivers/s390/char/tape_class.h
+++ b/drivers/s390/char/tape_class.h
@@ -55,5 +55,7 @@ struct tape_class_device *register_tape_dev(
char * node_name
);
void unregister_tape_dev(struct device *device, struct tape_class_device *tcd);
+int tape_class_init(void);
+void tape_class_exit(void);
#endif /* __TAPE_CLASS_H__ */
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 0250076a7d9f..a62b650ea3c2 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -28,6 +28,7 @@
#include "tape.h"
#include "tape_std.h"
+#include "tape_class.h"
#define LONG_BUSY_TIMEOUT 180 /* seconds */
@@ -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 43a5586685ff..96b7440126d2 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 2cf9f725b3b3..2b67b0dc9ddd 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 4c85df7a548e..ac24e019020e 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -235,7 +235,7 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid,
return sch;
err:
- kfree(sch);
+ put_device(&sch->dev);
return ERR_PTR(ret);
}
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index a445494fd2be..19cd27e9a3f3 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -52,24 +52,24 @@ MODULE_LICENSE("GPL");
int ap_domain_index = -1; /* Adjunct Processor Domain Index */
static DEFINE_SPINLOCK(ap_domain_lock);
-module_param_named(domain, ap_domain_index, int, 0440);
+module_param_named(domain, ap_domain_index, int, 0444);
MODULE_PARM_DESC(domain, "domain index for ap devices");
EXPORT_SYMBOL(ap_domain_index);
static int ap_thread_flag;
-module_param_named(poll_thread, ap_thread_flag, int, 0440);
+module_param_named(poll_thread, ap_thread_flag, int, 0444);
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
static char *apm_str;
-module_param_named(apmask, apm_str, charp, 0440);
+module_param_named(apmask, apm_str, charp, 0444);
MODULE_PARM_DESC(apmask, "AP bus adapter mask.");
static char *aqm_str;
-module_param_named(aqmask, aqm_str, charp, 0440);
+module_param_named(aqmask, aqm_str, charp, 0444);
MODULE_PARM_DESC(aqmask, "AP bus domain mask.");
static int ap_useirq = 1;
-module_param_named(useirq, ap_useirq, int, 0440);
+module_param_named(useirq, ap_useirq, int, 0444);
MODULE_PARM_DESC(useirq, "Use interrupt if available, default is 1 (on).");
atomic_t ap_max_msg_size = ATOMIC_INIT(AP_DEFAULT_MAX_MSG_SIZE);
@@ -130,7 +130,7 @@ debug_info_t *ap_dbf_info;
*/
static mempool_t *ap_msg_pool;
static unsigned int ap_msg_pool_min_items = 8;
-module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0440);
+module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0400);
MODULE_PARM_DESC(msgpool_min_items, "AP message pool minimal items");
/*
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 7a3b99f065f2..c796773fbce8 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -50,7 +50,7 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
MODULE_LICENSE("GPL");
unsigned int zcrypt_mempool_threshold = 5;
-module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0440);
+module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0400);
MODULE_PARM_DESC(mempool_threshold, "CCA and EP11 request/reply mempool minimal items (min: 1)");
/*