summaryrefslogtreecommitdiffstats
path: root/linux-3.3-virtio-scsi.patch
diff options
context:
space:
mode:
authorJustin M. Forbes <jforbes@redhat.com>2012-02-07 14:31:54 -0600
committerJustin M. Forbes <jforbes@redhat.com>2012-02-07 14:31:54 -0600
commit0aaee590a8513bbf0e39bbaafc8e016b46c4cd82 (patch)
treeb42b61e96ae989cddb5233509a2db0726dbad460 /linux-3.3-virtio-scsi.patch
parentbddef7d222b7f68d44b28d9aaeb5d17dba2f939d (diff)
downloadkernel-0aaee590a8513bbf0e39bbaafc8e016b46c4cd82.tar.gz
kernel-0aaee590a8513bbf0e39bbaafc8e016b46c4cd82.tar.xz
kernel-0aaee590a8513bbf0e39bbaafc8e016b46c4cd82.zip
Add virtio-scsi support
Diffstat (limited to 'linux-3.3-virtio-scsi.patch')
-rw-r--r--linux-3.3-virtio-scsi.patch993
1 files changed, 993 insertions, 0 deletions
diff --git a/linux-3.3-virtio-scsi.patch b/linux-3.3-virtio-scsi.patch
new file mode 100644
index 000000000..70b3673a6
--- /dev/null
+++ b/linux-3.3-virtio-scsi.patch
@@ -0,0 +1,993 @@
+From 43cf1b6a4ee31e69581042a0c85d1398f83dcedc Mon Sep 17 00:00:00 2001
+From: Paolo Bonzini <pbonzini@redhat.com>
+Date: Fri, 20 Jan 2012 17:27:20 +0100
+Cc: <linux-scsi@vger.kernel.org>
+Cc: Rusty Russell <rusty@rustcorp.com.au>
+Cc: kvm@vger.kernel.org
+Cc: Pekka Enberg <penberg@kernel.org>
+Cc: Michael S. Tsirkin <mst@redhat.com>
+Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>, Mike Christie <michaelc@cs.wisc.edu>
+Subject: [PATCH v5 0/3] virtio-scsi driver
+
+This is the first implementation of the virtio-scsi driver, a virtual
+HBA that will be supported by KVM. It implements a subset of the spec,
+in particular it does not implement asynchronous notifications for either
+LUN reset/removal/addition or CD-ROM media events, but it is already
+functional and usable.
+
+Other matching bits:
+
+- spec at http://people.redhat.com/pbonzini/virtio-spec.pdf
+
+- QEMU implementation at git://github.com/bonzini/qemu.git,
+ branch virtio-scsi
+
+Please review. Getting this in 3.3 is starting to look like wishful thinking,
+but the possibility of regressions is obviously zero so I'm still dreaming.
+Otherwise, that would be 3.4.
+
+Paolo Bonzini (3):
+ virtio-scsi: first version
+ virtio-scsi: add error handling
+ virtio-scsi: add power management support
+
+v4->v5: change virtio id from 7 to 8
+
+v3->v4: renamed VIRTIO_SCSI_S_UNDERRUN to VIRTIO_SCSI_S_OVERRUN;
+ fixed 32-bit compilation; added power management support;
+ adjusted calls to virtqueue_add_buf
+
+ drivers/scsi/Kconfig | 8 +
+ drivers/scsi/Makefile | 1 +
+ drivers/scsi/virtio_scsi.c | 594 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/virtio_ids.h | 1 +
+ include/linux/virtio_scsi.h | 114 +++++++++
+ 5 files changed, 718 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/scsi/virtio_scsi.c
+ create mode 100644 include/linux/virtio_scsi.h
+
+From 84ad93b7215e18ab1755a625ede0fb00175e79bb Mon Sep 17 00:00:00 2001
+From: Paolo Bonzini <pbonzini@redhat.com>
+Date: Tue, 29 Nov 2011 16:31:09 +0100
+Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>, Mike Christie <michaelc@cs.wisc.edu>, Pekka Enberg <penberg@kernel.org>
+Subject: [PATCH v5 1/3] virtio-scsi: first version
+
+The virtio-scsi HBA is the basis of an alternative storage stack
+for QEMU-based virtual machines (including KVM). Compared to
+virtio-blk it is more scalable, because it supports many LUNs
+on a single PCI slot), more powerful (it more easily supports
+passthrough of host devices to the guest) and more easily
+extensible (new SCSI features implemented by QEMU should not
+require updating the driver in the guest).
+
+Cc: linux-scsi <linux-scsi@vger.kernel.org>
+Cc: Rusty Russell <rusty@rustcorp.com.au>
+Cc: Michael S. Tsirkin <mst@redhat.com>
+Cc: kvm@vger.kernel.org
+Acked-by: Pekka Enberg <penberg@kernel.org>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ v4->v5: change virtio id from 7 to 8
+
+ v3->v4: renamed VIRTIO_SCSI_S_UNDERRUN to VIRTIO_SCSI_S_OVERRUN;
+ fixed 32-bit compilation; adjust call to virtqueue_add_buf
+
+ v2->v3: added mempool, formatting fixes
+
+ v1->v2: use dbg_dev, sdev_printk, scmd_printk
+ - renamed lock to vq_lock
+ - renamed cmd_vq to req_vq (and other similar changes)
+ - fixed missing break in VIRTIO_SCSI_S_OVERRUN
+ - added VIRTIO_SCSI_S_BUSY
+ - removed unused argument from virtscsi_map_cmd
+ - fixed two tabs that had slipped in
+ - moved max_sectors and cmd_per_lun from template to config space
+ - __attribute__((packed)) -> __packed
+
+ drivers/scsi/Kconfig | 8 +
+ drivers/scsi/Makefile | 1 +
+ drivers/scsi/virtio_scsi.c | 503 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/virtio_ids.h | 1 +
+ include/linux/virtio_scsi.h | 114 ++++++++++
+ 5 files changed, 627 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/scsi/virtio_scsi.c
+ create mode 100644 include/linux/virtio_scsi.h
+
+diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
+index 16570aa..827ebaf 100644
+--- a/drivers/scsi/Kconfig
++++ b/drivers/scsi/Kconfig
+@@ -1897,6 +1897,14 @@ config SCSI_BFA_FC
+ To compile this driver as a module, choose M here. The module will
+ be called bfa.
+
++config SCSI_VIRTIO
++ tristate "virtio-scsi support (EXPERIMENTAL)"
++ depends on EXPERIMENTAL && VIRTIO
++ help
++ This is the virtual HBA driver for virtio. If the kernel will
++ be used in a virtual machine, say Y or M.
++
++
+ endif # SCSI_LOWLEVEL
+
+ source "drivers/scsi/pcmcia/Kconfig"
+diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
+index 2b88749..351b28b 100644
+--- a/drivers/scsi/Makefile
++++ b/drivers/scsi/Makefile
+@@ -141,6 +141,7 @@ obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libiscsi.o libiscsi_tcp.o cxgbi/
+ obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
+ obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
+ obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
++obj-$(CONFIG_SCSI_VIRTIO) += virtio_scsi.o
+ obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
+
+ obj-$(CONFIG_ARM) += arm/
+diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
+new file mode 100644
+index 0000000..3f87ae0
+--- /dev/null
++++ b/drivers/scsi/virtio_scsi.c
+@@ -0,0 +1,503 @@
++/*
++ * Virtio SCSI HBA driver
++ *
++ * Copyright IBM Corp. 2010
++ * Copyright Red Hat, Inc. 2011
++ *
++ * Authors:
++ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
++ * Paolo Bonzini <pbonzini@redhat.com>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/mempool.h>
++#include <linux/virtio.h>
++#include <linux/virtio_ids.h>
++#include <linux/virtio_config.h>
++#include <linux/virtio_scsi.h>
++#include <scsi/scsi_host.h>
++#include <scsi/scsi_device.h>
++#include <scsi/scsi_cmnd.h>
++
++#define VIRTIO_SCSI_MEMPOOL_SZ 64
++
++/* Command queue element */
++struct virtio_scsi_cmd {
++ struct scsi_cmnd *sc;
++ union {
++ struct virtio_scsi_cmd_req cmd;
++ struct virtio_scsi_ctrl_tmf_req tmf;
++ struct virtio_scsi_ctrl_an_req an;
++ } req;
++ union {
++ struct virtio_scsi_cmd_resp cmd;
++ struct virtio_scsi_ctrl_tmf_resp tmf;
++ struct virtio_scsi_ctrl_an_resp an;
++ struct virtio_scsi_event evt;
++ } resp;
++} ____cacheline_aligned_in_smp;
++
++/* Driver instance state */
++struct virtio_scsi {
++ /* Protects ctrl_vq, req_vq and sg[] */
++ spinlock_t vq_lock;
++
++ struct virtio_device *vdev;
++ struct virtqueue *ctrl_vq;
++ struct virtqueue *event_vq;
++ struct virtqueue *req_vq;
++
++ /* For sglist construction when adding commands to the virtqueue. */
++ struct scatterlist sg[];
++};
++
++static struct kmem_cache *virtscsi_cmd_cache;
++static mempool_t *virtscsi_cmd_pool;
++
++static inline struct Scsi_Host *virtio_scsi_host(struct virtio_device *vdev)
++{
++ return vdev->priv;
++}
++
++static void virtscsi_compute_resid(struct scsi_cmnd *sc, u32 resid)
++{
++ if (!resid)
++ return;
++
++ if (!scsi_bidi_cmnd(sc)) {
++ scsi_set_resid(sc, resid);
++ return;
++ }
++
++ scsi_in(sc)->resid = min(resid, scsi_in(sc)->length);
++ scsi_out(sc)->resid = resid - scsi_in(sc)->resid;
++}
++
++/**
++ * virtscsi_complete_cmd - finish a scsi_cmd and invoke scsi_done
++ *
++ * Called with vq_lock held.
++ */
++static void virtscsi_complete_cmd(void *buf)
++{
++ struct virtio_scsi_cmd *cmd = buf;
++ struct scsi_cmnd *sc = cmd->sc;
++ struct virtio_scsi_cmd_resp *resp = &cmd->resp.cmd;
++
++ dev_dbg(&sc->device->sdev_gendev,
++ "cmd %p response %u status %#02x sense_len %u\n",
++ sc, resp->response, resp->status, resp->sense_len);
++
++ sc->result = resp->status;
++ virtscsi_compute_resid(sc, resp->resid);
++ switch (resp->response) {
++ case VIRTIO_SCSI_S_OK:
++ set_host_byte(sc, DID_OK);
++ break;
++ case VIRTIO_SCSI_S_OVERRUN:
++ set_host_byte(sc, DID_ERROR);
++ break;
++ case VIRTIO_SCSI_S_ABORTED:
++ set_host_byte(sc, DID_ABORT);
++ break;
++ case VIRTIO_SCSI_S_BAD_TARGET:
++ set_host_byte(sc, DID_BAD_TARGET);
++ break;
++ case VIRTIO_SCSI_S_RESET:
++ set_host_byte(sc, DID_RESET);
++ break;
++ case VIRTIO_SCSI_S_BUSY:
++ set_host_byte(sc, DID_BUS_BUSY);
++ break;
++ case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
++ set_host_byte(sc, DID_TRANSPORT_DISRUPTED);
++ break;
++ case VIRTIO_SCSI_S_TARGET_FAILURE:
++ set_host_byte(sc, DID_TARGET_FAILURE);
++ break;
++ case VIRTIO_SCSI_S_NEXUS_FAILURE:
++ set_host_byte(sc, DID_NEXUS_FAILURE);
++ break;
++ default:
++ scmd_printk(KERN_WARNING, sc, "Unknown response %d",
++ resp->response);
++ /* fall through */
++ case VIRTIO_SCSI_S_FAILURE:
++ set_host_byte(sc, DID_ERROR);
++ break;
++ }
++
++ WARN_ON(resp->sense_len > VIRTIO_SCSI_SENSE_SIZE);
++ if (sc->sense_buffer) {
++ memcpy(sc->sense_buffer, resp->sense,
++ min_t(u32, resp->sense_len, VIRTIO_SCSI_SENSE_SIZE));
++ if (resp->sense_len)
++ set_driver_byte(sc, DRIVER_SENSE);
++ }
++
++ mempool_free(cmd, virtscsi_cmd_pool);
++ sc->scsi_done(sc);
++}
++
++static void virtscsi_vq_done(struct virtqueue *vq, void (*fn)(void *buf))
++{
++ struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
++ struct virtio_scsi *vscsi = shost_priv(sh);
++ void *buf;
++ unsigned long flags;
++ unsigned int len;
++
++ spin_lock_irqsave(&vscsi->vq_lock, flags);
++
++ do {
++ virtqueue_disable_cb(vq);
++ while ((buf = virtqueue_get_buf(vq, &len)) != NULL)
++ fn(buf);
++ } while (!virtqueue_enable_cb(vq));
++
++ spin_unlock_irqrestore(&vscsi->vq_lock, flags);
++}
++
++static void virtscsi_req_done(struct virtqueue *vq)
++{
++ virtscsi_vq_done(vq, virtscsi_complete_cmd);
++};
++
++/* These are still stubs. */
++static void virtscsi_complete_free(void *buf)
++{
++ struct virtio_scsi_cmd *cmd = buf;
++
++ mempool_free(cmd, virtscsi_cmd_pool);
++}
++
++static void virtscsi_ctrl_done(struct virtqueue *vq)
++{
++ virtscsi_vq_done(vq, virtscsi_complete_free);
++};
++
++static void virtscsi_event_done(struct virtqueue *vq)
++{
++ virtscsi_vq_done(vq, virtscsi_complete_free);
++};
++
++static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
++ struct scsi_data_buffer *sdb)
++{
++ struct sg_table *table = &sdb->table;
++ struct scatterlist *sg_elem;
++ unsigned int idx = *p_idx;
++ int i;
++
++ for_each_sg(table->sgl, sg_elem, table->nents, i)
++ sg_set_buf(&sg[idx++], sg_virt(sg_elem), sg_elem->length);
++
++ *p_idx = idx;
++}
++
++/**
++ * virtscsi_map_cmd - map a scsi_cmd to a virtqueue scatterlist
++ * @vscsi : virtio_scsi state
++ * @cmd : command structure
++ * @out_num : number of read-only elements
++ * @in_num : number of write-only elements
++ * @req_size : size of the request buffer
++ * @resp_size : size of the response buffer
++ *
++ * Called with vq_lock held.
++ */
++static void virtscsi_map_cmd(struct virtio_scsi *vscsi,
++ struct virtio_scsi_cmd *cmd,
++ unsigned *out_num, unsigned *in_num,
++ size_t req_size, size_t resp_size)
++{
++ struct scsi_cmnd *sc = cmd->sc;
++ struct scatterlist *sg = vscsi->sg;
++ unsigned int idx = 0;
++
++ if (sc) {
++ struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
++ BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize);
++
++ /* TODO: check feature bit and fail if unsupported? */
++ BUG_ON(sc->sc_data_direction == DMA_BIDIRECTIONAL);
++ }
++
++ /* Request header. */
++ sg_set_buf(&sg[idx++], &cmd->req, req_size);
++
++ /* Data-out buffer. */
++ if (sc && sc->sc_data_direction != DMA_FROM_DEVICE)
++ virtscsi_map_sgl(sg, &idx, scsi_out(sc));
++
++ *out_num = idx;
++
++ /* Response header. */
++ sg_set_buf(&sg[idx++], &cmd->resp, resp_size);
++
++ /* Data-in buffer */
++ if (sc && sc->sc_data_direction != DMA_TO_DEVICE)
++ virtscsi_map_sgl(sg, &idx, scsi_in(sc));
++
++ *in_num = idx - *out_num;
++}
++
++static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtqueue *vq,
++ struct virtio_scsi_cmd *cmd,
++ size_t req_size, size_t resp_size, gfp_t gfp)
++{
++ unsigned int out_num, in_num;
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&vscsi->vq_lock, flags);
++
++ virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size);
++
++ ret = virtqueue_add_buf(vq, vscsi->sg, out_num, in_num, cmd, gfp);
++ if (ret >= 0)
++ virtqueue_kick(vq);
++
++ spin_unlock_irqrestore(&vscsi->vq_lock, flags);
++ return ret;
++}
++
++static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
++{
++ struct virtio_scsi *vscsi = shost_priv(sh);
++ struct virtio_scsi_cmd *cmd;
++ int ret;
++
++ dev_dbg(&sc->device->sdev_gendev,
++ "cmd %p CDB: %#02x\n", sc, sc->cmnd[0]);
++
++ ret = SCSI_MLQUEUE_HOST_BUSY;
++ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_ATOMIC);
++ if (!cmd)
++ goto out;
++
++ memset(cmd, 0, sizeof(*cmd));
++ cmd->sc = sc;
++ cmd->req.cmd = (struct virtio_scsi_cmd_req){
++ .lun[0] = 1,
++ .lun[1] = sc->device->id,
++ .lun[2] = (sc->device->lun >> 8) | 0x40,
++ .lun[3] = sc->device->lun & 0xff,
++ .tag = (unsigned long)sc,
++ .task_attr = VIRTIO_SCSI_S_SIMPLE,
++ .prio = 0,
++ .crn = 0,
++ };
++
++ BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
++ memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
++
++ if (virtscsi_kick_cmd(vscsi, vscsi->req_vq, cmd,
++ sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
++ GFP_ATOMIC) >= 0)
++ ret = 0;
++
++out:
++ return ret;
++}
++
++static struct scsi_host_template virtscsi_host_template = {
++ .module = THIS_MODULE,
++ .name = "Virtio SCSI HBA",
++ .proc_name = "virtio_scsi",
++ .queuecommand = virtscsi_queuecommand,
++ .this_id = -1,
++
++ .can_queue = 1024,
++ .dma_boundary = UINT_MAX,
++ .use_clustering = ENABLE_CLUSTERING,
++};
++
++#define virtscsi_config_get(vdev, fld) \
++ ({ \
++ typeof(((struct virtio_scsi_config *)0)->fld) __val; \
++ vdev->config->get(vdev, \
++ offsetof(struct virtio_scsi_config, fld), \
++ &__val, sizeof(__val)); \
++ __val; \
++ })
++
++#define virtscsi_config_set(vdev, fld, val) \
++ (void)({ \
++ typeof(((struct virtio_scsi_config *)0)->fld) __val = (val); \
++ vdev->config->set(vdev, \
++ offsetof(struct virtio_scsi_config, fld), \
++ &__val, sizeof(__val)); \
++ })
++
++static int __devinit virtscsi_init(struct virtio_device *vdev,
++ struct virtio_scsi *vscsi)
++{
++ int err;
++ struct virtqueue *vqs[3];
++ vq_callback_t *callbacks[] = {
++ virtscsi_ctrl_done,
++ virtscsi_event_done,
++ virtscsi_req_done
++ };
++ const char *names[] = {
++ "control",
++ "event",
++ "request"
++ };
++
++ /* Discover virtqueues and write information to configuration. */
++ err = vdev->config->find_vqs(vdev, 3, vqs, callbacks, names);
++ if (err)
++ return err;
++
++ vscsi->ctrl_vq = vqs[0];
++ vscsi->event_vq = vqs[1];
++ vscsi->req_vq = vqs[2];
++
++ virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
++ virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
++ return 0;
++}
++
++static int __devinit virtscsi_probe(struct virtio_device *vdev)
++{
++ struct Scsi_Host *shost;
++ struct virtio_scsi *vscsi;
++ int err;
++ u32 sg_elems;
++ u32 cmd_per_lun;
++
++ /* We need to know how many segments before we allocate.
++ * We need an extra sg elements at head and tail.
++ */
++ sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1;
++
++ /* Allocate memory and link the structs together. */
++ shost = scsi_host_alloc(&virtscsi_host_template,
++ sizeof(*vscsi) + sizeof(vscsi->sg[0]) * (sg_elems + 2));
++
++ if (!shost)
++ return -ENOMEM;
++
++ shost->sg_tablesize = sg_elems;
++ vscsi = shost_priv(shost);
++ vscsi->vdev = vdev;
++ vdev->priv = shost;
++
++ /* Random initializations. */
++ spin_lock_init(&vscsi->vq_lock);
++ sg_init_table(vscsi->sg, sg_elems + 2);
++
++ err = virtscsi_init(vdev, vscsi);
++ if (err)
++ goto virtscsi_init_failed;
++
++ cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1;
++ shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue);
++ shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF;
++ shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1;
++ shost->max_id = virtscsi_config_get(vdev, max_target) + 1;
++ shost->max_channel = 0;
++ shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
++ err = scsi_add_host(shost, &vdev->dev);
++ if (err)
++ goto scsi_add_host_failed;
++
++ scsi_scan_host(shost);
++
++ return 0;
++
++scsi_add_host_failed:
++ vdev->config->del_vqs(vdev);
++virtscsi_init_failed:
++ scsi_host_put(shost);
++ return err;
++}
++
++static void __devexit virtscsi_remove_vqs(struct virtio_device *vdev)
++{
++ /* Stop all the virtqueues. */
++ vdev->config->reset(vdev);
++
++ vdev->config->del_vqs(vdev);
++}
++
++static void __devexit virtscsi_remove(struct virtio_device *vdev)
++{
++ struct Scsi_Host *shost = virtio_scsi_host(vdev);
++
++ scsi_remove_host(shost);
++
++ virtscsi_remove_vqs(vdev);
++ scsi_host_put(shost);
++}
++
++static struct virtio_device_id id_table[] = {
++ { VIRTIO_ID_SCSI, VIRTIO_DEV_ANY_ID },
++ { 0 },
++};
++
++static struct virtio_driver virtio_scsi_driver = {
++ .driver.name = KBUILD_MODNAME,
++ .driver.owner = THIS_MODULE,
++ .id_table = id_table,
++ .probe = virtscsi_probe,
++ .remove = __devexit_p(virtscsi_remove),
++};
++
++static int __init init(void)
++{
++ int ret = -ENOMEM;
++
++ virtscsi_cmd_cache = KMEM_CACHE(virtio_scsi_cmd, 0);
++ if (!virtscsi_cmd_cache) {
++ printk(KERN_ERR "kmem_cache_create() for "
++ "virtscsi_cmd_cache failed\n");
++ goto error;
++ }
++
++
++ virtscsi_cmd_pool =
++ mempool_create_slab_pool(VIRTIO_SCSI_MEMPOOL_SZ,
++ virtscsi_cmd_cache);
++ if (!virtscsi_cmd_pool) {
++ printk(KERN_ERR "mempool_create() for"
++ "virtscsi_cmd_pool failed\n");
++ goto error;
++ }
++ ret = register_virtio_driver(&virtio_scsi_driver);
++ if (ret < 0)
++ goto error;
++
++ return 0;
++
++error:
++ if (virtscsi_cmd_pool) {
++ mempool_destroy(virtscsi_cmd_pool);
++ virtscsi_cmd_pool = NULL;
++ }
++ if (virtscsi_cmd_cache) {
++ kmem_cache_destroy(virtscsi_cmd_cache);
++ virtscsi_cmd_cache = NULL;
++ }
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ unregister_virtio_driver(&virtio_scsi_driver);
++ mempool_destroy(virtscsi_cmd_pool);
++ kmem_cache_destroy(virtscsi_cmd_cache);
++}
++module_init(init);
++module_exit(fini);
++
++MODULE_DEVICE_TABLE(virtio, id_table);
++MODULE_DESCRIPTION("Virtio SCSI HBA driver");
++MODULE_LICENSE("GPL");
+diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h
+index 85bb0bb..d83ae52 100644
+--- a/include/linux/virtio_ids.h
++++ b/include/linux/virtio_ids.h
+@@ -34,6 +34,7 @@
+ #define VIRTIO_ID_CONSOLE 3 /* virtio console */
+ #define VIRTIO_ID_RNG 4 /* virtio ring */
+ #define VIRTIO_ID_BALLOON 5 /* virtio balloon */
++#define VIRTIO_ID_SCSI 8 /* virtio scsi */
+ #define VIRTIO_ID_9P 9 /* 9p virtio console */
+
+ #endif /* _LINUX_VIRTIO_IDS_H */
+diff --git a/include/linux/virtio_scsi.h b/include/linux/virtio_scsi.h
+new file mode 100644
+index 0000000..8ddeafd
+--- /dev/null
++++ b/include/linux/virtio_scsi.h
+@@ -0,0 +1,114 @@
++#ifndef _LINUX_VIRTIO_SCSI_H
++#define _LINUX_VIRTIO_SCSI_H
++/* This header is BSD licensed so anyone can use the definitions to implement
++ * compatible drivers/servers. */
++
++#define VIRTIO_SCSI_CDB_SIZE 32
++#define VIRTIO_SCSI_SENSE_SIZE 96
++
++/* SCSI command request, followed by data-out */
++struct virtio_scsi_cmd_req {
++ u8 lun[8]; /* Logical Unit Number */
++ u64 tag; /* Command identifier */
++ u8 task_attr; /* Task attribute */
++ u8 prio;
++ u8 crn;
++ u8 cdb[VIRTIO_SCSI_CDB_SIZE];
++} __packed;
++
++/* Response, followed by sense data and data-in */
++struct virtio_scsi_cmd_resp {
++ u32 sense_len; /* Sense data length */
++ u32 resid; /* Residual bytes in data buffer */
++ u16 status_qualifier; /* Status qualifier */
++ u8 status; /* Command completion status */
++ u8 response; /* Response values */
++ u8 sense[VIRTIO_SCSI_SENSE_SIZE];
++} __packed;
++
++/* Task Management Request */
++struct virtio_scsi_ctrl_tmf_req {
++ u32 type;
++ u32 subtype;
++ u8 lun[8];
++ u64 tag;
++} __packed;
++
++struct virtio_scsi_ctrl_tmf_resp {
++ u8 response;
++} __packed;
++
++/* Asynchronous notification query/subscription */
++struct virtio_scsi_ctrl_an_req {
++ u32 type;
++ u8 lun[8];
++ u32 event_requested;
++} __packed;
++
++struct virtio_scsi_ctrl_an_resp {
++ u32 event_actual;
++ u8 response;
++} __packed;
++
++struct virtio_scsi_event {
++ u32 event;
++ u8 lun[8];
++ u32 reason;
++} __packed;
++
++struct virtio_scsi_config {
++ u32 num_queues;
++ u32 seg_max;
++ u32 max_sectors;
++ u32 cmd_per_lun;
++ u32 event_info_size;
++ u32 sense_size;
++ u32 cdb_size;
++ u16 max_channel;
++ u16 max_target;
++ u32 max_lun;
++} __packed;
++
++/* Response codes */
++#define VIRTIO_SCSI_S_OK 0
++#define VIRTIO_SCSI_S_OVERRUN 1
++#define VIRTIO_SCSI_S_ABORTED 2
++#define VIRTIO_SCSI_S_BAD_TARGET 3
++#define VIRTIO_SCSI_S_RESET 4
++#define VIRTIO_SCSI_S_BUSY 5
++#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
++#define VIRTIO_SCSI_S_TARGET_FAILURE 7
++#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
++#define VIRTIO_SCSI_S_FAILURE 9
++#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
++#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
++#define VIRTIO_SCSI_S_INCORRECT_LUN 12
++
++/* Controlq type codes. */
++#define VIRTIO_SCSI_T_TMF 0
++#define VIRTIO_SCSI_T_AN_QUERY 1
++#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2
++
++/* Valid TMF subtypes. */
++#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
++#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
++#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
++#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
++#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
++#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
++#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
++#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
++
++/* Events. */
++#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000
++#define VIRTIO_SCSI_T_NO_EVENT 0
++#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
++#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
++
++#define VIRTIO_SCSI_S_SIMPLE 0
++#define VIRTIO_SCSI_S_ORDERED 1
++#define VIRTIO_SCSI_S_HEAD 2
++#define VIRTIO_SCSI_S_ACA 3
++
++
++#endif /* _LINUX_VIRTIO_SCSI_H */
+--
+1.7.1
+
+
+From 3c0e8846ac0fc2175dd0e06f495b16a30b549762 Mon Sep 17 00:00:00 2001
+From: Paolo Bonzini <pbonzini@redhat.com>
+Date: Tue, 29 Nov 2011 16:33:28 +0100
+Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>, Mike Christie <michaelc@cs.wisc.edu>, Pekka Enberg <penberg@kernel.org>
+Subject: [PATCH v5 2/3] virtio-scsi: add error handling
+
+This commit adds basic error handling to the virtio-scsi
+HBA device. Task management functions are sent synchronously
+via the control virtqueue.
+
+Cc: linux-scsi <linux-scsi@vger.kernel.org>
+Cc: Rusty Russell <rusty@rustcorp.com.au>
+Cc: Michael S. Tsirkin <mst@redhat.com>
+Cc: kvm@vger.kernel.org
+Acked-by: Pekka Enberg <penberg@kernel.org>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ v3->v4: fixed 32-bit compilation; adjusted call to virtscsi_kick_cmd
+
+ v2->v3: added mempool, used GFP_NOIO instead of GFP_ATOMIC,
+ formatting fixes
+
+ v1->v2: use scmd_printk
+
+ drivers/scsi/virtio_scsi.c | 73 +++++++++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 72 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
+index 3f87ae0..68104cd 100644
+--- a/drivers/scsi/virtio_scsi.c
++++ b/drivers/scsi/virtio_scsi.c
+@@ -29,6 +29,7 @@
+ /* Command queue element */
+ struct virtio_scsi_cmd {
+ struct scsi_cmnd *sc;
++ struct completion *comp;
+ union {
+ struct virtio_scsi_cmd_req cmd;
+ struct virtio_scsi_ctrl_tmf_req tmf;
+@@ -168,11 +169,12 @@ static void virtscsi_req_done(struct virtqueue *vq)
+ virtscsi_vq_done(vq, virtscsi_complete_cmd);
+ };
+
+-/* These are still stubs. */
+ static void virtscsi_complete_free(void *buf)
+ {
+ struct virtio_scsi_cmd *cmd = buf;
+
++ if (cmd->comp)
++ complete_all(cmd->comp);
+ mempool_free(cmd, virtscsi_cmd_pool);
+ }
+
+@@ -306,12 +308,81 @@ out:
+ return ret;
+ }
+
++static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
++{
++ DECLARE_COMPLETION_ONSTACK(comp);
++ int ret;
++
++ cmd->comp = &comp;
++ ret = virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd,
++ sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
++ GFP_NOIO);
++ if (ret < 0)
++ return FAILED;
++
++ wait_for_completion(&comp);
++ if (cmd->resp.tmf.response != VIRTIO_SCSI_S_OK &&
++ cmd->resp.tmf.response != VIRTIO_SCSI_S_FUNCTION_SUCCEEDED)
++ return FAILED;
++
++ return SUCCESS;
++}
++
++static int virtscsi_device_reset(struct scsi_cmnd *sc)
++{
++ struct virtio_scsi *vscsi = shost_priv(sc->device->host);
++ struct virtio_scsi_cmd *cmd;
++
++ sdev_printk(KERN_INFO, sc->device, "device reset\n");
++ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_NOIO);
++ if (!cmd)
++ return FAILED;
++
++ memset(cmd, 0, sizeof(*cmd));
++ cmd->sc = sc;
++ cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){
++ .type = VIRTIO_SCSI_T_TMF,
++ .subtype = VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET,
++ .lun[0] = 1,
++ .lun[1] = sc->device->id,
++ .lun[2] = (sc->device->lun >> 8) | 0x40,
++ .lun[3] = sc->device->lun & 0xff,
++ };
++ return virtscsi_tmf(vscsi, cmd);
++}
++
++static int virtscsi_abort(struct scsi_cmnd *sc)
++{
++ struct virtio_scsi *vscsi = shost_priv(sc->device->host);
++ struct virtio_scsi_cmd *cmd;
++
++ scmd_printk(KERN_INFO, sc, "abort\n");
++ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_NOIO);
++ if (!cmd)
++ return FAILED;
++
++ memset(cmd, 0, sizeof(*cmd));
++ cmd->sc = sc;
++ cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){
++ .type = VIRTIO_SCSI_T_TMF,
++ .subtype = VIRTIO_SCSI_T_TMF_ABORT_TASK,
++ .lun[0] = 1,
++ .lun[1] = sc->device->id,
++ .lun[2] = (sc->device->lun >> 8) | 0x40,
++ .lun[3] = sc->device->lun & 0xff,
++ .tag = (unsigned long)sc,
++ };
++ return virtscsi_tmf(vscsi, cmd);
++}
++
+ static struct scsi_host_template virtscsi_host_template = {
+ .module = THIS_MODULE,
+ .name = "Virtio SCSI HBA",
+ .proc_name = "virtio_scsi",
+ .queuecommand = virtscsi_queuecommand,
+ .this_id = -1,
++ .eh_abort_handler = virtscsi_abort,
++ .eh_device_reset_handler = virtscsi_device_reset,
+
+ .can_queue = 1024,
+ .dma_boundary = UINT_MAX,
+--
+1.7.1
+
+
+From 43cf1b6a4ee31e69581042a0c85d1398f83dcedc Mon Sep 17 00:00:00 2001
+From: Paolo Bonzini <pbonzini@redhat.com>
+Date: Fri, 13 Jan 2012 15:30:08 +0100
+Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>, Mike Christie <michaelc@cs.wisc.edu>, Pekka Enberg <penberg@kernel.org>
+Subject: [PATCH v5 3/3] virtio-scsi: add power management support
+
+This patch adds freeze/restore handlers for the HBA. Block queues
+are managed independently by the disk devices.
+
+Cc: linux-scsi <linux-scsi@vger.kernel.org>
+Cc: Rusty Russell <rusty@rustcorp.com.au>
+Cc: Michael S. Tsirkin <mst@redhat.com>
+Cc: kvm@vger.kernel.org
+Acked-by: Pekka Enberg <penberg@kernel.org>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ The feature has been merged in the virtio core for 3.3, so the patch
+ is new in v4.
+
+ drivers/scsi/virtio_scsi.c | 26 +++++++++++++++++++++++---
+ 1 files changed, 23 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
+index 68104cd..efccd72 100644
+--- a/drivers/scsi/virtio_scsi.c
++++ b/drivers/scsi/virtio_scsi.c
+@@ -406,8 +406,8 @@ static struct scsi_host_template virtscsi_host_template = {
+ &__val, sizeof(__val)); \
+ })
+
+-static int __devinit virtscsi_init(struct virtio_device *vdev,
+- struct virtio_scsi *vscsi)
++static int virtscsi_init(struct virtio_device *vdev,
++ struct virtio_scsi *vscsi)
+ {
+ int err;
+ struct virtqueue *vqs[3];
+@@ -491,7 +491,7 @@ virtscsi_init_failed:
+ return err;
+ }
+
+-static void __devexit virtscsi_remove_vqs(struct virtio_device *vdev)
++static void virtscsi_remove_vqs(struct virtio_device *vdev)
+ {
+ /* Stop all the virtqueues. */
+ vdev->config->reset(vdev);
+@@ -509,6 +509,22 @@ static void __devexit virtscsi_remove(struct virtio_device *vdev)
+ scsi_host_put(shost);
+ }
+
++#ifdef CONFIG_PM
++static int virtscsi_freeze(struct virtio_device *vdev)
++{
++ virtscsi_remove_vqs(vdev);
++ return 0;
++}
++
++static int virtscsi_restore(struct virtio_device *vdev)
++{
++ struct Scsi_Host *sh = virtio_scsi_host(vdev);
++ struct virtio_scsi *vscsi = shost_priv(sh);
++
++ return virtscsi_init(vdev, vscsi);
++}
++#endif
++
+ static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_SCSI, VIRTIO_DEV_ANY_ID },
+ { 0 },
+@@ -519,6 +535,10 @@ static struct virtio_driver virtio_scsi_driver = {
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = virtscsi_probe,
++#ifdef CONFIG_PM
++ .freeze = virtscsi_freeze,
++ .restore = virtscsi_restore,
++#endif
+ .remove = __devexit_p(virtscsi_remove),
+ };
+
+--
+1.7.1
+