sunvdc.c

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

C
890
字号
/* sunvdc.c: Sun LDOM Virtual Disk Client. * * Copyright (C) 2007 David S. Miller <davem@davemloft.net> */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/blkdev.h>#include <linux/hdreg.h>#include <linux/genhd.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/list.h>#include <linux/scatterlist.h>#include <asm/vio.h>#include <asm/ldc.h>#define DRV_MODULE_NAME		"sunvdc"#define PFX DRV_MODULE_NAME	": "#define DRV_MODULE_VERSION	"1.0"#define DRV_MODULE_RELDATE	"June 25, 2007"static char version[] __devinitdata =	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");MODULE_DESCRIPTION("Sun LDOM virtual disk client driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_MODULE_VERSION);#define VDC_TX_RING_SIZE	256#define WAITING_FOR_LINK_UP	0x01#define WAITING_FOR_TX_SPACE	0x02#define WAITING_FOR_GEN_CMD	0x04#define WAITING_FOR_ANY		-1struct vdc_req_entry {	struct request		*req;};struct vdc_port {	struct vio_driver_state	vio;	struct gendisk		*disk;	struct vdc_completion	*cmp;	u64			req_id;	u64			seq;	struct vdc_req_entry	rq_arr[VDC_TX_RING_SIZE];	unsigned long		ring_cookies;	u64			max_xfer_size;	u32			vdisk_block_size;	/* The server fills these in for us in the disk attribute	 * ACK packet.	 */	u64			operations;	u32			vdisk_size;	u8			vdisk_type;	char			disk_name[32];	struct vio_disk_geom	geom;	struct vio_disk_vtoc	label;};static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio){	return container_of(vio, struct vdc_port, vio);}/* Ordered from largest major to lowest */static struct vio_version vdc_versions[] = {	{ .major = 1, .minor = 0 },};#define VDCBLK_NAME	"vdisk"static int vdc_major;#define PARTITION_SHIFT	3static inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr){	return vio_dring_avail(dr, VDC_TX_RING_SIZE);}static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo){	struct gendisk *disk = bdev->bd_disk;	struct vdc_port *port = disk->private_data;	geo->heads = (u8) port->geom.num_hd;	geo->sectors = (u8) port->geom.num_sec;	geo->cylinders = port->geom.num_cyl;	return 0;}static struct block_device_operations vdc_fops = {	.owner		= THIS_MODULE,	.getgeo		= vdc_getgeo,};static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for){	if (vio->cmp &&	    (waiting_for == -1 ||	     vio->cmp->waiting_for == waiting_for)) {		vio->cmp->err = err;		complete(&vio->cmp->com);		vio->cmp = NULL;	}}static void vdc_handshake_complete(struct vio_driver_state *vio){	vdc_finish(vio, 0, WAITING_FOR_LINK_UP);}static int vdc_handle_unknown(struct vdc_port *port, void *arg){	struct vio_msg_tag *pkt = arg;	printk(KERN_ERR PFX "Received unknown msg [%02x:%02x:%04x:%08x]\n",	       pkt->type, pkt->stype, pkt->stype_env, pkt->sid);	printk(KERN_ERR PFX "Resetting connection.\n");	ldc_disconnect(port->vio.lp);	return -ECONNRESET;}static int vdc_send_attr(struct vio_driver_state *vio){	struct vdc_port *port = to_vdc_port(vio);	struct vio_disk_attr_info pkt;	memset(&pkt, 0, sizeof(pkt));	pkt.tag.type = VIO_TYPE_CTRL;	pkt.tag.stype = VIO_SUBTYPE_INFO;	pkt.tag.stype_env = VIO_ATTR_INFO;	pkt.tag.sid = vio_send_sid(vio);	pkt.xfer_mode = VIO_DRING_MODE;	pkt.vdisk_block_size = port->vdisk_block_size;	pkt.max_xfer_size = port->max_xfer_size;	viodbg(HS, "SEND ATTR xfer_mode[0x%x] blksz[%u] max_xfer[%lu]\n",	       pkt.xfer_mode, pkt.vdisk_block_size, pkt.max_xfer_size);	return vio_ldc_send(&port->vio, &pkt, sizeof(pkt));}static int vdc_handle_attr(struct vio_driver_state *vio, void *arg){	struct vdc_port *port = to_vdc_port(vio);	struct vio_disk_attr_info *pkt = arg;	viodbg(HS, "GOT ATTR stype[0x%x] ops[%lx] disk_size[%lu] disk_type[%x] "	       "xfer_mode[0x%x] blksz[%u] max_xfer[%lu]\n",	       pkt->tag.stype, pkt->operations,	       pkt->vdisk_size, pkt->vdisk_type,	       pkt->xfer_mode, pkt->vdisk_block_size,	       pkt->max_xfer_size);	if (pkt->tag.stype == VIO_SUBTYPE_ACK) {		switch (pkt->vdisk_type) {		case VD_DISK_TYPE_DISK:		case VD_DISK_TYPE_SLICE:			break;		default:			printk(KERN_ERR PFX "%s: Bogus vdisk_type 0x%x\n",			       vio->name, pkt->vdisk_type);			return -ECONNRESET;		}		if (pkt->vdisk_block_size > port->vdisk_block_size) {			printk(KERN_ERR PFX "%s: BLOCK size increased "			       "%u --> %u\n",			       vio->name,			       port->vdisk_block_size, pkt->vdisk_block_size);			return -ECONNRESET;		}		port->operations = pkt->operations;		port->vdisk_size = pkt->vdisk_size;		port->vdisk_type = pkt->vdisk_type;		if (pkt->max_xfer_size < port->max_xfer_size)			port->max_xfer_size = pkt->max_xfer_size;		port->vdisk_block_size = pkt->vdisk_block_size;		return 0;	} else {		printk(KERN_ERR PFX "%s: Attribute NACK\n", vio->name);		return -ECONNRESET;	}}static void vdc_end_special(struct vdc_port *port, struct vio_disk_desc *desc){	int err = desc->status;	vdc_finish(&port->vio, -err, WAITING_FOR_GEN_CMD);}static void vdc_end_request(struct request *req, int uptodate, int num_sectors){	if (end_that_request_first(req, uptodate, num_sectors))		return;	add_disk_randomness(req->rq_disk);	end_that_request_last(req, uptodate);}static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,			unsigned int index){	struct vio_disk_desc *desc = vio_dring_entry(dr, index);	struct vdc_req_entry *rqe = &port->rq_arr[index];	struct request *req;	if (unlikely(desc->hdr.state != VIO_DESC_DONE))		return;	ldc_unmap(port->vio.lp, desc->cookies, desc->ncookies);	desc->hdr.state = VIO_DESC_FREE;	dr->cons = (index + 1) & (VDC_TX_RING_SIZE - 1);	req = rqe->req;	if (req == NULL) {		vdc_end_special(port, desc);		return;	}	rqe->req = NULL;	vdc_end_request(req, !desc->status, desc->size >> 9);	if (blk_queue_stopped(port->disk->queue))		blk_start_queue(port->disk->queue);}static int vdc_ack(struct vdc_port *port, void *msgbuf){	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];	struct vio_dring_data *pkt = msgbuf;	if (unlikely(pkt->dring_ident != dr->ident ||		     pkt->start_idx != pkt->end_idx ||		     pkt->start_idx >= VDC_TX_RING_SIZE))		return 0;	vdc_end_one(port, dr, pkt->start_idx);	return 0;}static int vdc_nack(struct vdc_port *port, void *msgbuf){	/* XXX Implement me XXX */	return 0;}static void vdc_event(void *arg, int event){	struct vdc_port *port = arg;	struct vio_driver_state *vio = &port->vio;	unsigned long flags;	int err;	spin_lock_irqsave(&vio->lock, flags);	if (unlikely(event == LDC_EVENT_RESET ||		     event == LDC_EVENT_UP)) {		vio_link_state_change(vio, event);		spin_unlock_irqrestore(&vio->lock, flags);		return;	}	if (unlikely(event != LDC_EVENT_DATA_READY)) {		printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event);		spin_unlock_irqrestore(&vio->lock, flags);		return;	}	err = 0;	while (1) {		union {			struct vio_msg_tag tag;			u64 raw[8];		} msgbuf;		err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf));		if (unlikely(err < 0)) {			if (err == -ECONNRESET)				vio_conn_reset(vio);			break;		}		if (err == 0)			break;		viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n",		       msgbuf.tag.type,		       msgbuf.tag.stype,		       msgbuf.tag.stype_env,		       msgbuf.tag.sid);		err = vio_validate_sid(vio, &msgbuf.tag);		if (err < 0)			break;		if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) {			if (msgbuf.tag.stype == VIO_SUBTYPE_ACK)				err = vdc_ack(port, &msgbuf);			else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK)				err = vdc_nack(port, &msgbuf);			else				err = vdc_handle_unknown(port, &msgbuf);		} else if (msgbuf.tag.type == VIO_TYPE_CTRL) {			err = vio_control_pkt_engine(vio, &msgbuf);		} else {			err = vdc_handle_unknown(port, &msgbuf);		}		if (err < 0)			break;	}	if (err < 0)		vdc_finish(&port->vio, err, WAITING_FOR_ANY);	spin_unlock_irqrestore(&vio->lock, flags);}static int __vdc_tx_trigger(struct vdc_port *port){	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];	struct vio_dring_data hdr = {		.tag = {			.type		= VIO_TYPE_DATA,			.stype		= VIO_SUBTYPE_INFO,			.stype_env	= VIO_DRING_DATA,			.sid		= vio_send_sid(&port->vio),		},		.dring_ident		= dr->ident,		.start_idx		= dr->prod,		.end_idx		= dr->prod,	};	int err, delay;	hdr.seq = dr->snd_nxt;	delay = 1;	do {		err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr));		if (err > 0) {			dr->snd_nxt++;			break;		}		udelay(delay);		if ((delay <<= 1) > 128)			delay = 128;	} while (err == -EAGAIN);	return err;}static int __send_request(struct request *req){	struct vdc_port *port = req->rq_disk->private_data;	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];	struct scatterlist sg[port->ring_cookies];	struct vdc_req_entry *rqe;	struct vio_disk_desc *desc;	unsigned int map_perm;	int nsg, err, i;	u64 len;	u8 op;	map_perm = LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO;	if (rq_data_dir(req) == READ) {		map_perm |= LDC_MAP_W;		op = VD_OP_BREAD;	} else {		map_perm |= LDC_MAP_R;		op = VD_OP_BWRITE;	}	sg_init_table(sg, port->ring_cookies);	nsg = blk_rq_map_sg(req->q, req, sg);	len = 0;	for (i = 0; i < nsg; i++)		len += sg[i].length;	if (unlikely(vdc_tx_dring_avail(dr) < 1)) {		blk_stop_queue(port->disk->queue);		err = -ENOMEM;		goto out;	}	desc = vio_dring_cur(dr);	err = ldc_map_sg(port->vio.lp, sg, nsg,			 desc->cookies, port->ring_cookies,			 map_perm);	if (err < 0) {		printk(KERN_ERR PFX "ldc_map_sg() failure, err=%d.\n", err);		return err;	}	rqe = &port->rq_arr[dr->prod];	rqe->req = req;	desc->hdr.ack = VIO_ACK_ENABLE;	desc->req_id = port->req_id;	desc->operation = op;	if (port->vdisk_type == VD_DISK_TYPE_DISK) {		desc->slice = 0xff;	} else {		desc->slice = 0;	}	desc->status = ~0;	desc->offset = (req->sector << 9) / port->vdisk_block_size;	desc->size = 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) {		printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err);	} else {		port->req_id++;		dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);	}out:

⌨️ 快捷键说明

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