diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index a364243fac7c..ce2899e4c1c8 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -229,9 +229,11 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
if (error)
goto fail;
- error = scsi_mq_setup_tags(shost);
- if (error)
- goto fail;
+ if (!shost->per_device_tag_set) {
+ error = scsi_mq_setup_tags(shost, &shost->tag_set);
+ if (error)
+ goto fail;
+ }
if (!shost->shost_gendev.parent)
shost->shost_gendev.parent = dev ? dev : &platform_bus;@@ -345,7 +347,7 @@ static void scsi_host_dev_release(struct device *dev)
kfree(dev_name(&shost->shost_dev));
}
- if (shost->tag_set.tags)
+ if (!shost->per_device_tag_set && shost->tag_set.tags)
scsi_mq_destroy_tags(shost);
kfree(shost->shost_data);
@@ -427,6 +429,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
shost->cmd_per_lun = sht->cmd_per_lun;
shost->no_write_same = sht->no_write_same;
shost->hctx_share_tags = sht->hctx_share_tags;
+ shost->per_device_tag_set = sht->per_device_tag_set;
if (shost_eh_deadline == -1 || !sht->eh_host_reset_handler)
shost->eh_deadline = -1;
@@ -566,7 +569,7 @@ struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(scsi_host_get);
-static bool scsi_host_check_in_flight(struct request *rq, void *data,
+bool scsi_check_in_flight(struct request *rq, void *data,
bool reserved)
{
int *count = data;@@ -585,9 +588,17 @@ static bool scsi_host_check_in_flight(struct request *rq, void *data,
int scsi_host_busy(struct Scsi_Host *shost)
{
int cnt = 0;
+ if (shost->per_device_tag_set) {
- blk_mq_tagset_busy_iter(&shost->tag_set,
- scsi_host_check_in_flight, &cnt);
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost) {
+ blk_mq_tagset_busy_iter(sdev->request_queue->tag_set,
+ scsi_check_in_flight, &cnt);
+ }
+ } else
+ blk_mq_tagset_busy_iter(&shost->tag_set, scsi_check_in_flight,
+ &cnt);
return cnt;
}
EXPORT_SYMBOL(scsi_host_busy);@@ -687,6 +698,8 @@ static bool complete_all_cmds_iter(struct request *rq, void *data, bool rsvd)
void scsi_host_complete_all_commands(struct Scsi_Host *shost,
enum scsi_host_status status)
{
+ if (shost->per_device_tag_set)
+ return;
blk_mq_tagset_busy_iter(&shost->tag_set, complete_all_cmds_iter,
&status);
}@@ -724,6 +737,9 @@ void scsi_host_busy_iter(struct Scsi_Host *shost,
.priv = priv,
};
+ if (shost->per_device_tag_set)
+ return;
+
blk_mq_tagset_busy_iter(&shost->tag_set, __scsi_host_busy_iter_fn,
&iter_data);
}
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index bba66e29d38c..ec69a526c397 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1369,6 +1369,17 @@ static inline int scsi_target_queue_ready(struct Scsi_Host *shost,
return 0;
}
+static int scsi_tag_set_busy(struct scsi_device *sdevice)
+{
+ struct blk_mq_tag_set *tag_set = sdevice->request_queue->tag_set;
+
+ int cnt = 0;
+
+ blk_mq_tagset_busy_iter(tag_set,
+ scsi_check_in_flight, &cnt);
+ return cnt;
+}
+
/*
* scsi_host_queue_ready: if we can send requests to shost, return 1 else
* return 0. We must end up running the queue again whenever 0 is@@ -1383,9 +1394,11 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
return 0;
if (atomic_read(&shost->host_blocked) > 0) {
- if (scsi_host_busy(shost) > 0)
+ if (shost->per_device_tag_set) {
+ if (scsi_tag_set_busy(sdev) > 0)
+ goto starved;
+ } else if (scsi_host_busy(shost) > 0)
goto starved;
-
/*
* unblock after host_blocked iterates to zero
*/@@ -1961,11 +1974,9 @@ static const struct blk_mq_ops scsi_mq_ops = {
.get_rq_budget_token = scsi_mq_get_rq_budget_token,
};
-int scsi_mq_setup_tags(struct Scsi_Host *shost)
+int scsi_mq_setup_tags(struct Scsi_Host *shost, struct blk_mq_tag_set *tag_set)
{
unsigned int cmd_size, sgl_size;
- struct blk_mq_tag_set *tag_set = &shost->tag_set;
-
sgl_size = max_t(unsigned int, sizeof(struct scatterlist),
scsi_mq_inline_sgl_size(shost));
cmd_size = sizeof(struct scsi_cmnd) + shost->hostt->cmd_size + sgl_size;@@ -2955,7 +2966,8 @@ scsi_host_block(struct Scsi_Host *shost)
* SCSI never enables blk-mq's BLK_MQ_F_BLOCKING flag so
* calling synchronize_rcu() once is enough.
*/
- WARN_ON_ONCE(shost->tag_set.flags & BLK_MQ_F_BLOCKING);
+ if (!shost->per_device_tag_set)
+ WARN_ON_ONCE(shost->tag_set.flags & BLK_MQ_F_BLOCKING);
if (!ret)
synchronize_rcu();
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 5c4786310a31..aac2fbfb7d94 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -93,7 +93,7 @@ extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
extern void scsi_run_host_queues(struct Scsi_Host *shost);
extern void scsi_requeue_run_queue(struct work_struct *work);
extern void scsi_start_queue(struct scsi_device *sdev);
-extern int scsi_mq_setup_tags(struct Scsi_Host *shost);
+extern int scsi_mq_setup_tags(struct Scsi_Host *shost, struct blk_mq_tag_set *tag_set);
extern void scsi_mq_destroy_tags(struct Scsi_Host *shost);
extern void scsi_exit_queue(void);
extern void scsi_evt_thread(struct work_struct *work);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index f4e6c68ac99e..3d213da4a87f 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -276,6 +276,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
unsigned int depth;
struct scsi_device *sdev;
struct request_queue *q;
+ struct blk_mq_tag_set *tag_set;
int display_failure_msg = 1, ret;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
@@ -324,16 +325,27 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
* doesn't
*/
sdev->borken = 1;
+ if (shost->per_device_tag_set) {
+ tag_set = kzalloc(sizeof(struct blk_mq_tag_set), GFP_KERNEL);
+ sdev->tag_set = tag_set;
+ if (!tag_set)
+ goto out;
+ if (scsi_mq_setup_tags(shost, tag_set))
+ goto out_free_tag_set;
+ } else {
+ tag_set = &shost->tag_set;
+ sdev->tag_set = NULL;
+ }
sdev->sg_reserved_size = INT_MAX;
- q = blk_mq_init_queue(&sdev->host->tag_set);
+ q = blk_mq_init_queue(tag_set);
if (IS_ERR(q)) {
/* release fn is set up in scsi_sysfs_device_initialise, so
* have to free and put manually here */
put_device(&starget->dev);
kfree(sdev);
- goto out;
+ goto out_free_tag_set;
}
sdev->request_queue = q;
q->queuedata = sdev;@@ -351,7 +363,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
if (scsi_realloc_sdev_budget_map(sdev, depth)) {
put_device(&starget->dev);
kfree(sdev);
- goto out;
+ goto out_free_tag_set;
}
scsi_change_queue_depth(sdev, depth);@@ -367,14 +379,20 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
*/
if (ret == -ENXIO)
display_failure_msg = 0;
- goto out_device_destroy;
+ __scsi_remove_device(sdev);
+ /*
+ * __scsi_remove_device() will free the tag_set, so go
+ * to "out" label.
+ */
+ goto out;
}
}
return sdev;
-out_device_destroy:
- __scsi_remove_device(sdev);
+out_free_tag_set:
+ if (shost->per_device_tag_set)
+ kfree(tag_set);
out:
if (display_failure_msg)
printk(ALLOC_FAILURE_MSG, __func__);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index f1e0c131b77c..dd11f3d59663 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -399,7 +399,10 @@ show_nr_hw_queues(struct device *dev, struct device_attribute *attr, char *buf)
struct Scsi_Host *shost = class_to_shost(dev);
struct blk_mq_tag_set *tag_set = &shost->tag_set;
- return snprintf(buf, 20, "%d\n", tag_set->nr_hw_queues);
+ if (shost->per_device_tag_set)
+ return 0;
+ else
+ return snprintf(buf, 20, "%d\n", tag_set->nr_hw_queues);
}
static DEVICE_ATTR(nr_hw_queues, S_IRUGO, show_nr_hw_queues, NULL);
@@ -1467,6 +1470,12 @@ void __scsi_remove_device(struct scsi_device *sdev)
sdev->host->hostt->slave_destroy(sdev);
transport_destroy_device(dev);
+ if (sdev->host->per_device_tag_set) {
+ blk_mq_free_tag_set(sdev->tag_set);
+ kfree(sdev->tag_set);
+ sdev->tag_set = NULL;
+ }
+
/*
* Paired with the kref_get() in scsi_sysfs_initialize(). We have
* removed sysfs visibility from the device, so make the targetdiff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 647c53b26105..aa8c7f860a34 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -103,6 +103,7 @@ struct scsi_vpd {
struct scsi_device {
struct Scsi_Host *host;
struct request_queue *request_queue;
+ struct blk_mq_tag_set *tag_set;
/* the next two are protected by the host->host_lock */
struct list_head siblings; /* list of all devices on this host */diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 1255e8c164f6..a625a8490ea8 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -456,7 +456,39 @@ struct scsi_host_template {
/* True if the controller does not support WRITE SAME */
unsigned no_write_same:1;
+ /*
+ * True if hardware queues will share blk_mq_tags
+ * (BLK_MQ_F_TAG_HCTX_SHARED will be set)
+ */
unsigned hctx_share_tags:1;
+ /*
+ * True if the LLD will create blk_mq_tag_sets for each scsi_device
+ * If False, request_queues will share the single blk_mq_tag_set in the
+ * Scsi_Host.
+ *
+ * If per_device_tag_set is False and hctx_share_tags is True, all
+ * devices on the Scsi_Host share a single blk_mq_tag_set referenced
+ * through the host and all hardware queues share a blk_mq_tags.
+ *
+ * If per_device_tag_set is True and hctx_share_tags is True, all
+ * devices on the Scsi_Host have their own blk_mq_tag_set, allocated and
+ * maintained by the LLD, and all hardware queues on a given device
+ * share one blk_mq_tags.
+ *
+ * If per_device_tag_set is False and hctx_share_tags is False, all
+ * devices on the Scsi_Host share a single blk_mq_tag_set referenced
+ * through the host and hardware queues have their own blk_mq_tags. That
+ * is, hardware context 1 from device 1 and hardware context 1 from
+ * device 2 will share the same blk_mq_tags in the host blk_mq_tag_set
+ * but, hardware context 2 from device 1 and hardware context 2 from
+ * device 2 will share another blk_mq_tags in the host blk_mq_tag_set.
+ *
+ * If per_device_tag_set is True and hctx_share_tags is False, all
+ * devices on the Scsi_Host will have their own blk_mq_tag_set and all
+ * hardware queues will have their own blk_mq_tags.
+ */
+ unsigned per_device_tag_set:1;
+
/*
* Countdown for host blocking with no commands outstanding.@@ -649,8 +681,14 @@ struct Scsi_Host {
/* The controller does not support WRITE SAME */
unsigned no_write_same:1;
+ /*
+ * See comment in struct scsi_host_template for details on how
+ * hctx_share_tags and per_device_tag_set interact.
+ */
unsigned hctx_share_tags:1;
+ unsigned per_device_tag_set:1;
+
/* Host responded with short (<36 bytes) INQUIRY result */
unsigned short_inquiry:1;
@@ -754,6 +792,9 @@ extern void scsi_rescan_device(struct device *);
extern void scsi_remove_host(struct Scsi_Host *);
extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);
extern int scsi_host_busy(struct Scsi_Host *shost);
+// TODO: does this belong in another file?
+extern bool scsi_check_in_flight(struct request *rq, void *data,
+ bool reserved);
extern void scsi_host_put(struct Scsi_Host *t);
extern struct Scsi_Host *scsi_host_lookup(unsigned short);
extern const char *scsi_host_state_name(enum scsi_host_state);
diff --git a/include/scsi/scsi_tcq.h b/include/scsi/scsi_tcq.h
index ea7848e74d25..443e4d917dd3 100644
--- a/include/scsi/scsi_tcq.h
+++ b/include/scsi/scsi_tcq.h
@@ -19,18 +19,17 @@
* Note: for devices using multiple hardware queues tag must have been
* generated by blk_mq_unique_tag().
**/
-static inline struct scsi_cmnd *scsi_host_find_tag(struct Scsi_Host *shost,
- int tag)
+static inline struct scsi_cmnd *scsi_find_tag(struct blk_mq_tag_set *tag_set, int tag)
{
struct request *req = NULL;
u16 hwq;
if (tag == SCSI_NO_TAG)
return NULL;
-
hwq = blk_mq_unique_tag_to_hwq(tag);
- if (hwq < shost->tag_set.nr_hw_queues) {
- req = blk_mq_tag_to_rq(shost->tag_set.tags[hwq],
+
+ if (hwq < tag_set->nr_hw_queues) {
+ req = blk_mq_tag_to_rq(tag_set->tags[hwq],
blk_mq_unique_tag_to_tag(tag));
}
@@ -39,5 +38,11 @@ static inline struct scsi_cmnd *scsi_host_find_tag(struct Scsi_Host *shost,
return blk_mq_rq_to_pdu(req);
}
+static inline struct scsi_cmnd *scsi_host_find_tag(struct Scsi_Host *shost,
+ int tag)
+{
+ return scsi_find_tag(&shost->tag_set, tag);
+}
+
#endif /* CONFIG_BLOCK */
#endif /* _SCSI_SCSI_TCQ_H */--
2.25.1