diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 20:17:03 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 20:17:03 -0800 |
| commit | c48953d81972bfe16a9e3551883992aa6efe541a (patch) | |
| tree | f59a8a296e4b5e6239267465a46c83bfbbf22346 /drivers | |
| parent | 861ea34546dcde8600878d5e7f746795f22fc818 (diff) | |
| parent | 5ae76830c76cf38708399245606e4e07a33fe51c (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
...
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/s390/char/Kconfig | 33 | ||||
| -rw-r--r-- | drivers/s390/char/Makefile | 6 | ||||
| -rw-r--r-- | drivers/s390/char/tape.h | 13 | ||||
| -rw-r--r-- | drivers/s390/char/tape_3490.c | 825 | ||||
| -rw-r--r-- | drivers/s390/char/tape_34xx.c | 1204 | ||||
| -rw-r--r-- | drivers/s390/char/tape_3590.c | 1612 | ||||
| -rw-r--r-- | drivers/s390/char/tape_3590.h | 175 | ||||
| -rw-r--r-- | drivers/s390/char/tape_char.c | 5 | ||||
| -rw-r--r-- | drivers/s390/char/tape_class.c | 15 | ||||
| -rw-r--r-- | drivers/s390/char/tape_class.h | 2 | ||||
| -rw-r--r-- | drivers/s390/char/tape_core.c | 14 | ||||
| -rw-r--r-- | drivers/s390/char/tape_std.c | 32 | ||||
| -rw-r--r-- | drivers/s390/char/tape_std.h | 45 | ||||
| -rw-r--r-- | drivers/s390/cio/css.c | 2 | ||||
| -rw-r--r-- | drivers/s390/crypto/ap_bus.c | 12 | ||||
| -rw-r--r-- | drivers/s390/crypto/zcrypt_api.c | 2 |
16 files changed, 862 insertions, 3135 deletions
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)"); /* |
