[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 2/6] scsifront driver



# 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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.