]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: scsi_transport_fc: Change to use per-rport devloss_work_q
authorEwan D. Milne <emilne@redhat.com>
Mon, 7 Jul 2025 20:22:25 +0000 (16:22 -0400)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 15 Jul 2025 00:57:08 +0000 (20:57 -0400)
Configurations with large numbers of FC rports per host instance are
taking a very long time to complete all devloss work.  Increase potential
parallelism by using a per-rport devloss_work_q for dev_loss_work and
fast_io_fail_work.

Signed-off-by: Ewan D. Milne <emilne@redhat.com>
Link: https://lore.kernel.org/r/20250707202225.1203189-1-emilne@redhat.com
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi_transport_fc.c
include/scsi/scsi_transport_fc.h

index 6b165a3ec6de13a94ab67f8cab2acc96614ba81a..82d091d627c09a0674e0178a4cdae2d21c2fa8fd 100644 (file)
@@ -446,13 +446,6 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
                return -ENOMEM;
 
        fc_host->dev_loss_tmo = fc_dev_loss_tmo;
-       fc_host->devloss_work_q = alloc_workqueue("fc_dl_%d", 0, 0,
-                                       shost->host_no);
-       if (!fc_host->devloss_work_q) {
-               destroy_workqueue(fc_host->work_q);
-               fc_host->work_q = NULL;
-               return -ENOMEM;
-       }
 
        fc_bsg_hostadd(shost, fc_host);
        /* ignore any bsg add error - we just can't do sgio */
@@ -2821,10 +2814,10 @@ fc_flush_work(struct Scsi_Host *shost)
  *     1 on success / 0 already queued / < 0 for error
  */
 static int
-fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work,
-                               unsigned long delay)
+fc_queue_devloss_work(struct Scsi_Host *shost, struct fc_rport *rport,
+                     struct delayed_work *work, unsigned long delay)
 {
-       if (unlikely(!fc_host_devloss_work_q(shost))) {
+       if (unlikely(!rport->devloss_work_q)) {
                printk(KERN_ERR
                        "ERROR: FC host '%s' attempted to queue work, "
                        "when no workqueue created.\n", shost->hostt->name);
@@ -2833,7 +2826,7 @@ fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work,
                return -EINVAL;
        }
 
-       return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay);
+       return queue_delayed_work(rport->devloss_work_q, work, delay);
 }
 
 /**
@@ -2841,9 +2834,9 @@ fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work,
  * @shost:     Pointer to Scsi_Host bound to fc_host.
  */
 static void
-fc_flush_devloss(struct Scsi_Host *shost)
+fc_flush_devloss(struct Scsi_Host *shost, struct fc_rport *rport)
 {
-       if (!fc_host_devloss_work_q(shost)) {
+       if (unlikely(!rport->devloss_work_q)) {
                printk(KERN_ERR
                        "ERROR: FC host '%s' attempted to flush work, "
                        "when no workqueue created.\n", shost->hostt->name);
@@ -2851,7 +2844,7 @@ fc_flush_devloss(struct Scsi_Host *shost)
                return;
        }
 
-       flush_workqueue(fc_host_devloss_work_q(shost));
+       flush_workqueue(rport->devloss_work_q);
 }
 
 
@@ -2913,13 +2906,6 @@ fc_remove_host(struct Scsi_Host *shost)
                fc_host->work_q = NULL;
                destroy_workqueue(work_q);
        }
-
-       /* flush all devloss work items, then kill it  */
-       if (fc_host->devloss_work_q) {
-               work_q = fc_host->devloss_work_q;
-               fc_host->devloss_work_q = NULL;
-               destroy_workqueue(work_q);
-       }
 }
 EXPORT_SYMBOL(fc_remove_host);
 
@@ -2967,6 +2953,7 @@ fc_rport_final_delete(struct work_struct *work)
        struct device *dev = &rport->dev;
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
+       struct workqueue_struct *work_q;
        unsigned long flags;
        int do_callback = 0;
 
