From: Mike Anderson <andmike@linux.vnet.ibm.com>                                                                                     

Performance optimization for high contention of the q->queue_lock 
in dm_table_unplug_all under certain workloads.

A OLTP type workload on a 16core Intel box using a volume containing 48 logical
drives across 3 dual port fiber adapters showed some high oprofile values.

A high oprofile sampling value was seen in dm_table_unplug_all coming from
high contention of the q->queue_lock. Since dm_table_unplug_all will unplug all
devices in the table it can cause contention on the queue_lock with processes
trying to pull requests off the queue even on devices that are not plugged. As
dm_table_unplug_all is coded now blk_unplug will result in the queue_lock being
taken for all checks of the queue_flags +(blk_remove_plug).

The change lead to a ~16 percent performance improvement on a Red Hat
distro kernel. It appears to be a fairly safe change as the worst case if
dm missed a queue to unplug, the queue will get unplugged when the number
of requests exceed (unplug_thresh (4) or unplug_delay (3 ms). It can also
be the case that if post getting the queue_lock and checking for the queue
to be plugged that once the lock is dropped plugging can occur.

[AGK: Some doubts have been expressed about this patch now - it can hit
performance in some cases (stacking).  A better solution is being sought.]

Signed-off-by: Mike Anderson <andmike@linux.vnet.ibm.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
---
 drivers/md/dm-table.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletion(-)

Index: linux-2.6.25/drivers/md/dm-table.c
===================================================================
--- linux-2.6.25.orig/drivers/md/dm-table.c	2008-04-18 12:40:15.000000000 +0100
+++ linux-2.6.25/drivers/md/dm-table.c	2008-04-18 12:40:20.000000000 +0100
@@ -974,7 +974,8 @@ void dm_table_unplug_all(struct dm_table
 	list_for_each_entry(dd, devices, list) {
 		struct request_queue *q = bdev_get_queue(dd->bdev);
 
-		blk_unplug(q);
+		if (blk_queue_plugged(q))
+			blk_unplug(q);
 	}
 }
 
