zram supports stream-based parallel compression. IOW, it can compress in parallel on multi-core system only if there are *several* streams in the system because each stream can be compressed on each CPUs.
However, if there is *a* stream in the system, it cannot be compressed in parallel although the system supports multiple CPUs. This patch enables parallel compression using multiple CPUs even though there is a single stream in the system. When I tested benchmark, random read which is important for zram-swap case was worse so it supports async-write at the moment. Later, We might support async-read, I hope. It is useful for single stream scenario. I tested on 4-CPU ARM machine. FIO job=1 benchmark result Before: seq-write: (groupid=0, jobs=1): err= 0: pid=2971: Tue Sep 6 08:14:06 2016 write: io=163840KB, bw=28239KB/s, iops=441, runt= 5802msec rand-write: (groupid=1, jobs=1): err= 0: pid=2977: Tue Sep 6 08:14:06 2016 write: io=163840KB, bw=22300KB/s, iops=5575, runt= 7347msec seq-read: (groupid=2, jobs=1): err= 0: pid=2983: Tue Sep 6 08:14:06 2016 read : io=163840KB, bw=66928KB/s, iops=1045, runt= 2448msec rand-read: (groupid=3, jobs=1): err= 0: pid=2984: Tue Sep 6 08:14:06 2016 read : io=163840KB, bw=40980KB/s, iops=10245, runt= 3998msec mixed-seq: (groupid=4, jobs=1): err= 0: pid=2985: Tue Sep 6 08:14:06 2016 read : io=82240KB, bw=18308KB/s, iops=286, runt= 4492msec write: io=81600KB, bw=18166KB/s, iops=283, runt= 4492msec mixed-rand: (groupid=5, jobs=1): err= 0: pid=2989: Tue Sep 6 08:14:06 2016 read : io=84120KB, bw=14771KB/s, iops=3692, runt= 5695msec write: io=79720KB, bw=13998KB/s, iops=3499, runt= 5695msec After: write: io=163840KB, bw=60547KB/s, iops=946, runt= 2706msec rand-write: (groupid=1, jobs=1): err= 0: pid=2940: Tue Sep 6 08:13:04 2016 write: io=163840KB, bw=39337KB/s, iops=9834, runt= 4165msec seq-read: (groupid=2, jobs=1): err= 0: pid=2946: Tue Sep 6 08:13:04 2016 read : io=163840KB, bw=66225KB/s, iops=1034, runt= 2474msec rand-read: (groupid=3, jobs=1): err= 0: pid=2947: Tue Sep 6 08:13:04 2016 read : io=163840KB, bw=40970KB/s, iops=10242, runt= 3999msec mixed-seq: (groupid=4, jobs=1): err= 0: pid=2948: Tue Sep 6 08:13:04 2016 read : io=82240KB, bw=31963KB/s, iops=499, runt= 2573msec write: io=81600KB, bw=31714KB/s, iops=495, runt= 2573msec mixed-rand: (groupid=5, jobs=1): err= 0: pid=2952: Tue Sep 6 08:13:04 2016 read : io=84120KB, bw=20192KB/s, iops=5048, runt= 4166msec write: io=79720KB, bw=19136KB/s, iops=4783, runt= 4166msec So, write/mixed-rw is 2 times faster. I tested fio 4 jobs to catch up regression of full stream workloads and result is not regression but enhanced two times. FIO job=4 benchmark result Before: seq-write: (groupid=0, jobs=4): err= 0: pid=3060: Tue Sep 6 08:22:13 2016 write: io=655360KB, bw=114834KB/s, iops=1794, runt= 5707msec rand-write: (groupid=1, jobs=4): err= 0: pid=3071: Tue Sep 6 08:22:13 2016 write: io=655360KB, bw=95520KB/s, iops=23879, runt= 6861msec seq-read: (groupid=2, jobs=4): err= 0: pid=3083: Tue Sep 6 08:22:13 2016 read : io=655360KB, bw=533247KB/s, iops=8331, runt= 1229msec rand-read: (groupid=3, jobs=4): err= 0: pid=3087: Tue Sep 6 08:22:13 2016 read : io=655360KB, bw=295874KB/s, iops=73968, runt= 2215msec mixed-seq: (groupid=4, jobs=4): err= 0: pid=3091: Tue Sep 6 08:22:13 2016 read : io=326272KB, bw=85861KB/s, iops=1341, runt= 3800msec write: io=329088KB, bw=86602KB/s, iops=1353, runt= 3800msec mixed-rand: (groupid=5, jobs=4): err= 0: pid=3101: Tue Sep 6 08:22:13 2016 read : io=326296KB, bw=49521KB/s, iops=12380, runt= 6589msec write: io=329064KB, bw=49941KB/s, iops=12485, runt= 6589msec After: seq-write: (groupid=0, jobs=4): err= 0: pid=3129: Tue Sep 6 08:23:02 2016 write: io=655360KB, bw=246098KB/s, iops=3845, runt= 2663msec rand-write: (groupid=1, jobs=4): err= 0: pid=3141: Tue Sep 6 08:23:02 2016 write: io=655360KB, bw=179158KB/s, iops=44789, runt= 3658msec seq-read: (groupid=2, jobs=4): err= 0: pid=3154: Tue Sep 6 08:23:02 2016 read : io=655360KB, bw=560616KB/s, iops=8759, runt= 1169msec rand-read: (groupid=3, jobs=4): err= 0: pid=3158: Tue Sep 6 08:23:02 2016 read : io=655360KB, bw=290368KB/s, iops=72591, runt= 2257msec mixed-seq: (groupid=4, jobs=4): err= 0: pid=3162: Tue Sep 6 08:23:02 2016 read : io=326272KB, bw=196905KB/s, iops=3076, runt= 1657msec write: io=329088KB, bw=198605KB/s, iops=3103, runt= 1657msec mixed-rand: (groupid=5, jobs=4): err= 0: pid=3172: Tue Sep 6 08:23:02 2016 read : io=326296KB, bw=89152KB/s, iops=22287, runt= 3660msec write: io=329064KB, bw=89908KB/s, iops=22477, runt= 3660msec Signed-off-by: Minchan Kim <minc...@kernel.org> --- It's a RFC so intentionally, I didn't add any docuement about use_aio because it would waste my time to spend a document if we change mind after discussion(e.g., We might go default IO model as aio so user never need to know use_aio). Other thing I should do is that we should change the number of zram thread with changing the number of online CPU. It would be trivial. drivers/block/zram/zram_drv.c | 558 +++++++++++++++++++++++++++++++++++++----- drivers/block/zram/zram_drv.h | 1 + 2 files changed, 504 insertions(+), 55 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 04365b17ee67..feb6a4195c2f 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/vmalloc.h> +#include <linux/kthread.h> #include <linux/err.h> #include <linux/idr.h> #include <linux/sysfs.h> @@ -366,6 +367,46 @@ static ssize_t comp_algorithm_store(struct device *dev, return len; } +static ssize_t use_aio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + val = zram->use_aio; + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t use_aio_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + u16 do_async; + struct zram *zram = dev_to_zram(dev); + + ret = kstrtou16(buf, 10, &do_async); + if (ret) + return ret; + + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't change for initialized device\n"); + return -EBUSY; + } + + if (do_async) + zram->use_aio = true; + else + zram->use_aio = false; + up_write(&zram->init_lock); + + return len; +} + static ssize_t compact_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -872,7 +913,7 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, return ret; } -static void __zram_make_request(struct zram *zram, struct bio *bio) +static void __zram_make_sync_request(struct zram *zram, struct bio *bio) { int offset; u32 index; @@ -883,12 +924,6 @@ static void __zram_make_request(struct zram *zram, struct bio *bio) offset = (bio->bi_iter.bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; - if (unlikely(bio_op(bio) == REQ_OP_DISCARD)) { - zram_bio_discard(zram, index, offset, bio); - bio_endio(bio); - return; - } - bio_for_each_segment(bvec, bio, iter) { int max_transfer_size = PAGE_SIZE - offset; @@ -921,95 +956,502 @@ static void __zram_make_request(struct zram *zram, struct bio *bio) } bio_endio(bio); + zram_meta_put(zram); return; out: bio_io_error(bio); + zram_meta_put(zram); } -/* - * Handler function for all zram I/O requests. - */ -static blk_qc_t zram_make_request(struct request_queue *queue, struct bio *bio) +static int zram_rw_sync_page(struct block_device *bdev, struct zram *zram, + struct bio_vec *bv, u32 index, + int offset, bool is_write) { - struct zram *zram = queue->queuedata; + int err; - if (unlikely(!zram_meta_get(zram))) - goto error; + err = zram_bvec_rw(zram, bv, index, offset, is_write); + /* + * If I/O fails, just return error(ie, non-zero) without + * calling page_endio. + * It causes resubmit the I/O with bio request by upper functions + * of rw_page(e.g., swap_readpage, __swap_writepage) and + * bio->bi_end_io does things to handle the error + * (e.g., SetPageError, set_page_dirty and extra works). + */ + if (err == 0) + page_endio(bv->bv_page, is_write, 0); - blk_queue_split(queue, &bio, queue->bio_split); + zram_meta_put(zram); + return err; +} - if (!valid_io_request(zram, bio->bi_iter.bi_sector, - bio->bi_iter.bi_size)) { - atomic64_inc(&zram->stats.invalid_io); - goto put_zram; +const int NR_BATCH_PAGES = 64; + +struct zram_worker { + struct task_struct *task; + struct list_head list; +}; + +struct zram_workers { + spinlock_t req_lock; + struct list_head req_list; + unsigned int nr_req; + struct list_head worker_list; + wait_queue_head_t req_wait; + int nr_running; +} workers; + +struct bio_request { + struct bio *bio; + atomic_t nr_pages; +}; + +struct page_request { + struct zram *zram; + struct bio_request *bio_req; + struct bio_vec bvec; + u32 index; + int offset; + bool write; + struct list_head list; +}; + +static void worker_wake_up(void) +{ + if (workers.nr_running * NR_BATCH_PAGES < workers.nr_req) { + int nr_wakeup = (workers.nr_req + NR_BATCH_PAGES) / + NR_BATCH_PAGES - workers.nr_running; + + WARN_ON(!nr_wakeup); + wake_up_nr(&workers.req_wait, nr_wakeup); } +} - __zram_make_request(zram, bio); - zram_meta_put(zram); - return BLK_QC_T_NONE; -put_zram: - zram_meta_put(zram); -error: - bio_io_error(bio); - return BLK_QC_T_NONE; +static void zram_unplug(struct blk_plug_cb *cb, bool from_schedule) +{ + spin_lock_irq(&workers.req_lock); + if (workers.nr_req) + worker_wake_up(); + spin_unlock_irq(&workers.req_lock); + kfree(cb); } -static void zram_slot_free_notify(struct block_device *bdev, - unsigned long index) +static int zram_check_plugged(void) +{ + return !!blk_check_plugged(zram_unplug, NULL, + sizeof(struct blk_plug_cb)); +} + +int queue_page_request(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset, bool write) +{ + struct page_request *page_req = kmalloc(sizeof(*page_req), GFP_NOIO); + + if (!page_req) + return -ENOMEM; + + page_req->bio_req = NULL; + page_req->zram = zram; + page_req->bvec = *bvec; + page_req->index = index; + page_req->offset = offset; + page_req->write = write; + + spin_lock(&workers.req_lock); + list_add(&page_req->list, &workers.req_list); + workers.nr_req += 1; + if (!zram_check_plugged()) + worker_wake_up(); + spin_unlock(&workers.req_lock); + + + return 0; +} + +int queue_page_request_list(struct zram *zram, struct bio_request *bio_req, + struct bio_vec *bvec, u32 index, int offset, + bool write, struct list_head *page_list) +{ + struct page_request *page_req = kmalloc(sizeof(*page_req), GFP_NOIO); + + if (!page_req) { + while (!list_empty(page_list)) { + page_req = list_first_entry(page_list, + struct page_request, list); + list_del(&page_req->list); + kfree(page_req); + } + + return -ENOMEM; + } + + page_req->bio_req = bio_req; + atomic_inc(&bio_req->nr_pages); + page_req->zram = zram; + page_req->bvec = *bvec; + page_req->index = index; + page_req->offset = offset; + page_req->write = write; + + list_add_tail(&page_req->list, page_list); + + return 0; +} + +/* Caller should hold on req_lock */ +static void get_page_requests(struct list_head *page_list) +{ + struct page_request *page_req; + struct bio_request *bio_req; + int nr_batch = NR_BATCH_PAGES; + + while (nr_batch--) { + if (list_empty(&workers.req_list)) + break; + + page_req = list_first_entry(&workers.req_list, + struct page_request, list); + list_move(&page_req->list, page_list); + bio_req = page_req->bio_req; + workers.nr_req--; + } +} + +static int page_request_rw(struct page_request *page_req) +{ + struct zram *zram = page_req->zram; + + return zram_bvec_rw(zram, &page_req->bvec, page_req->index, + page_req->offset, page_req->write); +} + +static void run_worker(struct bio *bio, struct list_head *page_list, + unsigned int nr_pages) { + WARN_ON(list_empty(page_list)); + + spin_lock(&workers.req_lock); + list_splice_tail(page_list, &workers.req_list); + workers.nr_req += nr_pages; + if (bio->bi_opf & REQ_SYNC || !zram_check_plugged()) + worker_wake_up(); + spin_unlock(&workers.req_lock); +} + +static int __zram_make_async_request(struct zram *zram, struct bio *bio) +{ + int offset; + u32 index; + struct bio_vec bvec; + struct bvec_iter iter; + LIST_HEAD(page_list); + struct bio_request *bio_req; + unsigned int nr_pages = 0; + bool write = op_is_write(bio_op(bio)); + + index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_iter.bi_sector & + (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + bio_req = kmalloc(sizeof(*bio_req), GFP_NOIO); + if (!bio_req) + return 1; + + /* + * Keep bi_vcnt to complete bio handling when all of pages + * in the bio are handled. + */ + bio_req->bio = bio; + atomic_set(&bio_req->nr_pages, 0); + + bio_for_each_segment(bvec, bio, iter) { + int max_transfer_size = PAGE_SIZE - offset; + + if (bvec.bv_len > max_transfer_size) { + /* + * zram_bvec_rw() can only make operation on a single + * zram page. Split the bio vector. + */ + struct bio_vec bv; + + bv.bv_page = bvec.bv_page; + bv.bv_len = max_transfer_size; + bv.bv_offset = bvec.bv_offset; + + if (queue_page_request_list(zram, bio_req, &bv, + index, offset, write, &page_list)) + goto out; + nr_pages++; + + bv.bv_len = bvec.bv_len - max_transfer_size; + bv.bv_offset += max_transfer_size; + if (queue_page_request_list(zram, bio_req, &bv, + index + 1, 0, write, &page_list)) + goto out; + nr_pages++; + } else + if (queue_page_request_list(zram, bio_req, &bvec, + index, offset, write, &page_list)) + goto out; + nr_pages++; + + update_position(&index, &offset, &bvec); + } + + run_worker(bio, &page_list, nr_pages); + return 0; + +out: + kfree(bio_req); + + WARN_ON(!list_empty(&page_list)); + return 1; +} + + +void page_requests_rw(struct list_head *page_list) +{ + struct page_request *page_req; + bool write; + struct page *page; struct zram *zram; - struct zram_meta *meta; - zram = bdev->bd_disk->private_data; - meta = zram->meta; + while (!list_empty(page_list)) { + bool free_bio = false; + struct bio_request *bio_req; + int err; + + page_req = list_last_entry(page_list, struct page_request, + list); + write = page_req->write; + page = page_req->bvec.bv_page; + zram = page_req->zram; + bio_req = page_req->bio_req; + if (bio_req && atomic_dec_and_test(&bio_req->nr_pages)) + free_bio = true; + list_del(&page_req->list); + + err = page_request_rw(page_req); + kfree(page_req); + /* page-based request */ + if (!bio_req) { + page_endio(page, write, err); + zram_meta_put(zram); + /* bio-based request */ + } else if (free_bio) { + if (likely(!err)) + bio_endio(bio_req->bio); + else + bio_io_error(bio_req->bio); + kfree(bio_req); + zram_meta_put(zram); + } - bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); - zram_free_page(zram, index); - bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); - atomic64_inc(&zram->stats.notify_free); + } +} + +static int zram_thread(void *data) +{ + DEFINE_WAIT(wait); + LIST_HEAD(page_list); + + spin_lock(&workers.req_lock); + workers.nr_running++; + + while (1) { + if (kthread_should_stop()) { + workers.nr_running--; + spin_unlock(&workers.req_lock); + break; + } + + if (list_empty(&workers.req_list)) { + prepare_to_wait_exclusive(&workers.req_wait, &wait, + TASK_INTERRUPTIBLE); + workers.nr_running--; + spin_unlock(&workers.req_lock); + schedule(); + spin_lock(&workers.req_lock); + workers.nr_running++; + finish_wait(&workers.req_wait, &wait); + continue; + } + + get_page_requests(&page_list); + if (list_empty(&page_list)) + continue; + + spin_unlock(&workers.req_lock); + page_requests_rw(&page_list); + WARN_ON(!list_empty(&page_list)); + cond_resched(); + spin_lock(&workers.req_lock); + } + + return 0; +} + +static void destroy_workers(void) +{ + struct zram_worker *worker; + + while (!list_empty(&workers.worker_list)) { + worker = list_first_entry(&workers.worker_list, + struct zram_worker, + list); + kthread_stop(worker->task); + list_del(&worker->list); + kfree(worker); + } + + WARN_ON(workers.nr_running); +} + +static int create_workers(void) +{ + int i; + int nr_cpu = num_online_cpus(); + struct zram_worker *worker; + + INIT_LIST_HEAD(&workers.worker_list); + INIT_LIST_HEAD(&workers.req_list); + spin_lock_init(&workers.req_lock); + init_waitqueue_head(&workers.req_wait); + + for (i = 0; i < nr_cpu; i++) { + worker = kmalloc(sizeof(*worker), GFP_KERNEL); + if (!worker) + goto error; + + worker->task = kthread_run(zram_thread, NULL, "zramd-%d", i); + if (IS_ERR(worker->task)) { + kfree(worker); + goto error; + } + + list_add(&worker->list, &workers.worker_list); + } + + return 0; + +error: + destroy_workers(); + return 1; +} + +static int zram_rw_async_page(struct zram *zram, + struct bio_vec *bv, u32 index, int offset, + bool is_write) +{ + + return queue_page_request(zram, bv, index, offset, is_write); } static int zram_rw_page(struct block_device *bdev, sector_t sector, struct page *page, bool is_write) { - int offset, err = -EIO; - u32 index; + int err = -EIO; struct zram *zram; + int offset; + u32 index; struct bio_vec bv; zram = bdev->bd_disk->private_data; if (unlikely(!zram_meta_get(zram))) - goto out; + return err; if (!valid_io_request(zram, sector, PAGE_SIZE)) { atomic64_inc(&zram->stats.invalid_io); - err = -EINVAL; - goto put_zram; + zram_meta_put(zram); + return -EINVAL; } + index = sector >> SECTORS_PER_PAGE_SHIFT; - offset = sector & (SECTORS_PER_PAGE - 1) << SECTOR_SHIFT; + offset = (sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; bv.bv_page = page; bv.bv_len = PAGE_SIZE; bv.bv_offset = 0; - err = zram_bvec_rw(zram, &bv, index, offset, is_write); -put_zram: - zram_meta_put(zram); -out: + if (!zram->use_aio || !is_write) { + err = zram_rw_sync_page(bdev, zram, &bv, index, offset, + is_write); + } else { + err = zram_rw_async_page(zram, &bv, index, offset, is_write); + if (err) + err = zram_rw_sync_page(bdev, zram, &bv, index, + offset, is_write); + } + + return err; +} + + +static blk_qc_t zram_make_request(struct request_queue *queue, struct bio *bio) +{ + struct zram *zram = queue->queuedata; + /* - * If I/O fails, just return error(ie, non-zero) without - * calling page_endio. - * It causes resubmit the I/O with bio request by upper functions - * of rw_page(e.g., swap_readpage, __swap_writepage) and - * bio->bi_end_io does things to handle the error - * (e.g., SetPageError, set_page_dirty and extra works). + * request handler should take care of reference count and + * bio_endio. */ - if (err == 0) - page_endio(page, is_write, 0); - return err; + if (unlikely(!zram_meta_get(zram))) + goto error; + + blk_queue_split(queue, &bio, queue->bio_split); + + if (!valid_io_request(zram, bio->bi_iter.bi_sector, + bio->bi_iter.bi_size)) { + atomic64_inc(&zram->stats.invalid_io); + goto fail; + } + + if (unlikely(bio_op(bio) == REQ_OP_DISCARD)) { + int offset; + u32 index; + + index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_iter.bi_sector & + (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + zram_bio_discard(zram, index, offset, bio); + bio_endio(bio); + zram_meta_put(zram); + goto out; + } + + if (!zram->use_aio || !op_is_write(bio_op(bio))) { + __zram_make_sync_request(zram, bio); + } else { + if (__zram_make_async_request(zram, bio)) + __zram_make_sync_request(zram, bio); + } + + return BLK_QC_T_NONE; + +fail: + zram_meta_put(zram); +error: + bio_io_error(bio); +out: + return BLK_QC_T_NONE; +} + +static void zram_slot_free_notify(struct block_device *bdev, + unsigned long index) +{ + struct zram *zram; + struct zram_meta *meta; + + zram = bdev->bd_disk->private_data; + meta = zram->meta; + + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); + zram_free_page(zram, index); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); + atomic64_inc(&zram->stats.notify_free); } static void zram_reset_device(struct zram *zram) @@ -1190,6 +1632,7 @@ static DEVICE_ATTR_RW(mem_limit); static DEVICE_ATTR_RW(mem_used_max); static DEVICE_ATTR_RW(max_comp_streams); static DEVICE_ATTR_RW(comp_algorithm); +static DEVICE_ATTR_RW(use_aio); static struct attribute *zram_disk_attrs[] = { &dev_attr_disksize.attr, @@ -1210,6 +1653,7 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_mem_used_max.attr, &dev_attr_max_comp_streams.attr, &dev_attr_comp_algorithm.attr, + &dev_attr_use_aio.attr, &dev_attr_io_stat.attr, &dev_attr_mm_stat.attr, &dev_attr_debug_stat.attr, @@ -1464,6 +1908,9 @@ static int __init zram_init(void) num_devices--; } + if (create_workers()) + goto out_error; + return 0; out_error: @@ -1474,6 +1921,7 @@ static int __init zram_init(void) static void __exit zram_exit(void) { destroy_devices(); + destroy_workers(); } module_init(zram_init); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 74fcf10da374..4819a33ab1cf 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -119,5 +119,6 @@ struct zram { * zram is claimed so open request will be failed */ bool claim; /* Protected by bdev->bd_mutex */ + bool use_aio; /* asynchronous IO mode */ }; #endif -- 2.7.4