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}, "Wrap parameter of the emulated drive", 0},
{"debug-block-order", 'b', "ORDER", OPTION_HIDDEN, {"debug-block-order", 'b', "ORDER", OPTION_HIDDEN,
"Block size of the emulated drive is 2^ORDER Bytes", 0}, "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, {"debug-keep-file", 'k', NULL, OPTION_HIDDEN,
"Don't remove file used for emulating the drive", 0}, "Don't remove file used for emulating the drive", 0},
{"reset-type", 's', "TYPE", 0, {"reset-type", 's', "TYPE", 0,
@ -66,6 +70,8 @@ struct args {
uint64_t fake_size_byte; uint64_t fake_size_byte;
int wrap; int wrap;
int block_order; int block_order;
int cache_order;
int strict_cache;
/* What to do. */ /* What to do. */
uint64_t first_block; uint64_t first_block;
@ -118,6 +124,20 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
args->debug = true; args->debug = true;
break; 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': case 'k':
args->keep_file = true; args->keep_file = true;
args->debug = true; args->debug = true;
@ -384,6 +404,8 @@ int main(int argc, char **argv)
.fake_size_byte = 1ULL << 34, .fake_size_byte = 1ULL << 34,
.wrap = 31, .wrap = 31,
.block_order = 0, .block_order = 0,
.cache_order = -1,
.strict_cache = false,
.first_block = 0, .first_block = 0,
.last_block = -1ULL, .last_block = -1ULL,
}; };
@ -397,7 +419,7 @@ int main(int argc, char **argv)
dev = args.debug dev = args.debug
? create_file_device(args.filename, args.real_size_byte, ? create_file_device(args.filename, args.real_size_byte,
args.fake_size_byte, args.wrap, args.block_order, 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); : create_block_device(args.filename, args.reset_type);
if (!dev) { if (!dev) {
fprintf(stderr, "\nApplication cannot continue, finishing...\n"); 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}, "Wrap parameter of the emulated drive", 0},
{"debug-block-order", 'b', "ORDER", OPTION_HIDDEN, {"debug-block-order", 'b', "ORDER", OPTION_HIDDEN,
"Block size of the emulated drive is 2^ORDER Bytes", 0}, "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, {"debug-keep-file", 'k', NULL, OPTION_HIDDEN,
"Don't remove file used for emulating the drive", 0}, "Don't remove file used for emulating the drive", 0},
{"debug-unit-test", 'u', NULL, OPTION_HIDDEN, {"debug-unit-test", 'u', NULL, OPTION_HIDDEN,
@ -70,6 +74,8 @@ struct args {
uint64_t fake_size_byte; uint64_t fake_size_byte;
int wrap; int wrap;
int block_order; int block_order;
int cache_order;
int strict_cache;
}; };
static error_t parse_opt(int key, char *arg, struct argp_state *state) 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; args->debug = true;
break; 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': case 'k':
args->keep_file = true; args->keep_file = true;
args->debug = true; args->debug = true;
@ -184,29 +204,43 @@ struct unit_test_item {
uint64_t fake_size_byte; uint64_t fake_size_byte;
int wrap; int wrap;
int block_order; int block_order;
int cache_order;
int strict_cache;
}; };
static const struct unit_test_item ftype_to_params[] = { static const struct unit_test_item ftype_to_params[] = {
/* Smallest good drive. */ /* Smallest good drive. */
{1ULL << 20, 1ULL << 20, 20, 9}, {1ULL << 21, 1ULL << 21, 21, 9, -1, false},
/* Good, 4KB-block, 1GB drive. */ /* Good, 4KB-block, 1GB drive. */
{1ULL << 30, 1ULL << 30, 30, 12}, {1ULL << 30, 1ULL << 30, 30, 12, -1, false},
/* Bad drive. */ /* Bad drive. */
{0, 1ULL << 30, 30, 9}, {0, 1ULL << 30, 30, 9, -1, false},
/* Geometry of a real limbo drive. */ /* Geometry of a real limbo drive. */
{1777645568ULL, 32505331712ULL, 35, 9}, {1777645568ULL, 32505331712ULL, 35, 9, -1, false},
/* Wraparound drive. */ /* Wraparound drive. */
{1ULL << 31, 1ULL << 34, 31, 9}, {1ULL << 31, 1ULL << 34, 31, 9, -1, false},
/* Chain drive. */ /* Chain drive. */
{1ULL << 31, 1ULL << 34, 32, 9}, {1ULL << 31, 1ULL << 34, 32, 9, -1, false},
/* Extreme case for memory usage (limbo drive). */ /* 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 \ #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, dev = create_file_device(filename, item->real_size_byte,
item->fake_size_byte, item->wrap, item->block_order, item->fake_size_byte, item->wrap, item->block_order,
false); item->cache_order, item->strict_cache, false);
assert(dev); assert(dev);
max_probe_blocks = probe_device_max_blocks(dev); max_probe_blocks = probe_device_max_blocks(dev);
assert(!probe_device(dev, &real_size_byte, &announced_size_byte, assert(!probe_device(dev, &real_size_byte, &announced_size_byte,
@ -314,7 +348,7 @@ static int test_device(struct args *args)
dev = args->debug dev = args->debug
? create_file_device(args->filename, args->real_size_byte, ? create_file_device(args->filename, args->real_size_byte,
args->fake_size_byte, args->wrap, args->block_order, 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); : create_block_device(args->filename, args->reset_type);
if (!dev) { if (!dev) {
fprintf(stderr, "\nApplication cannot continue, finishing...\n"); fprintf(stderr, "\nApplication cannot continue, finishing...\n");
@ -448,6 +482,8 @@ int main(int argc, char **argv)
.fake_size_byte = 1ULL << 34, .fake_size_byte = 1ULL << 34,
.wrap = 31, .wrap = 31,
.block_order = 0, .block_order = 0,
.cache_order = -1,
.strict_cache = false,
}; };
/* Read parameters. */ /* Read parameters. */

View File

@ -154,6 +154,9 @@ struct file_device {
int fd; int fd;
uint64_t real_size_byte; uint64_t real_size_byte;
uint64_t address_mask; 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) 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; offset &= fdev->address_mask;
if (offset >= fdev->real_size_byte) { 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; return 0;
} }
@ -193,6 +211,10 @@ static int fdev_read_block(struct device *dev, char *buf, int length,
} while (done < length); } while (done < length);
return 0; return 0;
no_block:
memset(buf, 0, length);
return 0;
} }
static int write_all(int fd, const char *buf, int count) 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; off_t off_ret;
offset &= fdev->address_mask; 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; 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)
@ -230,6 +268,8 @@ static int fdev_write_block(struct device *dev, const char *buf, int length,
static void fdev_free(struct device *dev) static void fdev_free(struct device *dev)
{ {
struct file_device *fdev = dev_fdev(dev); struct file_device *fdev = dev_fdev(dev);
free(fdev->cache_blocks);
free(fdev->cache_entries);
free((void *)fdev->filename); free((void *)fdev->filename);
assert(!close(fdev->fd)); 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, struct device *create_file_device(const char *filename,
uint64_t real_size_byte, uint64_t fake_size_byte, int wrap, 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; struct file_device *fdev;
@ -253,10 +294,29 @@ struct device *create_file_device(const char *filename,
if (!fdev->filename) if (!fdev->filename)
goto fdev; 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); fdev->fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fdev->fd < 0) { if (fdev->fd < 0) {
err(errno, "Can't create file `%s'", filename); err(errno, "Can't create file `%s'", filename);
goto filename; goto cache;
} }
if (!keep_file) { if (!keep_file) {
/* Unlinking the file now guarantees that it won't exist if /* Unlinking the file now guarantees that it won't exist if
@ -294,7 +354,10 @@ keep_file:
if (keep_file) if (keep_file)
unlink(filename); unlink(filename);
assert(!close(fdev->fd)); 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); free((void *)fdev->filename);
fdev: fdev:
free(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, struct device *create_file_device(const char *filename,
uint64_t real_size_byte, uint64_t fake_size_byte, int wrap, 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. */ /* XXX Add support for block devices backed by SCSI and ATA. */
enum reset_type { enum reset_type {