f3probe/f3brew: add cache to the model

F3 users have identified fake flashes that reserve a portion of
their good memory to use as a permanent cache.
This patch adds this feature to our model in order to allow us to
develop a new probe algorithm to deal with it.
This commit is contained in:
Michel Machado 2015-11-04 10:59:20 -05:00
parent 57fded9501
commit 3f0efeb12f
4 changed files with 142 additions and 20 deletions

View File

@ -33,6 +33,10 @@ static struct argp_option options[] = {
"Wrap parameter of the emulated drive", 0},
{"debug-block-order", 'b', "ORDER", OPTION_HIDDEN,
"Block size of the emulated drive is 2^ORDER Bytes", 0},
{"debug-cache-order", 'c', "ORDER", OPTION_HIDDEN,
"Cache size of the emulated drive is 2^ORDER blocks", 0},
{"debug-strict-cache", 'o', NULL, OPTION_HIDDEN,
"Force the cache to be strict", 0},
{"debug-keep-file", 'k', NULL, OPTION_HIDDEN,
"Don't remove file used for emulating the drive", 0},
{"reset-type", 's', "TYPE", 0,
@ -66,6 +70,8 @@ struct args {
uint64_t fake_size_byte;
int wrap;
int block_order;
int cache_order;
int strict_cache;
/* What to do. */
uint64_t first_block;
@ -118,6 +124,20 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
args->debug = true;
break;
case 'c':
ll = arg_to_ll_bytes(state, arg);
if (ll < -1 || ll > 64)
argp_error(state,
"Cache order must be in the interval [-1, 64]");
args->cache_order = ll;
args->debug = true;
break;
case 'o':
args->strict_cache = true;
args->debug = true;
break;
case 'k':
args->keep_file = true;
args->debug = true;
@ -384,6 +404,8 @@ int main(int argc, char **argv)
.fake_size_byte = 1ULL << 34,
.wrap = 31,
.block_order = 0,
.cache_order = -1,
.strict_cache = false,
.first_block = 0,
.last_block = -1ULL,
};
@ -397,7 +419,7 @@ int main(int argc, char **argv)
dev = args.debug
? create_file_device(args.filename, args.real_size_byte,
args.fake_size_byte, args.wrap, args.block_order,
args.keep_file)
args.cache_order, args.strict_cache, args.keep_file)
: create_block_device(args.filename, args.reset_type);
if (!dev) {
fprintf(stderr, "\nApplication cannot continue, finishing...\n");

View File

@ -35,6 +35,10 @@ static struct argp_option options[] = {
"Wrap parameter of the emulated drive", 0},
{"debug-block-order", 'b', "ORDER", OPTION_HIDDEN,
"Block size of the emulated drive is 2^ORDER Bytes", 0},
{"debug-cache-order", 'c', "ORDER", OPTION_HIDDEN,
"Cache size of the emulated drive is 2^ORDER blocks", 0},
{"debug-strict-cache", 'o', NULL, OPTION_HIDDEN,
"Force the cache to be strict", 0},
{"debug-keep-file", 'k', NULL, OPTION_HIDDEN,
"Don't remove file used for emulating the drive", 0},
{"debug-unit-test", 'u', NULL, OPTION_HIDDEN,
@ -70,6 +74,8 @@ struct args {
uint64_t fake_size_byte;
int wrap;
int block_order;
int cache_order;
int strict_cache;
};
static error_t parse_opt(int key, char *arg, struct argp_state *state)
@ -118,6 +124,20 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
args->debug = true;
break;
case 'c':
ll = arg_to_ll_bytes(state, arg);
if (ll < -1 || ll > 64)
argp_error(state,
"Cache order must be in the interval [-1, 64]");
args->cache_order = ll;
args->debug = true;
break;
case 'o':
args->strict_cache = true;
args->debug = true;
break;
case 'k':
args->keep_file = true;
args->debug = true;
@ -184,29 +204,43 @@ struct unit_test_item {
uint64_t fake_size_byte;
int wrap;
int block_order;
int cache_order;
int strict_cache;
};
static const struct unit_test_item ftype_to_params[] = {
/* Smallest good drive. */
{1ULL << 20, 1ULL << 20, 20, 9},
{1ULL << 21, 1ULL << 21, 21, 9, -1, false},
/* Good, 4KB-block, 1GB drive. */
{1ULL << 30, 1ULL << 30, 30, 12},
{1ULL << 30, 1ULL << 30, 30, 12, -1, false},
/* Bad drive. */
{0, 1ULL << 30, 30, 9},
{0, 1ULL << 30, 30, 9, -1, false},
/* Geometry of a real limbo drive. */
{1777645568ULL, 32505331712ULL, 35, 9},
{1777645568ULL, 32505331712ULL, 35, 9, -1, false},
/* Wraparound drive. */
{1ULL << 31, 1ULL << 34, 31, 9},
{1ULL << 31, 1ULL << 34, 31, 9, -1, false},
/* Chain drive. */
{1ULL << 31, 1ULL << 34, 32, 9},
{1ULL << 31, 1ULL << 34, 32, 9, -1, false},
/* Extreme case for memory usage (limbo drive). */
{1ULL << 20, 1ULL << 40, 40, 9},
{(1ULL<<20)+512,1ULL << 40, 40, 9, -1, false},
/* Geomerty of a real limbo drive with 256MB of strict cache. */
{7600799744ULL, 67108864000ULL, 36, 9, 19, true},
/* The drive before with a non-strict cache. */
{7600799744ULL, 67108864000ULL, 36, 9, 19, false},
/* The devil drive I. */
{0, 1ULL << 40, 40, 9, 21, true},
/* The devil drive II. */
{0, 1ULL << 40, 40, 9, 21, false},
};
#define UNIT_TEST_N_CASES \
@ -232,7 +266,7 @@ static int unit_test(const char *filename)
dev = create_file_device(filename, item->real_size_byte,
item->fake_size_byte, item->wrap, item->block_order,
false);
item->cache_order, item->strict_cache, false);
assert(dev);
max_probe_blocks = probe_device_max_blocks(dev);
assert(!probe_device(dev, &real_size_byte, &announced_size_byte,
@ -314,7 +348,7 @@ static int test_device(struct args *args)
dev = args->debug
? create_file_device(args->filename, args->real_size_byte,
args->fake_size_byte, args->wrap, args->block_order,
args->keep_file)
args->cache_order, args->strict_cache, args->keep_file)
: create_block_device(args->filename, args->reset_type);
if (!dev) {
fprintf(stderr, "\nApplication cannot continue, finishing...\n");
@ -448,6 +482,8 @@ int main(int argc, char **argv)
.fake_size_byte = 1ULL << 34,
.wrap = 31,
.block_order = 0,
.cache_order = -1,
.strict_cache = false,
};
/* Read parameters. */

View File

@ -150,10 +150,13 @@ struct file_device {
/* This must be the first field. See dev_fdev() for details. */
struct device dev;
const char *filename;
int fd;
uint64_t real_size_byte;
uint64_t address_mask;
const char *filename;
int fd;
uint64_t real_size_byte;
uint64_t address_mask;
uint64_t cache_mask;
uint64_t *cache_entries;
char *cache_blocks;
};
static inline struct file_device *dev_fdev(struct device *dev)
@ -170,7 +173,22 @@ static int fdev_read_block(struct device *dev, char *buf, int length,
offset &= fdev->address_mask;
if (offset >= fdev->real_size_byte) {
memset(buf, 0, length);
int block_order;
uint64_t cache_pos;
if (!fdev->cache_blocks)
goto no_block; /* No cache available. */
block_order = dev_get_block_order(&fdev->dev);
cache_pos = (offset >> block_order) & fdev->cache_mask;
assert(length == (1LL << block_order));
if (fdev->cache_entries &&
fdev->cache_entries[cache_pos] != offset)
goto no_block;
memmove(buf, &fdev->cache_blocks[cache_pos << block_order],
length);
return 0;
}
@ -193,6 +211,10 @@ static int fdev_read_block(struct device *dev, char *buf, int length,
} while (done < length);
return 0;
no_block:
memset(buf, 0, length);
return 0;
}
static int write_all(int fd, const char *buf, int count)
@ -216,8 +238,24 @@ static int fdev_write_block(struct device *dev, const char *buf, int length,
off_t off_ret;
offset &= fdev->address_mask;
if (offset >= fdev->real_size_byte)
if (offset >= fdev->real_size_byte) {
/* Block beyond real memory. */
int block_order;
uint64_t cache_pos;
if (!fdev->cache_blocks)
return 0; /* No cache available. */
block_order = dev_get_block_order(&fdev->dev);
cache_pos = (offset >> block_order) & fdev->cache_mask;
assert(length == (1LL << block_order));
memmove(&fdev->cache_blocks[cache_pos << block_order],
buf, length);
if (fdev->cache_entries)
fdev->cache_entries[cache_pos] = offset;
return 0;
}
off_ret = lseek(fdev->fd, offset, SEEK_SET);
if (off_ret < 0)
@ -230,6 +268,8 @@ static int fdev_write_block(struct device *dev, const char *buf, int length,
static void fdev_free(struct device *dev)
{
struct file_device *fdev = dev_fdev(dev);
free(fdev->cache_blocks);
free(fdev->cache_entries);
free((void *)fdev->filename);
assert(!close(fdev->fd));
}
@ -241,7 +281,8 @@ static const char *fdev_get_filename(struct device *dev)
struct device *create_file_device(const char *filename,
uint64_t real_size_byte, uint64_t fake_size_byte, int wrap,
int block_order, int keep_file)
int block_order, int cache_order, int strict_cache,
int keep_file)
{
struct file_device *fdev;
@ -253,10 +294,29 @@ struct device *create_file_device(const char *filename,
if (!fdev->filename)
goto fdev;
fdev->cache_mask = 0;
fdev->cache_entries = NULL;
fdev->cache_blocks = NULL;
if (cache_order >= 0) {
fdev->cache_mask = (((uint64_t)1) << cache_order) - 1;
if (strict_cache) {
size_t size = sizeof(*fdev->cache_entries) <<
cache_order;
fdev->cache_entries = malloc(size);
if (!fdev->cache_entries)
goto cache;
memset(fdev->cache_entries, 0, size);
}
fdev->cache_blocks = malloc(((uint64_t)1) <<
(cache_order + block_order));
if (!fdev->cache_blocks)
goto cache;
}
fdev->fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fdev->fd < 0) {
err(errno, "Can't create file `%s'", filename);
goto filename;
goto cache;
}
if (!keep_file) {
/* Unlinking the file now guarantees that it won't exist if
@ -294,7 +354,10 @@ keep_file:
if (keep_file)
unlink(filename);
assert(!close(fdev->fd));
filename:
cache:
free(fdev->cache_blocks);
free(fdev->cache_entries);
/* filename: this label is not being used. */
free((void *)fdev->filename);
fdev:
free(fdev);

View File

@ -68,7 +68,8 @@ static inline int dev_write_and_reset(struct device *dev, const char *buf,
struct device *create_file_device(const char *filename,
uint64_t real_size_byte, uint64_t fake_size_byte, int wrap,
int block_order, int keep_file);
int block_order, int cache_order, int strict_cache,
int keep_file);
/* XXX Add support for block devices backed by SCSI and ATA. */
enum reset_type {