libata: take advantage of cmwq and remove concurrency limitations
authorTejun Heo <tj@kernel.org>
Fri, 2 Jul 2010 08:03:52 +0000 (10:03 +0200)
committerTejun Heo <tj@kernel.org>
Fri, 2 Jul 2010 08:59:24 +0000 (10:59 +0200)
libata has two concurrency related limitations.

a. ata_wq which is used for polling PIO has single thread per CPU.  If
   there are multiple devices doing polling PIO on the same CPU, they
   can't be executed simultaneously.

b. ata_aux_wq which is used for SCSI probing has single thread.  In
   cases where SCSI probing is stalled for extended period of time
   which is possible for ATAPI devices, this will stall all probing.

#a is solved by increasing maximum concurrency of ata_wq.  Please note
that polling PIO might be used under allocation path and thus needs to
be served by a separate wq with a rescuer.

#b is solved by using the default wq instead and achieving exclusion
via per-port mutex.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Jeff Garzik <jgarzik@pobox.com>
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
drivers/ata/libata-scsi.c
drivers/ata/libata-sff.c
drivers/ata/libata.h
include/linux/libata.h

index ddf8e48627878999c17f686fcfb05dce669a2b9c..4f78741692dc19078ebe10df68c306db6d00561d 100644 (file)
@@ -98,8 +98,6 @@ static unsigned long ata_dev_blacklisted(const struct ata_device *dev);
 
 unsigned int ata_print_id = 1;
 
-struct workqueue_struct *ata_aux_wq;
-
 struct ata_force_param {
        const char      *name;
        unsigned int    cbl;
@@ -5611,6 +5609,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
        ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN;
 #endif
 
+       mutex_init(&ap->scsi_scan_mutex);
        INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
        INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
        INIT_LIST_HEAD(&ap->eh_done_q);
@@ -6549,29 +6548,20 @@ static int __init ata_init(void)
 
        ata_parse_force_param();
 
-       ata_aux_wq = create_singlethread_workqueue("ata_aux");
-       if (!ata_aux_wq)
-               goto fail;
-
        rc = ata_sff_init();
-       if (rc)
-               goto fail;
+       if (rc) {
+               kfree(ata_force_tbl);
+               return rc;
+       }
 
        printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
        return 0;
-
-fail:
-       kfree(ata_force_tbl);
-       if (ata_aux_wq)
-               destroy_workqueue(ata_aux_wq);
-       return rc;
 }
 
 static void __exit ata_exit(void)
 {
        ata_sff_exit();
        kfree(ata_force_tbl);
-       destroy_workqueue(ata_aux_wq);
 }
 
 subsys_initcall(ata_init);
index f77a67303f8b3d0d724e4fccd3510a269a72b0c8..4d2af824dd23f50674ef334c5eebc048576753eb 100644 (file)
@@ -727,7 +727,7 @@ void ata_scsi_error(struct Scsi_Host *host)
        if (ap->pflags & ATA_PFLAG_LOADING)
                ap->pflags &= ~ATA_PFLAG_LOADING;
        else if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG)
-               queue_delayed_work(ata_aux_wq, &ap->hotplug_task, 0);
+               schedule_delayed_work(&ap->hotplug_task, 0);
 
        if (ap->pflags & ATA_PFLAG_RECOVERED)
                ata_port_printk(ap, KERN_INFO, "EH complete\n");
@@ -2944,7 +2944,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                        ehc->i.flags |= ATA_EHI_SETMODE;
 
                        /* schedule the scsi_rescan_device() here */
-                       queue_work(ata_aux_wq, &(ap->scsi_rescan_task));
+                       schedule_work(&(ap->scsi_rescan_task));
                } else if (dev->class == ATA_DEV_UNKNOWN &&
                           ehc->tries[dev->devno] &&
                           ata_class_enabled(ehc->classes[dev->devno])) {
index a54273d2c3c6a9f432ae60d49496335452ecc1d6..d75c9c479d1a2dda6f545852d8c3f4161c162cb1 100644 (file)
@@ -3435,7 +3435,7 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
                                "                  switching to async\n");
        }
 
-       queue_delayed_work(ata_aux_wq, &ap->hotplug_task,
+       queue_delayed_work(system_long_wq, &ap->hotplug_task,
                           round_jiffies_relative(HZ));
 }
 
@@ -3582,6 +3582,7 @@ void ata_scsi_hotplug(struct work_struct *work)
        }
 
        DPRINTK("ENTER\n");
+       mutex_lock(&ap->scsi_scan_mutex);
 
        /* Unplug detached devices.  We cannot use link iterator here
         * because PMP links have to be scanned even if PMP is
@@ -3595,6 +3596,7 @@ void ata_scsi_hotplug(struct work_struct *work)
        /* scan for new ones */
        ata_scsi_scan_host(ap, 0);
 
+       mutex_unlock(&ap->scsi_scan_mutex);
        DPRINTK("EXIT\n");
 }
 
@@ -3673,9 +3675,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
  *     @work: Pointer to ATA port to perform scsi_rescan_device()
  *
  *     After ATA pass thru (SAT) commands are executed successfully,
- *     libata need to propagate the changes to SCSI layer.  This
- *     function must be executed from ata_aux_wq such that sdev
- *     attach/detach don't race with rescan.
+ *     libata need to propagate the changes to SCSI layer.
  *
  *     LOCKING:
  *     Kernel thread context (may sleep).
@@ -3688,6 +3688,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
        struct ata_device *dev;
        unsigned long flags;
 
+       mutex_lock(&ap->scsi_scan_mutex);
        spin_lock_irqsave(ap->lock, flags);
 
        ata_for_each_link(link, ap, EDGE) {
@@ -3707,6 +3708,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
        }
 
        spin_unlock_irqrestore(ap->lock, flags);
+       mutex_unlock(&ap->scsi_scan_mutex);
 }
 
 /**
index efa4a18cfb9d92fb9a79670abf69669f96f2d436..674c1436491f5e5e6df3a2334ee7f995775f7634 100644 (file)
@@ -3318,14 +3318,7 @@ void ata_sff_port_init(struct ata_port *ap)
 
 int __init ata_sff_init(void)
 {
-       /*
-        * FIXME: In UP case, there is only one workqueue thread and if you
-        * have more than one PIO device, latency is bloody awful, with
-        * occasional multi-second "hiccups" as one PIO device waits for
-        * another.  It's an ugly wart that users DO occasionally complain
-        * about; luckily most users have at most one PIO polled device.
-        */
-       ata_sff_wq = create_workqueue("ata_sff");
+       ata_sff_wq = alloc_workqueue("ata_sff", WQ_RESCUER, WQ_MAX_ACTIVE);
        if (!ata_sff_wq)
                return -ENOMEM;
 
index 4b84ed60324a4956cc031bc6b9e68fa6efdb6872..9ce1ecc63e394227348aae2a513a6cd204748748 100644 (file)
@@ -54,7 +54,6 @@ enum {
 };
 
 extern unsigned int ata_print_id;
-extern struct workqueue_struct *ata_aux_wq;
 extern int atapi_passthru16;
 extern int libata_fua;
 extern int libata_noacpi;
index b85f3ff34d7d3d71cb99f0ded4db0ce186780b57..f010f18a0f863f39e139d65469debae94232b259 100644 (file)
@@ -751,6 +751,7 @@ struct ata_port {
        struct ata_host         *host;
        struct device           *dev;
 
+       struct mutex            scsi_scan_mutex;
        struct delayed_work     hotplug_task;
        struct work_struct      scsi_rescan_task;