diff options
author | David Teigland <teigland@redhat.com> | 2017-08-07 15:59:55 -0500 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2017-10-18 10:03:57 -0500 |
commit | b0485554b1f6ad6e7cdb0484d161bce2084a8a9a (patch) | |
tree | bbdb6b14eec6bea9d2b81a19cbdeb2a4ec8c7323 | |
parent | b63378b11a8d4af490b2100a504da579878ef8ee (diff) | |
download | lvm2-b0485554b1f6ad6e7cdb0484d161bce2084a8a9a.tar.gz |
dev-io: add layer around async io
Use the actual aio system calls only in dev-io.c
and use an obfuscation layer above that in lvm.
-rw-r--r-- | lib/device/dev-io.c | 134 | ||||
-rw-r--r-- | lib/device/device.h | 40 | ||||
-rw-r--r-- | lib/label/label.c | 165 | ||||
-rw-r--r-- | lib/label/label.h | 10 |
4 files changed, 224 insertions, 125 deletions
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c index 519b5c7e1..25f241904 100644 --- a/lib/device/dev-io.c +++ b/lib/device/dev-io.c @@ -827,3 +827,137 @@ int dev_set(struct device *dev, uint64_t offset, size_t len, int value) return (len == 0); } + +/* io_setup() wrapper */ + +struct dev_async_context *dev_async_context_setup(unsigned async_event_count) +{ + struct dev_async_context *ac; + int max_submit_events = MAX_ASYNC_EVENTS; + int error; + + if (async_event_count) + max_submit_events = async_event_count; + + if (!(ac = malloc(sizeof(struct dev_async_context)))) + return_0; + + memset(ac, 0, sizeof(struct dev_async_context)); + + error = io_setup(max_submit_events, &ac->aio_ctx); + + if (error < 0) { + free(ac); + return_0; + } + return ac; +} + +struct dev_async_io *dev_async_io_alloc(int buf_len) +{ + struct dev_async_io *aio; + char *buf; + char **p_buf; + + /* + * mem pool doesn't seem to work for this, probably because + * of the memalign that follows. + */ + if (!(aio = malloc(sizeof(struct dev_async_io)))) + return_0; + + memset(aio, 0, sizeof(struct dev_async_io)); + + buf = NULL; + p_buf = &buf; + + if (posix_memalign((void *)p_buf, getpagesize(), buf_len)) { + free(aio); + return_NULL; + } + + memset(buf, 0, buf_len); + + aio->buf = buf; + aio->buf_len = buf_len; + return aio; +} + +void dev_async_context_destroy(struct dev_async_context *ac) +{ + io_destroy(ac->aio_ctx); + free(ac); +} + +void dev_async_io_destroy(struct dev_async_io *aio) +{ + if (aio->buf) + free(aio->buf); + free(aio); +} + +/* io_submit() wrapper */ + +int dev_async_read_submit(struct dev_async_context *ac, struct dev_async_io *aio, + struct device *dev, uint32_t len, uint64_t offset, int *nospace) +{ + struct iocb *iocb = &aio->iocb; + int error; + + *nospace = 0; + + if (len > aio->buf_len) + return_0; + + aio->len = len; + + iocb->data = aio; + iocb->aio_fildes = dev_fd(dev); + iocb->aio_lio_opcode = IO_CMD_PREAD; + iocb->u.c.buf = aio->buf; + iocb->u.c.nbytes = len; + iocb->u.c.offset = offset; + + error = io_submit(ac->aio_ctx, 1, &iocb); + if (error == -EAGAIN) + *nospace = 1; + if (error < 0) + return 0; + return 1; +} + +/* io_getevents() wrapper */ + +int dev_async_getevents(struct dev_async_context *ac, int wait_count, struct timespec *timeout) +{ + int wait_nr; + int rv; + int i; + + retry: + memset(&ac->events, 0, sizeof(ac->events)); + + if (wait_count >= MAX_GET_EVENTS) + wait_nr = MAX_GET_EVENTS; + else + wait_nr = wait_count; + + rv = io_getevents(ac->aio_ctx, 1, wait_nr, (struct io_event *)&ac->events, timeout); + + if (rv == -EINTR) + goto retry; + if (rv < 0) + return 0; + if (!rv) + return 1; + + for (i = 0; i < rv; i++) { + struct iocb *iocb = ac->events[i].obj; + struct dev_async_io *aio = iocb->data; + aio->result = ac->events[i].res; + aio->done = 1; + } + + return 1; +} + diff --git a/lib/device/device.h b/lib/device/device.h index fa03f1061..87d19bd66 100644 --- a/lib/device/device.h +++ b/lib/device/device.h @@ -19,6 +19,7 @@ #include "uuid.h" #include <fcntl.h> +#include <libaio.h> #define DEV_ACCESSED_W 0x00000001 /* Device written to? */ #define DEV_REGULAR 0x00000002 /* Regular file? */ @@ -91,6 +92,37 @@ struct device_area { }; /* + * We'll collect the results of this many async reads + * in one system call. It shouldn't matter much what + * number is used here. + */ +#define MAX_GET_EVENTS 16 + +/* + * The number of events to use in io_setup(), + * which is the limit on the number of concurrent + * async i/o's we can submit. After all these are + * used, io_submit() returns -EAGAIN, and we revert + * to doing synchronous io. + */ +#define MAX_ASYNC_EVENTS 1024 + +struct dev_async_context { + io_context_t aio_ctx; + struct io_event events[MAX_GET_EVENTS]; +}; + +struct dev_async_io { + char *buf; + struct iocb iocb; + struct device *dev; + uint32_t buf_len; /* size of buf */ + uint32_t len; /* size of submitted io */ + int done; + int result; +}; + +/* * Support for external device info. */ const char *dev_ext_name(struct device *dev); @@ -144,4 +176,12 @@ void dev_destroy_file(struct device *dev); /* Return a valid device name from the alias list; NULL otherwise */ const char *dev_name_confirmed(struct device *dev, int quiet); +struct dev_async_context *dev_async_context_setup(unsigned async_event_count); +struct dev_async_io *dev_async_io_alloc(int buf_len); +void dev_async_context_destroy(struct dev_async_context *ac); +void dev_async_io_destroy(struct dev_async_io *aio); +int dev_async_read_submit(struct dev_async_context *ac, struct dev_async_io *aio, + struct device *dev, uint32_t len, uint64_t offset, int *nospace); +int dev_async_getevents(struct dev_async_context *ac, int wait_count, struct timespec *timeout); + #endif diff --git a/lib/label/label.c b/lib/label/label.c index 77b498f3f..6662be00c 100644 --- a/lib/label/label.c +++ b/lib/label/label.c @@ -457,8 +457,8 @@ static void _free_label_read_list(int do_close) dm_list_del(&ld->list); if (do_close) dev_close(ld->dev); - if (ld->buf) - free(ld->buf); + if (ld->aio) + dev_async_io_destroy(ld->aio); free(ld); } } @@ -477,31 +477,15 @@ struct label_read_data *get_label_read_data(struct cmd_context *cmd, struct devi /* * Start label aio read on a device. */ -static int _label_read_async_start(struct cmd_context *cmd, io_context_t aio_ctx, struct label_read_data *ld) +static int _label_read_async_start(struct dev_async_context *ac, struct label_read_data *ld) { - struct iocb *iocb = &ld->iocb; - int ret; + int nospace = 0; - iocb->data = ld; - iocb->aio_fildes = dev_fd(ld->dev); - iocb->aio_lio_opcode = IO_CMD_PREAD; - iocb->u.c.buf = ld->buf; - iocb->u.c.nbytes = ld->buf_len; - iocb->u.c.offset = 0; - - ret = io_submit(aio_ctx, 1, &iocb); - - /* - * This means that the number of devices exceeded the number of events - * set up in io_setup(). - */ - if (ret == -EAGAIN) { - log_debug_devs("Reading label no aio event for %s", dev_name(ld->dev)); - return 0; - } - - if (ret < 0) { - log_debug_devs("Reading label aio submit error %d for %s", ret, dev_name(ld->dev)); + if (!dev_async_read_submit(ac, ld->aio, ld->dev, ld->buf_len, 0, &nospace)) { + if (nospace) + log_debug_devs("Reading label no aio event for %s", dev_name(ld->dev)); + else + log_debug_devs("Reading label aio submit error for %s", dev_name(ld->dev)); return 0; } @@ -509,46 +493,11 @@ static int _label_read_async_start(struct cmd_context *cmd, io_context_t aio_ctx } /* - * We'll collect the results of this many async reads - * in one system call. It shouldn't matter much what - * number is used here. - */ -#define MAX_GET_EVENTS 16 - -/* * Reap aio reads from devices. */ -static int _label_read_async_wait(struct cmd_context *cmd, io_context_t aio_ctx, int wait_count) +static int _label_read_async_wait(struct dev_async_context *ac, int wait_count) { - struct io_event events[MAX_GET_EVENTS]; - int wait_nr; - int ret; - int i; - - retry: - memset(&events, 0, sizeof(events)); - - if (wait_count >= MAX_GET_EVENTS) - wait_nr = MAX_GET_EVENTS; - else - wait_nr = wait_count; - - ret = io_getevents(aio_ctx, 1, wait_nr, (struct io_event *)&events, NULL); - if (ret == -EINTR) - goto retry; - if (ret < 0) - return 0; - if (!ret) - return 1; - - for (i = 0; i < ret; i++) { - struct iocb *iocb = events[i].obj; - struct label_read_data *ld = iocb->data; - ld->read_result = events[i].res; - ld->read_done = 1; - } - - return 1; + return dev_async_getevents(ac, wait_count, NULL); } /* @@ -565,9 +514,9 @@ static int _label_read_async_process(struct cmd_context *cmd, struct label_read_ uint64_t sector; int r = 0; - if ((ld->read_result < 0) || (ld->read_result != ld->buf_len)) { + if ((ld->aio->result < 0) || (ld->aio->result != ld->aio->len)) { /* FIXME: handle errors */ - log_error("Reading label sectors aio error %d from %s", ld->read_result, dev_name(ld->dev)); + log_error("Reading label sectors aio error %d from %s", ld->aio->result, dev_name(ld->dev)); goto out; } @@ -600,15 +549,6 @@ static int _label_read_async_process(struct cmd_context *cmd, struct label_read_ } /* - * The number of events to use in io_setup(), - * which is the limit on the number of concurrent - * async i/o's we can submit. After all these are - * used, io_submit() returns -EAGAIN, and we revert - * to doing synchronous io. - */ -#define MAX_ASYNC_EVENTS 1024 - -/* * label_scan run at the start of a command iterates over all visible devices, * looking for any that belong to lvm, and fills lvmcache with basic info about * them. It's main job is to prepare for subsequent vg_reads. vg_read(vgname) @@ -628,15 +568,12 @@ int label_scan_async(struct cmd_context *cmd) struct label_read_data *ld, *ld2; struct dev_iter *iter; struct device *dev; - io_context_t aio_ctx; + struct dev_async_context *ac; struct lvmcache_info *info; - char *buf; - char **p_buf; int buf_len; int need_wait_count; int need_process_count; int dev_count = 0; - int error; _free_label_read_list(0); @@ -661,17 +598,19 @@ int label_scan_async(struct cmd_context *cmd) */ buf_len = ASYNC_SCAN_SIZE; - memset(&aio_ctx, 0, sizeof(io_context_t)); - /* * if aio setup fails, caller will revert to sync scan * The number of events set up here is the max number of * concurrent async reads that can be submitted. After * all of those are used, we revert to synchronous reads. + * + * FIXME: add a config setting to control the number passed + * into setup used for max async events in io_setup(). + * (0 uses default) */ - error = io_setup(MAX_ASYNC_EVENTS, &aio_ctx); - if (error < 0) { - log_debug_devs("async io setup error %d, reverting to sync io.", error); + + if (!(ac = dev_async_context_setup(0))) { + log_debug_devs("async io setup error, reverting to sync io."); return_0; } @@ -710,24 +649,18 @@ int label_scan_async(struct cmd_context *cmd) /* * FIXME: mem pool code doesn't work for this, probably because * of the posix_memalign below. Try using mem pool to allocate - * all the ld structs first, then allocate all the aligned aio - * buffers. + * all the ld structs first, then allocate all aio and aio->buf. */ if (!(ld = malloc(sizeof(*ld)))) goto_bad; memset(ld, 0, sizeof(*ld)); - buf = NULL; - p_buf = &buf; - - if (posix_memalign((void *)p_buf, getpagesize(), buf_len)) + if (!(ld->aio = dev_async_io_alloc(buf_len))) goto_bad; - memset(buf, 0, buf_len); - ld->dev = dev; - ld->buf = buf; + ld->buf = ld->aio->buf; ld->buf_len = buf_len; dm_list_add(&label_read_list, &ld->list); @@ -740,7 +673,7 @@ int label_scan_async(struct cmd_context *cmd) * fail and the next loop will try a sync read for it. */ dm_list_iterate_items(ld, &label_read_list) { - if (!_label_read_async_start(cmd, aio_ctx, ld)) + if (!_label_read_async_start(ac, ld)) ld->try_sync = 1; else log_debug_devs("Reading sectors from device %s async", dev_name(ld->dev)); @@ -748,6 +681,7 @@ int label_scan_async(struct cmd_context *cmd) /* * Try a synchronous read for any dev where aio couldn't be submitted. + * Reuse the aio buffer, result and done fields. */ dm_list_iterate_items(ld, &label_read_list) { if (ld->try_sync) { @@ -755,11 +689,11 @@ int label_scan_async(struct cmd_context *cmd) if (!dev_read(ld->dev, 0, ld->buf_len, ld->buf)) { log_debug_devs("%s: Failed to read label area", dev_name(ld->dev)); - ld->read_result = -1; + ld->aio->result = -1; } else { - ld->read_result = ld->buf_len; + ld->aio->result = ld->buf_len; } - ld->read_done = 1; + ld->aio->done = 1; } } @@ -772,7 +706,7 @@ int label_scan_async(struct cmd_context *cmd) need_process_count = 0; dm_list_iterate_items(ld, &label_read_list) { - if (!ld->read_done) + if (!ld->aio->done) need_wait_count++; else if (!ld->process_done) need_process_count++; @@ -784,7 +718,7 @@ int label_scan_async(struct cmd_context *cmd) */ if (need_process_count) { dm_list_iterate_items(ld, &label_read_list) { - if (!ld->read_done || ld->process_done) + if (!ld->aio->done || ld->process_done) continue; log_debug_devs("Parsing label and data from device %s", dev_name(ld->dev)); _label_read_async_process(cmd, ld); @@ -796,7 +730,7 @@ int label_scan_async(struct cmd_context *cmd) * Wait for more devices to finish reading label sectors. */ if (need_wait_count) { - if (_label_read_async_wait(cmd, aio_ctx, need_wait_count)) + if (_label_read_async_wait(ac, need_wait_count)) goto check_aio; /* TODO: handle this error */ @@ -805,7 +739,7 @@ int label_scan_async(struct cmd_context *cmd) log_error(INTERNAL_ERROR "aio getevents error"); } - io_destroy(aio_ctx); + dev_async_context_destroy(ac); dm_list_iterate_items(ld, &label_read_list) dev_close(ld->dev); @@ -818,13 +752,12 @@ bad: log_error("async label scan failed, reverting to sync scan."); dev_iter_destroy(iter); - io_destroy(aio_ctx); + dev_async_context_destroy(ac); dm_list_iterate_items_safe(ld, ld2, &label_read_list) { dm_list_del(&ld->list); dev_close(ld->dev); - if (ld->buf) - free(ld->buf); + dev_async_io_destroy(ld->aio); free(ld); } return 0; @@ -841,19 +774,15 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) struct label_read_data *ld, *ld2; struct device_list *devl; struct device *dev; - io_context_t aio_ctx; + struct dev_async_context *ac; int need_wait_count; int need_process_count; int dev_count = 0; - int error; dm_list_init(&tmp_label_read_list); - memset(&aio_ctx, 0, sizeof(io_context_t)); - - error = io_setup(MAX_ASYNC_EVENTS, &aio_ctx); - if (error < 0) { - log_debug_devs("async io setup error %d, reverting to sync io.", error); + if (!(ac = dev_async_context_setup(0))) { + log_debug_devs("async io setup error, reverting to sync io."); return_0; } @@ -877,10 +806,10 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) } ld->try_sync = 0; - ld->read_done = 0; + ld->aio->done = 0; ld->process_done = 0; - if (!_label_read_async_start(cmd, aio_ctx, ld)) + if (!_label_read_async_start(ac, ld)) ld->try_sync = 1; else log_debug_devs("Reading sectors from device %s async", dev_name(ld->dev)); @@ -897,11 +826,11 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) if (!dev_read(ld->dev, 0, ld->buf_len, ld->buf)) { log_debug_devs("%s: Failed to read label area", dev_name(ld->dev)); - ld->read_result = -1; + ld->aio->result = -1; } else { - ld->read_result = ld->buf_len; + ld->aio->result = ld->buf_len; } - ld->read_done = 1; + ld->aio->done = 1; } } @@ -914,7 +843,7 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) need_process_count = 0; dm_list_iterate_items(ld, &tmp_label_read_list) { - if (!ld->read_done) + if (!ld->aio->done) need_wait_count++; else if (!ld->process_done) need_process_count++; @@ -932,7 +861,7 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) */ if (need_process_count) { dm_list_iterate_items(ld, &tmp_label_read_list) { - if (!ld->read_done || ld->process_done) + if (!ld->aio->done || ld->process_done) continue; log_debug_devs("Parsing label and data from device %s", dev_name(ld->dev)); _label_read_async_process(cmd, ld); @@ -944,7 +873,7 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) * Wait for more devices to finish reading label sectors. */ if (need_wait_count) { - if (_label_read_async_wait(cmd, aio_ctx, need_wait_count)) + if (_label_read_async_wait(ac, need_wait_count)) goto check_aio; /* TODO: handle this error */ @@ -953,7 +882,7 @@ int label_rescan_async(struct cmd_context *cmd, struct dm_list *devs) log_error(INTERNAL_ERROR "aio getevents error"); } - io_destroy(aio_ctx); + dev_async_context_destroy(ac); dm_list_iterate_items(ld, &tmp_label_read_list) dev_close(ld->dev); diff --git a/lib/label/label.h b/lib/label/label.h index 26424e5bd..e3e4520f3 100644 --- a/lib/label/label.h +++ b/lib/label/label.h @@ -20,8 +20,6 @@ #include "device.h" #include "toolcontext.h" -#include <libaio.h> - #define LABEL_ID "LABELONE" #define LABEL_SIZE SECTOR_SIZE /* Think very carefully before changing this */ #define LABEL_SCAN_SECTORS 4L @@ -40,14 +38,12 @@ void allow_reads_with_lvmetad(void); #define ASYNC_SCAN_SIZE (128 * 1024) struct label_read_data { - char *buf; /* ASYNC_SCAN_SIZE aligned memory buffer */ - struct iocb iocb; + struct dev_async_io *aio; + char *buf; /* points to aio->buf */ struct device *dev; struct dm_list list; - int buf_len; /* ASYNC_SCAN_SIZE */ + int buf_len; /* same as aio->buf_len */ int try_sync; - int read_done; - int read_result; int process_done; }; |