mirror of
https://github.com/AltraMayor/f3.git
synced 2025-09-17 11:06:16 -04:00
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:
parent
5d76cd84b6
commit
067802ca5a
75
f3brew.c
75
f3brew.c
@ -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
273
libdevs.c
@ -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:
|
||||||
|
34
libdevs.h
34
libdevs.h
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user