📄 tape_core.c
字号:
break; default: 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; } DBF_LH(3, "(%08x): Drive set offline.\n", device->cdev_id); return 0;}/* * Allocate memory for a new device structure. */static struct tape_device *tape_alloc_device(void){ struct tape_device *device; device = (struct tape_device *) kmalloc(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); if (device->modeset_byte == NULL) { DBF_EXCEPTION(2, "ti:no mem\n"); PRINT_INFO("can't allocate memory for modeset byte\n"); kfree(device); return ERR_PTR(-ENOMEM); } INIT_LIST_HEAD(&device->req_queue); INIT_LIST_HEAD(&device->node); init_waitqueue_head(&device->state_change_wq); device->tape_state = TS_INIT; device->medium_state = MS_UNKNOWN; *device->modeset_byte = 0; device->first_minor = -1; atomic_set(&device->ref_count, 1); INIT_WORK(&device->tape_dnr, tape_delayed_next_request, device); return device;}/* * Get a reference to an existing device structure. This will automatically * increment the reference count. */struct tape_device *tape_get_device_reference(struct tape_device *device){ DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device, atomic_inc_return(&device->ref_count)); return device;}/* * Decrease the reference counter of a devices structure. If the * reference counter reaches zero free the device structure. * The function returns a NULL pointer to be used by the caller * for clearing reference pointers. */struct tape_device *tape_put_device(struct tape_device *device){ int remain; remain = atomic_dec_return(&device->ref_count); if (remain > 0) { DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain); } else { if (remain < 0) { DBF_EVENT(4, "put device without reference\n"); PRINT_ERR("put device without reference\n"); } else { DBF_EVENT(4, "tape_free_device(%p)\n", device); kfree(device->modeset_byte); kfree(device); } } return NULL; }/* * Find tape device by a device index. */struct tape_device *tape_get_device(int devindex){ struct tape_device *device, *tmp; device = ERR_PTR(-ENODEV); read_lock(&tape_device_lock); list_for_each_entry(tmp, &tape_device_list, node) { if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) { device = tape_get_device_reference(tmp); break; } } read_unlock(&tape_device_lock); return device;}/* * Driverfs tape probe function. */inttape_generic_probe(struct ccw_device *cdev){ struct tape_device *device; device = tape_alloc_device(); if (IS_ERR(device)) return -ENODEV; PRINT_INFO("tape device %s found\n", cdev->dev.bus_id); cdev->dev.driver_data = device; device->cdev = cdev; device->cdev_id = busid_to_int(cdev->dev.bus_id); cdev->handler = __tape_do_irq; ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); return 0;}static inline void__tape_discard_requests(struct tape_device *device){ struct tape_request * request; struct list_head * l, *n; 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); }}/* * 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. */voidtape_generic_remove(struct ccw_device *cdev){ struct tape_device * device; device = cdev->dev.driver_data; if (!device) { PRINT_ERR("No device pointer in tape_generic_remove!\n"); return; } DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev); spin_lock_irq(get_ccwdev_lock(device->cdev)); switch (device->tape_state) { case TS_INIT: tape_state_set(device, TS_NOT_OPER); case TS_NOT_OPER: /* * Nothing to do. */ spin_unlock_irq(get_ccwdev_lock(device->cdev)); break; case TS_UNUSED: /* * Need only to release the device. */ tape_state_set(device, TS_NOT_OPER); spin_unlock_irq(get_ccwdev_lock(device->cdev)); tape_cleanup_device(device); break; default: /* * There may be requests on the queue. We will not get * an interrupt for a request that was running. So we * just post them all as I/O errors. */ 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); tape_state_set(device, TS_NOT_OPER); __tape_discard_requests(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); tape_cleanup_device(device); } if (cdev->dev.driver_data != NULL) { sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data); }}/* * Allocate a new tape ccw request */struct tape_request *tape_alloc_request(int cplength, int datasize){ struct tape_request *request; if (datasize > PAGE_SIZE || (cplength*sizeof(struct ccw1)) > PAGE_SIZE) BUG(); DBF_LH(6, "tape_alloc_request(%d, %d)\n", cplength, datasize); request = (struct tape_request *) kmalloc(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), 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); 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); return request;}/* * Free tape ccw request */voidtape_free_request (struct tape_request * request){ DBF_LH(6, "Free request %p\n", request); if (request->device != NULL) { request->device = tape_put_device(request->device); } kfree(request->cpdata); kfree(request->cpaddr); kfree(request);}static inline int__tape_start_io(struct tape_device *device, struct tape_request *request){ int rc;#ifdef CONFIG_S390_TAPE_BLOCK if (request->op == TO_BLOCK) device->discipline->check_locate(device, request);#endif rc = ccw_device_start( device->cdev, request->cpaddr, (unsigned long) request, 0x00, request->options ); if (rc == 0) { request->status = TAPE_REQUEST_IN_IO; } else if (rc == -EBUSY) { /* The common I/O subsystem is currently busy. Retry later. */ request->status = TAPE_REQUEST_QUEUED; schedule_work(&device->tape_dnr); rc = 0; } else { /* Start failed. Remove request and indicate failure. */ DBF_EVENT(1, "tape: start request failed with RC = %i\n", rc); } return rc;}static inline void__tape_start_next_request(struct tape_device *device){ struct list_head *l, *n; struct tape_request *request; int rc; DBF_LH(6, "__tape_start_next_request(%p)\n", device); /* * Try to start each request on request queue until one is * started successful. */ list_for_each_safe(l, n, &device->req_queue) { request = list_entry(l, struct tape_request, list); /* * Avoid race condition if bottom-half was triggered more than * once. */ if (request->status == TAPE_REQUEST_IN_IO) return; /* * We wanted to cancel the request but the common I/O layer * was busy at that time. This can only happen if this * function is called by delayed_next_request. * Otherwise we start the next request on the queue. */ if (request->status == TAPE_REQUEST_CANCEL) { rc = __tape_cancel_io(device, request); } else { rc = __tape_start_io(device, request); } if (rc == 0) return; /* Set ending status. */ request->rc = rc; request->status = TAPE_REQUEST_DONE; /* Remove from request queue. */ list_del(&request->list); /* Do callback. */ if (request->callback != NULL) request->callback(request, request->callback_data); }}static voidtape_delayed_next_request(void *data){ struct tape_device * device; device = (struct tape_device *) data; DBF_LH(6, "tape_delayed_next_request(%p)\n", device); spin_lock_irq(get_ccwdev_lock(device->cdev)); __tape_start_next_request(device); spin_unlock_irq(get_ccwdev_lock(device->cdev));}static inline void__tape_end_request( struct tape_device * device, struct tape_request * request, int rc){ DBF_LH(6, "__tape_end_request(%p, %p, %i)\n", device, request, rc); if (request) { request->rc = rc; request->status = TAPE_REQUEST_DONE; /* Remove from request queue. */ list_del(&request->list); /* Do callback. */ if (request->callback != NULL) request->callback(request, request->callback_data); } /* Start next request. */ if (!list_empty(&device->req_queue)) __tape_start_next_request(device);}/* * Write sense data to console/dbf */voidtape_dump_sense(struct tape_device* device, struct tape_request *request, struct irb *irb){ unsigned int *sptr; PRINT_INFO("-------------------------------------------------\n"); PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n", irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa); PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id); if (request != NULL) PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]); sptr = (unsigned int *) irb->ecw; PRINT_INFO("Sense data: %08X %08X %08X %08X \n", sptr[0], sptr[1], sptr[2], sptr[3]); PRINT_INFO("Sense data: %08X %08X %08X %08X \n", sptr[4], sptr[5], sptr[6], sptr[7]); PRINT_INFO("--------------------------------------------------\n");}/* * Write sense data to dbf */voidtape_dump_sense_dbf(struct tape_device *device, struct tape_request *request, struct irb *irb){ unsigned int *sptr; const char* op; if (request != NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -