viodasd.c
来自「linux 内核源代码」· C语言 代码 · 共 822 行 · 第 1/2 页
C
822 行
if (hvrc != 0) { printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc); return 0; } wait_for_completion(&we.com); if (we.rc != 0) { if (flags != 0) return 0; /* try again with read only flag set */ flags = vioblockflags_ro; goto retry; } if (we.max_disk > (MAX_DISKNO - 1)) { static int warned; if (warned == 0) { warned++; printk(VIOD_KERN_INFO "Only examining the first %d " "of %d disks connected\n", MAX_DISKNO, we.max_disk + 1); } } /* Send the close event to OS/400. We DON'T expect a response */ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_blockio | vioblockclose, HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), 0, VIOVERSION << 16, ((u64)dev_no << 48) | ((u64)flags << 32), 0, 0, 0); if (hvrc != 0) { printk(VIOD_KERN_WARNING "bad rc sending event to OS/400 %d\n", (int)hvrc); return 0; } if (d->dev == NULL) { /* this is when we reprobe for new disks */ if (vio_create_viodasd(dev_no) == NULL) { printk(VIOD_KERN_WARNING "cannot allocate virtual device for disk %d\n", dev_no); return 0; } /* * The vio_create_viodasd will have recursed into this * routine with d->dev set to the new vio device and * will finish the setup of the disk below. */ return 1; } /* create the request queue for the disk */ spin_lock_init(&d->q_lock); q = blk_init_queue(do_viodasd_request, &d->q_lock); if (q == NULL) { printk(VIOD_KERN_WARNING "cannot allocate queue for disk %d\n", dev_no); return 0; } g = alloc_disk(1 << PARTITION_SHIFT); if (g == NULL) { printk(VIOD_KERN_WARNING "cannot allocate disk structure for disk %d\n", dev_no); blk_cleanup_queue(q); return 0; } d->disk = g; blk_queue_max_hw_segments(q, VIOMAXBLOCKDMA); blk_queue_max_phys_segments(q, VIOMAXBLOCKDMA); blk_queue_max_sectors(q, VIODASD_MAXSECTORS); g->major = VIODASD_MAJOR; g->first_minor = dev_no << PARTITION_SHIFT; if (dev_no >= 26) snprintf(g->disk_name, sizeof(g->disk_name), VIOD_GENHD_NAME "%c%c", 'a' + (dev_no / 26) - 1, 'a' + (dev_no % 26)); else snprintf(g->disk_name, sizeof(g->disk_name), VIOD_GENHD_NAME "%c", 'a' + (dev_no % 26)); g->fops = &viodasd_fops; g->queue = q; g->private_data = d; g->driverfs_dev = d->dev; set_capacity(g, d->size >> 9); printk(VIOD_KERN_INFO "disk %d: %lu sectors (%lu MB) " "CHS=%d/%d/%d sector size %d%s\n", dev_no, (unsigned long)(d->size >> 9), (unsigned long)(d->size >> 20), (int)d->cylinders, (int)d->tracks, (int)d->sectors, (int)d->bytes_per_sector, d->read_only ? " (RO)" : ""); /* register us in the global list */ add_disk(g); return 1;}/* returns the total number of scatterlist elements converted */static int block_event_to_scatterlist(const struct vioblocklpevent *bevent, struct scatterlist *sg, int *total_len){ int i, numsg; const struct rw_data *rw_data = &bevent->u.rw_data; static const int offset = offsetof(struct vioblocklpevent, u.rw_data.dma_info); static const int element_size = sizeof(rw_data->dma_info[0]); numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size; if (numsg > VIOMAXBLOCKDMA) numsg = VIOMAXBLOCKDMA; *total_len = 0; memset(sg, 0, sizeof(sg[0]) * VIOMAXBLOCKDMA); for (i = 0; (i < numsg) && (rw_data->dma_info[i].len > 0); ++i) { sg_dma_address(&sg[i]) = rw_data->dma_info[i].token; sg_dma_len(&sg[i]) = rw_data->dma_info[i].len; *total_len += rw_data->dma_info[i].len; } return i;}/* * Restart all queues, starting with the one _after_ the disk given, * thus reducing the chance of starvation of higher numbered disks. */static void viodasd_restart_all_queues_starting_from(int first_index){ int i; for (i = first_index + 1; i < MAX_DISKNO; ++i) if (viodasd_devices[i].disk) blk_run_queue(viodasd_devices[i].disk->queue); for (i = 0; i <= first_index; ++i) if (viodasd_devices[i].disk) blk_run_queue(viodasd_devices[i].disk->queue);}/* * For read and write requests, decrement the number of outstanding requests, * Free the DMA buffers we allocated. */static int viodasd_handle_read_write(struct vioblocklpevent *bevent){ int num_sg, num_sect, pci_direction, total_len; struct request *req; struct scatterlist sg[VIOMAXBLOCKDMA]; struct HvLpEvent *event = &bevent->event; unsigned long irq_flags; struct viodasd_device *d; int error; spinlock_t *qlock; num_sg = block_event_to_scatterlist(bevent, sg, &total_len); num_sect = total_len >> 9; if (event->xSubtype == (viomajorsubtype_blockio | vioblockread)) pci_direction = DMA_FROM_DEVICE; else pci_direction = DMA_TO_DEVICE; req = (struct request *)bevent->event.xCorrelationToken; d = req->rq_disk->private_data; dma_unmap_sg(d->dev, sg, num_sg, pci_direction); /* * Since this is running in interrupt mode, we need to make sure * we're not stepping on any global I/O operations */ spin_lock_irqsave(&viodasd_spinlock, irq_flags); num_req_outstanding--; spin_unlock_irqrestore(&viodasd_spinlock, irq_flags); error = event->xRc != HvLpEvent_Rc_Good; if (error) { const struct vio_error_entry *err; err = vio_lookup_rc(viodasd_err_table, bevent->sub_result); printk(VIOD_KERN_WARNING "read/write error %d:0x%04x (%s)\n", event->xRc, bevent->sub_result, err->msg); num_sect = req->hard_nr_sectors; } qlock = req->q->queue_lock; spin_lock_irqsave(qlock, irq_flags); viodasd_end_request(req, !error, num_sect); spin_unlock_irqrestore(qlock, irq_flags); /* Finally, try to get more requests off of this device's queue */ viodasd_restart_all_queues_starting_from(DEVICE_NO(d)); return 0;}/* This routine handles incoming block LP events */static void handle_block_event(struct HvLpEvent *event){ struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; struct viodasd_waitevent *pwe; if (event == NULL) /* Notification that a partition went away! */ return; /* First, we should NEVER get an int here...only acks */ if (hvlpevent_is_int(event)) { printk(VIOD_KERN_WARNING "Yikes! got an int in viodasd event handler!\n"); if (hvlpevent_need_ack(event)) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } } switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { case vioblockopen: /* * Handle a response to an open request. We get all the * disk information in the response, so update it. The * correlation token contains a pointer to a waitevent * structure that has a completion in it. update the * return code in the waitevent structure and post the * completion to wake up the guy who sent the request */ pwe = (struct viodasd_waitevent *)event->xCorrelationToken; pwe->rc = event->xRc; pwe->sub_result = bevent->sub_result; if (event->xRc == HvLpEvent_Rc_Good) { const struct open_data *data = &bevent->u.open_data; struct viodasd_device *device = &viodasd_devices[bevent->disk]; device->read_only = bevent->flags & vioblockflags_ro; device->size = data->disk_size; device->cylinders = data->cylinders; device->tracks = data->tracks; device->sectors = data->sectors; device->bytes_per_sector = data->bytes_per_sector; pwe->max_disk = data->max_disk; } complete(&pwe->com); break; case vioblockclose: break; case vioblockread: case vioblockwrite: viodasd_handle_read_write(bevent); break; default: printk(VIOD_KERN_WARNING "invalid subtype!"); if (hvlpevent_need_ack(event)) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } }}/* * Get the driver to reprobe for more disks. */static ssize_t probe_disks(struct device_driver *drv, const char *buf, size_t count){ struct viodasd_device *d; for (d = viodasd_devices; d < &viodasd_devices[MAX_DISKNO]; d++) { if (d->disk == NULL) probe_disk(d); } return count;}static DRIVER_ATTR(probe, S_IWUSR, NULL, probe_disks);static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id){ struct viodasd_device *d = &viodasd_devices[vdev->unit_address]; d->dev = &vdev->dev; if (!probe_disk(d)) return -ENODEV; return 0;}static int viodasd_remove(struct vio_dev *vdev){ struct viodasd_device *d; d = &viodasd_devices[vdev->unit_address]; if (d->disk) { del_gendisk(d->disk); blk_cleanup_queue(d->disk->queue); put_disk(d->disk); d->disk = NULL; } d->dev = NULL; return 0;}/** * viodasd_device_table: Used by vio.c to match devices that we * support. */static struct vio_device_id viodasd_device_table[] __devinitdata = { { "block", "IBM,iSeries-viodasd" }, { "", "" }};MODULE_DEVICE_TABLE(vio, viodasd_device_table);static struct vio_driver viodasd_driver = { .id_table = viodasd_device_table, .probe = viodasd_probe, .remove = viodasd_remove, .driver = { .name = "viodasd", .owner = THIS_MODULE, }};static int need_delete_probe;/* * Initialize the whole device driver. Handle module and non-module * versions */static int __init viodasd_init(void){ int rc; if (!firmware_has_feature(FW_FEATURE_ISERIES)) { rc = -ENODEV; goto early_fail; } /* Try to open to our host lp */ if (viopath_hostLp == HvLpIndexInvalid) vio_set_hostlp(); if (viopath_hostLp == HvLpIndexInvalid) { printk(VIOD_KERN_WARNING "invalid hosting partition\n"); rc = -EIO; goto early_fail; } printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n", viopath_hostLp); /* register the block device */ rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); if (rc) { printk(VIOD_KERN_WARNING "Unable to get major number %d for %s\n", VIODASD_MAJOR, VIOD_GENHD_NAME); goto early_fail; } /* Actually open the path to the hosting partition */ rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); if (rc) { printk(VIOD_KERN_WARNING "error opening path to host partition %d\n", viopath_hostLp); goto unregister_blk; } /* Initialize our request handler */ vio_setHandler(viomajorsubtype_blockio, handle_block_event); rc = vio_register_driver(&viodasd_driver); if (rc) { printk(VIOD_KERN_WARNING "vio_register_driver failed\n"); goto unset_handler; } /* * If this call fails, it just means that we cannot dynamically * add virtual disks, but the driver will still work fine for * all existing disk, so ignore the failure. */ if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe)) need_delete_probe = 1; return 0;unset_handler: vio_clearHandler(viomajorsubtype_blockio); viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);unregister_blk: unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);early_fail: return rc;}module_init(viodasd_init);void __exit viodasd_exit(void){ if (need_delete_probe) driver_remove_file(&viodasd_driver.driver, &driver_attr_probe); vio_unregister_driver(&viodasd_driver); vio_clearHandler(viomajorsubtype_blockio); viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);}module_exit(viodasd_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?