Implement the 'rescan' virtio-scsi feature. Rescanning works by
sending a 'rescan' virtio-scsi command with the next requested
target id to the backend. The backend will respond with the next
used target id or '-1' if no more targets are found.
This avoids scanning all possible targets.
Signed-off-by: Hannes Reinecke
---
drivers/scsi/virtio_scsi.c | 239 ++-
include/uapi/linux/virtio_scsi.h | 15 +++
2 files changed, 250 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 7c28e8d..a561e90 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -46,12 +46,14 @@ struct virtio_scsi_cmd {
struct virtio_scsi_cmd_req_picmd_pi;
struct virtio_scsi_ctrl_tmf_req tmf;
struct virtio_scsi_ctrl_an_req an;
+ struct virtio_scsi_rescan_reqrescan;
} 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;
+ struct virtio_scsi_rescan_resp rescan;
} resp;
} cacheline_aligned_in_smp;
@@ -115,6 +117,10 @@ struct virtio_scsi {
/* Protected by event_vq lock */
bool stop_events;
+ int next_target_id;
+ struct work_struct rescan_work;
+ spinlock_t rescan_lock;
+
struct virtio_scsi_vq ctrl_vq;
struct virtio_scsi_vq event_vq;
struct virtio_scsi_vq req_vqs[];
@@ -318,6 +324,11 @@ static void virtscsi_cancel_event_work(struct virtio_scsi
*vscsi)
for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++)
cancel_work_sync(>event_list[i].work);
+
+ spin_lock_irq(>rescan_lock);
+ vscsi->next_target_id = -1;
+ spin_unlock_irq(>rescan_lock);
+ cancel_work_sync(>rescan_work);
}
static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi,
@@ -805,6 +816,168 @@ static enum blk_eh_timer_return
virtscsi_eh_timed_out(struct scsi_cmnd *scmnd)
return BLK_EH_RESET_TIMER;
}
+static void virtscsi_rescan_work(struct work_struct *work)
+{
+ struct virtio_scsi *vscsi =
+ container_of(work, struct virtio_scsi, rescan_work);
+ struct Scsi_Host *sh = virtio_scsi_host(vscsi->vdev);
+ int target_id, ret;
+ struct virtio_scsi_cmd *cmd;
+ DECLARE_COMPLETION_ONSTACK(comp);
+
+ spin_lock_irq(>rescan_lock);
+ target_id = vscsi->next_target_id;
+ if (target_id == -1) {
+ shost_printk(KERN_INFO, sh, "rescan: terminated\n");
+ spin_unlock_irq(>rescan_lock);
+ return;
+ }
+ spin_unlock_irq(>rescan_lock);
+
+ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_NOIO);
+ if (!cmd) {
+ shost_printk(KERN_INFO, sh, "rescan: no memory\n");
+ goto scan_host;
+ }
+ shost_printk(KERN_INFO, sh, "rescan: next target %d\n", target_id);
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->comp =
+ cmd->sc = NULL;
+ cmd->req.rescan = (struct virtio_scsi_rescan_req){
+ .type = VIRTIO_SCSI_T_RESCAN,
+ .next_id = cpu_to_virtio32(vscsi->vdev, target_id),
+ };
+
+ ret = virtscsi_kick_cmd(>ctrl_vq, cmd, sizeof(cmd->req.rescan),
+ sizeof(cmd->resp.rescan));
+ if (ret < 0) {
+ mempool_free(cmd, virtscsi_cmd_pool);
+ goto scan_host;
+ }
+
+ wait_for_completion();
+ target_id = virtio32_to_cpu(vscsi->vdev, cmd->resp.rescan.id);
+ if (target_id != -1) {
+ int transport = virtio32_to_cpu(vscsi->vdev,
+ cmd->resp.rescan.transport);
+ spin_lock_irq(>rescan_lock);
+ vscsi->next_target_id = target_id + 1;
+ spin_unlock_irq(>rescan_lock);
+ shost_printk(KERN_INFO, sh,
+"found %s target %d (WWN %*phN)\n",
+transport == SCSI_PROTOCOL_FCP ? "FC" : "SAS",
+target_id, 8,
+cmd->resp.rescan.port_wwn);
+ scsi_scan_target(>shost_gendev, 0, target_id,
+SCAN_WILD_CARD, SCSI_SCAN_INITIAL);
+ queue_work(system_freezable_wq, >rescan_work);
+ } else {
+ shost_printk(KERN_INFO, sh,
+"rescan: no more targets\n");
+ spin_lock_irq(>rescan_lock);
+ vscsi->next_target_id = -1;
+ spin_unlock_irq(>rescan_lock);
+ }
+ mempool_free(cmd, virtscsi_cmd_pool);
+ return;
+scan_host:
+ spin_lock_irq(>rescan_lock);
+ vscsi->next_target_id = -1;
+ spin_unlock_irq(>rescan_lock);
+