aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2017-11-16 09:16:22 +0100
committerHannes Reinecke <hare@suse.com>2021-03-14 12:14:39 +0100
commitd5a2b439cfd4818e84e84c43c7e499d258af4500 (patch)
tree8c38ebde196f0b87671528bf0b7403105d6adf16
parentdacea834b95ee600d0e00c18a9782a3f26caad00 (diff)
downloadscsi-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.c175
-rw-r--r--include/uapi/linux/virtio_scsi.h15
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 = &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 = &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