📄 tape_core.c
字号:
op = tape_op_verbose[request->op]; else op = "---"; DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n", irb->scsw.dstat,irb->scsw.cstat); DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op); sptr = (unsigned int *) irb->ecw; 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[4], sptr[5]); DBF_EVENT(3, "%08x %08x\n", sptr[6], sptr[7]);}/* * I/O helper function. Adds the request to the request queue * and starts it if the tape is idle. Has to be called with * the device lock held. */static inline int__tape_start_request(struct tape_device *device, struct tape_request *request){ int rc; switch (request->op) { case TO_MSEN: case TO_ASSIGN: case TO_UNASSIGN: case TO_READ_ATTMSG: if (device->tape_state == TS_INIT) break; if (device->tape_state == TS_UNUSED) break; default: if (device->tape_state == TS_BLKUSE) break; if (device->tape_state != TS_IN_USE) return -ENODEV; } /* Increase use count of device for the added request. */ request->device = tape_get_device_reference(device); if (list_empty(&device->req_queue)) { /* No other requests are on the queue. Start this one. */ rc = __tape_start_io(device, request); if (rc) return rc; DBF_LH(5, "Request %p added for execution.\n", request); list_add(&request->list, &device->req_queue); } else { DBF_LH(5, "Request %p add to queue.\n", request); request->status = TAPE_REQUEST_QUEUED; list_add_tail(&request->list, &device->req_queue); } return 0;}/* * Add the request to the request queue, try to start it if the * tape is idle. Return without waiting for end of i/o. */inttape_do_io_async(struct tape_device *device, struct tape_request *request){ int rc; DBF_LH(6, "tape_do_io_async(%p, %p)\n", device, request); spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Add request to request queue and try to start it. */ rc = __tape_start_request(device, request); spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc;}/* * tape_do_io/__tape_wake_up * Add the request to the request queue, try to start it if the * tape is idle and wait uninterruptible for its completion. */static void__tape_wake_up(struct tape_request *request, void *data){ request->callback = NULL; wake_up((wait_queue_head_t *) data);}inttape_do_io(struct tape_device *device, struct tape_request *request){ wait_queue_head_t wq; int rc; init_waitqueue_head(&wq); spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Setup callback */ request->callback = __tape_wake_up; request->callback_data = &wq; /* Add request to request queue and try to start it. */ rc = __tape_start_request(device, request); spin_unlock_irq(get_ccwdev_lock(device->cdev)); if (rc) return rc; /* Request added to the queue. Wait for its completion. */ wait_event(wq, (request->callback == NULL)); /* Get rc from request */ return request->rc;}/* * tape_do_io_interruptible/__tape_wake_up_interruptible * Add the request to the request queue, try to start it if the * tape is idle and wait uninterruptible for its completion. */static void__tape_wake_up_interruptible(struct tape_request *request, void *data){ request->callback = NULL; wake_up_interruptible((wait_queue_head_t *) data);}inttape_do_io_interruptible(struct tape_device *device, struct tape_request *request){ wait_queue_head_t wq; int rc; init_waitqueue_head(&wq); spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Setup callback */ request->callback = __tape_wake_up_interruptible; request->callback_data = &wq; rc = __tape_start_request(device, request); spin_unlock_irq(get_ccwdev_lock(device->cdev)); if (rc) return rc; /* Request added to the queue. Wait for its completion. */ rc = wait_event_interruptible(wq, (request->callback == NULL)); if (rc != -ERESTARTSYS) /* Request finished normally. */ return request->rc; /* Interrupted by a signal. We have to stop the current request. */ spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = __tape_cancel_io(device, request); spin_unlock_irq(get_ccwdev_lock(device->cdev)); if (rc == 0) { /* Wait for the interrupt that acknowledges the halt. */ do { rc = wait_event_interruptible( wq, (request->callback == NULL) ); } while (rc != -ERESTARTSYS); DBF_EVENT(3, "IO stopped on %08x\n", device->cdev_id); rc = -ERESTARTSYS; } return rc;}/* * Tape interrupt routine, called from the ccw_device layer */static void__tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ struct tape_device *device; struct tape_request *request; int rc; device = (struct tape_device *) cdev->dev.driver_data; if (device == NULL) { PRINT_ERR("could not get device structure for %s " "in interrupt\n", cdev->dev.bus_id); return; } request = (struct tape_request *) intparm; 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)) { /* FIXME: What to do with the request? */ switch (PTR_ERR(irb)) { case -ETIMEDOUT: PRINT_WARN("(%s): Request timed out\n", cdev->dev.bus_id); case -EIO: __tape_end_request(device, request, -EIO); break; default: PRINT_ERR("(%s): Unexpected i/o error %li\n", cdev->dev.bus_id, PTR_ERR(irb)); } return; } /* * 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. */ 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); return; } /* May be an unsolicited irq */ if(request != NULL) request->rescnt = irb->scsw.count; if (irb->scsw.dstat != 0x0c) { /* Set the 'ONLINE' flag depending on sense byte 1 */ if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE) device->tape_generic_status |= GMT_ONLINE(~0); else device->tape_generic_status &= ~GMT_ONLINE(~0); /* * Any request that does not come back with channel end * and device end is unusual. Log the sense data. */ DBF_EVENT(3,"-- Tape Interrupthandler --\n"); tape_dump_sense_dbf(device, request, irb); } else { /* Upon normal completion the device _is_ online */ device->tape_generic_status |= GMT_ONLINE(~0); } if (device->tape_state == TS_NOT_OPER) { DBF_EVENT(6, "tape:device is not operational\n"); return; } /* * Request that were canceled still come back with an interrupt. * To detect these request the state will be set to TAPE_REQUEST_DONE. */ if(request != NULL && request->status == TAPE_REQUEST_DONE) { __tape_end_request(device, request, -EIO); return; } rc = device->discipline->irq(device, request, irb); /* * rc < 0 : request finished unsuccessfully. * rc == TAPE_IO_SUCCESS: request finished successfully. * rc == TAPE_IO_PENDING: request is still running. Ignore rc. * rc == TAPE_IO_RETRY: request finished but needs another go. * rc == TAPE_IO_STOP: request needs to get terminated. */ switch (rc) { case TAPE_IO_SUCCESS: /* Upon normal completion the device _is_ online */ device->tape_generic_status |= GMT_ONLINE(~0); __tape_end_request(device, request, rc); break; case TAPE_IO_PENDING: break; case TAPE_IO_RETRY: rc = __tape_start_io(device, request); if (rc) __tape_end_request(device, request, rc); break; case TAPE_IO_STOP: rc = __tape_cancel_io(device, request); if (rc) __tape_end_request(device, request, rc); break; default: if (rc > 0) { DBF_EVENT(6, "xunknownrc\n"); PRINT_ERR("Invalid return code from discipline " "interrupt function.\n"); __tape_end_request(device, request, -EIO); } else { __tape_end_request(device, request, rc); } break; }}/* * Tape device open function used by tape_char & tape_block frontends. */inttape_open(struct tape_device *device){ int rc; spin_lock(get_ccwdev_lock(device->cdev)); if (device->tape_state == TS_NOT_OPER) { DBF_EVENT(6, "TAPE:nodev\n"); rc = -ENODEV; } else if (device->tape_state == TS_IN_USE) { DBF_EVENT(6, "TAPE:dbusy\n"); rc = -EBUSY; } else if (device->tape_state == TS_BLKUSE) { DBF_EVENT(6, "TAPE:dbusy\n"); rc = -EBUSY; } else if (device->discipline != NULL && !try_module_get(device->discipline->owner)) { DBF_EVENT(6, "TAPE:nodisc\n"); rc = -ENODEV; } else { tape_state_set(device, TS_IN_USE); rc = 0; } spin_unlock(get_ccwdev_lock(device->cdev)); return rc;}/* * Tape device release function used by tape_char & tape_block frontends. */inttape_release(struct tape_device *device){ spin_lock(get_ccwdev_lock(device->cdev)); if (device->tape_state == TS_IN_USE) tape_state_set(device, TS_UNUSED); module_put(device->discipline->owner); spin_unlock(get_ccwdev_lock(device->cdev)); return 0;}/* * Execute a magnetic tape command a number of times. */inttape_mtop(struct tape_device *device, int mt_op, int mt_count){ tape_mtop_fn fn; int rc; DBF_EVENT(6, "TAPE:mtio\n"); DBF_EVENT(6, "TAPE:ioop: %x\n", mt_op); DBF_EVENT(6, "TAPE:arg: %x\n", mt_count); if (mt_op < 0 || mt_op >= TAPE_NR_MTOPS) return -EINVAL; fn = device->discipline->mtop_array[mt_op]; if (fn == NULL) return -EINVAL; /* We assume that the backends can handle count up to 500. */ if (mt_op == MTBSR || mt_op == MTFSR || mt_op == MTFSF || mt_op == MTBSF || mt_op == MTFSFM || mt_op == MTBSFM) { rc = 0; for (; mt_count > 500; mt_count -= 500) if ((rc = fn(device, 500)) != 0) break; if (rc == 0) rc = fn(device, mt_count); } else rc = fn(device, mt_count); return rc;}/* * Tape init function. */static inttape_init (void){ TAPE_DBF_AREA = debug_register ( "tape", 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, "tape init: ($Revision: 1.54 $)\n"); tape_proc_init(); tapechar_init (); tapeblock_init (); return 0;}/* * Tape exit function. */static voidtape_exit(void){ DBF_EVENT(6, "tape exit\n"); /* Get rid of the frontends */ tapechar_exit(); tapeblock_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 ($Revision: 1.54 $)");MODULE_LICENSE("GPL");module_init(tape_init);module_exit(tape_exit);EXPORT_SYMBOL(tape_generic_remove);EXPORT_SYMBOL(tape_generic_probe);EXPORT_SYMBOL(tape_generic_online);EXPORT_SYMBOL(tape_generic_offline);EXPORT_SYMBOL(tape_put_device);EXPORT_SYMBOL(tape_get_device_reference);EXPORT_SYMBOL(tape_state_verbose);EXPORT_SYMBOL(tape_op_verbose);EXPORT_SYMBOL(tape_state_set);EXPORT_SYMBOL(tape_med_state_set);EXPORT_SYMBOL(tape_alloc_request);EXPORT_SYMBOL(tape_free_request);EXPORT_SYMBOL(tape_dump_sense);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_mtop);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -