mirror of
https://github.com/AltraMayor/f3.git
synced 2025-08-04 02:55:57 -04:00
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:
parent
57fded9501
commit
3f0efeb12f
24
f3brew.c
24
f3brew.c
@ -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");
|
||||||
|
54
f3probe.c
54
f3probe.c
@ -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. */
|
||||||
|
73
libdevs.c
73
libdevs.c
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user