summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlasdair G Kergon <agk@redhat.com>2018-01-10 13:19:12 +0000
committerAlasdair G Kergon <agk@redhat.com>2018-01-10 15:52:03 +0000
commit366493a1d1900757f4a203d3b89185afb878ad30 (patch)
treefe748b9ccd4cae787be33098eabc6c0922858731
parentdcb2a5a6116ed4853933e1a72e185a8109aab9cc (diff)
downloadlvm2-366493a1d1900757f4a203d3b89185afb878ad30.tar.gz
device: Suppress repeated reads of the same data.
If the data being requested is present in last_[extra_]devbuf, return that directly instead of reading it from disk again. Typical LVM2 access patterns request data within two adjacent 4k blocks so we eliminate some read() system calls by always reading at least 8k.
-rw-r--r--WHATS_NEW1
-rw-r--r--lib/device/dev-io.c33
2 files changed, 32 insertions, 2 deletions
diff --git a/WHATS_NEW b/WHATS_NEW
index 897576c69..489c31530 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.178 -
=====================================
+ Suppress some repeated reads of the same disk data at the device layer.
Avoid exceeding array bounds in allocation tag processing.
Refactor metadata reading code to use callback functions.
Move memory allocation for the key dev_reads into the device layer.
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
index 30e4235aa..bacc7deff 100644
--- a/lib/device/dev-io.c
+++ b/lib/device/dev-io.c
@@ -53,6 +53,12 @@
# endif
#endif
+/*
+ * Always read at least 8k from disk.
+ * This seems to be a good compromise for the existing LVM2 metadata layout.
+ */
+#define MIN_READ_SIZE (8 * 1024)
+
static DM_LIST_INIT(_open_devices);
static unsigned _dev_size_seqno = 1;
@@ -273,6 +279,11 @@ static int _aligned_io(struct device_area *where, char *buffer,
if (!block_size)
block_size = lvm_getpagesize();
+
+ /* Apply minimum read size */
+ if (!should_write && block_size < MIN_READ_SIZE)
+ block_size = MIN_READ_SIZE;
+
mask = block_size - 1;
_widen_region(block_size, where, &widened);
@@ -785,14 +796,32 @@ static void _dev_inc_error_count(struct device *dev)
static int _dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason)
{
struct device_area where;
+ struct device_buffer *devbuf;
+ uint64_t buf_end;
int ret;
- if (!dev->open_count)
- return_0;
+ if (!dev->open_count) {
+ log_error(INTERNAL_ERROR "Attempt to access device %s while closed.", dev_name(dev));
+ return 0;
+ }
if (!_dev_is_valid(dev))
return 0;
+ /*
+ * Can we satisfy this from data we stored last time we read?
+ */
+ if ((devbuf = DEV_DEVBUF(dev, reason)) && devbuf->malloc_address) {
+ buf_end = devbuf->where.start + devbuf->where.size - 1;
+ if (offset >= devbuf->where.start && offset <= buf_end && offset + len - 1 <= buf_end) {
+ /* Reuse this buffer */
+ devbuf->data = (char *) devbuf->buf + (offset - devbuf->where.start);
+ log_debug_io("Cached read for %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)",
+ len, (uint64_t) offset, dev_name(dev), _reason_text(reason));
+ return 1;
+ }
+ }
+
where.dev = dev;
where.start = offset;
where.size = len;