diff options
Diffstat (limited to 'drivers/s390/char/tape_core.c')
-rw-r--r-- | drivers/s390/char/tape_core.c | 69 |
1 files changed, 58 insertions, 11 deletions
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index c6c2e918b99..e2a8a1a04ba 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -3,7 +3,7 @@ * basic function of the tape device driver * * S390 and zSeries version - * Copyright (C) 2001,2005 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 2001,2006 * Author(s): Carsten Otte <cotte@de.ibm.com> * Michael Holzheu <holzheu@de.ibm.com> * Tuan Ngo-Anh <ngoanh@de.ibm.com> @@ -26,9 +26,11 @@ #include "tape_std.h" #define PRINTK_HEADER "TAPE_CORE: " +#define LONG_BUSY_TIMEOUT 180 /* seconds */ static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *); static void tape_delayed_next_request(struct work_struct *); +static void tape_long_busy_timeout(unsigned long data); /* * One list to contain all tape devices of all disciplines, so @@ -69,10 +71,12 @@ 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_UNASSIGN] = "UAS", [TO_CRYPT_ON] = "CON", + [TO_CRYPT_OFF] = "COF", [TO_KEKL_SET] = "KLS", + [TO_KEKL_QUERY] = "KLQ", }; -static inline int +static int busid_to_int(char *bus_id) { int dec; @@ -252,7 +256,7 @@ tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate) /* * Stop running ccw. Has to be called with the device lock held. */ -static inline int +static int __tape_cancel_io(struct tape_device *device, struct tape_request *request) { int retries; @@ -346,6 +350,9 @@ tape_generic_online(struct tape_device *device, return -EINVAL; } + init_timer(&device->lb_timeout); + device->lb_timeout.function = tape_long_busy_timeout; + /* Let the discipline have a go at the device. */ device->discipline = discipline; if (!try_module_get(discipline->owner)) { @@ -385,7 +392,7 @@ out: return rc; } -static inline void +static void tape_cleanup_device(struct tape_device *device) { tapeblock_cleanup_device(device); @@ -563,7 +570,7 @@ tape_generic_probe(struct ccw_device *cdev) return ret; } -static inline void +static void __tape_discard_requests(struct tape_device *device) { struct tape_request * request; @@ -703,7 +710,7 @@ tape_free_request (struct tape_request * request) kfree(request); } -static inline int +static int __tape_start_io(struct tape_device *device, struct tape_request *request) { int rc; @@ -733,7 +740,7 @@ __tape_start_io(struct tape_device *device, struct tape_request *request) return rc; } -static inline void +static void __tape_start_next_request(struct tape_device *device) { struct list_head *l, *n; @@ -801,7 +808,23 @@ tape_delayed_next_request(struct work_struct *work) spin_unlock_irq(get_ccwdev_lock(device->cdev)); } -static inline void +static void tape_long_busy_timeout(unsigned long data) +{ + struct tape_request *request; + struct tape_device *device; + + device = (struct tape_device *) data; + spin_lock_irq(get_ccwdev_lock(device->cdev)); + request = list_entry(device->req_queue.next, struct tape_request, list); + if (request->status != TAPE_REQUEST_LONG_BUSY) + BUG(); + DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id); + __tape_start_next_request(device); + device->lb_timeout.data = (unsigned long) tape_put_device(device); + spin_unlock_irq(get_ccwdev_lock(device->cdev)); +} + +static void __tape_end_request( struct tape_device * device, struct tape_request * request, @@ -878,7 +901,7 @@ tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request, * and starts it if the tape is idle. Has to be called with * the device lock held. */ -static inline int +static int __tape_start_request(struct tape_device *device, struct tape_request *request) { int rc; @@ -1094,7 +1117,22 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* May be an unsolicited irq */ if(request != NULL) request->rescnt = irb->scsw.count; - + else if ((irb->scsw.dstat == 0x85 || irb->scsw.dstat == 0x80) && + !list_empty(&device->req_queue)) { + /* Not Ready to Ready after long busy ? */ + struct tape_request *req; + req = list_entry(device->req_queue.next, + struct tape_request, list); + if (req->status == TAPE_REQUEST_LONG_BUSY) { + DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id); + if (del_timer(&device->lb_timeout)) { + device->lb_timeout.data = (unsigned long) + tape_put_device(device); + __tape_start_next_request(device); + } + return; + } + } if (irb->scsw.dstat != 0x0c) { /* Set the 'ONLINE' flag depending on sense byte 1 */ if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE) @@ -1142,6 +1180,15 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) break; case TAPE_IO_PENDING: break; + case TAPE_IO_LONG_BUSY: + device->lb_timeout.data = + (unsigned long)tape_get_device_reference(device); + device->lb_timeout.expires = jiffies + + LONG_BUSY_TIMEOUT * HZ; + DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id); + add_timer(&device->lb_timeout); + request->status = TAPE_REQUEST_LONG_BUSY; + break; case TAPE_IO_RETRY: rc = __tape_start_io(device, request); if (rc) |