SCSI: Add virtio_scsi driver (Closes: #686636)
svn path=/dists/sid/linux/; revision=19794
This commit is contained in:
parent
77f1faf401
commit
a9e39ce1ee
|
@ -127,6 +127,7 @@ linux (3.2.38-1) UNRELEASED; urgency=low
|
|||
- Also allow this behaviour to be enabled via module parameter
|
||||
invert_brightness=1
|
||||
* [amd64] edac: Enable EDAC_SBRIDGE as module (Closes: #699283)
|
||||
* SCSI: Add virtio_scsi driver (Closes: #686636)
|
||||
|
||||
[ Aurelien Jarno ]
|
||||
* [armhf/vexpress] Add kernel udebs.
|
||||
|
|
|
@ -2626,6 +2626,7 @@ CONFIG_SCSI_PMCRAID=m
|
|||
CONFIG_SCSI_PM8001=m
|
||||
CONFIG_SCSI_SRP=m
|
||||
CONFIG_SCSI_BFA_FC=m
|
||||
CONFIG_SCSI_VIRTIO=m
|
||||
|
||||
##
|
||||
## file: drivers/scsi/aic7xxx/Kconfig.aic79xx
|
||||
|
|
|
@ -30,6 +30,8 @@ ignore-changes:
|
|||
# wanrouter was totally broken
|
||||
module:drivers/net/wan/cycx_drv
|
||||
module:net/wanrouter/wanrouter
|
||||
# No-one tries to use virtio from OOT
|
||||
module:drivers/virtio/*
|
||||
|
||||
[base]
|
||||
arches:
|
||||
|
|
|
@ -0,0 +1,782 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Sun, 5 Feb 2012 12:16:00 +0100
|
||||
Subject: [01/12] [SCSI] virtio-scsi: SCSI driver for QEMU based virtual
|
||||
machines
|
||||
|
||||
commit 4fe74b1cb051dc9d47a80e263c388cf1651783d4 upstream.
|
||||
|
||||
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).
|
||||
|
||||
Acked-by: Rusty Russell <rusty@rustcorp.com.au>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
[bwh: Backported to 3.2:
|
||||
- s/virtqueue_add_buf/&_gfp/
|
||||
- Remove virtio driver PM operations]
|
||||
---
|
||||
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(+)
|
||||
create mode 100644 drivers/scsi/virtio_scsi.c
|
||||
create mode 100644 include/linux/virtio_scsi.h
|
||||
|
||||
--- a/drivers/scsi/Kconfig
|
||||
+++ b/drivers/scsi/Kconfig
|
||||
@@ -1910,6 +1910,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"
|
||||
--- a/drivers/scsi/Makefile
|
||||
+++ b/drivers/scsi/Makefile
|
||||
@@ -141,6 +141,7 @@ obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libisc
|
||||
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_HYPERV_STORAGE) += hv_storvsc.o
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -0,0 +1,594 @@
|
||||
+/*
|
||||
+ * 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;
|
||||
+ struct completion *comp;
|
||||
+ 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);
|
||||
+};
|
||||
+
|
||||
+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);
|
||||
+}
|
||||
+
|
||||
+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_gfp(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 int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
|
||||
+{
|
||||
+ DECLARE_COMPLETION_ONSTACK(comp);
|
||||
+ int ret;
|
||||
+
|
||||
+ cmd->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,
|
||||
+ .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 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 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);
|
||||
+}
|
||||
+
|
||||
+#if 0
|
||||
+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 },
|
||||
+};
|
||||
+
|
||||
+static struct virtio_driver virtio_scsi_driver = {
|
||||
+ .driver.name = KBUILD_MODNAME,
|
||||
+ .driver.owner = THIS_MODULE,
|
||||
+ .id_table = id_table,
|
||||
+ .probe = virtscsi_probe,
|
||||
+#if 0
|
||||
+ .freeze = virtscsi_freeze,
|
||||
+ .restore = virtscsi_restore,
|
||||
+#endif
|
||||
+ .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");
|
||||
--- 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 */
|
||||
--- /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 */
|
68
debian/patches/features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch
vendored
Normal file
68
debian/patches/features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Fri, 4 May 2012 12:32:04 +0200
|
||||
Subject: [02/12] [SCSI] virtio_scsi: fix TMF use-after-free
|
||||
|
||||
commit e4594bb50518eb89c447be97dabd5bd99f405d71 upstream.
|
||||
|
||||
Fix a use-after-free in the TMF path, where cmd may have been already
|
||||
freed by virtscsi_complete_free when wait_for_completion restarts
|
||||
executing virtscsi_tmf. Technically a race, but in practice the command
|
||||
will always be freed long before the completion waiter is awoken.
|
||||
|
||||
The fix is to make callers specifying a completion responsible for
|
||||
freeing the command in all cases.
|
||||
|
||||
Signed-off-by: Hu Tao <hutao@cn.fujitsu.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 24 +++++++++++++-----------
|
||||
1 file changed, 13 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
|
||||
index efccd72..1b38431 100644
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -175,7 +175,8 @@ static void virtscsi_complete_free(void *buf)
|
||||
|
||||
if (cmd->comp)
|
||||
complete_all(cmd->comp);
|
||||
- mempool_free(cmd, virtscsi_cmd_pool);
|
||||
+ else
|
||||
+ mempool_free(cmd, virtscsi_cmd_pool);
|
||||
}
|
||||
|
||||
static void virtscsi_ctrl_done(struct virtqueue *vq)
|
||||
@@ -311,21 +312,22 @@ out:
|
||||
static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(comp);
|
||||
- int ret;
|
||||
+ int ret = FAILED;
|
||||
|
||||
cmd->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;
|
||||
+ if (virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd,
|
||||
+ sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||
+ GFP_NOIO) < 0)
|
||||
+ goto out;
|
||||
|
||||
wait_for_completion(&comp);
|
||||
- if (cmd->resp.tmf.response != VIRTIO_SCSI_S_OK &&
|
||||
- cmd->resp.tmf.response != VIRTIO_SCSI_S_FUNCTION_SUCCEEDED)
|
||||
- return FAILED;
|
||||
+ if (cmd->resp.tmf.response == VIRTIO_SCSI_S_OK ||
|
||||
+ cmd->resp.tmf.response == VIRTIO_SCSI_S_FUNCTION_SUCCEEDED)
|
||||
+ ret = SUCCESS;
|
||||
|
||||
- return SUCCESS;
|
||||
+out:
|
||||
+ mempool_free(cmd, virtscsi_cmd_pool);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int virtscsi_device_reset(struct scsi_cmnd *sc)
|
30
debian/patches/features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch
vendored
Normal file
30
debian/patches/features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Wed, 13 Jun 2012 16:56:31 +0200
|
||||
Subject: [03/12] [SCSI] virtio-scsi: unlock during kick
|
||||
|
||||
commit b5ee8f2802c559fccb177c0a117f5cd56c1049cc upstream.
|
||||
|
||||
Separate virtqueue_kick_prepare from virtqueue_notify, so that the
|
||||
expensive vmexit is done without holding the lock.
|
||||
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -264,9 +264,11 @@ static int virtscsi_kick_cmd(struct virt
|
||||
|
||||
ret = virtqueue_add_buf_gfp(vq, vscsi->sg, out_num, in_num, cmd, gfp);
|
||||
if (ret >= 0)
|
||||
- virtqueue_kick(vq);
|
||||
+ ret = virtqueue_kick_prepare(vq);
|
||||
|
||||
spin_unlock_irqrestore(&vscsi->vq_lock, flags);
|
||||
+ if (ret > 0)
|
||||
+ virtqueue_notify(vq);
|
||||
return ret;
|
||||
}
|
||||
|
195
debian/patches/features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch
vendored
Normal file
195
debian/patches/features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Wed, 13 Jun 2012 16:56:32 +0200
|
||||
Subject: [04/12] [SCSI] virtio-scsi: split locking per vq
|
||||
|
||||
commit 139fe45abc2234b20fd5ecbcb7ea6d3688fed5e5 upstream.
|
||||
|
||||
Keep a separate lock for each virtqueue. While not particularly
|
||||
important now, it prepares the code for when we will add support
|
||||
for multiple request queues. It is also more tidy as soon as
|
||||
we introduce a separate lock for the sglist.
|
||||
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
[bwh: Backported to 3.2: s/virtqueue_add_buf/&_gfp/]
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 77 ++++++++++++++++++++++++++++++--------------
|
||||
1 file changed, 52 insertions(+), 25 deletions(-)
|
||||
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -43,15 +43,22 @@ struct virtio_scsi_cmd {
|
||||
} resp;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
+struct virtio_scsi_vq {
|
||||
+ /* Protects vq */
|
||||
+ spinlock_t vq_lock;
|
||||
+
|
||||
+ struct virtqueue *vq;
|
||||
+};
|
||||
+
|
||||
/* Driver instance state */
|
||||
struct virtio_scsi {
|
||||
- /* Protects ctrl_vq, req_vq and sg[] */
|
||||
- spinlock_t vq_lock;
|
||||
+ /* Protects sg[]. The lock hierarchy is sg_lock -> vq_lock. */
|
||||
+ spinlock_t sg_lock;
|
||||
|
||||
struct virtio_device *vdev;
|
||||
- struct virtqueue *ctrl_vq;
|
||||
- struct virtqueue *event_vq;
|
||||
- struct virtqueue *req_vq;
|
||||
+ struct virtio_scsi_vq ctrl_vq;
|
||||
+ struct virtio_scsi_vq event_vq;
|
||||
+ struct virtio_scsi_vq req_vq;
|
||||
|
||||
/* For sglist construction when adding commands to the virtqueue. */
|
||||
struct scatterlist sg[];
|
||||
@@ -147,26 +154,25 @@ static void virtscsi_complete_cmd(void *
|
||||
|
||||
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)
|
||||
{
|
||||
+ struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
+ struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&vscsi->req_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vq, virtscsi_complete_cmd);
|
||||
+ spin_unlock_irqrestore(&vscsi->req_vq.vq_lock, flags);
|
||||
};
|
||||
|
||||
static void virtscsi_complete_free(void *buf)
|
||||
@@ -181,12 +187,24 @@ static void virtscsi_complete_free(void
|
||||
|
||||
static void virtscsi_ctrl_done(struct virtqueue *vq)
|
||||
{
|
||||
+ struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
+ struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&vscsi->ctrl_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vq, virtscsi_complete_free);
|
||||
+ spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags);
|
||||
};
|
||||
|
||||
static void virtscsi_event_done(struct virtqueue *vq)
|
||||
{
|
||||
+ struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
+ struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vq, virtscsi_complete_free);
|
||||
+ spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags);
|
||||
};
|
||||
|
||||
static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
|
||||
@@ -250,7 +268,7 @@ static void virtscsi_map_cmd(struct virt
|
||||
*in_num = idx - *out_num;
|
||||
}
|
||||
|
||||
-static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtqueue *vq,
|
||||
+static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *vq,
|
||||
struct virtio_scsi_cmd *cmd,
|
||||
size_t req_size, size_t resp_size, gfp_t gfp)
|
||||
{
|
||||
@@ -258,17 +276,19 @@ static int virtscsi_kick_cmd(struct virt
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
- spin_lock_irqsave(&vscsi->vq_lock, flags);
|
||||
-
|
||||
+ spin_lock_irqsave(&vscsi->sg_lock, flags);
|
||||
virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size);
|
||||
|
||||
- ret = virtqueue_add_buf_gfp(vq, vscsi->sg, out_num, in_num, cmd, gfp);
|
||||
+ spin_lock(&vq->vq_lock);
|
||||
+ ret = virtqueue_add_buf_gfp(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp);
|
||||
if (ret >= 0)
|
||||
- ret = virtqueue_kick_prepare(vq);
|
||||
+ ret = virtqueue_kick_prepare(vq->vq);
|
||||
+
|
||||
+ spin_unlock(&vq->vq_lock);
|
||||
+ spin_unlock_irqrestore(&vscsi->sg_lock, flags);
|
||||
|
||||
- spin_unlock_irqrestore(&vscsi->vq_lock, flags);
|
||||
if (ret > 0)
|
||||
- virtqueue_notify(vq);
|
||||
+ virtqueue_notify(vq->vq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -302,7 +322,7 @@ static int virtscsi_queuecommand(struct
|
||||
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,
|
||||
+ if (virtscsi_kick_cmd(vscsi, &vscsi->req_vq, cmd,
|
||||
sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
|
||||
GFP_ATOMIC) >= 0)
|
||||
ret = 0;
|
||||
@@ -317,7 +337,7 @@ static int virtscsi_tmf(struct virtio_sc
|
||||
int ret = FAILED;
|
||||
|
||||
cmd->comp = ∁
|
||||
- if (virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd,
|
||||
+ if (virtscsi_kick_cmd(vscsi, &vscsi->ctrl_vq, cmd,
|
||||
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||
GFP_NOIO) < 0)
|
||||
goto out;
|
||||
@@ -410,6 +430,13 @@ static struct scsi_host_template virtscs
|
||||
&__val, sizeof(__val)); \
|
||||
})
|
||||
|
||||
+static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq,
|
||||
+ struct virtqueue *vq)
|
||||
+{
|
||||
+ spin_lock_init(&virtscsi_vq->vq_lock);
|
||||
+ virtscsi_vq->vq = vq;
|
||||
+}
|
||||
+
|
||||
static int virtscsi_init(struct virtio_device *vdev,
|
||||
struct virtio_scsi *vscsi)
|
||||
{
|
||||
@@ -431,9 +458,9 @@ static int virtscsi_init(struct virtio_d
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- vscsi->ctrl_vq = vqs[0];
|
||||
- vscsi->event_vq = vqs[1];
|
||||
- vscsi->req_vq = vqs[2];
|
||||
+ virtscsi_init_vq(&vscsi->ctrl_vq, vqs[0]);
|
||||
+ virtscsi_init_vq(&vscsi->event_vq, vqs[1]);
|
||||
+ virtscsi_init_vq(&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);
|
||||
@@ -466,7 +493,7 @@ static int __devinit virtscsi_probe(stru
|
||||
vdev->priv = shost;
|
||||
|
||||
/* Random initializations. */
|
||||
- spin_lock_init(&vscsi->vq_lock);
|
||||
+ spin_lock_init(&vscsi->sg_lock);
|
||||
sg_init_table(vscsi->sg, sg_elems + 2);
|
||||
|
||||
err = virtscsi_init(vdev, vscsi);
|
|
@ -0,0 +1,49 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Wed, 13 Jun 2012 16:56:33 +0200
|
||||
Subject: [05/12] [SCSI] virtio-scsi: release sg_lock after add_buf
|
||||
|
||||
commit bce750b1633927be3eecf821f4d17975c3ba5b6a upstream.
|
||||
|
||||
We do not need the sglist after calling virtqueue_add_buf. Hence we
|
||||
can "pipeline" the locked operations and start preparing the sglist
|
||||
for the next request while we kick the virtqueue.
|
||||
|
||||
Together with the previous two patches, this improves performance as
|
||||
follows. For a simple "if=/dev/sda of=/dev/null bs=128M iflag=direct"
|
||||
(the source being a 10G disk, residing entirely in the host buffer cache),
|
||||
the additional locking does not cause any penalty with only one dd
|
||||
process, but 2 simultaneous I/O operations improve their times by 3%:
|
||||
|
||||
number of simultaneous dd
|
||||
1 2
|
||||
----------------------------------------
|
||||
current 5.9958s 10.2640s
|
||||
patched 5.9531s 9.8663s
|
||||
|
||||
(Times are best of 10).
|
||||
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
[bwh: Backported to 3.2: adjust context]
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
|
||||
index 0ecf95e..facfc90 100644
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -281,11 +281,11 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *v
|
||||
|
||||
spin_lock(&vq->vq_lock);
|
||||
ret = virtqueue_add_buf_gfp(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp);
|
||||
+ spin_unlock(&vscsi->sg_lock);
|
||||
if (ret >= 0)
|
||||
ret = virtqueue_kick_prepare(vq->vq);
|
||||
|
||||
- spin_unlock(&vq->vq_lock);
|
||||
- spin_unlock_irqrestore(&vscsi->sg_lock, flags);
|
||||
+ spin_unlock_irqrestore(&vq->vq_lock, flags);
|
||||
|
||||
if (ret > 0)
|
||||
virtqueue_notify(vq->vq);
|
290
debian/patches/features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch
vendored
Normal file
290
debian/patches/features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch
vendored
Normal file
|
@ -0,0 +1,290 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Wed, 13 Jun 2012 16:56:34 +0200
|
||||
Subject: [06/12] [SCSI] virtio-scsi: split scatterlist per target
|
||||
|
||||
commit 2bd37f0fde99cbf8b78fb55f1128e8c3a63cf1da upstream.
|
||||
|
||||
To improve performance for I/O to different targets, add a separate
|
||||
scatterlist for each of them.
|
||||
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
[bwh: Backported to 3.2: s/virtqueue_add_buf/&_gfp/]
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 141 +++++++++++++++++++++++++++++---------------
|
||||
1 file changed, 94 insertions(+), 47 deletions(-)
|
||||
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -50,18 +50,24 @@ struct virtio_scsi_vq {
|
||||
struct virtqueue *vq;
|
||||
};
|
||||
|
||||
+/* Per-target queue state */
|
||||
+struct virtio_scsi_target_state {
|
||||
+ /* Protects sg. Lock hierarchy is tgt_lock -> vq_lock. */
|
||||
+ spinlock_t tgt_lock;
|
||||
+
|
||||
+ /* For sglist construction when adding commands to the virtqueue. */
|
||||
+ struct scatterlist sg[];
|
||||
+};
|
||||
+
|
||||
/* Driver instance state */
|
||||
struct virtio_scsi {
|
||||
- /* Protects sg[]. The lock hierarchy is sg_lock -> vq_lock. */
|
||||
- spinlock_t sg_lock;
|
||||
-
|
||||
struct virtio_device *vdev;
|
||||
+
|
||||
struct virtio_scsi_vq ctrl_vq;
|
||||
struct virtio_scsi_vq event_vq;
|
||||
struct virtio_scsi_vq req_vq;
|
||||
|
||||
- /* For sglist construction when adding commands to the virtqueue. */
|
||||
- struct scatterlist sg[];
|
||||
+ struct virtio_scsi_target_state *tgt[];
|
||||
};
|
||||
|
||||
static struct kmem_cache *virtscsi_cmd_cache;
|
||||
@@ -230,25 +236,17 @@ static void virtscsi_map_sgl(struct scat
|
||||
* @req_size : size of the request buffer
|
||||
* @resp_size : size of the response buffer
|
||||
*
|
||||
- * Called with vq_lock held.
|
||||
+ * Called with tgt_lock held.
|
||||
*/
|
||||
-static void virtscsi_map_cmd(struct virtio_scsi *vscsi,
|
||||
+static void virtscsi_map_cmd(struct virtio_scsi_target_state *tgt,
|
||||
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;
|
||||
+ struct scatterlist *sg = tgt->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);
|
||||
|
||||
@@ -268,7 +266,8 @@ static void virtscsi_map_cmd(struct virt
|
||||
*in_num = idx - *out_num;
|
||||
}
|
||||
|
||||
-static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *vq,
|
||||
+static int virtscsi_kick_cmd(struct virtio_scsi_target_state *tgt,
|
||||
+ struct virtio_scsi_vq *vq,
|
||||
struct virtio_scsi_cmd *cmd,
|
||||
size_t req_size, size_t resp_size, gfp_t gfp)
|
||||
{
|
||||
@@ -276,12 +275,12 @@ static int virtscsi_kick_cmd(struct virt
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
- spin_lock_irqsave(&vscsi->sg_lock, flags);
|
||||
- virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size);
|
||||
+ spin_lock_irqsave(&tgt->tgt_lock, flags);
|
||||
+ virtscsi_map_cmd(tgt, cmd, &out_num, &in_num, req_size, resp_size);
|
||||
|
||||
spin_lock(&vq->vq_lock);
|
||||
- ret = virtqueue_add_buf_gfp(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp);
|
||||
- spin_unlock(&vscsi->sg_lock);
|
||||
+ ret = virtqueue_add_buf_gfp(vq->vq, tgt->sg, out_num, in_num, cmd, gfp);
|
||||
+ spin_unlock(&tgt->tgt_lock);
|
||||
if (ret >= 0)
|
||||
ret = virtqueue_kick_prepare(vq->vq);
|
||||
|
||||
@@ -295,9 +294,16 @@ static int virtscsi_kick_cmd(struct virt
|
||||
static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
|
||||
{
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
+ struct virtio_scsi_target_state *tgt = vscsi->tgt[sc->device->id];
|
||||
struct virtio_scsi_cmd *cmd;
|
||||
int ret;
|
||||
|
||||
+ 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);
|
||||
+
|
||||
dev_dbg(&sc->device->sdev_gendev,
|
||||
"cmd %p CDB: %#02x\n", sc, sc->cmnd[0]);
|
||||
|
||||
@@ -322,7 +328,7 @@ static int virtscsi_queuecommand(struct
|
||||
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,
|
||||
+ if (virtscsi_kick_cmd(tgt, &vscsi->req_vq, cmd,
|
||||
sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
|
||||
GFP_ATOMIC) >= 0)
|
||||
ret = 0;
|
||||
@@ -334,10 +340,11 @@ out:
|
||||
static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(comp);
|
||||
+ struct virtio_scsi_target_state *tgt = vscsi->tgt[cmd->sc->device->id];
|
||||
int ret = FAILED;
|
||||
|
||||
cmd->comp = ∁
|
||||
- if (virtscsi_kick_cmd(vscsi, &vscsi->ctrl_vq, cmd,
|
||||
+ if (virtscsi_kick_cmd(tgt, &vscsi->ctrl_vq, cmd,
|
||||
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||
GFP_NOIO) < 0)
|
||||
goto out;
|
||||
@@ -437,11 +444,49 @@ static void virtscsi_init_vq(struct virt
|
||||
virtscsi_vq->vq = vq;
|
||||
}
|
||||
|
||||
+static struct virtio_scsi_target_state *virtscsi_alloc_tgt(
|
||||
+ struct virtio_device *vdev, int sg_elems)
|
||||
+{
|
||||
+ struct virtio_scsi_target_state *tgt;
|
||||
+ gfp_t gfp_mask = GFP_KERNEL;
|
||||
+
|
||||
+ /* We need extra sg elements at head and tail. */
|
||||
+ tgt = kmalloc(sizeof(*tgt) + sizeof(tgt->sg[0]) * (sg_elems + 2),
|
||||
+ gfp_mask);
|
||||
+
|
||||
+ if (!tgt)
|
||||
+ return NULL;
|
||||
+
|
||||
+ spin_lock_init(&tgt->tgt_lock);
|
||||
+ sg_init_table(tgt->sg, sg_elems + 2);
|
||||
+ return tgt;
|
||||
+}
|
||||
+
|
||||
+static void virtscsi_remove_vqs(struct virtio_device *vdev)
|
||||
+{
|
||||
+ struct Scsi_Host *sh = virtio_scsi_host(vdev);
|
||||
+ struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
+ u32 i, num_targets;
|
||||
+
|
||||
+ /* Stop all the virtqueues. */
|
||||
+ vdev->config->reset(vdev);
|
||||
+
|
||||
+ num_targets = sh->max_id;
|
||||
+ for (i = 0; i < num_targets; i++) {
|
||||
+ kfree(vscsi->tgt[i]);
|
||||
+ vscsi->tgt[i] = NULL;
|
||||
+ }
|
||||
+
|
||||
+ vdev->config->del_vqs(vdev);
|
||||
+}
|
||||
+
|
||||
static int virtscsi_init(struct virtio_device *vdev,
|
||||
- struct virtio_scsi *vscsi)
|
||||
+ struct virtio_scsi *vscsi, int num_targets)
|
||||
{
|
||||
int err;
|
||||
struct virtqueue *vqs[3];
|
||||
+ u32 i, sg_elems;
|
||||
+
|
||||
vq_callback_t *callbacks[] = {
|
||||
virtscsi_ctrl_done,
|
||||
virtscsi_event_done,
|
||||
@@ -464,7 +509,23 @@ static int virtscsi_init(struct virtio_d
|
||||
|
||||
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
|
||||
virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
|
||||
- return 0;
|
||||
+
|
||||
+ /* We need to know how many segments before we allocate. */
|
||||
+ sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1;
|
||||
+
|
||||
+ for (i = 0; i < num_targets; i++) {
|
||||
+ vscsi->tgt[i] = virtscsi_alloc_tgt(vdev, sg_elems);
|
||||
+ if (!vscsi->tgt[i]) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+ err = 0;
|
||||
+
|
||||
+out:
|
||||
+ if (err)
|
||||
+ virtscsi_remove_vqs(vdev);
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static int __devinit virtscsi_probe(struct virtio_device *vdev)
|
||||
@@ -472,31 +533,25 @@ static int __devinit virtscsi_probe(stru
|
||||
struct Scsi_Host *shost;
|
||||
struct virtio_scsi *vscsi;
|
||||
int err;
|
||||
- u32 sg_elems;
|
||||
+ u32 sg_elems, num_targets;
|
||||
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. */
|
||||
+ num_targets = virtscsi_config_get(vdev, max_target) + 1;
|
||||
shost = scsi_host_alloc(&virtscsi_host_template,
|
||||
- sizeof(*vscsi) + sizeof(vscsi->sg[0]) * (sg_elems + 2));
|
||||
+ sizeof(*vscsi)
|
||||
+ + num_targets * sizeof(struct virtio_scsi_target_state));
|
||||
|
||||
if (!shost)
|
||||
return -ENOMEM;
|
||||
|
||||
+ sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1;
|
||||
shost->sg_tablesize = sg_elems;
|
||||
vscsi = shost_priv(shost);
|
||||
vscsi->vdev = vdev;
|
||||
vdev->priv = shost;
|
||||
|
||||
- /* Random initializations. */
|
||||
- spin_lock_init(&vscsi->sg_lock);
|
||||
- sg_init_table(vscsi->sg, sg_elems + 2);
|
||||
-
|
||||
- err = virtscsi_init(vdev, vscsi);
|
||||
+ err = virtscsi_init(vdev, vscsi, num_targets);
|
||||
if (err)
|
||||
goto virtscsi_init_failed;
|
||||
|
||||
@@ -504,7 +559,7 @@ static int __devinit virtscsi_probe(stru
|
||||
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_id = num_targets;
|
||||
shost->max_channel = 0;
|
||||
shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
|
||||
err = scsi_add_host(shost, &vdev->dev);
|
||||
@@ -522,14 +577,6 @@ virtscsi_init_failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
-static void 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);
|
||||
@@ -552,7 +599,7 @@ static int virtscsi_restore(struct virti
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vdev);
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
|
||||
- return virtscsi_init(vdev, vscsi);
|
||||
+ return virtscsi_init(vdev, vscsi, sh->max_id);
|
||||
}
|
||||
#endif
|
||||
|
229
debian/patches/features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch
vendored
Normal file
229
debian/patches/features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch
vendored
Normal file
|
@ -0,0 +1,229 @@
|
|||
From: Cong Meng <mc@linux.vnet.ibm.com>
|
||||
Date: Thu, 5 Jul 2012 17:06:43 +0800
|
||||
Subject: [07/12] [SCSI] virtio-scsi: hotplug support for virtio-scsi
|
||||
|
||||
commit 365a7150094114a0f8ef0b6164e6b04b519039e8 upstream.
|
||||
|
||||
This patch implements the hotplug support for virtio-scsi.
|
||||
When there is a device attached/detached, the virtio-scsi driver will be
|
||||
signaled via event virtual queue and it will add/remove the scsi device
|
||||
in question automatically.
|
||||
|
||||
Signed-off-by: Sen Wang <senwang@linux.vnet.ibm.com>
|
||||
Signed-off-by: Cong Meng <mc@linux.vnet.ibm.com>
|
||||
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
[bwh: Backported to 3.2: s/virtqueue_add_buf/&_gfp/]
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 124 ++++++++++++++++++++++++++++++++++++++++++-
|
||||
include/linux/virtio_scsi.h | 9 ++++
|
||||
2 files changed, 132 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#define VIRTIO_SCSI_MEMPOOL_SZ 64
|
||||
+#define VIRTIO_SCSI_EVENT_LEN 8
|
||||
|
||||
/* Command queue element */
|
||||
struct virtio_scsi_cmd {
|
||||
@@ -43,6 +44,12 @@ struct virtio_scsi_cmd {
|
||||
} resp;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
+struct virtio_scsi_event_node {
|
||||
+ struct virtio_scsi *vscsi;
|
||||
+ struct virtio_scsi_event event;
|
||||
+ struct work_struct work;
|
||||
+};
|
||||
+
|
||||
struct virtio_scsi_vq {
|
||||
/* Protects vq */
|
||||
spinlock_t vq_lock;
|
||||
@@ -67,6 +74,9 @@ struct virtio_scsi {
|
||||
struct virtio_scsi_vq event_vq;
|
||||
struct virtio_scsi_vq req_vq;
|
||||
|
||||
+ /* Get some buffers ready for event vq */
|
||||
+ struct virtio_scsi_event_node event_list[VIRTIO_SCSI_EVENT_LEN];
|
||||
+
|
||||
struct virtio_scsi_target_state *tgt[];
|
||||
};
|
||||
|
||||
@@ -202,6 +212,105 @@ static void virtscsi_ctrl_done(struct vi
|
||||
spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags);
|
||||
};
|
||||
|
||||
+static int virtscsi_kick_event(struct virtio_scsi *vscsi,
|
||||
+ struct virtio_scsi_event_node *event_node)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct scatterlist sg;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ sg_set_buf(&sg, &event_node->event, sizeof(struct virtio_scsi_event));
|
||||
+
|
||||
+ spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
|
||||
+
|
||||
+ ret = virtqueue_add_buf_gfp(vscsi->event_vq.vq, &sg, 0, 1, event_node, GFP_ATOMIC);
|
||||
+ if (ret >= 0)
|
||||
+ virtqueue_kick(vscsi->event_vq.vq);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int virtscsi_kick_event_all(struct virtio_scsi *vscsi)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) {
|
||||
+ vscsi->event_list[i].vscsi = vscsi;
|
||||
+ virtscsi_kick_event(vscsi, &vscsi->event_list[i]);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++)
|
||||
+ cancel_work_sync(&vscsi->event_list[i].work);
|
||||
+}
|
||||
+
|
||||
+static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi,
|
||||
+ struct virtio_scsi_event *event)
|
||||
+{
|
||||
+ struct scsi_device *sdev;
|
||||
+ struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
|
||||
+ unsigned int target = event->lun[1];
|
||||
+ unsigned int lun = (event->lun[2] << 8) | event->lun[3];
|
||||
+
|
||||
+ switch (event->reason) {
|
||||
+ case VIRTIO_SCSI_EVT_RESET_RESCAN:
|
||||
+ scsi_add_device(shost, 0, target, lun);
|
||||
+ break;
|
||||
+ case VIRTIO_SCSI_EVT_RESET_REMOVED:
|
||||
+ sdev = scsi_device_lookup(shost, 0, target, lun);
|
||||
+ if (sdev) {
|
||||
+ scsi_remove_device(sdev);
|
||||
+ scsi_device_put(sdev);
|
||||
+ } else {
|
||||
+ pr_err("SCSI device %d 0 %d %d not found\n",
|
||||
+ shost->host_no, target, lun);
|
||||
+ }
|
||||
+ break;
|
||||
+ default:
|
||||
+ pr_info("Unsupport virtio scsi event reason %x\n", event->reason);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void virtscsi_handle_event(struct work_struct *work)
|
||||
+{
|
||||
+ struct virtio_scsi_event_node *event_node =
|
||||
+ container_of(work, struct virtio_scsi_event_node, work);
|
||||
+ struct virtio_scsi *vscsi = event_node->vscsi;
|
||||
+ struct virtio_scsi_event *event = &event_node->event;
|
||||
+
|
||||
+ if (event->event & VIRTIO_SCSI_T_EVENTS_MISSED) {
|
||||
+ event->event &= ~VIRTIO_SCSI_T_EVENTS_MISSED;
|
||||
+ scsi_scan_host(virtio_scsi_host(vscsi->vdev));
|
||||
+ }
|
||||
+
|
||||
+ switch (event->event) {
|
||||
+ case VIRTIO_SCSI_T_NO_EVENT:
|
||||
+ break;
|
||||
+ case VIRTIO_SCSI_T_TRANSPORT_RESET:
|
||||
+ virtscsi_handle_transport_reset(vscsi, event);
|
||||
+ break;
|
||||
+ default:
|
||||
+ pr_err("Unsupport virtio scsi event %x\n", event->event);
|
||||
+ }
|
||||
+ virtscsi_kick_event(vscsi, event_node);
|
||||
+}
|
||||
+
|
||||
+static void virtscsi_complete_event(void *buf)
|
||||
+{
|
||||
+ struct virtio_scsi_event_node *event_node = buf;
|
||||
+
|
||||
+ INIT_WORK(&event_node->work, virtscsi_handle_event);
|
||||
+ schedule_work(&event_node->work);
|
||||
+}
|
||||
+
|
||||
static void virtscsi_event_done(struct virtqueue *vq)
|
||||
{
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
@@ -209,7 +318,7 @@ static void virtscsi_event_done(struct v
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
|
||||
- virtscsi_vq_done(vq, virtscsi_complete_free);
|
||||
+ virtscsi_vq_done(vq, virtscsi_complete_event);
|
||||
spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags);
|
||||
};
|
||||
|
||||
@@ -510,6 +619,9 @@ static int virtscsi_init(struct virtio_d
|
||||
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
|
||||
virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
|
||||
|
||||
+ if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
|
||||
+ virtscsi_kick_event_all(vscsi);
|
||||
+
|
||||
/* We need to know how many segments before we allocate. */
|
||||
sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1;
|
||||
|
||||
@@ -580,6 +692,10 @@ virtscsi_init_failed:
|
||||
static void __devexit virtscsi_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct Scsi_Host *shost = virtio_scsi_host(vdev);
|
||||
+ struct virtio_scsi *vscsi = shost_priv(shost);
|
||||
+
|
||||
+ if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
|
||||
+ virtscsi_cancel_event_work(vscsi);
|
||||
|
||||
scsi_remove_host(shost);
|
||||
|
||||
@@ -608,7 +724,13 @@ static struct virtio_device_id id_table[
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
+static unsigned int features[] = {
|
||||
+ VIRTIO_SCSI_F_HOTPLUG
|
||||
+};
|
||||
+
|
||||
static struct virtio_driver virtio_scsi_driver = {
|
||||
+ .feature_table = features,
|
||||
+ .feature_table_size = ARRAY_SIZE(features),
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
--- a/include/linux/virtio_scsi.h
|
||||
+++ b/include/linux/virtio_scsi.h
|
||||
@@ -69,6 +69,10 @@ struct virtio_scsi_config {
|
||||
u32 max_lun;
|
||||
} __packed;
|
||||
|
||||
+/* Feature Bits */
|
||||
+#define VIRTIO_SCSI_F_INOUT 0
|
||||
+#define VIRTIO_SCSI_F_HOTPLUG 1
|
||||
+
|
||||
/* Response codes */
|
||||
#define VIRTIO_SCSI_S_OK 0
|
||||
#define VIRTIO_SCSI_S_OVERRUN 1
|
||||
@@ -105,6 +109,11 @@ struct virtio_scsi_config {
|
||||
#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
|
||||
#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
|
||||
|
||||
+/* Reasons of transport reset event */
|
||||
+#define VIRTIO_SCSI_EVT_RESET_HARD 0
|
||||
+#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
||||
+#define VIRTIO_SCSI_EVT_RESET_REMOVED 2
|
||||
+
|
||||
#define VIRTIO_SCSI_S_SIMPLE 0
|
||||
#define VIRTIO_SCSI_S_ORDERED 1
|
||||
#define VIRTIO_SCSI_S_HEAD 2
|
|
@ -0,0 +1,92 @@
|
|||
From: Nicholas Bellinger <nab@linux-iscsi.org>
|
||||
Date: Wed, 11 Jul 2012 21:22:16 +0000
|
||||
Subject: [08/12] [SCSI] virtio-scsi: Add vdrv->scan for post
|
||||
VIRTIO_CONFIG_S_DRIVER_OK LUN scanning
|
||||
|
||||
commit 59057fbc37178f10a196ab7ec170b80273f75a47 upstream.
|
||||
|
||||
This patch changes virtio-scsi to use a new virtio_driver->scan() callback
|
||||
so that scsi_scan_host() can be properly invoked once virtio_dev_probe() has
|
||||
set add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK) to signal active virtio-ring
|
||||
operation, instead of from within virtscsi_probe().
|
||||
|
||||
This fixes a bug where SCSI LUN scanning for both virtio-scsi-raw and
|
||||
virtio-scsi/tcm_vhost setups was happening before VIRTIO_CONFIG_S_DRIVER_OK
|
||||
had been set, causing VIRTIO_SCSI_S_BAD_TARGET to occur. This fixes a bug
|
||||
with virtio-scsi/tcm_vhost where LUN scan was not detecting LUNs.
|
||||
|
||||
Tested with virtio-scsi-raw + virtio-scsi/tcm_vhost w/ IBLOCK on 3.5-rc2 code.
|
||||
|
||||
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
|
||||
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
[bwh: Backported to 3.2: adjust context]
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 15 ++++++++++++---
|
||||
drivers/virtio/virtio.c | 5 ++++-
|
||||
include/linux/virtio.h | 1 +
|
||||
3 files changed, 17 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -571,6 +571,13 @@ static struct virtio_scsi_target_state *
|
||||
return tgt;
|
||||
}
|
||||
|
||||
+static void virtscsi_scan(struct virtio_device *vdev)
|
||||
+{
|
||||
+ struct Scsi_Host *shost = (struct Scsi_Host *)vdev->priv;
|
||||
+
|
||||
+ scsi_scan_host(shost);
|
||||
+}
|
||||
+
|
||||
static void virtscsi_remove_vqs(struct virtio_device *vdev)
|
||||
{
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vdev);
|
||||
@@ -677,9 +684,10 @@ static int __devinit virtscsi_probe(stru
|
||||
err = scsi_add_host(shost, &vdev->dev);
|
||||
if (err)
|
||||
goto scsi_add_host_failed;
|
||||
-
|
||||
- scsi_scan_host(shost);
|
||||
-
|
||||
+ /*
|
||||
+ * scsi_scan_host() happens in virtscsi_scan() via virtio_driver->scan()
|
||||
+ * after VIRTIO_CONFIG_S_DRIVER_OK has been set..
|
||||
+ */
|
||||
return 0;
|
||||
|
||||
scsi_add_host_failed:
|
||||
@@ -735,6 +743,7 @@ static struct virtio_driver virtio_scsi_
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
.probe = virtscsi_probe,
|
||||
+ .scan = virtscsi_scan,
|
||||
#if 0
|
||||
.freeze = virtscsi_freeze,
|
||||
.restore = virtscsi_restore,
|
||||
--- a/drivers/virtio/virtio.c
|
||||
+++ b/drivers/virtio/virtio.c
|
||||
@@ -140,8 +140,11 @@ static int virtio_dev_probe(struct devic
|
||||
err = drv->probe(dev);
|
||||
if (err)
|
||||
add_status(dev, VIRTIO_CONFIG_S_FAILED);
|
||||
- else
|
||||
+ else {
|
||||
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
+ if (drv->scan)
|
||||
+ drv->scan(dev);
|
||||
+ }
|
||||
|
||||
return err;
|
||||
}
|
||||
--- a/include/linux/virtio.h
|
||||
+++ b/include/linux/virtio.h
|
||||
@@ -148,6 +148,7 @@ struct virtio_driver {
|
||||
const unsigned int *feature_table;
|
||||
unsigned int feature_table_size;
|
||||
int (*probe)(struct virtio_device *dev);
|
||||
+ void (*scan)(struct virtio_device *dev);
|
||||
void (*remove)(struct virtio_device *dev);
|
||||
void (*config_changed)(struct virtio_device *dev);
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
From: Wang Sen <senwang@linux.vnet.ibm.com>
|
||||
Date: Mon, 30 Jul 2012 14:25:06 +0800
|
||||
Subject: [09/12] [SCSI] scsi: virtio-scsi: Fix address translation failure of
|
||||
HighMem pages used by sg list
|
||||
|
||||
commit 27e99ade81368e6fdda3212bff9345177cf9e57a upstream.
|
||||
|
||||
When using the commands below to write some data to a virtio-scsi LUN of the
|
||||
QEMU guest(32-bit) with 1G physical memory(qemu -m 1024), the qemu will crash.
|
||||
|
||||
# sudo mkfs.ext4 /dev/sdb (/dev/sdb is the virtio-scsi LUN.)
|
||||
# sudo mount /dev/sdb /mnt
|
||||
# dd if=/dev/zero of=/mnt/file bs=1M count=1024
|
||||
|
||||
In current implementation, sg_set_buf is called to add buffers to sg list which
|
||||
is put into the virtqueue eventually. But if there are some HighMem pages in
|
||||
table->sgl you can not get virtual address by sg_virt. So, sg_virt(sg_elem) may
|
||||
return NULL value. This will cause QEMU exit when virtqueue_map_sg is called
|
||||
in QEMU because an invalid GPA is passed by virtqueue.
|
||||
|
||||
Two solutions are discussed here:
|
||||
http://lkml.indiana.edu/hypermail/linux/kernel/1207.3/00675.html
|
||||
|
||||
Finally, value assignment approach was adopted because:
|
||||
|
||||
Value assignment creates a well-formed scatterlist, because the termination
|
||||
marker in source sg_list has been set in blk_rq_map_sg(). The last entry of the
|
||||
source sg_list is just copied to the the last entry in destination list. Note
|
||||
that, for now, virtio_ring does not care about the form of the scatterlist and
|
||||
simply processes the first out_num + in_num consecutive elements of the sg[]
|
||||
array.
|
||||
|
||||
I have tested the patch on my workstation. QEMU would not crash any more.
|
||||
|
||||
Signed-off-by: Wang Sen <senwang@linux.vnet.ibm.com>
|
||||
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
|
||||
index c7030fb..3e79a2f 100644
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -331,7 +331,7 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *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);
|
||||
+ sg[idx++] = *sg_elem;
|
||||
|
||||
*p_idx = idx;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||||
Date: Tue, 2 Oct 2012 17:25:46 +0200
|
||||
Subject: [10/12] [SCSI] virtio-scsi: initialize scatterlist structure
|
||||
|
||||
commit 2e9c9dfde00a6466441e93033cf2c37f720bdacf upstream.
|
||||
|
||||
The sg struct is used without being initialized, which breaks
|
||||
when CONFIG_DEBUG_SG is enabled.
|
||||
|
||||
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
|
||||
index 3e79a2f..7554d78 100644
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -219,7 +219,7 @@ static int virtscsi_kick_event(struct virtio_scsi *vscsi,
|
||||
struct scatterlist sg;
|
||||
unsigned long flags;
|
||||
|
||||
- sg_set_buf(&sg, &event_node->event, sizeof(struct virtio_scsi_event));
|
||||
+ sg_init_one(&sg, &event_node->event, sizeof(struct virtio_scsi_event));
|
||||
|
||||
spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
|
||||
|
35
debian/patches/features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch
vendored
Normal file
35
debian/patches/features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Tue, 2 Oct 2012 17:25:47 +0200
|
||||
Subject: [11/12] [SCSI] virtio-scsi: fix LUNs greater than 255
|
||||
|
||||
commit 9da5f5ac6affad8dd8cd80f5cca26e4335e1728b upstream.
|
||||
|
||||
virtio-scsi needs to report LUNs greater than 256 using the "flat"
|
||||
format. Because the Linux SCSI layer just maps the SCSI LUN to
|
||||
an u32, without any parsing, these end up in the range from 16640
|
||||
to 32767. Fix max_lun to account for the possibility that logical
|
||||
unit numbers are encoded with the "flat" format.
|
||||
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
|
||||
index 7554d78..a7cf726 100644
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -677,7 +677,11 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev)
|
||||
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;
|
||||
+
|
||||
+ /* LUNs > 256 are reported with format 1, so they go in the range
|
||||
+ * 16640-32767.
|
||||
+ */
|
||||
+ shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1 + 0x4000;
|
||||
shost->max_id = num_targets;
|
||||
shost->max_channel = 0;
|
||||
shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
|
|
@ -0,0 +1,96 @@
|
|||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Tue, 2 Oct 2012 17:25:48 +0200
|
||||
Subject: [12/12] [SCSI] virtio-scsi: support online resizing of disks
|
||||
|
||||
commit 865b58c05b841964f48f574c2027311bd04db8a1 upstream.
|
||||
|
||||
Support the LUN parameter change event. Currently, the host fires this event
|
||||
when the capacity of a disk is changed from the virtual machine monitor.
|
||||
The resize then appears in the kernel log like this:
|
||||
|
||||
sd 0:0:0:0: [sda] 46137344 512-byte logical blocks: (23.6 GB/22.0 GIb)
|
||||
sda: detected capacity change from 22548578304 to 23622320128
|
||||
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
|
||||
---
|
||||
drivers/scsi/virtio_scsi.c | 31 ++++++++++++++++++++++++++++++-
|
||||
include/linux/virtio_scsi.h | 2 ++
|
||||
2 files changed, 32 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
|
||||
index a7cf726..595af1a 100644
|
||||
--- a/drivers/scsi/virtio_scsi.c
|
||||
+++ b/drivers/scsi/virtio_scsi.c
|
||||
@@ -279,6 +279,31 @@ static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi,
|
||||
}
|
||||
}
|
||||
|
||||
+static void virtscsi_handle_param_change(struct virtio_scsi *vscsi,
|
||||
+ struct virtio_scsi_event *event)
|
||||
+{
|
||||
+ struct scsi_device *sdev;
|
||||
+ struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
|
||||
+ unsigned int target = event->lun[1];
|
||||
+ unsigned int lun = (event->lun[2] << 8) | event->lun[3];
|
||||
+ u8 asc = event->reason & 255;
|
||||
+ u8 ascq = event->reason >> 8;
|
||||
+
|
||||
+ sdev = scsi_device_lookup(shost, 0, target, lun);
|
||||
+ if (!sdev) {
|
||||
+ pr_err("SCSI device %d 0 %d %d not found\n",
|
||||
+ shost->host_no, target, lun);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Handle "Parameters changed", "Mode parameters changed", and
|
||||
+ "Capacity data has changed". */
|
||||
+ if (asc == 0x2a && (ascq == 0x00 || ascq == 0x01 || ascq == 0x09))
|
||||
+ scsi_rescan_device(&sdev->sdev_gendev);
|
||||
+
|
||||
+ scsi_device_put(sdev);
|
||||
+}
|
||||
+
|
||||
static void virtscsi_handle_event(struct work_struct *work)
|
||||
{
|
||||
struct virtio_scsi_event_node *event_node =
|
||||
@@ -297,6 +322,9 @@ static void virtscsi_handle_event(struct work_struct *work)
|
||||
case VIRTIO_SCSI_T_TRANSPORT_RESET:
|
||||
virtscsi_handle_transport_reset(vscsi, event);
|
||||
break;
|
||||
+ case VIRTIO_SCSI_T_PARAM_CHANGE:
|
||||
+ virtscsi_handle_param_change(vscsi, event);
|
||||
+ break;
|
||||
default:
|
||||
pr_err("Unsupport virtio scsi event %x\n", event->event);
|
||||
}
|
||||
@@ -737,7 +765,8 @@ static struct virtio_device_id id_table[] = {
|
||||
};
|
||||
|
||||
static unsigned int features[] = {
|
||||
- VIRTIO_SCSI_F_HOTPLUG
|
||||
+ VIRTIO_SCSI_F_HOTPLUG,
|
||||
+ VIRTIO_SCSI_F_CHANGE,
|
||||
};
|
||||
|
||||
static struct virtio_driver virtio_scsi_driver = {
|
||||
diff --git a/include/linux/virtio_scsi.h b/include/linux/virtio_scsi.h
|
||||
index dc8d305..d6b4440 100644
|
||||
--- a/include/linux/virtio_scsi.h
|
||||
+++ b/include/linux/virtio_scsi.h
|
||||
@@ -72,6 +72,7 @@ struct virtio_scsi_config {
|
||||
/* Feature Bits */
|
||||
#define VIRTIO_SCSI_F_INOUT 0
|
||||
#define VIRTIO_SCSI_F_HOTPLUG 1
|
||||
+#define VIRTIO_SCSI_F_CHANGE 2
|
||||
|
||||
/* Response codes */
|
||||
#define VIRTIO_SCSI_S_OK 0
|
||||
@@ -108,6 +109,7 @@ struct virtio_scsi_config {
|
||||
#define VIRTIO_SCSI_T_NO_EVENT 0
|
||||
#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
|
||||
#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
|
||||
+#define VIRTIO_SCSI_T_PARAM_CHANGE 3
|
||||
|
||||
/* Reasons of transport reset event */
|
||||
#define VIRTIO_SCSI_EVT_RESET_HARD 0
|
122
debian/patches/features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch
vendored
Normal file
122
debian/patches/features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
From: Rusty Russell <rusty@rustcorp.com.au>
|
||||
Date: Thu, 12 Jan 2012 15:44:43 +1030
|
||||
Subject: virtio: support unlocked queue kick
|
||||
|
||||
commit 41f0377f73039ca6fe97a469d1941a89cd9757f1 upstream.
|
||||
|
||||
Based on patch by Christoph for virtio_blk speedup:
|
||||
|
||||
Split virtqueue_kick to be able to do the actual notification
|
||||
outside the lock protecting the virtqueue. This patch was
|
||||
originally done by Stefan Hajnoczi, but I can't find the
|
||||
original one anymore and had to recreated it from memory.
|
||||
Pointers to the original or corrections for the commit message
|
||||
are welcome.
|
||||
|
||||
Stefan's patch was here:
|
||||
|
||||
https://github.com/stefanha/linux/commit/a6d06644e3a58e57a774e77d7dc34c4a5a2e7496
|
||||
http://www.spinics.net/lists/linux-virtualization/msg14616.html
|
||||
|
||||
Third time's the charm!
|
||||
|
||||
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
|
||||
[bwh: Backported to 3.2: adjust context]
|
||||
---
|
||||
drivers/virtio/virtio_ring.c | 60 +++++++++++++++++++++++++++++++++---------
|
||||
include/linux/virtio.h | 4 +++
|
||||
2 files changed, 52 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/drivers/virtio/virtio_ring.c
|
||||
+++ b/drivers/virtio/virtio_ring.c
|
||||
@@ -245,10 +245,23 @@ add_head:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtqueue_add_buf_gfp);
|
||||
|
||||
-void virtqueue_kick(struct virtqueue *_vq)
|
||||
+/**
|
||||
+ * virtqueue_kick_prepare - first half of split virtqueue_kick call.
|
||||
+ * @vq: the struct virtqueue
|
||||
+ *
|
||||
+ * Instead of virtqueue_kick(), you can do:
|
||||
+ * if (virtqueue_kick_prepare(vq))
|
||||
+ * virtqueue_notify(vq);
|
||||
+ *
|
||||
+ * This is sometimes useful because the virtqueue_kick_prepare() needs
|
||||
+ * to be serialized, but the actual virtqueue_notify() call does not.
|
||||
+ */
|
||||
+bool virtqueue_kick_prepare(struct virtqueue *_vq)
|
||||
{
|
||||
struct vring_virtqueue *vq = to_vvq(_vq);
|
||||
u16 new, old;
|
||||
+ bool needs_kick;
|
||||
+
|
||||
START_USE(vq);
|
||||
/* Descriptors and available array need to be set before we expose the
|
||||
* new available array entries. */
|
||||
@@ -261,13 +274,46 @@ void virtqueue_kick(struct virtqueue *_v
|
||||
/* Need to update avail index before checking if we should notify */
|
||||
virtio_mb();
|
||||
|
||||
- if (vq->event ?
|
||||
- vring_need_event(vring_avail_event(&vq->vring), new, old) :
|
||||
- !(vq->vring.used->flags & VRING_USED_F_NO_NOTIFY))
|
||||
- /* Prod other side to tell it about changes. */
|
||||
- vq->notify(&vq->vq);
|
||||
-
|
||||
+ if (vq->event) {
|
||||
+ needs_kick = vring_need_event(vring_avail_event(&vq->vring),
|
||||
+ new, old);
|
||||
+ } else {
|
||||
+ needs_kick = !(vq->vring.used->flags & VRING_USED_F_NO_NOTIFY);
|
||||
+ }
|
||||
END_USE(vq);
|
||||
+ return needs_kick;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(virtqueue_kick_prepare);
|
||||
+
|
||||
+/**
|
||||
+ * virtqueue_notify - second half of split virtqueue_kick call.
|
||||
+ * @vq: the struct virtqueue
|
||||
+ *
|
||||
+ * This does not need to be serialized.
|
||||
+ */
|
||||
+void virtqueue_notify(struct virtqueue *_vq)
|
||||
+{
|
||||
+ struct vring_virtqueue *vq = to_vvq(_vq);
|
||||
+
|
||||
+ /* Prod other side to tell it about changes. */
|
||||
+ vq->notify(_vq);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(virtqueue_notify);
|
||||
+
|
||||
+/**
|
||||
+ * virtqueue_kick - update after add_buf
|
||||
+ * @vq: the struct virtqueue
|
||||
+ *
|
||||
+ * After one or more virtqueue_add_buf calls, invoke this to kick
|
||||
+ * the other side.
|
||||
+ *
|
||||
+ * Caller must ensure we don't call this with other virtqueue
|
||||
+ * operations at the same time (except where noted).
|
||||
+ */
|
||||
+void virtqueue_kick(struct virtqueue *vq)
|
||||
+{
|
||||
+ if (virtqueue_kick_prepare(vq))
|
||||
+ virtqueue_notify(vq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtqueue_kick);
|
||||
|
||||
--- a/include/linux/virtio.h
|
||||
+++ b/include/linux/virtio.h
|
||||
@@ -90,6 +90,10 @@ static inline int virtqueue_add_buf(stru
|
||||
|
||||
void virtqueue_kick(struct virtqueue *vq);
|
||||
|
||||
+bool virtqueue_kick_prepare(struct virtqueue *vq);
|
||||
+
|
||||
+void virtqueue_notify(struct virtqueue *vq);
|
||||
+
|
||||
void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
|
||||
|
||||
void virtqueue_disable_cb(struct virtqueue *vq);
|
|
@ -502,3 +502,18 @@ bugfix/x86/drm-i915-Disable-AsyncFlip-performance-optimisations.patch
|
|||
bugfix/x86/drm-i915-GFX_MODE-Flush-TLB-Invalidate-Mode-must-be-.patch
|
||||
bugfix/x86/drm-i915-dump-UTS_RELEASE-into-the-error_state.patch
|
||||
features/all/drm/efi-Make-efi_enabled-a-function-to-query-EFI-facilit.patch
|
||||
|
||||
# virtio-scsi from 3.7
|
||||
features/all/virtio_scsi/0001-SCSI-virtio-scsi-SCSI-driver-for-QEMU-based-virtual-.patch
|
||||
features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch
|
||||
features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch
|
||||
features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch
|
||||
features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch
|
||||
features/all/virtio_scsi/0005-SCSI-virtio-scsi-release-sg_lock-after-add_buf.patch
|
||||
features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch
|
||||
features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch
|
||||
features/all/virtio_scsi/0008-SCSI-virtio-scsi-Add-vdrv-scan-for-post-VIRTIO_CONFI.patch
|
||||
features/all/virtio_scsi/0009-SCSI-scsi-virtio-scsi-Fix-address-translation-failur.patch
|
||||
features/all/virtio_scsi/0010-SCSI-virtio-scsi-initialize-scatterlist-structure.patch
|
||||
features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch
|
||||
features/all/virtio_scsi/0012-SCSI-virtio-scsi-support-online-resizing-of-disks.patch
|
||||
|
|
Loading…
Reference in New Issue