# HG changeset patch # User fujita.tomonori@lab.ntt.co.jp # Node ID 4e1a4618df3a66d8100b3f50c96fafe523858440 # Parent 7111077b493ea53ef055ce38098f8af67f87d749 SCSI backend driver Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> 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@acm.org> + * + * 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@acm.org> + * + * 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@lists.xensource.com http://lists.xensource.com/xen-devel