⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 viodasd.c

📁 Linux块设备驱动源码
💻 C
📖 第 1 页 / 共 2 页
字号:
	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 + -