# HG changeset patch
# User fujita.tomonori@xxxxxxxxxxxxx
# Node ID 4e1a4618df3a66d8100b3f50c96fafe523858440
# Parent 7111077b493ea53ef055ce38098f8af67f87d749
SCSI backend driver
Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx>
diff -r 7111077b493e -r 4e1a4618df3a
linux-2.6-xen-sparse/drivers/xen/scsiback/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsiback/Makefile Wed Aug 02
15:15:04 2006 +0900
@@ -0,0 +1,2 @@
+obj-$(CONFIG_XEN_SCSI_BACKEND) += scsibk.o
+scsibk-y += interface.o libsrp.o scsiback.o
diff -r 7111077b493e -r 4e1a4618df3a
linux-2.6-xen-sparse/drivers/xen/scsiback/interface.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsiback/interface.c Wed Aug 02
15:15:04 2006 +0900
@@ -0,0 +1,153 @@
+/******************************************************************************
+ * arch/xen/drivers/blkif/backend/interface.c
+ *
+ * Block-device interface management.
+ *
+ * Copyright (c) 2004, Keir Fraser
+ *
+ * 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 <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/driver_util.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>
+#include "scsiback_priv.h"
+
+static int map_frontend_page(struct scsiback_info *info, unsigned long
shared_page)
+{
+ struct gnttab_map_grant_ref op;
+ int err;
+
+ gnttab_set_map_op(&op, (unsigned long)info->ring_area->addr,
+ GNTMAP_host_map, shared_page,
+ info->dev->otherend_id);
+
+ lock_vm_area(info->ring_area);
+ err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1);
+ unlock_vm_area(info->ring_area);
+ BUG_ON(err);
+
+ if (op.status) {
+ printk(" Grant table operation failure !\n");
+ return op.status;
+ }
+
+ info->shmem_ref = shared_page;
+ info->shmem_handle = op.handle;
+
+#ifdef CONFIG_XEN_IA64_DOM0_NON_VP
+ /* on some arch's, map_grant_ref behaves like mmap, in that the
+ * passed address is a hint and a different address may be returned */
+ info->ring_area->addr = gnttab_map_vaddr(op);
+#endif
+
+ return 0;
+}
+
+static void unmap_frontend_page(struct scsiback_info *info)
+{
+ struct gnttab_unmap_grant_ref op;
+ int err;
+
+ op.host_addr = (unsigned long)info->ring_area->addr;
+ op.handle = info->shmem_handle;
+ op.dev_bus_addr = 0;
+
+ lock_vm_area(info->ring_area);
+ err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1);
+ unlock_vm_area(info->ring_area);
+ BUG_ON(err);
+}
+
+int scsiback_init_sring(struct scsiback_info *info,
+ unsigned long shared_page, unsigned int evtchn)
+{
+ struct scsi_sring *sring;
+ int err;
+ struct evtchn_bind_interdomain bind_interdomain;
+
+ if (info->irq) {
+ printk("Already connected through?\n");
+ return 0;
+ }
+
+ info->ring_area = alloc_vm_area(PAGE_SIZE);
+ if (!info)
+ return -ENOMEM;
+
+ err = map_frontend_page(info, shared_page);
+ if (err)
+ goto free_vm;
+
+ bind_interdomain.remote_dom = info->dev->otherend_id;
+ bind_interdomain.remote_port = evtchn;
+
+ err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
+ &bind_interdomain);
+ if (err)
+ goto unmap_page;
+
+ info->evtchn = bind_interdomain.local_port;
+
+ sring = (struct scsi_sring *) info->ring_area->addr;
+ BACK_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+ info->irq = bind_evtchn_to_irqhandler(info->evtchn, scsiback_intr,
+ 0, "scsi-backend", info);
+ return 0;
+
+unmap_page:
+ unmap_frontend_page(info);
+free_vm:
+ free_vm_area(info->ring_area);
+ return err;
+}
+
+void scsiback_exit_sring(struct scsiback_info *info)
+{
+ /* Already disconnected? */
+ if (info->irq)
+ unbind_from_irqhandler(info->irq, info);
+
+ if (info->ring.sring) {
+ unmap_frontend_page(info);
+ free_vm_area(info->ring_area);
+ }
+}
diff -r 7111077b493e -r 4e1a4618df3a
linux-2.6-xen-sparse/drivers/xen/scsiback/libsrp.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsiback/libsrp.c Wed Aug 02
15:15:04 2006 +0900
@@ -0,0 +1,264 @@
+/*
+ * SCSI RDAM Protocol lib functions
+ *
+ * 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/err.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_tgt.h>
+#include <scsi/srp.h>
+#include "libsrp.h"
+
+enum srp_task_attributes {
+ SRP_SIMPLE_TASK = 0,
+ SRP_HEAD_TASK = 1,
+ SRP_ORDERED_TASK = 2,
+ SRP_ACA_TASK = 4
+};
+
+/* tmp - will replace with SCSI logging stuff */
+#define eprintk(fmt, args...) \
+do { \
+ printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \
+} while (0)
+#define dprintk eprintk
+/* #define dprintk(fmt, args...) */
+
+static int data_out_desc_size(struct srp_cmd *cmd)
+{
+ int size = 0;
+ u8 fmt = cmd->buf_fmt >> 4;
+
+ switch (fmt) {
+ case SRP_NO_DATA_DESC:
+ break;
+ case SRP_DATA_DESC_DIRECT:
+ size = sizeof(struct srp_direct_buf);
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ size = sizeof(struct srp_indirect_buf) +
+ sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt;
+ break;
+ default:
+ eprintk("client error. Invalid data_out_format %x\n", fmt);
+ break;
+ }
+ return size;
+}
+
+int srp_rw(struct srp_cmd *cmd)
+{
+ return (cmd->buf_fmt >> 4) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+}
+
+static u8 srp_format(struct srp_cmd *cmd)
+{
+ if (srp_rw(cmd) == DMA_TO_DEVICE)
+ return cmd->buf_fmt >> 4;
+ else
+ return cmd->buf_fmt & ((1U << 4) - 1);
+}
+
+static int srp_offset(struct srp_cmd *cmd)
+{
+ int offset, rw;
+
+ offset = cmd->add_cdb_len * 4;
+ rw = srp_rw(cmd);
+ if (rw == DMA_FROM_DEVICE)
+ offset += data_out_desc_size(cmd);
+
+ return offset;
+}
+
+int srp_nmd(struct srp_cmd *cmd)
+{
+ struct srp_indirect_buf *id;
+ u8 format;
+ int rw, nmd, offset;
+
+ rw = srp_rw(cmd);
+ offset = srp_offset(cmd);
+ format = srp_format(cmd);
+ switch (format) {
+ case SRP_NO_DATA_DESC:
+ nmd = 0;
+ break;
+ case SRP_DATA_DESC_DIRECT:
+ nmd = 1;
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ id = (struct srp_indirect_buf *)(cmd->add_data + offset);
+ nmd = id->table_desc.len / sizeof(struct srp_direct_buf);
+ break;
+ default:
+ eprintk("Unknown format %x\n", format);
+ nmd = -EINVAL;
+ break;
+ }
+
+ return nmd;
+}
+
+static struct srp_direct_buf *srp_md(struct srp_cmd *cmd, int idx)
+{
+ struct srp_direct_buf *md = NULL;
+ struct srp_indirect_buf *id;
+ int rw, nmd, offset;
+ u8 format;
+
+ rw = srp_rw(cmd);
+ offset = srp_offset(cmd);
+ format = srp_format(cmd);
+ switch (format) {
+ case SRP_DATA_DESC_DIRECT:
+ md = (struct srp_direct_buf *)(cmd->add_data + offset);
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ id = (struct srp_indirect_buf *)
+ (cmd->add_data + offset);
+ nmd = id->table_desc.len / sizeof(struct srp_direct_buf);
+
+ /* This should be true for Xen scsifront */
+ if ((rw == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) ||
+ (rw == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt))
+ md = &id->desc_list[0];
+ else
+ BUG_ON(1);
+
+ md = &md[idx];
+ break;
+ case SRP_NO_DATA_DESC:
+ default:
+ eprintk("invalid %x\n", format);
+ break;
+ }
+
+ return md;
+}
+
+u64 srp_key(struct srp_cmd *cmd, int idx)
+{
+ struct srp_direct_buf *md = NULL;
+
+ md = srp_md(cmd, idx);
+ return md ? md->key : 0;
+}
+
+u64 srp_addr(struct srp_cmd *cmd, int idx)
+{
+ struct srp_direct_buf *md = NULL;
+
+ md = srp_md(cmd, idx);
+ return md ? md->va : 0;
+}
+
+u32 srp_len(struct srp_cmd *cmd, int idx)
+{
+ struct srp_direct_buf *md = NULL;
+
+ md = srp_md(cmd, idx);
+ return md ? md->len : 0;
+}
+
+static int vscsis_data_length(struct srp_cmd *cmd, enum dma_data_direction dir)
+{
+ struct srp_direct_buf *md;
+ struct srp_indirect_buf *id;
+ int len = 0, offset = cmd->add_cdb_len * 4;
+ u8 fmt;
+
+ if (dir == DMA_TO_DEVICE)
+ fmt = cmd->buf_fmt >> 4;
+ else {
+ fmt = cmd->buf_fmt & ((1U << 4) - 1);
+ offset += data_out_desc_size(cmd);
+ }
+
+ switch (fmt) {
+ case SRP_NO_DATA_DESC:
+ break;
+ case SRP_DATA_DESC_DIRECT:
+ md = (struct srp_direct_buf *) (cmd->add_data + offset);
+ len = md->len;
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ id = (struct srp_indirect_buf *) (cmd->add_data + offset);
+ len = id->len;
+ break;
+ default:
+ eprintk("invalid data format %x\n", fmt);
+ break;
+ }
+ return len;
+}
+
+int srp_cmd_perform(struct Scsi_Host *host, struct srp_cmd *cmd, void *data,
u64 uaddr)
+{
+ enum dma_data_direction data_dir;
+ struct scsi_cmnd *sc;
+ int tag, len;
+
+ tag = MSG_SIMPLE_TAG;
+
+ switch (cmd->task_attr) {
+ case SRP_SIMPLE_TASK:
+ tag = MSG_SIMPLE_TAG;
+ break;
+ case SRP_ORDERED_TASK:
+ tag = MSG_ORDERED_TAG;
+ break;
+ case SRP_HEAD_TASK:
+ tag = MSG_HEAD_TAG;
+ break;
+ default:
+ eprintk("Task attribute %d not supported\n", cmd->task_attr);
+ tag = MSG_ORDERED_TAG;
+ }
+
+ if (cmd->buf_fmt >> 4)
+ data_dir = DMA_TO_DEVICE;
+ else
+ data_dir = DMA_FROM_DEVICE;
+ len = vscsis_data_length(cmd, data_dir);
+
+ dprintk("%x %llx %d %d %d %llx\n", cmd->cdb[0],
+ (unsigned long long) cmd->lun, data_dir,
+ len, tag, (unsigned long long) cmd->tag);
+
+ sc = scsi_host_get_command(host, data_dir, GFP_KERNEL);
+ BUG_ON(!sc);
+ memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE);
+ sc->request_bufflen = len;
+ sc->request_buffer = (void *) (unsigned long)uaddr;
+ sc->tag = tag;
+ sc->SCp.ptr = data;
+ sc->host_scribble = (void *) host;
+ scsi_tgt_queue_command(sc, (struct scsi_lun *) &cmd->lun, cmd->tag);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("SCSI RDAM Protocol lib functions");
+MODULE_AUTHOR("FUJITA Tomonori");
+MODULE_LICENSE("GPL");
diff -r 7111077b493e -r 4e1a4618df3a
linux-2.6-xen-sparse/drivers/xen/scsiback/libsrp.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsiback/libsrp.h Wed Aug 02
15:15:04 2006 +0900
@@ -0,0 +1,20 @@
+#ifndef __LIBSRP_H__
+#define __LIBSRP_H__
+
+#include <linux/list.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/srp.h>
+
+typedef int (rdma_io_t) (struct scsi_cmnd *, struct scatterlist *, int,
+ struct srp_direct_buf *, int, unsigned int);
+
+extern int srp_cmd_perform(struct Scsi_Host *, struct srp_cmd *, void *, u64);
+extern int srp_transfer_data(struct scsi_cmnd *, struct srp_cmd *, rdma_io_t);
+extern int srp_nmd(struct srp_cmd *);
+extern int srp_rw(struct srp_cmd *);
+extern u64 srp_key(struct srp_cmd *, int);
+extern u64 srp_addr(struct srp_cmd *, int);
+extern u32 srp_len(struct srp_cmd *, int);
+
+#endif
diff -r 7111077b493e -r 4e1a4618df3a
linux-2.6-xen-sparse/drivers/xen/scsiback/scsiback.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsiback/scsiback.c Wed Aug 02
15:15:04 2006 +0900
@@ -0,0 +1,579 @@
+/*
+ * Xen SCSI backend 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
+ *
+ * Based on the blktap driver code.
+ *
+ * Copyright (c) 2004-2005, Andrew Warfield and Julian Chesterfield
+ *
+ */
+
+#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/scsi_tgt.h>
+#include <scsi/srp.h>
+#include <xen/evtchn.h>
+#include <xen/balloon.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>
+#include "scsiback_priv.h"
+#include "libsrp.h"
+
+#define eprintk(fmt, args...) \
+do { \
+ printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \
+} while (0)
+
+#define dprintk eprintk
+
+static unsigned int debug = 0;
+static int major;
+static struct workqueue_struct *scsibkd;
+
+module_param(debug, int, 0644);
+
+static int req_index(struct scsiback_info *info, struct scsi_request *req)
+{
+ return (req - RING_GET_REQUEST(&info->ring, 0));
+}
+
+static int __idx(struct scsiback_info *info, struct scsi_request *req,
+ int idx)
+{
+ return req_index(info, req) * SRP_MAX_INDIRECT + idx;
+}
+
+static unsigned long vaddr(struct scsiback_info *info, u64 start,
+ struct scsi_request *req, int i)
+{
+ return start + (__idx(info, req, i) << PAGE_SHIFT);
+}
+
+static int scsiback_send_rsp(struct scsiback_info *info, struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct scsi_back_ring *ring = &info->ring;
+ struct scsi_response *rsp;
+ struct scsi_request *req = (struct scsi_request *) sc->SCp.ptr;
+ struct srp_cmd *cmd = (struct srp_cmd *) req->buf;
+ struct srp_rsp *srsp;
+ int notify;
+
+ rsp = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt);
+ srsp = (struct srp_rsp *) rsp->buf;
+ srsp->opcode = SRP_RSP;
+ srsp->tag = cmd->tag;
+ srsp->resp_data_len = 0;
+ srsp->status = NO_SENSE;
+ srsp->data_in_res_cnt = 0;
+ srsp->data_out_res_cnt = 0;
+ srsp->flags &= ~SRP_RSP_FLAG_RSPVALID;
+
+ ring->rsp_prod_pvt++;
+
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify);
+
+ notify_remote_via_irq(info->irq);
+
+ done(sc);
+ return 0;
+}
+
+static int scsiback_cmd_done(struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = (struct Scsi_Host *) sc->host_scribble;
+ struct scsiback_info *info = (struct scsiback_info *) host->hostdata;
+ struct scsi_request *req = (struct scsi_request *) sc->SCp.ptr;
+ struct srp_cmd *cmd = (struct srp_cmd *) req->buf;
+ struct vm_area_struct *vma = info->mmap_vma;
+ struct gnttab_unmap_grant_ref unmap[SRP_MAX_INDIRECT * 2];
+ int i, op, err, nmd, offset;
+
+ nmd = srp_nmd(cmd);
+ if (!nmd)
+ goto send_rsp;
+ for (op = i = 0; i < nmd; i++) {
+ u64 kaddr, uaddr, ptep;
+ struct page *page, **map = vma->vm_private_data;
+
+ uaddr = vaddr(info, info->ustart, req, i);
+ kaddr = vaddr(info, info->kstart, req, i);
+
+ page = pfn_to_page(__pa(kaddr) >> PAGE_SHIFT);
+ ClearPageReserved(page);
+ offset = (uaddr - vma->vm_start) >> PAGE_SHIFT;
+ map[offset] = NULL;
+
+ gnttab_set_unmap_op(&unmap[op++], kaddr, GNTMAP_host_map,
+ info->handle[__idx(info, req, i)].k);
+
+ err = create_lookup_pte_addr(vma->vm_mm, uaddr, &ptep);
+ BUG_ON(err); /* FIXME */
+
+ gnttab_set_unmap_op(&unmap[op++], ptep, GNTMAP_host_map,
+ info->handle[__idx(info, req, i)].u);
+ }
+ err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap, op);
+ BUG_ON(err);
+
+ zap_page_range(vma, vaddr(info, info->ustart, req, 0),
+ nmd << PAGE_SHIFT, NULL);
+
+send_rsp:
+ scsiback_send_rsp(info, sc, done);
+ return 0;
+}
+
+static int scsiback_eh_abort_handler(struct scsi_cmnd *scmd)
+{
+ BUG_ON(1);
+ return 0;
+}
+
+static struct scsi_host_template scsiback_sht = {
+ .module = THIS_MODULE,
+ .name = "scsiback",
+ .can_queue = SRP_CAN_QUEUE,
+ .sg_tablesize = SG_ALL,
+ .use_clustering = DISABLE_CLUSTERING,
+ .transfer_response = scsiback_cmd_done,
+ .eh_abort_handler = scsiback_eh_abort_handler,
+};
+
+static void scsiback_worker(void *data)
+{
+ struct scsiback_info *info = data;
+ struct scsi_back_ring *ring = &info->ring;
+ struct scsi_request *req;
+ struct srp_cmd *cmd;
+ struct scsi_iovec *vec;
+ struct vm_area_struct *vma = info->mmap_vma;
+ struct gnttab_map_grant_ref map[SRP_MAX_INDIRECT * 2];
+ struct page *page;
+ struct iovec *iov;
+ RING_IDX rc, rp;
+ int i, op, nmd, err;
+ u64 uaddr, kaddr, ptep;
+
+ rc = ring->req_cons;
+ rp = ring->sring->req_prod;
+ rmb();
+
+ while ((rc != rp) && !RING_REQUEST_CONS_OVERFLOW(ring, rc)) {
+ eprintk("%u %u\n", rc, rp);
+ req = RING_GET_REQUEST(ring, rc);
+ ring->req_cons = ++rc;
+ cmd = (struct srp_cmd *) req->buf;
+ nmd = srp_nmd(cmd);
+
+ eprintk("%d %x %x\n", nmd, cmd->opcode, cmd->cdb[0]);
+
+ vec = (struct scsi_iovec *)
+ (info->uring + sizeof(*vec) * req_index(info, req));
+ vec->iovcnt = nmd;
+ if (!nmd)
+ goto no_data;
+
+ for (op = i = 0; i < nmd; i++) {
+ u32 flags;
+ int dir;
+
+ uaddr = vaddr(info, info->ustart, req, i);
+ kaddr = vaddr(info, info->kstart, req, i);
+
+ eprintk("%d %llx %llx\n", i, uaddr, kaddr);
+
+ page = virt_to_page(kaddr);
+
+ dir = srp_rw(cmd);
+ flags = GNTMAP_host_map;
+ if (dir == DMA_TO_DEVICE)
+ flags |= GNTMAP_readonly;
+
+ gnttab_set_map_op(&map[op], kaddr, flags,
+ srp_key(cmd, i),
+ info->dev->otherend_id);
+ op++;
+
+ err = create_lookup_pte_addr(vma->vm_mm, uaddr, &ptep);
+ BUG_ON(err); /* FIXME */
+
+ flags = GNTMAP_host_map | GNTMAP_application_map
+ | GNTMAP_contains_pte;
+ if (dir == DMA_TO_DEVICE)
+ flags |= GNTMAP_readonly;
+
+ gnttab_set_map_op(&map[op], ptep, flags,
+ srp_key(cmd, i),
+ info->dev->otherend_id);
+ op++;
+ }
+
+ err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map,
op);
+ BUG_ON(err);
+
+ iov = vec->iov;
+ for (i = 0; i < nmd; i++) {
+ int offset, j, idx;
+
+ j = i * 2;
+ idx = __idx(info, req, i);
+
+ uaddr = vaddr(info, info->ustart, req, i);
+ kaddr = vaddr(info, info->kstart, req, i);
+
+ /* FIXME */
+ BUG_ON(map[j].status || map[j + 1].status);
+
+ info->handle[idx].k = map[j].handle;
+ info->handle[idx].u = map[j + 1].handle;
+ set_phys_to_machine(__pa(kaddr) >> PAGE_SHIFT,
+ FOREIGN_FRAME(map[j].dev_bus_addr
>> PAGE_SHIFT));
+ offset = (uaddr - vma->vm_start) >> PAGE_SHIFT;
+ page = pfn_to_page(__pa(kaddr) >> PAGE_SHIFT);
+ ((struct page **) vma->vm_private_data)[offset] = page;
+ SetPageReserved(page);
+
+ offset = srp_addr(cmd, i) & (PAGE_SIZE-1);
+ iov[i].iov_base = (void *) ((unsigned long) uaddr +
offset);
+ iov[i].iov_len = srp_len(cmd, i);
+
+ eprintk("%llx %d %p %d\n", uaddr, offset,
iov[i].iov_base,
+ iov[i].iov_len);
+ }
+
+ no_data:
+ srp_cmd_perform(info->host, cmd, req,
+ vma->vm_start + sizeof(*vec) * req_index(info,
req));
+ }
+}
+
+irqreturn_t scsiback_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct scsiback_info *info = (struct scsiback_info *) dev_id;
+
+ queue_work(scsibkd, &info->scsiback_work);
+
+ return IRQ_HANDLED;
+}
+
+static int scsiback_connect(struct scsiback_info *info)
+{
+ struct xenbus_device *dev = info->dev;
+ unsigned long ring_ref;
+ unsigned int evtchn;
+ int err;
+
+ err = xenbus_gather(XBT_NIL, dev->otherend, "ring-ref", "%lu",
+ &ring_ref, "event-channel", "%u", &evtchn, NULL);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "reading %s ring", dev->otherend);
+ return err;
+ }
+
+ return scsiback_init_sring(info, ring_ref, evtchn);
+}
+
+static void scsiback_frontend_changed(struct xenbus_device *dev,
+ enum xenbus_state frontend_state)
+{
+ struct scsiback_info *info = dev->dev.driver_data;
+ int err;
+
+ dprintk("%p %u %u\n", dev, dev->state, frontend_state);
+ switch (frontend_state) {
+ case XenbusStateInitialising:
+ break;
+
+ case XenbusStateInitialised:
+ case XenbusStateConnected:
+ if (dev->state == XenbusStateConnected)
+ break;
+
+ err = scsiback_connect(info);
+ if (err)
+ break;
+
+ err = xenbus_switch_state(dev, XenbusStateConnected);
+ if (err)
+ xenbus_dev_fatal(dev, err, "switching to Connected
state",
+ dev->nodename);
+ break;
+
+ case XenbusStateClosing:
+ xenbus_switch_state(dev, XenbusStateClosing);
+ break;
+
+ case XenbusStateClosed:
+ device_unregister(&dev->dev);
+ break;
+
+ case XenbusStateUnknown:
+ case XenbusStateInitWait:
+ default:
+ xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+ frontend_state);
+ break;
+ }
+}
+
+static void scsiback_backend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ struct scsiback_info *info =
+ container_of(watch, struct scsiback_info, backend_watch);
+
+ dprintk("%p %u\n", info->dev, info->dev->state);
+
+ /* TODO */
+}
+
+static int scsiback_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int i, err, nr = SRP_RING_PAGES + SRP_MAPPED_PAGES;
+ struct Scsi_Host *host;
+ struct scsiback_info *info;
+ struct page *page;
+ struct xenbus_transaction xbt;
+
+ dprintk("%p %d\n", dev, dev->otherend_id);
+
+ host = scsi_host_alloc(&scsiback_sht, sizeof(struct scsiback_info));
+ if (!host)
+ return -ENOMEM;
+ err = scsi_tgt_alloc_queue(host);
+ if (err)
+ goto put_host;
+
+ err = scsi_add_host(host, &dev->dev);
+ if (err)
+ goto put_host;
+
+ info = (struct scsiback_info *) host->hostdata;
+ dev->dev.driver_data = info;
+ info->dev = dev;
+ info->host = host;
+
+ info->uring = get_zeroed_page(GFP_KERNEL);
+ if (!info->uring)
+ goto put_host;
+
+ page = balloon_alloc_empty_page_range(nr);
+ if (!page)
+ goto free_ring;
+ SetPageReserved(virt_to_page(info->uring));
+
+ for (i = 0; i < nr; i++)
+ get_page(&page[i]);
+ info->kstart = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
+ info->mmap_page = page;
+
+ INIT_WORK(&info->scsiback_work, scsiback_worker, info);
+
+ err = xenbus_transaction_start(&xbt);
+ if (err)
+ eprintk("fail to transcation %d\n", err);
+
+ err = xenbus_printf(xbt, dev->nodename, "hostno", "%u", host->host_no);
+ if (err)
+ eprintk("fail to transcation %d\n", err);
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err)
+ eprintk("fail to transcation %d\n", err);
+
+ err = xenbus_watch_path2(dev, dev->nodename,
+ "scsi-host",
+ &info->backend_watch,
+ scsiback_backend_changed);
+ if (err)
+ goto free_page;
+
+ err = xenbus_switch_state(dev, XenbusStateInitWait);
+ if (err)
+ goto stop_watch;
+
+ return 0;
+
+stop_watch:
+ /* free resource */
+free_page:
+ balloon_dealloc_empty_page_range(info->mmap_page, nr);
+free_ring:
+ free_page(info->uring);
+put_host:
+ scsi_host_put(host);
+ return err;
+}
+
+static int scsiback_remove(struct xenbus_device *dev)
+{
+ struct scsiback_info *info = dev->dev.driver_data;
+ struct Scsi_Host *host = info->host;
+ struct vm_area_struct *vma = info->mmap_vma;
+
+ if (vma) {
+ zap_page_range(vma, vma->vm_start,
+ vma->vm_end - vma->vm_start, NULL);
+ info->mmap_vma = NULL;
+ }
+
+ free_page(info->uring);
+ balloon_dealloc_empty_page_range(info->mmap_page,
+ SRP_RING_PAGES + SRP_MAPPED_PAGES);
+ scsi_remove_host(host);
+ scsi_host_put(host);
+ return 0;
+}
+
+static int scsiback_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int i, err;
+ unsigned long nr;
+ unsigned int hostno = MINOR(filp->f_dentry->d_inode->i_rdev);
+ struct page **map;
+ struct scsiback_info *info;
+ struct Scsi_Host *host;
+
+ dprintk("%u start %lx, end %lx\n", hostno, vma->vm_start, vma->vm_end);
+
+ host = scsi_host_lookup(hostno);
+ if (!host) {
+ eprintk("no scsi host %d\n", hostno);
+ return -EAGAIN;
+ }
+ info = (struct scsiback_info *)host->hostdata;
+
+ /* TODO we need to prevent this scsi host from going away. */
+
+ vma->vm_flags |= VM_RESERVED;
+/* vma->vm_ops = &vscsiback_vm_ops; */
+
+ nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ if (nr != SRP_RING_PAGES + SRP_MAPPED_PAGES) {
+ eprintk("you _must_ map exactly %lu pages!\n", nr);
+ err = -EINVAL;
+ goto host_put;
+ }
+
+ /* not sure if I really need to do this... */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ err = remap_pfn_range(vma, vma->vm_start,
+ __pa(info->uring) >> PAGE_SHIFT,
+ PAGE_SIZE, vma->vm_page_prot);
+ if (err) {
+ eprintk("fail to map frontend ring %d!\n", err);
+ goto host_put;
+ }
+
+ /* Mark this VM as containing foreign pages, and set up mappings. */
+ map = kzalloc(nr * sizeof(struct page_struct *), GFP_KERNEL);
+ if (!map) {
+ eprintk("Couldn't alloc VM_FOREIGN map.\n");
+ err = -ENOMEM;
+ goto zap;
+ }
+
+ for (i = 0; i < nr; i++)
+ map[i] = NULL;
+
+ vma->vm_private_data = map;
+ vma->vm_flags |= VM_FOREIGN;
+
+ info->ustart = vma->vm_start + (SRP_RING_PAGES << PAGE_SHIFT);
+
+ dprintk("%u start %lx, end %lx ustart %llx\n",
+ hostno, vma->vm_start, vma->vm_end, info->ustart);
+
+ info->mmap_vma = vma;
+zap:
+ if (err)
+ zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start,
NULL);
+host_put:
+ scsi_host_put(host);
+
+ return err;
+}
+
+static struct file_operations scsiback_fops = {
+ .owner = THIS_MODULE,
+ .mmap = scsiback_mmap,
+};
+
+static struct xenbus_device_id scsiback_ids[] = {
+ { "scsi" },
+ { "" }
+};
+
+static struct xenbus_driver scsiback = {
+ .name = "scsi",
+ .owner = THIS_MODULE,
+ .ids = scsiback_ids,
+ .probe = scsiback_probe,
+ .remove = scsiback_remove,
+ .otherend_changed = scsiback_frontend_changed
+};
+
+static int __init scsiback_init(void)
+{
+ int err = -ENOMEM;
+
+ eprintk("%u %lu\n", SRP_MAPPED_PAGES, SRP_RING_PAGES);
+
+ if (!is_running_on_xen())
+ return -ENODEV;
+
+ major = register_chrdev(0, "scsiback", &scsiback_fops);
+ if (major < 0)
+ return major;
+
+ scsibkd = create_singlethread_workqueue("scsibkd");
+ if (!scsibkd)
+ goto free_dev;
+
+ err = xenbus_register_backend(&scsiback);
+ if (err)
+ goto destroy_wq;
+ return 0;
+free_dev:
+ unregister_chrdev(major, "scsiback");
+destroy_wq:
+ destroy_workqueue(scsibkd);
+ return err;
+}
+
+module_init(scsiback_init);
+
+MODULE_AUTHOR("FUJITA Tomonori");
+MODULE_DESCRIPTION("Xen SCSI backend driver");
+MODULE_LICENSE("GPL");
diff -r 7111077b493e -r 4e1a4618df3a
linux-2.6-xen-sparse/drivers/xen/scsiback/scsiback_priv.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/scsiback/scsiback_priv.h Wed Aug 02
15:15:04 2006 +0900
@@ -0,0 +1,36 @@
+struct grant_handle_pair {
+ grant_handle_t k;
+ grant_handle_t u;
+};
+
+struct scsiback_info {
+ struct xenbus_device *dev;
+ struct Scsi_Host *host;
+ struct xenbus_watch backend_watch;
+
+ unsigned int evtchn;
+ unsigned int irq;
+
+ struct scsi_back_ring ring;
+ struct vm_struct *ring_area;
+
+ grant_handle_t shmem_handle;
+ grant_ref_t shmem_ref;
+
+ struct work_struct scsiback_work;
+
+ /* Add something tgt code to support this kind of stuff? */
+ unsigned long uring;
+
+ struct vm_area_struct *mmap_vma;
+ struct page *mmap_page;
+ u64 kstart;
+ u64 ustart;
+
+ struct grant_handle_pair handle[SRP_CAN_QUEUE * SRP_MAX_INDIRECT];
+};
+
+extern irqreturn_t scsiback_intr(int, void *, struct pt_regs *);
+extern int scsiback_init_sring(struct scsiback_info *,
+ unsigned long, unsigned int);
+extern void scsiback_exit_sring(struct scsiback_info *);
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|