[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [linux-2.6.18-xen] pvSCSI frontend driver
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1212397136 -3600 # Node ID 3b3701ad4eece5b99e88394d15846c2fc54a4506 # Parent 66faefe721ebb4c9490f3b06b653386d9d2ad4af pvSCSI frontend driver Signed-off-by: Tomonari Horikoshi <t.horikoshi@xxxxxxxxxxxxxx> Signed-off-by: Jun Kamada <kama@xxxxxxxxxxxxxx> --- buildconfigs/linux-defconfig_xen0_ia64 | 1 buildconfigs/linux-defconfig_xen0_x86_32 | 1 buildconfigs/linux-defconfig_xen0_x86_64 | 1 buildconfigs/linux-defconfig_xenU_ia64 | 1 buildconfigs/linux-defconfig_xenU_x86_32 | 1 buildconfigs/linux-defconfig_xenU_x86_64 | 1 buildconfigs/linux-defconfig_xen_ia64 | 1 buildconfigs/linux-defconfig_xen_x86_32 | 1 buildconfigs/linux-defconfig_xen_x86_64 | 1 drivers/xen/Kconfig | 8 drivers/xen/Makefile | 1 drivers/xen/scsifront/Makefile | 3 drivers/xen/scsifront/common.h | 132 +++++++ drivers/xen/scsifront/scsifront.c | 519 +++++++++++++++++++++++++++++++ drivers/xen/scsifront/xenbus.c | 446 ++++++++++++++++++++++++++ 15 files changed, 1118 insertions(+) diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xen0_ia64 --- a/buildconfigs/linux-defconfig_xen0_ia64 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xen0_ia64 Mon Jun 02 09:58:56 2008 +0100 @@ -1684,6 +1684,7 @@ CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_FRAMEBUFFER=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xen0_x86_32 --- a/buildconfigs/linux-defconfig_xen0_x86_32 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xen0_x86_32 Mon Jun 02 09:58:56 2008 +0100 @@ -1421,6 +1421,7 @@ CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_SCRUB_PAGES=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xen0_x86_64 --- a/buildconfigs/linux-defconfig_xen0_x86_64 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xen0_x86_64 Mon Jun 02 09:58:56 2008 +0100 @@ -1362,6 +1362,7 @@ CONFIG_XEN_PCIDEV_BACKEND_PASS=y # CONFIG_XEN_SCSI_BACKEND is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_SCRUB_PAGES=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xenU_ia64 --- a/buildconfigs/linux-defconfig_xenU_ia64 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xenU_ia64 Mon Jun 02 09:58:56 2008 +0100 @@ -1492,6 +1492,7 @@ CONFIG_XEN_XENBUS_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m # CONFIG_XEN_GRANT_DEV is not set # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_FRAMEBUFFER=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xenU_x86_32 --- a/buildconfigs/linux-defconfig_xenU_x86_32 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xenU_x86_32 Mon Jun 02 09:58:56 2008 +0100 @@ -916,6 +916,7 @@ CONFIG_XEN_XENBUS_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m # CONFIG_XEN_GRANT_DEV is not set # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_SCRUB_PAGES=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xenU_x86_64 --- a/buildconfigs/linux-defconfig_xenU_x86_64 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xenU_x86_64 Mon Jun 02 09:58:56 2008 +0100 @@ -878,6 +878,7 @@ CONFIG_XEN_XENBUS_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_SCRUB_PAGES=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xen_ia64 --- a/buildconfigs/linux-defconfig_xen_ia64 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xen_ia64 Mon Jun 02 09:58:56 2008 +0100 @@ -1684,6 +1684,7 @@ CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_FRAMEBUFFER=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xen_x86_32 --- a/buildconfigs/linux-defconfig_xen_x86_32 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xen_x86_32 Mon Jun 02 09:58:56 2008 +0100 @@ -3279,6 +3279,7 @@ CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_FRAMEBUFFER=y diff -r 66faefe721eb -r 3b3701ad4eec buildconfigs/linux-defconfig_xen_x86_64 --- a/buildconfigs/linux-defconfig_xen_x86_64 Mon Jun 02 09:58:27 2008 +0100 +++ b/buildconfigs/linux-defconfig_xen_x86_64 Mon Jun 02 09:58:56 2008 +0100 @@ -3109,6 +3109,7 @@ CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_SCSI_BACKEND=m CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_SCSI_FRONTEND=m CONFIG_XEN_GRANT_DEV=y # CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND is not set CONFIG_XEN_FRAMEBUFFER=y diff -r 66faefe721eb -r 3b3701ad4eec drivers/xen/Kconfig --- a/drivers/xen/Kconfig Mon Jun 02 09:58:27 2008 +0100 +++ b/drivers/xen/Kconfig Mon Jun 02 09:58:56 2008 +0100 @@ -199,6 +199,14 @@ config XEN_NETDEV_ACCEL_SFC_FRONTEND select XEN_NETDEV_ACCEL_SFC_UTIL default m +config XEN_SCSI_FRONTEND + tristate "SCSI frontend driver" + depends on SCSI + default m + help + The SCSI frontend driver allows the kernel to access SCSI Devices + within another guest OS. + config XEN_GRANT_DEV tristate "User-space granted page access driver" default XEN_PRIVILEGED_GUEST diff -r 66faefe721eb -r 3b3701ad4eec drivers/xen/Makefile --- a/drivers/xen/Makefile Mon Jun 02 09:58:27 2008 +0100 +++ b/drivers/xen/Makefile Mon Jun 02 09:58:56 2008 +0100 @@ -17,6 +17,7 @@ obj-$(CONFIG_XEN_FRAMEBUFFER) += fbfron obj-$(CONFIG_XEN_FRAMEBUFFER) += fbfront/ obj-$(CONFIG_XEN_KEYBOARD) += fbfront/ obj-$(CONFIG_XEN_SCSI_BACKEND) += scsiback/ +obj-$(CONFIG_XEN_SCSI_FRONTEND) += scsifront/ obj-$(CONFIG_XEN_PRIVCMD) += privcmd/ obj-$(CONFIG_XEN_GRANT_DEV) += gntdev/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL) += sfc_netutil/ diff -r 66faefe721eb -r 3b3701ad4eec drivers/xen/scsifront/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/Makefile Mon Jun 02 09:58:56 2008 +0100 @@ -0,0 +1,3 @@ + +obj-$(CONFIG_XEN_SCSI_FRONTEND) := xenscsi.o +xenscsi-objs := scsifront.o xenbus.o diff -r 66faefe721eb -r 3b3701ad4eec drivers/xen/scsifront/common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/common.h Mon Jun 02 09:58:56 2008 +0100 @@ -0,0 +1,132 @@ +/* + * Xen SCSI frontend driver + * + * Copyright (c) 2008, FUJITSU Limited + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __XEN_DRIVERS_SCSIFRONT_H__ +#define __XEN_DRIVERS_SCSIFRONT_H__ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/kthread.h> +#include <linux/wait.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/blkdev.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <xen/xenbus.h> +#include <xen/gnttab.h> +#include <xen/evtchn.h> +#include <xen/interface/xen.h> +#include <xen/interface/io/ring.h> +#include <xen/interface/io/vscsiif.h> +#include <asm/delay.h> + + +#define GRANT_INVALID_REF 0 +#define VSCSI_IN_ABORT 1 +#define VSCSI_IN_RESET 2 + +/* tuning point*/ +#define VSCSIIF_DEFAULT_CMD_PER_LUN 10 +#define VSCSIIF_MAX_TARGET 64 +#define VSCSIIF_MAX_LUN 255 + +#define VSCSIIF_RING_SIZE \ + __RING_SIZE((struct vscsiif_sring *)0, PAGE_SIZE) +#define VSCSIIF_MAX_REQS VSCSIIF_RING_SIZE + +struct vscsifrnt_shadow { + uint16_t next_free; + + /* command between backend and frontend + * VSCSIIF_ACT_SCSI_CDB or VSCSIIF_ACT_SCSI_RESET */ + unsigned char act; + + /* do reset function */ + wait_queue_head_t wq_reset; /* reset work queue */ + int wait_reset; /* reset work queue condition */ + int32_t rslt_reset; /* reset response status */ + /* (SUCESS or FAILED) */ + + /* for DMA_TO_DEVICE(1), DMA_FROM_DEVICE(2), DMA_NONE(3) + requests */ + unsigned int sc_data_direction; + + /* Number of pieces of scatter-gather */ + unsigned int nr_segments; + + /* requested struct scsi_cmnd is stored from kernel */ + unsigned long req_scsi_cmnd; + int gref[VSCSIIF_SG_TABLESIZE]; +}; + +struct vscsifrnt_info { + struct xenbus_device *dev; + + struct Scsi_Host *host; + + spinlock_t io_lock; + spinlock_t shadow_lock; + unsigned int evtchn; + unsigned int irq; + + grant_ref_t ring_ref; + struct vscsiif_front_ring ring; + struct vscsiif_response ring_res; + + struct vscsifrnt_shadow shadow[VSCSIIF_MAX_REQS]; + uint32_t shadow_free; + + struct task_struct *kthread; + wait_queue_head_t wq; + unsigned int waiting_resp; + + /* abort reset condition bit*/ + atomic_t abort_reset_cond; + +}; + +#define DPRINTK(_f, _a...) \ + pr_debug("(file=%s, line=%d) " _f, \ + __FILE__ , __LINE__ , ## _a ) + +int scsifront_xenbus_init(void); +void scsifront_xenbus_unregister(void); +int scsifront_schedule(void *data); +irqreturn_t scsifront_intr(int irq, void *dev_id, struct pt_regs *ptregs); +int scsifront_cmd_done(struct vscsifrnt_info *info); + + +#endif /* __XEN_DRIVERS_SCSIFRONT_H__ */ diff -r 66faefe721eb -r 3b3701ad4eec drivers/xen/scsifront/scsifront.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/scsifront.c Mon Jun 02 09:58:56 2008 +0100 @@ -0,0 +1,519 @@ +/* + * Xen SCSI frontend driver + * + * Copyright (c) 2008, FUJITSU Limited + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#include <linux/version.h> +#include "common.h" + +static int get_id_from_freelist(struct vscsifrnt_info *info) +{ + unsigned long flags; + uint32_t free; + + spin_lock_irqsave(&info->shadow_lock, flags); + + free = info->shadow_free; + BUG_ON(free > VSCSIIF_MAX_REQS); + info->shadow_free = info->shadow[free].next_free; + info->shadow[free].next_free = 0x0fff; + + info->shadow[free].wait_reset = 0; + + spin_unlock_irqrestore(&info->shadow_lock, flags); + + return free; +} + +static void add_id_to_freelist(struct vscsifrnt_info *info, uint32_t id) +{ + unsigned long flags; + + spin_lock_irqsave(&info->shadow_lock, flags); + + info->shadow[id].next_free = info->shadow_free; + info->shadow[id].req_scsi_cmnd = 0; + info->shadow_free = id; + + spin_unlock_irqrestore(&info->shadow_lock, flags); +} + + +struct vscsiif_request * scsifront_pre_request(struct vscsifrnt_info *info) +{ + struct vscsiif_front_ring *ring = &(info->ring); + vscsiif_request_t *ring_req; + uint32_t id; + + ring_req = RING_GET_REQUEST(&(info->ring), ring->req_prod_pvt); + + ring->req_prod_pvt++; + + id = get_id_from_freelist(info); /* use id by response */ + ring_req->rqid = (uint16_t)id; + + return ring_req; +} + + +static void scsifront_notify_work(struct vscsifrnt_info *info) +{ + info->waiting_resp = 1; + wake_up(&info->wq); +} + + +static void scsifront_do_request(struct vscsifrnt_info *info) +{ + struct vscsiif_front_ring *ring = &(info->ring); + unsigned int irq = info->irq; + int notify; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify); + if (notify) + notify_remote_via_irq(irq); +} + +irqreturn_t scsifront_intr(int irq, void *dev_id, struct pt_regs *ptregs) +{ + scsifront_notify_work((struct vscsifrnt_info *)dev_id); + return IRQ_HANDLED; +} + + +static void scsifront_gnttab_done(struct vscsifrnt_shadow *s, uint32_t id) +{ + int i; + + if (s->sc_data_direction == DMA_NONE) + return; + + if (s->nr_segments) { + for (i = 0; i < s->nr_segments; i++) { + if (unlikely(gnttab_query_foreign_access( + s->gref[i]) != 0)) { + printk(KERN_ALERT "scsifront: " + "grant still in use by backend.\n"); + BUG(); + } + gnttab_end_foreign_access(s->gref[i], 0UL); + } + } + + return; +} + + +static void scsifront_cdb_cmd_done(struct vscsifrnt_info *info, + vscsiif_response_t *ring_res) +{ + struct scsi_cmnd *sc; + uint32_t id; + uint8_t sense_len; + + id = ring_res->rqid; + sc = (struct scsi_cmnd *)info->shadow[id].req_scsi_cmnd; + + if (sc == NULL) + BUG(); + + scsifront_gnttab_done(&info->shadow[id], id); + add_id_to_freelist(info, id); + + rmb(); + if (atomic_read(&info->abort_reset_cond) == VSCSI_IN_RESET) { + sc->result = (DID_RESET << 16); + } else { + sc->result = ring_res->rslt; + } + + sc->resid = 0; + + if (ring_res->sense_len > VSCSIIF_SENSE_BUFFERSIZE) + sense_len = VSCSIIF_SENSE_BUFFERSIZE; + else + sense_len = ring_res->sense_len; + + if (sense_len) + memcpy(sc->sense_buffer, ring_res->sense_buffer, sense_len); + + sc->scsi_done(sc); + + return; +} + + +static void scsifront_sync_cmd_done(struct vscsifrnt_info *info, + vscsiif_response_t *ring_res) +{ + uint16_t id = ring_res->rqid; + unsigned long flags; + + spin_lock_irqsave(&info->shadow_lock, flags); + info->shadow[id].wait_reset = 1; + info->shadow[id].rslt_reset = ring_res->rslt; + spin_unlock_irqrestore(&info->shadow_lock, flags); + + wake_up(&(info->shadow[id].wq_reset)); +} + + +int scsifront_cmd_done(struct vscsifrnt_info *info) +{ + vscsiif_response_t *ring_res; + + RING_IDX i, rp; + int more_to_do = 0; + unsigned long flags; + + spin_lock_irqsave(&info->io_lock, flags); + + rp = info->ring.sring->rsp_prod; + rmb(); + for (i = info->ring.rsp_cons; i != rp; i++) { + + ring_res = RING_GET_RESPONSE(&info->ring, i); + + if (info->shadow[ring_res->rqid].act == VSCSIIF_ACT_SCSI_CDB) + scsifront_cdb_cmd_done(info, ring_res); + else + scsifront_sync_cmd_done(info, ring_res); + } + + info->ring.rsp_cons = i; + + if (i != info->ring.req_prod_pvt) { + RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do); + } else { + info->ring.sring->rsp_event = i + 1; + } + + spin_unlock_irqrestore(&info->io_lock, flags); + + return more_to_do; +} + + + + +int scsifront_schedule(void *data) +{ + struct vscsifrnt_info *info = (struct vscsifrnt_info *)data; + + while (!kthread_should_stop()) { + wait_event_interruptible( + info->wq, + info->waiting_resp || kthread_should_stop()); + + info->waiting_resp = 0; + smp_mb(); + + if (scsifront_cmd_done(info)) + info->waiting_resp = 1; + } + + info->kthread = NULL; + + return 0; +} + + + +static int map_data_for_request(struct vscsifrnt_info *info, + struct scsi_cmnd *sc, vscsiif_request_t *ring_req, uint32_t id) +{ + grant_ref_t gref_head; + struct page *page; + int err, i, ref, ref_cnt = 0; + int write = (sc->sc_data_direction == DMA_TO_DEVICE); + int nr_pages, off, len, bytes; + unsigned long buffer_pfn; + unsigned int data_len = 0; + + if (sc->sc_data_direction == DMA_NONE) + return 0; + + err = gnttab_alloc_grant_references(VSCSIIF_SG_TABLESIZE, &gref_head); + if (err) { + printk(KERN_ERR "scsifront: gnttab_alloc_grant_references() error\n"); + return -ENOMEM; + } + + if (sc->use_sg) { + /* quoted scsi_lib.c/scsi_req_map_sg . */ + struct scatterlist *sg = (struct scatterlist *)sc->request_buffer; + nr_pages = (sc->request_bufflen + sg[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT; + + if (nr_pages > VSCSIIF_SG_TABLESIZE) { + printk(KERN_ERR "scsifront: Unable to map request_buffer for command!\n"); + ref_cnt = (-E2BIG); + goto big_to_sg; + } + + for (i = 0; i < sc->use_sg; i++) { + page = sg[i].page; + off = sg[i].offset; + len = sg[i].length; + data_len += len; + + buffer_pfn = page_to_phys(page) >> PAGE_SHIFT; + + while (len > 0) { + bytes = min_t(unsigned int, len, PAGE_SIZE - off); + + ref = gnttab_claim_grant_reference(&gref_head); + BUG_ON(ref == -ENOSPC); + + gnttab_grant_foreign_access_ref(ref, info->dev->otherend_id, + buffer_pfn, write); + + info->shadow[id].gref[ref_cnt] = ref; + ring_req->seg[ref_cnt].gref = ref; + ring_req->seg[ref_cnt].offset = (uint16_t)off; + ring_req->seg[ref_cnt].length = (uint16_t)bytes; + + buffer_pfn++; + len -= bytes; + off = 0; + ref_cnt++; + } + } + } else if (sc->request_bufflen) { + unsigned long end = ((unsigned long)sc->request_buffer + + sc->request_bufflen + PAGE_SIZE - 1) >> PAGE_SHIFT; + unsigned long start = (unsigned long)sc->request_buffer >> PAGE_SHIFT; + + page = virt_to_page(sc->request_buffer); + nr_pages = end - start; + len = sc->request_bufflen; + + if (nr_pages > VSCSIIF_SG_TABLESIZE) { + ref_cnt = (-E2BIG); + goto big_to_sg; + } + + buffer_pfn = page_to_phys(page) >> PAGE_SHIFT; + + off = offset_in_page((unsigned long)sc->request_buffer); + for (i = 0; i < nr_pages; i++) { + bytes = PAGE_SIZE - off; + + if (bytes > len) + bytes = len; + + ref = gnttab_claim_grant_reference(&gref_head); + BUG_ON(ref == -ENOSPC); + + gnttab_grant_foreign_access_ref(ref, info->dev->otherend_id, + buffer_pfn, write); + + info->shadow[id].gref[i] = ref; + ring_req->seg[i].gref = ref; + ring_req->seg[i].offset = (uint16_t)off; + ring_req->seg[i].length = (uint16_t)bytes; + + buffer_pfn++; + len -= bytes; + off = 0; + ref_cnt++; + } + } + +big_to_sg: + + gnttab_free_grant_references(gref_head); + + return ref_cnt; +} + +static int scsifront_queuecommand(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)) +{ + struct vscsifrnt_info *info = + (struct vscsifrnt_info *) sc->device->host->hostdata; + vscsiif_request_t *ring_req; + int ref_cnt; + uint16_t rqid; + + if (RING_FULL(&info->ring)) { + goto out_host_busy; + } + + sc->scsi_done = done; + sc->result = 0; + + ring_req = scsifront_pre_request(info); + rqid = ring_req->rqid; + ring_req->act = VSCSIIF_ACT_SCSI_CDB; + + ring_req->id = sc->device->id; + ring_req->lun = sc->device->lun; + ring_req->channel = sc->device->channel; + ring_req->cmd_len = sc->cmd_len; + + BUG_ON(sc->cmd_len > VSCSIIF_MAX_COMMAND_SIZE); + + if ( sc->cmd_len ) + memcpy(ring_req->cmnd, sc->cmnd, sc->cmd_len); + else + memset(ring_req->cmnd, 0, VSCSIIF_MAX_COMMAND_SIZE); + + ring_req->sc_data_direction = (uint8_t)sc->sc_data_direction; + ring_req->timeout_per_command = (sc->timeout_per_command / HZ); + + info->shadow[rqid].req_scsi_cmnd = (unsigned long)sc; + info->shadow[rqid].sc_data_direction = sc->sc_data_direction; + info->shadow[rqid].act = ring_req->act; + + ref_cnt = map_data_for_request(info, sc, ring_req, rqid); + if (ref_cnt < 0) { + add_id_to_freelist(info, rqid); + if (ref_cnt == (-ENOMEM)) + goto out_host_busy; + else { + sc->result = (DID_ERROR << 16); + goto out_fail_command; + } + } + + ring_req->nr_segments = (uint8_t)ref_cnt; + info->shadow[rqid].nr_segments = ref_cnt; + + scsifront_do_request(info); + + return 0; + +out_host_busy: + return SCSI_MLQUEUE_HOST_BUSY; + +out_fail_command: + done(sc); + return 0; +} + + +static int scsifront_eh_abort_handler(struct scsi_cmnd *sc) +{ + return (FAILED); +} + +/* vscsi supports only device_reset, because it is each of LUNs */ +static int scsifront_dev_reset_handler(struct scsi_cmnd *sc) +{ + struct Scsi_Host *host = sc->device->host; + struct vscsifrnt_info *info = + (struct vscsifrnt_info *) sc->device->host->hostdata; + + vscsiif_request_t *ring_req; + int err; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) + spin_lock_irq(host->host_lock); +#endif + + atomic_set(&info->abort_reset_cond, VSCSI_IN_RESET); + while (RING_HAS_UNCONSUMED_RESPONSES(&info->ring)) { + if (!scsifront_cmd_done(info)) + break; + } + + ring_req = scsifront_pre_request(info); + ring_req->act = VSCSIIF_ACT_SCSI_RESET; + + info->shadow[ring_req->rqid].act = VSCSIIF_ACT_SCSI_RESET; + + ring_req->channel = sc->device->channel; + ring_req->id = sc->device->id; + ring_req->lun = sc->device->lun; + ring_req->cmd_len = sc->cmd_len; + + if ( sc->cmd_len ) + memcpy(ring_req->cmnd, sc->cmnd, sc->cmd_len); + else + memset(ring_req->cmnd, 0, VSCSIIF_MAX_COMMAND_SIZE); + + ring_req->sc_data_direction = (uint8_t)sc->sc_data_direction; + ring_req->timeout_per_command = (sc->timeout_per_command / HZ); + ring_req->nr_segments = 0; + + spin_unlock_irq(host->host_lock); + scsifront_do_request(info); + wait_event_interruptible(info->shadow[ring_req->rqid].wq_reset, + info->shadow[ring_req->rqid].wait_reset); + + spin_lock_irq(host->host_lock); + + err = info->shadow[ring_req->rqid].rslt_reset; + atomic_set(&info->abort_reset_cond, 0); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) + spin_unlock_irq(host->host_lock); +#endif + + return (err); +} + + +struct scsi_host_template scsifront_sht = { + .module = THIS_MODULE, + .name = "Xen SCSI frontend driver", + .queuecommand = scsifront_queuecommand, + .eh_abort_handler = scsifront_eh_abort_handler, + .eh_device_reset_handler= scsifront_dev_reset_handler, + .cmd_per_lun = VSCSIIF_DEFAULT_CMD_PER_LUN, + .can_queue = VSCSIIF_MAX_REQS, + .this_id = -1, + .sg_tablesize = VSCSIIF_SG_TABLESIZE, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "scsifront", +}; + + +static int __init scsifront_init(void) +{ + int err; + + if (!is_running_on_xen()) + return -ENODEV; + + err = scsifront_xenbus_init(); + + return err; +} + +static void __exit scsifront_exit(void) +{ + scsifront_xenbus_unregister(); +} + +module_init(scsifront_init); +module_exit(scsifront_exit); + +MODULE_DESCRIPTION("Xen SCSI frontend driver"); +MODULE_LICENSE("GPL"); diff -r 66faefe721eb -r 3b3701ad4eec drivers/xen/scsifront/xenbus.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/xenbus.c Mon Jun 02 09:58:56 2008 +0100 @@ -0,0 +1,446 @@ +/* + * Xen SCSI frontend driver + * + * Copyright (c) 2008, FUJITSU Limited + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#include <linux/version.h> +#include "common.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + #define DEFAULT_TASK_COMM_LEN 16 +#else + #define DEFAULT_TASK_COMM_LEN TASK_COMM_LEN +#endif + +extern struct scsi_host_template scsifront_sht; + +static void scsifront_free(struct vscsifrnt_info *info) +{ + struct Scsi_Host *host = info->host; + + if (scsi_host_get(host) != NULL) + scsi_host_put(host); + + if (info->ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->ring_ref, + (unsigned long)info->ring.sring); + info->ring_ref = GRANT_INVALID_REF; + info->ring.sring = NULL; + } + + if (info->irq) + unbind_from_irqhandler(info->irq, info); + info->irq = 0; +} + + +static int scsifront_alloc_ring(struct vscsifrnt_info *info) +{ + struct xenbus_device *dev = info->dev; + struct vscsiif_sring *sring; + int err = -ENOMEM; + + + info->ring_ref = GRANT_INVALID_REF; + + /***** Frontend to Backend ring start *****/ + sring = (struct vscsiif_sring *) __get_free_page(GFP_KERNEL); + if (!sring) { + xenbus_dev_fatal(dev, err, "fail to allocate shared ring (Front to Back)"); + return err; + } + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(sring)); + if (err < 0) { + free_page((unsigned long) sring); + info->ring.sring = NULL; + xenbus_dev_fatal(dev, err, "fail to grant shared ring (Front to Back)"); + goto free_sring; + } + info->ring_ref = err; + + err = bind_listening_port_to_irqhandler( + dev->otherend_id, scsifront_intr, + SA_SAMPLE_RANDOM, "scsifront", info); + + if (err <= 0) { + xenbus_dev_fatal(dev, err, "bind_listening_port_to_irqhandler"); + goto fail; + } + info->irq = err; + + return 0; +fail: + /* free resource */ +free_sring: + scsifront_free(info); + + return err; +} + + +static int scsifront_init_ring(struct vscsifrnt_info *info) +{ + struct xenbus_device *dev = info->dev; + struct xenbus_transaction xbt; + int err; + + DPRINTK("%s\n",__FUNCTION__); + + err = scsifront_alloc_ring(info); + if (err) + return err; + DPRINTK("%u %u\n", info->ring_ref, info->evtchn); + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + xenbus_dev_fatal(dev, err, "starting transaction"); + } + + err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u", + info->ring_ref); + if (err) { + xenbus_dev_fatal(dev, err, "%s", "writing ring-ref"); + goto fail; + } + + err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + irq_to_evtchn_port(info->irq)); + + if (err) { + xenbus_dev_fatal(dev, err, "%s", "writing event-channel"); + goto fail; + } + + err = xenbus_printf(xbt, dev->nodename, "vhostno", "%u", + info->host->host_no); + if (err) { + xenbus_dev_fatal(dev, err, "%s", "writing vhostno"); + goto fail; + } + + err = xenbus_transaction_end(xbt, 0); + if (err) { + if (err == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, err, "completing transaction"); + } else + xenbus_switch_state(dev, XenbusStateInitialised); + + return 0; + +fail: + xenbus_transaction_end(xbt, 1); + /* free resource */ + scsifront_free(info); + + return err; +} + + +static int scsifront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + struct vscsifrnt_info *info; + struct Scsi_Host *host; + int i, err = -ENOMEM; + char name[DEFAULT_TASK_COMM_LEN]; + + host = scsi_host_alloc(&scsifront_sht, sizeof(*info)); + if (!host) { + xenbus_dev_fatal(dev, err, "fail to allocate scsi host"); + return err; + } + info = (struct vscsifrnt_info *) host->hostdata; + info->host = host; + + + dev->dev.driver_data = info; + info->dev = dev; + + for (i = 0; i < VSCSIIF_MAX_REQS; i++) { + info->shadow[i].next_free = i + 1; + init_waitqueue_head(&(info->shadow[i].wq_reset)); + info->shadow[i].wait_reset = 0; + } + info->shadow[VSCSIIF_MAX_REQS - 1].next_free = 0x0fff; + + atomic_set(&info->abort_reset_cond, 0); + + err = scsifront_init_ring(info); + if (err) { + scsi_host_put(host); + return err; + } + + init_waitqueue_head(&info->wq); + spin_lock_init(&info->io_lock); + spin_lock_init(&info->shadow_lock); + + snprintf(name, DEFAULT_TASK_COMM_LEN, "vscsiif.%d", info->host->host_no); + + info->kthread = kthread_run(scsifront_schedule, info, name); + if (IS_ERR(info->kthread)) { + err = PTR_ERR(info->kthread); + info->kthread = NULL; + } + + host->max_id = VSCSIIF_MAX_TARGET; + host->max_channel = 0; + host->max_lun = VSCSIIF_MAX_LUN; + host->max_sectors = (VSCSIIF_SG_TABLESIZE * PAGE_SIZE / 512); + + err = scsi_add_host(host, &dev->dev); + if (err) { + printk(KERN_ERR "scsifront: fail to add scsi host %d\n", err); + return err; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + + #if 0 + /* All SCSI device scan */ + scsi_scan_host(host); + + err = xenbus_printf(XBT_NIL, dev->nodename, "hotplug-status", "%s", + "connected"); + if (err) { + xenbus_dev_fatal(dev, err, "%s", "writing hotplug-status"); + return err; + } + #endif + return 0; +} + +static int scsifront_remove(struct xenbus_device *dev) +{ + struct vscsifrnt_info *info = dev->dev.driver_data; + + DPRINTK("%s: %s removed\n",__FUNCTION__ ,dev->nodename); + + if (info->kthread) { + kthread_stop(info->kthread); + info->kthread = NULL; + } + + scsifront_free(info); + + return 0; +} + + +static int scsifront_disconnect(struct vscsifrnt_info *info) +{ + struct xenbus_device *dev = info->dev; + struct Scsi_Host *host = info->host; + + unsigned long flags; + + DPRINTK("%s: %s disconnect\n",__FUNCTION__ ,dev->nodename); + + spin_lock_irqsave(host->host_lock, flags); + while (RING_HAS_UNCONSUMED_RESPONSES(&info->ring)) { + if (!scsifront_cmd_done(info)) + break; + } + + spin_unlock_irqrestore(host->host_lock, flags); + + spin_lock(&info->io_lock); + + scsi_remove_host(host); + scsi_host_put(host); + + spin_unlock(&info->io_lock); + + + xenbus_frontend_closed(dev); + + return 0; +} + +#define VSCSIFRONT_OP_ADD_LUN 1 +#define VSCSIFRONT_OP_DEL_LUN 2 + +static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op) +{ + struct xenbus_device *dev = info->dev; + int i, err = 0; + char str[64], state_str[64]; + char **dir; + unsigned int dir_n = 0; + unsigned int device_state; + unsigned int hst, chn, tgt, lun; + struct scsi_device *sdev; + + dir = xenbus_directory(XBT_NIL, dev->otherend, "vscsi-devs", &dir_n); + if (IS_ERR(dir)) + return; + + for (i = 0; i < dir_n; i++) { + /* read status */ + snprintf(str, sizeof(str), "vscsi-devs/%s/state", dir[i]); + err = xenbus_scanf(XBT_NIL, dev->otherend, str, "%u", + &device_state); + if (XENBUS_EXIST_ERR(err)) + goto fail; + + /* virtual SCSI device */ + snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", dir[i]); + err = xenbus_scanf(XBT_NIL, dev->otherend, str, + "%u:%u:%u:%u", &hst, &chn, &tgt, &lun); + if (XENBUS_EXIST_ERR(err)) + goto fail; + + /* front device state path */ + snprintf(state_str, sizeof(state_str), "vscsi-devs/%s/state", dir[i]); + + switch (op) { + case VSCSIFRONT_OP_ADD_LUN: + if (device_state == XenbusStateInitialised) { + sdev = scsi_device_lookup(info->host, chn, tgt, lun); + if (sdev) { + xenbus_printf(XBT_NIL, dev->nodename, + state_str, "%d", XenbusStateClosing); + } else { + scsi_add_device(info->host, chn, tgt, lun); + xenbus_printf(XBT_NIL, dev->nodename, + state_str, "%d", XenbusStateInitialised); + } + } + break; + case VSCSIFRONT_OP_DEL_LUN: + if (device_state == XenbusStateClosing) { + sdev = scsi_device_lookup(info->host, chn, tgt, lun); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + xenbus_printf(XBT_NIL, dev->nodename, + state_str, "%d", XenbusStateClosing); + } else { + err = PTR_ERR(sdev); + goto scsi_del_fail; + } + } + break; + default: + goto fail; + break; + } + } + + kfree(dir); + return; + +fail: + kfree(dir); + xenbus_dev_fatal(dev, err, "read or write %s ", str); + return; + +scsi_del_fail: + kfree(dir); + printk(KERN_ERR "scsifront: fail to del scsi lun %d\n", err); + return; + +} + + + + +static void scsifront_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct vscsifrnt_info *info = dev->dev.driver_data; + + DPRINTK("%p %u %u\n", dev, dev->state, backend_state); + + switch (backend_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateClosed: + break; + + case XenbusStateInitialised: + break; + + case XenbusStateConnected: + if (xenbus_read_driver_state(dev->nodename) == + XenbusStateInitialised) { + scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN); + } + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateClosing: + scsifront_disconnect(info); + break; + + case XenbusStateReconfiguring: + scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_DEL_LUN); + xenbus_switch_state(dev, XenbusStateReconfiguring); + break; + + case XenbusStateReconfigured: + scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN); + xenbus_switch_state(dev, XenbusStateConnected); + break; + } +} + + +static struct xenbus_device_id scsifront_ids[] = { + { "vscsi" }, + { "" } +}; + + +static struct xenbus_driver scsifront_driver = { + .name = "vscsi", + .owner = THIS_MODULE, + .ids = scsifront_ids, + .probe = scsifront_probe, + .remove = scsifront_remove, +/* .resume = scsifront_resume, */ + .otherend_changed = scsifront_backend_changed, +}; + +int scsifront_xenbus_init(void) +{ + return xenbus_register_frontend(&scsifront_driver); +} + +void scsifront_xenbus_unregister(void) +{ + xenbus_unregister_driver(&scsifront_driver); +} + _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |