From a9e39ce1eeef75301ab9ca4e9c0261633d34fb06 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 7 Feb 2013 04:42:39 +0000 Subject: [PATCH] SCSI: Add virtio_scsi driver (Closes: #686636) svn path=/dists/sid/linux/; revision=19794 --- debian/changelog | 1 + debian/config/config | 1 + debian/config/defines | 2 + ...-SCSI-driver-for-QEMU-based-virtual-.patch | 782 ++++++++++++++++++ ...I-virtio_scsi-fix-TMF-use-after-free.patch | 68 ++ ...-SCSI-virtio-scsi-unlock-during-kick.patch | 30 + ...CSI-virtio-scsi-split-locking-per-vq.patch | 195 +++++ ...o-scsi-release-sg_lock-after-add_buf.patch | 49 ++ ...io-scsi-split-scatterlist-per-target.patch | 290 +++++++ ...scsi-hotplug-support-for-virtio-scsi.patch | 229 +++++ ...-Add-vdrv-scan-for-post-VIRTIO_CONFI.patch | 92 +++ ...-scsi-Fix-address-translation-failur.patch | 54 ++ ...csi-initialize-scatterlist-structure.patch | 29 + ...irtio-scsi-fix-LUNs-greater-than-255.patch | 35 + ...csi-support-online-resizing-of-disks.patch | 96 +++ .../virtio-support-unlocked-queue-kick.patch | 122 +++ debian/patches/series | 15 + 17 files changed, 2090 insertions(+) create mode 100644 debian/patches/features/all/virtio_scsi/0001-SCSI-virtio-scsi-SCSI-driver-for-QEMU-based-virtual-.patch create mode 100644 debian/patches/features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch create mode 100644 debian/patches/features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch create mode 100644 debian/patches/features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch create mode 100644 debian/patches/features/all/virtio_scsi/0005-SCSI-virtio-scsi-release-sg_lock-after-add_buf.patch create mode 100644 debian/patches/features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch create mode 100644 debian/patches/features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch create mode 100644 debian/patches/features/all/virtio_scsi/0008-SCSI-virtio-scsi-Add-vdrv-scan-for-post-VIRTIO_CONFI.patch create mode 100644 debian/patches/features/all/virtio_scsi/0009-SCSI-scsi-virtio-scsi-Fix-address-translation-failur.patch create mode 100644 debian/patches/features/all/virtio_scsi/0010-SCSI-virtio-scsi-initialize-scatterlist-structure.patch create mode 100644 debian/patches/features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch create mode 100644 debian/patches/features/all/virtio_scsi/0012-SCSI-virtio-scsi-support-online-resizing-of-disks.patch create mode 100644 debian/patches/features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch diff --git a/debian/changelog b/debian/changelog index 43a0a31fd..030fd0086 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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. diff --git a/debian/config/config b/debian/config/config index 10ab6eb5b..48c400e4d 100644 --- a/debian/config/config +++ b/debian/config/config @@ -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 diff --git a/debian/config/defines b/debian/config/defines index 4960c72ba..59669b15a 100644 --- a/debian/config/defines +++ b/debian/config/defines @@ -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: diff --git a/debian/patches/features/all/virtio_scsi/0001-SCSI-virtio-scsi-SCSI-driver-for-QEMU-based-virtual-.patch b/debian/patches/features/all/virtio_scsi/0001-SCSI-virtio-scsi-SCSI-driver-for-QEMU-based-virtual-.patch new file mode 100644 index 000000000..5d272adbd --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0001-SCSI-virtio-scsi-SCSI-driver-for-QEMU-based-virtual-.patch @@ -0,0 +1,782 @@ +From: Paolo Bonzini +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 +Signed-off-by: Paolo Bonzini +Signed-off-by: James Bottomley +[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 ++ * Paolo Bonzini ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 */ diff --git a/debian/patches/features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch b/debian/patches/features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch new file mode 100644 index 000000000..bb7d02446 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0002-SCSI-virtio_scsi-fix-TMF-use-after-free.patch @@ -0,0 +1,68 @@ +From: Paolo Bonzini +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 +Signed-off-by: Paolo Bonzini +Signed-off-by: James Bottomley +--- + 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) diff --git a/debian/patches/features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch b/debian/patches/features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch new file mode 100644 index 000000000..14e3ed7a8 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0003-SCSI-virtio-scsi-unlock-during-kick.patch @@ -0,0 +1,30 @@ +From: Paolo Bonzini +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 +Signed-off-by: James Bottomley +--- + 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; + } + diff --git a/debian/patches/features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch b/debian/patches/features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch new file mode 100644 index 000000000..33549dc2b --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0004-SCSI-virtio-scsi-split-locking-per-vq.patch @@ -0,0 +1,195 @@ +From: Paolo Bonzini +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 +Signed-off-by: James Bottomley +[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); diff --git a/debian/patches/features/all/virtio_scsi/0005-SCSI-virtio-scsi-release-sg_lock-after-add_buf.patch b/debian/patches/features/all/virtio_scsi/0005-SCSI-virtio-scsi-release-sg_lock-after-add_buf.patch new file mode 100644 index 000000000..e38d5d11f --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0005-SCSI-virtio-scsi-release-sg_lock-after-add_buf.patch @@ -0,0 +1,49 @@ +From: Paolo Bonzini +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 +Signed-off-by: James Bottomley +[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); diff --git a/debian/patches/features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch b/debian/patches/features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch new file mode 100644 index 000000000..958c4a30b --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0006-SCSI-virtio-scsi-split-scatterlist-per-target.patch @@ -0,0 +1,290 @@ +From: Paolo Bonzini +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 +Signed-off-by: James Bottomley +[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 + diff --git a/debian/patches/features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch b/debian/patches/features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch new file mode 100644 index 000000000..db27d4f60 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0007-SCSI-virtio-scsi-hotplug-support-for-virtio-scsi.patch @@ -0,0 +1,229 @@ +From: Cong Meng +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 +Signed-off-by: Cong Meng +Acked-by: Paolo Bonzini +Signed-off-by: James Bottomley +[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 + + #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 diff --git a/debian/patches/features/all/virtio_scsi/0008-SCSI-virtio-scsi-Add-vdrv-scan-for-post-VIRTIO_CONFI.patch b/debian/patches/features/all/virtio_scsi/0008-SCSI-virtio-scsi-Add-vdrv-scan-for-post-VIRTIO_CONFI.patch new file mode 100644 index 000000000..38d58af87 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0008-SCSI-virtio-scsi-Add-vdrv-scan-for-post-VIRTIO_CONFI.patch @@ -0,0 +1,92 @@ +From: Nicholas Bellinger +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 +Acked-by: Paolo Bonzini +Signed-off-by: James Bottomley +[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); + }; diff --git a/debian/patches/features/all/virtio_scsi/0009-SCSI-scsi-virtio-scsi-Fix-address-translation-failur.patch b/debian/patches/features/all/virtio_scsi/0009-SCSI-scsi-virtio-scsi-Fix-address-translation-failur.patch new file mode 100644 index 000000000..df2943b53 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0009-SCSI-scsi-virtio-scsi-Fix-address-translation-failur.patch @@ -0,0 +1,54 @@ +From: Wang Sen +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 +Acked-by: Paolo Bonzini +Signed-off-by: James Bottomley +--- + 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; + } diff --git a/debian/patches/features/all/virtio_scsi/0010-SCSI-virtio-scsi-initialize-scatterlist-structure.patch b/debian/patches/features/all/virtio_scsi/0010-SCSI-virtio-scsi-initialize-scatterlist-structure.patch new file mode 100644 index 000000000..66e732efb --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0010-SCSI-virtio-scsi-initialize-scatterlist-structure.patch @@ -0,0 +1,29 @@ +From: "Richard W.M. Jones" +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 +Signed-off-by: Paolo Bonzini +Signed-off-by: James Bottomley +--- + 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); + diff --git a/debian/patches/features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch b/debian/patches/features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch new file mode 100644 index 000000000..ba8a0cdad --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0011-SCSI-virtio-scsi-fix-LUNs-greater-than-255.patch @@ -0,0 +1,35 @@ +From: Paolo Bonzini +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 +Signed-off-by: James Bottomley +--- + 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; diff --git a/debian/patches/features/all/virtio_scsi/0012-SCSI-virtio-scsi-support-online-resizing-of-disks.patch b/debian/patches/features/all/virtio_scsi/0012-SCSI-virtio-scsi-support-online-resizing-of-disks.patch new file mode 100644 index 000000000..1d93ffa79 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/0012-SCSI-virtio-scsi-support-online-resizing-of-disks.patch @@ -0,0 +1,96 @@ +From: Paolo Bonzini +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 +Signed-off-by: James Bottomley +--- + 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 diff --git a/debian/patches/features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch b/debian/patches/features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch new file mode 100644 index 000000000..01d58c712 --- /dev/null +++ b/debian/patches/features/all/virtio_scsi/virtio-support-unlocked-queue-kick.patch @@ -0,0 +1,122 @@ +From: Rusty Russell +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 +[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); diff --git a/debian/patches/series b/debian/patches/series index e62a8f4e0..2764f9b0e 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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