sunvdc.c

来自「linux 内核源代码」· C语言 代码 · 共 890 行 · 第 1/2 页

C
890
字号
	return err;}static void do_vdc_request(struct request_queue *q){	while (1) {		struct request *req = elv_next_request(q);		if (!req)			break;		blkdev_dequeue_request(req);		if (__send_request(req) < 0)			vdc_end_request(req, 0, req->hard_nr_sectors);	}}static int generic_request(struct vdc_port *port, u8 op, void *buf, int len){	struct vio_dring_state *dr;	struct vio_completion comp;	struct vio_disk_desc *desc;	unsigned int map_perm;	unsigned long flags;	int op_len, err;	void *req_buf;	if (!(((u64)1 << ((u64)op - 1)) & port->operations))		return -EOPNOTSUPP;	switch (op) {	case VD_OP_BREAD:	case VD_OP_BWRITE:	default:		return -EINVAL;	case VD_OP_FLUSH:		op_len = 0;		map_perm = 0;		break;	case VD_OP_GET_WCE:		op_len = sizeof(u32);		map_perm = LDC_MAP_W;		break;	case VD_OP_SET_WCE:		op_len = sizeof(u32);		map_perm = LDC_MAP_R;		break;	case VD_OP_GET_VTOC:		op_len = sizeof(struct vio_disk_vtoc);		map_perm = LDC_MAP_W;		break;	case VD_OP_SET_VTOC:		op_len = sizeof(struct vio_disk_vtoc);		map_perm = LDC_MAP_R;		break;	case VD_OP_GET_DISKGEOM:		op_len = sizeof(struct vio_disk_geom);		map_perm = LDC_MAP_W;		break;	case VD_OP_SET_DISKGEOM:		op_len = sizeof(struct vio_disk_geom);		map_perm = LDC_MAP_R;		break;	case VD_OP_SCSICMD:		op_len = 16;		map_perm = LDC_MAP_RW;		break;	case VD_OP_GET_DEVID:		op_len = sizeof(struct vio_disk_devid);		map_perm = LDC_MAP_W;		break;	case VD_OP_GET_EFI:	case VD_OP_SET_EFI:		return -EOPNOTSUPP;		break;	};	map_perm |= LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO;	op_len = (op_len + 7) & ~7;	req_buf = kzalloc(op_len, GFP_KERNEL);	if (!req_buf)		return -ENOMEM;	if (len > op_len)		len = op_len;	if (map_perm & LDC_MAP_R)		memcpy(req_buf, buf, len);	spin_lock_irqsave(&port->vio.lock, flags);	dr = &port->vio.drings[VIO_DRIVER_TX_RING];	/* XXX If we want to use this code generically we have to	 * XXX handle TX ring exhaustion etc.	 */	desc = vio_dring_cur(dr);	err = ldc_map_single(port->vio.lp, req_buf, op_len,			     desc->cookies, port->ring_cookies,			     map_perm);	if (err < 0) {		spin_unlock_irqrestore(&port->vio.lock, flags);		kfree(req_buf);		return err;	}	init_completion(&comp.com);	comp.waiting_for = WAITING_FOR_GEN_CMD;	port->vio.cmp = &comp;	desc->hdr.ack = VIO_ACK_ENABLE;	desc->req_id = port->req_id;	desc->operation = op;	desc->slice = 0;	desc->status = ~0;	desc->offset = 0;	desc->size = op_len;	desc->ncookies = err;	/* This has to be a non-SMP write barrier because we are writing	 * to memory which is shared with the peer LDOM.	 */	wmb();	desc->hdr.state = VIO_DESC_READY;	err = __vdc_tx_trigger(port);	if (err >= 0) {		port->req_id++;		dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);		spin_unlock_irqrestore(&port->vio.lock, flags);		wait_for_completion(&comp.com);		err = comp.err;	} else {		port->vio.cmp = NULL;		spin_unlock_irqrestore(&port->vio.lock, flags);	}	if (map_perm & LDC_MAP_W)		memcpy(buf, req_buf, len);	kfree(req_buf);	return err;}static int __devinit vdc_alloc_tx_ring(struct vdc_port *port){	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];	unsigned long len, entry_size;	int ncookies;	void *dring;	entry_size = sizeof(struct vio_disk_desc) +		(sizeof(struct ldc_trans_cookie) * port->ring_cookies);	len = (VDC_TX_RING_SIZE * entry_size);	ncookies = VIO_MAX_RING_COOKIES;	dring = ldc_alloc_exp_dring(port->vio.lp, len,				    dr->cookies, &ncookies,				    (LDC_MAP_SHADOW |				     LDC_MAP_DIRECT |				     LDC_MAP_RW));	if (IS_ERR(dring))		return PTR_ERR(dring);	dr->base = dring;	dr->entry_size = entry_size;	dr->num_entries = VDC_TX_RING_SIZE;	dr->prod = dr->cons = 0;	dr->pending = VDC_TX_RING_SIZE;	dr->ncookies = ncookies;	return 0;}static void vdc_free_tx_ring(struct vdc_port *port){	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];	if (dr->base) {		ldc_free_exp_dring(port->vio.lp, dr->base,				   (dr->entry_size * dr->num_entries),				   dr->cookies, dr->ncookies);		dr->base = NULL;		dr->entry_size = 0;		dr->num_entries = 0;		dr->pending = 0;		dr->ncookies = 0;	}}static int probe_disk(struct vdc_port *port){	struct vio_completion comp;	struct request_queue *q;	struct gendisk *g;	int err;	init_completion(&comp.com);	comp.err = 0;	comp.waiting_for = WAITING_FOR_LINK_UP;	port->vio.cmp = &comp;	vio_port_up(&port->vio);	wait_for_completion(&comp.com);	if (comp.err)		return comp.err;	err = generic_request(port, VD_OP_GET_VTOC,			      &port->label, sizeof(port->label));	if (err < 0) {		printk(KERN_ERR PFX "VD_OP_GET_VTOC returns error %d\n", err);		return err;	}	err = generic_request(port, VD_OP_GET_DISKGEOM,			      &port->geom, sizeof(port->geom));	if (err < 0) {		printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "		       "error %d\n", err);		return err;	}	port->vdisk_size = ((u64)port->geom.num_cyl *			    (u64)port->geom.num_hd *			    (u64)port->geom.num_sec);	q = blk_init_queue(do_vdc_request, &port->vio.lock);	if (!q) {		printk(KERN_ERR PFX "%s: Could not allocate queue.\n",		       port->vio.name);		return -ENOMEM;	}	g = alloc_disk(1 << PARTITION_SHIFT);	if (!g) {		printk(KERN_ERR PFX "%s: Could not allocate gendisk.\n",		       port->vio.name);		blk_cleanup_queue(q);		return -ENOMEM;	}	port->disk = g;	blk_queue_max_hw_segments(q, port->ring_cookies);	blk_queue_max_phys_segments(q, port->ring_cookies);	blk_queue_max_sectors(q, port->max_xfer_size);	g->major = vdc_major;	g->first_minor = port->vio.vdev->dev_no << PARTITION_SHIFT;	strcpy(g->disk_name, port->disk_name);	g->fops = &vdc_fops;	g->queue = q;	g->private_data = port;	g->driverfs_dev = &port->vio.vdev->dev;	set_capacity(g, port->vdisk_size);	printk(KERN_INFO PFX "%s: %u sectors (%u MB)\n",	       g->disk_name,	       port->vdisk_size, (port->vdisk_size >> (20 - 9)));	add_disk(g);	return 0;}static struct ldc_channel_config vdc_ldc_cfg = {	.event		= vdc_event,	.mtu		= 64,	.mode		= LDC_MODE_UNRELIABLE,};static struct vio_driver_ops vdc_vio_ops = {	.send_attr		= vdc_send_attr,	.handle_attr		= vdc_handle_attr,	.handshake_complete	= vdc_handshake_complete,};static void print_version(void){	static int version_printed;	if (version_printed++ == 0)		printk(KERN_INFO "%s", version);}static int __devinit vdc_port_probe(struct vio_dev *vdev,				    const struct vio_device_id *id){	struct mdesc_handle *hp;	struct vdc_port *port;	int err;	print_version();	hp = mdesc_grab();	err = -ENODEV;	if ((vdev->dev_no << PARTITION_SHIFT) & ~(u64)MINORMASK) {		printk(KERN_ERR PFX "Port id [%lu] too large.\n",		       vdev->dev_no);		goto err_out_release_mdesc;	}	port = kzalloc(sizeof(*port), GFP_KERNEL);	err = -ENOMEM;	if (!port) {		printk(KERN_ERR PFX "Cannot allocate vdc_port.\n");		goto err_out_release_mdesc;	}	if (vdev->dev_no >= 26)		snprintf(port->disk_name, sizeof(port->disk_name),			 VDCBLK_NAME "%c%c",			 'a' + ((int)vdev->dev_no / 26) - 1,			 'a' + ((int)vdev->dev_no % 26));	else		snprintf(port->disk_name, sizeof(port->disk_name),			 VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26));	err = vio_driver_init(&port->vio, vdev, VDEV_DISK,			      vdc_versions, ARRAY_SIZE(vdc_versions),			      &vdc_vio_ops, port->disk_name);	if (err)		goto err_out_free_port;	port->vdisk_block_size = 512;	port->max_xfer_size = ((128 * 1024) / port->vdisk_block_size);	port->ring_cookies = ((port->max_xfer_size *			       port->vdisk_block_size) / PAGE_SIZE) + 2;	err = vio_ldc_alloc(&port->vio, &vdc_ldc_cfg, port);	if (err)		goto err_out_free_port;	err = vdc_alloc_tx_ring(port);	if (err)		goto err_out_free_ldc;	err = probe_disk(port);	if (err)		goto err_out_free_tx_ring;	dev_set_drvdata(&vdev->dev, port);	mdesc_release(hp);	return 0;err_out_free_tx_ring:	vdc_free_tx_ring(port);err_out_free_ldc:	vio_ldc_free(&port->vio);err_out_free_port:	kfree(port);err_out_release_mdesc:	mdesc_release(hp);	return err;}static int vdc_port_remove(struct vio_dev *vdev){	struct vdc_port *port = dev_get_drvdata(&vdev->dev);	if (port) {		del_timer_sync(&port->vio.timer);		vdc_free_tx_ring(port);		vio_ldc_free(&port->vio);		dev_set_drvdata(&vdev->dev, NULL);		kfree(port);	}	return 0;}static struct vio_device_id vdc_port_match[] = {	{		.type = "vdc-port",	},	{},};MODULE_DEVICE_TABLE(vio, vdc_port_match);static struct vio_driver vdc_port_driver = {	.id_table	= vdc_port_match,	.probe		= vdc_port_probe,	.remove		= vdc_port_remove,	.driver		= {		.name	= "vdc_port",		.owner	= THIS_MODULE,	}};static int __init vdc_init(void){	int err;	err = register_blkdev(0, VDCBLK_NAME);	if (err < 0)		goto out_err;	vdc_major = err;	err = vio_register_driver(&vdc_port_driver);	if (err)		goto out_unregister_blkdev;	return 0;out_unregister_blkdev:	unregister_blkdev(vdc_major, VDCBLK_NAME);	vdc_major = 0;out_err:	return err;}static void __exit vdc_exit(void){	vio_unregister_driver(&vdc_port_driver);	unregister_blkdev(vdc_major, VDCBLK_NAME);}module_init(vdc_init);module_exit(vdc_exit);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?