summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/char/tape.h1
-rw-r--r--drivers/s390/char/tape_core.c32
-rw-r--r--drivers/s390/char/tape_std.c15
3 files changed, 35 insertions, 13 deletions
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_core.c b/drivers/s390/char/tape_core.c
index 5d17149a6529..c6fab5dbdd44 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -761,6 +761,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
@@ -1024,6 +1031,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
@@ -1068,12 +1089,12 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
* 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);
+ 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_work(&device->tape_dnr);
+ schedule_delayed_work(&device->tape_dnr, HZ);
return;
}
@@ -1287,4 +1308,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