]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/scsi/virtio_scsi.c
Merge tag 'for-5.4/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device...
[linux.git] / drivers / scsi / virtio_scsi.c
index 297e1076e571ddaf84898dddef3e45e950b55d79..bfec84aacd90b6c812659418f27f2ebcd009099b 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/seqlock.h>
 #include <linux/blk-mq-virtio.h>
 
+#include "sd.h"
+
 #define VIRTIO_SCSI_MEMPOOL_SZ 64
 #define VIRTIO_SCSI_EVENT_LEN 8
 #define VIRTIO_SCSI_VQ_BASE 2
@@ -324,6 +326,36 @@ static void virtscsi_handle_param_change(struct virtio_scsi *vscsi,
        scsi_device_put(sdev);
 }
 
+static void virtscsi_rescan_hotunplug(struct virtio_scsi *vscsi)
+{
+       struct scsi_device *sdev;
+       struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
+       unsigned char scsi_cmd[MAX_COMMAND_SIZE];
+       int result, inquiry_len, inq_result_len = 256;
+       char *inq_result = kmalloc(inq_result_len, GFP_KERNEL);
+
+       shost_for_each_device(sdev, shost) {
+               inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
+
+               memset(scsi_cmd, 0, sizeof(scsi_cmd));
+               scsi_cmd[0] = INQUIRY;
+               scsi_cmd[4] = (unsigned char) inquiry_len;
+
+               memset(inq_result, 0, inq_result_len);
+
+               result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
+                                         inq_result, inquiry_len, NULL,
+                                         SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+
+               if (result == 0 && inq_result[0] >> 5) {
+                       /* PQ indicates the LUN is not attached */
+                       scsi_remove_device(sdev);
+               }
+       }
+
+       kfree(inq_result);
+}
+
 static void virtscsi_handle_event(struct work_struct *work)
 {
        struct virtio_scsi_event_node *event_node =
@@ -335,6 +367,7 @@ static void virtscsi_handle_event(struct work_struct *work)
            cpu_to_virtio32(vscsi->vdev, VIRTIO_SCSI_T_EVENTS_MISSED)) {
                event->event &= ~cpu_to_virtio32(vscsi->vdev,
                                                   VIRTIO_SCSI_T_EVENTS_MISSED);
+               virtscsi_rescan_hotunplug(vscsi);
                scsi_scan_host(virtio_scsi_host(vscsi->vdev));
        }
 
@@ -369,14 +402,7 @@ static void virtscsi_event_done(struct virtqueue *vq)
        virtscsi_vq_done(vscsi, &vscsi->event_vq, virtscsi_complete_event);
 };
 
-/**
- * virtscsi_add_cmd - add a virtio_scsi_cmd to a virtqueue
- * @vq         : the struct virtqueue we're talking about
- * @cmd                : command structure
- * @req_size   : size of the request buffer
- * @resp_size  : size of the response buffer
- */
-static int virtscsi_add_cmd(struct virtqueue *vq,
+static int __virtscsi_add_cmd(struct virtqueue *vq,
                            struct virtio_scsi_cmd *cmd,
                            size_t req_size, size_t resp_size)
 {
@@ -421,17 +447,39 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
        return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, GFP_ATOMIC);
 }
 
-static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq,
+static void virtscsi_kick_vq(struct virtio_scsi_vq *vq)
+{
+       bool needs_kick;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vq->vq_lock, flags);
+       needs_kick = virtqueue_kick_prepare(vq->vq);
+       spin_unlock_irqrestore(&vq->vq_lock, flags);
+
+       if (needs_kick)
+               virtqueue_notify(vq->vq);
+}
+
+/**
+ * virtscsi_add_cmd - add a virtio_scsi_cmd to a virtqueue, optionally kick it
+ * @vq         : the struct virtqueue we're talking about
+ * @cmd                : command structure
+ * @req_size   : size of the request buffer
+ * @resp_size  : size of the response buffer
+ * @kick       : whether to kick the virtqueue immediately
+ */
+static int virtscsi_add_cmd(struct virtio_scsi_vq *vq,
                             struct virtio_scsi_cmd *cmd,
-                            size_t req_size, size_t resp_size)
+                            size_t req_size, size_t resp_size,
+                            bool kick)
 {
        unsigned long flags;
        int err;
        bool needs_kick = false;
 
        spin_lock_irqsave(&vq->vq_lock, flags);
-       err = virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size);
-       if (!err)
+       err = __virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size);
+       if (!err && kick)
                needs_kick = virtqueue_kick_prepare(vq->vq);
 
        spin_unlock_irqrestore(&vq->vq_lock, flags);
@@ -496,6 +544,7 @@ static int virtscsi_queuecommand(struct Scsi_Host *shost,
        struct virtio_scsi *vscsi = shost_priv(shost);
        struct virtio_scsi_vq *req_vq = virtscsi_pick_vq_mq(vscsi, sc);
        struct virtio_scsi_cmd *cmd = scsi_cmd_priv(sc);
+       bool kick;
        unsigned long flags;
        int req_size;
        int ret;
@@ -525,7 +574,8 @@ static int virtscsi_queuecommand(struct Scsi_Host *shost,
                req_size = sizeof(cmd->req.cmd);
        }
 
-       ret = virtscsi_kick_cmd(req_vq, cmd, req_size, sizeof(cmd->resp.cmd));
+       kick = (sc->flags & SCMD_LAST) != 0;
+       ret = virtscsi_add_cmd(req_vq, cmd, req_size, sizeof(cmd->resp.cmd), kick);
        if (ret == -EIO) {
                cmd->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
                spin_lock_irqsave(&req_vq->vq_lock, flags);
@@ -543,8 +593,8 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
        int ret = FAILED;
 
        cmd->comp = &comp;
-       if (virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd,
-                             sizeof cmd->req.tmf, sizeof cmd->resp.tmf) < 0)
+       if (virtscsi_add_cmd(&vscsi->ctrl_vq, cmd,
+                             sizeof cmd->req.tmf, sizeof cmd->resp.tmf, true) < 0)
                goto out;
 
        wait_for_completion(&comp);
@@ -658,6 +708,13 @@ static int virtscsi_map_queues(struct Scsi_Host *shost)
        return blk_mq_virtio_map_queues(qmap, vscsi->vdev, 2);
 }
 
+static void virtscsi_commit_rqs(struct Scsi_Host *shost, u16 hwq)
+{
+       struct virtio_scsi *vscsi = shost_priv(shost);
+
+       virtscsi_kick_vq(&vscsi->req_vqs[hwq]);
+}
+
 /*
  * The host guarantees to respond to each command, although I/O
  * latencies might be higher than on bare metal.  Reset the timer
@@ -675,6 +732,7 @@ static struct scsi_host_template virtscsi_host_template = {
        .this_id = -1,
        .cmd_size = sizeof(struct virtio_scsi_cmd),
        .queuecommand = virtscsi_queuecommand,
+       .commit_rqs = virtscsi_commit_rqs,
        .change_queue_depth = virtscsi_change_queue_depth,
        .eh_abort_handler = virtscsi_abort,
        .eh_device_reset_handler = virtscsi_device_reset,