WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 6/6] add scsi target patch

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 6/6] add scsi target patch
From: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx>
Date: Wed, 02 Aug 2006 17:32:29 +0900
Delivery-date: Wed, 02 Aug 2006 01:37:24 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User fujita.tomonori@xxxxxxxxxxxxx
# Node ID 3749a0e2580a668bfb029fb0348e9f89660becd6
# Parent  840f33e54054270e3f4b9704111ed52bd381653b
Add scsi target patch

This patch includes the SCSI target framework (tgt) that adds target
driver support in Linux SCSI mid layer. This is a modified version of
the version included in the -mm tree.

diff -r 840f33e54054 -r 3749a0e2580a patches/linux-2.6.16.13/tgt.patch
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/linux-2.6.16.13/tgt.patch Wed Aug 02 15:03:05 2006 +0900
@@ -0,0 +1,3253 @@
+Subject: [PATCH] scsi target: add target support and IBM VIO driver
+
+---
+
+ block/ll_rw_blk.c                |   40 +-
+ block/scsi_ioctl.c               |    3 
+ drivers/scsi/Kconfig             |   30 +
+ drivers/scsi/Makefile            |    5 
+ drivers/scsi/hosts.c             |    5 
+ drivers/scsi/ibmvscsi/Makefile   |    2 
+ drivers/scsi/ibmvscsi/ibmvstgt.c |  943 ++++++++++++++++++++++++++++++++++++++
+ drivers/scsi/libsrp.c            |  450 ++++++++++++++++++
+ drivers/scsi/scsi.c              |   43 +-
+ drivers/scsi/scsi_lib.c          |   33 +
+ drivers/scsi/scsi_tgt_if.c       |  316 +++++++++++++
+ drivers/scsi/scsi_tgt_lib.c      |  707 ++++++++++++++++++++++++++++
+ drivers/scsi/scsi_tgt_priv.h     |   24 +
+ fs/bio.c                         |   19 -
+ include/linux/blkdev.h           |    3 
+ include/scsi/libsrp.h            |   75 +++
+ include/scsi/scsi_cmnd.h         |    8 
+ include/scsi/scsi_host.h         |   43 ++
+ include/scsi/scsi_tgt.h          |   17 +
+ include/scsi/scsi_tgt_if.h       |   91 ++++
+ 20 files changed, 2793 insertions(+), 64 deletions(-)
+ create mode 100644 drivers/scsi/ibmvscsi/ibmvstgt.c
+ create mode 100644 drivers/scsi/libsrp.c
+ create mode 100644 drivers/scsi/scsi_tgt_if.c
+ create mode 100644 drivers/scsi/scsi_tgt_lib.c
+ create mode 100644 drivers/scsi/scsi_tgt_priv.h
+ create mode 100644 include/scsi/libsrp.h
+ create mode 100644 include/scsi/scsi_tgt.h
+ create mode 100644 include/scsi/scsi_tgt_if.h
+
+f21c20da255c84fe2072df460048f379af3eeb29
+diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
+index 7eb36c5..4fe85d7 100644
+--- a/block/ll_rw_blk.c
++++ b/block/ll_rw_blk.c
+@@ -2351,19 +2351,20 @@ int blk_rq_map_user(request_queue_t *q, 
+       else
+               bio = bio_copy_user(q, uaddr, len, reading);
+ 
+-      if (!IS_ERR(bio)) {
+-              rq->bio = rq->biotail = bio;
+-              blk_rq_bio_prep(q, rq, bio);
++      if (IS_ERR(bio))
++              return PTR_ERR(bio);
+ 
+-              rq->buffer = rq->data = NULL;
+-              rq->data_len = len;
+-              return 0;
++      if (bio->bi_size != len) {
++              bio_endio(bio, bio->bi_size, 0);
++              bio_unmap_user(bio);
++              return -EINVAL;
+       }
+ 
+-      /*
+-       * bio is the err-ptr
+-       */
+-      return PTR_ERR(bio);
++      rq->bio = rq->biotail = bio;
++      blk_rq_bio_prep(q, rq, bio);
++      rq->buffer = rq->data = NULL;
++      rq->data_len = len;
++      return 0;
+ }
+ 
+ EXPORT_SYMBOL(blk_rq_map_user);
+@@ -2389,7 +2390,7 @@ EXPORT_SYMBOL(blk_rq_map_user);
+  *    unmapping.
+  */
+ int blk_rq_map_user_iov(request_queue_t *q, struct request *rq,
+-                      struct sg_iovec *iov, int iov_count)
++                      struct sg_iovec *iov, int iov_count, unsigned int len)
+ {
+       struct bio *bio;
+ 
+@@ -2403,6 +2404,12 @@ int blk_rq_map_user_iov(request_queue_t 
+       if (IS_ERR(bio))
+               return PTR_ERR(bio);
+ 
++      if (bio->bi_size != len) {
++              bio_endio(bio, bio->bi_size, 0);
++              bio_unmap_user(bio);
++              return -EINVAL;
++      }
++
+       rq->bio = rq->biotail = bio;
+       blk_rq_bio_prep(q, rq, bio);
+       rq->buffer = rq->data = NULL;
+@@ -2826,16 +2833,12 @@ static void init_request_from_bio(struct
+ 
+       req->errors = 0;
+       req->hard_sector = req->sector = bio->bi_sector;
+-      req->hard_nr_sectors = req->nr_sectors = bio_sectors(bio);
+-      req->current_nr_sectors = req->hard_cur_sectors = bio_cur_sectors(bio);
+-      req->nr_phys_segments = bio_phys_segments(req->q, bio);
+-      req->nr_hw_segments = bio_hw_segments(req->q, bio);
+-      req->buffer = bio_data(bio);    /* see ->buffer comment above */
+       req->waiting = NULL;
+-      req->bio = req->biotail = bio;
+       req->ioprio = bio_prio(bio);
+       req->rq_disk = bio->bi_bdev->bd_disk;
+       req->start_time = jiffies;
++
++      blk_rq_bio_prep(req->q, req, bio);
+ }
+ 
+ static int __make_request(request_queue_t *q, struct bio *bio)
+@@ -3487,9 +3490,6 @@ EXPORT_SYMBOL(end_request);
+ 
+ void blk_rq_bio_prep(request_queue_t *q, struct request *rq, struct bio *bio)
+ {
+-      /* first three bits are identical in rq->flags and bio->bi_rw */
+-      rq->flags |= (bio->bi_rw & 7);
+-
+       rq->nr_phys_segments = bio_phys_segments(q, bio);
+       rq->nr_hw_segments = bio_hw_segments(q, bio);
+       rq->current_nr_sectors = bio_cur_sectors(bio);
+diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
+index b33eda2..b77e185 100644
+--- a/block/scsi_ioctl.c
++++ b/block/scsi_ioctl.c
+@@ -274,7 +274,8 @@ static int sg_io(struct file *file, requ
+                       goto out;
+               }
+ 
+-              ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count);
++              ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count,
++                                        hdr->dxfer_len);
+               kfree(iov);
+       } else if (hdr->dxfer_len)
+               ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len);
+diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
+index a480a37..82234ec 100644
+--- a/drivers/scsi/Kconfig
++++ b/drivers/scsi/Kconfig
+@@ -27,6 +27,13 @@ config SCSI
+         However, do not compile this as a module if your root file system
+         (the one containing the directory /) is located on a SCSI device.
+ 
++config SCSI_TGT
++      tristate "SCSI target support"
++      depends on SCSI && NET && EXPERIMENTAL
++      ---help---
++        If you want to use SCSI target mode drivers enable this option.
++        If you choose M, the module will be called scsi_tgt.
++
+ config SCSI_PROC_FS
+       bool "legacy /proc/scsi/ support"
+       depends on SCSI && PROC_FS
+@@ -900,6 +907,20 @@ config SCSI_IBMVSCSI
+         To compile this driver as a module, choose M here: the
+         module will be called ibmvscsic.
+ 
++config SCSI_IBMVSCSIS
++      tristate "IBM Virtual SCSI Server support"
++      depends on PPC_PSERIES && SCSI_TGT && SCSI_SRP
++      help
++        This is the SRP target driver for IBM pSeries virtual environments.
++
++        The userspace component needed to initialize the driver and
++        documentation can be found:
++
++        http://stgt.berlios.de/
++
++        To compile this driver as a module, choose M here: the
++        module will be called ibmvstgt.
++
+ config SCSI_INITIO
+       tristate "Initio 9100U(W) support"
+       depends on PCI && SCSI
+@@ -1829,6 +1850,15 @@ config ZFCP
+           called zfcp. If you want to compile it as a module, say M here
+           and read <file:Documentation/modules.txt>.
+ 
++config SCSI_SRP
++      tristate "SCSI RDMA Protocol helper library"
++      depends on SCSI
++      help
++        If you wish to use SRP target drivers, say Y.
++
++        To compile this driver as a module, choose M here: the
++        module will be called libsrp.
++
+ endmenu
+ 
+ source "drivers/scsi/pcmcia/Kconfig"
+diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
+index 81803a1..b4eb854 100644
+--- a/drivers/scsi/Makefile
++++ b/drivers/scsi/Makefile
+@@ -21,6 +21,7 @@ CFLAGS_seagate.o =   -DARBITRATE -DPARIT
+ subdir-$(CONFIG_PCMCIA)               += pcmcia
+ 
+ obj-$(CONFIG_SCSI)            += scsi_mod.o
++obj-$(CONFIG_SCSI_TGT)                += scsi_tgt.o
+ 
+ obj-$(CONFIG_RAID_ATTRS)      += raid_class.o
+ 
+@@ -120,7 +121,9 @@ obj-$(CONFIG_SCSI_FCAL)            += fcal.o
+ obj-$(CONFIG_SCSI_LASI700)    += 53c700.o lasi700.o
+ obj-$(CONFIG_SCSI_NSP32)      += nsp32.o
+ obj-$(CONFIG_SCSI_IPR)                += ipr.o
++obj-$(CONFIG_SCSI_SRP)                += libsrp.o
+ obj-$(CONFIG_SCSI_IBMVSCSI)   += ibmvscsi/
++obj-$(CONFIG_SCSI_IBMVSCSIS)  += ibmvscsi/
+ obj-$(CONFIG_SCSI_SATA_AHCI)  += libata.o ahci.o
+ obj-$(CONFIG_SCSI_SATA_SVW)   += libata.o sata_svw.o
+ obj-$(CONFIG_SCSI_ATA_PIIX)   += libata.o ata_piix.o
+@@ -156,6 +159,8 @@ scsi_mod-y                 += scsi.o hosts.o scsi_ioct
+ scsi_mod-$(CONFIG_SYSCTL)     += scsi_sysctl.o
+ scsi_mod-$(CONFIG_SCSI_PROC_FS)       += scsi_proc.o
+ 
++scsi_tgt-y                    += scsi_tgt_lib.o scsi_tgt_if.o
++
+ sd_mod-objs   := sd.o
+ sr_mod-objs   := sr.o sr_ioctl.o sr_vendor.o
+ ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
+diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
+index dfcb96f..f8cce09 100644
+--- a/drivers/scsi/hosts.c
++++ b/drivers/scsi/hosts.c
+@@ -264,6 +264,11 @@ static void scsi_host_dev_release(struct
+       if (shost->work_q)
+               destroy_workqueue(shost->work_q);
+ 
++      if (shost->uspace_req_q) {
++              kfree(shost->uspace_req_q->queuedata);
++              scsi_free_queue(shost->uspace_req_q);
++      }
++
+       scsi_destroy_command_freelist(shost);
+       kfree(shost->shost_data);
+ 
+diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
+index 4e247b6..6ac0633 100644
+--- a/drivers/scsi/ibmvscsi/Makefile
++++ b/drivers/scsi/ibmvscsi/Makefile
+@@ -3,3 +3,5 @@ obj-$(CONFIG_SCSI_IBMVSCSI)    += ibmvscsic
+ ibmvscsic-y                   += ibmvscsi.o
+ ibmvscsic-$(CONFIG_PPC_ISERIES)       += iseries_vscsi.o 
+ ibmvscsic-$(CONFIG_PPC_PSERIES)       += rpa_vscsi.o 
++
++obj-$(CONFIG_SCSI_IBMVSCSIS)  += ibmvstgt.o
+diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c 
b/drivers/scsi/ibmvscsi/ibmvstgt.c
+new file mode 100644
+index 0000000..cf1e851
+--- /dev/null
++++ b/drivers/scsi/ibmvscsi/ibmvstgt.c
+@@ -0,0 +1,943 @@
++/*
++ * IBM eServer i/pSeries Virtual SCSI Target Driver
++ * Copyright (C) 2003-2005 Dave Boutcher (boutcher@xxxxxxxxxx) IBM Corp.
++ *                       Santiago Leon (santil@xxxxxxxxxx) IBM Corp.
++ *                       Linda Xie (lxie@xxxxxxxxxx) IBM Corp.
++ *
++ * Copyright (C) 2005-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <scsi/scsi.h>
++#include <scsi/scsi_host.h>
++#include <scsi/scsi_tgt.h>
++#include <scsi/libsrp.h>
++#include <asm/hvcall.h>
++#include <asm/iommu.h>
++#include <asm/prom.h>
++#include <asm/vio.h>
++
++#include "ibmvscsi.h"
++
++#define       INITIAL_SRP_LIMIT       16
++#define       DEFAULT_MAX_SECTORS     512
++
++#define       TGT_NAME        "ibmvstgt"
++
++/*
++ * Hypervisor calls.
++ */
++#define h_copy_rdma(l, sa, sb, da, db) \
++                      plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db)
++#define h_send_crq(ua, l, h) \
++                      plpar_hcall_norets(H_SEND_CRQ, ua, l, h)
++#define h_reg_crq(ua, tok, sz)\
++                      plpar_hcall_norets(H_REG_CRQ, ua, tok, sz);
++#define h_free_crq(ua) \
++                      plpar_hcall_norets(H_FREE_CRQ, ua);
++
++/* 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...)
++
++struct vio_port {
++      struct vio_dev *dma_dev;
++
++      struct crq_queue crq_queue;
++      struct work_struct crq_work;
++
++      unsigned long liobn;
++      unsigned long riobn;
++};
++
++static struct workqueue_struct *vtgtd;
++
++/*
++ * These are fixed for the system and come from the Open Firmware device tree.
++ * We just store them here to save getting them every time.
++ */
++static char system_id[64] = "";
++static char partition_name[97] = "UNKNOWN";
++static unsigned int partition_number = -1;
++
++static struct vio_port *target_to_port(struct srp_target *target)
++{
++      return (struct vio_port *) target->ldata;
++}
++
++static inline union viosrp_iu *vio_iu(struct iu_entry *iue)
++{
++      return (union viosrp_iu *) (iue->sbuf->buf);
++}
++
++static int send_iu(struct iu_entry *iue, uint64_t length, uint8_t format)
++{
++      struct srp_target *target = iue->target;
++      struct vio_port *vport = target_to_port(target);
++      long rc, rc1;
++      union {
++              struct viosrp_crq cooked;
++              uint64_t raw[2];
++      } crq;
++
++      /* First copy the SRP */
++      rc = h_copy_rdma(length, vport->liobn, iue->sbuf->dma,
++                       vport->riobn, iue->remote_token);
++
++      if (rc)
++              eprintk("Error %ld transferring data\n", rc);
++
++      crq.cooked.valid = 0x80;
++      crq.cooked.format = format;
++      crq.cooked.reserved = 0x00;
++      crq.cooked.timeout = 0x00;
++      crq.cooked.IU_length = length;
++      crq.cooked.IU_data_ptr = vio_iu(iue)->srp.rsp.tag;
++
++      if (rc == 0)
++              crq.cooked.status = 0x99;       /* Just needs to be non-zero */
++      else
++              crq.cooked.status = 0x00;
++
++      rc1 = h_send_crq(vport->dma_dev->unit_address, crq.raw[0], crq.raw[1]);
++
++      if (rc1) {
++              eprintk("%ld sending response\n", rc1);
++              return rc1;
++      }
++
++      return rc;
++}
++
++#define SRP_RSP_SENSE_DATA_LEN        18
++
++static int send_rsp(struct iu_entry *iue, unsigned char status,
++                  unsigned char asc)
++{
++      union viosrp_iu *iu = vio_iu(iue);
++      uint64_t tag = iu->srp.rsp.tag;
++
++      /* If the linked bit is on and status is good */
++      if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE))
++              status = 0x10;
++
++      memset(iu, 0, sizeof(struct srp_rsp));
++      iu->srp.rsp.opcode = SRP_RSP;
++      iu->srp.rsp.req_lim_delta = 1;
++      iu->srp.rsp.tag = tag;
++
++      if (test_bit(V_DIOVER, &iue->flags))
++              iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER;
++
++      iu->srp.rsp.data_in_res_cnt = 0;
++      iu->srp.rsp.data_out_res_cnt = 0;
++
++      iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID;
++
++      iu->srp.rsp.resp_data_len = 0;
++      iu->srp.rsp.status = status;
++      if (status) {
++              uint8_t *sense = iu->srp.rsp.data;
++
++              if (iue->scmd) {
++                      iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
++                      iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE;
++                      memcpy(sense, iue->scmd->sense_buffer,
++                             SCSI_SENSE_BUFFERSIZE);
++              } else {
++                      iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION;
++                      iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
++                      iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN;
++
++                      /* Valid bit and 'current errors' */
++                      sense[0] = (0x1 << 7 | 0x70);
++                      /* Sense key */
++                      sense[2] = status;
++                      /* Additional sense length */
++                      sense[7] = 0xa; /* 10 bytes */
++                      /* Additional sense code */
++                      sense[12] = asc;
++              }
++      }
++
++      send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN,
++              VIOSRP_SRP_FORMAT);
++
++      return 0;
++}
++
++static void handle_cmd_queue(struct srp_target *target)
++{
++      struct iu_entry *iue;
++      unsigned long flags;
++
++retry:
++      spin_lock_irqsave(&target->lock, flags);
++
++      list_for_each_entry(iue, &target->cmd_queue, ilist) {
++              if (!test_and_set_bit(V_FLYING, &iue->flags)) {
++                      spin_unlock_irqrestore(&target->lock, flags);
++                      srp_cmd_perform(iue, (struct srp_cmd *) iue->sbuf->buf);
++                      goto retry;
++              }
++      }
++
++      spin_unlock_irqrestore(&target->lock, flags);
++}
++
++static int ibmvstgt_rdma(struct iu_entry *iue, struct scatterlist *sg, int 
nsg,
++                       struct srp_direct_buf *md, int nmd,
++                       enum dma_data_direction dir, unsigned int rest)
++{
++      struct srp_target *target = iue->target;
++      struct vio_port *vport = target_to_port(target);
++      dma_addr_t token;
++      long err;
++      unsigned int done = 0;
++      int i, sidx, soff;
++
++      sidx = soff = 0;
++      token = sg_dma_address(sg + sidx);
++
++      for (i = 0; i < nmd && rest; i++) {
++              unsigned int mdone, mlen;
++
++              mlen = min(rest, md[i].len);
++              for (mdone = 0; mlen;) {
++                      int slen = min(sg_dma_len(sg + sidx) - soff, mlen);
++
++                      if (dir == DMA_TO_DEVICE)
++                              err = h_copy_rdma(slen,
++                                                vport->riobn,
++                                                md[i].va + mdone,
++                                                vport->liobn,
++                                                token + soff);
++                      else
++                              err = h_copy_rdma(slen,
++                                                vport->liobn,
++                                                token + soff,
++                                                vport->riobn,
++                                                md[i].va + mdone);
++
++                      if (err != H_SUCCESS) {
++                              eprintk("rdma error %d %d\n", dir, slen);
++                              goto out;
++                      }
++
++                      mlen -= slen;
++                      mdone += slen;
++                      soff += slen;
++                      done += slen;
++
++                      if (soff == sg_dma_len(sg + sidx)) {
++                              sidx++;
++                              soff = 0;
++                              token = sg_dma_address(sg + sidx);
++
++                              if (sidx > nsg) {
++                                      eprintk("out of sg %p %d %d\n",
++                                              iue, sidx, nsg);
++                                      goto out;
++                              }
++                      }
++              };
++
++              rest -= mlen;
++      }
++out:
++
++      return 0;
++}
++
++static int ibmvstgt_transfer_data(struct scsi_cmnd *scmd,
++                                void (*done)(struct scsi_cmnd *))
++{
++      struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr;
++      int err;
++
++      err = srp_transfer_data(scmd, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma);
++      done(scmd);
++
++      return err;
++}
++
++static int ibmvstgt_cmd_done(struct scsi_cmnd *scmd,
++                           void (*done)(struct scsi_cmnd *))
++{
++      unsigned long flags;
++      struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr;
++      struct srp_target *target = iue->target;
++
++      dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]);
++
++      spin_lock_irqsave(&target->lock, flags);
++      list_del(&iue->ilist);
++      spin_unlock_irqrestore(&target->lock, flags);
++
++      if (scmd->result != SAM_STAT_GOOD) {
++              eprintk("operation failed %p %d %x\n",
++                      iue, scmd->result, vio_iu(iue)->srp.cmd.cdb[0]);
++              send_rsp(iue, HARDWARE_ERROR, 0x00);
++      } else
++              send_rsp(iue, NO_SENSE, 0x00);
++
++      done(scmd);
++      srp_iu_put(iue);
++      return 0;
++}
++
++int send_adapter_info(struct iu_entry *iue,
++                    dma_addr_t remote_buffer, uint16_t length)
++{
++      struct srp_target *target = iue->target;
++      struct vio_port *vport = target_to_port(target);
++      struct Scsi_Host *shost = target->shost;
++      dma_addr_t data_token;
++      struct mad_adapter_info_data *info;
++      int err;
++
++      info = dma_alloc_coherent(target->dev, sizeof(*info), &data_token,
++                                GFP_KERNEL);
++      if (!info) {
++              eprintk("bad dma_alloc_coherent %p\n", target);
++              return 1;
++      }
++
++      /* Get remote info */
++      err = h_copy_rdma(sizeof(*info), vport->riobn, remote_buffer,
++                        vport->liobn, data_token);
++      if (err == H_SUCCESS) {
++              dprintk("Client connect: %s (%d)\n",
++                      info->partition_name, info->partition_number);
++      }
++
++      memset(info, 0, sizeof(*info));
++
++      strcpy(info->srp_version, "16.a");
++      strncpy(info->partition_name, partition_name,
++              sizeof(info->partition_name));
++      info->partition_number = partition_number;
++      info->mad_version = 1;
++      info->os_type = 2;
++      info->port_max_txu[0] = shost->hostt->max_sectors << 9;
++
++      /* Send our info to remote */
++      err = h_copy_rdma(sizeof(*info), vport->liobn, data_token,
++                        vport->riobn, remote_buffer);
++
++      dma_free_coherent(target->dev, sizeof(*info), info, data_token);
++
++      if (err != H_SUCCESS) {
++              eprintk("Error sending adapter info %d\n", err);
++              return 1;
++      }
++
++      return 0;
++}
++
++static void process_login(struct iu_entry *iue)
++{
++      union viosrp_iu *iu = vio_iu(iue);
++      struct srp_login_rsp *rsp = &iu->srp.login_rsp;
++      uint64_t tag = iu->srp.rsp.tag;
++
++      /* TODO handle case that requested size is wrong and
++       * buffer format is wrong
++       */
++      memset(iu, 0, sizeof(struct srp_login_rsp));
++      rsp->opcode = SRP_LOGIN_RSP;
++      rsp->req_lim_delta = INITIAL_SRP_LIMIT;
++      rsp->tag = tag;
++      rsp->max_it_iu_len = sizeof(union srp_iu);
++      rsp->max_ti_iu_len = sizeof(union srp_iu);
++      /* direct and indirect */
++      rsp->buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT;
++
++      send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT);
++}
++
++static inline void queue_cmd(struct iu_entry *iue)
++{
++      struct srp_target *target = iue->target;
++      unsigned long flags;
++
++      spin_lock_irqsave(&target->lock, flags);
++      list_add_tail(&iue->ilist, &target->cmd_queue);
++      spin_unlock_irqrestore(&target->lock, flags);
++}
++
++static int process_tsk_mgmt(struct iu_entry *iue)
++{
++      union viosrp_iu *iu = vio_iu(iue);
++      int fn;
++
++      dprintk("%p %u\n", iue, iu->srp.tsk_mgmt.tsk_mgmt_func);
++
++      switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
++      case SRP_TSK_ABORT_TASK:
++              fn = ABORT_TASK;
++              break;
++      case SRP_TSK_ABORT_TASK_SET:
++              fn = ABORT_TASK_SET;
++              break;
++      case SRP_TSK_CLEAR_TASK_SET:
++              fn = CLEAR_TASK_SET;
++              break;
++      case SRP_TSK_LUN_RESET:
++              fn = LOGICAL_UNIT_RESET;
++              break;
++      case SRP_TSK_CLEAR_ACA:
++              fn = CLEAR_ACA;
++              break;
++      default:
++              fn = 0;
++      }
++      if (fn)
++              scsi_tgt_tsk_mgmt_request(iue->target->shost, fn,
++                                        iu->srp.tsk_mgmt.task_tag,
++                                        (struct scsi_lun *) 
&iu->srp.tsk_mgmt.lun,
++                                        iue);
++      else
++              send_rsp(iue, ILLEGAL_REQUEST, 0x20);
++
++      return !fn;
++}
++
++static int process_mad_iu(struct iu_entry *iue)
++{
++      union viosrp_iu *iu = vio_iu(iue);
++      struct viosrp_adapter_info *info;
++      struct viosrp_host_config *conf;
++
++      switch (iu->mad.empty_iu.common.type) {
++      case VIOSRP_EMPTY_IU_TYPE:
++              eprintk("%s\n", "Unsupported EMPTY MAD IU");
++              break;
++      case VIOSRP_ERROR_LOG_TYPE:
++              eprintk("%s\n", "Unsupported ERROR LOG MAD IU");
++              iu->mad.error_log.common.status = 1;
++              send_iu(iue, sizeof(iu->mad.error_log), VIOSRP_MAD_FORMAT);
++              break;
++      case VIOSRP_ADAPTER_INFO_TYPE:
++              info = &iu->mad.adapter_info;
++              info->common.status = send_adapter_info(iue, info->buffer,
++                                                      info->common.length);
++              send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT);
++              break;
++      case VIOSRP_HOST_CONFIG_TYPE:
++              conf = &iu->mad.host_config;
++              conf->common.status = 1;
++              send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT);
++              break;
++      default:
++              eprintk("Unknown type %u\n", iu->srp.rsp.opcode);
++      }
++
++      return 1;
++}
++
++static int process_srp_iu(struct iu_entry *iue)
++{
++      union viosrp_iu *iu = vio_iu(iue);
++      int done = 1;
++      u8 opcode = iu->srp.rsp.opcode;
++
++      switch (opcode) {
++      case SRP_LOGIN_REQ:
++              process_login(iue);
++              break;
++      case SRP_TSK_MGMT:
++              done = process_tsk_mgmt(iue);
++              break;
++      case SRP_CMD:
++              queue_cmd(iue);
++              done = 0;
++              break;
++      case SRP_LOGIN_RSP:
++      case SRP_I_LOGOUT:
++      case SRP_T_LOGOUT:
++      case SRP_RSP:
++      case SRP_CRED_REQ:
++      case SRP_CRED_RSP:
++      case SRP_AER_REQ:
++      case SRP_AER_RSP:
++              eprintk("Unsupported type %u\n", opcode);
++              break;
++      default:
++              eprintk("Unknown type %u\n", opcode);
++      }
++
++      return done;
++}
++
++static void process_iu(struct viosrp_crq *crq, struct srp_target *target)
++{
++      struct vio_port *vport = target_to_port(target);
++      struct iu_entry *iue;
++      long err, done;
++
++      iue = srp_iu_get(target);
++      if (!iue) {
++              eprintk("Error getting IU from pool, %p\n", target);
++              return;
++      }
++
++      iue->remote_token = crq->IU_data_ptr;
++
++      err = h_copy_rdma(crq->IU_length, vport->riobn,
++                        iue->remote_token, vport->liobn, iue->sbuf->dma);
++
++      if (err != H_SUCCESS)
++              eprintk("%ld transferring data error %p\n", err, iue);
++
++      if (crq->format == VIOSRP_MAD_FORMAT)
++              done = process_mad_iu(iue);
++      else
++              done = process_srp_iu(iue);
++
++      if (done)
++              srp_iu_put(iue);
++}
++
++static irqreturn_t ibmvstgt_interrupt(int irq, void *data, struct pt_regs 
*regs)
++{
++      struct srp_target *target = (struct srp_target *) data;
++      struct vio_port *vport = target_to_port(target);
++
++      vio_disable_interrupts(vport->dma_dev);
++      queue_work(vtgtd, &vport->crq_work);
++
++      return IRQ_HANDLED;
++}
++
++static int crq_queue_create(struct crq_queue *queue, struct srp_target 
*target)
++{
++      int err;
++      struct vio_port *vport = target_to_port(target);
++
++      queue->msgs = (struct viosrp_crq *) get_zeroed_page(GFP_KERNEL);
++      if (!queue->msgs)
++              goto malloc_failed;
++      queue->size = PAGE_SIZE / sizeof(*queue->msgs);
++
++      queue->msg_token = dma_map_single(target->dev, queue->msgs,
++                                        queue->size * sizeof(*queue->msgs),
++                                        DMA_BIDIRECTIONAL);
++
++      if (dma_mapping_error(queue->msg_token))
++              goto map_failed;
++
++      err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token,
++                      PAGE_SIZE);
++
++      /* If the adapter was left active for some reason (like kexec)
++       * try freeing and re-registering
++       */
++      if (err == H_RESOURCE) {
++          do {
++              err = h_free_crq(vport->dma_dev->unit_address);
++          } while (err == H_BUSY || H_IS_LONG_BUSY(err));
++
++          err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token,
++                          PAGE_SIZE);
++      }
++
++      if (err != H_SUCCESS && err != 2) {
++              eprintk("Error 0x%x opening virtual adapter\n", err);
++              goto reg_crq_failed;
++      }
++
++      err = request_irq(vport->dma_dev->irq, &ibmvstgt_interrupt,
++                        SA_INTERRUPT, "ibmvstgt", target);
++      if (err)
++              goto req_irq_failed;
++
++      vio_enable_interrupts(vport->dma_dev);
++
++      h_send_crq(vport->dma_dev->unit_address, 0xC001000000000000, 0);
++
++      queue->cur = 0;
++      spin_lock_init(&queue->lock);
++
++      return 0;
++
++req_irq_failed:
++      do {
++              err = h_free_crq(vport->dma_dev->unit_address);
++      } while (err == H_BUSY || H_IS_LONG_BUSY(err));
++
++reg_crq_failed:
++      dma_unmap_single(target->dev, queue->msg_token,
++                       queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
++map_failed:
++      free_page((unsigned long) queue->msgs);
++
++malloc_failed:
++      return -ENOMEM;
++}
++
++static void crq_queue_destroy(struct srp_target *target)
++{
++      struct vio_port *vport = target_to_port(target);
++      struct crq_queue *queue = &vport->crq_queue;
++      int err;
++
++      free_irq(vport->dma_dev->irq, target);
++      do {
++              err = h_free_crq(vport->dma_dev->unit_address);
++      } while (err == H_BUSY || H_IS_LONG_BUSY(err));
++
++      dma_unmap_single(target->dev, queue->msg_token,
++                       queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
++
++      free_page((unsigned long) queue->msgs);
++}
++
++static void process_crq(struct viosrp_crq *crq,       struct srp_target 
*target)
++{
++      struct vio_port *vport = target_to_port(target);
++      dprintk("%x %x\n", crq->valid, crq->format);
++
++      switch (crq->valid) {
++      case 0xC0:
++              /* initialization */
++              switch (crq->format) {
++              case 0x01:
++                      h_send_crq(vport->dma_dev->unit_address,
++                                 0xC002000000000000, 0);
++                      break;
++              case 0x02:
++                      break;
++              default:
++                      eprintk("Unknown format %u\n", crq->format);
++              }
++              break;
++      case 0xFF:
++              /* transport event */
++              break;
++      case 0x80:
++              /* real payload */
++              switch (crq->format) {
++              case VIOSRP_SRP_FORMAT:
++              case VIOSRP_MAD_FORMAT:
++                      process_iu(crq, target);
++                      break;
++              case VIOSRP_OS400_FORMAT:
++              case VIOSRP_AIX_FORMAT:
++              case VIOSRP_LINUX_FORMAT:
++              case VIOSRP_INLINE_FORMAT:
++                      eprintk("Unsupported format %u\n", crq->format);
++                      break;
++              default:
++                      eprintk("Unknown format %u\n", crq->format);
++              }
++              break;
++      default:
++              eprintk("unknown message type 0x%02x!?\n", crq->valid);
++      }
++}
++
++static inline struct viosrp_crq *next_crq(struct crq_queue *queue)
++{
++      struct viosrp_crq *crq;
++      unsigned long flags;
++
++      spin_lock_irqsave(&queue->lock, flags);
++      crq = &queue->msgs[queue->cur];
++      if (crq->valid & 0x80) {
++              if (++queue->cur == queue->size)
++                      queue->cur = 0;
++      } else
++              crq = NULL;
++      spin_unlock_irqrestore(&queue->lock, flags);
++
++      return crq;
++}
++
++static void handle_crq(void *data)
++{
++      struct srp_target *target = (struct srp_target *) data;
++      struct vio_port *vport = target_to_port(target);
++      struct viosrp_crq *crq;
++      int done = 0;
++
++      while (!done) {
++              while ((crq = next_crq(&vport->crq_queue)) != NULL) {
++                      process_crq(crq, target);
++                      crq->valid = 0x00;
++              }
++
++              vio_enable_interrupts(vport->dma_dev);
++
++              crq = next_crq(&vport->crq_queue);
++              if (crq) {
++                      vio_disable_interrupts(vport->dma_dev);
++                      process_crq(crq, target);
++                      crq->valid = 0x00;
++              } else
++                      done = 1;
++      }
++
++      handle_cmd_queue(target);
++}
++
++
++static int ibmvstgt_eh_abort_handler(struct scsi_cmnd *scmd)
++{
++      unsigned long flags;
++      struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr;
++      struct srp_target *target = iue->target;
++
++      dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]);
++
++      spin_lock_irqsave(&target->lock, flags);
++      list_del(&iue->ilist);
++      spin_unlock_irqrestore(&target->lock, flags);
++
++      srp_iu_put(iue);
++
++      return 0;
++}
++
++static int ibmvstgt_tsk_mgmt_response(u64 mid, int result)
++{
++      struct iu_entry *iue = (struct iu_entry *) ((void *) mid);
++      union viosrp_iu *iu = vio_iu(iue);
++      unsigned char status, asc;
++
++      eprintk("%p %d\n", iue, result);
++      status = NO_SENSE;
++      asc = 0;
++
++      switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
++      case SRP_TSK_ABORT_TASK:
++              asc = 0x14;
++              if (result)
++                      status = ABORTED_COMMAND;
++              break;
++      default:
++              break;
++      }
++
++      send_rsp(iue, status, asc);
++      srp_iu_put(iue);
++
++      return 0;
++}
++
++static ssize_t system_id_show(struct class_device *cdev, char *buf)
++{
++      return snprintf(buf, PAGE_SIZE, "%s\n", system_id);
++}
++
++static ssize_t partition_number_show(struct class_device *cdev, char *buf)
++{
++      return snprintf(buf, PAGE_SIZE, "%x\n", partition_number);
++}
++
++static ssize_t unit_address_show(struct class_device *cdev, char *buf)
++{
++      struct Scsi_Host *shost = class_to_shost(cdev);
++      struct srp_target *target = host_to_target(shost);
++      struct vio_port *vport = target_to_port(target);
++      return snprintf(buf, PAGE_SIZE, "%x\n", vport->dma_dev->unit_address);
++}
++
++static CLASS_DEVICE_ATTR(system_id, S_IRUGO, system_id_show, NULL);
++static CLASS_DEVICE_ATTR(partition_number, S_IRUGO, partition_number_show, 
NULL);
++static CLASS_DEVICE_ATTR(unit_address, S_IRUGO, unit_address_show, NULL);
++
++static struct class_device_attribute *ibmvstgt_attrs[] = {
++      &class_device_attr_system_id,
++      &class_device_attr_partition_number,
++      &class_device_attr_unit_address,
++      NULL,
++};
++
++static struct scsi_host_template ibmvstgt_sht = {
++      .name                   = TGT_NAME,
++      .module                 = THIS_MODULE,
++      .can_queue              = INITIAL_SRP_LIMIT,
++      .sg_tablesize           = SG_ALL,
++      .use_clustering         = DISABLE_CLUSTERING,
++      .max_sectors            = DEFAULT_MAX_SECTORS,
++      .transfer_response      = ibmvstgt_cmd_done,
++      .transfer_data          = ibmvstgt_transfer_data,
++      .eh_abort_handler       = ibmvstgt_eh_abort_handler,
++      .tsk_mgmt_response      = ibmvstgt_tsk_mgmt_response,
++      .shost_attrs            = ibmvstgt_attrs,
++      .proc_name              = TGT_NAME,
++};
++
++static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id)
++{
++      struct Scsi_Host *shost;
++      struct srp_target *target;
++      struct vio_port *vport;
++      unsigned int *dma, dma_size;
++      int err = -ENOMEM;
++
++      vport = kzalloc(sizeof(struct vio_port), GFP_KERNEL);
++      if (!vport)
++              return err;
++      shost = scsi_host_alloc(&ibmvstgt_sht, sizeof(struct srp_target));
++      if (!shost)
++              goto free_vport;
++      err = scsi_tgt_alloc_queue(shost);
++      if (err)
++              goto put_host;
++
++      target = host_to_target(shost);
++      target->shost = shost;
++      vport->dma_dev = dev;
++      target->ldata = vport;
++      err = srp_target_alloc(target, &dev->dev, INITIAL_SRP_LIMIT,
++                             SRP_MAX_IU_LEN);
++      if (err)
++              goto put_host;
++
++      dma = (unsigned int *) vio_get_attribute(dev, "ibm,my-dma-window",
++                                               &dma_size);
++      if (!dma || dma_size != 40) {
++              eprintk("Couldn't get window property %d\n", dma_size);
++              err = -EIO;
++              goto free_srp_target;
++      }
++      vport->liobn = dma[0];
++      vport->riobn = dma[5];
++
++      INIT_WORK(&vport->crq_work, handle_crq, target);
++
++      err = crq_queue_create(&vport->crq_queue, target);
++      if (err)
++              goto free_srp_target;
++
++      err = scsi_add_host(shost, target->dev);
++      if (err)
++              goto destroy_queue;
++      return 0;
++
++destroy_queue:
++      crq_queue_destroy(target);
++free_srp_target:
++      srp_target_free(target);
++put_host:
++      scsi_host_put(shost);
++free_vport:
++      kfree(vport);
++      return err;
++}
++
++static int ibmvstgt_remove(struct vio_dev *dev)
++{
++      struct srp_target *target = (struct srp_target *) dev->dev.driver_data;
++      struct Scsi_Host *shost = target->shost;
++
++      srp_target_free(target);
++      crq_queue_destroy(target);
++      scsi_remove_host(shost);
++      scsi_host_put(shost);
++      return 0;
++}
++
++static struct vio_device_id ibmvstgt_device_table[] __devinitdata = {
++      {"v-scsi-host", "IBM,v-scsi-host"},
++      {"",""}
++};
++
++MODULE_DEVICE_TABLE(vio, ibmvstgt_device_table);
++
++static struct vio_driver ibmvstgt_driver = {
++      .id_table = ibmvstgt_device_table,
++      .probe = ibmvstgt_probe,
++      .remove = ibmvstgt_remove,
++      .driver = {
++              .name = "ibmvscsi",
++              .owner = THIS_MODULE,
++      }
++};
++
++static int get_system_info(void)
++{
++      struct device_node *rootdn;
++      char *id, *model, *name;
++      unsigned int *num;
++
++      rootdn = find_path_device("/");
++      if (!rootdn)
++              return -ENOENT;
++
++      model = get_property(rootdn, "model", NULL);
++      id = get_property(rootdn, "system-id", NULL);
++      if (model && id)
++              snprintf(system_id, sizeof(system_id), "%s-%s", model, id);
++
++      name = get_property(rootdn, "ibm,partition-name", NULL);
++      if (name)
++              strncpy(partition_name, name, sizeof(partition_name));
++
++      num = (unsigned int *) get_property(rootdn, "ibm,partition-no", NULL);
++      if (num)
++              partition_number = *num;
++
++      return 0;
++}
++
++static int ibmvstgt_init(void)
++{
++      int err = -ENOMEM;
++
++      printk("IBM eServer i/pSeries Virtual SCSI Target Driver\n");
++
++      vtgtd = create_workqueue("ibmvtgtd");
++      if (!vtgtd)
++              return err;
++
++      err = get_system_info();
++      if (err)
++              goto destroy_wq;
++
++      err = vio_register_driver(&ibmvstgt_driver);
++      if (err)
++              goto destroy_wq;
++
++      return 0;
++
++destroy_wq:
++      destroy_workqueue(vtgtd);
++      return err;
++}
++
++static void ibmvstgt_exit(void)
++{
++      printk("Unregister IBM virtual SCSI driver\n");
++
++      destroy_workqueue(vtgtd);
++      vio_unregister_driver(&ibmvstgt_driver);
++}
++
++MODULE_DESCRIPTION("IBM Virtual SCSI Target");
++MODULE_AUTHOR("Dave Boutcher");
++MODULE_LICENSE("GPL");
++
++module_init(ibmvstgt_init);
++module_exit(ibmvstgt_exit);
+diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
+new file mode 100644
+index 0000000..36b5b39
+--- /dev/null
++++ b/drivers/scsi/libsrp.c
+@@ -0,0 +1,450 @@
++/*
++ * 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 <linux/pci.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 <scsi/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 srp_iu_pool_alloc(struct srp_queue *q, size_t max,
++                           struct srp_buf **ring)
++{
++      int i;
++      struct iu_entry *iue;
++
++      q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL);
++      if (!q->pool)
++              return -ENOMEM;
++      q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL);
++      if (!q->items)
++              goto free_pool;
++
++      spin_lock_init(&q->lock);
++      q->queue = kfifo_init((void *) q->pool, max * sizeof(void *),
++                            GFP_KERNEL, &q->lock);
++      if (IS_ERR(q->queue))
++              goto free_item;
++
++      for (i = 0, iue = q->items; i < max; i++) {
++              __kfifo_put(q->queue, (void *) &iue, sizeof(void *));
++              iue->sbuf = ring[i];
++              iue++;
++      }
++      return 0;
++
++free_item:
++      kfree(q->items);
++free_pool:
++      kfree(q->pool);
++      return -ENOMEM;
++}
++
++static void srp_iu_pool_free(struct srp_queue *q)
++{
++      kfree(q->items);
++      kfree(q->pool);
++}
++
++static struct srp_buf ** srp_ring_alloc(struct device *dev,
++                                      size_t max, size_t size)
++{
++      int i;
++      struct srp_buf **ring;
++
++      ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL);
++      if (!ring)
++              return NULL;
++
++      for (i = 0; i < max; i++) {
++              ring[i] = kzalloc(sizeof(struct srp_buf), GFP_KERNEL);
++              if (!ring[i])
++                      goto out;
++              ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma,
++                                                GFP_KERNEL);
++              if (!ring[i]->buf)
++                      goto out;
++      }
++      return ring;
++
++out:
++      for (i = 0; i < max && ring[i]; i++) {
++              if (ring[i]->buf)
++                      dma_free_coherent(dev, size, ring[i]->buf, 
ring[i]->dma);
++              kfree(ring[i]);
++      }
++      kfree(ring);
++
++      return NULL;
++}
++
++static void srp_ring_free(struct device *dev, struct srp_buf **ring, size_t 
max,
++                        size_t size)
++{
++      int i;
++
++      for (i = 0; i < max; i++) {
++              dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
++              kfree(ring[i]);
++      }
++}
++
++int srp_target_alloc(struct srp_target *target, struct device *dev,
++                   size_t nr, size_t iu_size)
++{
++      int err;
++
++      spin_lock_init(&target->lock);
++      INIT_LIST_HEAD(&target->cmd_queue);
++
++      target->dev = dev;
++      target->dev->driver_data = target;
++
++      target->srp_iu_size = iu_size;
++      target->rx_ring_size = nr;
++      target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size);
++      if (!target->rx_ring)
++              return -ENOMEM;
++      err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring);
++      if (err)
++              goto free_ring;
++
++      return 0;
++
++free_ring:
++      srp_ring_free(target->dev, target->rx_ring, nr, iu_size);
++      return -ENOMEM;
++}
++EXPORT_SYMBOL_GPL(srp_target_alloc);
++
++void srp_target_free(struct srp_target *target)
++{
++      srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size,
++                    target->srp_iu_size);
++      srp_iu_pool_free(&target->iu_queue);
++}
++EXPORT_SYMBOL_GPL(srp_target_free);
++
++struct iu_entry *srp_iu_get(struct srp_target *target)
++{
++      struct iu_entry *iue = NULL;
++
++      kfifo_get(target->iu_queue.queue, (void *) &iue, sizeof(void *));
++      BUG_ON(!iue);
++
++      iue->target = target;
++      iue->scmd = NULL;
++      INIT_LIST_HEAD(&iue->ilist);
++      iue->flags = 0;
++      return iue;
++}
++EXPORT_SYMBOL_GPL(srp_iu_get);
++
++void srp_iu_put(struct iu_entry *iue)
++{
++      kfifo_put(iue->target->iu_queue.queue, (void *) &iue, sizeof(void *));
++}
++EXPORT_SYMBOL_GPL(srp_iu_put);
++
++static int direct_data(struct scsi_cmnd *scmd, struct srp_direct_buf *md,
++                     enum dma_data_direction dir, rdma_io_t rdma_io)
++{
++      struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr;
++      struct srp_target *target = iue->target;
++      struct scatterlist *sg = scmd->request_buffer;
++      int nsg, err;
++
++      dprintk("%p %u %u %u %d\n", iue, scmd->request_bufflen, scmd->bufflen,
++              md->len, scmd->use_sg);
++
++      nsg = dma_map_sg(target->dev, sg, scmd->use_sg, DMA_BIDIRECTIONAL);
++      if (!nsg) {
++              printk("fail to map %p %d\n", iue, scmd->use_sg);
++              return 0;
++      }
++      err = rdma_io(iue, sg, nsg, md, 1, dir,
++                    min(scmd->request_bufflen, md->len));
++
++      dma_unmap_sg(target->dev, sg, nsg, DMA_BIDIRECTIONAL);
++
++      return err;
++}
++
++static int indirect_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd,
++                       struct srp_indirect_buf *id,
++                       enum dma_data_direction dir, rdma_io_t rdma_io)
++{
++      struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr;
++      struct srp_target *target = iue->target;
++      struct srp_direct_buf *md;
++      struct scatterlist dummy, *sg = scmd->request_buffer;
++      dma_addr_t token = 0;
++      long err;
++      unsigned int done = 0;
++      int nmd, nsg;
++
++      nmd = id->table_desc.len / sizeof(struct srp_direct_buf);
++
++      dprintk("%p %u %u %u %u %d %d %d\n",
++              iue, scmd->request_bufflen, scmd->bufflen,
++              id->len, scmd->offset, nmd,
++              cmd->data_in_desc_cnt, cmd->data_out_desc_cnt);
++
++      if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) ||
++          (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) {
++              md = &id->desc_list[0];
++              goto rdma;
++      }
++
++      md = dma_alloc_coherent(target->dev, id->table_desc.len,
++                               &token, GFP_KERNEL);
++      if (!md) {
++              eprintk("Can't get dma memory %u\n", id->table_desc.len);
++              return 0;
++      }
++
++      sg_init_one(&dummy, md, id->table_desc.len);
++      sg_dma_address(&dummy) = token;
++      err = rdma_io(iue, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE,
++                    id->table_desc.len);
++      if (err < 0) {
++              eprintk("Error copying indirect table %ld\n", err);
++              goto free_mem;
++      }
++
++rdma:
++      nsg = dma_map_sg(target->dev, sg, scmd->use_sg, DMA_BIDIRECTIONAL);
++      if (!nsg) {
++              eprintk("fail to map %p %d\n", iue, scmd->use_sg);
++              goto free_mem;
++      }
++
++      err = rdma_io(iue, sg, nsg, md, nmd, dir,
++                    min(scmd->request_bufflen, id->len));
++      dma_unmap_sg(target->dev, sg, nsg, DMA_BIDIRECTIONAL);
++
++free_mem:
++      if (token)
++              dma_free_coherent(target->dev, id->table_desc.len, md, token);
++
++      return done;
++}
++
++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;
++}
++
++static int __srp_transfer_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd,
++                             enum dma_data_direction dir, rdma_io_t rdma_io)
++{
++      struct srp_direct_buf *md;
++      struct srp_indirect_buf *id;
++      int offset, err = 0;
++      u8 format;
++
++      offset = cmd->add_cdb_len * 4;
++      if (dir == DMA_FROM_DEVICE)
++              offset += data_out_desc_size(cmd);
++
++      if (dir == DMA_TO_DEVICE)
++              format = cmd->buf_fmt >> 4;
++      else
++              format = cmd->buf_fmt & ((1U << 4) - 1);
++
++      switch (format) {
++      case SRP_NO_DATA_DESC:
++              break;
++      case SRP_DATA_DESC_DIRECT:
++              md = (struct srp_direct_buf *)
++                      (cmd->add_data + offset);
++              err = direct_data(scmd, md, dir, rdma_io);
++              break;
++      case SRP_DATA_DESC_INDIRECT:
++              id = (struct srp_indirect_buf *)
++                      (cmd->add_data + offset);
++              err = indirect_data(scmd, cmd, id, dir, rdma_io);
++              break;
++      default:
++              eprintk("Unknown format %d %x\n", dir, format);
++              break;
++      }
++
++      return err;
++}
++
++/* TODO: this can be called multiple times for a single command. */
++int srp_transfer_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd,
++                    rdma_io_t rdma_io)
++{
++      struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr;
++      enum dma_data_direction dir;
++
++      if (test_bit(V_WRITE, &iue->flags))
++              dir = DMA_TO_DEVICE;
++      else
++              dir = DMA_FROM_DEVICE;
++      __srp_transfer_data(scmd, cmd, dir, rdma_io);
++      return 0;
++}
++EXPORT_SYMBOL_GPL(srp_transfer_data);
++
++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;
++}
++
++static uint8_t getcontrolbyte(u8 *cdb)
++{
++      return cdb[COMMAND_SIZE(cdb[0]) - 1];
++}
++
++static inline uint8_t getlink(struct srp_cmd *cmd)
++{
++      return (getcontrolbyte(cmd->cdb) & 0x01);
++}
++
++int srp_cmd_perform(struct iu_entry *iue, struct srp_cmd *cmd)
++{
++      struct Scsi_Host *shost = iue->target->shost;
++      enum dma_data_direction data_dir;
++      struct scsi_cmnd *scmd;
++      int tag, len;
++
++      if (getlink(cmd))
++              __set_bit(V_LINKED, &iue->flags);
++
++      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;
++      }
++
++      switch (cmd->cdb[0]) {
++      case WRITE_6:
++      case WRITE_10:
++      case WRITE_VERIFY:
++      case WRITE_12:
++      case WRITE_VERIFY_12:
++              __set_bit(V_WRITE, &iue->flags);
++      }
++
++      if (cmd->buf_fmt >> 4)
++              data_dir = DMA_TO_DEVICE;
++      else
++              data_dir = DMA_FROM_DEVICE;
++      len = vscsis_data_length(cmd, data_dir);
++
++      dprintk("%p %x %lx %d %d %d %llx\n", iue, cmd->cdb[0],
++              cmd->lun, data_dir, len, tag, (unsigned long long) cmd->tag);
++
++      scmd = scsi_host_get_command(shost, data_dir, GFP_KERNEL);
++      BUG_ON(!scmd);
++      scmd->SCp.ptr = (char *) iue;
++      memcpy(scmd->cmnd, cmd->cdb, MAX_COMMAND_SIZE);
++      scmd->request_buffer = 0;
++      scmd->request_bufflen = len;
++      scmd->tag = tag;
++      iue->scmd = scmd;
++      scsi_tgt_queue_command(scmd, (struct scsi_lun *) &cmd->lun, cmd->tag);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(srp_cmd_perform);
++
++MODULE_DESCRIPTION("SCSI RDAM Protocol lib functions");
++MODULE_AUTHOR("FUJITA Tomonori");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
+index 73994e2..0591b93 100644
+--- a/drivers/scsi/scsi.c
++++ b/drivers/scsi/scsi.c
+@@ -210,8 +210,7 @@ static struct scsi_host_cmd_pool scsi_cm
+ 
+ static DEFINE_MUTEX(host_cmd_pool_mutex);
+ 
+-static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost,
+-                                          gfp_t gfp_mask)
++struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask)
+ {
+       struct scsi_cmnd *cmd;
+ 
+@@ -232,6 +231,7 @@ static struct scsi_cmnd *__scsi_get_comm
+ 
+       return cmd;
+ }
++EXPORT_SYMBOL_GPL(__scsi_get_command);
+ 
+ /*
+  * Function:  scsi_get_command()
+@@ -268,9 +268,29 @@ struct scsi_cmnd *scsi_get_command(struc
+               put_device(&dev->sdev_gendev);
+ 
+       return cmd;
+-}                             
++}
+ EXPORT_SYMBOL(scsi_get_command);
+ 
++void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd,
++                      struct device *dev)
++{
++      unsigned long flags;
++
++      /* changing locks here, don't need to restore the irq state */
++      spin_lock_irqsave(&shost->free_list_lock, flags);
++      if (unlikely(list_empty(&shost->free_list))) {
++              list_add(&cmd->list, &shost->free_list);
++              cmd = NULL;
++      }
++      spin_unlock_irqrestore(&shost->free_list_lock, flags);
++
++      if (likely(cmd != NULL))
++              kmem_cache_free(shost->cmd_pool->slab, cmd);
++
++      put_device(dev);
++}
++EXPORT_SYMBOL(__scsi_put_command);
++
+ /*
+  * Function:  scsi_put_command()
+  *
+@@ -285,26 +305,15 @@ EXPORT_SYMBOL(scsi_get_command);
+ void scsi_put_command(struct scsi_cmnd *cmd)
+ {
+       struct scsi_device *sdev = cmd->device;
+-      struct Scsi_Host *shost = sdev->host;
+       unsigned long flags;
+-      
++
+       /* serious error if the command hasn't come from a device list */
+       spin_lock_irqsave(&cmd->device->list_lock, flags);
+       BUG_ON(list_empty(&cmd->list));
+       list_del_init(&cmd->list);
+-      spin_unlock(&cmd->device->list_lock);
+-      /* changing locks here, don't need to restore the irq state */
+-      spin_lock(&shost->free_list_lock);
+-      if (unlikely(list_empty(&shost->free_list))) {
+-              list_add(&cmd->list, &shost->free_list);
+-              cmd = NULL;
+-      }
+-      spin_unlock_irqrestore(&shost->free_list_lock, flags);
++      spin_unlock_irqrestore(&cmd->device->list_lock, flags);
+ 
+-      if (likely(cmd != NULL))
+-              kmem_cache_free(shost->cmd_pool->slab, cmd);
+-
+-      put_device(&sdev->sdev_gendev);
++      __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev);
+ }
+ EXPORT_SYMBOL(scsi_put_command);
+ 
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+index faee475..f8ac3d5 100644
+--- a/drivers/scsi/scsi_lib.c
++++ b/drivers/scsi/scsi_lib.c
+@@ -803,7 +803,7 @@ static struct scsi_cmnd *scsi_end_reques
+       return NULL;
+ }
+ 
+-static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t 
gfp_mask)
++struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+ {
+       struct scsi_host_sg_pool *sgp;
+       struct scatterlist *sgl;
+@@ -844,7 +844,9 @@ static struct scatterlist *scsi_alloc_sg
+       return sgl;
+ }
+ 
+-static void scsi_free_sgtable(struct scatterlist *sgl, int index)
++EXPORT_SYMBOL(scsi_alloc_sgtable);
++
++void scsi_free_sgtable(struct scatterlist *sgl, int index)
+ {
+       struct scsi_host_sg_pool *sgp;
+ 
+@@ -854,6 +856,8 @@ static void scsi_free_sgtable(struct sca
+       mempool_free(sgl, sgp->pool);
+ }
+ 
++EXPORT_SYMBOL(scsi_free_sgtable);
++
+ /*
+  * Function:    scsi_release_buffers()
+  *
+@@ -1714,29 +1718,40 @@ u64 scsi_calculate_bounce_limit(struct S
+ }
+ EXPORT_SYMBOL(scsi_calculate_bounce_limit);
+ 
+-struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
++struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,
++                                       request_fn_proc *request_fn)
+ {
+-      struct Scsi_Host *shost = sdev->host;
+       struct request_queue *q;
+ 
+-      q = blk_init_queue(scsi_request_fn, NULL);
++      q = blk_init_queue(request_fn, NULL);
+       if (!q)
+               return NULL;
+ 
+-      blk_queue_prep_rq(q, scsi_prep_fn);
+-
+       blk_queue_max_hw_segments(q, shost->sg_tablesize);
+       blk_queue_max_phys_segments(q, SCSI_MAX_PHYS_SEGMENTS);
+       blk_queue_max_sectors(q, shost->max_sectors);
+       blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));
+       blk_queue_segment_boundary(q, shost->dma_boundary);
+-      blk_queue_issue_flush_fn(q, scsi_issue_flush_fn);
+-      blk_queue_softirq_done(q, scsi_softirq_done);
+ 
+       if (!shost->use_clustering)
+               clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
+       return q;
+ }
++EXPORT_SYMBOL(__scsi_alloc_queue);
++
++struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
++{
++      struct request_queue *q;
++
++      q = __scsi_alloc_queue(sdev->host, scsi_request_fn);
++      if (!q)
++              return NULL;
++
++      blk_queue_prep_rq(q, scsi_prep_fn);
++      blk_queue_issue_flush_fn(q, scsi_issue_flush_fn);
++      blk_queue_softirq_done(q, scsi_softirq_done);
++      return q;
++}
+ 
+ void scsi_free_queue(struct request_queue *q)
+ {
+diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c
+new file mode 100644
+index 0000000..c9d15a7
+--- /dev/null
++++ b/drivers/scsi/scsi_tgt_if.c
+@@ -0,0 +1,316 @@
++/*
++ * SCSI target kernel/user interface functions
++ *
++ * Copyright (C) 2005 FUJITA Tomonori <tomof@xxxxxxx>
++ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
++ *
++ * 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/blkdev.h>
++#include <linux/file.h>
++#include <net/tcp.h>
++#include <scsi/scsi.h>
++#include <scsi/scsi_cmnd.h>
++#include <scsi/scsi_device.h>
++#include <scsi/scsi_host.h>
++#include <scsi/scsi_tgt.h>
++#include <scsi/scsi_tgt_if.h>
++
++#include "scsi_tgt_priv.h"
++
++struct rbuf {
++      u32 idx;
++      u32 nr_entry;
++      int entry_size;
++      char *buf;
++      int buf_size;
++      spinlock_t lock;
++};
++
++static int chrdev;
++static struct rbuf txbuf, rxbuf;
++static DECLARE_WAIT_QUEUE_HEAD(tgt_poll_wait);
++
++static inline struct rbuf_hdr *head_rbuf_hdr(struct rbuf *rbuf, u32 idx)
++{
++      u32 offset = (idx & (rbuf->nr_entry - 1)) * rbuf->entry_size;
++      return (struct rbuf_hdr *) (rbuf->buf + offset);
++}
++
++static void rbuf_init(struct rbuf *rbuf, char *buf, int bsize, int esize)
++{
++      int i;
++
++      esize += sizeof(struct rbuf_hdr);
++      rbuf->idx = 0;
++      rbuf->entry_size = esize;
++      rbuf->buf = buf;
++      spin_lock_init(&rbuf->lock);
++
++      bsize /= esize;
++      for (i = 0; (1 << i) < bsize && (1 << (i + 1)) <= bsize; i++)
++              ;
++      rbuf->nr_entry = 1 << i;
++}
++
++static int send_event_rsp(u32 type, struct tgt_event *p)
++{
++      struct tgt_event *ev;
++      struct rbuf_hdr *hdr;
++      struct page *sp, *ep;
++      unsigned long flags;
++      int err = 0;
++
++      spin_lock_irqsave(&txbuf.lock, flags);
++
++      hdr = head_rbuf_hdr(&txbuf, txbuf.idx);
++      if (hdr->status)
++              err = 1;
++      else
++              txbuf.idx++;
++
++      spin_unlock_irqrestore(&txbuf.lock, flags);
++
++      if (err)
++              return err;
++
++      ev = (struct tgt_event *) hdr->data;
++      memcpy(ev, p, sizeof(*ev));
++      ev->type = type;
++      hdr->status = 1;
++      mb();
++
++      sp = virt_to_page(hdr);
++      ep = virt_to_page((char *) hdr->data + sizeof(*ev));
++      for (;sp <= ep; sp++)
++              flush_dcache_page(sp);
++
++      wake_up_interruptible(&tgt_poll_wait);
++
++      return 0;
++}
++
++int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, u64 
tag)
++{
++      struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
++      struct tgt_event ev;
++      int err;
++
++      memset(&ev, 0, sizeof(ev));
++      ev.k.cmd_req.host_no = shost->host_no;
++      ev.k.cmd_req.cid = cmd->request->tag;
++      ev.k.cmd_req.data_len = cmd->request_bufflen;
++      memcpy(ev.k.cmd_req.scb, cmd->cmnd, sizeof(ev.k.cmd_req.scb));
++      memcpy(ev.k.cmd_req.lun, lun, sizeof(ev.k.cmd_req.lun));
++      ev.k.cmd_req.attribute = cmd->tag;
++      ev.k.cmd_req.tag = tag;
++      ev.k.cmd_req.uaddr = (u64) (unsigned long) cmd->request_buffer;
++
++      dprintk("%p %d %u %u %x %llx\n", cmd, shost->host_no, ev.k.cmd_req.cid,
++              ev.k.cmd_req.data_len, cmd->tag,
++              (unsigned long long) ev.k.cmd_req.tag);
++
++      err = send_event_rsp(TGT_KEVENT_CMD_REQ, &ev);
++      if (err)
++              eprintk("tx buf is full, could not send\n");
++      return err;
++}
++
++int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd)
++{
++      struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
++      struct tgt_event ev;
++      int err;
++
++      memset(&ev, 0, sizeof(ev));
++      ev.k.cmd_done.host_no = shost->host_no;
++      ev.k.cmd_done.cid = cmd->request->tag;
++      ev.k.cmd_done.result = cmd->result;
++
++      dprintk("%p %d %u %u %x %llx\n", cmd, shost->host_no, ev.k.cmd_req.cid,
++              ev.k.cmd_req.data_len, cmd->tag,
++              (unsigned long long) ev.k.cmd_req.tag);
++
++      err = send_event_rsp(TGT_KEVENT_CMD_DONE, &ev);
++      if (err)
++              eprintk("tx buf is full, could not send\n");
++      return err;
++}
++
++int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag,
++                                struct scsi_lun *scsilun, void *data)
++{
++      struct tgt_event ev;
++      int err;
++
++      memset(&ev, 0, sizeof(ev));
++      ev.k.tsk_mgmt_req.host_no = host_no;
++      ev.k.tsk_mgmt_req.function = function;
++      ev.k.tsk_mgmt_req.tag = tag;
++      memcpy(ev.k.tsk_mgmt_req.lun, scsilun, sizeof(ev.k.tsk_mgmt_req.lun));
++      ev.k.tsk_mgmt_req.mid = (u64) (unsigned long) data;
++
++      dprintk("%d %x %llx %llx\n", host_no, function, (unsigned long long) 
tag,
++              (unsigned long long) ev.k.tsk_mgmt_req.mid);
++
++      err = send_event_rsp(TGT_KEVENT_TSK_MGMT_REQ, &ev);
++      if (err)
++              eprintk("tx buf is full, could not send\n");
++      return err;
++}
++
++static int event_recv_msg(struct tgt_event *ev)
++{
++      int err = 0;
++
++      switch (ev->type) {
++      case TGT_UEVENT_CMD_RSP:
++              err = scsi_tgt_kspace_exec(ev->u.cmd_rsp.host_no,
++                                         ev->u.cmd_rsp.cid,
++                                         ev->u.cmd_rsp.result,
++                                         ev->u.cmd_rsp.len,
++                                         ev->u.cmd_rsp.uaddr,
++                                         ev->u.cmd_rsp.rw);
++              break;
++      case TGT_UEVENT_TSK_MGMT_RSP:
++              err = scsi_tgt_kspace_tsk_mgmt(ev->u.tsk_mgmt_rsp.host_no,
++                                             ev->u.tsk_mgmt_rsp.mid,
++                                             ev->u.tsk_mgmt_rsp.result);
++              break;
++      default:
++              eprintk("unknown type %d\n", ev->type);
++              err = -EINVAL;
++      }
++
++      return err;
++}
++
++static ssize_t tgt_write(struct file *file, const char __user * buffer,
++                       size_t count, loff_t * ppos)
++{
++      struct rbuf_hdr *hdr;
++      struct tgt_event *ev;
++      struct page *sp, *ep;
++
++retry:
++      hdr = head_rbuf_hdr(&rxbuf, rxbuf.idx);
++
++      sp = virt_to_page(hdr);
++      ep = virt_to_page((char *) hdr->data + sizeof(*ev));
++      for (;sp <= ep; sp++)
++              flush_dcache_page(sp);
++
++      if (!hdr->status)
++              return count;
++
++      rxbuf.idx++;
++      ev = (struct tgt_event *) hdr->data;
++      event_recv_msg(ev);
++      hdr->status = 0;
++
++      goto retry;
++}
++
++static unsigned int tgt_poll(struct file * file, struct poll_table_struct 
*wait)
++{
++      struct rbuf_hdr *hdr;
++      unsigned long flags;
++      unsigned int mask = 0;
++
++      poll_wait(file, &tgt_poll_wait, wait);
++
++      spin_lock_irqsave(&txbuf.lock, flags);
++
++      hdr = head_rbuf_hdr(&txbuf, txbuf.idx - 1);
++      if (hdr->status)
++              mask |= POLLIN | POLLRDNORM;
++
++      spin_unlock_irqrestore(&txbuf.lock, flags);
++
++      return mask;
++}
++
++static int tgt_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++      unsigned long size, addr;
++      struct page *page;
++      int err, i;
++
++      if (vma->vm_pgoff) {
++              eprintk("bug\n");
++              return -EINVAL;
++      }
++
++      size = vma->vm_end - vma->vm_start;
++      if (size != TGT_RINGBUF_SIZE * 2) {
++              eprintk("%lu\n", size);
++              return -EINVAL;
++      }
++      addr = vma->vm_start;
++      page = virt_to_page(txbuf.buf);
++      for (i = 0; i < size >> PAGE_SHIFT; i++) {
++              err = vm_insert_page(vma, addr, page);
++              if (err) {
++                      eprintk("%d %d %lu\n", err, i, addr);
++                      return -EINVAL;
++              }
++              addr += PAGE_SIZE;
++              page++;
++      }
++
++      return 0;
++}
++
++static struct file_operations tgt_fops = {
++      .owner  = THIS_MODULE,
++      .poll   = tgt_poll,
++      .write  = tgt_write,
++      .mmap   = tgt_mmap,
++};
++
++void __exit scsi_tgt_if_exit(void)
++{
++      int order = long_log2(TGT_RINGBUF_SIZE * 2);
++
++      unregister_chrdev(chrdev, "tgt");
++      free_pages((unsigned long) txbuf.buf, order);
++}
++
++int __init scsi_tgt_if_init(void)
++{
++      u32 bsize = TGT_RINGBUF_SIZE;
++      int order;
++      char *buf;
++
++      chrdev = register_chrdev(0, "tgt", &tgt_fops);
++      if (chrdev < 0)
++              return chrdev;
++
++      order = long_log2((bsize * 2) >> PAGE_SHIFT);
++      buf = (char *) __get_free_pages(GFP_KERNEL | __GFP_COMP | __GFP_ZERO,
++                                      order);
++      if (!buf)
++              goto free_dev;
++      rbuf_init(&txbuf, buf, bsize, sizeof(struct tgt_event));
++      rbuf_init(&rxbuf, buf + bsize, bsize, sizeof(struct tgt_event));
++
++      return 0;
++
++free_dev:
++      unregister_chrdev(chrdev, "tgt");
++
++      return -ENOMEM;
++}
+diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c
+new file mode 100644
+index 0000000..447bf88
+--- /dev/null
++++ b/drivers/scsi/scsi_tgt_lib.c
+@@ -0,0 +1,707 @@
++/*
++ * SCSI target lib functions
++ *
++ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
++ * Copyright (C) 2005 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/blkdev.h>
++#include <linux/hash.h>
++#include <linux/module.h>
++#include <linux/pagemap.h>
++#include <scsi/scsi.h>
++#include <scsi/scsi_cmnd.h>
++#include <scsi/scsi_device.h>
++#include <scsi/scsi_host.h>
++#include <scsi/scsi_tgt.h>
++#include <../drivers/md/dm-bio-list.h>
++
++#include "scsi_tgt_priv.h"
++
++static struct workqueue_struct *scsi_tgtd;
++static kmem_cache_t *scsi_tgt_cmd_cache;
++
++/*
++ * TODO: this struct will be killed when the block layer supports large bios
++ * and James's work struct code is in
++ */
++struct scsi_tgt_cmd {
++      /* TODO replace work with James b's code */
++      struct work_struct work;
++      /* TODO replace the lists with a large bio */
++      struct bio_list xfer_done_list;
++      struct bio_list xfer_list;
++
++      struct list_head hash_list;
++      struct request *rq;
++
++      void *buffer;
++      unsigned bufflen;
++};
++
++#define TGT_HASH_ORDER        4
++#define cmd_hashfn(cid)       hash_long((cid), TGT_HASH_ORDER)
++
++struct scsi_tgt_queuedata {
++      struct Scsi_Host *shost;
++      struct list_head cmd_hash[1 << TGT_HASH_ORDER];
++      spinlock_t cmd_hash_lock;
++};
++
++/*
++ * Function:  scsi_host_get_command()
++ *
++ * Purpose:   Allocate and setup a scsi command block and blk request
++ *
++ * Arguments: shost   - scsi host
++ *            data_dir - dma data dir
++ *            gfp_mask- allocator flags
++ *
++ * Returns:   The allocated scsi command structure.
++ *
++ * This should be called by target LLDs to get a command.
++ */
++struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost,
++                                      enum dma_data_direction data_dir,
++                                      gfp_t gfp_mask)
++{
++      int write = (data_dir == DMA_TO_DEVICE);
++      struct request *rq;
++      struct scsi_cmnd *cmd;
++      struct scsi_tgt_cmd *tcmd;
++
++      /* Bail if we can't get a reference to the device */
++      if (!get_device(&shost->shost_gendev))
++              return NULL;
++
++      tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC);
++      if (!tcmd)
++              goto put_dev;
++
++      rq = blk_get_request(shost->uspace_req_q, write, gfp_mask);
++      if (!rq)
++              goto free_tcmd;
++
++      cmd = __scsi_get_command(shost, gfp_mask);
++      if (!cmd)
++              goto release_rq;
++
++      memset(cmd, 0, sizeof(*cmd));
++      cmd->sc_data_direction = data_dir;
++      cmd->jiffies_at_alloc = jiffies;
++      cmd->request = rq;
++
++      rq->special = cmd;
++      rq->flags |= REQ_SPECIAL | REQ_BLOCK_PC;
++      rq->end_io_data = tcmd;
++
++      bio_list_init(&tcmd->xfer_list);
++      bio_list_init(&tcmd->xfer_done_list);
++      tcmd->rq = rq;
++
++      return cmd;
++
++release_rq:
++      blk_put_request(rq);
++free_tcmd:
++      kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
++put_dev:
++      put_device(&shost->shost_gendev);
++      return NULL;
++
++}
++EXPORT_SYMBOL_GPL(scsi_host_get_command);
++
++/*
++ * Function:  scsi_host_put_command()
++ *
++ * Purpose:   Free a scsi command block
++ *
++ * Arguments: shost   - scsi host
++ *            cmd     - command block to free
++ *
++ * Returns:   Nothing.
++ *
++ * Notes:     The command must not belong to any lists.
++ */
++static void scsi_host_put_command(struct Scsi_Host *shost,
++                                struct scsi_cmnd *cmd)
++{
++      struct request_queue *q = shost->uspace_req_q;
++      struct request *rq = cmd->request;
++      struct scsi_tgt_cmd *tcmd = rq->end_io_data;
++      unsigned long flags;
++
++      kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
++
++      spin_lock_irqsave(q->queue_lock, flags);
++      __blk_put_request(q, rq);
++      spin_unlock_irqrestore(q->queue_lock, flags);
++
++      __scsi_put_command(shost, cmd, &shost->shost_gendev);
++}
++
++static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd)
++{
++      struct bio *bio;
++
++      /* must call bio_endio in case bio was bounced */
++      while ((bio = bio_list_pop(&tcmd->xfer_done_list))) {
++              bio_endio(bio, bio->bi_size, 0);
++              bio_unmap_user(bio);
++      }
++
++      while ((bio = bio_list_pop(&tcmd->xfer_list))) {
++              bio_endio(bio, bio->bi_size, 0);
++              bio_unmap_user(bio);
++      }
++}
++
++static void cmd_hashlist_del(struct scsi_cmnd *cmd)
++{
++      struct request_queue *q = cmd->request->q;
++      struct scsi_tgt_queuedata *qdata = q->queuedata;
++      unsigned long flags;
++      struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
++
++      spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
++      list_del(&tcmd->hash_list);
++      spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
++}
++
++static void scsi_tgt_cmd_destroy(void *data)
++{
++      struct scsi_cmnd *cmd = data;
++      struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
++
++      dprintk("cmd %p %d %lu\n", cmd, cmd->sc_data_direction,
++              rq_data_dir(cmd->request));
++
++      cmd_hashlist_del(cmd);
++
++      /*
++       * We must set rq->flags here because bio_map_user and
++       * blk_rq_bio_prep ruined ti.
++       */
++      if (cmd->sc_data_direction == DMA_TO_DEVICE)
++              cmd->request->flags |= 1;
++      else
++              cmd->request->flags &= ~1UL;
++
++      scsi_unmap_user_pages(tcmd);
++      scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd);
++}
++
++static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd)
++{
++      struct scsi_tgt_queuedata *qdata = rq->q->queuedata;
++      unsigned long flags;
++      struct list_head *head;
++      static u32 tag = 0;
++
++      spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
++      rq->tag = tag++;
++      head = &qdata->cmd_hash[cmd_hashfn(rq->tag)];
++      list_add(&tcmd->hash_list, head);
++      spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
++}
++
++/*
++ * scsi_tgt_alloc_queue - setup queue used for message passing
++ * shost: scsi host
++ *
++ * This should be called by the LLD after host allocation.
++ * And will be released when the host is released.
++ */
++int scsi_tgt_alloc_queue(struct Scsi_Host *shost)
++{
++      struct scsi_tgt_queuedata *queuedata;
++      struct request_queue *q;
++      int err, i;
++
++      /*
++       * Do we need to send a netlink event or should uspace
++       * just respond to the hotplug event?
++       */
++      q = __scsi_alloc_queue(shost, NULL);
++      if (!q)
++              return -ENOMEM;
++
++      queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL);
++      if (!queuedata) {
++              err = -ENOMEM;
++              goto cleanup_queue;
++      }
++      queuedata->shost = shost;
++      q->queuedata = queuedata;
++
++      /*
++       * this is a silly hack. We should probably just queue as many
++       * command as is recvd to userspace. uspace can then make
++       * sure we do not overload the HBA
++       */
++      q->nr_requests = shost->hostt->can_queue;
++      /*
++       * We currently only support software LLDs so this does
++       * not matter for now. Do we need this for the cards we support?
++       * If so we should make it a host template value.
++       */
++      blk_queue_dma_alignment(q, 0);
++      shost->uspace_req_q = q;
++
++      for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++)
++              INIT_LIST_HEAD(&queuedata->cmd_hash[i]);
++      spin_lock_init(&queuedata->cmd_hash_lock);
++
++      return 0;
++
++cleanup_queue:
++      blk_cleanup_queue(q);
++      return err;
++}
++EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue);
++
++struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd)
++{
++      struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata;
++      return queue->shost;
++}
++EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host);
++
++/*
++ * scsi_tgt_queue_command - queue command for userspace processing
++ * @cmd:      scsi command
++ * @scsilun:  scsi lun
++ * @tag:      unique value to identify this command for tmf
++ */
++int scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun,
++                         u64 tag)
++{
++      struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
++      int err;
++
++      init_scsi_tgt_cmd(cmd->request, tcmd);
++      err = scsi_tgt_uspace_send_cmd(cmd, scsilun, tag);
++      if (err)
++              cmd_hashlist_del(cmd);
++
++      return err;
++}
++EXPORT_SYMBOL_GPL(scsi_tgt_queue_command);
++
++/*
++ * This is run from a interrpt handler normally and the unmap
++ * needs process context so we must queue
++ */
++static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd)
++{
++      struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
++
++      dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request));
++
++      scsi_tgt_uspace_send_status(cmd);
++      INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy, cmd);
++      queue_work(scsi_tgtd, &tcmd->work);
++}
++
++static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd)
++{
++      struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
++      int err;
++
++      dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request));
++
++      err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done);
++      switch (err) {
++      case SCSI_MLQUEUE_HOST_BUSY:
++      case SCSI_MLQUEUE_DEVICE_BUSY:
++              return -EAGAIN;
++      }
++
++      return 0;
++}
++
++static void scsi_tgt_transfer_response(struct scsi_cmnd *cmd)
++{
++      int err;
++
++      err = __scsi_tgt_transfer_response(cmd);
++      if (!err)
++              return;
++
++      cmd->result = DID_BUS_BUSY << 16;
++      err = scsi_tgt_uspace_send_status(cmd);
++      if (err <= 0)
++              /* the eh will have to pick this up */
++              printk(KERN_ERR "Could not send cmd %p status\n", cmd);
++}
++
++static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask)
++{
++      struct request *rq = cmd->request;
++      struct scsi_tgt_cmd *tcmd = rq->end_io_data;
++      int count;
++
++      cmd->use_sg = rq->nr_phys_segments;
++      cmd->request_buffer = scsi_alloc_sgtable(cmd, gfp_mask);
++      if (!cmd->request_buffer)
++              return -ENOMEM;
++
++      cmd->request_bufflen = rq->data_len;
++
++      dprintk("cmd %p addr %p cnt %d %lu\n", cmd, tcmd->buffer, cmd->use_sg,
++              rq_data_dir(rq));
++      count = blk_rq_map_sg(rq->q, rq, cmd->request_buffer);
++      if (likely(count <= cmd->use_sg)) {
++              cmd->use_sg = count;
++              return 0;
++      }
++
++      eprintk("cmd %p addr %p cnt %d\n", cmd, tcmd->buffer, cmd->use_sg);
++      scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
++      return -EINVAL;
++}
++
++/* TODO: test this crap and replace bio_map_user with new interface maybe */
++static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd 
*cmd,
++                             int rw)
++{
++      struct request_queue *q = cmd->request->q;
++      struct request *rq = cmd->request;
++      void *uaddr = tcmd->buffer;
++      unsigned int len = tcmd->bufflen;
++      struct bio *bio;
++      int err;
++
++      while (len > 0) {
++              dprintk("%lx %u\n", (unsigned long) uaddr, len);
++              bio = bio_map_user(q, NULL, (unsigned long) uaddr, len, rw);
++              if (IS_ERR(bio)) {
++                      err = PTR_ERR(bio);
++                      dprintk("fail to map %lx %u %d %x\n",
++                              (unsigned long) uaddr, len, err, cmd->cmnd[0]);
++                      goto unmap_bios;
++              }
++
++              uaddr += bio->bi_size;
++              len -= bio->bi_size;
++
++              /*
++               * The first bio is added and merged. We could probably
++               * try to add others using scsi_merge_bio() but for now
++               * we keep it simple. The first bio should be pretty large
++               * (either hitting the 1 MB bio pages limit or a queue limit)
++               * already but for really large IO we may want to try and
++               * merge these.
++               */
++              if (!rq->bio) {
++                      blk_rq_bio_prep(q, rq, bio);
++                      rq->data_len = bio->bi_size;
++              } else
++                      /* put list of bios to transfer in next go around */
++                      bio_list_add(&tcmd->xfer_list, bio);
++      }
++
++      cmd->offset = 0;
++      err = scsi_tgt_init_cmd(cmd, GFP_KERNEL);
++      if (err)
++              goto unmap_bios;
++
++      return 0;
++
++unmap_bios:
++      if (rq->bio) {
++              bio_unmap_user(rq->bio);
++              while ((bio = bio_list_pop(&tcmd->xfer_list)))
++                      bio_unmap_user(bio);
++      }
++
++      return err;
++}
++
++static int scsi_tgt_transfer_data(struct scsi_cmnd *);
++
++static void scsi_tgt_data_transfer_done(struct scsi_cmnd *cmd)
++{
++      struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
++      struct bio *bio;
++      int err;
++
++      /* should we free resources here on error ? */
++      if (cmd->result) {
++send_uspace_err:
++              err = scsi_tgt_uspace_send_status(cmd);
++              if (err <= 0)
++                      /* the tgt uspace eh will have to pick this up */
++                      printk(KERN_ERR "Could not send cmd %p status\n", cmd);
++              return;
++      }
++
++      dprintk("cmd %p request_bufflen %u bufflen %u\n",
++              cmd, cmd->request_bufflen, tcmd->bufflen);
++
++      scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
++      bio_list_add(&tcmd->xfer_done_list, cmd->request->bio);
++
++      tcmd->buffer += cmd->request_bufflen;
++      cmd->offset += cmd->request_bufflen;
++
++      if (!tcmd->xfer_list.head) {
++              scsi_tgt_transfer_response(cmd);
++              return;
++      }
++
++      dprintk("cmd2 %p request_bufflen %u bufflen %u\n",
++              cmd, cmd->request_bufflen, tcmd->bufflen);
++
++      bio = bio_list_pop(&tcmd->xfer_list);
++      BUG_ON(!bio);
++
++      blk_rq_bio_prep(cmd->request->q, cmd->request, bio);
++      cmd->request->data_len = bio->bi_size;
++      err = scsi_tgt_init_cmd(cmd, GFP_ATOMIC);
++      if (err) {
++              cmd->result = DID_ERROR << 16;
++              goto send_uspace_err;
++      }
++
++      if (scsi_tgt_transfer_data(cmd)) {
++              cmd->result = DID_NO_CONNECT << 16;
++              goto send_uspace_err;
++      }
++}
++
++static int scsi_tgt_transfer_data(struct scsi_cmnd *cmd)
++{
++      int err;
++      struct Scsi_Host *host = scsi_tgt_cmd_to_host(cmd);
++
++      err = host->hostt->transfer_data(cmd, scsi_tgt_data_transfer_done);
++      switch (err) {
++              case SCSI_MLQUEUE_HOST_BUSY:
++              case SCSI_MLQUEUE_DEVICE_BUSY:
++                      return -EAGAIN;
++      default:
++              return 0;
++      }
++}
++
++static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr,
++                              unsigned len)
++{
++      char __user *p = (char __user *) uaddr;
++
++      if (copy_from_user(cmd->sense_buffer, p,
++                         min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) {
++              printk(KERN_ERR "Could not copy the sense buffer\n");
++              return -EIO;
++      }
++      return 0;
++}
++
++static int scsi_tgt_abort_cmd(struct Scsi_Host *host, struct scsi_cmnd *cmd)
++{
++      int err;
++
++      err = host->hostt->eh_abort_handler(cmd);
++      if (err)
++              eprintk("fail to abort %p\n", cmd);
++
++      scsi_tgt_cmd_destroy(cmd);
++      return err;
++}
++
++static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u32 cid)
++{
++      struct scsi_tgt_queuedata *qdata = q->queuedata;
++      struct request *rq = NULL;
++      struct list_head *head;
++      struct scsi_tgt_cmd *tcmd;
++      unsigned long flags;
++
++      head = &qdata->cmd_hash[cmd_hashfn(cid)];
++      spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
++      list_for_each_entry(tcmd, head, hash_list) {
++              if (tcmd->rq->tag == cid) {
++                      rq = tcmd->rq;
++                      break;
++              }
++      }
++      spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
++
++      return rq;
++}
++
++int scsi_tgt_kspace_exec(int host_no, u32 cid, int result, u32 len,
++                       unsigned long uaddr, u8 rw)
++{
++      struct Scsi_Host *shost;
++      struct scsi_cmnd *cmd;
++      struct request *rq;
++      struct scsi_tgt_cmd *tcmd;
++      int err = 0;
++
++      dprintk("%d %u %d %u %lx %u\n", host_no, cid, result,
++              len, uaddr, rw);
++
++      /* TODO: replace with a O(1) alg */
++      shost = scsi_host_lookup(host_no);
++      if (IS_ERR(shost)) {
++              printk(KERN_ERR "Could not find host no %d\n", host_no);
++              return -EINVAL;
++      }
++
++      if (!shost->uspace_req_q) {
++              printk(KERN_ERR "Not target scsi host %d\n", host_no);
++              goto done;
++      }
++
++      rq = tgt_cmd_hash_lookup(shost->uspace_req_q, cid);
++      if (!rq) {
++              printk(KERN_ERR "Could not find cid %u\n", cid);
++              err = -EINVAL;
++              goto done;
++      }
++      cmd = rq->special;
++
++      dprintk("cmd %p result %d len %d bufflen %u %lu %x\n", cmd,
++              result, len, cmd->request_bufflen, rq_data_dir(rq), 
cmd->cmnd[0]);
++
++      if (result == TASK_ABORTED) {
++              scsi_tgt_abort_cmd(shost, cmd);
++              goto done;
++      }
++      /*
++       * store the userspace values here, the working values are
++       * in the request_* values
++       */
++      tcmd = cmd->request->end_io_data;
++      tcmd->buffer = (void *)uaddr;
++      tcmd->bufflen = len;
++      cmd->result = result;
++
++      if (!tcmd->bufflen || cmd->request_buffer) {
++              err = __scsi_tgt_transfer_response(cmd);
++              goto done;
++      }
++
++      /*
++       * TODO: Do we need to handle case where request does not
++       * align with LLD.
++       */
++      err = scsi_map_user_pages(rq->end_io_data, cmd, rw);
++      if (err) {
++              eprintk("%p %d\n", cmd, err);
++              err = -EAGAIN;
++              goto done;
++      }
++
++      /* userspace failure */
++      if (cmd->result) {
++              if (status_byte(cmd->result) == CHECK_CONDITION)
++                      scsi_tgt_copy_sense(cmd, uaddr, len);
++              err = __scsi_tgt_transfer_response(cmd);
++              goto done;
++      }
++      /* ask the target LLD to transfer the data to the buffer */
++      err = scsi_tgt_transfer_data(cmd);
++
++done:
++      scsi_host_put(shost);
++      return err;
++}
++
++int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, int function, u64 tag,
++                            struct scsi_lun *scsilun, void *data)
++{
++      int err;
++
++      /* TODO: need to retry if this fails. */
++      err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, function,
++                                          tag, scsilun, data);
++      if (err < 0)
++              eprintk("The task management request lost!\n");
++      return err;
++}
++EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request);
++
++int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result)
++{
++      struct Scsi_Host *shost;
++      int err = -EINVAL;
++
++      dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid);
++
++      shost = scsi_host_lookup(host_no);
++      if (IS_ERR(shost)) {
++              printk(KERN_ERR "Could not find host no %d\n", host_no);
++              return -EINVAL;
++      }
++
++      if (!shost->uspace_req_q) {
++              printk(KERN_ERR "Not target scsi host %d\n", host_no);
++              goto done;
++      }
++
++      err = shost->hostt->tsk_mgmt_response(mid, result);
++done:
++      scsi_host_put(shost);
++
++      return err;
++}
++
++static int __init scsi_tgt_init(void)
++{
++      int err;
++
++      scsi_tgt_cmd_cache = kmem_cache_create("scsi_tgt_cmd",
++                                             sizeof(struct scsi_tgt_cmd),
++                                             0, 0, NULL, NULL);
++      if (!scsi_tgt_cmd_cache)
++              return -ENOMEM;
++
++      scsi_tgtd = create_workqueue("scsi_tgtd");
++      if (!scsi_tgtd) {
++              err = -ENOMEM;
++              goto free_kmemcache;
++      }
++
++      err = scsi_tgt_if_init();
++      if (err)
++              goto destroy_wq;
++
++      return 0;
++
++destroy_wq:
++      destroy_workqueue(scsi_tgtd);
++free_kmemcache:
++      kmem_cache_destroy(scsi_tgt_cmd_cache);
++      return err;
++}
++
++static void __exit scsi_tgt_exit(void)
++{
++      destroy_workqueue(scsi_tgtd);
++      scsi_tgt_if_exit();
++      kmem_cache_destroy(scsi_tgt_cmd_cache);
++}
++
++module_init(scsi_tgt_init);
++module_exit(scsi_tgt_exit);
++
++MODULE_DESCRIPTION("SCSI target core");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h
+new file mode 100644
+index 0000000..bd16a2c
+--- /dev/null
++++ b/drivers/scsi/scsi_tgt_priv.h
+@@ -0,0 +1,24 @@
++struct scsi_cmnd;
++struct scsi_lun;
++struct Scsi_Host;
++struct task_struct;
++
++/* tmp - will replace with SCSI logging stuff */
++#define eprintk(fmt, args...)                                 \
++do {                                                          \
++      printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);  \
++} while (0)
++
++#define dprintk eprintk
++
++extern void scsi_tgt_if_exit(void);
++extern int scsi_tgt_if_init(void);
++
++extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun 
*lun,
++                                  u64 tag);
++extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd);
++extern int scsi_tgt_kspace_exec(int host_no, u32 cid, int result, u32 len,
++                              unsigned long uaddr, u8 rw);
++extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag,
++                                       struct scsi_lun *scsilun, void *data);
++extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result);
+diff --git a/fs/bio.c b/fs/bio.c
+index 6a0b9ad..ade77bf 100644
+--- a/fs/bio.c
++++ b/fs/bio.c
+@@ -621,10 +621,9 @@ static struct bio *__bio_map_user_iov(re
+ 
+               nr_pages += end - start;
+               /*
+-               * transfer and buffer must be aligned to at least hardsector
+-               * size for now, in the future we can relax this restriction
++               * buffer must be aligned to at least hardsector size for now
+                */
+-              if ((uaddr & queue_dma_alignment(q)) || (len & 
queue_dma_alignment(q)))
++              if (uaddr & queue_dma_alignment(q))
+                       return ERR_PTR(-EINVAL);
+       }
+ 
+@@ -750,7 +749,6 @@ struct bio *bio_map_user_iov(request_que
+                            int write_to_vm)
+ {
+       struct bio *bio;
+-      int len = 0, i;
+ 
+       bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm);
+ 
+@@ -765,18 +763,7 @@ struct bio *bio_map_user_iov(request_que
+        */
+       bio_get(bio);
+ 
+-      for (i = 0; i < iov_count; i++)
+-              len += iov[i].iov_len;
+-
+-      if (bio->bi_size == len)
+-              return bio;
+-
+-      /*
+-       * don't support partial mappings
+-       */
+-      bio_endio(bio, bio->bi_size, 0);
+-      bio_unmap_user(bio);
+-      return ERR_PTR(-EINVAL);
++      return bio;
+ }
+ 
+ static void __bio_unmap_user(struct bio *bio)
+diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
+index 59e1259..cec7ccf 100644
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -624,7 +624,8 @@ extern void blk_queue_activity_fn(reques
+ extern int blk_rq_map_user(request_queue_t *, struct request *, void __user 
*, unsigned int);
+ extern int blk_rq_unmap_user(struct bio *, unsigned int);
+ extern int blk_rq_map_kern(request_queue_t *, struct request *, void *, 
unsigned int, gfp_t);
+-extern int blk_rq_map_user_iov(request_queue_t *, struct request *, struct 
sg_iovec *, int);
++extern int blk_rq_map_user_iov(request_queue_t *, struct request *,
++                             struct sg_iovec *, int, unsigned int);
+ extern int blk_execute_rq(request_queue_t *, struct gendisk *,
+                         struct request *, int);
+ extern void blk_execute_rq_nowait(request_queue_t *, struct gendisk *,
+diff --git a/include/scsi/libsrp.h b/include/scsi/libsrp.h
+new file mode 100644
+index 0000000..9dd10ff
+--- /dev/null
++++ b/include/scsi/libsrp.h
+@@ -0,0 +1,75 @@
++#ifndef __LIBSRP_H__
++#define __LIBSRP_H__
++
++#include <linux/list.h>
++#include <scsi/scsi_cmnd.h>
++#include <scsi/scsi_host.h>
++#include <scsi/srp.h>
++
++enum iue_flags {
++      V_DIOVER,
++      V_WRITE,
++      V_LINKED,
++      V_FLYING,
++};
++
++struct srp_buf {
++      dma_addr_t dma;
++      void *buf;
++};
++
++struct srp_queue {
++      void *pool;
++      void *items;
++      struct kfifo *queue;
++      spinlock_t lock;
++};
++
++struct srp_target {
++      struct Scsi_Host *shost;
++      struct device *dev;
++
++      spinlock_t lock;
++      struct list_head cmd_queue;
++
++      size_t srp_iu_size;
++      struct srp_queue iu_queue;
++      size_t rx_ring_size;
++      struct srp_buf **rx_ring;
++
++      /* IB needs tx_ring too */
++
++      void *ldata;
++};
++
++struct iu_entry {
++      struct srp_target *target;
++      struct scsi_cmnd *scmd;
++
++      struct list_head ilist;
++      dma_addr_t remote_token;
++      unsigned long flags;
++
++      struct srp_buf *sbuf;
++};
++
++typedef int (rdma_io_t) (struct iu_entry *, struct scatterlist *, int,
++                       struct srp_direct_buf *, int,
++                       enum dma_data_direction, unsigned int);
++
++static inline struct srp_target *host_to_target(struct Scsi_Host *host)
++{
++      return (struct srp_target *) host->hostdata;
++}
++
++extern int srp_target_alloc(struct srp_target *, struct device *, size_t, 
size_t);
++extern void srp_target_free(struct srp_target *);
++
++extern struct iu_entry *srp_iu_get(struct srp_target *);
++extern void srp_iu_put(struct iu_entry *);
++
++extern int srp_cmd_perform(struct iu_entry *iue, struct srp_cmd *cmd);
++extern int srp_transfer_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd,
++                           rdma_io_t rdma_io);
++
++#endif
+diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
+index 1ace1b9..d69dbf5 100644
+--- a/include/scsi/scsi_cmnd.h
++++ b/include/scsi/scsi_cmnd.h
+@@ -8,6 +8,7 @@
+ 
+ struct request;
+ struct scatterlist;
++struct Scsi_Host;
+ struct scsi_device;
+ struct scsi_request;
+ 
+@@ -84,6 +85,8 @@ struct scsi_cmnd {
+       unsigned short sglist_len;      /* size of malloc'd scatter-gather list 
*/
+       unsigned bufflen;       /* Size of data buffer */
+       void *buffer;           /* Data buffer */
++      /* offset in cmd we are at (for multi-transfer tgt cmds) */
++      unsigned offset;
+ 
+       unsigned underflow;     /* Return error if less than
+                                  this amount is transferred */
+@@ -148,8 +151,13 @@ struct scsi_cmnd {
+ 
+ 
+ extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t);
++extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t);
+ extern void scsi_put_command(struct scsi_cmnd *);
++extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *,
++                             struct device *);
+ extern void scsi_io_completion(struct scsi_cmnd *, unsigned int, unsigned 
int);
+ extern void scsi_finish_command(struct scsi_cmnd *cmd);
++extern struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *, gfp_t);
++extern void scsi_free_sgtable(struct scatterlist *, int);
+ 
+ #endif /* _SCSI_SCSI_CMND_H */
+diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
+index de6ce54..353abe5 100644
+--- a/include/scsi/scsi_host.h
++++ b/include/scsi/scsi_host.h
+@@ -7,6 +7,7 @@
+ #include <linux/workqueue.h>
+ #include <linux/mutex.h>
+ 
++struct request_queue;
+ struct block_device;
+ struct completion;
+ struct module;
+@@ -123,6 +124,39 @@ struct scsi_host_template {
+                            void (*done)(struct scsi_cmnd *));
+ 
+       /*
++       * The transfer functions are used to queue a scsi command to
++       * the LLD. When the driver is finished processing the command
++       * the done callback is invoked.
++       *
++       * return values: see queuecommand
++       *
++       * If the LLD accepts the cmd, it should set the result to an
++       * appropriate value when completed before calling the done function.
++       *
++       * STATUS: REQUIRED FOR TARGET DRIVERS
++       */
++      /* TODO: rename */
++      int (* transfer_response)(struct scsi_cmnd *,
++                                void (*done)(struct scsi_cmnd *));
++      /*
++       * This is called to inform the LLD to transfer cmd->request_bufflen
++       * bytes of the cmd at cmd->offset in the cmd. The cmd->use_sg
++       * speciefies the number of scatterlist entried in the command
++       * and cmd->request_buffer contains the scatterlist.
++       *
++       * If the command cannot be processed in one transfer_data call
++       * becuase a scatterlist within the LLD's limits cannot be
++       * created then transfer_data will be called multiple times.
++       * It is initially called from process context, and later
++       * calls are from the interrup context.
++       */
++      int (* transfer_data)(struct scsi_cmnd *,
++                            void (*done)(struct scsi_cmnd *));
++
++      /* Used as callback for the completion of task management request. */
++      int (* tsk_mgmt_response)(u64 mid, int result);
++
++      /*
+        * This is an error handling strategy routine.  You don't need to
+        * define one of these if you don't want to - there is a default
+        * routine that is present that should work in most cases.  For those
+@@ -557,6 +591,12 @@ struct Scsi_Host {
+        */
+       unsigned int max_host_blocked;
+ 
++      /*
++       * q used for scsi_tgt msgs, async events or any other requests that
++       * need to be processed in userspace
++       */
++      struct request_queue *uspace_req_q;
++
+       /* legacy crap */
+       unsigned long base;
+       unsigned long io_port;
+@@ -659,6 +699,9 @@ extern void scsi_unblock_requests(struct
+ extern void scsi_block_requests(struct Scsi_Host *);
+ 
+ struct class_container;
++
++extern struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,
++                                           void (*) (struct request_queue *));
+ /*
+  * These two functions are used to allocate and free a pseudo device
+  * which will connect to the host adapter itself rather than any
+diff --git a/include/scsi/scsi_tgt.h b/include/scsi/scsi_tgt.h
+new file mode 100644
+index 0000000..61e8ee9
+--- /dev/null
++++ b/include/scsi/scsi_tgt.h
+@@ -0,0 +1,17 @@
++/*
++ * SCSI target definitions
++ */
++
++#include <linux/dma-mapping.h>
++
++struct Scsi_Host;
++struct scsi_cmnd;
++struct scsi_lun;
++
++extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *);
++extern int scsi_tgt_alloc_queue(struct Scsi_Host *);
++extern int scsi_tgt_queue_command(struct scsi_cmnd *, struct scsi_lun *, u64);
++extern int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *, int, u64, struct 
scsi_lun *,
++                                   void *);
++extern struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *,
++                                             enum dma_data_direction, gfp_t);
+diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h
+new file mode 100644
+index 0000000..0cc1b9b
+--- /dev/null
++++ b/include/scsi/scsi_tgt_if.h
+@@ -0,0 +1,91 @@
++/*
++ * SCSI target kernel/user interface
++ *
++ * Copyright (C) 2005 FUJITA Tomonori <tomof@xxxxxxx>
++ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
++ *
++ * 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
++ */
++#ifndef __SCSI_TARGET_IF_H
++#define __SCSI_TARGET_IF_H
++
++enum tgt_event_type {
++      /* user -> kernel */
++      TGT_UEVENT_CMD_RSP,
++      TGT_UEVENT_TSK_MGMT_RSP,
++
++      /* kernel -> user */
++      TGT_KEVENT_CMD_REQ,
++      TGT_KEVENT_CMD_DONE,
++      TGT_KEVENT_TSK_MGMT_REQ,
++};
++
++struct tgt_event {
++      uint32_t type;
++      /* user-> kernel */
++      union {
++              struct {
++                      int host_no;
++                      uint32_t cid;
++                      uint32_t len;
++                      int result;
++                      uint64_t uaddr;
++                      uint8_t rw;
++              } cmd_rsp;
++              struct {
++                      int host_no;
++                      uint64_t mid;
++                      int result;
++              } tsk_mgmt_rsp;
++      } u;
++
++      /* kernel -> user */
++      union {
++              struct {
++                      int host_no;
++                      uint32_t cid;
++                      uint32_t data_len;
++                      uint8_t scb[16];
++                      uint8_t lun[8];
++                      int attribute;
++                      uint64_t tag;
++                      uint64_t uaddr;
++              } cmd_req;
++              struct {
++                      int host_no;
++                      uint32_t cid;
++                      int result;
++              } cmd_done;
++              struct {
++                      int host_no;
++                      int function;
++                      uint64_t tag;
++                      uint8_t lun[8];
++                      uint64_t mid;
++              } tsk_mgmt_req;
++      } k;
++
++} __attribute__ ((aligned (sizeof(uint64_t))));
++
++#define TGT_RINGBUF_SIZE (1UL << 16)
++
++struct rbuf_hdr {
++      uint32_t status;
++      uint32_t len;
++      uint64_t data[0];
++} __attribute__ ((aligned (sizeof(uint64_t))));
++
++#endif
+-- 
+1.1.5

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH 6/6] add scsi target patch, FUJITA Tomonori <=