Commit ff3d0776 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: tape driver.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

s390 tape driver changes:
 - Prevent offline while device is in use.
 - Do not use bus_id string in debug feature messages.
 - Check for IS_ERR(irb) error conditions in interrupt handler.
 - Fix removing tape discipline modules.
parent 0dc80d7c
...@@ -198,6 +198,7 @@ struct tape_device { ...@@ -198,6 +198,7 @@ struct tape_device {
/* entry in tape_device_list */ /* entry in tape_device_list */
struct list_head node; struct list_head node;
int cdev_id;
struct ccw_device * cdev; struct ccw_device * cdev;
struct tape_class_device * nt; struct tape_class_device * nt;
struct tape_class_device * rt; struct tape_class_device * rt;
...@@ -263,8 +264,8 @@ extern int tape_release(struct tape_device *); ...@@ -263,8 +264,8 @@ extern int tape_release(struct tape_device *);
extern int tape_mtop(struct tape_device *, int, int); extern int tape_mtop(struct tape_device *, int, int);
extern void tape_state_set(struct tape_device *, enum tape_state); extern void tape_state_set(struct tape_device *, enum tape_state);
extern int tape_enable_device(struct tape_device *, struct tape_discipline *); extern int tape_generic_online(struct tape_device *, struct tape_discipline *);
extern void tape_disable_device(struct tape_device *device); extern int tape_generic_offline(struct tape_device *device);
/* Externals from tape_devmap.c */ /* Externals from tape_devmap.c */
extern int tape_generic_probe(struct ccw_device *); extern int tape_generic_probe(struct ccw_device *);
......
...@@ -202,8 +202,7 @@ tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) ...@@ -202,8 +202,7 @@ tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb)
tape_34xx_delete_sbid_from(device, 0); tape_34xx_delete_sbid_from(device, 0);
tape_34xx_schedule_work(device, TO_MSEN); tape_34xx_schedule_work(device, TO_MSEN);
} else { } else {
DBF_EVENT(3, "unsol.irq! dev end: %s\n", DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
device->cdev->dev.bus_id);
PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); PRINT_WARN("Unsolicited IRQ (Device End) caught.\n");
tape_dump_sense(device, NULL, irb); tape_dump_sense(device, NULL, irb);
} }
...@@ -1314,17 +1313,18 @@ static struct ccw_device_id tape_34xx_ids[] = { ...@@ -1314,17 +1313,18 @@ static struct ccw_device_id tape_34xx_ids[] = {
}; };
static int static int
tape_34xx_enable(struct ccw_device *cdev) tape_34xx_online(struct ccw_device *cdev)
{ {
return tape_enable_device(cdev->dev.driver_data, return tape_generic_online(
&tape_discipline_34xx); cdev->dev.driver_data,
&tape_discipline_34xx
);
} }
static int static int
tape_34xx_disable(struct ccw_device *cdev) tape_34xx_offline(struct ccw_device *cdev)
{ {
tape_disable_device(cdev->dev.driver_data); return tape_generic_offline(cdev->dev.driver_data);
return 0;
} }
static struct ccw_driver tape_34xx_driver = { static struct ccw_driver tape_34xx_driver = {
...@@ -1333,8 +1333,8 @@ static struct ccw_driver tape_34xx_driver = { ...@@ -1333,8 +1333,8 @@ static struct ccw_driver tape_34xx_driver = {
.ids = tape_34xx_ids, .ids = tape_34xx_ids,
.probe = tape_generic_probe, .probe = tape_generic_probe,
.remove = tape_generic_remove, .remove = tape_generic_remove,
.set_online = tape_34xx_enable, .set_online = tape_34xx_online,
.set_offline = tape_34xx_disable, .set_offline = tape_34xx_offline,
}; };
static int static int
...@@ -1342,7 +1342,7 @@ tape_34xx_init (void) ...@@ -1342,7 +1342,7 @@ tape_34xx_init (void)
{ {
int rc; int rc;
DBF_EVENT(3, "34xx init: $Revision: 1.18 $\n"); DBF_EVENT(3, "34xx init: $Revision: 1.19 $\n");
/* Register driver for 3480/3490 tapes. */ /* Register driver for 3480/3490 tapes. */
rc = ccw_driver_register(&tape_34xx_driver); rc = ccw_driver_register(&tape_34xx_driver);
if (rc) if (rc)
...@@ -1361,7 +1361,7 @@ tape_34xx_exit(void) ...@@ -1361,7 +1361,7 @@ tape_34xx_exit(void)
MODULE_DEVICE_TABLE(ccw, tape_34xx_ids); MODULE_DEVICE_TABLE(ccw, tape_34xx_ids);
MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH"); MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape " MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape "
"device driver ($Revision: 1.18 $)"); "device driver ($Revision: 1.19 $)");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(tape_34xx_init); module_init(tape_34xx_init);
......
...@@ -274,12 +274,19 @@ tapeblock_cleanup_device(struct tape_device *device) ...@@ -274,12 +274,19 @@ tapeblock_cleanup_device(struct tape_device *device)
flush_scheduled_work(); flush_scheduled_work();
device->blk_data.requeue_task.data = tape_put_device(device); device->blk_data.requeue_task.data = tape_put_device(device);
if (!device->blk_data.disk) {
PRINT_ERR("(%s): No gendisk to clean up!\n",
device->cdev->dev.bus_id);
goto cleanup_queue;
}
del_gendisk(device->blk_data.disk); del_gendisk(device->blk_data.disk);
device->blk_data.disk->private_data = device->blk_data.disk->private_data =
tape_put_device(device->blk_data.disk->private_data); tape_put_device(device->blk_data.disk->private_data);
put_disk(device->blk_data.disk); put_disk(device->blk_data.disk);
device->blk_data.disk = NULL; device->blk_data.disk = NULL;
cleanup_queue:
device->blk_data.request_queue->queuedata = tape_put_device(device); device->blk_data.request_queue->queuedata = tape_put_device(device);
blk_cleanup_queue(device->blk_data.request_queue); blk_cleanup_queue(device->blk_data.request_queue);
......
...@@ -69,6 +69,34 @@ const char *tape_op_verbose[TO_SIZE] = ...@@ -69,6 +69,34 @@ const char *tape_op_verbose[TO_SIZE] =
[TO_UNASSIGN] = "UAS" [TO_UNASSIGN] = "UAS"
}; };
static inline int
busid_to_int(char *bus_id)
{
int dec;
int d;
char * s;
for(s = bus_id, d = 0; *s != '\0' && *s != '.'; s++)
d = (d * 10) + (*s - '0');
dec = d;
for(s++, d = 0; *s != '\0' && *s != '.'; s++)
d = (d * 10) + (*s - '0');
dec = (dec << 8) + d;
for(s++; *s != '\0'; s++) {
if (*s >= '0' && *s <= '9') {
d = *s - '0';
} else if (*s >= 'a' && *s <= 'f') {
d = *s - 'a' + 10;
} else {
d = *s - 'A' + 10;
}
dec = (dec << 4) + d;
}
return dec;
}
/* /*
* Some channel attached tape specific attributes. * Some channel attached tape specific attributes.
* *
...@@ -296,10 +324,15 @@ tape_remove_minor(struct tape_device *device) ...@@ -296,10 +324,15 @@ tape_remove_minor(struct tape_device *device)
} }
/* /*
* Enable tape device * Set a device online.
*
* This function is called by the common I/O layer to move a device from the
* detected but offline into the online state.
* If we return an error (RC < 0) the device remains in the offline state. This
* can happen if the device is assigned somewhere else, for example.
*/ */
int int
tape_enable_device(struct tape_device *device, tape_generic_online(struct tape_device *device,
struct tape_discipline *discipline) struct tape_discipline *discipline)
{ {
int rc; int rc;
...@@ -328,6 +361,9 @@ tape_enable_device(struct tape_device *device, ...@@ -328,6 +361,9 @@ tape_enable_device(struct tape_device *device,
goto out_char; goto out_char;
tape_state_set(device, TS_UNUSED); tape_state_set(device, TS_UNUSED);
DBF_LH(3, "(%08x): Drive set online\n", device->cdev_id);
return 0; return 0;
out_char: out_char:
...@@ -342,37 +378,49 @@ tape_enable_device(struct tape_device *device, ...@@ -342,37 +378,49 @@ tape_enable_device(struct tape_device *device,
} }
/* /*
* Disable tape device. Check if there is a running request and * Set device offline.
* terminate it. Post all queued requests with -EIO. *
* Called by the common I/O layer if the drive should set offline on user
* request. We may prevent this by returning an error.
* Manual offline is only allowed while the drive is not in use.
*/ */
void int
tape_disable_device(struct tape_device *device) tape_generic_offline(struct tape_device *device)
{ {
struct list_head *l, *n; if (!device) {
struct tape_request *request; PRINT_ERR("tape_generic_offline: no such device\n");
return -ENODEV;
}
DBF_LH(3, "(%08x): tape_generic_offline(%p)\n",
device->cdev_id, device);
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
/* Post remaining requests with -EIO */ switch (device->tape_state) {
list_for_each_safe(l, n, &device->req_queue) { case TS_INIT:
request = list_entry(l, struct tape_request, list); case TS_NOT_OPER:
if (request->status == TAPE_REQUEST_IN_IO) break;
__tape_halt_io(device, request); case TS_UNUSED:
list_del(&request->list); tapeblock_cleanup_device(device);
/* Decrease ref_count for removed request. */ tapechar_cleanup_device(device);
request->device = tape_put_device(device); device->discipline->cleanup_device(device);
request->rc = -EIO; tape_remove_minor(device);
if (request->callback != NULL) default:
request->callback(request, request->callback_data); DBF_EVENT(3, "(%08x): Set offline failed "
"- drive in use.\n",
device->cdev_id);
PRINT_WARN("(%s): Set offline failed "
"- drive in use.\n",
device->cdev->dev.bus_id);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return -EBUSY;
} }
spin_unlock_irq(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
tapeblock_cleanup_device(device);
tapechar_cleanup_device(device);
device->discipline->cleanup_device(device);
tape_remove_minor(device);
tape_med_state_set(device, MS_UNKNOWN); tape_med_state_set(device, MS_UNKNOWN);
device->tape_state = TS_INIT;
DBF_LH(3, "(%08x): Drive set offline.\n", device->cdev_id);
return 0;
} }
/* /*
...@@ -479,14 +527,14 @@ int ...@@ -479,14 +527,14 @@ int
tape_generic_probe(struct ccw_device *cdev) tape_generic_probe(struct ccw_device *cdev)
{ {
struct tape_device *device; struct tape_device *device;
char *bus_id = cdev->dev.bus_id;
device = tape_alloc_device(); device = tape_alloc_device();
if (IS_ERR(device)) if (IS_ERR(device))
return -ENODEV; return -ENODEV;
PRINT_INFO("tape device %s found\n", bus_id); PRINT_INFO("tape device %s found\n", cdev->dev.bus_id);
cdev->dev.driver_data = device; cdev->dev.driver_data = device;
device->cdev = cdev; device->cdev = cdev;
device->cdev_id = busid_to_int(cdev->dev.bus_id);
cdev->handler = __tape_do_irq; cdev->handler = __tape_do_irq;
ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
...@@ -497,15 +545,58 @@ tape_generic_probe(struct ccw_device *cdev) ...@@ -497,15 +545,58 @@ tape_generic_probe(struct ccw_device *cdev)
/* /*
* Driverfs tape remove function. * Driverfs tape remove function.
*
* This function is called whenever the common I/O layer detects the device
* gone. This can happen at any time and we cannot refuse.
*/ */
void void
tape_generic_remove(struct ccw_device *cdev) tape_generic_remove(struct ccw_device *cdev)
{ {
ccw_device_set_offline(cdev); struct tape_device * device;
struct tape_request * request;
struct list_head * l, *n;
device = cdev->dev.driver_data;
DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev);
/*
* No more requests may be processed. So just post them as i/o errors.
*/
spin_lock_irq(get_ccwdev_lock(device->cdev));
list_for_each_safe(l, n, &device->req_queue) {
request = list_entry(l, struct tape_request, list);
if (request->status == TAPE_REQUEST_IN_IO)
request->status = TAPE_REQUEST_DONE;
list_del(&request->list);
/* Decrease ref_count for removed request. */
request->device = tape_put_device(device);
request->rc = -EIO;
if (request->callback != NULL)
request->callback(request, request->callback_data);
}
if (device->tape_state != TS_UNUSED && device->tape_state != TS_INIT) {
DBF_EVENT(3, "(%08x): Drive in use vanished!\n",
device->cdev_id);
PRINT_WARN("(%s): Drive in use vanished - expect trouble!\n",
device->cdev->dev.bus_id);
PRINT_WARN("State was %i\n", device->tape_state);
device->tape_state = TS_NOT_OPER;
tapeblock_cleanup_device(device);
tapechar_cleanup_device(device);
device->discipline->cleanup_device(device);
tape_remove_minor(device);
}
device->tape_state = TS_NOT_OPER;
tape_med_state_set(device, MS_UNKNOWN);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
if (cdev->dev.driver_data != NULL) { if (cdev->dev.driver_data != NULL) {
sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group);
cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data); cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data);
} }
} }
/* /*
...@@ -665,7 +756,7 @@ tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request, ...@@ -665,7 +756,7 @@ tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request,
op = "---"; op = "---";
DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n", DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n",
irb->scsw.dstat,irb->scsw.cstat); irb->scsw.dstat,irb->scsw.cstat);
DBF_EVENT(3, "DEVICE: %s OP\t: %s\n", device->cdev->dev.bus_id,op); DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op);
sptr = (unsigned int *) irb->ecw; sptr = (unsigned int *) irb->ecw;
DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]); DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);
DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]); DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]);
...@@ -815,13 +906,31 @@ tape_do_io_interruptible(struct tape_device *device, ...@@ -815,13 +906,31 @@ tape_do_io_interruptible(struct tape_device *device,
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = __tape_halt_io(device, request); rc = __tape_halt_io(device, request);
if (rc == 0) { if (rc == 0) {
DBF_EVENT(3, "IO stopped on %s\n", device->cdev->dev.bus_id); DBF_EVENT(3, "IO stopped on %08x\n", device->cdev_id);
rc = -ERESTARTSYS; rc = -ERESTARTSYS;
} }
spin_unlock_irq(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc; return rc;
} }
/*
* Handle requests that return an i/o error in the irb.
*/
static inline void
tape_handle_killed_request(
struct tape_device *device,
struct tape_request *request)
{
if(request != NULL) {
/* Set ending status. FIXME: Should the request be retried? */
request->rc = -EIO;
request->status = TAPE_REQUEST_DONE;
__tape_remove_request(device, request);
} else {
__tape_do_io_list(device);
}
}
/* /*
* Tape interrupt routine, called from the ccw_device layer * Tape interrupt routine, called from the ccw_device layer
*/ */
...@@ -835,7 +944,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -835,7 +944,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
device = (struct tape_device *) cdev->dev.driver_data; device = (struct tape_device *) cdev->dev.driver_data;
if (device == NULL) { if (device == NULL) {
PRINT_ERR("could not get device structure for bus_id %s " PRINT_ERR("could not get device structure for %s "
"in interrupt\n", cdev->dev.bus_id); "in interrupt\n", cdev->dev.bus_id);
return; return;
} }
...@@ -843,6 +952,23 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -843,6 +952,23 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
DBF_LH(6, "__tape_do_irq(device=%p, request=%p)\n", device, request); DBF_LH(6, "__tape_do_irq(device=%p, request=%p)\n", device, request);
/* On special conditions irb is an error pointer */
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -ETIMEDOUT:
PRINT_WARN("(%s): Request timed out\n",
cdev->dev.bus_id);
case -EIO:
tape_handle_killed_request(device, request);
break;
default:
PRINT_ERR("(%s): Unexpected i/o error %li\n",
cdev->dev.bus_id,
PTR_ERR(irb));
}
return;
}
/* May be an unsolicited irq */ /* May be an unsolicited irq */
if(request != NULL) if(request != NULL)
request->rescnt = irb->scsw.count; request->rescnt = irb->scsw.count;
...@@ -1023,7 +1149,7 @@ tape_init (void) ...@@ -1023,7 +1149,7 @@ tape_init (void)
#ifdef DBF_LIKE_HELL #ifdef DBF_LIKE_HELL
debug_set_level(tape_dbf_area, 6); debug_set_level(tape_dbf_area, 6);
#endif #endif
DBF_EVENT(3, "tape init: ($Revision: 1.44 $)\n"); DBF_EVENT(3, "tape init: ($Revision: 1.48 $)\n");
tape_proc_init(); tape_proc_init();
tapechar_init (); tapechar_init ();
tapeblock_init (); tapeblock_init ();
...@@ -1048,7 +1174,7 @@ tape_exit(void) ...@@ -1048,7 +1174,7 @@ tape_exit(void)
MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and " MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
"Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)"); "Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)");
MODULE_DESCRIPTION("Linux on zSeries channel attached " MODULE_DESCRIPTION("Linux on zSeries channel attached "
"tape device driver ($Revision: 1.44 $)"); "tape device driver ($Revision: 1.48 $)");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(tape_init); module_init(tape_init);
...@@ -1056,9 +1182,9 @@ module_exit(tape_exit); ...@@ -1056,9 +1182,9 @@ module_exit(tape_exit);
EXPORT_SYMBOL(tape_dbf_area); EXPORT_SYMBOL(tape_dbf_area);
EXPORT_SYMBOL(tape_generic_remove); EXPORT_SYMBOL(tape_generic_remove);
EXPORT_SYMBOL(tape_disable_device);
EXPORT_SYMBOL(tape_generic_probe); EXPORT_SYMBOL(tape_generic_probe);
EXPORT_SYMBOL(tape_enable_device); EXPORT_SYMBOL(tape_generic_online);
EXPORT_SYMBOL(tape_generic_offline);
EXPORT_SYMBOL(tape_put_device); EXPORT_SYMBOL(tape_put_device);
EXPORT_SYMBOL(tape_get_device_reference); EXPORT_SYMBOL(tape_get_device_reference);
EXPORT_SYMBOL(tape_state_verbose); EXPORT_SYMBOL(tape_state_verbose);
......
...@@ -42,8 +42,8 @@ tape_std_assign_timeout(unsigned long data) ...@@ -42,8 +42,8 @@ tape_std_assign_timeout(unsigned long data)
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
if (request->callback != NULL) { if (request->callback != NULL) {
DBF_EVENT(3, "%s: Assignment timeout. Device busy.\n", DBF_EVENT(3, "%08x: Assignment timeout. Device busy.\n",
device->cdev->dev.bus_id); device->cdev_id);
PRINT_ERR("%s: Assignment timeout. Device busy.\n", PRINT_ERR("%s: Assignment timeout. Device busy.\n",
device->cdev->dev.bus_id); device->cdev->dev.bus_id);
ccw_device_clear(device->cdev, (long) request); ccw_device_clear(device->cdev, (long) request);
...@@ -84,10 +84,10 @@ tape_std_assign(struct tape_device *device) ...@@ -84,10 +84,10 @@ tape_std_assign(struct tape_device *device)
if (rc != 0) { if (rc != 0) {
PRINT_WARN("%s: assign failed - device might be busy\n", PRINT_WARN("%s: assign failed - device might be busy\n",
device->cdev->dev.bus_id); device->cdev->dev.bus_id);
DBF_EVENT(3, "%s: assign failed - device might be busy\n", DBF_EVENT(3, "%08x: assign failed - device might be busy\n",
device->cdev->dev.bus_id); device->cdev_id);
} else { } else {
DBF_EVENT(3, "%s: Tape assigned\n", device->cdev->dev.bus_id); DBF_EVENT(3, "%08x: Tape assigned\n", device->cdev_id);
} }
tape_free_request(request); tape_free_request(request);
return rc; return rc;
...@@ -102,6 +102,14 @@ tape_std_unassign (struct tape_device *device) ...@@ -102,6 +102,14 @@ tape_std_unassign (struct tape_device *device)
int rc; int rc;
struct tape_request *request; struct tape_request *request;
if (device->tape_state == TS_NOT_OPER) {
DBF_EVENT(3, "(%08x): Can't unassign device\n",
device->cdev_id);
PRINT_WARN("(%s): Can't unassign device - device gone\n",
device->cdev->dev.bus_id);
return -EIO;
}
request = tape_alloc_request(2, 11); request = tape_alloc_request(2, 11);
if (IS_ERR(request)) if (IS_ERR(request))
return PTR_ERR(request); return PTR_ERR(request);
...@@ -111,10 +119,10 @@ tape_std_unassign (struct tape_device *device) ...@@ -111,10 +119,10 @@ tape_std_unassign (struct tape_device *device)
tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
if ((rc = tape_do_io(device, request)) != 0) { if ((rc = tape_do_io(device, request)) != 0) {
DBF_EVENT(3, "%s: Unassign failed\n", device->cdev->dev.bus_id); DBF_EVENT(3, "%08x: Unassign failed\n", device->cdev_id);
PRINT_WARN("%s: Unassign failed\n", device->cdev->dev.bus_id); PRINT_WARN("%s: Unassign failed\n", device->cdev->dev.bus_id);
} else { } else {
DBF_EVENT(3, "%s: Tape unassigned\n", device->cdev->dev.bus_id); DBF_EVENT(3, "%08x: Tape unassigned\n", device->cdev_id);
} }
tape_free_request(request); tape_free_request(request);
return rc; return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment