diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/s390/char/fs3270.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/keyboard.c | 12 | ||||
-rw-r--r-- | drivers/s390/char/monreader.c | 6 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.c | 42 | ||||
-rw-r--r-- | drivers/s390/char/tape.h | 1 | ||||
-rw-r--r-- | drivers/s390/char/tape_34xx.c | 8 | ||||
-rw-r--r-- | drivers/s390/char/tape_3590.c | 1301 | ||||
-rw-r--r-- | drivers/s390/char/tape_3590.h | 124 | ||||
-rw-r--r-- | drivers/s390/char/tape_class.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/tape_core.c | 57 | ||||
-rw-r--r-- | drivers/s390/char/tape_std.c | 15 | ||||
-rw-r--r-- | drivers/s390/char/tape_std.h | 12 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.c | 9 | ||||
-rw-r--r-- | drivers/s390/char/vmlogrdr.c | 3 |
15 files changed, 1514 insertions, 83 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 6377a96735df..0c0162ff6c0c 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -26,4 +26,5 @@ tape-$(CONFIG_PROC_FS) += tape_proc.o tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y) obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o +obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 5f6fa4c67843..a6415377bc73 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -368,10 +368,9 @@ fs3270_alloc_view(void) { struct fs3270 *fp; - fp = (struct fs3270 *) kmalloc(sizeof(struct fs3270),GFP_KERNEL); + fp = kzalloc(sizeof(struct fs3270),GFP_KERNEL); if (!fp) return ERR_PTR(-ENOMEM); - memset(fp, 0, sizeof(struct fs3270)); fp->init = raw3270_request_alloc(0); if (IS_ERR(fp->init)) { kfree(fp); diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index a317a123daba..6badd8403409 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -50,14 +50,12 @@ kbd_alloc(void) { struct kbd_data *kbd; int i, len; - kbd = kmalloc(sizeof(struct kbd_data), GFP_KERNEL); + kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL); if (!kbd) goto out; - memset(kbd, 0, sizeof(struct kbd_data)); - kbd->key_maps = kmalloc(sizeof(key_maps), GFP_KERNEL); + kbd->key_maps = kzalloc(sizeof(key_maps), GFP_KERNEL); if (!key_maps) goto out_kbd; - memset(kbd->key_maps, 0, sizeof(key_maps)); for (i = 0; i < ARRAY_SIZE(key_maps); i++) { if (key_maps[i]) { kbd->key_maps[i] = @@ -68,10 +66,9 @@ kbd_alloc(void) { sizeof(u_short)*NR_KEYS); } } - kbd->func_table = kmalloc(sizeof(func_table), GFP_KERNEL); + kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL); if (!kbd->func_table) goto out_maps; - memset(kbd->func_table, 0, sizeof(func_table)); for (i = 0; i < ARRAY_SIZE(func_table); i++) { if (func_table[i]) { len = strlen(func_table[i]) + 1; @@ -82,10 +79,9 @@ kbd_alloc(void) { } } kbd->fn_handler = - kmalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL); + kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL); if (!kbd->fn_handler) goto out_func; - memset(kbd->fn_handler, 0, sizeof(fn_handler_fn *) * NR_FN_HANDLER); kbd->accent_table = kmalloc(sizeof(struct kbdiacr)*MAX_DIACR, GFP_KERNEL); if (!kbd->accent_table) diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 5fd3ad867386..fb7bc9e5eebc 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -257,14 +257,13 @@ mon_alloc_mem(void) int i,j; struct mon_private *monpriv; - monpriv = kmalloc(sizeof(struct mon_private), GFP_KERNEL); + monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); if (!monpriv) { P_ERROR("no memory for monpriv\n"); return NULL; } - memset(monpriv, 0, sizeof(struct mon_private)); for (i = 0; i < MON_MSGLIM; i++) { - monpriv->msg_array[i] = kmalloc(sizeof(struct mon_msg), + monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg), GFP_KERNEL); if (!monpriv->msg_array[i]) { P_ERROR("open, no memory for msg_array\n"); @@ -272,7 +271,6 @@ mon_alloc_mem(void) kfree(monpriv->msg_array[j]); return NULL; } - memset(monpriv->msg_array[i], 0, sizeof(struct mon_msg)); } return monpriv; } diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 1026f2bc3185..eecb2afad5c2 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -28,6 +28,7 @@ #include <linux/major.h> #include <linux/kdev_t.h> #include <linux/device.h> +#include <linux/mutex.h> struct class *class3270; @@ -59,7 +60,7 @@ struct raw3270 { #define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ /* Semaphore to protect global data of raw3270 (devices, views, etc). */ -static DECLARE_MUTEX(raw3270_sem); +static DEFINE_MUTEX(raw3270_mutex); /* List of 3270 devices. */ static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices); @@ -115,10 +116,9 @@ raw3270_request_alloc(size_t size) struct raw3270_request *rq; /* Allocate request structure */ - rq = kmalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); + rq = kzalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); if (!rq) return ERR_PTR(-ENOMEM); - memset(rq, 0, sizeof(struct raw3270_request)); /* alloc output buffer. */ if (size > 0) { @@ -816,7 +816,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) * number for it. Note: there is no device with minor 0, * see special case for fs3270.c:fs3270_open(). */ - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); /* Keep the list sorted. */ minor = RAW3270_FIRSTMINOR; rp->minor = -1; @@ -833,7 +833,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) rp->minor = minor; list_add_tail(&rp->list, &raw3270_devices); } - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); /* No free minor number? Then give up. */ if (rp->minor == -1) return -EUSERS; @@ -1004,7 +1004,7 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) if (minor <= 0) return -ENODEV; - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); rc = -ENODEV; list_for_each_entry(rp, &raw3270_devices, list) { if (rp->minor != minor) @@ -1025,7 +1025,7 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); break; } - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); return rc; } @@ -1039,7 +1039,7 @@ raw3270_find_view(struct raw3270_fn *fn, int minor) struct raw3270_view *view, *tmp; unsigned long flags; - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); view = ERR_PTR(-ENODEV); list_for_each_entry(rp, &raw3270_devices, list) { if (rp->minor != minor) @@ -1058,7 +1058,7 @@ raw3270_find_view(struct raw3270_fn *fn, int minor) spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); break; } - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); return view; } @@ -1105,7 +1105,7 @@ raw3270_delete_device(struct raw3270 *rp) struct ccw_device *cdev; /* Remove from device chain. */ - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); if (rp->clttydev) class_device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); @@ -1113,7 +1113,7 @@ raw3270_delete_device(struct raw3270 *rp) class_device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, rp->minor)); list_del_init(&rp->list); - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); /* Disconnect from ccw_device. */ cdev = rp->cdev; @@ -1209,13 +1209,13 @@ int raw3270_register_notifier(void (*notifier)(int, int)) if (!np) return -ENOMEM; np->notifier = notifier; - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); list_add_tail(&np->list, &raw3270_notifier); list_for_each_entry(rp, &raw3270_devices, list) { get_device(&rp->cdev->dev); notifier(rp->minor, 1); } - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); return 0; } @@ -1223,14 +1223,14 @@ void raw3270_unregister_notifier(void (*notifier)(int, int)) { struct raw3270_notifier *np; - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) if (np->notifier == notifier) { list_del(&np->list); kfree(np); break; } - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); } /* @@ -1257,10 +1257,10 @@ raw3270_set_online (struct ccw_device *cdev) goto failure; raw3270_create_attributes(rp); set_bit(RAW3270_FLAGS_READY, &rp->flags); - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) np->notifier(rp->minor, 1); - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); return 0; failure: @@ -1308,10 +1308,10 @@ raw3270_remove (struct ccw_device *cdev) } spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) np->notifier(rp->minor, 0); - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); /* Reset 3270 device. */ raw3270_reset_device(rp); @@ -1371,13 +1371,13 @@ raw3270_init(void) rc = ccw_driver_register(&raw3270_ccw_driver); if (rc == 0) { /* Create attributes for early (= console) device. */ - down(&raw3270_sem); + mutex_lock(&raw3270_mutex); class3270 = class_create(THIS_MODULE, "3270"); list_for_each_entry(rp, &raw3270_devices, list) { get_device(&rp->cdev->dev); raw3270_create_attributes(rp); } - up(&raw3270_sem); + mutex_unlock(&raw3270_mutex); } return rc; } diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 01d865d93791..cd51ace8b610 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -250,6 +250,7 @@ extern void tape_free_request(struct tape_request *); extern int tape_do_io(struct tape_device *, struct tape_request *); extern int tape_do_io_async(struct tape_device *, struct tape_request *); extern int tape_do_io_interruptible(struct tape_device *, struct tape_request *); +extern int tape_cancel_io(struct tape_device *, struct tape_request *); void tape_hotplug_event(struct tape_device *, int major, int action); static inline int diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 682039cac15b..d4f2da738078 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -2,8 +2,7 @@ * drivers/s390/char/tape_34xx.c * tape device discipline for 3480/3490 tapes. * - * S390 and zSeries version - * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) IBM Corp. 2001,2006 * Author(s): Carsten Otte <cotte@de.ibm.com> * Tuan Ngo-Anh <ngoanh@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -28,11 +27,6 @@ debug_info_t *TAPE_DBF_AREA = NULL; EXPORT_SYMBOL(TAPE_DBF_AREA); -enum tape_34xx_type { - tape_3480, - tape_3490, -}; - #define TAPE34XX_FMT_3480 0 #define TAPE34XX_FMT_3480_2_XF 1 #define TAPE34XX_FMT_3480_XF 2 diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c new file mode 100644 index 000000000000..c3915f60a3aa --- /dev/null +++ b/drivers/s390/char/tape_3590.c @@ -0,0 +1,1301 @@ +/* + * drivers/s390/char/tape_3590.c + * tape device discipline for 3590 tapes. + * + * Copyright (C) IBM Corp. 2001,2006 + * Author(s): Stefan Bader <shbader@de.ibm.com> + * Michael Holzheu <holzheu@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/bio.h> + +#define TAPE_DBF_AREA tape_3590_dbf + +#include "tape.h" +#include "tape_std.h" +#include "tape_3590.h" + +/* + * Pointer to debug area. + */ +debug_info_t *TAPE_DBF_AREA = NULL; +EXPORT_SYMBOL(TAPE_DBF_AREA); + +/******************************************************************* + * Error Recovery fuctions: + * - Read Opposite: implemented + * - Read Device (buffered) log: BRA + * - Read Library log: BRA + * - Swap Devices: BRA + * - Long Busy: BRA + * - Special Intercept: BRA + * - Read Alternate: implemented + *******************************************************************/ + +#define PRINTK_HEADER "TAPE_3590: " + +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", +}; + +/* + * 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); + } + 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); +} + +/* + * 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 Opposite Error Recovery Function: + * Used, when Read Forward does not work + */ +static void +tape_3590_read_opposite(struct tape_device *device, + struct tape_request *request) +{ + struct tape_3590_disc_data *data; + + /* + * We have allocated 4 ccws in tape_std_read, so we can now + * transform the request to a read backward, followed by a + * forward space block. + */ + request->op = TO_RBA; + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + data = device->discdata; + tape_ccw_cc_idal(request->cpaddr + 1, data->read_back_op, + device->char_data.idal_buf); + tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL); + tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL); + DBF_EVENT(6, "xrop ccwg\n"); +} + +/* + * 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 int +tape_3590_read_attmsg(struct tape_device *device) +{ + struct tape_request *request; + char *buf; + + request = tape_alloc_request(3, 4096); + if (IS_ERR(request)) + return PTR_ERR(request); + 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); + return tape_do_io_free(device, request); +} + +/* + * These functions are used to schedule follow-up actions from within an + * interrupt context (like unsolicited interrupts). + */ +static void +tape_3590_work_handler(void *data) +{ + struct { + struct tape_device *device; + enum tape_op op; + struct work_struct work; + } *p = data; + + switch (p->op) { + case TO_MSEN: + tape_3590_sense_medium(p->device); + break; + case TO_READ_ATTMSG: + tape_3590_read_attmsg(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 { + struct tape_device *device; + enum tape_op op; + struct work_struct work; + } *p; + + if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) + return -ENOMEM; + + INIT_WORK(&p->work, tape_3590_work_handler, p); + + p->device = tape_get_device_reference(device); + p->op = op; + + schedule_work(&p->work); + return 0; +} + +#ifdef CONFIG_S390_TAPE_BLOCK +/* + * Tape Block READ + */ +static struct tape_request * +tape_3590_bread(struct tape_device *device, struct request *req) +{ + struct tape_request *request; + struct ccw1 *ccw; + int count = 0, start_block, i; + unsigned off; + char *dst; + struct bio_vec *bv; + struct bio *bio; + + DBF_EVENT(6, "xBREDid:"); + start_block = req->sector >> TAPEBLOCK_HSEC_S2B; + DBF_EVENT(6, "start_block = %i\n", start_block); + + rq_for_each_bio(bio, req) { + bio_for_each_segment(bv, bio, i) { + count += bv->bv_len >> (TAPEBLOCK_HSEC_S2B + 9); + } + } + request = tape_alloc_request(2 + count + 1, 4); + if (IS_ERR(request)) + return request; + request->op = TO_BLOCK; + *(__u32 *) request->cpdata = start_block; + ccw = request->cpaddr; + ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte); + + /* + * We always setup a nop after the mode set ccw. This slot is + * used in tape_std_check_locate to insert a locate ccw if the + * current tape position doesn't match the start block to be read. + */ + ccw = tape_ccw_cc(ccw, NOP, 0, NULL); + + rq_for_each_bio(bio, req) { + bio_for_each_segment(bv, bio, i) { + dst = kmap(bv->bv_page) + bv->bv_offset; + for (off = 0; off < bv->bv_len; + off += TAPEBLOCK_HSEC_SIZE) { + ccw->flags = CCW_FLAG_CC; + ccw->cmd_code = READ_FORWARD; + ccw->count = TAPEBLOCK_HSEC_SIZE; + set_normalized_cda(ccw, (void *) __pa(dst)); + ccw++; + dst += TAPEBLOCK_HSEC_SIZE; + } + if (off > bv->bv_len) + BUG(); + } + } + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + DBF_EVENT(6, "xBREDccwg\n"); + return request; +} + +static void +tape_3590_free_bread(struct tape_request *request) +{ + struct ccw1 *ccw; + + /* Last ccw is a nop and doesn't need clear_normalized_cda */ + for (ccw = request->cpaddr; ccw->flags & CCW_FLAG_CC; ccw++) + if (ccw->cmd_code == READ_FORWARD) + clear_normalized_cda(ccw); + tape_free_request(request); +} + +/* + * check_locate is called just before the tape request is passed to + * the common io layer for execution. It has to check the current + * tape position and insert a locate ccw if it doesn't match the + * start block for the request. + */ +static void +tape_3590_check_locate(struct tape_device *device, struct tape_request *request) +{ + __u32 *start_block; + + start_block = (__u32 *) request->cpdata; + if (*start_block != device->blk_data.block_position) { + /* Add the start offset of the file to get the real block. */ + *start_block += device->bof; + tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); + } +} +#endif + +/* + * 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) +{ + struct tape_3590_med_sense *sense; + + 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); + break; + case TO_MSEN: + sense = (struct tape_3590_med_sense *) request->cpdata; + if (sense->masst == MSENSE_UNASSOCIATED) + tape_med_state_set(device, MS_UNLOADED); + if (sense->masst == MSENSE_ASSOCIATED_MOUNT) + tape_med_state_set(device, MS_LOADED); + 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: + break; + case TO_SIZE: + break; + } + return TAPE_IO_SUCCESS; +} + +/* + * This fuction is called, when error recovery was successfull + */ +static inline int +tape_3590_erp_succeded(struct tape_device *device, struct tape_request *request) +{ + DBF_EVENT(3, "Error Recovery successfull for %s\n", + tape_op_verbose[request->op]); + return tape_3590_done(device, request); +} + +/* + * This fuction is called, when error recovery was not successfull + */ +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.dstat == DEV_STAT_CHN_END) + /* Probably result of halt ssch */ + return TAPE_IO_PENDING; + else if (irb->scsw.dstat == 0x85) + /* Device Ready -> check medium state */ + tape_3590_schedule_work(device, TO_MSEN); + else if (irb->scsw.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); + PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); + tape_dump_sense(device, NULL, irb); + } + 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_succeded(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: + PRINT_ERR("Unknown BRA %x - This should not happen!\n", + sense->bra); + 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! + */ + PRINT_WARN("(%s): Swap Tape Device!\n", device->cdev->dev.bus_id); + 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) +{ + /* FIXME: how about WAITING for a minute ? */ + PRINT_WARN("(%s): Device is busy! Please wait a minute!\n", + device->cdev->dev.bus_id); + return tape_3590_erp_basic(device, request, irb, -EBUSY); +} + +/* + * 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); +} + +/* + * RDA: Read Alternate + */ +static int +tape_3590_erp_read_alternate(struct tape_device *device, + struct tape_request *request, struct irb *irb) +{ + struct tape_3590_disc_data *data; + + /* + * The issued Read Backward or Read Previous command is not + * supported by the device + * The recovery action should be to issue another command: + * Read Revious: if Read Backward is not supported + * Read Backward: if Read Previous is not supported + */ + data = device->discdata; + if (data->read_back_op == READ_PREVIOUS) { + DBF_EVENT(2, "(%08x): No support for READ_PREVIOUS command\n", + device->cdev_id); + data->read_back_op = READ_BACKWARD; + } else { + DBF_EVENT(2, "(%08x): No support for READ_BACKWARD command\n", + device->cdev_id); + data->read_back_op = READ_PREVIOUS; + } + tape_3590_read_opposite(device, request); + return tape_3590_erp_retry(device, request, irb); +} + +/* + * Error Recovery read opposite + */ +static int +tape_3590_erp_read_opposite(struct tape_device *device, + struct tape_request *request, struct irb *irb) +{ + switch (request->op) { + case TO_RFO: + /* + * We did read forward, but the data could not be read. + * We will read backward and then skip forward again. + */ + tape_3590_read_opposite(device, request); + return tape_3590_erp_retry(device, request, irb); + case TO_RBA: + /* We tried to read forward and backward, but hat no success */ + return tape_3590_erp_failed(device, request, irb, -EIO); + break; + default: + PRINT_WARN("read_opposite_recovery_called_with_op: %s\n", + tape_op_verbose[request->op]); + return tape_3590_erp_failed(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; + + sense = (struct tape_3590_sense *) irb->ecw; + /* Exception Message */ + switch (sense->fmt.f70.emc) { + case 0x02: + PRINT_WARN("(%s): Data degraded\n", device->cdev->dev.bus_id); + break; + case 0x03: + PRINT_WARN("(%s): Data degraded in partion %i\n", + device->cdev->dev.bus_id, sense->fmt.f70.mp); + break; + case 0x04: + PRINT_WARN("(%s): Medium degraded\n", device->cdev->dev.bus_id); + break; + case 0x05: + PRINT_WARN("(%s): Medium degraded in partition %i\n", + device->cdev->dev.bus_id, sense->fmt.f70.mp); + break; + case 0x06: + PRINT_WARN("(%s): Block 0 Error\n", device->cdev->dev.bus_id); + break; + case 0x07: + PRINT_WARN("(%s): Medium Exception 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f70.md); + break; + default: + PRINT_WARN("(%s): MIM ExMsg: 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f70.emc); + break; + } + /* Service Message */ + switch (sense->fmt.f70.smc) { + case 0x02: + PRINT_WARN("(%s): Reference Media maintenance procedure %i\n", + device->cdev->dev.bus_id, sense->fmt.f70.md); + break; + default: + PRINT_WARN("(%s): MIM ServiceMsg: 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f70.smc); + break; + } +} + +/* + * 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; + + sense = (struct tape_3590_sense *) irb->ecw; + /* Exception Message */ + switch (sense->fmt.f71.emc) { + case 0x01: + PRINT_WARN("(%s): Effect of failure is unknown\n", + device->cdev->dev.bus_id); + break; + case 0x02: + PRINT_WARN("(%s): CU Exception - no performance impact\n", + device->cdev->dev.bus_id); + break; + case 0x03: + PRINT_WARN("(%s): CU Exception on channel interface 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x04: + PRINT_WARN("(%s): CU Exception on device path 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x05: + PRINT_WARN("(%s): CU Exception on library path 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x06: + PRINT_WARN("(%s): CU Exception on node 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x07: + PRINT_WARN("(%s): CU Exception on partition 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + default: + PRINT_WARN("(%s): SIM ExMsg: 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.emc); + } + /* Service Message */ + switch (sense->fmt.f71.smc) { + case 0x01: + PRINT_WARN("(%s): Repair impact is unknown\n", + device->cdev->dev.bus_id); + break; + case 0x02: + PRINT_WARN("(%s): Repair will not impact cu performance\n", + device->cdev->dev.bus_id); + break; + case 0x03: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable node " + "0x%x on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable nodes " + "(0x%x-0x%x) on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x04: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable cannel path " + "0x%x on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable cannel paths " + "(0x%x-0x%x) on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x05: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable device path " + "0x%x on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable device paths " + "(0x%x-0x%x) on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x06: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable library path " + "0x%x on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable library paths " + "(0x%x-0x%x) on CU\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x07: + PRINT_WARN("(%s): Repair will disable access to CU\n", + device->cdev->dev.bus_id); + break; + default: + PRINT_WARN("(%s): SIM ServiceMsg: 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.smc); + } +} + +/* + * 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; + + sense = (struct tape_3590_sense *) irb->ecw; + /* Exception Message */ + switch (sense->fmt.f71.emc) { + case 0x01: + PRINT_WARN("(%s): Effect of failure is unknown\n", + device->cdev->dev.bus_id); + break; + case 0x02: + PRINT_WARN("(%s): DV Exception - no performance impact\n", + device->cdev->dev.bus_id); + break; + case 0x03: + PRINT_WARN("(%s): DV Exception on channel interface 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x04: + PRINT_WARN("(%s): DV Exception on loader 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x05: + PRINT_WARN("(%s): DV Exception on message display 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.md[0]); + break; + case 0x06: + PRINT_WARN("(%s): DV Exception in tape path\n", + device->cdev->dev.bus_id); + break; + case 0x07: + PRINT_WARN("(%s): DV Exception in drive\n", + device->cdev->dev.bus_id); + break; + default: + PRINT_WARN("(%s): DSIM ExMsg: 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.emc); + } + /* Service Message */ + switch (sense->fmt.f71.smc) { + case 0x01: + PRINT_WARN("(%s): Repair impact is unknown\n", + device->cdev->dev.bus_id); + break; + case 0x02: + PRINT_WARN("(%s): Repair will not impact device performance\n", + device->cdev->dev.bus_id); + break; + case 0x03: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable channel path " + "0x%x on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable channel path " + "(0x%x-0x%x) on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x04: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable interface 0x%x " + "on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable interfaces " + "(0x%x-0x%x) on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x05: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable loader 0x%x " + "on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable loader " + "(0x%x-0x%x) on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x07: + PRINT_WARN("(%s): Repair will disable access to DV\n", + device->cdev->dev.bus_id); + break; + case 0x08: + if (sense->fmt.f71.mdf == 0) + PRINT_WARN("(%s): Repair will disable message " + "display 0x%x on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1]); + else + PRINT_WARN("(%s): Repair will disable message " + "displays (0x%x-0x%x) on DV\n", + device->cdev->dev.bus_id, + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + break; + case 0x09: + PRINT_WARN("(%s): Clean DV\n", device->cdev->dev.bus_id); + break; + default: + PRINT_WARN("(%s): DSIM ServiceMsg: 0x%02x\n", + device->cdev->dev.bus_id, sense->fmt.f71.smc); + } +} + +/* + * 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) + PRINT_WARN("(%s): %s\n", device->cdev->dev.bus_id, + tape_3590_msg[sense->mc]); + else { + PRINT_WARN("(%s): Message Code 0x%x\n", + device->cdev->dev.bus_id, sense->mc); + } + return; + } + if (sense->mc == 0xf0) { + /* Standard Media Information Message */ + PRINT_WARN("(%s): MIM SEV=%i, MC=%02x, ES=%x/%x, " + "RC=%02x-%04x-%02x\n", device->cdev->dev.bus_id, + 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 */ + PRINT_WARN("(%s): IOSIM SEV=%i, DEVTYPE=3590/%02x, " + "MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n", + device->cdev->dev.bus_id, 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 */ + PRINT_WARN("(%s): DEVSIM SEV=%i, DEVTYPE=3590/%02x, " + "MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n", + device->cdev->dev.bus_id, 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; + } + PRINT_WARN("(%s): Device Message(%x)\n", + device->cdev->dev.bus_id, sense->mc); +} + +/* + * 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; + int rc; + +#ifdef CONFIG_S390_TAPE_BLOCK + if (request->op == TO_BLOCK) { + /* + * Recovery for block device requests. Set the block_position + * to something invalid and retry. + */ + device->blk_data.block_position = -1; + if (request->retries-- <= 0) + return tape_3590_erp_failed(device, request, irb, -EIO); + else + return tape_3590_erp_retry(device, request, irb); + } +#endif + + sense = (struct tape_3590_sense *) irb->ecw; + + /* + * 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 + */ + rc = -EIO; + switch (sense->rc_rqc) { + + case 0x1110: + tape_3590_print_era_msg(device, irb); + return tape_3590_erp_read_buf_log(device, request, irb); + + case 0x2011: + tape_3590_print_era_msg(device, irb); + return tape_3590_erp_read_alternate(device, request, irb); + + case 0x2230: + case 0x2231: + tape_3590_print_era_msg(device, irb); + return tape_3590_erp_special_interrupt(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); + return tape_3590_erp_basic(device, request, irb, 0); + + case 0x4010: + /* + * print additional msg since default msg + * "device intervention" is not very meaningfull + */ + PRINT_WARN("(%s): Tape operation when medium not loaded\n", + device->cdev->dev.bus_id); + tape_med_state_set(device, MS_UNLOADED); + return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM); + case 0x4012: /* Device Long Busy */ + tape_3590_print_era_msg(device, irb); + 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); + } + if (sense->rac == 0x26) { + /* Read Opposite */ + tape_3590_print_era_msg(device, irb); + return tape_3590_erp_read_opposite(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); + return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM); + + case 0x6020: + PRINT_WARN("(%s): Cartridge of wrong type ?\n", + device->cdev->dev.bus_id); + return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE); + + case 0x8011: + PRINT_WARN("(%s): Another host has reserved the tape device\n", + device->cdev->dev.bus_id); + return tape_3590_erp_basic(device, request, irb, -EPERM); + case 0x8013: + PRINT_WARN("(%s): Another host has priviliged access to the " + "tape device\n", device->cdev->dev.bus_id); + PRINT_WARN("(%s): To solve the problem unload the current " + "cartridge!\n", device->cdev->dev.bus_id); + 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.dstat & DEV_STAT_UNIT_EXCEP) && + (irb->scsw.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.dstat & DEV_STAT_UNIT_CHECK) + return tape_3590_unit_check(device, request, irb); + + if (irb->scsw.dstat & DEV_STAT_DEV_END) { + if (irb->scsw.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.dstat & DEV_STAT_CHN_END) { + DBF_EVENT(2, "cannel end\n"); + return TAPE_IO_PENDING; + } + + if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + DBF_EVENT(2, "Unit Attention when busy..\n"); + return TAPE_IO_PENDING; + } + + DBF_EVENT(6, "xunknownirq\n"); + PRINT_ERR("Unexpected interrupt.\n"); + PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]); + tape_dump_sense(device, request, irb); + return TAPE_IO_STOP; +} + +/* + * Setup device function + */ +static int +tape_3590_setup_device(struct tape_device *device) +{ + int rc; + struct tape_3590_disc_data *data; + + DBF_EVENT(6, "3590 device setup\n"); + data = kmalloc(sizeof(struct tape_3590_disc_data), + GFP_KERNEL | GFP_DMA); + if (data == NULL) + return -ENOMEM; + data->read_back_op = READ_PREVIOUS; + device->discdata = data; + + if ((rc = tape_std_assign(device)) == 0) { + /* Try to find out if medium is loaded */ + if ((rc = tape_3590_sense_medium(device)) != 0) + DBF_LH(3, "3590 medium sense returned %d\n", rc); + } + + return rc; +} + +/* + * Cleanup device function + */ +static void +tape_3590_cleanup_device(struct tape_device *device) +{ + 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, +#ifdef CONFIG_S390_TAPE_BLOCK + .bread = tape_3590_bread, + .free_bread = tape_3590_free_bread, + .check_locate = tape_3590_check_locate, +#endif + .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}, + { /* end of list */ } +}; + +static int +tape_3590_online(struct ccw_device *cdev) +{ + return tape_generic_online(cdev->dev.driver_data, + &tape_discipline_3590); +} + +static int +tape_3590_offline(struct ccw_device *cdev) +{ + return tape_generic_offline(cdev->dev.driver_data); +} + +static struct ccw_driver tape_3590_driver = { + .name = "tape_3590", + .owner = THIS_MODULE, + .ids = tape_3590_ids, + .probe = tape_generic_probe, + .remove = tape_generic_remove, + .set_offline = tape_3590_offline, + .set_online = tape_3590_online, +}; + +/* + * 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"); + /* Register driver for 3590 tapes. */ + rc = ccw_driver_register(&tape_3590_driver); + if (rc) + 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); + + 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 new file mode 100644 index 000000000000..cf274b9445a6 --- /dev/null +++ b/drivers/s390/char/tape_3590.h @@ -0,0 +1,124 @@ +/* + * drivers/s390/char/tape_3590.h + * tape device discipline for 3590 tapes. + * + * Copyright (C) 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 TAPE_3590_MAX_MSG 0xb0 + +/* Datatypes */ + +struct tape_3590_disc_data { + unsigned char modeset_byte; + int 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 pad[127]; +} __attribute__ ((packed)); + +#endif /* _TAPE_3590_H */ diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index b3569c82bb16..a5c68e60fcf4 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -44,11 +44,10 @@ struct tape_class_device *register_tape_dev( int rc; char * s; - tcd = kmalloc(sizeof(struct tape_class_device), GFP_KERNEL); + tcd = kzalloc(sizeof(struct tape_class_device), GFP_KERNEL); if (!tcd) return ERR_PTR(-ENOMEM); - memset(tcd, 0, sizeof(struct tape_class_device)); strncpy(tcd->device_name, device_name, TAPECLASS_NAME_LEN); for (s = strchr(tcd->device_name, '/'); s; s = strchr(s, '/')) *s = '!'; diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 4ea438c749c9..389ee2c0f443 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -453,16 +453,14 @@ tape_alloc_device(void) { struct tape_device *device; - device = (struct tape_device *) - kmalloc(sizeof(struct tape_device), GFP_KERNEL); + device = kzalloc(sizeof(struct tape_device), GFP_KERNEL); if (device == NULL) { DBF_EXCEPTION(2, "ti:no mem\n"); PRINT_INFO ("can't allocate memory for " "tape info structure\n"); return ERR_PTR(-ENOMEM); } - memset(device, 0, sizeof(struct tape_device)); - device->modeset_byte = (char *) kmalloc(1, GFP_KERNEL | GFP_DMA); + device->modeset_byte = kmalloc(1, GFP_KERNEL | GFP_DMA); if (device->modeset_byte == NULL) { DBF_EXCEPTION(2, "ti:no mem\n"); PRINT_INFO("can't allocate memory for modeset byte\n"); @@ -659,34 +657,30 @@ tape_alloc_request(int cplength, int datasize) DBF_LH(6, "tape_alloc_request(%d, %d)\n", cplength, datasize); - request = (struct tape_request *) kmalloc(sizeof(struct tape_request), - GFP_KERNEL); + request = kzalloc(sizeof(struct tape_request), GFP_KERNEL); if (request == NULL) { DBF_EXCEPTION(1, "cqra nomem\n"); return ERR_PTR(-ENOMEM); } - memset(request, 0, sizeof(struct tape_request)); /* allocate channel program */ if (cplength > 0) { - request->cpaddr = kmalloc(cplength*sizeof(struct ccw1), + request->cpaddr = kcalloc(cplength, sizeof(struct ccw1), GFP_ATOMIC | GFP_DMA); if (request->cpaddr == NULL) { DBF_EXCEPTION(1, "cqra nomem\n"); kfree(request); return ERR_PTR(-ENOMEM); } - memset(request->cpaddr, 0, cplength*sizeof(struct ccw1)); } /* alloc small kernel buffer */ if (datasize > 0) { - request->cpdata = kmalloc(datasize, GFP_KERNEL | GFP_DMA); + request->cpdata = kzalloc(datasize, GFP_KERNEL | GFP_DMA); if (request->cpdata == NULL) { DBF_EXCEPTION(1, "cqra nomem\n"); kfree(request->cpaddr); kfree(request); return ERR_PTR(-ENOMEM); } - memset(request->cpdata, 0, datasize); } DBF_LH(6, "New request %p(%p/%p)\n", request, request->cpaddr, request->cpdata); @@ -761,6 +755,13 @@ __tape_start_next_request(struct tape_device *device) */ if (request->status == TAPE_REQUEST_IN_IO) return; + /* + * Request has already been stopped. We have to wait until + * the request is removed from the queue in the interrupt + * handling. + */ + if (request->status == TAPE_REQUEST_DONE) + return; /* * We wanted to cancel the request but the common I/O layer @@ -1015,7 +1016,7 @@ tape_do_io_interruptible(struct tape_device *device, wq, (request->callback == NULL) ); - } while (rc != -ERESTARTSYS); + } while (rc == -ERESTARTSYS); DBF_EVENT(3, "IO stopped on %08x\n", device->cdev_id); rc = -ERESTARTSYS; @@ -1024,6 +1025,20 @@ tape_do_io_interruptible(struct tape_device *device, } /* + * Stop running ccw. + */ +int +tape_cancel_io(struct tape_device *device, struct tape_request *request) +{ + int rc; + + spin_lock_irq(get_ccwdev_lock(device->cdev)); + rc = __tape_cancel_io(device, request); + spin_unlock_irq(get_ccwdev_lock(device->cdev)); + return rc; +} + +/* * Tape interrupt routine, called from the ccw_device layer */ static void @@ -1064,15 +1079,16 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* * If the condition code is not zero and the start function bit is * still set, this is an deferred error and the last start I/O did - * not succeed. Restart the request now. + * not succeed. At this point the condition that caused the deferred + * error might still apply. So we just schedule the request to be + * started later. */ - if (irb->scsw.cc != 0 && (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) { - PRINT_WARN("(%s): deferred cc=%i. restaring\n", - cdev->dev.bus_id, - irb->scsw.cc); - rc = __tape_start_io(device, request); - if (rc) - __tape_end_request(device, request, rc); + if (irb->scsw.cc != 0 && (irb->scsw.fctl & SCSW_FCTL_START_FUNC) && + (request->status == TAPE_REQUEST_IN_IO)) { + DBF_EVENT(3,"(%08x): deferred cc=%i, fctl=%i. restarting\n", + device->cdev_id, irb->scsw.cc, irb->scsw.fctl); + request->status = TAPE_REQUEST_QUEUED; + schedule_delayed_work(&device->tape_dnr, HZ); return; } @@ -1286,4 +1302,5 @@ EXPORT_SYMBOL(tape_dump_sense_dbf); EXPORT_SYMBOL(tape_do_io); EXPORT_SYMBOL(tape_do_io_async); EXPORT_SYMBOL(tape_do_io_interruptible); +EXPORT_SYMBOL(tape_cancel_io); EXPORT_SYMBOL(tape_mtop); diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 2f9fe30989a7..99cf881f41db 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -37,20 +37,19 @@ tape_std_assign_timeout(unsigned long data) { struct tape_request * request; struct tape_device * device; + int rc; request = (struct tape_request *) data; if ((device = request->device) == NULL) BUG(); - spin_lock_irq(get_ccwdev_lock(device->cdev)); - if (request->callback != NULL) { - DBF_EVENT(3, "%08x: Assignment timeout. Device busy.\n", + DBF_EVENT(3, "%08x: Assignment timeout. Device busy.\n", device->cdev_id); - PRINT_ERR("%s: Assignment timeout. Device busy.\n", - device->cdev->dev.bus_id); - ccw_device_clear(device->cdev, (long) request); - } - spin_unlock_irq(get_ccwdev_lock(device->cdev)); + rc = tape_cancel_io(device, request); + if(rc) + PRINT_ERR("(%s): Assign timeout: Cancel failed with rc = %i\n", + device->cdev->dev.bus_id, rc); + } int diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index 3ab6aafb7343..2d311798edf4 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -1,9 +1,8 @@ /* - * drivers/s390/char/tape_34xx.h + * drivers/s390/char/tape_std.h * standard tape device functions for ibm tapes. * - * S390 and zSeries version - * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) IBM Corp. 2001,2006 * Author(s): Carsten Otte <cotte@de.ibm.com> * Tuan Ngo-Anh <ngoanh@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -149,4 +148,11 @@ void tape_std_error_recovery_do_retry(struct tape_device *); void tape_std_error_recovery_read_opposite(struct tape_device *); void tape_std_error_recovery_HWBUG(struct tape_device *, int condno); +/* S390 tape types */ +enum s390_tape_type { + tape_3480, + tape_3490, + tape_3590, +}; + #endif // _TAPE_STD_H diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 4b9069370388..9a141776873f 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -691,10 +691,9 @@ tty3270_alloc_view(void) struct tty3270 *tp; int pages; - tp = kmalloc(sizeof(struct tty3270),GFP_KERNEL); + tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL); if (!tp) goto out_err; - memset(tp, 0, sizeof(struct tty3270)); tp->freemem_pages = kmalloc(sizeof(void *) * TTY3270_STRING_PAGES, GFP_KERNEL); if (!tp->freemem_pages) @@ -767,16 +766,14 @@ tty3270_alloc_screen(struct tty3270 *tp) int lines; size = sizeof(struct tty3270_line) * (tp->view.rows - 2); - tp->screen = kmalloc(size, GFP_KERNEL); + tp->screen = kzalloc(size, GFP_KERNEL); if (!tp->screen) goto out_err; - memset(tp->screen, 0, size); for (lines = 0; lines < tp->view.rows - 2; lines++) { size = sizeof(struct tty3270_cell) * tp->view.cols; - tp->screen[lines].cells = kmalloc(size, GFP_KERNEL); + tp->screen[lines].cells = kzalloc(size, GFP_KERNEL); if (!tp->screen[lines].cells) goto out_screen; - memset(tp->screen[lines].cells, 0, size); } return 0; out_screen: diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index b2d75de144c6..c625b69ebd19 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -759,9 +759,8 @@ vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) { struct device *dev; int ret; - dev = kmalloc(sizeof(struct device), GFP_KERNEL); + dev = kzalloc(sizeof(struct device), GFP_KERNEL); if (dev) { - memset(dev, 0, sizeof(struct device)); snprintf(dev->bus_id, BUS_ID_SIZE, "%s", priv->internal_name); dev->bus = &iucv_bus; |