f3brew: speed up writing and reading

This patch changes the interface of struct device to allow
writes and reads of sequential blocks with a single call.
This commit is contained in:
Michel Machado 2015-11-11 11:16:57 -05:00
parent 5d76cd84b6
commit 067802ca5a
3 changed files with 229 additions and 153 deletions

View File

@ -214,19 +214,37 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL}; static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL};
static void write_blocks(char *stamp_blk, struct device *dev, /* It must be a power of 2 greater than, or equal to 2^20.
* The current vaule is 1MB.
*/
#define BIG_BLOCK_SIZE_BYTE (1 << 20)
static void write_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block) uint64_t first_block, uint64_t last_block)
{ {
const int block_size = dev_get_block_size(dev);
const int block_order = dev_get_block_order(dev); const int block_order = dev_get_block_order(dev);
const int block_size = dev_get_block_size(dev);
char stack[align_head(block_order) + BIG_BLOCK_SIZE_BYTE];
char *buffer = align_mem(stack, block_order);
char *stamp_blk = buffer;
char *flush_blk = buffer + BIG_BLOCK_SIZE_BYTE;
uint64_t offset = first_block << block_order; uint64_t offset = first_block << block_order;
uint64_t i; uint64_t pos, first_pos = first_block;
for (i = first_block; i <= last_block; i++) { assert(BIG_BLOCK_SIZE_BYTE >= block_size);
for (pos = first_block; pos <= last_block; pos++) {
fill_buffer_with_block(stamp_blk, block_order, offset, 0); fill_buffer_with_block(stamp_blk, block_order, offset, 0);
if (dev_write_block(dev, stamp_blk, i)) stamp_blk += block_size;
warn("Failed writing block 0x%" PRIx64, i);
offset += block_size; offset += block_size;
if (stamp_blk == flush_blk || pos == last_block) {
if (dev_write_blocks(dev, buffer, first_pos, pos))
warn("Failed to write blocks from 0x%" PRIx64
" to 0x%" PRIx64, first_pos, pos);
stamp_blk = buffer;
first_pos = pos + 1;
}
} }
} }
@ -234,15 +252,10 @@ static void write_blocks(char *stamp_blk, struct device *dev,
static void test_write_blocks(struct device *dev, static void test_write_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block) uint64_t first_block, uint64_t last_block)
{ {
const int block_order = dev_get_block_order(dev);
const int block_size = dev_get_block_size(dev);
char stack[align_head(block_order) + block_size];
char *blk = align_mem(stack, block_order);
printf("Writing blocks from 0x%" PRIx64 " to 0x%" PRIx64 "...", printf("Writing blocks from 0x%" PRIx64 " to 0x%" PRIx64 "...",
first_block, last_block); first_block, last_block);
fflush(stdout); fflush(stdout);
write_blocks(blk, dev, first_block, last_block); write_blocks(dev, first_block, last_block);
printf(" Done\n\n"); printf(" Done\n\n");
} }
@ -347,12 +360,16 @@ static void validate_block(uint64_t expected_sector_offset,
} }
} }
static void read_blocks(char *probe_blk, struct device *dev, static void read_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block) uint64_t first_block, uint64_t last_block)
{ {
const int block_size = dev_get_block_size(dev); const int block_size = dev_get_block_size(dev);
const int block_order = dev_get_block_order(dev); const int block_order = dev_get_block_order(dev);
char stack[align_head(block_order) + BIG_BLOCK_SIZE_BYTE];
char *buffer = align_mem(stack, block_order);
uint64_t expected_sector_offset = first_block << block_order; uint64_t expected_sector_offset = first_block << block_order;
uint64_t first_pos = first_block;
uint64_t step = (BIG_BLOCK_SIZE_BYTE >> block_order) - 1;
struct block_range range = { struct block_range range = {
.state = bs_unknown, .state = bs_unknown,
.block_order = block_order, .block_order = block_order,
@ -360,15 +377,28 @@ static void read_blocks(char *probe_blk, struct device *dev,
.end_sector_offset = 0, .end_sector_offset = 0,
.found_sector_offset = 0, .found_sector_offset = 0,
}; };
uint64_t i;
for (i = first_block; i <= last_block; i++) { assert(BIG_BLOCK_SIZE_BYTE >= block_size);
if (!dev_read_block(dev, probe_blk, i))
while (first_pos <= last_block) {
uint64_t next_pos = first_pos + step;
char *probe_blk = buffer;
uint64_t pos;
if (next_pos > last_block)
next_pos = last_block;
if (dev_read_blocks(dev, buffer, first_pos, next_pos))
warn("Failed to read blocks from 0x%" PRIx64
" to 0x%" PRIx64, first_pos, next_pos);
for (pos = first_pos; pos <= next_pos; pos++) {
validate_block(expected_sector_offset, probe_blk, validate_block(expected_sector_offset, probe_blk,
block_order, &range); block_order, &range);
else expected_sector_offset += block_size;
warn("Failed reading block 0x%" PRIx64, i); probe_blk += block_size;
expected_sector_offset += block_size; }
first_pos = next_pos + 1;
} }
if (range.state != bs_unknown) if (range.state != bs_unknown)
print_block_range(&range); print_block_range(&range);
@ -380,14 +410,9 @@ static void read_blocks(char *probe_blk, struct device *dev,
static void test_read_blocks(struct device *dev, static void test_read_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block) uint64_t first_block, uint64_t last_block)
{ {
const int block_order = dev_get_block_order(dev);
const int block_size = dev_get_block_size(dev);
char stack[align_head(block_order) + block_size];
char *blk = align_mem(stack, block_order);
printf("Reading blocks from 0x%" PRIx64 " to 0x%" PRIx64 ":\n", printf("Reading blocks from 0x%" PRIx64 " to 0x%" PRIx64 ":\n",
first_block, last_block); first_block, last_block);
read_blocks(blk, dev, first_block, last_block); read_blocks(dev, first_block, last_block);
printf("\n"); printf("\n");
} }

273
libdevs.c
View File

@ -89,10 +89,10 @@ struct device {
uint64_t size_byte; uint64_t size_byte;
int block_order; int block_order;
int (*read_block)(struct device *dev, char *buf, int length, int (*read_blocks)(struct device *dev, char *buf,
uint64_t offset); uint64_t first_pos, uint64_t last_pos);
int (*write_block)(struct device *dev, const char *buf, int length, int (*write_blocks)(struct device *dev, const char *buf,
uint64_t offset); uint64_t first_pos, uint64_t last_pos);
int (*reset)(struct device *dev); int (*reset)(struct device *dev);
void (*free)(struct device *dev); void (*free)(struct device *dev);
const char *(*get_filename)(struct device *dev); const char *(*get_filename)(struct device *dev);
@ -113,20 +113,27 @@ int dev_get_block_size(struct device *dev)
return 1 << dev->block_order; return 1 << dev->block_order;
} }
int dev_read_block(struct device *dev, char *buf, uint64_t block) const char *dev_get_filename(struct device *dev)
{ {
const int block_size = 1 << dev->block_order; return dev->get_filename(dev);
uint64_t offset = block << dev->block_order;
assert(offset + block_size <= dev->size_byte);
return dev->read_block(dev, buf, block_size, offset);
} }
int dev_write_block(struct device *dev, const char *buf, uint64_t block) int dev_read_blocks(struct device *dev, char *buf,
uint64_t first_pos, uint64_t last_pos)
{ {
const int block_size = 1 << dev->block_order; if (first_pos > last_pos)
uint64_t offset = block << dev->block_order; return false;
assert(offset + block_size <= dev->size_byte); assert(last_pos < (dev->size_byte >> dev->block_order));
return dev->write_block(dev, buf, block_size, offset); return dev->read_blocks(dev, buf, first_pos, last_pos);
}
int dev_write_blocks(struct device *dev, const char *buf,
uint64_t first_pos, uint64_t last_pos)
{
if (first_pos > last_pos)
return false;
assert(last_pos < (dev->size_byte >> dev->block_order));
return dev->write_blocks(dev, buf, first_pos, last_pos);
} }
int dev_reset(struct device *dev) int dev_reset(struct device *dev)
@ -141,11 +148,6 @@ void free_device(struct device *dev)
free(dev); free(dev);
} }
const char *dev_get_filename(struct device *dev)
{
return dev->get_filename(dev);
}
struct file_device { struct file_device {
/* This must be the first field. See dev_fdev() for details. */ /* This must be the first field. See dev_fdev() for details. */
struct device dev; struct device dev;
@ -164,62 +166,75 @@ static inline struct file_device *dev_fdev(struct device *dev)
return (struct file_device *)dev; return (struct file_device *)dev;
} }
static int fdev_read_block(struct device *dev, char *buf, int length, static int fdev_read_block(struct device *dev, char *buf, uint64_t block_pos)
uint64_t offset)
{ {
struct file_device *fdev = dev_fdev(dev); struct file_device *fdev = dev_fdev(dev);
off_t off_ret; const int block_size = dev_get_block_size(dev);
const int block_order = dev_get_block_order(dev);
off_t off_ret, offset = block_pos << block_order;
int done; int done;
offset &= fdev->address_mask; offset &= fdev->address_mask;
if (offset >= fdev->real_size_byte) { if ((uint64_t)offset >= fdev->real_size_byte) {
int block_order;
uint64_t cache_pos; uint64_t cache_pos;
if (!fdev->cache_blocks) if (!fdev->cache_blocks)
goto no_block; /* No cache available. */ goto no_block; /* No cache available. */
block_order = dev_get_block_order(&fdev->dev); cache_pos = block_pos & fdev->cache_mask;
cache_pos = (offset >> block_order) & fdev->cache_mask;
assert(length == (1LL << block_order));
if (fdev->cache_entries && if (fdev->cache_entries &&
fdev->cache_entries[cache_pos] != offset) fdev->cache_entries[cache_pos] != block_pos)
goto no_block; goto no_block;
memmove(buf, &fdev->cache_blocks[cache_pos << block_order], memmove(buf, &fdev->cache_blocks[cache_pos << block_order],
length); block_size);
return 0; return 0;
} }
off_ret = lseek(fdev->fd, offset, SEEK_SET); off_ret = lseek(fdev->fd, offset, SEEK_SET);
if (off_ret < 0) if (off_ret < 0)
return - errno; return - errno;
assert((uint64_t)off_ret == offset); assert(off_ret == offset);
done = 0; done = 0;
do { do {
ssize_t rc = read(fdev->fd, buf + done, length - done); ssize_t rc = read(fdev->fd, buf + done, block_size - done);
assert(rc >= 0); assert(rc >= 0);
if (!rc) { if (!rc) {
/* Tried to read beyond the end of the file. */ /* Tried to read beyond the end of the file. */
assert(!done); assert(!done);
memset(buf, 0, length); memset(buf, 0, block_size);
done += length; done += block_size;
} }
done += rc; done += rc;
} while (done < length); } while (done < block_size);
return 0; return 0;
no_block: no_block:
memset(buf, 0, length); memset(buf, 0, block_size);
return 0; return 0;
} }
static int write_all(int fd, const char *buf, int count) static int fdev_read_blocks(struct device *dev, char *buf,
uint64_t first_pos, uint64_t last_pos)
{ {
int done = 0; const int block_size = dev_get_block_size(dev);
uint64_t pos;
for (pos = first_pos; pos <= last_pos; pos++) {
int rc = fdev_read_block(dev, buf, pos);
if (rc)
return rc;
buf += block_size;
}
return 0;
}
static int write_all(int fd, const char *buf, size_t count)
{
size_t done = 0;
do { do {
ssize_t rc = write(fd, buf + done, count - done); ssize_t rc = write(fd, buf + done, count - done);
if (rc < 0) { if (rc < 0) {
@ -231,28 +246,27 @@ static int write_all(int fd, const char *buf, int count)
return 0; return 0;
} }
static int fdev_write_block(struct device *dev, const char *buf, int length, static int fdev_write_block(struct device *dev, const char *buf,
uint64_t offset) uint64_t block_pos)
{ {
struct file_device *fdev = dev_fdev(dev); struct file_device *fdev = dev_fdev(dev);
off_t off_ret; const int block_size = dev_get_block_size(dev);
const int block_order = dev_get_block_order(dev);
off_t off_ret, offset = block_pos << block_order;
offset &= fdev->address_mask; offset &= fdev->address_mask;
if (offset >= fdev->real_size_byte) { if ((uint64_t)offset >= fdev->real_size_byte) {
/* Block beyond real memory. */ /* Block beyond real memory. */
int block_order;
uint64_t cache_pos; uint64_t cache_pos;
if (!fdev->cache_blocks) if (!fdev->cache_blocks)
return 0; /* No cache available. */ return 0; /* No cache available. */
block_order = dev_get_block_order(&fdev->dev); cache_pos = block_pos & fdev->cache_mask;
cache_pos = (offset >> block_order) & fdev->cache_mask;
assert(length == (1LL << block_order));
memmove(&fdev->cache_blocks[cache_pos << block_order], memmove(&fdev->cache_blocks[cache_pos << block_order],
buf, length); buf, block_size);
if (fdev->cache_entries) if (fdev->cache_entries)
fdev->cache_entries[cache_pos] = offset; fdev->cache_entries[cache_pos] = block_pos;
return 0; return 0;
} }
@ -260,9 +274,24 @@ static int fdev_write_block(struct device *dev, const char *buf, int length,
off_ret = lseek(fdev->fd, offset, SEEK_SET); off_ret = lseek(fdev->fd, offset, SEEK_SET);
if (off_ret < 0) if (off_ret < 0)
return - errno; return - errno;
assert((uint64_t)off_ret == offset); assert(off_ret == offset);
return write_all(fdev->fd, buf, length); return write_all(fdev->fd, buf, block_size);
}
static int fdev_write_blocks(struct device *dev, const char *buf,
uint64_t first_pos, uint64_t last_pos)
{
const int block_size = dev_get_block_size(dev);
uint64_t pos;
for (pos = first_pos; pos <= last_pos; pos++) {
int rc = fdev_write_block(dev, buf, pos);
if (rc)
return rc;
buf += block_size;
}
return 0;
} }
static void fdev_free(struct device *dev) static void fdev_free(struct device *dev)
@ -342,8 +371,8 @@ struct device *create_file_device(const char *filename,
fdev->dev.size_byte = fake_size_byte; fdev->dev.size_byte = fake_size_byte;
fdev->dev.block_order = block_order; fdev->dev.block_order = block_order;
fdev->dev.read_block = fdev_read_block; fdev->dev.read_blocks = fdev_read_blocks;
fdev->dev.write_block = fdev_write_block; fdev->dev.write_blocks = fdev_write_blocks;
fdev->dev.reset = NULL; fdev->dev.reset = NULL;
fdev->dev.free = fdev_free; fdev->dev.free = fdev_free;
fdev->dev.get_filename = fdev_get_filename; fdev->dev.get_filename = fdev_get_filename;
@ -378,9 +407,9 @@ static inline struct block_device *dev_bdev(struct device *dev)
return (struct block_device *)dev; return (struct block_device *)dev;
} }
static int read_all(int fd, char *buf, int count) static int read_all(int fd, char *buf, size_t count)
{ {
int done = 0; size_t done = 0;
do { do {
ssize_t rc = read(fd, buf + done, count - done); ssize_t rc = read(fd, buf + done, count - done);
if (rc < 0) { if (rc < 0) {
@ -393,25 +422,31 @@ static int read_all(int fd, char *buf, int count)
return 0; return 0;
} }
static int bdev_read_block(struct device *dev, char *buf, int length, static int bdev_read_blocks(struct device *dev, char *buf,
uint64_t offset) uint64_t first_pos, uint64_t last_pos)
{ {
struct block_device *bdev = dev_bdev(dev); struct block_device *bdev = dev_bdev(dev);
const int block_order = dev_get_block_order(dev);
size_t length = (last_pos - first_pos + 1) << block_order;
off_t offset = first_pos << block_order;
off_t off_ret = lseek(bdev->fd, offset, SEEK_SET); off_t off_ret = lseek(bdev->fd, offset, SEEK_SET);
if (off_ret < 0) if (off_ret < 0)
return - errno; return - errno;
assert((uint64_t)off_ret == offset); assert(off_ret == offset);
return read_all(bdev->fd, buf, length); return read_all(bdev->fd, buf, length);
} }
static int bdev_write_block(struct device *dev, const char *buf, int length, static int bdev_write_blocks(struct device *dev, const char *buf,
uint64_t offset) uint64_t first_pos, uint64_t last_pos)
{ {
struct block_device *bdev = dev_bdev(dev); struct block_device *bdev = dev_bdev(dev);
const int block_order = dev_get_block_order(dev);
size_t length = (last_pos - first_pos + 1) << block_order;
off_t offset = first_pos << block_order;
off_t off_ret = lseek(bdev->fd, offset, SEEK_SET); off_t off_ret = lseek(bdev->fd, offset, SEEK_SET);
if (off_ret < 0) if (off_ret < 0)
return - errno; return - errno;
assert((uint64_t)off_ret == offset); assert(off_ret == offset);
return write_all(bdev->fd, buf, length); return write_all(bdev->fd, buf, length);
} }
@ -878,8 +913,8 @@ struct device *create_block_device(const char *filename, enum reset_type rt)
assert(block_size == (1 << block_order)); assert(block_size == (1 << block_order));
bdev->dev.block_order = block_order; bdev->dev.block_order = block_order;
bdev->dev.read_block = bdev_read_block; bdev->dev.read_blocks = bdev_read_blocks;
bdev->dev.write_block = bdev_write_block; bdev->dev.write_blocks = bdev_write_blocks;
bdev->dev.free = bdev_free; bdev->dev.free = bdev_free;
bdev->dev.get_filename = bdev_get_filename; bdev->dev.get_filename = bdev_get_filename;
@ -918,34 +953,34 @@ static inline struct perf_device *dev_pdev(struct device *dev)
return (struct perf_device *)dev; return (struct perf_device *)dev;
} }
static int pdev_read_block(struct device *dev, char *buf, int length, static int pdev_read_blocks(struct device *dev, char *buf,
uint64_t offset) uint64_t first_pos, uint64_t last_pos)
{ {
struct perf_device *pdev = dev_pdev(dev); struct perf_device *pdev = dev_pdev(dev);
struct timeval t1, t2; struct timeval t1, t2;
int rc; int rc;
assert(!gettimeofday(&t1, NULL)); assert(!gettimeofday(&t1, NULL));
rc = pdev->shadow_dev->read_block(pdev->shadow_dev, buf, rc = pdev->shadow_dev->read_blocks(pdev->shadow_dev, buf,
length, offset); first_pos, last_pos);
assert(!gettimeofday(&t2, NULL)); assert(!gettimeofday(&t2, NULL));
pdev->read_count++; pdev->read_count += last_pos - first_pos + 1;
pdev->read_time_us += diff_timeval_us(&t1, &t2); pdev->read_time_us += diff_timeval_us(&t1, &t2);
return rc; return rc;
} }
static int pdev_write_block(struct device *dev, const char *buf, int length, static int pdev_write_blocks(struct device *dev, const char *buf,
uint64_t offset) uint64_t first_pos, uint64_t last_pos)
{ {
struct perf_device *pdev = dev_pdev(dev); struct perf_device *pdev = dev_pdev(dev);
struct timeval t1, t2; struct timeval t1, t2;
int rc; int rc;
assert(!gettimeofday(&t1, NULL)); assert(!gettimeofday(&t1, NULL));
rc = pdev->shadow_dev->write_block(pdev->shadow_dev, buf, rc = pdev->shadow_dev->write_blocks(pdev->shadow_dev, buf,
length, offset); first_pos, last_pos);
assert(!gettimeofday(&t2, NULL)); assert(!gettimeofday(&t2, NULL));
pdev->write_count++; pdev->write_count += last_pos - first_pos + 1;
pdev->write_time_us += diff_timeval_us(&t1, &t2); pdev->write_time_us += diff_timeval_us(&t1, &t2);
return rc; return rc;
} }
@ -1003,8 +1038,8 @@ struct device *create_perf_device(struct device *dev)
pdev->dev.size_byte = dev->size_byte; pdev->dev.size_byte = dev->size_byte;
pdev->dev.block_order = dev->block_order; pdev->dev.block_order = dev->block_order;
pdev->dev.read_block = pdev_read_block; pdev->dev.read_blocks = pdev_read_blocks;
pdev->dev.write_block = pdev_write_block; pdev->dev.write_blocks = pdev_write_blocks;
pdev->dev.reset = pdev_reset; pdev->dev.reset = pdev_reset;
pdev->dev.free = pdev_free; pdev->dev.free = pdev_free;
pdev->dev.get_filename = pdev_get_filename; pdev->dev.get_filename = pdev_get_filename;
@ -1044,7 +1079,7 @@ struct safe_device {
struct device *shadow_dev; struct device *shadow_dev;
char *saved_blocks; char *saved_blocks;
uint64_t *sb_offsets; uint64_t *sb_positions;
SDEV_BITMAP_WORD *sb_bitmap; SDEV_BITMAP_WORD *sb_bitmap;
int sb_n; int sb_n;
int sb_max; int sb_max;
@ -1055,69 +1090,70 @@ static inline struct safe_device *dev_sdev(struct device *dev)
return (struct safe_device *)dev; return (struct safe_device *)dev;
} }
static int sdev_read_block(struct device *dev, char *buf, int length, static int sdev_read_blocks(struct device *dev, char *buf,
uint64_t offset) uint64_t first_pos, uint64_t last_pos)
{ {
struct safe_device *sdev = dev_sdev(dev); struct safe_device *sdev = dev_sdev(dev);
return sdev->shadow_dev->read_block(sdev->shadow_dev, buf, return sdev->shadow_dev->read_blocks(sdev->shadow_dev, buf,
length, offset); first_pos, last_pos);
} }
static int sdev_save_block(struct safe_device *sdev, static int sdev_save_block(struct safe_device *sdev, uint64_t block_pos)
int length, uint64_t offset)
{ {
const int block_order = dev_get_block_order(sdev->shadow_dev); const int block_order = dev_get_block_order(sdev->shadow_dev);
lldiv_t idx = lldiv(offset >> block_order, SDEV_BITMAP_BITS_PER_WORD); lldiv_t idx = lldiv(block_pos, SDEV_BITMAP_BITS_PER_WORD);
SDEV_BITMAP_WORD set_bit = (SDEV_BITMAP_WORD)1 << idx.rem; SDEV_BITMAP_WORD set_bit = (SDEV_BITMAP_WORD)1 << idx.rem;
char *block; char *block_buf;
int rc; int rc;
/* The current implementation doesn't support variable lengths. */
assert(length == dev_get_block_size(sdev->shadow_dev));
/* Is this block already saved? */ /* Is this block already saved? */
if (!sdev->sb_bitmap) { if (!sdev->sb_bitmap) {
int i; int i;
/* Running without bitmap. */ /* Running without bitmap. */
for (i = 0; i < sdev->sb_n; i++) for (i = 0; i < sdev->sb_n; i++)
if (sdev->sb_offsets[i] == offset) { if (sdev->sb_positions[i] == block_pos) {
/* The block at @offset is already saved. */ /* The block is already saved. */
return 0; return 0;
} }
} else if (sdev->sb_bitmap[idx.quot] & set_bit) { } else if (sdev->sb_bitmap[idx.quot] & set_bit) {
/* The block at @offset is already saved. */ /* The block is already saved. */
return 0; return 0;
} }
/* The block at @offset hasn't been saved before. Save this block. */ /* The block hasn't been saved before. Save it. */
assert(sdev->sb_n < sdev->sb_max); assert(sdev->sb_n < sdev->sb_max);
block = (char *)align_mem(sdev->saved_blocks, block_order) + block_buf = (char *)align_mem(sdev->saved_blocks, block_order) +
(sdev->sb_n << block_order); (sdev->sb_n << block_order);
rc = sdev->shadow_dev->read_block(sdev->shadow_dev, block, rc = sdev->shadow_dev->read_blocks(sdev->shadow_dev, block_buf,
length, offset); block_pos, block_pos);
if (rc) if (rc)
return rc; return rc;
/* Bookkeeping. */ /* Bookkeeping. */
if (sdev->sb_bitmap) if (sdev->sb_bitmap)
sdev->sb_bitmap[idx.quot] |= set_bit; sdev->sb_bitmap[idx.quot] |= set_bit;
sdev->sb_offsets[sdev->sb_n] = offset; sdev->sb_positions[sdev->sb_n] = block_pos;
sdev->sb_n++; sdev->sb_n++;
return 0; return 0;
} }
static int sdev_write_block(struct device *dev, const char *buf, int length, static int sdev_write_blocks(struct device *dev, const char *buf,
uint64_t offset) uint64_t first_pos, uint64_t last_pos)
{ {
struct safe_device *sdev = dev_sdev(dev); struct safe_device *sdev = dev_sdev(dev);
int rc; uint64_t pos;
rc = sdev_save_block(sdev, length, offset); /* XXX The code should take advantage of the fact that
if (rc) * the blocks are sequential and execute a single read.
return rc; */
for (pos = first_pos; pos <= last_pos; pos++) {
int rc = sdev_save_block(sdev, pos);
if (rc)
return rc;
}
return sdev->shadow_dev->write_block(sdev->shadow_dev, buf, return sdev->shadow_dev->write_blocks(sdev->shadow_dev, buf,
length, offset); first_pos, last_pos);
} }
static int sdev_reset(struct device *dev) static int sdev_reset(struct device *dev)
@ -1129,32 +1165,35 @@ static void sdev_free(struct device *dev)
{ {
struct safe_device *sdev = dev_sdev(dev); struct safe_device *sdev = dev_sdev(dev);
/* XXX The code should take advantage of fact that
* some blocks are sequential and execute less write calls.
*/
if (sdev->sb_n > 0) { if (sdev->sb_n > 0) {
const int block_order = dev_get_block_order(sdev->shadow_dev); const int block_order = dev_get_block_order(sdev->shadow_dev);
char *first_block = align_mem(sdev->saved_blocks, block_order); char *first_block = align_mem(sdev->saved_blocks, block_order);
char *block = first_block + ((sdev->sb_n - 1) << block_order); char *block_buf = first_block +
uint64_t *poffset = &sdev->sb_offsets[sdev->sb_n - 1]; ((sdev->sb_n - 1) << block_order);
uint64_t *ppos = &sdev->sb_positions[sdev->sb_n - 1];
int block_size = dev_get_block_size(sdev->shadow_dev); int block_size = dev_get_block_size(sdev->shadow_dev);
/* Restore blocks in reverse order to cope with /* Restore blocks in reverse order to cope with
* wraparound and chain drives. * wraparound and chain drives.
*/ */
do { do {
int rc = sdev->shadow_dev->write_block( int rc = sdev->shadow_dev->write_blocks(
sdev->shadow_dev, block, block_size, *poffset); sdev->shadow_dev, block_buf, *ppos, *ppos);
if (rc) { if (rc) {
/* Do not abort, try to recover all bocks. */ /* Do not abort, try to recover all bocks. */
warn("Failed to recover block at offset 0x%" warn("Failed to recover block 0x%" PRIx64
PRIx64 " due to a write error", " due to a write error", *ppos);
*poffset);
} }
block -= block_size; block_buf -= block_size;
poffset--; ppos--;
} while (block >= first_block); } while (block_buf >= first_block);
} }
free(sdev->sb_bitmap); free(sdev->sb_bitmap);
free(sdev->sb_offsets); free(sdev->sb_positions);
free(sdev->saved_blocks); free(sdev->saved_blocks);
free_device(sdev->shadow_dev); free_device(sdev->shadow_dev);
} }
@ -1180,8 +1219,8 @@ struct device *create_safe_device(struct device *dev, int max_blocks,
if (!sdev->saved_blocks) if (!sdev->saved_blocks)
goto sdev; goto sdev;
sdev->sb_offsets = malloc(max_blocks * sizeof(*sdev->sb_offsets)); sdev->sb_positions = malloc(max_blocks * sizeof(*sdev->sb_positions));
if (!sdev->sb_offsets) if (!sdev->sb_positions)
goto saved_blocks; goto saved_blocks;
if (!min_memory) { if (!min_memory) {
@ -1203,8 +1242,8 @@ struct device *create_safe_device(struct device *dev, int max_blocks,
sdev->dev.size_byte = dev->size_byte; sdev->dev.size_byte = dev->size_byte;
sdev->dev.block_order = block_order; sdev->dev.block_order = block_order;
sdev->dev.read_block = sdev_read_block; sdev->dev.read_blocks = sdev_read_blocks;
sdev->dev.write_block = sdev_write_block; sdev->dev.write_blocks = sdev_write_blocks;
sdev->dev.reset = sdev_reset; sdev->dev.reset = sdev_reset;
sdev->dev.free = sdev_free; sdev->dev.free = sdev_free;
sdev->dev.get_filename = sdev_get_filename; sdev->dev.get_filename = sdev_get_filename;
@ -1212,7 +1251,7 @@ struct device *create_safe_device(struct device *dev, int max_blocks,
return &sdev->dev; return &sdev->dev;
offsets: offsets:
free(sdev->sb_offsets); free(sdev->sb_positions);
saved_blocks: saved_blocks:
free(sdev->saved_blocks); free(sdev->saved_blocks);
sdev: sdev:

View File

@ -40,28 +40,41 @@ enum fake_type dev_param_to_type(uint64_t real_size_byte,
struct device; struct device;
/* Properties. */ /*
* Properties
*/
uint64_t dev_get_size_byte(struct device *dev); uint64_t dev_get_size_byte(struct device *dev);
int dev_get_block_order(struct device *dev); int dev_get_block_order(struct device *dev);
int dev_get_block_size(struct device *dev); int dev_get_block_size(struct device *dev);
/* Methods. */
int dev_read_block(struct device *dev, char *buf, uint64_t block);
int dev_write_block(struct device *dev, const char *buf, uint64_t block);
int dev_reset(struct device *dev);
void free_device(struct device *dev);
/* File name of the device. /* File name of the device.
* This information is important because the filename may change due to resets. * This information is important because the filename may change due to resets.
*/ */
const char *dev_get_filename(struct device *dev); const char *dev_get_filename(struct device *dev);
static inline int dev_write_and_reset(struct device *dev, const char *buf, /*
* Methods
*/
int dev_read_blocks(struct device *dev, char *buf,
uint64_t first_pos, uint64_t last_pos);
int dev_write_blocks(struct device *dev, const char *buf,
uint64_t first_pos, uint64_t last_pos);
static inline int dev_read_block(struct device *dev, char *buf, uint64_t block)
{
return dev_read_blocks(dev, buf, block, block);
}
static inline int dev_write_block(struct device *dev, const char *buf,
uint64_t block) uint64_t block)
{ {
int rc = dev_write_block(dev, buf, block); return dev_write_blocks(dev, buf, block, block);
return rc ? rc : dev_reset(dev);
} }
int dev_reset(struct device *dev);
void free_device(struct device *dev);
/* /*
* Concrete devices * Concrete devices
*/ */
@ -71,7 +84,6 @@ struct device *create_file_device(const char *filename,
int block_order, int cache_order, int strict_cache, int block_order, int cache_order, int strict_cache,
int keep_file); int keep_file);
/* XXX Add support for block devices backed by SCSI and ATA. */
enum reset_type { enum reset_type {
RT_MANUAL_USB = 0, RT_MANUAL_USB = 0,
RT_USB, RT_USB,