mirror of https://gitee.com/openkylin/qemu.git
block/blklogwrites: Add an option for appending to an old log
Suggested by Kevin Wolf. May be useful when testing multiple batches of writes or doing long-term testing involving restarts of the VM. Signed-off-by: Ari Sundholm <ari@tuxera.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
2dacaf7c82
commit
0878b3c113
|
@ -24,6 +24,10 @@
|
||||||
#define LOG_FUA_FLAG (1 << 1)
|
#define LOG_FUA_FLAG (1 << 1)
|
||||||
#define LOG_DISCARD_FLAG (1 << 2)
|
#define LOG_DISCARD_FLAG (1 << 2)
|
||||||
#define LOG_MARK_FLAG (1 << 3)
|
#define LOG_MARK_FLAG (1 << 3)
|
||||||
|
#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
|
||||||
|
| LOG_FUA_FLAG \
|
||||||
|
| LOG_DISCARD_FLAG \
|
||||||
|
| LOG_MARK_FLAG)
|
||||||
|
|
||||||
#define WRITE_LOG_VERSION 1ULL
|
#define WRITE_LOG_VERSION 1ULL
|
||||||
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
|
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
|
||||||
|
@ -57,6 +61,11 @@ static QemuOptsList runtime_opts = {
|
||||||
.name = "blklogwrites",
|
.name = "blklogwrites",
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||||
.desc = {
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = "log-append",
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
.help = "Append to an existing log",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "log-sector-size",
|
.name = "log-sector-size",
|
||||||
.type = QEMU_OPT_SIZE,
|
.type = QEMU_OPT_SIZE,
|
||||||
|
@ -72,6 +81,53 @@ static inline uint32_t blk_log_writes_log2(uint32_t value)
|
||||||
return 31 - clz32(value);
|
return 31 - clz32(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
|
||||||
|
{
|
||||||
|
return sector_size < (1ull << 24) && is_power_of_2(sector_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
|
||||||
|
uint32_t sector_size,
|
||||||
|
uint64_t nr_entries,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
uint64_t cur_sector = 1;
|
||||||
|
uint64_t cur_idx = 0;
|
||||||
|
uint32_t sector_bits = blk_log_writes_log2(sector_size);
|
||||||
|
struct log_write_entry cur_entry;
|
||||||
|
|
||||||
|
while (cur_idx < nr_entries) {
|
||||||
|
int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
|
||||||
|
sizeof(cur_entry));
|
||||||
|
if (read_ret < 0) {
|
||||||
|
error_setg_errno(errp, -read_ret,
|
||||||
|
"Failed to read log entry %"PRIu64, cur_idx);
|
||||||
|
return (uint64_t)-1ull;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
|
||||||
|
error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
|
||||||
|
le64_to_cpu(cur_entry.flags), cur_idx);
|
||||||
|
return (uint64_t)-1ull;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Account for the sector of the entry itself */
|
||||||
|
++cur_sector;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Account for the data of the write.
|
||||||
|
* For discards, this data is not present.
|
||||||
|
*/
|
||||||
|
if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
|
||||||
|
cur_sector += le64_to_cpu(cur_entry.nr_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
++cur_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur_sector;
|
||||||
|
}
|
||||||
|
|
||||||
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -80,6 +136,7 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
uint64_t log_sector_size;
|
uint64_t log_sector_size;
|
||||||
|
bool log_append;
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
@ -98,20 +155,6 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
|
|
||||||
BDRV_SECTOR_SIZE);
|
|
||||||
|
|
||||||
if (log_sector_size > (1ull << 23) || !is_power_of_2(log_sector_size)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->sectorsize = log_sector_size;
|
|
||||||
s->sectorbits = blk_log_writes_log2(log_sector_size);
|
|
||||||
s->cur_log_sector = 1;
|
|
||||||
s->nr_entries = 0;
|
|
||||||
|
|
||||||
/* Open the log file */
|
/* Open the log file */
|
||||||
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
|
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
|
||||||
&local_err);
|
&local_err);
|
||||||
|
@ -121,7 +164,83 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_append = qemu_opt_get_bool(opts, "log-append", false);
|
||||||
|
|
||||||
|
if (log_append) {
|
||||||
|
struct log_write_super log_sb = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
if (qemu_opt_find(opts, "log-sector-size")) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_setg(errp, "log-append and log-sector-size are mutually "
|
||||||
|
"exclusive");
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read log superblock or fake one for an empty log */
|
||||||
|
if (!bdrv_getlength(s->log_file->bs)) {
|
||||||
|
log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
|
||||||
|
log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
|
||||||
|
log_sb.nr_entries = cpu_to_le64(0);
|
||||||
|
log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
|
||||||
|
} else {
|
||||||
|
ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Could not read log superblock");
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_setg(errp, "Invalid log superblock magic");
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_setg(errp, "Unsupported log version %"PRIu64,
|
||||||
|
le64_to_cpu(log_sb.version));
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_sector_size = le32_to_cpu(log_sb.sectorsize);
|
||||||
|
s->cur_log_sector = 1;
|
||||||
|
s->nr_entries = 0;
|
||||||
|
|
||||||
|
if (blk_log_writes_sector_size_valid(log_sector_size)) {
|
||||||
|
s->cur_log_sector =
|
||||||
|
blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
|
||||||
|
le64_to_cpu(log_sb.nr_entries), &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->nr_entries = le64_to_cpu(log_sb.nr_entries);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
|
||||||
|
BDRV_SECTOR_SIZE);
|
||||||
|
s->cur_log_sector = 1;
|
||||||
|
s->nr_entries = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blk_log_writes_sector_size_valid(log_sector_size)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->sectorsize = log_sector_size;
|
||||||
|
s->sectorbits = blk_log_writes_log2(log_sector_size);
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
fail_log:
|
||||||
|
if (ret < 0) {
|
||||||
|
bdrv_unref_child(bs, s->log_file);
|
||||||
|
s->log_file = NULL;
|
||||||
|
}
|
||||||
fail:
|
fail:
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_unref_child(bs, bs->file);
|
bdrv_unref_child(bs, bs->file);
|
||||||
|
|
|
@ -3062,7 +3062,8 @@
|
||||||
{ 'struct': 'BlockdevOptionsBlklogwrites',
|
{ 'struct': 'BlockdevOptionsBlklogwrites',
|
||||||
'data': { 'file': 'BlockdevRef',
|
'data': { 'file': 'BlockdevRef',
|
||||||
'log': 'BlockdevRef',
|
'log': 'BlockdevRef',
|
||||||
'*log-sector-size': 'uint32' } }
|
'*log-sector-size': 'uint32',
|
||||||
|
'*log-append': 'bool' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptionsBlkverify:
|
# @BlockdevOptionsBlkverify:
|
||||||
|
|
Loading…
Reference in New Issue