diff options
author | David Teigland <teigland@redhat.com> | 2017-10-24 14:53:01 -0500 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2017-10-25 13:40:20 -0500 |
commit | b3253b98ccf5b3d67a69f87a1bae515eb3d6450e (patch) | |
tree | d7689dec503cb44a60d165fa1887f3d99eed87a6 | |
parent | 10c76ce35a294b3d5fc84bc8af3ff118168282d7 (diff) | |
download | lvm2-b3253b98ccf5b3d67a69f87a1bae515eb3d6450e.tar.gz |
io: add low level async io support
The interface consists of:
- A context struct, one for the entire command.
- An io struct, one per io operation (read).
- dev_async_context_setup() creates an aio context.
- dev_async_context_destroy() destroys an aio context.
- dev_async_alloc_ios() allocates a specified number of io structs,
along with an associated buffer for the data.
- dev_async_free_ios() frees all the allocated io structs+buffers.
- dev_async_io_get() gets an available io struct from those
allocated in alloc_ios. If none are available, it will allocate
a new io struct if under limit.
- dev_async_io_put() puts a used io struct back into the set
of unused io structs, making it available for get.
- dev_async_read_submit() start an async read io.
- dev_async_getevents() collect async io completions.
-rwxr-xr-x | configure | 69 | ||||
-rw-r--r-- | configure.in | 22 | ||||
-rw-r--r-- | daemons/clvmd/Makefile.in | 4 | ||||
-rw-r--r-- | lib/config/defaults.h | 2 | ||||
-rw-r--r-- | lib/device/dev-io.c | 305 | ||||
-rw-r--r-- | lib/device/device.h | 49 | ||||
-rw-r--r-- | liblvm/Makefile.in | 4 | ||||
-rw-r--r-- | make.tmpl.in | 1 | ||||
-rw-r--r-- | scripts/Makefile.in | 4 | ||||
-rw-r--r-- | tools/Makefile.in | 4 |
10 files changed, 464 insertions, 0 deletions
@@ -704,7 +704,9 @@ FSADM ELDFLAGS DM_LIB_PATCHLEVEL DMEVENTD_PATH +AIO_LIBS DL_LIBS +AIO DEVMAPPER DEFAULT_USE_LVMLOCKD DEFAULT_USE_LVMPOLLD @@ -950,6 +952,7 @@ enable_profiling enable_testing enable_valgrind_pool enable_devmapper +enable_aio enable_lvmetad enable_lvmpolld enable_lvmlockd_sanlock @@ -1688,6 +1691,7 @@ Optional Features: --enable-testing enable testing targets in the makefile --enable-valgrind-pool enable valgrind awareness of pools --disable-devmapper disable LVM2 device-mapper interaction + --disable-aio disable async i/o --enable-lvmetad enable the LVM Metadata Daemon --enable-lvmpolld enable the LVM Polling Daemon --enable-lvmlockd-sanlock @@ -3175,6 +3179,7 @@ case "$host_os" in LDDEPS="$LDDEPS .export.sym" LIB_SUFFIX=so DEVMAPPER=yes + AIO=yes BUILD_LVMETAD=no BUILD_LVMPOLLD=no LOCKDSANLOCK=no @@ -3194,6 +3199,7 @@ case "$host_os" in CLDNOWHOLEARCHIVE= LIB_SUFFIX=dylib DEVMAPPER=yes + AIO=no ODIRECT=no DM_IOCTLS=no SELINUX=no @@ -11817,6 +11823,67 @@ $as_echo "#define DEVMAPPER_SUPPORT 1" >>confdefs.h fi ################################################################################ +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use aio" >&5 +$as_echo_n "checking whether to use aio... " >&6; } +# Check whether --enable-aio was given. +if test "${enable_aio+set}" = set; then : + enableval=$enable_aio; AIO=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AIO" >&5 +$as_echo "$AIO" >&6; } + +if test "$AIO" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for io_setup in -laio" >&5 +$as_echo_n "checking for io_setup in -laio... " >&6; } +if ${ac_cv_lib_aio_io_setup+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-laio $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char io_setup (); +int +main () +{ +return io_setup (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_aio_io_setup=yes +else + ac_cv_lib_aio_io_setup=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_aio_io_setup" >&5 +$as_echo "$ac_cv_lib_aio_io_setup" >&6; } +if test "x$ac_cv_lib_aio_io_setup" = xyes; then : + +$as_echo "#define AIO_SUPPORT 1" >>confdefs.h + + AIO_LIBS="-laio" + AIO_SUPPORT=yes +else + AIO_LIBS= + AIO_SUPPORT=no +fi + +fi + +################################################################################ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build LVMetaD" >&5 $as_echo_n "checking whether to build LVMetaD... " >&6; } # Check whether --enable-lvmetad was given. @@ -15766,6 +15833,8 @@ _ACEOF + + ################################################################################ ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" diff --git a/configure.in b/configure.in index 1366dc4aa..eb182fa74 100644 --- a/configure.in +++ b/configure.in @@ -39,6 +39,7 @@ case "$host_os" in LDDEPS="$LDDEPS .export.sym" LIB_SUFFIX=so DEVMAPPER=yes + AIO=yes BUILD_LVMETAD=no BUILD_LVMPOLLD=no LOCKDSANLOCK=no @@ -58,6 +59,7 @@ case "$host_os" in CLDNOWHOLEARCHIVE= LIB_SUFFIX=dylib DEVMAPPER=yes + AIO=no ODIRECT=no DM_IOCTLS=no SELINUX=no @@ -1116,6 +1118,24 @@ if test "$DEVMAPPER" = yes; then fi ################################################################################ +dnl -- Disable aio +AC_MSG_CHECKING(whether to use aio) +AC_ARG_ENABLE(aio, + AC_HELP_STRING([--disable-aio], + [disable async i/o]), + AIO=$enableval) +AC_MSG_RESULT($AIO) + +if test "$AIO" = yes; then + AC_CHECK_LIB(aio, io_setup, + [AC_DEFINE([AIO_SUPPORT], 1, [Define to 1 if aio is available.]) + AIO_LIBS="-laio" + AIO_SUPPORT=yes], + [AIO_LIBS= + AIO_SUPPORT=no ]) +fi + +################################################################################ dnl -- Build lvmetad AC_MSG_CHECKING(whether to build LVMetaD) AC_ARG_ENABLE(lvmetad, @@ -2056,9 +2076,11 @@ AC_SUBST(DEFAULT_USE_LVMETAD) AC_SUBST(DEFAULT_USE_LVMPOLLD) AC_SUBST(DEFAULT_USE_LVMLOCKD) AC_SUBST(DEVMAPPER) +AC_SUBST(AIO) AC_SUBST(DLM_CFLAGS) AC_SUBST(DLM_LIBS) AC_SUBST(DL_LIBS) +AC_SUBST(AIO_LIBS) AC_SUBST(DMEVENTD_PATH) AC_SUBST(DM_LIB_PATCHLEVEL) AC_SUBST(ELDFLAGS) diff --git a/daemons/clvmd/Makefile.in b/daemons/clvmd/Makefile.in index 7b1b49bf9..397592115 100644 --- a/daemons/clvmd/Makefile.in +++ b/daemons/clvmd/Makefile.in @@ -77,6 +77,10 @@ include $(top_builddir)/make.tmpl LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS) CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS) +ifeq ("@AIO@", "yes") + LIBS += $(AIO_LIBS) +endif + INSTALL_TARGETS = \ install_clvmd diff --git a/lib/config/defaults.h b/lib/config/defaults.h index d9e19d971..8da8670d1 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -267,4 +267,6 @@ #define DEFAULT_THIN_POOL_AUTOEXTEND_THRESHOLD 100 #define DEFAULT_THIN_POOL_AUTOEXTEND_PERCENT 20 +#define DEFAULT_ASYNC_EVENTS 100 + #endif /* _LVM_DEFAULTS_H */ diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c index 519b5c7e1..ca1737bdb 100644 --- a/lib/device/dev-io.c +++ b/lib/device/dev-io.c @@ -827,3 +827,308 @@ int dev_set(struct device *dev, uint64_t offset, size_t len, int value) return (len == 0); } + +#ifdef AIO_SUPPORT + +/* + * io_setup() wrapper: + * async_event_count is the max number of concurrent async + * i/os, i.e. the number of devices that can be read at once + * + * max_io_alloc_count: max number of aio structs to allocate, + * each with a buf_len size buffer. + * + * max_buf_alloc_bytes: max number of bytes to use for buffers + * attached to all aio structs; each aio struct gets a + * buf_len size buffer. + * + * When only max_io_alloc_count is set, it is used directly. + * + * When only max_buf_alloc_bytes is set, the number of aio + * structs is determined by this number divided by buf_len. + * + * When both are set, max_io_alloc_count is reduced, if needed, + * to whatever value max_buf_alloc_bytes would allow. + * + * When both are zero, there is no limit on the number of aio + * structs. If allocation fails for an aio struct or its buffer, + * the code should revert to synchronous io. + */ + +struct dev_async_context *dev_async_context_setup(unsigned async_event_count, + unsigned max_io_alloc_count, + unsigned max_buf_alloc_bytes, + int buf_len) +{ + struct dev_async_context *ac; + unsigned nr_events = DEFAULT_ASYNC_EVENTS; + int count; + int error; + + if (async_event_count) + nr_events = async_event_count; + + if (!(ac = malloc(sizeof(struct dev_async_context)))) + return_0; + + memset(ac, 0, sizeof(struct dev_async_context)); + + dm_list_init(&ac->unused_ios); + + error = io_setup(nr_events, &ac->aio_ctx); + + if (error < 0) { + log_warn("WARNING: async io setup error %d with %u events.", error, nr_events); + free(ac); + return_0; + } + + + if (!max_io_alloc_count && !max_buf_alloc_bytes) + count = 0; + else if (!max_io_alloc_count && max_buf_alloc_bytes) + count = max_buf_alloc_bytes / buf_len; + else if (max_io_alloc_count && max_buf_alloc_bytes) { + if (max_io_alloc_count * buf_len > max_buf_alloc_bytes) + count = max_buf_alloc_bytes / buf_len; + } else + count = max_io_alloc_count; + + ac->max_ios = count; + return ac; +} + +void dev_async_context_destroy(struct dev_async_context *ac) +{ + io_destroy(ac->aio_ctx); + free(ac); +} + +static struct dev_async_io *_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; +} + +static void _async_io_free(struct dev_async_io *aio) +{ + if (aio->buf) + free(aio->buf); + free(aio); +} + +int dev_async_alloc_ios(struct dev_async_context *ac, int num, int buf_len, int *available) +{ + struct dev_async_io *aio; + int count; + int i; + + /* + * When no limit is used and no pre-alloc number is set, + * then no ios are allocated up front, but the are + * allocated as needed in get(). + */ + if (!ac->max_ios && !num) { + *available = 1; + return 1; + } + + if (num && !ac->max_ios) + count = num; + else if (!num && ac->max_ios) + count = ac->max_ios; + else if (num > ac->max_ios) + count = ac->max_ios; + else if (num < ac->max_ios) + count = num; + else + count = ac->max_ios; + + for (i = 0; i < count; i++) { + if (!(aio = _async_io_alloc(buf_len))) { + ac->num_ios = i; + *available = i; + return 1; + } + dm_list_add(&ac->unused_ios, &aio->list); + } + + ac->num_ios = count; + *available = count; + return 1; +} + +void dev_async_free_ios(struct dev_async_context *ac) +{ + struct dev_async_io *aio, *aio2; + + dm_list_iterate_items_safe(aio, aio2, &ac->unused_ios) { + dm_list_del(&aio->list); + _async_io_free(aio); + } +} + +struct dev_async_io *dev_async_io_get(struct dev_async_context *ac, int buf_len) +{ + struct dev_async_io *aio; + + if (!(aio = dm_list_item(dm_list_first(&ac->unused_ios), struct dev_async_io))) { + /* alloc on demand if there is no max or we have used less than max */ + if (!ac->max_ios || (ac->num_ios < ac->max_ios)) { + if ((aio = _async_io_alloc(buf_len))) { + ac->num_ios++; + return aio; + } + } + + return NULL; + } + dm_list_del(&aio->list); + return aio; +} + +void dev_async_io_put(struct dev_async_context *ac, struct dev_async_io *aio) +{ + /* + * Some paths don't have cmd->ac available, so it's simpler for now + * to just free the aio struct in those cases. + */ + if (!ac) + _async_io_free(aio); + else + dm_list_add(&ac->unused_ios, &aio->list); +} + +/* 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; +} + +#else /* AIO_SUPPORT */ + +struct dev_async_context *dev_async_context_setup(unsigned async_event_count, + unsigned max_io_alloc_count, + unsigned max_buf_alloc_bytes, + int buf_len) +{ + return NULL; +} + +void dev_async_context_destroy(struct dev_async_context *ac) +{ +} + +int dev_async_alloc_ios(struct dev_async_context *ac, int num, int buf_len, int *available) +{ + return 0; +} + +void dev_async_free_ios(struct dev_async_context *ac) +{ +} + +struct dev_async_io *dev_async_io_get(struct dev_async_context *ac) +{ + return NULL; +} + +void dev_async_io_put(struct dev_async_context *ac, 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) +{ + return 0; +} + +int dev_async_getevents(struct dev_async_context *ac, int wait_count, struct timespec *timeout) +{ + return 0; +} + +#endif /* AIO_SUPPORT */ diff --git a/lib/device/device.h b/lib/device/device.h index fa03f1061..328e48421 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,32 @@ 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 + +struct dev_async_context { + io_context_t aio_ctx; + struct io_event events[MAX_GET_EVENTS]; /* for processing completions */ + struct dm_list unused_ios; /* unused/available aio structcs */ + int num_ios; /* number of allocated aio structs */ + int max_ios; /* max number of aio structs to allocate */ +}; + +struct dev_async_io { + struct dm_list list; + struct iocb iocb; + struct device *dev; + char *buf; + 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 +171,26 @@ 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, + unsigned max_io_alloc_count, + unsigned max_buf_alloc_bytes, + int buf_len); +void dev_async_context_destroy(struct dev_async_context *ac); + +/* allocate aio structs (with buffers), up to the max specified during context setup */ +int dev_async_alloc_ios(struct dev_async_context *ac, int num, int buf_len, int *available); + +/* free aio structs (and buffers) */ +void dev_async_free_ios(struct dev_async_context *ac); + +/* get an available aio struct (with buffer) */ +struct dev_async_io *dev_async_io_get(struct dev_async_context *ac, int buf_len); + +/* make an aio struct (with buffer) available for use (by another get) */ +void dev_async_io_put(struct dev_async_context *ac, 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/liblvm/Makefile.in b/liblvm/Makefile.in index 6d0325c60..1d1498bbc 100644 --- a/liblvm/Makefile.in +++ b/liblvm/Makefile.in @@ -45,6 +45,10 @@ include $(top_builddir)/make.tmpl LDFLAGS += -L$(top_builddir)/lib -L$(top_builddir)/daemons/dmeventd LIBS += $(LVMINTERNAL_LIBS) -ldevmapper +ifeq ("@AIO@", "yes") + LIBS += $(AIO_LIBS) +endif + .PHONY: install_dynamic install_static install_include install_pkgconfig INSTALL_TYPE = install_dynamic diff --git a/make.tmpl.in b/make.tmpl.in index 65362d87c..1040b6358 100644 --- a/make.tmpl.in +++ b/make.tmpl.in @@ -64,6 +64,7 @@ LDDEPS += @LDDEPS@ LIB_SUFFIX = @LIB_SUFFIX@ LVMINTERNAL_LIBS = -llvm-internal $(DMEVENT_LIBS) $(DAEMON_LIBS) $(SYSTEMD_LIBS) $(UDEV_LIBS) $(DL_LIBS) $(BLKID_LIBS) DL_LIBS = @DL_LIBS@ +AIO_LIBS = @AIO_LIBS@ RT_LIBS = @RT_LIBS@ M_LIBS = @M_LIBS@ PTHREAD_LIBS = @PTHREAD_LIBS@ diff --git a/scripts/Makefile.in b/scripts/Makefile.in index d06766f67..767b89bca 100644 --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -31,6 +31,10 @@ endif LVMLIBS = @LVM2APP_LIB@ -ldevmapper endif +ifeq ("@AIO@", "yes") + LVMLIBS += $(AIO_LIBS) +endif + LVM_SCRIPTS = lvmdump.sh lvmconf.sh DM_SCRIPTS = diff --git a/tools/Makefile.in b/tools/Makefile.in index de5b628f8..adf9294e0 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -109,6 +109,10 @@ ifeq ("@CMDLIB@", "yes") INSTALL_LVM_TARGETS += $(INSTALL_CMDLIB_TARGETS) endif +ifeq ("@AIO@", "yes") + LVMLIBS += $(AIO_LIBS) +endif + EXPORTED_HEADER = $(srcdir)/lvm2cmd.h EXPORTED_FN_PREFIX = lvm2 |