mirror of
https://github.com/AltraMayor/f3.git
synced 2025-08-03 18:46:00 -04:00
f3probe: speed up non-destructive mode
This patch makes f3probe issue single calls of sequential reads and writes wherever is possible to speed up the non-destructive (i.e. conservative) mode. Notice that the non-destructive mode only recovers the usable blocks of fake drives, and all blocks of legit drives.
This commit is contained in:
parent
08dd0e9061
commit
1a576cb6dc
15
f3probe.c
15
f3probe.c
@ -360,7 +360,7 @@ static int test_device(struct args *args)
|
|||||||
{
|
{
|
||||||
struct timeval t1, t2;
|
struct timeval t1, t2;
|
||||||
double time_s;
|
double time_s;
|
||||||
struct device *dev, *pdev;
|
struct device *dev, *pdev, *sdev;
|
||||||
enum fake_type fake_type;
|
enum fake_type fake_type;
|
||||||
uint64_t real_size_byte, announced_size_byte, cache_size_block;
|
uint64_t real_size_byte, announced_size_byte, cache_size_block;
|
||||||
int wrap, need_reset, block_order;
|
int wrap, need_reset, block_order;
|
||||||
@ -388,7 +388,7 @@ static int test_device(struct args *args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (args->save) {
|
if (args->save) {
|
||||||
struct device *sdev = create_safe_device(dev,
|
sdev = create_safe_device(dev,
|
||||||
probe_device_max_blocks(dev), args->min_mem);
|
probe_device_max_blocks(dev), args->min_mem);
|
||||||
if (!sdev) {
|
if (!sdev) {
|
||||||
if (!args->min_mem)
|
if (!args->min_mem)
|
||||||
@ -426,16 +426,21 @@ static int test_device(struct args *args)
|
|||||||
&write_count, &write_time_us,
|
&write_count, &write_time_us,
|
||||||
&reset_count, &reset_time_us);
|
&reset_count, &reset_time_us);
|
||||||
if (args->save) {
|
if (args->save) {
|
||||||
|
uint64_t very_last_pos = real_size_byte >> block_order;
|
||||||
printf("Probe finished, recovering blocks...");
|
printf("Probe finished, recovering blocks...");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
if (very_last_pos > 0) {
|
||||||
|
very_last_pos--;
|
||||||
|
sdev_recover(sdev, very_last_pos);
|
||||||
|
}
|
||||||
|
printf(" Done\n");
|
||||||
|
sdev_flush(sdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
final_dev_filename = strdup(dev_get_filename(dev));
|
final_dev_filename = strdup(dev_get_filename(dev));
|
||||||
free_device(dev);
|
|
||||||
assert(final_dev_filename);
|
assert(final_dev_filename);
|
||||||
|
free_device(dev);
|
||||||
|
|
||||||
if (args->save)
|
|
||||||
printf(" Done\n");
|
|
||||||
if (args->save || (!args->debug && args->reset_type == RT_MANUAL_USB))
|
if (args->save || (!args->debug && args->reset_type == RT_MANUAL_USB))
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
228
libdevs.c
228
libdevs.c
@ -1103,8 +1103,8 @@ struct safe_device {
|
|||||||
char *saved_blocks;
|
char *saved_blocks;
|
||||||
uint64_t *sb_positions;
|
uint64_t *sb_positions;
|
||||||
SDEV_BITMAP_WORD *sb_bitmap;
|
SDEV_BITMAP_WORD *sb_bitmap;
|
||||||
int sb_n;
|
uint64_t sb_n;
|
||||||
int sb_max;
|
uint64_t sb_max;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct safe_device *dev_sdev(struct device *dev)
|
static inline struct safe_device *dev_sdev(struct device *dev)
|
||||||
@ -1120,42 +1120,96 @@ static int sdev_read_blocks(struct device *dev, char *buf,
|
|||||||
first_pos, last_pos);
|
first_pos, last_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdev_save_block(struct safe_device *sdev, uint64_t block_pos)
|
static int sdev_is_block_saved(struct safe_device *sdev, uint64_t pos)
|
||||||
{
|
{
|
||||||
const int block_order = dev_get_block_order(sdev->shadow_dev);
|
lldiv_t idx;
|
||||||
lldiv_t idx = lldiv(block_pos, SDEV_BITMAP_BITS_PER_WORD);
|
SDEV_BITMAP_WORD set_bit;
|
||||||
SDEV_BITMAP_WORD set_bit = (SDEV_BITMAP_WORD)1 << idx.rem;
|
|
||||||
char *block_buf;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* Is this block already saved? */
|
|
||||||
if (!sdev->sb_bitmap) {
|
if (!sdev->sb_bitmap) {
|
||||||
int i;
|
uint64_t 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_positions[i] == block_pos) {
|
if (sdev->sb_positions[i] == pos) {
|
||||||
/* The block is already saved. */
|
/* The block is already saved. */
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (sdev->sb_bitmap[idx.quot] & set_bit) {
|
return false;
|
||||||
/* The block is already saved. */
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The block hasn't been saved before. Save it. */
|
idx = lldiv(pos, SDEV_BITMAP_BITS_PER_WORD);
|
||||||
assert(sdev->sb_n < sdev->sb_max);
|
set_bit = (SDEV_BITMAP_WORD)1 << idx.rem;
|
||||||
block_buf = (char *)align_mem(sdev->saved_blocks, block_order) +
|
return !!(sdev->sb_bitmap[idx.quot] & set_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdev_mark_blocks(struct safe_device *sdev,
|
||||||
|
uint64_t first_pos, uint64_t last_pos)
|
||||||
|
{
|
||||||
|
uint64_t pos;
|
||||||
|
|
||||||
|
for (pos = first_pos; pos <= last_pos; pos++) {
|
||||||
|
if (sdev->sb_bitmap) {
|
||||||
|
lldiv_t idx = lldiv(pos, SDEV_BITMAP_BITS_PER_WORD);
|
||||||
|
SDEV_BITMAP_WORD set_bit = (SDEV_BITMAP_WORD)1 <<
|
||||||
|
idx.rem;
|
||||||
|
sdev->sb_bitmap[idx.quot] |= set_bit;
|
||||||
|
}
|
||||||
|
sdev->sb_positions[sdev->sb_n] = pos;
|
||||||
|
sdev->sb_n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load blocks into cache. */
|
||||||
|
static int sdev_load_blocks(struct safe_device *sdev,
|
||||||
|
uint64_t first_pos, uint64_t last_pos)
|
||||||
|
{
|
||||||
|
const int block_order = dev_get_block_order(sdev->shadow_dev);
|
||||||
|
char *block_buf = (char *)align_mem(sdev->saved_blocks, block_order) +
|
||||||
(sdev->sb_n << block_order);
|
(sdev->sb_n << block_order);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
assert(sdev->sb_n + (last_pos - first_pos + 1) < sdev->sb_max);
|
||||||
|
|
||||||
rc = sdev->shadow_dev->read_blocks(sdev->shadow_dev, block_buf,
|
rc = sdev->shadow_dev->read_blocks(sdev->shadow_dev, block_buf,
|
||||||
block_pos, block_pos);
|
first_pos, last_pos);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
/* Bookkeeping. */
|
/* Bookkeeping. */
|
||||||
if (sdev->sb_bitmap)
|
sdev_mark_blocks(sdev, first_pos, last_pos);
|
||||||
sdev->sb_bitmap[idx.quot] |= set_bit;
|
return 0;
|
||||||
sdev->sb_positions[sdev->sb_n] = block_pos;
|
}
|
||||||
sdev->sb_n++;
|
|
||||||
|
static int sdev_save_block(struct safe_device *sdev,
|
||||||
|
uint64_t first_pos, uint64_t last_pos)
|
||||||
|
{
|
||||||
|
uint64_t pos, start_pos;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
start_pos = first_pos;
|
||||||
|
for (pos = first_pos; pos <= last_pos; pos++) {
|
||||||
|
if (sdev_is_block_saved(sdev, pos)) {
|
||||||
|
if (start_pos < pos) {
|
||||||
|
/* The blocks haven't been saved before.
|
||||||
|
* Save them now.
|
||||||
|
*/
|
||||||
|
rc = sdev_load_blocks(sdev, start_pos, pos - 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
} else if (start_pos == pos) {
|
||||||
|
/* Do nothing. */
|
||||||
|
} else {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
start_pos = pos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_pos <= last_pos) {
|
||||||
|
rc = sdev_load_blocks(sdev, start_pos, last_pos);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1163,16 +1217,10 @@ static int sdev_write_blocks(struct device *dev, const char *buf,
|
|||||||
uint64_t first_pos, uint64_t last_pos)
|
uint64_t first_pos, uint64_t last_pos)
|
||||||
{
|
{
|
||||||
struct safe_device *sdev = dev_sdev(dev);
|
struct safe_device *sdev = dev_sdev(dev);
|
||||||
uint64_t pos;
|
int rc = sdev_save_block(sdev, first_pos, last_pos);
|
||||||
|
|
||||||
/* XXX The code should take advantage of the fact that
|
|
||||||
* the blocks are sequential and execute a single read.
|
|
||||||
*/
|
|
||||||
for (pos = first_pos; pos <= last_pos; pos++) {
|
|
||||||
int rc = sdev_save_block(sdev, pos);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
|
||||||
|
|
||||||
return sdev->shadow_dev->write_blocks(sdev->shadow_dev, buf,
|
return sdev->shadow_dev->write_blocks(sdev->shadow_dev, buf,
|
||||||
first_pos, last_pos);
|
first_pos, last_pos);
|
||||||
@ -1183,36 +1231,99 @@ static int sdev_reset(struct device *dev)
|
|||||||
return dev_reset(dev_sdev(dev)->shadow_dev);
|
return dev_reset(dev_sdev(dev)->shadow_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdev_carefully_recover(struct safe_device *sdev, char *buffer,
|
||||||
|
uint64_t first_pos, uint64_t last_pos)
|
||||||
|
{
|
||||||
|
const int block_size = dev_get_block_size(sdev->shadow_dev);
|
||||||
|
uint64_t pos;
|
||||||
|
int rc = sdev->shadow_dev->write_blocks(sdev->shadow_dev,
|
||||||
|
buffer, first_pos, last_pos);
|
||||||
|
if (!rc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (pos = first_pos; pos <= last_pos; pos++) {
|
||||||
|
int rc = sdev->shadow_dev->write_blocks(sdev->shadow_dev,
|
||||||
|
buffer, pos, pos);
|
||||||
|
if (rc) {
|
||||||
|
/* Do not abort, try to recover all bocks. */
|
||||||
|
warn("Failed to recover block 0x%" PRIx64
|
||||||
|
" due to a write error", pos);
|
||||||
|
}
|
||||||
|
buffer += block_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t sdev_bitmap_length(struct device *dev)
|
||||||
|
{
|
||||||
|
const int block_order = dev_get_block_order(dev);
|
||||||
|
lldiv_t idx = lldiv(dev_get_size_byte(dev) >> block_order,
|
||||||
|
SDEV_BITMAP_BITS_PER_WORD);
|
||||||
|
return (idx.quot + (idx.rem ? 1 : 0)) * sizeof(SDEV_BITMAP_WORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdev_recover(struct device *dev, uint64_t very_last_pos)
|
||||||
|
{
|
||||||
|
struct safe_device *sdev = dev_sdev(dev);
|
||||||
|
const int block_order = dev_get_block_order(sdev->shadow_dev);
|
||||||
|
char *first_block = align_mem(sdev->saved_blocks, block_order);
|
||||||
|
uint64_t i, first_pos, last_pos;
|
||||||
|
char *start_buf;
|
||||||
|
int has_seq;
|
||||||
|
|
||||||
|
has_seq = false;
|
||||||
|
for (i = 0; i < sdev->sb_n; i++) {
|
||||||
|
uint64_t pos = sdev->sb_positions[i];
|
||||||
|
|
||||||
|
if (!has_seq) {
|
||||||
|
if (pos > very_last_pos)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
last_pos = first_pos = pos;
|
||||||
|
start_buf = first_block + (i << block_order);
|
||||||
|
has_seq = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos <= very_last_pos && pos == last_pos + 1) {
|
||||||
|
last_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdev_carefully_recover(sdev, start_buf, first_pos, last_pos);
|
||||||
|
|
||||||
|
has_seq = pos <= very_last_pos;
|
||||||
|
if (has_seq) {
|
||||||
|
last_pos = first_pos = pos;
|
||||||
|
start_buf = first_block + (i << block_order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_seq) {
|
||||||
|
sdev_carefully_recover(sdev, start_buf, first_pos, last_pos);
|
||||||
|
has_seq = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdev_flush(struct device *dev)
|
||||||
|
{
|
||||||
|
struct safe_device *sdev = dev_sdev(dev);
|
||||||
|
|
||||||
|
if (sdev->sb_n <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdev->sb_n = 0;
|
||||||
|
|
||||||
|
if (sdev->sb_bitmap)
|
||||||
|
memset(sdev->sb_bitmap, 0,
|
||||||
|
sdev_bitmap_length(sdev->shadow_dev));
|
||||||
|
}
|
||||||
|
|
||||||
static void sdev_free(struct device *dev)
|
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
|
sdev_recover(dev, UINT_LEAST64_MAX);
|
||||||
* some blocks are sequential and execute less write calls.
|
sdev_flush(dev);
|
||||||
*/
|
|
||||||
if (sdev->sb_n > 0) {
|
|
||||||
const int block_order = dev_get_block_order(sdev->shadow_dev);
|
|
||||||
char *first_block = align_mem(sdev->saved_blocks, block_order);
|
|
||||||
char *block_buf = first_block +
|
|
||||||
((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);
|
|
||||||
|
|
||||||
/* Restore blocks in reverse order to cope with
|
|
||||||
* wraparound and chain drives.
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
int rc = sdev->shadow_dev->write_blocks(
|
|
||||||
sdev->shadow_dev, block_buf, *ppos, *ppos);
|
|
||||||
if (rc) {
|
|
||||||
/* Do not abort, try to recover all bocks. */
|
|
||||||
warn("Failed to recover block 0x%" PRIx64
|
|
||||||
" due to a write error", *ppos);
|
|
||||||
}
|
|
||||||
block_buf -= block_size;
|
|
||||||
ppos--;
|
|
||||||
} while (block_buf >= first_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(sdev->sb_bitmap);
|
free(sdev->sb_bitmap);
|
||||||
free(sdev->sb_positions);
|
free(sdev->sb_positions);
|
||||||
@ -1246,10 +1357,7 @@ struct device *create_safe_device(struct device *dev, uint64_t max_blocks,
|
|||||||
goto saved_blocks;
|
goto saved_blocks;
|
||||||
|
|
||||||
if (!min_memory) {
|
if (!min_memory) {
|
||||||
lldiv_t idx = lldiv(dev_get_size_byte(dev) >> block_order,
|
length = sdev_bitmap_length(dev);
|
||||||
SDEV_BITMAP_BITS_PER_WORD);
|
|
||||||
length = (idx.quot + (idx.rem ? 1 : 0)) *
|
|
||||||
sizeof(SDEV_BITMAP_WORD);
|
|
||||||
sdev->sb_bitmap = malloc(length);
|
sdev->sb_bitmap = malloc(length);
|
||||||
if (!sdev->sb_bitmap)
|
if (!sdev->sb_bitmap)
|
||||||
goto offsets;
|
goto offsets;
|
||||||
|
@ -103,4 +103,7 @@ struct device *pdev_detach_and_free(struct device *dev);
|
|||||||
struct device *create_safe_device(struct device *dev, uint64_t max_blocks,
|
struct device *create_safe_device(struct device *dev, uint64_t max_blocks,
|
||||||
int min_memory);
|
int min_memory);
|
||||||
|
|
||||||
|
void sdev_recover(struct device *dev, uint64_t very_last_pos);
|
||||||
|
void sdev_flush(struct device *dev);
|
||||||
|
|
||||||
#endif /* HEADER_LIBDEVS_H */
|
#endif /* HEADER_LIBDEVS_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user