📄 viodasd.c
字号:
return -1;}/* * This is the external request processing routine */static void do_viodasd_request(request_queue_t *q){ struct request *req; /* * If we already have the maximum number of requests * outstanding to OS/400 just bail out. We'll come * back later. */ while (num_req_outstanding < VIOMAXREQ) { req = elv_next_request(q); if (req == NULL) return; /* dequeue the current request from the queue */ blkdev_dequeue_request(req); /* check that request contains a valid command */ if (!blk_fs_request(req)) { viodasd_end_request(req, 0, req->hard_nr_sectors); continue; } /* Try sending the request */ if (send_request(req) != 0) viodasd_end_request(req, 0, req->hard_nr_sectors); }}/* * Probe a single disk and fill in the viodasd_device structure * for it. */static void probe_disk(struct viodasd_device *d){ HvLpEvent_Rc hvrc; struct viodasd_waitevent we; int dev_no = DEVICE_NO(d); struct gendisk *g; struct request_queue *q; u16 flags = 0;retry: init_completion(&we.com); /* Send the open event to OS/400 */ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_blockio | vioblockopen, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), (u64)(unsigned long)&we, VIOVERSION << 16, ((u64)dev_no << 48) | ((u64)flags<< 32), 0, 0, 0); if (hvrc != 0) { printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc); return; } wait_for_completion(&we.com); if (we.rc != 0) { if (flags != 0) return; /* 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; } /* 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; } 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; } 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)); snprintf(g->devfs_name, sizeof(g->devfs_name), "%s%d", VIOD_GENHD_DEVFS_NAME, dev_no); 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);}/* 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 (event->xFlags.xFunction == HvLpEvent_Function_Int) { printk(VIOD_KERN_WARNING "Yikes! got an int in viodasd event handler!\n"); if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { 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 (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { 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; probe_disk(d); if (d->disk == NULL) 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 = { { "viodasd", "" }, { "", "" }};MODULE_DEVICE_TABLE(vio, viodasd_device_table);static struct vio_driver viodasd_driver = { .name = "viodasd", .id_table = viodasd_device_table, .probe = viodasd_probe, .remove = viodasd_remove};/* * Initialize the whole device driver. Handle module and non-module * versions */static int __init viodasd_init(void){ int rc; /* 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"); return -EIO; } printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n", viopath_hostLp); /* register the block device */ if (register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME)) { printk(VIOD_KERN_WARNING "Unable to get major number %d for %s\n", VIODASD_MAJOR, VIOD_GENHD_NAME); return -EIO; } /* Actually open the path to the hosting partition */ if (viopath_open(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2)) { printk(VIOD_KERN_WARNING "error opening path to host partition %d\n", viopath_hostLp); unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); return -EIO; } /* Initialize our request handler */ vio_setHandler(viomajorsubtype_blockio, handle_block_event); rc = vio_register_driver(&viodasd_driver); if (rc == 0) driver_create_file(&viodasd_driver.driver, &driver_attr_probe); return rc;}module_init(viodasd_init);void viodasd_exit(void){ driver_remove_file(&viodasd_driver.driver, &driver_attr_probe); vio_unregister_driver(&viodasd_driver); vio_clearHandler(viomajorsubtype_blockio); unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);}module_exit(viodasd_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -