1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

device: Keep the last data buffer read off each device.

If there's a second metadata area on device, we record that separately.

Note that the memory requirements aren't restricted yet.
This commit is contained in:
Alasdair G Kergon 2018-01-10 02:09:06 +00:00
parent bacc942333
commit bd0967a4b1
3 changed files with 137 additions and 60 deletions

View File

@ -1247,12 +1247,22 @@ int dev_cache_check_for_open_devices(void)
int dev_cache_exit(void) int dev_cache_exit(void)
{ {
struct btree_iter *b;
int num_open = 0; int num_open = 0;
if (_cache.names) if (_cache.names)
if ((num_open = _check_for_open_devices(1)) > 0) 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); log_error(INTERNAL_ERROR "%d device(s) were left open and have been closed.", num_open);
if (_cache.devices) {
/* FIXME Replace with structured devbuf cache */
b = btree_first(_cache.devices);
while (b) {
devbufs_release(btree_get_data(b));
b = btree_next(b);
}
}
if (_cache.mem) if (_cache.mem)
dm_pool_destroy(_cache.mem); dm_pool_destroy(_cache.mem);

View File

@ -74,38 +74,33 @@ static const char *_reason_text(dev_io_reason_t reason)
return _reasons[(unsigned) reason]; return _reasons[(unsigned) reason];
} }
/*
* Release the memory holding the last data we read
*/
static void _release_devbuf(struct device_buffer *devbuf)
{
dm_free(devbuf->malloc_address);
devbuf->malloc_address = NULL;
}
void devbufs_release(struct device *dev)
{
_release_devbuf(&dev->last_devbuf);
_release_devbuf(&dev->last_extra_devbuf);
}
/*----------------------------------------------------------------- /*-----------------------------------------------------------------
* The standard io loop that keeps submitting an io until it's * The standard io loop that keeps submitting an io until it's
* all gone. * all gone.
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
static int _io(struct device_area *where, char *buffer, int should_write, dev_io_reason_t reason) static int _io_sync(struct device_buffer *devbuf)
{ {
struct device_area *where = &devbuf->where;
int fd = dev_fd(where->dev); int fd = dev_fd(where->dev);
char *buffer = devbuf->buf;
ssize_t n = 0; ssize_t n = 0;
size_t total = 0; size_t total = 0;
if (fd < 0) {
log_error("Attempt to read an unopened device (%s).",
dev_name(where->dev));
return 0;
}
log_debug_io("%s %s:%8" PRIu64 " bytes (sync) at %" PRIu64 "%s (for %s)",
should_write ? "Write" : "Read ", dev_name(where->dev),
where->size, (uint64_t) where->start,
(should_write && test_mode()) ? " (test mode - suppressed)" : "", _reason_text(reason));
/*
* Skip all writes in test mode.
*/
if (should_write && test_mode())
return 1;
if (where->size > SSIZE_MAX) {
log_error("Read size too large: %" PRIu64, where->size);
return 0;
}
if (lseek(fd, (off_t) where->start, SEEK_SET) == (off_t) -1) { if (lseek(fd, (off_t) where->start, SEEK_SET) == (off_t) -1) {
log_error("%s: lseek %" PRIu64 " failed: %s", log_error("%s: lseek %" PRIu64 " failed: %s",
dev_name(where->dev), (uint64_t) where->start, dev_name(where->dev), (uint64_t) where->start,
@ -115,7 +110,7 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io
while (total < (size_t) where->size) { while (total < (size_t) where->size) {
do do
n = should_write ? n = devbuf->write ?
write(fd, buffer, (size_t) where->size - total) : write(fd, buffer, (size_t) where->size - total) :
read(fd, buffer, (size_t) where->size - total); read(fd, buffer, (size_t) where->size - total);
while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN))); while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
@ -123,7 +118,7 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io
if (n < 0) if (n < 0)
log_error_once("%s: %s failed after %" PRIu64 " of %" PRIu64 log_error_once("%s: %s failed after %" PRIu64 " of %" PRIu64
" at %" PRIu64 ": %s", dev_name(where->dev), " at %" PRIu64 ": %s", dev_name(where->dev),
should_write ? "write" : "read", devbuf->write ? "write" : "read",
(uint64_t) total, (uint64_t) total,
(uint64_t) where->size, (uint64_t) where->size,
(uint64_t) where->start, strerror(errno)); (uint64_t) where->start, strerror(errno));
@ -138,6 +133,36 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io
return (total == (size_t) where->size); return (total == (size_t) where->size);
} }
static int _io(struct device_buffer *devbuf, dev_io_reason_t reason)
{
struct device_area *where = &devbuf->where;
int fd = dev_fd(where->dev);
if (fd < 0) {
log_error("Attempt to read an unopened device (%s).",
dev_name(where->dev));
return 0;
}
log_debug_io("%s %s(fd %d):%8" PRIu64 " bytes (sync) at %" PRIu64 "%s (for %s)",
devbuf->write ? "Write" : "Read ", dev_name(where->dev), fd,
where->size, (uint64_t) where->start,
(devbuf->write && test_mode()) ? " (test mode - suppressed)" : "", _reason_text(reason));
/*
* Skip all writes in test mode.
*/
if (devbuf->write && test_mode())
return 1;
if (where->size > SSIZE_MAX) {
log_error("Read size too large: %" PRIu64, where->size);
return 0;
}
return _io_sync(devbuf);
}
/*----------------------------------------------------------------- /*-----------------------------------------------------------------
* LVM2 uses O_DIRECT when performing metadata io, which requires * LVM2 uses O_DIRECT when performing metadata io, which requires
* block size aligned accesses. If any io is not aligned we have * block size aligned accesses. If any io is not aligned we have
@ -230,12 +255,13 @@ static void _widen_region(unsigned int block_size, struct device_area *region,
static int _aligned_io(struct device_area *where, char *buffer, static int _aligned_io(struct device_area *where, char *buffer,
int should_write, dev_io_reason_t reason) int should_write, dev_io_reason_t reason)
{ {
char *bounce, *bounce_buf; char *bounce, *bounce_buf = NULL;
unsigned int physical_block_size = 0; unsigned int physical_block_size = 0;
unsigned int block_size = 0; unsigned int block_size = 0;
unsigned buffer_was_widened = 0; unsigned buffer_was_widened = 0;
uintptr_t mask; uintptr_t mask;
struct device_area widened; struct device_area widened;
struct device_buffer *devbuf;
int r = 0; int r = 0;
if (!(where->dev->flags & DEV_REGULAR) && if (!(where->dev->flags & DEV_REGULAR) &&
@ -253,17 +279,18 @@ static int _aligned_io(struct device_area *where, char *buffer,
buffer_was_widened = 1; buffer_was_widened = 1;
log_debug_io("Widening request for %" PRIu64 " bytes at %" PRIu64 " to %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)", log_debug_io("Widening request for %" PRIu64 " bytes at %" PRIu64 " to %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)",
where->size, (uint64_t) where->start, widened.size, (uint64_t) widened.start, dev_name(where->dev), _reason_text(reason)); where->size, (uint64_t) where->start, widened.size, (uint64_t) widened.start, dev_name(where->dev), _reason_text(reason));
} else if (!((uintptr_t) buffer & mask)) }
/* Perform the I/O directly. */
return _io(where, buffer, should_write, reason);
if (!buffer_was_widened && !((uintptr_t) buffer & mask))
/* Perform the I/O directly. */
bounce = buffer;
#ifndef DEBUG_MEM #ifndef DEBUG_MEM
/* Allocate a bounce buffer with an extra block */ else if (!(bounce_buf = bounce = dm_malloc_aligned((size_t) widened.size, 0))) {
if (!(bounce_buf = bounce = dm_malloc_aligned((size_t) widened.size, 0))) {
log_error("Bounce buffer malloc failed"); log_error("Bounce buffer malloc failed");
return 0; return 0;
} }
#else #else
else {
/* Allocate a bounce buffer with an extra block */ /* Allocate a bounce buffer with an extra block */
if (!(bounce_buf = bounce = dm_malloc((size_t) widened.size + block_size))) { if (!(bounce_buf = bounce = dm_malloc((size_t) widened.size + block_size))) {
log_error("Bounce buffer malloc failed"); log_error("Bounce buffer malloc failed");
@ -275,37 +302,58 @@ static int _aligned_io(struct device_area *where, char *buffer,
*/ */
if (((uintptr_t) bounce) & mask) if (((uintptr_t) bounce) & mask)
bounce = (char *) ((((uintptr_t) bounce) + mask) & ~mask); bounce = (char *) ((((uintptr_t) bounce) + mask) & ~mask);
}
#endif #endif
devbuf = EXTRA_IO(reason) ? &where->dev->last_extra_devbuf : &where->dev->last_devbuf;
_release_devbuf(devbuf);
devbuf->malloc_address = bounce_buf;
devbuf->buf = bounce;
devbuf->where.dev = where->dev;
devbuf->where.start = widened.start;
devbuf->where.size = widened.size;
devbuf->write = 0;
/* Do we need to read into the bounce buffer? */ /* Do we need to read into the bounce buffer? */
if ((!should_write || buffer_was_widened) && if ((!should_write || buffer_was_widened) &&
!_io(&widened, bounce, 0, reason)) { !_io(devbuf, reason)) {
if (!should_write) if (!should_write)
goto_out; goto_bad;
/* FIXME Handle errors properly! */ /* FIXME Handle errors properly! */
/* FIXME pre-extend the file */ /* FIXME pre-extend the file */
memset(bounce, '\n', widened.size); memset(bounce, '\n', widened.size);
} }
if (should_write) { if (should_write) {
if (bounce_buf) {
memcpy(bounce + (where->start - widened.start), buffer, memcpy(bounce + (where->start - widened.start), buffer,
(size_t) where->size); (size_t) where->size);
log_debug_io("Overwriting %" PRIu64 " bytes at %" PRIu64 " (for %s)", where->size,
/* ... then we write */ (uint64_t) where->start, _reason_text(reason));
if (!(r = _io(&widened, bounce, 1, reason)))
stack;
goto out;
} }
/* ... then we write */
devbuf->write = 1;
if (!(r = _io(devbuf, reason)))
goto_bad;
_release_devbuf(devbuf);
return 1;
}
/* read */
if (bounce_buf)
memcpy(buffer, bounce + (where->start - widened.start), memcpy(buffer, bounce + (where->start - widened.start),
(size_t) where->size); (size_t) where->size);
r = 1; /* We store what we just read as it often also satisfies the next request */
devbuf->data = bounce + (where->start - widened.start);
out: return 1;
dm_free(bounce_buf);
return r; bad:
_release_devbuf(devbuf);
return 0;
} }
static int _dev_get_size_file(struct device *dev, uint64_t *size) static int _dev_get_size_file(struct device *dev, uint64_t *size)

View File

@ -56,6 +56,22 @@ struct dev_ext {
void *handle; void *handle;
}; };
struct device_area {
struct device *dev;
uint64_t start; /* Bytes */
uint64_t size; /* Bytes */
};
struct device_buffer {
void *data; /* Location of start of requested data (inside buf) */
/* Private */
void *malloc_address; /* Start of allocated memory */
void *buf; /* Aligned buffer that contains data within it */
struct device_area where; /* Location of buf */
unsigned write:1; /* 1 if write; 0 if read */
};
/* /*
* All devices in LVM will be represented by one of these. * All devices in LVM will be represented by one of these.
* pointer comparisons are valid. * pointer comparisons are valid.
@ -78,6 +94,8 @@ struct device {
uint64_t end; uint64_t end;
struct dm_list open_list; struct dm_list open_list;
struct dev_ext ext; struct dev_ext ext;
struct device_buffer last_devbuf; /* Last data buffer read from the device */
struct device_buffer last_extra_devbuf; /* Last data buffer read from the device for extra metadata area */
const char *vgid; /* if device is an LV */ const char *vgid; /* if device is an LV */
const char *lvid; /* if device is an LV */ const char *lvid; /* if device is an LV */
@ -102,17 +120,16 @@ typedef enum dev_io_reason {
DEV_IO_LOG /* Logging messages */ DEV_IO_LOG /* Logging messages */
} dev_io_reason_t; } dev_io_reason_t;
/*
* Is this I/O for a device's extra metadata area?
*/
#define EXTRA_IO(reason) ((reason) == DEV_IO_MDA_EXTRA_HEADER || (reason) == DEV_IO_MDA_EXTRA_CONTENT)
struct device_list { struct device_list {
struct dm_list list; struct dm_list list;
struct device *dev; struct device *dev;
}; };
struct device_area {
struct device *dev;
uint64_t start; /* Bytes */
uint64_t size; /* Bytes */
};
/* /*
* Support for external device info. * Support for external device info.
*/ */
@ -174,6 +191,8 @@ struct device *dev_create_file(const char *filename, struct device *dev,
struct dm_str_list *alias, int use_malloc); struct dm_str_list *alias, int use_malloc);
void dev_destroy_file(struct device *dev); void dev_destroy_file(struct device *dev);
void devbufs_release(struct device *dev);
/* Return a valid device name from the alias list; NULL otherwise */ /* Return a valid device name from the alias list; NULL otherwise */
const char *dev_name_confirmed(struct device *dev, int quiet); const char *dev_name_confirmed(struct device *dev, int quiet);