master - partial revert "command: Skip some memory zeroing."
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=7cfe5ab9bc279bb1cea...
Commit: 7cfe5ab9bc279bb1cea42862a53d83556fb3ed52
Parent: 83258e33852166a3111a009f1720cc87f62efc73
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Feb 9 10:51:02 2018 +0100
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Feb 9 10:59:07 2018 +0100
partial revert "command: Skip some memory zeroing."
This partially reverts commit da37cbd24fc0073f3f00a3b7aac7807d2185b829.
As the _cmdline structure use mempool for allocated ellement
that is being release on cmd_context close.
Before the better fix is made - restore previous logic and
reinitialize cmd structures again for new cmd_context.
Problem can be hit with e.g. this test run:
make check_local T=foreign LVM_VALGRIND_DMEVENTD=1
Invalid read of size 1
at 0x4C31C83: strcmp (vg_replace_strmem.c:846)
by 0x6BA0939: _find_command (lvmcmdline.c:1555)
by 0x6BA4304: lvm_run_command (lvmcmdline.c:2810)
by 0x6BD5E02: lvm2_run (lvmcmdlib.c:91)
by 0x685607E: dmeventd_lvm2_run (dmeventd_lvm.c:118)
by 0x6652684: _use_policy (dmeventd_thin.c:117)
by 0x6652E56: process_event (dmeventd_thin.c:298)
by 0x10CC5A: _do_process_event (dmeventd.c:945)
by 0x10CF83: _monitor_thread (dmeventd.c:1033)
by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so)
by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so)
Address 0x6266270 is 4,352 bytes inside a block of size 8,192 free'd
at 0x4C2ED68: free (vg_replace_malloc.c:530)
by 0x5289142: dm_free_wrapper (dbg_malloc.c:393)
by 0x528998A: _free_chunk (pool-fast.c:318)
by 0x52892A6: dm_pool_destroy (pool-fast.c:78)
by 0x6A8E52C: destroy_toolcontext (toolcontext.c:2254)
by 0x6BA5BD6: lvm_fin (lvmcmdline.c:3327)
by 0x6BD5EA7: lvm2_exit (lvmcmdlib.c:123)
by 0x6856013: dmeventd_lvm2_exit (dmeventd_lvm.c:103)
by 0x66535B8: unregister_device (dmeventd_thin.c:432)
by 0x10CBBC: _do_unregister_device (dmeventd.c:926)
by 0x10CD74: _monitor_unregister (dmeventd.c:979)
by 0x10D094: _monitor_thread (dmeventd.c:1066)
by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so)
by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so)
Block was alloc'd at
at 0x4C2DBBB: malloc (vg_replace_malloc.c:299)
by 0x5288F46: dm_malloc_aux (dbg_malloc.c:287)
by 0x52890AC: dm_malloc_wrapper (dbg_malloc.c:371)
by 0x52898E6: _new_chunk (pool-fast.c:286)
by 0x52893BA: dm_pool_alloc_aligned (pool-fast.c:106)
by 0x5289310: dm_pool_alloc (pool-fast.c:90)
by 0x6A8A21A: _load_config_file (toolcontext.c:808)
by 0x6A8A3D9: _init_lvm_conf (toolcontext.c:842)
by 0x6A8D3BD: create_toolcontext (toolcontext.c:1941)
by 0x6BA5B24: init_lvm (lvmcmdline.c:3308)
by 0x6BD5B7C: cmdlib_lvm2_init (lvmcmdlib.c:34)
by 0x6BD5EB8: lvm2_init (lvm2cmd.c:20)
by 0x6855EA7: dmeventd_lvm2_init (dmeventd_lvm.c:67)
by 0x665305F: register_device (dmeventd_thin.c:352)
by 0x10CB7A: _do_register_device (dmeventd.c:916)
by 0x10CEE4: _monitor_thread (dmeventd.c:1006)
by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so)
by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so)
---
tools/lvmcmdline.c | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 246d332..6be4bcd 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -1262,6 +1262,15 @@ static const struct command_function *_find_command_id_function(int command_enum
return NULL;
}
+static void _unregister_commands(void)
+{
+ _cmdline.commands = NULL;
+ _cmdline.num_commands = 0;
+ _cmdline.command_names = NULL;
+ _cmdline.num_command_names = 0;
+ memset(&commands, 0, sizeof(commands));
+}
+
int lvm_register_commands(struct cmd_context *cmd, const char *run_name)
{
int i;
@@ -1270,6 +1279,8 @@ int lvm_register_commands(struct cmd_context *cmd, const char *run_name)
if (_cmdline.commands)
return 1;
+ memset(&commands, 0, sizeof(commands));
+
/*
* populate commands[] array with command definitions
* by parsing command-lines.in/command-lines-input.h
@@ -3324,6 +3335,7 @@ struct cmd_context *init_lvm(unsigned set_connections, unsigned set_filters)
void lvm_fin(struct cmd_context *cmd)
{
+ _unregister_commands();
destroy_toolcontext(cmd);
udev_fin_library_context();
}
6 years, 2 months
master - toolcontext: do not change stream for pthreaded programs
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=83258e33852166a3111...
Commit: 83258e33852166a3111a009f1720cc87f62efc73
Parent: 1b6d0346a37d34394471080ec2aff55cea1bdafe
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Feb 9 00:26:22 2018 +0100
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Feb 9 10:56:40 2018 +0100
toolcontext: do not change stream for pthreaded programs
With pthreaded daemons like 'dmeventd' using liblvm via plugin,
lvm2 actually should not 'play' with streams at all - as there
could be parallel outputs running.
As a current quick workaround just disable change for pthreaded
program (gettid() != getpid()).
TODO: it's possible the change of buffering actually doesn't serve us
any measurable benefit and could be dropped as whole later...
Meanwhile this patch is fixing this occasional valgrind race report:
Invalid read of size 4
at 0x571892C: vfprintf (in /usr/lib64/libc-2.26.9000.so)
by 0x57216B3: fprintf (in /usr/lib64/libc-2.26.9000.so)
by 0x5042886: dm_event_log (libdevmapper-event.c:925)
by 0x10B015: _dmeventd_log (dmeventd.c:125)
by 0x10D289: _unregister_for_event (dmeventd.c:1146)
by 0x10E52E: _handle_request (dmeventd.c:1583)
by 0x10E6D7: _do_process_request (dmeventd.c:1631)
by 0x10E7C6: _process_request (dmeventd.c:1660)
by 0x1101A4: main (dmeventd.c:2285)
Address 0x6264d30 is 192 bytes inside a block of size 552 free'd
at 0x4C2ED68: free (vg_replace_malloc.c:530)
by 0x573907D: fclose@(a)GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so)
by 0x6AC5C00: reopen_standard_stream (log.c:189)
by 0x6A8E62C: destroy_toolcontext (toolcontext.c:2271)
by 0x6BA5C22: lvm_fin (lvmcmdline.c:3339)
by 0x6BD5EF3: lvm2_exit (lvmcmdlib.c:123)
by 0x6856013: dmeventd_lvm2_exit (dmeventd_lvm.c:103)
by 0x66535B8: unregister_device (dmeventd_thin.c:432)
by 0x10CBBC: _do_unregister_device (dmeventd.c:926)
by 0x10CD74: _monitor_unregister (dmeventd.c:979)
by 0x10D094: _monitor_thread (dmeventd.c:1066)
by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so)
by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so)
Block was alloc'd at
at 0x4C2DBBB: malloc (vg_replace_malloc.c:299)
by 0x573932B: fdopen@(a)GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so)
by 0x6AC5DC2: reopen_standard_stream (log.c:200)
by 0x6A8D11D: create_toolcontext (toolcontext.c:1898)
by 0x6BA5B6B: init_lvm (lvmcmdline.c:3319)
by 0x6BD5BC8: cmdlib_lvm2_init (lvmcmdlib.c:34)
by 0x6BD5F04: lvm2_init (lvm2cmd.c:20)
by 0x6855EA7: dmeventd_lvm2_init (dmeventd_lvm.c:67)
by 0x665305F: register_device (dmeventd_thin.c:352)
by 0x10CB7A: _do_register_device (dmeventd.c:916)
by 0x10CEE4: _monitor_thread (dmeventd.c:1006)
by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so)
by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so)
....
Process terminating with default action of signal 6 (SIGABRT): dumping core
at 0x570016B: raise (in /usr/lib64/libc-2.26.9000.so)
by 0x5701520: abort (in /usr/lib64/libc-2.26.9000.so)
by 0x57437D8: __libc_message (in /usr/lib64/libc-2.26.9000.so)
by 0x5743831: __libc_fatal (in /usr/lib64/libc-2.26.9000.so)
by 0x5744056: _IO_vtable_check (in /usr/lib64/libc-2.26.9000.so)
by 0x574751C: __overflow (in /usr/lib64/libc-2.26.9000.so)
by 0x574191A: fputc (in /usr/lib64/libc-2.26.9000.so)
by 0x50428E3: dm_event_log (libdevmapper-event.c:934)
by 0x10B015: _dmeventd_log (dmeventd.c:125)
by 0x10D289: _unregister_for_event (dmeventd.c:1146)
by 0x10E52E: _handle_request (dmeventd.c:1583)
by 0x10E6D7: _do_process_request (dmeventd.c:1631)
by 0x10E7C6: _process_request (dmeventd.c:1660)
by 0x1101A4: main (dmeventd.c:2285)
---
WHATS_NEW | 1 +
lib/commands/toolcontext.c | 11 +++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 197adc3..bf46281 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Do not reopen output streams for multithreaded users of liblvm.
Use versionsort to fix archive file expiry beyond 100000 files.
Add devices/use_aio, aio_max, aio_memory to configure AIO limits.
Support asynchronous I/O when scanning devices.
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 0080a68..ba757dd 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -46,6 +46,7 @@
#include <locale.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <time.h>
@@ -1883,7 +1884,13 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
#ifndef VALGRIND_POOL
/* Set in/out stream buffering before glibc */
- if (set_buffering) {
+ if (set_buffering
+#ifdef SYS_gettid
+ /* For threaded programs no changes of streams */
+ /* On linux gettid() is implemented only via syscall */
+ && (syscall(SYS_gettid) == getpid())
+#endif
+ ) {
/* Allocate 2 buffers */
if (!(cmd->linebuffer = dm_malloc(2 * _linebuffer_size))) {
log_error("Failed to allocate line buffer.");
@@ -1914,7 +1921,7 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
}
}
/* Buffers are used for lines without '\n' */
- } else
+ } else if (!set_buffering)
/* Without buffering, must not use stdin/stdout */
init_silent(1);
#endif
6 years, 2 months
master - format_text: Use versionsort to sort archive files
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=1b6d0346a37d3439447...
Commit: 1b6d0346a37d34394471080ec2aff55cea1bdafe
Parent: d6cabbbc53de615fe92ff6372a570285704b59d2
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Fri Feb 9 01:08:55 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Fri Feb 9 01:08:55 2018 +0000
format_text: Use versionsort to sort archive files
Ensure that vg_100000-* follows vg_99999-* so that the expiry logic
doesn't stop too early.
https://bugzilla.redhat.com/1481085
---
WHATS_NEW | 1 +
lib/format_text/archive.c | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 9e6cb39..197adc3 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Use versionsort to fix archive file expiry beyond 100000 files.
Add devices/use_aio, aio_max, aio_memory to configure AIO limits.
Support asynchronous I/O when scanning devices.
Detect asynchronous I/O capability in configure or accept --disable-aio.
diff --git a/lib/format_text/archive.c b/lib/format_text/archive.c
index 4d4e7ac..a81a83b 100644
--- a/lib/format_text/archive.c
+++ b/lib/format_text/archive.c
@@ -136,8 +136,8 @@ static struct dm_list *_scan_archive(struct dm_pool *mem,
dm_list_init(results);
- /* Sort fails beyond 5-digit indexes */
- if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) {
+ /* Use versionsort to handle numbers beyond 5 digits */
+ if ((count = scandir(dir, &dirent, NULL, versionsort)) < 0) {
log_error("Couldn't scan the archive directory (%s).", dir);
return 0;
}
6 years, 2 months
master - device: Fix basic async I/O error handling
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=d6cabbbc53de615fe92...
Commit: d6cabbbc53de615fe92ff6372a570285704b59d2
Parent: 3e29c80122b8eb1123e42d143f17dd7bddefedcd
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Tue Feb 6 21:43:06 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Thu Feb 8 20:19:21 2018 +0000
device: Fix basic async I/O error handling
---
lib/cache/lvmcache.c | 2 ++
lib/config/config.c | 7 +++++--
lib/device/dev-io.c | 36 ++++++++++++++++++++++++++----------
lib/device/device.h | 6 +++---
lib/format_text/format-text.c | 32 +++++++++++++++++---------------
lib/format_text/import.c | 4 ++--
lib/format_text/text_label.c | 6 +++++-
lib/label/label.c | 15 +++++++--------
8 files changed, 67 insertions(+), 41 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 7678290..4022ea6 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1099,6 +1099,7 @@ next:
}
/* Track the number of outstanding label reads */
+/* FIXME Switch to struct and also track failed */
static void _process_label_data(int failed, unsigned ioflags, void *context, const void *data)
{
int *nr_labels_outstanding = context;
@@ -1160,6 +1161,7 @@ int lvmcache_label_scan(struct cmd_context *cmd)
_destroy_duplicate_device_list(&_found_duplicate_devs);
while ((dev = dev_iter_get(iter))) {
+ log_debug_io("Scanning device %s", dev_name(dev));
nr_labels_outstanding++;
if (!label_read_callback(dev, UINT64_C(0), AIO_SUPPORTED_CODE_PATH, _process_label_data, &nr_labels_outstanding))
nr_labels_outstanding--;
diff --git a/lib/config/config.c b/lib/config/config.c
index c69ac69..97c5db8 100644
--- a/lib/config/config.c
+++ b/lib/config/config.c
@@ -609,8 +609,11 @@ int config_file_read_fd(struct dm_pool *mem, struct dm_config_tree *cft, struct
goto_out;
_process_config_file_buffer(0, ioflags, pcfp, buf);
dm_free((void *)buf);
- } else if (!dev_read_callback(dev, (uint64_t) offset, size, reason, ioflags, _process_config_file_buffer, pcfp))
- goto_out;
+ } else {
+ dev_read_callback(dev, (uint64_t) offset, size, reason, ioflags, _process_config_file_buffer, pcfp);
+ if (config_file_read_fd_callback)
+ return 1;
+ }
r = pcfp->ret;
}
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
index 460c874..31506e8 100644
--- a/lib/device/dev-io.c
+++ b/lib/device/dev-io.c
@@ -245,7 +245,8 @@ int dev_async_getevents(void)
_release_devbuf(devbuf);
if (dev_read_callback_fn)
dev_read_callback_fn(1, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, NULL);
- r = 0;
+ else
+ r = 0;
}
}
@@ -1080,24 +1081,28 @@ static void _dev_inc_error_count(struct device *dev)
}
/*
- * Data is returned (read-only) at DEV_DEVBUF_DATA(dev, reason)
+ * Data is returned (read-only) at DEV_DEVBUF_DATA(dev, reason).
+ * If dev_read_callback_fn is supplied, we always return 1 and take
+ * responsibility for calling it exactly once. This might happen before the
+ * function returns (if there's an error or the I/O is synchronous) or after.
+ * Any error is passed to that function, which must track it if required.
*/
-int dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
- unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context)
+static int _dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
+ unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context)
{
struct device_area where;
struct device_buffer *devbuf;
uint64_t buf_end;
int cached = 0;
- int ret = 1;
+ int ret = 0;
if (!dev->open_count) {
log_error(INTERNAL_ERROR "Attempt to access device %s while closed.", dev_name(dev));
- return 0;
+ goto out;
}
if (!_dev_is_valid(dev))
- return 0;
+ goto_out;
/*
* Can we satisfy this from data we stored last time we read?
@@ -1110,6 +1115,7 @@ int dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_re
devbuf->data_offset = offset - devbuf->where.start;
log_debug_io("Cached read for %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)",
(uint64_t) len, (uint64_t) offset, dev_name(dev), _reason_text(reason));
+ ret = 1;
goto out;
}
}
@@ -1126,16 +1132,26 @@ int dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_re
out:
/* If we had an error or this was sync I/O, pass the result to any callback fn */
- if ((!ret || !_aio_ctx || !aio_supported_code_path(ioflags) || cached) && dev_read_callback_fn)
+ if ((!ret || !_aio_ctx || !aio_supported_code_path(ioflags) || cached) && dev_read_callback_fn) {
dev_read_callback_fn(!ret, ioflags, callback_context, DEV_DEVBUF_DATA(dev, reason));
+ return 1;
+ }
return ret;
}
+void dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
+ unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context)
+{
+ /* Always returns 1 if callback fn is supplied */
+ if (!_dev_read_callback(dev, offset, len, reason, ioflags, dev_read_callback_fn, callback_context))
+ log_error(INTERNAL_ERROR "_dev_read_callback failed");
+}
+
/* Returns pointer to read-only buffer. Caller does not free it. */
const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason)
{
- if (!dev_read_callback(dev, offset, len, reason, 0, NULL, NULL))
+ if (!_dev_read_callback(dev, offset, len, reason, 0, NULL, NULL))
return_NULL;
return DEV_DEVBUF_DATA(dev, reason);
@@ -1144,7 +1160,7 @@ const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_rea
/* Read into supplied retbuf owned by the caller. */
int dev_read_buf(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *retbuf)
{
- if (!dev_read_callback(dev, offset, len, reason, 0, NULL, NULL)) {
+ if (!_dev_read_callback(dev, offset, len, reason, 0, NULL, NULL)) {
log_error("Read from %s failed", dev_name(dev));
return 0;
}
diff --git a/lib/device/device.h b/lib/device/device.h
index 9061139..ea71d00 100644
--- a/lib/device/device.h
+++ b/lib/device/device.h
@@ -184,9 +184,9 @@ const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_rea
const char *dev_read_circular(struct device *dev, uint64_t offset, size_t len,
uint64_t offset2, size_t len2, dev_io_reason_t reason);
-/* Passes the data to dev_read_callback_fn */
-int dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
- unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context);
+/* Passes the data (or error) to dev_read_callback_fn */
+void dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
+ unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context);
/* Read data and copy it into a supplied private buffer. */
/* Only use for tiny reads or on unimportant code paths. */
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index 7f63ad6..e1603e7 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -385,8 +385,8 @@ static void _process_raw_mda_header(int failed, unsigned ioflags, void *context,
bad:
prmp->ret = 0;
out:
- if (prmp->ret && prmp->mdah_callback_fn)
- prmp->mdah_callback_fn(0, ioflags, prmp->mdah_callback_context, mdah);
+ if (prmp->mdah_callback_fn)
+ prmp->mdah_callback_fn(!prmp->ret, ioflags, prmp->mdah_callback_context, mdah);
}
static struct mda_header *_raw_read_mda_header(struct dm_pool *mem, struct device_area *dev_area, int primary_mda,
@@ -417,14 +417,15 @@ static struct mda_header *_raw_read_mda_header(struct dm_pool *mem, struct devic
prmp->mdah_callback_context = mdah_callback_context;
prmp->ret = 1;
- if (!dev_read_callback(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, MDA_HEADER_REASON(primary_mda),
- ioflags, _process_raw_mda_header, prmp))
- stack;
+ dev_read_callback(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, MDA_HEADER_REASON(primary_mda),
+ ioflags, _process_raw_mda_header, prmp);
+ if (mdah_callback_fn)
+ return mdah;
if (!prmp->ret)
return_NULL;
-
- return mdah;
+ else
+ return mdah;
}
struct mda_header *raw_read_mda_header(struct dm_pool *mem, struct device_area *dev_area, int primary_mda)
@@ -1404,8 +1405,7 @@ static void _vgname_from_mda_process(int failed, unsigned ioflags, void *context
}
out:
- if (vfmp->ret)
- vfmp->update_vgsummary_fn(0, ioflags, vfmp->update_vgsummary_context, vfmp->vgsummary);
+ vfmp->update_vgsummary_fn(!vfmp->ret, ioflags, vfmp->update_vgsummary_context, vfmp->vgsummary);
}
static void _vgname_from_mda_validate(int failed, unsigned ioflags, void *context, const void *data)
@@ -1469,7 +1469,8 @@ static void _vgname_from_mda_validate(int failed, unsigned ioflags, void *contex
}
out:
- ;
+ if (!vfmp->ret && vfmp->update_vgsummary_fn)
+ vfmp->update_vgsummary_fn(1, ioflags, vfmp->update_vgsummary_context, vfmp->vgsummary);
}
int vgname_from_mda(const struct format_type *fmt,
@@ -1517,11 +1518,12 @@ int vgname_from_mda(const struct format_type *fmt,
/* Do quick check for a vgname */
/* We cannot read the full metadata here because the name has to be validated before we use the size field */
- if (!dev_read_callback(dev_area->dev, dev_area->start + rlocn->offset, NAME_LEN, MDA_CONTENT_REASON(primary_mda),
- ioflags, _vgname_from_mda_validate, vfmp))
- return_0;
-
- return vfmp->ret;
+ dev_read_callback(dev_area->dev, dev_area->start + rlocn->offset, NAME_LEN, MDA_CONTENT_REASON(primary_mda),
+ ioflags, _vgname_from_mda_validate, vfmp);
+ if (update_vgsummary_fn)
+ return 1;
+ else
+ return vfmp->ret;
}
static int _scan_raw(const struct format_type *fmt, const char *vgname __attribute__((unused)))
diff --git a/lib/format_text/import.c b/lib/format_text/import.c
index 69af925..0138ddd 100644
--- a/lib/format_text/import.c
+++ b/lib/format_text/import.c
@@ -78,8 +78,8 @@ static void _import_vgsummary(int failed, unsigned ioflags, void *context, const
out:
config_destroy(ivsp->cft);
- if (ivsp->ret && ivsp->process_vgsummary_fn)
- ivsp->process_vgsummary_fn(0, ioflags, ivsp->process_vgsummary_context, NULL);
+ if (ivsp->process_vgsummary_fn)
+ ivsp->process_vgsummary_fn(!ivsp->ret, ioflags, ivsp->process_vgsummary_context, NULL);
}
/*
diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c
index d3d92f0..45136e9 100644
--- a/lib/format_text/text_label.c
+++ b/lib/format_text/text_label.c
@@ -344,6 +344,7 @@ static void _process_vgsummary(int failed, unsigned ioflags, void *context, cons
--pmp->umb->nr_outstanding_mdas;
+ /* FIXME Need to distinguish genuine errors here */
if (failed)
goto_out;
@@ -446,7 +447,10 @@ static int _update_mda(struct metadata_area *mda, void *baton)
return 1;
}
- return pmp->ret;
+ if (umb->read_label_callback_fn)
+ return 1;
+ else
+ return pmp->ret;
}
static int _text_read(struct labeller *l, struct device *dev, void *buf, unsigned ioflags,
diff --git a/lib/label/label.c b/lib/label/label.c
index 0997b96..ae3a2c1 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -155,8 +155,10 @@ out:
if (!dev_close(flp->dev))
stack;
- if (flp->process_label_data_fn)
+ if (flp->process_label_data_fn) {
+ log_debug_io("Completed label reading for %s", dev_name(flp->dev));
flp->process_label_data_fn(!flp->ret, ioflags, flp->process_label_data_context, NULL);
+ }
}
static void _find_labeller(int failed, unsigned ioflags, void *context, const void *data)
@@ -324,8 +326,10 @@ static int _label_read(struct device *dev, uint64_t scan_sector, struct label **
log_debug_devs("Reading label from lvmcache for %s", dev_name(dev));
if (result)
*result = lvmcache_get_label(info);
- if (process_label_data_fn)
+ if (process_label_data_fn) {
+ log_debug_io("Completed label reading for %s", dev_name(dev));
process_label_data_fn(0, ioflags, process_label_data_context, NULL);
+ }
return 1;
}
@@ -356,12 +360,7 @@ static int _label_read(struct device *dev, uint64_t scan_sector, struct label **
return 0;
}
- if (!(dev_read_callback(dev, scan_sector << SECTOR_SHIFT, LABEL_SCAN_SIZE, DEV_IO_LABEL, ioflags, _find_labeller, flp))) {
- log_debug_devs("%s: Failed to read label area", dev_name(dev));
- _set_label_read_result(1, ioflags, flp, NULL);
- return 0;
- }
-
+ dev_read_callback(dev, scan_sector << SECTOR_SHIFT, LABEL_SCAN_SIZE, DEV_IO_LABEL, ioflags, _find_labeller, flp);
if (process_label_data_fn)
return 1;
else
6 years, 2 months
master - device: Queue any aio beyond defined limits.
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=3e29c80122b8eb1123e...
Commit: 3e29c80122b8eb1123e42d143f17dd7bddefedcd
Parent: db41fe6c5dab7ff66db9c0568f0e1e1b31657be3
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Jan 22 18:26:03 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Thu Feb 8 20:15:37 2018 +0000
device: Queue any aio beyond defined limits.
---
WHATS_NEW | 1 +
conf/example.conf.in | 11 +++++
lib/config/config_settings.h | 7 +++
lib/config/defaults.h | 2 +
lib/device/dev-io.c | 85 ++++++++++++++++++++++++++++++++++++++++-
lib/device/device.h | 1 +
6 files changed, 104 insertions(+), 3 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 8ae79cf..9e6cb39 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Add devices/use_aio, aio_max, aio_memory to configure AIO limits.
Support asynchronous I/O when scanning devices.
Detect asynchronous I/O capability in configure or accept --disable-aio.
Add AIO_SUPPORTED_CODE_PATH to indicate whether AIO may be used.
diff --git a/conf/example.conf.in b/conf/example.conf.in
index 6a491fb..3b0638f 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -64,6 +64,17 @@ devices {
# This configuration option has an automatic default value.
# use_aio = 1
+ # Configuration option devices/aio_max.
+ # Maximum number of asynchronous I/Os to issue concurrently.
+ # This configuration option has an automatic default value.
+ # aio_max = 128
+
+ # Configuration option devices/aio_memory.
+ # Approximate maximum total amount of memory (in MB) used
+ # for asynchronous I/O buffers.
+ # This configuration option has an automatic default value.
+ # aio_memory = 10
+
# Configuration option devices/obtain_device_list_from_udev.
# Obtain the list of available devices from udev.
# This avoids opening or using any inapplicable non-block devices or
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 523e6fe..f1db797 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -229,6 +229,13 @@ cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_
cfg(devices_use_aio_CFG, "use_aio", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_USE_AIO, vsn(2, 2, 178), NULL, 0, NULL,
"Use linux asynchronous I/O for parallel device access where possible.\n")
+cfg(devices_aio_max_CFG, "aio_max", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_AIO_MAX, vsn(2, 2, 178), NULL, 0, NULL,
+ "Maximum number of asynchronous I/Os to issue concurrently.\n")
+
+cfg(devices_aio_memory_CFG, "aio_memory", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_AIO_MEMORY, vsn(2, 2, 178), NULL, 0, NULL,
+ "Approximate maximum total amount of memory (in MB) used\n"
+ "for asynchronous I/O buffers.\n")
+
cfg_array(devices_loopfiles_CFG, "loopfiles", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 2, 0), NULL, 0, NULL, NULL)
cfg(devices_obtain_device_list_from_udev_CFG, "obtain_device_list_from_udev", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV, vsn(2, 2, 85), NULL, 0, NULL,
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 5efab11..1c730a9 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -33,6 +33,8 @@
#define DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV 1
#define DEFAULT_EXTERNAL_DEVICE_INFO_SOURCE "none"
#define DEFAULT_USE_AIO 1
+#define DEFAULT_AIO_MAX 128
+#define DEFAULT_AIO_MEMORY 10
#define DEFAULT_SYSFS_SCAN 1
#define DEFAULT_MD_COMPONENT_DETECTION 1
#define DEFAULT_FW_RAID_COMPONENT_DETECTION 0
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
index f2bb128..460c874 100644
--- a/lib/device/dev-io.c
+++ b/lib/device/dev-io.c
@@ -104,7 +104,11 @@ void devbufs_release(struct device *dev)
static io_context_t _aio_ctx = 0;
static struct io_event *_aio_events = NULL;
-static int _aio_max = 128;
+static int _aio_max = 0;
+static int64_t _aio_memory_max = 0;
+static int _aio_must_queue = 0; /* Have we reached AIO capacity? */
+
+static DM_LIST_INIT(_aio_queue);
#define DEFAULT_AIO_COLLECTION_EVENTS 32
@@ -112,11 +116,21 @@ int dev_async_setup(struct cmd_context *cmd)
{
int r;
+ _aio_max = find_config_tree_int(cmd, devices_aio_max_CFG, NULL);
+ _aio_memory_max = find_config_tree_int(cmd, devices_aio_memory_CFG, NULL) * 1024 * 1024;
+
+ /* Threshold is zero? */
+ if (!_aio_max || !_aio_memory_max) {
+ if (_aio_ctx)
+ dev_async_exit();
+ return 1;
+ }
+
/* Already set up? */
if (_aio_ctx)
return 1;
- log_debug_io("Setting up aio context for up to %d events.", _aio_max);
+ log_debug_io("Setting up aio context for up to %" PRId64 " MB across %d events.", _aio_memory_max, _aio_max);
if (!_aio_events && !(_aio_events = dm_zalloc(sizeof(*_aio_events) * DEFAULT_AIO_COLLECTION_EVENTS))) {
log_error("Failed to allocate io_event array for asynchronous I/O.");
@@ -154,11 +168,29 @@ int dev_async_reset(struct cmd_context *cmd)
return dev_async_setup(cmd);
}
+/*
+ * Track the amount of in-flight async I/O.
+ * If it exceeds the defined threshold set _aio_must_queue.
+ */
+static void _update_aio_counters(int nr, ssize_t bytes)
+{
+ static int64_t aio_bytes = 0;
+ static int aio_count = 0;
+
+ aio_bytes += bytes;
+ aio_count += nr;
+
+ if (aio_count >= _aio_max || aio_bytes > _aio_memory_max)
+ _aio_must_queue = 1;
+ else
+ _aio_must_queue = 0;
+}
+
static int _io(struct device_buffer *devbuf, unsigned ioflags);
int dev_async_getevents(void)
{
- struct device_buffer *devbuf;
+ struct device_buffer *devbuf, *tmp;
lvm_callback_fn_t dev_read_callback_fn;
void *dev_read_callback_context;
int r, event_nr;
@@ -192,6 +224,8 @@ int dev_async_getevents(void)
devbuf = _aio_events[event_nr].obj->data;
dm_free(_aio_events[event_nr].obj);
+ _update_aio_counters(-1, -devbuf->where.size);
+
dev_read_callback_fn = devbuf->dev_read_callback_fn;
dev_read_callback_context = devbuf->dev_read_callback_context;
@@ -215,6 +249,14 @@ int dev_async_getevents(void)
}
}
+ /* Submit further queued events if we can */
+ dm_list_iterate_items_gen_safe(devbuf, tmp, &_aio_queue, aio_queued) {
+ if (_aio_must_queue)
+ break;
+ dm_list_del(&devbuf->aio_queued);
+ _io(devbuf, 1);
+ }
+
return 1;
}
@@ -224,6 +266,8 @@ static int _io_async(struct device_buffer *devbuf)
struct iocb *iocb;
int r;
+ _update_aio_counters(1, devbuf->where.size);
+
if (!(iocb = dm_malloc(sizeof(*iocb)))) {
log_error("Failed to allocate I/O control block array for asynchronous I/O.");
return 0;
@@ -260,11 +304,29 @@ static int _io_async(struct device_buffer *devbuf)
void dev_async_exit(void)
{
+ struct device_buffer *devbuf, *tmp;
+ lvm_callback_fn_t dev_read_callback_fn;
+ void *dev_read_callback_context;
int r;
if (!_aio_ctx)
return;
+ /* Discard any queued requests */
+ dm_list_iterate_items_gen_safe(devbuf, tmp, &_aio_queue, aio_queued) {
+ dm_list_del(&devbuf->aio_queued);
+
+ _update_aio_counters(-1, -devbuf->where.size);
+
+ dev_read_callback_fn = devbuf->dev_read_callback_fn;
+ dev_read_callback_context = devbuf->dev_read_callback_context;
+
+ _release_devbuf(devbuf);
+
+ if (dev_read_callback_fn)
+ dev_read_callback_fn(1, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, NULL);
+ }
+
log_debug_io("Destroying aio context.");
if ((r = io_destroy(_aio_ctx)) < 0)
/* Returns -ENOSYS if aio not in kernel or -EINVAL if _aio_ctx invalid */
@@ -276,9 +338,16 @@ void dev_async_exit(void)
_aio_ctx = 0;
}
+static void _queue_aio(struct device_buffer *devbuf)
+{
+ dm_list_add(&_aio_queue, &devbuf->aio_queued);
+ log_debug_io("Queueing aio.");
+}
+
#else
static int _aio_ctx = 0;
+static int _aio_must_queue = 0;
int dev_async_setup(struct cmd_context *cmd)
{
@@ -304,6 +373,10 @@ static int _io_async(struct device_buffer *devbuf)
return 0;
}
+static void _queue_aio(struct device_buffer *devbuf)
+{
+}
+
#endif /* AIO_SUPPORT */
/*-----------------------------------------------------------------
@@ -542,6 +615,12 @@ static int _aligned_io(struct device_area *where, char *write_buffer,
devbuf->buf = (char *) ((((uintptr_t) devbuf->buf) + mask) & ~mask);
}
+ /* If we've reached our concurrent AIO limit, add this request to the queue */
+ if (!devbuf->write && _aio_ctx && aio_supported_code_path(ioflags) && dev_read_callback_fn && _aio_must_queue) {
+ _queue_aio(devbuf);
+ return 1;
+ }
+
devbuf->write = 0;
/* Do we need to read into the bounce buffer? */
diff --git a/lib/device/device.h b/lib/device/device.h
index b52522d..9061139 100644
--- a/lib/device/device.h
+++ b/lib/device/device.h
@@ -100,6 +100,7 @@ struct device_buffer {
lvm_callback_fn_t dev_read_callback_fn;
void *dev_read_callback_context;
+ struct dm_list aio_queued; /* Queue of async I/O waiting to be issued */
};
/*
6 years, 2 months
master - lvmcache: Use asynchronous I/O when scanning devices.
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=db41fe6c5dab7ff66db...
Commit: db41fe6c5dab7ff66db9c0568f0e1e1b31657be3
Parent: 8c7bbcfb0f6771a10acd238057fdb7931c3fbb12
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Jan 22 18:17:58 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Thu Feb 8 20:15:29 2018 +0000
lvmcache: Use asynchronous I/O when scanning devices.
---
WHATS_NEW | 1 +
lib/cache/lvmcache.c | 11 +++
lib/device/dev-io.c | 165 ++++++++++++++++++++++++++++++++++++++++++++-----
lib/device/device.h | 4 +
4 files changed, 164 insertions(+), 17 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 5aa4ed8..8ae79cf 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Support asynchronous I/O when scanning devices.
Detect asynchronous I/O capability in configure or accept --disable-aio.
Add AIO_SUPPORTED_CODE_PATH to indicate whether AIO may be used.
Configure ensures /usr/bin dir is checked for dmpd tools.
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 2eaf479..7678290 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1103,6 +1103,11 @@ static void _process_label_data(int failed, unsigned ioflags, void *context, con
{
int *nr_labels_outstanding = context;
+ if (!*nr_labels_outstanding) {
+ log_error(INTERNAL_ERROR "_process_label_data called too many times");
+ return;
+ }
+
(*nr_labels_outstanding)--;
}
@@ -1163,6 +1168,12 @@ int lvmcache_label_scan(struct cmd_context *cmd)
dev_iter_destroy(iter);
+ while (nr_labels_outstanding) {
+ log_very_verbose("Scanned %d device labels (%d outstanding)", dev_count, nr_labels_outstanding);
+ if (!dev_async_getevents())
+ return_0;
+ }
+
log_very_verbose("Scanned %d device labels (%d outstanding)", dev_count, nr_labels_outstanding);
/*
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
index 48f5b8d..f2bb128 100644
--- a/lib/device/dev-io.c
+++ b/lib/device/dev-io.c
@@ -103,8 +103,11 @@ void devbufs_release(struct device *dev)
# include <libaio.h>
static io_context_t _aio_ctx = 0;
+static struct io_event *_aio_events = NULL;
static int _aio_max = 128;
+#define DEFAULT_AIO_COLLECTION_EVENTS 32
+
int dev_async_setup(struct cmd_context *cmd)
{
int r;
@@ -115,6 +118,11 @@ int dev_async_setup(struct cmd_context *cmd)
log_debug_io("Setting up aio context for up to %d events.", _aio_max);
+ if (!_aio_events && !(_aio_events = dm_zalloc(sizeof(*_aio_events) * DEFAULT_AIO_COLLECTION_EVENTS))) {
+ log_error("Failed to allocate io_event array for asynchronous I/O.");
+ return 0;
+ }
+
if ((r = io_setup(_aio_max, &_aio_ctx)) < 0) {
/*
* Possible errors:
@@ -126,6 +134,8 @@ int dev_async_setup(struct cmd_context *cmd)
*/
log_warn("WARNING: Asynchronous I/O setup for %d events failed: %s", _aio_max, strerror(-r));
log_warn("WARNING: Using only synchronous I/O.");
+ dm_free(_aio_events);
+ _aio_events = NULL;
_aio_ctx = 0;
return 0;
}
@@ -138,10 +148,116 @@ int dev_async_reset(struct cmd_context *cmd)
{
log_debug_io("Resetting asynchronous I/O context.");
_aio_ctx = 0;
+ dm_free(_aio_events);
+ _aio_events = NULL;
return dev_async_setup(cmd);
}
+static int _io(struct device_buffer *devbuf, unsigned ioflags);
+
+int dev_async_getevents(void)
+{
+ struct device_buffer *devbuf;
+ lvm_callback_fn_t dev_read_callback_fn;
+ void *dev_read_callback_context;
+ int r, event_nr;
+
+ if (!_aio_ctx)
+ return 1;
+
+ do {
+ /* FIXME Add timeout - currently NULL - waits for ever for at least 1 item */
+ r = io_getevents(_aio_ctx, 1, DEFAULT_AIO_COLLECTION_EVENTS, _aio_events, NULL);
+ if (r > 0)
+ break;
+ if (!r)
+ return 1; /* Timeout elapsed */
+ if (r == -EINTR)
+ continue;
+ if (r == -EAGAIN) {
+ usleep(100);
+ return 1; /* Give the caller the opportunity to do other work before repeating */
+ }
+ /*
+ * ENOSYS - not supported by kernel
+ * EFAULT - memory invalid
+ * EINVAL - _aio_ctx invalid or min_nr/nr/timeout out of range
+ */
+ log_error("Asynchronous event collection failed: %s", strerror(-r));
+ return 0;
+ } while (1);
+
+ for (event_nr = 0; event_nr < r; event_nr++) {
+ devbuf = _aio_events[event_nr].obj->data;
+ dm_free(_aio_events[event_nr].obj);
+
+ dev_read_callback_fn = devbuf->dev_read_callback_fn;
+ dev_read_callback_context = devbuf->dev_read_callback_context;
+
+ /* Clear the callbacks as a precaution */
+ devbuf->dev_read_callback_context = NULL;
+ devbuf->dev_read_callback_fn = NULL;
+
+ if (_aio_events[event_nr].res == devbuf->where.size) {
+ if (dev_read_callback_fn)
+ dev_read_callback_fn(0, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, (char *)devbuf->buf + devbuf->data_offset);
+ } else {
+ /* FIXME If partial read is possible, resubmit remainder */
+ log_error_once("%s: Asynchronous I/O failed: read only %" PRIu64 " of %" PRIu64 " bytes at %" PRIu64,
+ dev_name(devbuf->where.dev),
+ (uint64_t) _aio_events[event_nr].res, (uint64_t) devbuf->where.size,
+ (uint64_t) devbuf->where.start);
+ _release_devbuf(devbuf);
+ if (dev_read_callback_fn)
+ dev_read_callback_fn(1, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, NULL);
+ r = 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _io_async(struct device_buffer *devbuf)
+{
+ struct device_area *where = &devbuf->where;
+ struct iocb *iocb;
+ int r;
+
+ if (!(iocb = dm_malloc(sizeof(*iocb)))) {
+ log_error("Failed to allocate I/O control block array for asynchronous I/O.");
+ return 0;
+ }
+
+ io_prep_pread(iocb, dev_fd(where->dev), devbuf->buf, where->size, where->start);
+ iocb->data = devbuf;
+
+ do {
+ r = io_submit(_aio_ctx, 1L, &iocb);
+ if (r ==1)
+ break; /* Success */
+ if (r == -EAGAIN) {
+ /* Try to release some resources then retry */
+ usleep(100);
+ if (dev_async_getevents())
+ return_0;
+ /* FIXME Add counter/timeout so we can't get stuck here for ever */
+ continue;
+ }
+ /*
+ * Possible errors:
+ * EFAULT - invalid data
+ * ENOSYS - no aio support in kernel
+ * EBADF - bad file descriptor in iocb
+ * EINVAL - invalid _aio_ctx / iocb not initialised / invalid operation for this fd
+ */
+ log_error("Asynchronous event submission failed: %s", strerror(-r));
+ return 0;
+ } while (1);
+
+ return 1;
+}
+
void dev_async_exit(void)
{
int r;
@@ -154,6 +270,9 @@ void dev_async_exit(void)
/* Returns -ENOSYS if aio not in kernel or -EINVAL if _aio_ctx invalid */
log_error("Failed to destroy asynchronous I/O context: %s", strerror(-r));
+ dm_free(_aio_events);
+ _aio_events = NULL;
+
_aio_ctx = 0;
}
@@ -171,10 +290,20 @@ int dev_async_reset(struct cmd_context *cmd)
return 1;
}
+int dev_async_getevents(void)
+{
+ return 1;
+}
+
void dev_async_exit(void)
{
}
+static int _io_async(struct device_buffer *devbuf)
+{
+ return 0;
+}
+
#endif /* AIO_SUPPORT */
/*-----------------------------------------------------------------
@@ -225,6 +354,7 @@ static int _io(struct device_buffer *devbuf, unsigned ioflags)
{
struct device_area *where = &devbuf->where;
int fd = dev_fd(where->dev);
+ int async = (!devbuf->write && _aio_ctx && aio_supported_code_path(ioflags) && devbuf->dev_read_callback_fn) ? 1 : 0;
if (fd < 0) {
log_error("Attempt to read an unopened device (%s).",
@@ -237,9 +367,9 @@ static int _io(struct device_buffer *devbuf, unsigned ioflags)
return 0;
}
- log_debug_io("%s %s(fd %d):%8" PRIu64 " bytes (sync) at %" PRIu64 "%s (for %s)",
+ log_debug_io("%s %s(fd %d):%8" PRIu64 " bytes (%ssync) at %" PRIu64 "%s (for %s)",
devbuf->write ? "Write" : "Read ", dev_name(where->dev), fd,
- where->size, (uint64_t) where->start,
+ where->size, async ? "a" : "", (uint64_t) where->start,
(devbuf->write && test_mode()) ? " (test mode - suppressed)" : "", _reason_text(devbuf->reason));
/*
@@ -253,7 +383,7 @@ static int _io(struct device_buffer *devbuf, unsigned ioflags)
return 0;
}
- return _io_sync(devbuf);
+ return async ? _io_async(devbuf) : _io_sync(devbuf);
}
/*-----------------------------------------------------------------
@@ -347,7 +477,7 @@ static void _widen_region(unsigned int block_size, struct device_area *region,
static int _aligned_io(struct device_area *where, char *write_buffer,
int should_write, dev_io_reason_t reason,
- unsigned ioflags)
+ unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *dev_read_callback_context)
{
unsigned int physical_block_size = 0;
unsigned int block_size = 0;
@@ -386,6 +516,8 @@ static int _aligned_io(struct device_area *where, char *write_buffer,
devbuf->where.size = widened.size;
devbuf->write = should_write;
devbuf->reason = reason;
+ devbuf->dev_read_callback_fn = dev_read_callback_fn;
+ devbuf->dev_read_callback_context = dev_read_callback_context;
/* Store location of requested data relative to start of buf */
devbuf->data_offset = where->start - devbuf->where.start;
@@ -882,15 +1014,11 @@ int dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_re
if (!dev->open_count) {
log_error(INTERNAL_ERROR "Attempt to access device %s while closed.", dev_name(dev));
- ret = 0;
- goto out;
+ return 0;
}
- if (!_dev_is_valid(dev)) {
- log_error("Not reading from %s - too many errors.", dev_name(dev));
- ret = 0;
- goto out;
- }
+ if (!_dev_is_valid(dev))
+ return 0;
/*
* Can we satisfy this from data we stored last time we read?
@@ -911,14 +1039,15 @@ int dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_re
where.start = offset;
where.size = len;
- ret = _aligned_io(&where, NULL, 0, reason, ioflags);
+ ret = _aligned_io(&where, NULL, 0, reason, ioflags, dev_read_callback_fn, callback_context);
if (!ret) {
- log_error("Read from %s failed.", dev_name(dev));
+ log_error("Read from %s failed", dev_name(dev));
_dev_inc_error_count(dev);
}
out:
- if (dev_read_callback_fn)
+ /* If we had an error or this was sync I/O, pass the result to any callback fn */
+ if ((!ret || !_aio_ctx || !aio_supported_code_path(ioflags) || cached) && dev_read_callback_fn)
dev_read_callback_fn(!ret, ioflags, callback_context, DEV_DEVBUF_DATA(dev, reason));
return ret;
@@ -936,8 +1065,10 @@ const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_rea
/* Read into supplied retbuf owned by the caller. */
int dev_read_buf(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *retbuf)
{
- if (!dev_read_callback(dev, offset, len, reason, 0, NULL, NULL))
- return_0;
+ if (!dev_read_callback(dev, offset, len, reason, 0, NULL, NULL)) {
+ log_error("Read from %s failed", dev_name(dev));
+ return 0;
+ }
memcpy(retbuf, DEV_DEVBUF_DATA(dev, reason), len);
@@ -1016,7 +1147,7 @@ int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t r
dev->flags |= DEV_ACCESSED_W;
- ret = _aligned_io(&where, buffer, 1, reason, 0);
+ ret = _aligned_io(&where, buffer, 1, reason, 0, NULL, NULL);
if (!ret)
_dev_inc_error_count(dev);
diff --git a/lib/device/device.h b/lib/device/device.h
index 37d2fd2..b52522d 100644
--- a/lib/device/device.h
+++ b/lib/device/device.h
@@ -97,6 +97,9 @@ struct device_buffer {
struct device_area where; /* Location of buf */
dev_io_reason_t reason;
unsigned write:1; /* 1 if write; 0 if read */
+
+ lvm_callback_fn_t dev_read_callback_fn;
+ void *dev_read_callback_context;
};
/*
@@ -203,6 +206,7 @@ void devbufs_release(struct device *dev);
const char *dev_name_confirmed(struct device *dev, int quiet);
struct cmd_context;
+int dev_async_getevents(void);
int dev_async_setup(struct cmd_context *cmd);
void dev_async_exit(void);
int dev_async_reset(struct cmd_context *cmd);
6 years, 2 months
master - device: Basic config and setup to support async I/O.
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=8c7bbcfb0f6771a10ac...
Commit: 8c7bbcfb0f6771a10acd238057fdb7931c3fbb12
Parent: 7a9af3cd0e609e10840202ae1754ab76b6add511
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Jan 22 17:45:12 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Thu Feb 8 20:15:14 2018 +0000
device: Basic config and setup to support async I/O.
---
WHATS_NEW | 1 +
conf/example.conf.in | 5 +
configure | 69 ++++++++++++++
configure.in | 22 +++++
doc/aio_design.txt | 215 ++++++++++++++++++++++++++++++++++++++++++
include/configure.h.in | 3 +
lib/commands/toolcontext.c | 10 ++
lib/commands/toolcontext.h | 1 +
lib/config/config_settings.h | 3 +
lib/config/defaults.h | 1 +
lib/device/dev-cache.c | 2 +
lib/device/dev-io.c | 79 +++++++++++++++
lib/device/device.h | 5 +
make.tmpl.in | 3 +-
tools/toollib.c | 1 +
15 files changed, 419 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 87d7de8..5aa4ed8 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Detect asynchronous I/O capability in configure or accept --disable-aio.
Add AIO_SUPPORTED_CODE_PATH to indicate whether AIO may be used.
Configure ensures /usr/bin dir is checked for dmpd tools.
Restore pvmove support for wide-clustered active volumes (2.02.177).
diff --git a/conf/example.conf.in b/conf/example.conf.in
index aab274d..6a491fb 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -59,6 +59,11 @@ devices {
# This configuration option is advanced.
scan = [ "/dev" ]
+ # Configuration option devices/use_aio.
+ # Use linux asynchronous I/O for parallel device access where possible.
+ # This configuration option has an automatic default value.
+ # use_aio = 1
+
# Configuration option devices/obtain_device_list_from_udev.
# Obtain the list of available devices from udev.
# This avoids opening or using any inapplicable non-block devices or
diff --git a/configure b/configure
index 0a5d225..5409f68 100755
--- a/configure
+++ b/configure
@@ -707,7 +707,9 @@ FSADM
ELDFLAGS
DM_LIB_PATCHLEVEL
DMEVENTD_PATH
+AIO_LIBS
DL_LIBS
+AIO
DEVMAPPER
DEFAULT_USE_LVMLOCKD
DEFAULT_USE_LVMPOLLD
@@ -954,6 +956,7 @@ enable_profiling
enable_testing
enable_valgrind_pool
enable_devmapper
+enable_aio
enable_lvmetad
enable_lvmpolld
enable_lvmlockd_sanlock
@@ -1692,6 +1695,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 asynchronous I/O
--enable-lvmetad enable the LVM Metadata Daemon
--enable-lvmpolld enable the LVM Polling Daemon
--enable-lvmlockd-sanlock
@@ -3179,6 +3183,7 @@ case "$host_os" in
LDDEPS="$LDDEPS .export.sym"
LIB_SUFFIX=so
DEVMAPPER=yes
+ AIO=yes
BUILD_LVMETAD=no
BUILD_LVMPOLLD=no
LOCKDSANLOCK=no
@@ -3198,6 +3203,7 @@ case "$host_os" in
CLDNOWHOLEARCHIVE=
LIB_SUFFIX=dylib
DEVMAPPER=yes
+ AIO=no
ODIRECT=no
DM_IOCTLS=no
SELINUX=no
@@ -11827,6 +11833,67 @@ $as_echo "#define DEVMAPPER_SUPPORT 1" >>confdefs.h
fi
################################################################################
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use asynchronous I/O" >&5
+$as_echo_n "checking whether to asynchronous I/O... " >&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.
@@ -15778,6 +15845,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/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py 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_h
at.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 b8e1623..605b6b2 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
@@ -1123,6 +1125,24 @@ if test "$DEVMAPPER" = yes; then
fi
################################################################################
+dnl -- Disable aio
+AC_MSG_CHECKING(whether to use asynchronous I/O)
+AC_ARG_ENABLE(aio,
+ AC_HELP_STRING([--disable-aio],
+ [disable asynchronous 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,
@@ -2061,9 +2081,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/doc/aio_design.txt b/doc/aio_design.txt
new file mode 100644
index 0000000..c6eb443
--- /dev/null
+++ b/doc/aio_design.txt
@@ -0,0 +1,215 @@
+Introducing asynchronous I/O to LVM
+===================================
+
+Issuing I/O asynchronously means instructing the kernel to perform specific
+I/O and return immediately without waiting for it to complete. The data
+is collected from the kernel later.
+
+Advantages
+----------
+
+A1. While waiting for the I/O to happen, the program could perform other
+operations.
+
+A2. When LVM is searching for its Physical Volumes, it issues a small amount of
+I/O to a large number of disks. If this was issued in parallel the overall
+runtime might be shorter while there should be little effect on the cpu time.
+
+A3. If more than one timeout occurs when accessing any devices, these can be
+taken in parallel, again reducing the runtime. This applies globally,
+not just while the code is searching for Physical Volumes, so reading,
+writing and committing the metadata may occasionally benefit too to some
+extent and there are probably maintenance advantages in using the same
+method of I/O throughout the main body of the code.
+
+A4. By introducing a simple callback function mechanism, the conversion can be
+performed largely incrementally by first refactoring and continuing to
+use synchronous I/O with the callbacks performed immediately. This allows the
+callbacks to be introduced without changing the running sequence of the code
+initially. Future projects could refactor some of the calling sites to
+simplify the code structure and even eliminate some of the nesting.
+This allows each part of what might ultimately amount to a large change to be
+introduced and tested independently.
+
+
+Disadvantages
+-------------
+
+D1. The resulting code may be more complex with more failure modes to
+handle. Mitigate by thorough auditing and testing, rolling out
+gradually, and offering a simple switch to revert to the old behaviour.
+
+D2. The linux asynchronous I/O implementation is less mature than
+its synchronous I/O implementation and might show up problems that
+depend on the version of the kernel or library used. Fixes or
+workarounds for some of these might require kernel changes. For
+example, there are suggestions that despite being supposedly async,
+there are still cases where system calls can block. There might be
+resource dependencies on other processes running on the system that make
+it unsuitable for use while any devices are suspended. Mitigation
+as for D1.
+
+D3. The error handling within callbacks becomes more complicated.
+However we know that existing call paths can already sometimes discard
+errors, sometimes deliberately, sometimes not, so this aspect is in need
+of a complete review anyway and the new approach will make the error
+handling more transparent. Aim initially for overall behaviour that is
+no worse than that of the existing code, then work on improving it
+later.
+
+D4. The work will take a few weeks to code and test. This leads to a
+significant opportunity cost when compared against other enhancements
+that could be achieved in that time. However, the proof-of-concept work
+performed while writing this design has satisfied me that the work could
+proceed and be committed incrementally as a background task.
+
+
+Observations regarding LVM's I/O Architecture
+---------------------------------------------
+
+H1. All device, metadata and config file I/O is constrained to pass through a
+single route in lib/device.
+
+H2. The first step of the analysis was to instrument this code path with
+log_debug messages. I/O is split into the following categories:
+
+ "dev signatures",
+ "PV labels",
+ "VG metadata header",
+ "VG metadata content",
+ "extra VG metadata header",
+ "extra VG metadata content",
+ "LVM1 metadata",
+ "pool metadata",
+ "LV content",
+ "logging",
+
+H3. A bounce buffer is used for most I/O.
+
+H4. Most callers finish using the supplied data before any further I/O is
+issued. The few that don't could be converted trivially to do so.
+
+H5. There is one stream of I/O per metadata area on each device.
+
+H6. Some reads fall at offsets close to immediately preceding reads, so it's
+possible to avoid these by caching one "block" per metadata area I/O stream.
+
+H7. Simple analysis suggests a minimum aligned read size of 8k would deliver
+immediate gains from this caching. A larger size might perform worse because
+almost all the time the extra data read would not be used, but this can be
+re-examined and tuned after the code is in place.
+
+
+Proposal
+--------
+
+P1. Retain the "single I/O path" but offer an asynchronous option.
+
+P2. Eliminate the bounce buffer in most cases by improving alignment.
+
+P3. Reduce the number of reads by always reading a minimum of an aligned
+8k block.
+
+P4. Eliminate repeated reads by caching the last block read and changing
+the lib/device interface to return a pointer to read-only data within
+this block.
+
+P5. Only perform these interface changes for code on the critical path
+for now by converting other code sites to use wrappers around the new
+interface.
+
+P6. Treat asynchronous I/O as the interface of choice and optimise only
+for this case.
+
+P7. Convert the callers on the critical path to pass callback functions
+to the device layer. These functions will be called later with the
+read-only data, a context pointer and a success/failure indicator.
+Where an existing function performs a sequence of I/O, this has the
+advantage of breaking up the large function into smaller ones and
+wrapping the parameters used into structures. While this might look
+rather messy and ad-hoc in the short-term, it's a first step towards
+breaking up confusingly long functions into component parts and wrapping
+the existing long parameter lists into more appropriate structures and
+refactoring these parts of the code.
+
+P8. Limit the resources used by the asynchronous I/O by using two
+tunable parameters, one limiting the number of outstanding I/Os issued
+and another limiting the total amount of memory used.
+
+P9. Provide a fallback option if asynchronous I/O is unavailable by
+sharing the code paths but issuing the I/O synchronously and calling the
+callback immediately.
+
+P10. Only allocate the buffer for the I/O at the point where the I/O is
+about to be issued.
+
+P11. If the thresholds are exceeded, add the request to a simple queue,
+and process it later after some I/O has completed.
+
+
+Future work
+-----------
+F1. Perform a complete review of the error tracking so that device
+failures are handled and reported more cleanly, extending the existing
+basic error counting mechanism.
+
+F2. Consider whether some of the nested callbacks can be eliminated,
+which would allow for additional simplifications.
+
+F3. Adjust the contents of the adhoc context structs into more logical
+arrangements and use them more widely.
+
+F4. Perform wider refactoring of these areas of code.
+
+
+Testing considerations
+----------------------
+T1. The changes touch code on the device path, so a thorough re-test of
+the device layer is required. The new code needs a full audit down
+through the library layer into the kernel to check that all the error
+conditions that are currently implemented (such as EAGAIN) are handled
+sensibly. (LVM's I/O layer needs to remain as solid as we can make it.)
+
+T2. The current test suite provides a reasonably broad range of coverage
+of this area but is far from comprehensive.
+
+
+Acceptance criteria
+-------------------
+A1. The current test suite should pass to the same extent as before the
+changes.
+
+A2. When all debugging and logging is disabled, strace -c must show
+improvements e.g. the expected fewer number of reads.
+
+A3. Running a range of commands under valgrind must not reveal any
+new leaks due to the changes.
+
+A4. All new coverity reports from the change must be addressed.
+
+A5. CPU time should be similar to that before, as the same work
+is being done overall, just in a different order.
+
+A6. Tests need to show improved behaviour in targetted areas. For example,
+if several devices are slow and time out, the delays should occur
+in parallel and the elapsed time should be less than before.
+
+
+Release considerations
+----------------------
+R1. Async I/O should be widely available and largely reliable on linux
+nowadays (even though parts of its interface and implementation remain a
+matter of controversy) so we should try to make its use the default
+whereever it is supported. If certain types of systems have problems we
+should try to detect those cases and disable it automatically there.
+
+R2. Because the implications of an unexpected problem in the new code
+could be severe for the people affected, the roll out needs to be gentle
+without a deadline to allow us plenty of time to gain confidence in the
+new code. Our own testing will only be able to cover a tiny fraction of
+the different setups our users have, so we need to look out for problems
+caused by this proactively and encourage people to test it on their own
+systems and report back. It must go into the tree near the start of a
+release cycle rather than at the end to provide time for our confidence
+in it to grow.
+
diff --git a/include/configure.h.in b/include/configure.h.in
index be2f660..a491807 100644
--- a/include/configure.h.in
+++ b/include/configure.h.in
@@ -1,5 +1,8 @@
/* include/configure.h.in. Generated from configure.in by autoheader. */
+/* Define to 1 if aio is available. */
+#undef AIO_SUPPORT
+
/* Define to 1 to use libblkid detection of signatures when wiping. */
#undef BLKID_WIPING_SUPPORT
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 364587c..0080a68 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -635,6 +635,16 @@ static int _process_config(struct cmd_context *cmd)
*/
cmd->default_settings.udev_fallback = udev_disabled ? 1 : -1;
+#ifdef AIO_SUPPORT
+ cmd->use_aio = find_config_tree_bool(cmd, devices_use_aio_CFG, NULL);
+#else
+ cmd->use_aio = 0;
+#endif
+ if (cmd->use_aio && !dev_async_setup(cmd))
+ cmd->use_aio = 0;
+
+ log_debug_io("%ssing asynchronous I/O.", cmd->use_aio ? "U" : "Not u");
+
init_retry_deactivation(find_config_tree_bool(cmd, activation_retry_deactivation_CFG, NULL));
init_activation_checks(find_config_tree_bool(cmd, activation_checks_CFG, NULL));
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index bf2b251..4419daa 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -164,6 +164,7 @@ struct cmd_context {
unsigned vg_notify:1;
unsigned lv_notify:1;
unsigned pv_notify:1;
+ unsigned use_aio:1;
/*
* Filtering.
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 077fb15..523e6fe 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -226,6 +226,9 @@ cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING,
cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, "#S/dev", vsn(1, 0, 0), NULL, 0, NULL,
"Directories containing device nodes to use with LVM.\n")
+cfg(devices_use_aio_CFG, "use_aio", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_USE_AIO, vsn(2, 2, 178), NULL, 0, NULL,
+ "Use linux asynchronous I/O for parallel device access where possible.\n")
+
cfg_array(devices_loopfiles_CFG, "loopfiles", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 2, 0), NULL, 0, NULL, NULL)
cfg(devices_obtain_device_list_from_udev_CFG, "obtain_device_list_from_udev", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV, vsn(2, 2, 85), NULL, 0, NULL,
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index d9e19d9..5efab11 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -32,6 +32,7 @@
#define DEFAULT_SYSTEM_ID_SOURCE "none"
#define DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV 1
#define DEFAULT_EXTERNAL_DEVICE_INFO_SOURCE "none"
+#define DEFAULT_USE_AIO 1
#define DEFAULT_SYSFS_SCAN 1
#define DEFAULT_MD_COMPONENT_DETECTION 1
#define DEFAULT_FW_RAID_COMPONENT_DETECTION 0
diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
index 3d57735..2f74c81 100644
--- a/lib/device/dev-cache.c
+++ b/lib/device/dev-cache.c
@@ -1250,6 +1250,8 @@ int dev_cache_exit(void)
struct btree_iter *b;
int num_open = 0;
+ dev_async_exit();
+
if (_cache.names)
if ((num_open = _check_for_open_devices(1)) > 0)
log_error(INTERNAL_ERROR "%d device(s) were left open and have been closed.", num_open);
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
index d23f61e..48f5b8d 100644
--- a/lib/device/dev-io.c
+++ b/lib/device/dev-io.c
@@ -98,6 +98,85 @@ void devbufs_release(struct device *dev)
_release_devbuf(&dev->last_extra_devbuf);
}
+#ifdef AIO_SUPPORT
+
+# include <libaio.h>
+
+static io_context_t _aio_ctx = 0;
+static int _aio_max = 128;
+
+int dev_async_setup(struct cmd_context *cmd)
+{
+ int r;
+
+ /* Already set up? */
+ if (_aio_ctx)
+ return 1;
+
+ log_debug_io("Setting up aio context for up to %d events.", _aio_max);
+
+ if ((r = io_setup(_aio_max, &_aio_ctx)) < 0) {
+ /*
+ * Possible errors:
+ * ENOSYS - aio not available in current kernel
+ * EAGAIN - _aio_max is too big
+ * EFAULT - invalid pointer
+ * EINVAL - _aio_ctx != 0 or kernel aio limits exceeded
+ * ENOMEM
+ */
+ log_warn("WARNING: Asynchronous I/O setup for %d events failed: %s", _aio_max, strerror(-r));
+ log_warn("WARNING: Using only synchronous I/O.");
+ _aio_ctx = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Reset aio context after fork */
+int dev_async_reset(struct cmd_context *cmd)
+{
+ log_debug_io("Resetting asynchronous I/O context.");
+ _aio_ctx = 0;
+
+ return dev_async_setup(cmd);
+}
+
+void dev_async_exit(void)
+{
+ int r;
+
+ if (!_aio_ctx)
+ return;
+
+ log_debug_io("Destroying aio context.");
+ if ((r = io_destroy(_aio_ctx)) < 0)
+ /* Returns -ENOSYS if aio not in kernel or -EINVAL if _aio_ctx invalid */
+ log_error("Failed to destroy asynchronous I/O context: %s", strerror(-r));
+
+ _aio_ctx = 0;
+}
+
+#else
+
+static int _aio_ctx = 0;
+
+int dev_async_setup(struct cmd_context *cmd)
+{
+ return 1;
+}
+
+int dev_async_reset(struct cmd_context *cmd)
+{
+ return 1;
+}
+
+void dev_async_exit(void)
+{
+}
+
+#endif /* AIO_SUPPORT */
+
/*-----------------------------------------------------------------
* The standard io loop that keeps submitting an io until it's
* all gone.
diff --git a/lib/device/device.h b/lib/device/device.h
index 2dea689..37d2fd2 100644
--- a/lib/device/device.h
+++ b/lib/device/device.h
@@ -202,4 +202,9 @@ void devbufs_release(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 cmd_context;
+int dev_async_setup(struct cmd_context *cmd);
+void dev_async_exit(void);
+int dev_async_reset(struct cmd_context *cmd);
+
#endif
diff --git a/make.tmpl.in b/make.tmpl.in
index bdf2349..414b1dd 100644
--- a/make.tmpl.in
+++ b/make.tmpl.in
@@ -62,7 +62,8 @@ CLDFLAGS += @CLDFLAGS@
ELDFLAGS += @ELDFLAGS@
LDDEPS += @LDDEPS@
LIB_SUFFIX = @LIB_SUFFIX@
-LVMINTERNAL_LIBS = -llvm-internal $(DMEVENT_LIBS) $(DAEMON_LIBS) $(SYSTEMD_LIBS) $(UDEV_LIBS) $(DL_LIBS) $(BLKID_LIBS)
+LVMINTERNAL_LIBS = -llvm-internal $(DMEVENT_LIBS) $(DAEMON_LIBS) $(SYSTEMD_LIBS) $(UDEV_LIBS) $(DL_LIBS) $(BLKID_LIBS) $(AIO_LIBS)
+AIO_LIBS = @AIO_LIBS@
DL_LIBS = @DL_LIBS@
RT_LIBS = @RT_LIBS@
M_LIBS = @M_LIBS@
diff --git a/tools/toollib.c b/tools/toollib.c
index 0509b5e..0958f2f 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -116,6 +116,7 @@ int become_daemon(struct cmd_context *cmd, int skip_lvm)
/* FIXME Clean up properly here */
_exit(ECMD_FAILED);
}
+ dev_async_reset(cmd);
dev_close_all();
return 1;
6 years, 2 months
master - device: Add flag to indicate that a code path can support AIO
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=7a9af3cd0e609e10840...
Commit: 7a9af3cd0e609e10840202ae1754ab76b6add511
Parent: e869a52cc4e0a65f00c8d3f40cf94b6989e60871
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Jan 22 16:56:25 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Tue Feb 6 01:11:00 2018 +0000
device: Add flag to indicate that a code path can support AIO
Until the whole source supports AIO, library code can check for
AIO_SUPPORTED_CODE_PATH to determine whether or not it is OK
to use AIO.
---
WHATS_NEW | 1 +
lib/cache/lvmcache.c | 2 +-
lib/device/device.h | 5 +++++
3 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 911a6e3..87d7de8 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Add AIO_SUPPORTED_CODE_PATH to indicate whether AIO may be used.
Configure ensures /usr/bin dir is checked for dmpd tools.
Restore pvmove support for wide-clustered active volumes (2.02.177).
Avoid non-exclusive activation of exclusive segment types.
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 65728fd..2eaf479 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1156,7 +1156,7 @@ int lvmcache_label_scan(struct cmd_context *cmd)
while ((dev = dev_iter_get(iter))) {
nr_labels_outstanding++;
- if (!label_read_callback(dev, UINT64_C(0), 0, _process_label_data, &nr_labels_outstanding))
+ if (!label_read_callback(dev, UINT64_C(0), AIO_SUPPORTED_CODE_PATH, _process_label_data, &nr_labels_outstanding))
nr_labels_outstanding--;
dev_count++;
}
diff --git a/lib/device/device.h b/lib/device/device.h
index d5b076f..2dea689 100644
--- a/lib/device/device.h
+++ b/lib/device/device.h
@@ -32,6 +32,11 @@
#define DEV_ASSUMED_FOR_LV 0x00000200 /* Is device assumed for an LV */
#define DEV_NOT_O_NOATIME 0x00000400 /* Don't use O_NOATIME */
+/* ioflags */
+#define AIO_SUPPORTED_CODE_PATH 0x00000001 /* Set if the code path supports AIO */
+
+#define aio_supported_code_path(ioflags) (((ioflags) & AIO_SUPPORTED_CODE_PATH) ? 1 : 0)
+
/*
* Standard format for callback functions.
* When provided, callback functions are called exactly once.
6 years, 2 months
master - callbacks: Miscellaneous fixes for recent changes
by Alasdair Kergon
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e869a52cc4e0a65f00c...
Commit: e869a52cc4e0a65f00c8d3f40cf94b6989e60871
Parent: e727da6cf111b07b302c08ed479336e4994635b0
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Jan 22 15:41:11 2018 +0000
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Tue Feb 6 01:09:39 2018 +0000
callbacks: Miscellaneous fixes for recent changes
---
lib/device/dev-io.c | 2 +-
lib/format1/format1.c | 2 +-
lib/format1/lvm1-label.c | 2 +-
lib/format_pool/format_pool.c | 2 +-
lib/format_text/text_label.c | 2 +-
lib/label/label.c | 7 +++++--
6 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
index 3af4236..d23f61e 100644
--- a/lib/device/dev-io.c
+++ b/lib/device/dev-io.c
@@ -154,7 +154,7 @@ static int _io(struct device_buffer *devbuf, unsigned ioflags)
}
if (!devbuf->buf && !(devbuf->malloc_address = devbuf->buf = dm_malloc_aligned((size_t) devbuf->where.size, 0))) {
- log_error("Bounce buffer malloc failed");
+ log_error("I/O buffer malloc failed");
return 0;
}
diff --git a/lib/format1/format1.c b/lib/format1/format1.c
index b3569e0..7d84e69 100644
--- a/lib/format1/format1.c
+++ b/lib/format1/format1.c
@@ -182,7 +182,7 @@ static struct volume_group *_format1_vg_read(struct format_instance *fid,
struct metadata_area *mda __attribute__((unused)),
struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)),
unsigned *use_previous_vg __attribute__((unused)),
- int single_device __attribute__((unused)))
+ int single_device __attribute__((unused)), unsigned ioflags)
{
struct volume_group *vg;
struct disk_list *dl;
diff --git a/lib/format1/lvm1-label.c b/lib/format1/lvm1-label.c
index 5419d92..d334fdc 100644
--- a/lib/format1/lvm1-label.c
+++ b/lib/format1/lvm1-label.c
@@ -60,7 +60,7 @@ static int _lvm1_read(struct labeller *l, struct device *dev, void *buf, unsigne
struct pv_disk *pvd = (struct pv_disk *) buf;
struct vg_disk vgd;
struct lvmcache_info *info;
- struct label *label;
+ struct label *label = NULL;
const char *vgid = FMT_LVM1_ORPHAN_VG_NAME;
const char *vgname = FMT_LVM1_ORPHAN_VG_NAME;
unsigned exported = 0;
diff --git a/lib/format_pool/format_pool.c b/lib/format_pool/format_pool.c
index 1f257a2..c699058 100644
--- a/lib/format_pool/format_pool.c
+++ b/lib/format_pool/format_pool.c
@@ -103,7 +103,7 @@ static struct volume_group *_pool_vg_read(struct format_instance *fid,
struct metadata_area *mda __attribute__((unused)),
struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)),
unsigned *use_previous_vg __attribute__((unused)),
- int single_device __attribute__((unused)), int ioflags)
+ int single_device __attribute__((unused)), unsigned ioflags)
{
struct volume_group *vg;
struct user_subpool *usp;
diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c
index 49f4015..d3d92f0 100644
--- a/lib/format_text/text_label.c
+++ b/lib/format_text/text_label.c
@@ -551,7 +551,7 @@ out:
bad:
if (read_label_callback_fn)
- read_label_callback_fn(1, ioflags, read_label_callback_context, label);
+ read_label_callback_fn(1, ioflags, read_label_callback_context, NULL);
return 0;
}
diff --git a/lib/label/label.c b/lib/label/label.c
index d293caf..0997b96 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -156,7 +156,7 @@ out:
stack;
if (flp->process_label_data_fn)
- flp->process_label_data_fn(0, ioflags, flp->process_label_data_context, NULL);
+ flp->process_label_data_fn(!flp->ret, ioflags, flp->process_label_data_context, NULL);
}
static void _find_labeller(int failed, unsigned ioflags, void *context, const void *data)
@@ -362,7 +362,10 @@ static int _label_read(struct device *dev, uint64_t scan_sector, struct label **
return 0;
}
- return flp->ret;
+ if (process_label_data_fn)
+ return 1;
+ else
+ return flp->ret;
}
/* result may be NULL if caller doesn't need it */
6 years, 2 months
master - configure: ensure path /usr/sbin is checked for some tools
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e727da6cf111b07b302...
Commit: e727da6cf111b07b302c08ed479336e4994635b0
Parent: 4d4d5bf3234e90fbef233a376d6306e08ac6bf8d
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Mon Jan 29 16:20:53 2018 +0100
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu Feb 1 21:57:05 2018 +0100
configure: ensure path /usr/sbin is checked for some tools
Some tools are typically installed into /usr/sbin (or /sbin) dir.
And some systems do not add this path to user's $PATH var.
Ensure sbin paths are looked through...
---
WHATS_NEW | 1 +
configure | 37 +++++++++++++++++++------------------
configure.in | 19 ++++++++++---------
3 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 75a147b..911a6e3 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Configure ensures /usr/bin dir is checked for dmpd tools.
Restore pvmove support for wide-clustered active volumes (2.02.177).
Avoid non-exclusive activation of exclusive segment types.
Fix trimming sibling PVs when doing a pvmove of raid subLVs.
diff --git a/configure b/configure
index d04b9d3..0a5d225 100755
--- a/configure
+++ b/configure
@@ -4369,6 +4369,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
CFLAGS=$save_CFLAGS
CXXFLAGS=$save_CXXFLAGS
+PATH_SBIN="$PATH:/usr/sbin:/sbin"
ac_ext=c
@@ -8672,7 +8673,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -8715,7 +8716,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -8796,7 +8797,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -8839,7 +8840,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -8903,7 +8904,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -8946,7 +8947,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9010,7 +9011,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9053,7 +9054,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9221,7 +9222,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9264,7 +9265,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9357,7 +9358,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9400,7 +9401,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9464,7 +9465,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9507,7 +9508,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9571,7 +9572,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -9614,7 +9615,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -15310,7 +15311,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
@@ -15353,7 +15354,7 @@ else
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
+for as_dir in $PATH_SBIN
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
diff --git a/configure.in b/configure.in
index d879408..b8e1623 100644
--- a/configure.in
+++ b/configure.in
@@ -77,6 +77,7 @@ AC_PROG_CC
AC_PROG_CXX
CFLAGS=$save_CFLAGS
CXXFLAGS=$save_CXXFLAGS
+PATH_SBIN="$PATH:/usr/sbin:/sbin"
dnl probably no longer needed in 2008, but...
AC_PROG_GCC_TRADITIONAL
@@ -468,7 +469,7 @@ case "$THIN" in
internal|shared)
# Empty means a config way to ignore thin checking
if test "$THIN_CHECK_CMD" = "autodetect"; then
- AC_PATH_TOOL(THIN_CHECK_CMD, thin_check)
+ AC_PATH_TOOL(THIN_CHECK_CMD, thin_check, [], [$PATH_SBIN])
if test -z "$THIN_CHECK_CMD"; then
AC_MSG_WARN([thin_check not found in path $PATH])
THIN_CHECK_CMD=/usr/sbin/thin_check
@@ -492,7 +493,7 @@ case "$THIN" in
fi
# Empty means a config way to ignore thin dumping
if test "$THIN_DUMP_CMD" = "autodetect"; then
- AC_PATH_TOOL(THIN_DUMP_CMD, thin_dump)
+ AC_PATH_TOOL(THIN_DUMP_CMD, thin_dump, [], [$PATH_SBIN])
test -z "$THIN_DUMP_CMD" && {
AC_MSG_WARN(thin_dump not found in path $PATH)
THIN_DUMP_CMD=/usr/sbin/thin_dump
@@ -501,7 +502,7 @@ case "$THIN" in
fi
# Empty means a config way to ignore thin repairing
if test "$THIN_REPAIR_CMD" = "autodetect"; then
- AC_PATH_TOOL(THIN_REPAIR_CMD, thin_repair)
+ AC_PATH_TOOL(THIN_REPAIR_CMD, thin_repair, [], [$PATH_SBIN])
test -z "$THIN_REPAIR_CMD" && {
AC_MSG_WARN(thin_repair not found in path $PATH)
THIN_REPAIR_CMD=/usr/sbin/thin_repair
@@ -510,7 +511,7 @@ case "$THIN" in
fi
# Empty means a config way to ignore thin restoring
if test "$THIN_RESTORE_CMD" = "autodetect"; then
- AC_PATH_TOOL(THIN_RESTORE_CMD, thin_restore)
+ AC_PATH_TOOL(THIN_RESTORE_CMD, thin_restore, [], [$PATH_SBIN])
test -z "$THIN_RESTORE_CMD" && {
AC_MSG_WARN(thin_restore not found in path $PATH)
THIN_RESTORE_CMD=/usr/sbin/thin_restore
@@ -582,7 +583,7 @@ case "$CACHE" in
internal|shared)
# Empty means a config way to ignore cache checking
if test "$CACHE_CHECK_CMD" = "autodetect"; then
- AC_PATH_TOOL(CACHE_CHECK_CMD, cache_check)
+ AC_PATH_TOOL(CACHE_CHECK_CMD, cache_check, [], [$PATH_SBIN])
if test -z "$CACHE_CHECK_CMD"; then
AC_MSG_WARN([cache_check not found in path $PATH])
CACHE_CHECK_CMD=/usr/sbin/cache_check
@@ -617,7 +618,7 @@ case "$CACHE" in
fi
# Empty means a config way to ignore cache dumping
if test "$CACHE_DUMP_CMD" = "autodetect"; then
- AC_PATH_TOOL(CACHE_DUMP_CMD, cache_dump)
+ AC_PATH_TOOL(CACHE_DUMP_CMD, cache_dump, [], [$PATH_SBIN])
test -z "$CACHE_DUMP_CMD" && {
AC_MSG_WARN(cache_dump not found in path $PATH)
CACHE_DUMP_CMD=/usr/sbin/cache_dump
@@ -626,7 +627,7 @@ case "$CACHE" in
fi
# Empty means a config way to ignore cache repairing
if test "$CACHE_REPAIR_CMD" = "autodetect"; then
- AC_PATH_TOOL(CACHE_REPAIR_CMD, cache_repair)
+ AC_PATH_TOOL(CACHE_REPAIR_CMD, cache_repair, [], [$PATH_SBIN])
test -z "$CACHE_REPAIR_CMD" && {
AC_MSG_WARN(cache_repair not found in path $PATH)
CACHE_REPAIR_CMD=/usr/sbin/cache_repair
@@ -635,7 +636,7 @@ case "$CACHE" in
fi
# Empty means a config way to ignore cache restoring
if test "$CACHE_RESTORE_CMD" = "autodetect"; then
- AC_PATH_TOOL(CACHE_RESTORE_CMD, cache_restore)
+ AC_PATH_TOOL(CACHE_RESTORE_CMD, cache_restore, [], [$PATH_SBIN])
test -z "$CACHE_RESTORE_CMD" && {
AC_MSG_WARN(cache_restore not found in path $PATH)
CACHE_RESTORE_CMD=/usr/sbin/cache_restore
@@ -1870,7 +1871,7 @@ if test "$BUILD_DMFILEMAPD" = yes; then
fi
################################################################################
-AC_PATH_TOOL(MODPROBE_CMD, modprobe)
+AC_PATH_TOOL(MODPROBE_CMD, modprobe, [], [$PATH_SBIN])
if test -n "$MODPROBE_CMD"; then
AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.])
6 years, 2 months