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 + -
显示快捷键?