@@ -2988,9 +2975,9 @@ fc_rport_final_delete(struct work_struct *work)
        if (rport->flags & FC_RPORT_DEVLOSS_PENDING) {
                spin_unlock_irqrestore(shost->host_lock, flags);
                if (!cancel_delayed_work(&rport->fail_io_work))
-                       fc_flush_devloss(shost);
+                       fc_flush_devloss(shost, rport);
                if (!cancel_delayed_work(&rport->dev_loss_work))
-                       fc_flush_devloss(shost);
+                       fc_flush_devloss(shost, rport);
                cancel_work_sync(&rport->scan_work);
                spin_lock_irqsave(shost->host_lock, flags);
                rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
@@ -3021,6 +3008,12 @@ fc_rport_final_delete(struct work_struct *work)
 
        fc_bsg_remove(rport->rqst_q);
 
+       if (rport->devloss_work_q) {
+               work_q = rport->devloss_work_q;
+               rport->devloss_work_q = NULL;
+               destroy_workqueue(work_q);
+       }
+
        transport_remove_device(dev);
        device_del(dev);
        transport_destroy_device(dev);
@@ -3093,6 +3086,22 @@ fc_remote_port_create(struct Scsi_Host *shost, int channel,
 
        spin_unlock_irqrestore(shost->host_lock, flags);
 
+       rport->devloss_work_q = alloc_workqueue("fc_dl_%d_%d", 0, 0,
+                                               shost->host_no, rport->number);
+       if (!rport->devloss_work_q) {
+               printk(KERN_ERR "FC Remote Port alloc_workqueue failed\n");
+/*
+ * Note that we have not yet called device_initialize() / get_device()
+ * Cannot reclaim incremented rport->number because we released host_lock
+ */
+               spin_lock_irqsave(shost->host_lock, flags);
+               list_del(&rport->peers);
+               scsi_host_put(shost);                   /* for fc_host->rport list */
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               kfree(rport);
+               return NULL;
+       }
+
        dev = &rport->dev;
        device_initialize(dev);                 /* takes self reference */
        dev->parent = get_device(&shost->shost_gendev); /* parent reference */
@@ -3255,9 +3264,9 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel,
                                 * be checked and will NOOP the function.
                                 */
                                if (!cancel_delayed_work(&rport->fail_io_work))
-                                       fc_flush_devloss(shost);
+                                       fc_flush_devloss(shost, rport);
                                if (!cancel_delayed_work(&rport->dev_loss_work))
-                                       fc_flush_devloss(shost);
+                                       fc_flush_devloss(shost, rport);
 
                                spin_lock_irqsave(shost->host_lock, flags);
 
@@ -3451,11 +3460,12 @@ fc_remote_port_delete(struct fc_rport  *rport)
        /* see if we need to kill io faster than waiting for device loss */
        if ((rport->fast_io_fail_tmo != -1) &&
            (rport->fast_io_fail_tmo < timeout))
-               fc_queue_devloss_work(shost, &rport->fail_io_work,
-                                       rport->fast_io_fail_tmo * HZ);
+               fc_queue_devloss_work(shost, rport, &rport->fail_io_work,
+                                     rport->fast_io_fail_tmo * HZ);
 
        /* cap the length the devices can be blocked until they are deleted */
-       fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ);
+       fc_queue_devloss_work(shost, rport, &rport->dev_loss_work,
+                             timeout * HZ);
 }
 EXPORT_SYMBOL(fc_remote_port_delete);
 
@@ -3514,9 +3524,9 @@ fc_remote_port_rolechg(struct fc_rport  *rport, u32 roles)
                 * transaction.
                 */
                if (!cancel_delayed_work(&rport->fail_io_work))
-                       fc_flush_devloss(shost);
+                       fc_flush_devloss(shost, rport);
                if (!cancel_delayed_work(&rport->dev_loss_work))
-                       fc_flush_devloss(shost);
+                       fc_flush_devloss(shost, rport);
 
                spin_lock_irqsave(shost->host_lock, flags);
                rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT |
index d02b55261307e244bcbcd7a016c6bdd5efaa4977..b908aacfef48621ffa6c11a68a5e74ec7fee3529 100644 (file)
@@ -383,6 +383,8 @@ struct fc_rport {   /* aka fc_starget_attrs */
        struct work_struct stgt_delete_work;
        struct work_struct rport_delete_work;
        struct request_queue *rqst_q;   /* bsg support */
+
+       struct workqueue_struct *devloss_work_q;
 } __attribute__((aligned(sizeof(unsigned long))));
 
 /* bit field values for struct fc_rport "flags" field: */
@@ -576,7 +578,6 @@ struct fc_host_attrs {
 
        /* work queues for rport state manipulation */
        struct workqueue_struct *work_q;
-       struct workqueue_struct *devloss_work_q;
 
        /* bsg support */
        struct request_queue *rqst_q;
@@ -654,8 +655,6 @@ struct fc_host_attrs {
        (((struct fc_host_attrs *)(x)->shost_data)->npiv_vports_inuse)
 #define fc_host_work_q(x) \
        (((struct fc_host_attrs *)(x)->shost_data)->work_q)
-#define fc_host_devloss_work_q(x) \
-       (((struct fc_host_attrs *)(x)->shost_data)->devloss_work_q)
 #define fc_host_dev_loss_tmo(x) \
        (((struct fc_host_attrs *)(x)->shost_data)->dev_loss_tmo)
 #define fc_host_max_ct_payload(x)  \