# HG changeset patch
# User fujita.tomonori@xxxxxxxxxxxxx
# Node ID c84cd764b0d7f8bcf911b0e44cc29742b8c760e9
# Parent 4e1a4618df3a66d8100b3f50c96fafe523858440
SCSI frontend driver
Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx>
diff -r 4e1a4618df3a -r c84cd764b0d7
linux-2.6-xen-sparse/drivers/xen/scsifront/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsifront/Makefile Wed Aug 02
15:15:36 2006 +0900
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_SCSI_FRONTEND) += scsifront.o
diff -r 4e1a4618df3a -r c84cd764b0d7
linux-2.6-xen-sparse/drivers/xen/scsifront/scsifront.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsifront/scsifront.c Wed Aug 02
15:15:36 2006 +0900
@@ -0,0 +1,473 @@
+/*
+ * Xen SCSI frontend driver
+ *
+ * Copyright (C) 2006 FUJITA Tomonori <tomof@xxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/uio.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/srp.h>
+#include <xen/evtchn.h>
+#include <xen/xenbus.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/io/scsi.h>
+#include <xen/interface/io/ring.h>
+#include <xen/interface/grant_table.h>
+#include <xen/gnttab.h>
+#include <asm/hypervisor.h>
+
+#define eprintk(fmt, args...) \
+do { \
+ printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \
+} while (0)
+
+#define dprintk eprintk
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+
+struct scsifront_info {
+ struct xenbus_device *dev;
+ struct Scsi_Host *host;
+ unsigned int evtchn;
+ unsigned int irq;
+ unsigned long ring_ref;
+ struct scsi_front_ring ring;
+};
+
+static int map_data_for_srp_cmd(struct scsifront_info *info,
+ struct scsi_cmnd *sc, struct srp_cmd *cmd)
+{
+ struct scatterlist *sg = sc->request_buffer;
+ struct srp_direct_buf *buf;
+ grant_ref_t gref_head;
+ int err, i, ref;
+ u8 fmt;
+
+ if (!sg || sc->sc_data_direction == DMA_NONE)
+ return 0;
+
+ err = gnttab_alloc_grant_references(SRP_MAX_INDIRECT, &gref_head);
+ if (err)
+ return -ENOMEM;
+
+ if (sc->use_sg == 1) {
+ buf = (void *) cmd->add_data;
+ fmt = SRP_DATA_DESC_DIRECT;
+
+ ref = gnttab_claim_grant_reference(&gref_head);
+ gnttab_grant_foreign_access_ref(ref, info->dev->otherend_id,
+ page_to_phys(sg->page) >>
PAGE_SHIFT, 0);
+
+ buf->va = sg->offset;
+ buf->key = ref;
+ buf->len = sg->length;
+ } else {
+ struct srp_indirect_buf *ind = (void *) cmd->add_data;
+ int total = 0;
+ fmt = SRP_DATA_DESC_INDIRECT;
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE)
+ cmd->data_out_desc_cnt = sc->use_sg;
+ else
+ cmd->data_in_desc_cnt = sc->use_sg;
+
+ ind->table_desc.va = (u64) (unsigned long)ind->desc_list;
+ ind->table_desc.key = 0;
+ ind->table_desc.len = sizeof(*buf) * sc->use_sg;
+
+ buf = (struct srp_direct_buf *) ind->desc_list;
+ for (i = 0; i < sc->use_sg; i++, sg++) {
+ ref = gnttab_claim_grant_reference(&gref_head);
+ gnttab_grant_foreign_access_ref(ref,
info->dev->otherend_id,
+ page_to_phys(sg->page)
>> PAGE_SHIFT,
+ 0);
+ buf[i].va = sg->offset;
+ buf[i].key = ref;
+ buf[i].len = sg->length;
+ total += sg->length;
+ }
+
+ ind->len = total;
+ }
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE)
+ cmd->buf_fmt = fmt << 4;
+ else
+ cmd->buf_fmt = fmt;
+
+ gnttab_free_grant_references(gref_head);
+
+ return 0;
+}
+
+static int scsifront_queuecommand(struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = sc->device->host;
+ struct scsifront_info *info = (struct scsifront_info *) host->hostdata;
+ struct scsi_request *ring_req;
+ struct scsi_front_ring *ring = &info->ring;
+ struct srp_cmd *cmd;
+ int err, notify;
+
+ if (info->dev->state != XenbusStateConnected || RING_FULL(ring)) {
+ eprintk("busy %u!\n", info->dev->state);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ sc->scsi_done = done;
+ sc->result = 0;
+
+ ring_req = RING_GET_REQUEST(ring, ring->req_prod_pvt);
+ cmd = (struct srp_cmd *) ring_req->buf;
+
+ memset(cmd, 0, SRP_MAX_IU_LEN);
+ cmd->opcode = SRP_CMD;
+ int_to_scsilun(sc->device->lun, (struct scsi_lun *) &cmd->lun);
+ cmd->tag = (long) sc;
+ memcpy(cmd->cdb, sc->cmnd, sc->cmd_len);
+
+ err = map_data_for_srp_cmd(info, sc, cmd);
+ if (err)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ ring->req_prod_pvt++;
+
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify);
+ notify_remote_via_irq(info->irq);
+
+ return 0;
+}
+
+static int scsifront_eh_abort_handler(struct scsi_cmnd *sc)
+{
+ BUG();
+ return 0;
+}
+
+static void scsifront_cmd_done(struct scsifront_info *info, int idx)
+{
+ struct scsi_front_ring *ring = &info->ring;
+ struct scsi_request *ring_req;
+ struct srp_cmd *cmd;
+ struct scsi_cmnd *sc;
+ struct srp_direct_buf *buf;
+ int i;
+
+ ring_req = RING_GET_REQUEST(ring, idx);
+ cmd = (struct srp_cmd *) ring_req->buf;
+ sc = (struct scsi_cmnd *) (long) cmd->tag;
+
+ if (!sc->request_buffer || (sc->sc_data_direction != DMA_TO_DEVICE &&
+ sc->sc_data_direction != DMA_FROM_DEVICE))
+ return;
+
+ if (sc->use_sg == 1) {
+ buf = (void *) cmd->add_data;
+ gnttab_end_foreign_access(buf->key, 0, 0UL);
+ } else {
+ struct srp_indirect_buf *ind = (void *) cmd->add_data;
+ buf = (struct srp_direct_buf *) ind->desc_list;
+
+ for (i = 0; i < sc->use_sg; i++, buf++)
+ gnttab_end_foreign_access(buf->key, 0, 0UL);
+ }
+}
+
+static irqreturn_t scsifront_intr(int irq, void *dev_id,
+ struct pt_regs *ptregs)
+{
+ struct scsifront_info *info = (struct scsifront_info *) dev_id;
+ struct scsi_front_ring *ring = &info->ring;
+ struct scsi_response *ring_res;
+ struct scsi_cmnd *sc;
+ struct srp_rsp *rsp;
+ int i, rp;
+
+ if (info->dev->state != XenbusStateConnected)
+ return IRQ_HANDLED;
+
+again:
+ rp = info->ring.sring->rsp_prod;
+ rmb();
+
+ for (i = info->ring.rsp_cons; i != rp; i++) {
+ ring_res = RING_GET_RESPONSE(ring, i);
+
+ rsp = (struct srp_rsp *) ring_res->buf;
+ sc = ((void *) (unsigned long) rsp->tag);
+ sc->result = rsp->status;
+
+ scsifront_cmd_done(info, i);
+
+ if (rsp->flags & SRP_RSP_FLAG_SNSVALID) {
+ memcpy(sc->sense_buffer, rsp->data +
+ be32_to_cpu(rsp->resp_data_len),
+ min_t(int, be32_to_cpu(rsp->sense_data_len),
+ SCSI_SENSE_BUFFERSIZE));
+ }
+
+ if (rsp->flags & (SRP_RSP_FLAG_DOOVER | SRP_RSP_FLAG_DOUNDER))
+ sc->resid = be32_to_cpu(rsp->data_out_res_cnt);
+ else if (rsp->flags & (SRP_RSP_FLAG_DIOVER |
SRP_RSP_FLAG_DIUNDER))
+ sc->resid = be32_to_cpu(rsp->data_in_res_cnt);
+
+ sc->scsi_done(sc);
+ }
+
+ info->ring.rsp_cons = i;
+ if (i != info->ring.req_prod_pvt) {
+ int more_to_do;
+ RING_FINAL_CHECK_FOR_RESPONSES(ring, more_to_do);
+ if (more_to_do)
+ goto again;
+ } else
+ ring->sring->rsp_event = i + 1;
+
+ return IRQ_HANDLED;
+}
+
+static int scsifront_alloc_ring(struct scsifront_info *info)
+{
+ struct xenbus_device *dev = info->dev;
+ struct scsi_sring *sring;
+ int err = -ENOMEM;
+
+ sring = (struct scsi_sring *) __get_free_page(GFP_KERNEL);
+ if (!sring) {
+ xenbus_dev_fatal(dev, err, "fail to allocate shared ring");
+ return err;
+ }
+
+ SHARED_RING_INIT(sring);
+ FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
+ dprintk("%u\n", RING_SIZE(&info->ring));
+
+ err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
+ if (err < 0) {
+ xenbus_dev_fatal(dev, err, "fail to grant shared ring");
+ goto free_sring;
+ }
+ info->ring_ref = err;
+
+ err = xenbus_alloc_evtchn(dev, &info->evtchn);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "fail to allocate evtchn");
+ return err;
+ }
+
+ err = bind_evtchn_to_irqhandler(info->evtchn, scsifront_intr,
+ SA_SAMPLE_RANDOM, "scsifront", info);
+ if (err <= 0) {
+ xenbus_dev_fatal(dev, err, "bind_evtchn_to_irqhandler failed");
+ goto fail;
+ }
+ info->irq = err;
+
+ return 0;
+fail:
+ /* free resource */
+free_sring:
+ free_page((unsigned long) sring);
+
+ return err;
+}
+
+static int scsifront_init_ring(struct scsifront_info *info)
+{
+ struct xenbus_device *dev = info->dev;
+ struct xenbus_transaction xbt;
+ int err;
+
+ dprintk("");
+
+ err = scsifront_alloc_ring(info);
+ if (err)
+ return err;
+ dprintk("%lu %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", "%lu",
+ 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",
+ info->evtchn);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "%s", "writing event-channel");
+ 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 */
+ return err;
+}
+
+static struct scsi_host_template scsifront_sht = {
+ .module = THIS_MODULE,
+ .name = "Xen SCSI frontend driver",
+ .queuecommand = scsifront_queuecommand,
+ .eh_abort_handler = scsifront_eh_abort_handler,
+ .cmd_per_lun = SRP_CAN_QUEUE,
+ .can_queue = SRP_CAN_QUEUE,
+ .this_id = -1,
+ .sg_tablesize = SRP_MAX_INDIRECT,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "scsifront",
+};
+
+static int scsifront_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ struct Scsi_Host *host;
+ struct scsifront_info *info;
+ int err = -ENOMEM;
+
+ host = scsi_host_alloc(&scsifront_sht, sizeof(*info));
+ if (!host) {
+ xenbus_dev_fatal(dev, err, "fail to allocate scsi host");
+ return err;
+ }
+ info = (struct scsifront_info *) host->hostdata;
+ dev->dev.driver_data = info;
+ info->dev = dev;
+ info->host = host;
+
+ err = scsifront_init_ring(info);
+ if (err)
+ scsi_host_put(host);
+
+ return err;
+}
+
+static int scsifront_connect(struct scsifront_info *info)
+{
+ struct xenbus_device *dev = info->dev;
+ struct Scsi_Host *host = info->host;
+ int err = -ENOMEM;
+
+ dprintk("%u\n", dev->state);
+ if (dev->state == XenbusStateConnected)
+ return 0;
+
+ xenbus_switch_state(dev, XenbusStateConnected);
+
+ host->max_id = 1;
+ host->max_channel = 0;
+
+ err = scsi_add_host(host, &dev->dev);
+ if (err) {
+ eprintk("fail to add scsi host %d\n", err);
+ return err;
+ }
+ scsi_scan_host(host);
+
+ return 0;
+}
+
+static int scsifront_remove(struct xenbus_device *dev)
+{
+ struct scsifront_info *info = dev->dev.driver_data;
+
+ scsi_remove_host(info->host);
+ scsi_host_put(info->host);
+
+ return 0;
+}
+
+static void scsifront_backend_changed(struct xenbus_device *dev,
+ XenbusState backend_state)
+{
+ struct scsifront_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 XenbusStateInitialised:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateConnected:
+ scsifront_connect(info);
+ break;
+
+ case XenbusStateClosing:
+ break;
+ }
+}
+
+static struct xenbus_device_id scsifront_ids[] = {
+ { "scsi" },
+ { "" }
+};
+
+static struct xenbus_driver scsifront = {
+ .name = "scsi",
+ .owner = THIS_MODULE,
+ .ids = scsifront_ids,
+ .probe = scsifront_probe,
+ .remove = scsifront_remove,
+/* .resume = scsifront_resume, */
+ .otherend_changed = scsifront_backend_changed,
+};
+
+static int __init scsifront_init(void)
+{
+ if (!is_running_on_xen())
+ return -ENODEV;
+
+ return xenbus_register_frontend(&scsifront);
+}
+static void scsifront_exit(void)
+{
+ return xenbus_unregister_driver(&scsifront);
+}
+
+module_init(scsifront_init);
+module_exit(scsifront_exit);
+
+MODULE_AUTHOR("FUJITA Tomonori");
+MODULE_DESCRIPTION("Xen SCSI frontend driver");
+MODULE_LICENSE("GPL");
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|