diff options
| author | Hannes Reinecke <hare@suse.de> | 2017-11-16 09:16:22 +0100 |
|---|---|---|
| committer | Hannes Reinecke <hare@suse.com> | 2021-03-14 12:14:39 +0100 |
| commit | d5a2b439cfd4818e84e84c43c7e499d258af4500 (patch) | |
| tree | 8c38ebde196f0b87671528bf0b7403105d6adf16 | |
| parent | dacea834b95ee600d0e00c18a9782a3f26caad00 (diff) | |
| download | scsi-devel-d5a2b439cfd4818e84e84c43c7e499d258af4500.tar.gz | |
virtio-scsi: implement target rescan
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 <hare@suse.com>
| -rw-r--r-- | drivers/scsi/virtio_scsi.c | 175 | ||||
| -rw-r--r-- | include/uapi/linux/virtio_scsi.h | 15 |
2 files changed, 190 insertions, 0 deletions
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index b9c86a7e3b97d..da4529f057476 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -45,12 +45,14 @@ struct virtio_scsi_cmd { struct virtio_scsi_cmd_req_pi cmd_pi; struct virtio_scsi_ctrl_tmf_req tmf; struct virtio_scsi_ctrl_an_req an; + struct virtio_scsi_rescan_req rescan; } 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; @@ -81,6 +83,11 @@ struct virtio_scsi { /* Protected by event_vq lock */ bool stop_events; + int next_target_id; + struct work_struct rescan_work; + struct virtio_scsi_cmd rescan_cmd; + spinlock_t rescan_lock; + struct virtio_scsi_vq ctrl_vq; struct virtio_scsi_vq event_vq; struct virtio_scsi_vq req_vqs[]; @@ -272,6 +279,11 @@ static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi) for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) cancel_work_sync(&vscsi->event_list[i].work); + + spin_lock_irq(&vscsi->rescan_lock); + vscsi->next_target_id = -1; + spin_unlock_irq(&vscsi->rescan_lock); + cancel_work_sync(&vscsi->rescan_work); } static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi, @@ -738,6 +750,159 @@ 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, transport; + struct virtio_scsi_cmd *cmd = &vscsi->rescan_cmd; + DECLARE_COMPLETION_ONSTACK(comp); + + spin_lock_irq(&vscsi->rescan_lock); + target_id = vscsi->next_target_id; + if (target_id == -1) { + dev_dbg(&sh->shost_gendev, "rescan: terminated\n"); + spin_unlock_irq(&vscsi->rescan_lock); + return; + } + spin_unlock_irq(&vscsi->rescan_lock); + + dev_dbg(&sh->shost_gendev, "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(&vscsi->ctrl_vq, cmd, sizeof(cmd->req.rescan), + sizeof(cmd->resp.rescan)); + if (ret < 0) { + spin_lock_irq(&vscsi->rescan_lock); + vscsi->next_target_id = -1; + spin_unlock_irq(&vscsi->rescan_lock); + dev_dbg(&sh->shost_gendev, "rescan: failed to sent command\n"); + sh->sequential_scan = true; + scsi_scan_host(sh); + return; + } + + wait_for_completion(&comp); + target_id = virtio32_to_cpu(vscsi->vdev, cmd->resp.rescan.id); + if (target_id == -1) { + dev_dbg(&sh->shost_gendev, "rescan: no more targets\n"); + spin_lock_irq(&vscsi->rescan_lock); + vscsi->next_target_id = -1; + spin_unlock_irq(&vscsi->rescan_lock); + return; + } + transport = virtio32_to_cpu(vscsi->vdev, cmd->resp.rescan.transport); + spin_lock_irq(&vscsi->rescan_lock); + vscsi->next_target_id = target_id + 1; + spin_unlock_irq(&vscsi->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(&sh->shost_gendev, 0, target_id, + SCAN_WILD_CARD, SCSI_SCAN_INITIAL); + queue_work(system_freezable_wq, &vscsi->rescan_work); + return; +} + +static int virtscsi_scan_host(struct virtio_scsi *vscsi) +{ + struct Scsi_Host *sh = virtio_scsi_host(vscsi->vdev); + int ret, transport; + struct virtio_scsi_cmd *cmd = &vscsi->rescan_cmd; + DECLARE_COMPLETION_ONSTACK(comp); + + dev_dbg(&sh->shost_gendev, "rescan: scan host\n"); + 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, -1), + }; + + ret = virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd, sizeof(cmd->req.rescan), + sizeof(cmd->resp.rescan)); + if (ret < 0) + return ret; + + wait_for_completion(&comp); + if (cmd->resp.rescan.id != -1) { + dev_dbg(&sh->shost_gendev, "rescan: invalid target id\n"); + return -ENOSYS; + } + transport = virtio32_to_cpu(vscsi->vdev, cmd->resp.rescan.transport); + shost_printk(KERN_INFO, sh, "%s host wwnn %*phN wwpn %*phN\n", + transport == SCSI_PROTOCOL_FCP ? "FC" : "SAS", + 8, cmd->resp.rescan.node_wwn, + 8, cmd->resp.rescan.port_wwn); + return 0; +} + +static void virtscsi_scan_start(struct Scsi_Host *sh) +{ + struct virtio_scsi *vscsi = shost_priv(sh); + + if (!sh->sequential_scan && virtscsi_scan_host(vscsi) < 0) { + shost_printk(KERN_INFO, sh, + "rescan: use sequential scan\n"); + sh->sequential_scan = true; + } + if (sh->sequential_scan) { + scsi_scan_host(sh); + return; + } + spin_lock_irq(&vscsi->rescan_lock); + if (vscsi->next_target_id != -1) { + dev_dbg(&sh->shost_gendev, "rescan: already running\n"); + spin_unlock_irq(&vscsi->rescan_lock); + return; + } + vscsi->next_target_id = 0; + dev_dbg(&sh->shost_gendev, "rescan: start\n"); + spin_unlock_irq(&vscsi->rescan_lock); + queue_work(system_freezable_wq, &vscsi->rescan_work); +} + +int virtscsi_scan_finished(struct Scsi_Host *sh, unsigned long time) +{ + struct virtio_scsi *vscsi = shost_priv(sh); + int ret = 1; + + spin_lock_irq(&vscsi->rescan_lock); + if (vscsi->next_target_id != -1) + ret = 0; + spin_unlock_irq(&vscsi->rescan_lock); + if (!ret) + flush_work(&vscsi->rescan_work); + + dev_dbg(&sh->shost_gendev, "rescan: %sfinished\n", + ret ? "" : "not "); + return ret; +} + +static ssize_t virtscsi_host_store_rescan(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + virtscsi_scan_start(shost); + return count; +} +static DEVICE_ATTR(rescan, S_IWUSR, NULL, virtscsi_host_store_rescan); + +static struct device_attribute *virtscsi_shost_attrs[] = { + &dev_attr_rescan, + NULL, +}; + static struct scsi_host_template virtscsi_host_template = { .module = THIS_MODULE, .name = "Virtio SCSI HBA", @@ -755,6 +920,9 @@ static struct scsi_host_template virtscsi_host_template = { .dma_boundary = UINT_MAX, .map_queues = virtscsi_map_queues, .track_queue_depth = 1, + .scan_start = virtscsi_scan_start, + .scan_finished = virtscsi_scan_finished, + .shost_attrs = virtscsi_shost_attrs, }; #define virtscsi_config_get(vdev, fld) \ @@ -872,6 +1040,9 @@ static int virtscsi_probe(struct virtio_device *vdev) vscsi->vdev = vdev; vscsi->num_queues = num_queues; vdev->priv = shost; + vscsi->next_target_id = -1; + spin_lock_init(&vscsi->rescan_lock); + INIT_WORK(&vscsi->rescan_work, virtscsi_rescan_work); err = virtscsi_init(vdev, vscsi); if (err) @@ -883,6 +1054,9 @@ static int virtscsi_probe(struct virtio_device *vdev) shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; + if (!virtio_has_feature(vdev, VIRTIO_SCSI_F_RESCAN)) + shost->sequential_scan = true; + /* LUNs > 256 are reported with format 1, so they go in the range * 16640-32767. */ @@ -974,6 +1148,7 @@ static unsigned int features[] = { #ifdef CONFIG_BLK_DEV_INTEGRITY VIRTIO_SCSI_F_T10_PI, #endif + VIRTIO_SCSI_F_RESCAN, }; static struct virtio_driver virtio_scsi_driver = { diff --git a/include/uapi/linux/virtio_scsi.h b/include/uapi/linux/virtio_scsi.h index 0abaae4027c01..257a1edc70833 100644 --- a/include/uapi/linux/virtio_scsi.h +++ b/include/uapi/linux/virtio_scsi.h @@ -96,6 +96,19 @@ struct virtio_scsi_ctrl_an_resp { __u8 response; } __attribute__((packed)); +/* Target rescan */ +struct virtio_scsi_rescan_req { + __virtio32 type; + __virtio32 next_id; +} __attribute__((packed)); + +struct virtio_scsi_rescan_resp { + __virtio32 id; + __virtio32 transport; + uint8_t node_wwn[8]; + uint8_t port_wwn[8]; +} __attribute__((packed)); + struct virtio_scsi_event { __virtio32 event; __u8 lun[8]; @@ -120,6 +133,7 @@ struct virtio_scsi_config { #define VIRTIO_SCSI_F_HOTPLUG 1 #define VIRTIO_SCSI_F_CHANGE 2 #define VIRTIO_SCSI_F_T10_PI 3 +#define VIRTIO_SCSI_F_RESCAN 4 /* Response codes */ #define VIRTIO_SCSI_S_OK 0 @@ -140,6 +154,7 @@ struct virtio_scsi_config { #define VIRTIO_SCSI_T_TMF 0 #define VIRTIO_SCSI_T_AN_QUERY 1 #define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 +#define VIRTIO_SCSI_T_RESCAN 3 /* Valid TMF subtypes. */ #define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 |
