Perform hardware cache flush on all disks during barrier operation. This makes it possible to enable hardware write cache. The flush is performed concurrently for all the device in the table. Signed-off-by: Mikulas Patocka --- drivers/md/dm-table.c | 81 ++++++++++++++++++++++++++++++++++++++++++ drivers/md/dm.c | 9 ++++ include/linux/device-mapper.h | 5 ++ 3 files changed, 94 insertions(+), 1 deletion(-) Index: linux-2.6.29-rc1-devel/drivers/md/dm-table.c =================================================================== --- linux-2.6.29-rc1-devel.orig/drivers/md/dm-table.c 2009-01-23 19:07:09.000000000 +0100 +++ linux-2.6.29-rc1-devel/drivers/md/dm-table.c 2009-01-27 20:31:06.000000000 +0100 @@ -988,6 +988,86 @@ void dm_table_unplug_all(struct dm_table } } +struct flush_completion { + atomic_t count; + struct task_struct *sleeper; + atomic_t error; +}; + +static void bio_end_flush(struct bio *bio, int error) +{ + struct flush_completion *completion = bio->bi_private; + + bio_put(bio); + + if (unlikely(error) && unlikely(error != -EOPNOTSUPP)) + atomic_cmpxchg(&completion->error, 0, error); + + if (atomic_dec_and_test(&completion->count)) + wake_up_process(completion->sleeper); +} + +static void wait_for_flush_completion(struct flush_completion *completion) +{ + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + + if (!atomic_read(&completion->count)) + break; + + io_schedule(); + } + set_current_state(TASK_RUNNING); + +} + +static void bio_null_destructor(struct bio *bio) +{ +} + +int dm_table_flush_all(struct dm_table *t) +{ + struct dm_dev_internal *dd; + struct list_head *devices = dm_table_get_devices(t); + + struct bio reserved_bio; + + struct flush_completion completion; + atomic_set(&completion.count, 0); + completion.sleeper = current; + atomic_set(&completion.error, 0); + + list_for_each_entry(dd, devices, list) { + struct bio *bio; + + atomic_inc(&completion.count); + + bio = bio_kmalloc(GFP_ATOMIC, 0); + + /* + * If we are out of memory, use the on-stack bio. + * but in that case, we must sync devices one-by-one + */ + if (!bio) { + bio = &reserved_bio; + bio_init(&reserved_bio); + reserved_bio.bi_destructor = bio_null_destructor; + } + bio->bi_end_io = bio_end_flush; + bio->bi_private = &completion; + bio->bi_bdev = dd->dm_dev.bdev; + + submit_bio(WRITE_BARRIER, bio); + + if (unlikely(bio == &reserved_bio)) + wait_for_flush_completion(&completion); + } + + wait_for_flush_completion(&completion); + + return atomic_read(&completion.error); +} + struct mapped_device *dm_table_get_md(struct dm_table *t) { dm_get(t->md); @@ -1005,3 +1085,4 @@ EXPORT_SYMBOL(dm_table_get_md); EXPORT_SYMBOL(dm_table_put); EXPORT_SYMBOL(dm_table_get); EXPORT_SYMBOL(dm_table_unplug_all); +EXPORT_SYMBOL(dm_table_flush_all); Index: linux-2.6.29-rc1-devel/drivers/md/dm.c =================================================================== --- linux-2.6.29-rc1-devel.orig/drivers/md/dm.c 2009-01-23 19:07:13.000000000 +0100 +++ linux-2.6.29-rc1-devel/drivers/md/dm.c 2009-01-27 20:23:42.000000000 +0100 @@ -1407,8 +1407,15 @@ static int dm_wait_for_completion(struct static int dm_flush(struct mapped_device *md) { + int error; + struct dm_table *map; dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE); - return 0; + map = dm_get_table(md); + if (!map) + return 0; + error = dm_table_flush_all(map); + dm_table_put(map); + return error; } /* Index: linux-2.6.29-rc1-devel/include/linux/device-mapper.h =================================================================== --- linux-2.6.29-rc1-devel.orig/include/linux/device-mapper.h 2009-01-27 20:30:24.000000000 +0100 +++ linux-2.6.29-rc1-devel/include/linux/device-mapper.h 2009-01-27 20:30:53.000000000 +0100 @@ -264,6 +264,11 @@ int dm_table_complete(struct dm_table *t void dm_table_unplug_all(struct dm_table *t); /* + * Flush hardware cache on all devices in a table. + */ +int dm_table_flush_all(struct dm_table *t); + +/* * Table reference counting. */ struct dm_table *dm_get_table(struct mapped_device *md);