[lvm-devel] master - device: Queue any aio beyond defined limits.

Alasdair Kergon agk at sourceware.org
Fri Feb 9 01:32:24 UTC 2018


Gitweb:        https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=3e29c80122b8eb1123e42d143f17dd7bddefedcd
Commit:        3e29c80122b8eb1123e42d143f17dd7bddefedcd
Parent:        db41fe6c5dab7ff66db9c0568f0e1e1b31657be3
Author:        Alasdair G Kergon <agk at redhat.com>
AuthorDate:    Mon Jan 22 18:26:03 2018 +0000
Committer:     Alasdair G Kergon <agk at 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 */
 };
 
 /*




More information about the lvm-devel mailing list