-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1
 
 iQEcBAABAgAGBQJVlQAPAAoJEJykq7OBq3PIV7EH/iijbZ6q/i1UdinVsF09Dls3
 f++CLEb2VUx25KqReDwUDjVsimBeb9jntLUyqHuHAYlCwrrJGcbTPQqw3d2k+MBB
 pbWcaFyjGyHb7LOd1JOVRHK0z4QfjVNPu9IjGMsUXP3/JXtqkbIcLwg1UF6p/bzj
 NXSaCv5NOSEpJdxCN/wYXCi78G9WfsSF0X1qCOLsJ/+sm8hung21T/o9CTph0Yua
 6aiZodnB6vvk/YLPz1/RS37LabiMmgLeUdgbjxYhjBZBUQckmpOExm9gKRzujT/h
 wZCuBeySdwqEDF0sLWOB9gDZRoTujStsnwpK6MmD/o0lXiWGbeYBtjakx93EY0c=
 =HgNT
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging

# gpg: Signature made Thu Jul  2 10:10:39 2015 BST using RSA key ID 81AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>"
# gpg:                 aka "Stefan Hajnoczi <stefanha@gmail.com>"

* remotes/stefanha/tags/block-pull-request:
  block: remove redundant check before g_slist_find()
  block/nfs: limit maximum readahead size to 1MB
  block/iscsi: restore compatiblity with libiscsi 1.9.0
  iotests: Use event_wait in wait_ready
  qemu-iotests: Add test case for mirror with unmap
  qemu-iotests: Make block job methods common
  block: Remove bdrv_reset_dirty
  block: Fix dirty bitmap in bdrv_co_discard
  mirror: Do zero write on target if sectors not allocated
  qmp: Add optional bool "unmap" to drive-mirror
  block: Add bdrv_get_block_status_above
  timer: Use a single definition of NSEC_PER_SEC for the whole codebase
  timer: Move NANOSECONDS_PER_SECONDS to timer.h
  blockdev: no need to drain+flush in hmp_drive_del
  qapi: Rename 'dirty-bitmap' mode to 'incremental'
  qcow2: Handle EAGAIN returned from update_refcount
  block/iscsi: add support for request timeouts

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-07-02 10:44:34 +01:00
commit 6686ce3f16
31 changed files with 354 additions and 171 deletions

12
block.c
View File

@ -3528,18 +3528,6 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
} }
} }
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
int nr_sectors)
{
BdrvDirtyBitmap *bitmap;
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
continue;
}
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
}
}
/** /**
* Advance an HBitmapIter to an arbitrary offset. * Advance an HBitmapIter to an arbitrary offset.
*/ */

View File

@ -38,7 +38,7 @@ typedef struct CowRequest {
typedef struct BackupBlockJob { typedef struct BackupBlockJob {
BlockJob common; BlockJob common;
BlockDriverState *target; BlockDriverState *target;
/* bitmap for sync=dirty-bitmap */ /* bitmap for sync=incremental */
BdrvDirtyBitmap *sync_bitmap; BdrvDirtyBitmap *sync_bitmap;
MirrorSyncMode sync_mode; MirrorSyncMode sync_mode;
RateLimit limit; RateLimit limit;
@ -365,7 +365,7 @@ static void coroutine_fn backup_run(void *opaque)
qemu_coroutine_yield(); qemu_coroutine_yield();
job->common.busy = true; job->common.busy = true;
} }
} else if (job->sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) { } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
ret = backup_run_incremental(job); ret = backup_run_incremental(job);
} else { } else {
/* Both FULL and TOP SYNC_MODE's require copying.. */ /* Both FULL and TOP SYNC_MODE's require copying.. */
@ -497,10 +497,10 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
return; return;
} }
if (sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) { if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
if (!sync_bitmap) { if (!sync_bitmap) {
error_setg(errp, "must provide a valid bitmap name for " error_setg(errp, "must provide a valid bitmap name for "
"\"dirty-bitmap\" sync mode"); "\"incremental\" sync mode");
return; return;
} }
@ -535,7 +535,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
job->on_target_error = on_target_error; job->on_target_error = on_target_error;
job->target = target; job->target = target;
job->sync_mode = sync_mode; job->sync_mode = sync_mode;
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP ? job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
sync_bitmap : NULL; sync_bitmap : NULL;
job->common.len = len; job->common.len = len;
job->common.co = qemu_coroutine_create(backup_run); job->common.co = qemu_coroutine_create(backup_run);

View File

@ -283,7 +283,7 @@ void bdrv_drain_all(void)
} }
aio_context_release(aio_context); aio_context_release(aio_context);
if (!aio_ctxs || !g_slist_find(aio_ctxs, aio_context)) { if (!g_slist_find(aio_ctxs, aio_context)) {
aio_ctxs = g_slist_prepend(aio_ctxs, aio_context); aio_ctxs = g_slist_prepend(aio_ctxs, aio_context);
} }
} }
@ -1531,28 +1531,54 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
return ret; return ret;
} }
/* Coroutine wrapper for bdrv_get_block_status() */ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs,
static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque) BlockDriverState *base,
int64_t sector_num,
int nb_sectors,
int *pnum)
{
BlockDriverState *p;
int64_t ret = 0;
assert(bs != base);
for (p = bs; p != base; p = p->backing_hd) {
ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum);
if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) {
break;
}
/* [sector_num, pnum] unallocated on this layer, which could be only
* the first part of [sector_num, nb_sectors]. */
nb_sectors = MIN(nb_sectors, *pnum);
}
return ret;
}
/* Coroutine wrapper for bdrv_get_block_status_above() */
static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque)
{ {
BdrvCoGetBlockStatusData *data = opaque; BdrvCoGetBlockStatusData *data = opaque;
BlockDriverState *bs = data->bs;
data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors, data->ret = bdrv_co_get_block_status_above(data->bs, data->base,
data->pnum); data->sector_num,
data->nb_sectors,
data->pnum);
data->done = true; data->done = true;
} }
/* /*
* Synchronous wrapper around bdrv_co_get_block_status(). * Synchronous wrapper around bdrv_co_get_block_status_above().
* *
* See bdrv_co_get_block_status() for details. * See bdrv_co_get_block_status_above() for details.
*/ */
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, int64_t bdrv_get_block_status_above(BlockDriverState *bs,
int nb_sectors, int *pnum) BlockDriverState *base,
int64_t sector_num,
int nb_sectors, int *pnum)
{ {
Coroutine *co; Coroutine *co;
BdrvCoGetBlockStatusData data = { BdrvCoGetBlockStatusData data = {
.bs = bs, .bs = bs,
.base = base,
.sector_num = sector_num, .sector_num = sector_num,
.nb_sectors = nb_sectors, .nb_sectors = nb_sectors,
.pnum = pnum, .pnum = pnum,
@ -1561,11 +1587,11 @@ int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
if (qemu_in_coroutine()) { if (qemu_in_coroutine()) {
/* Fast-path if already in coroutine context */ /* Fast-path if already in coroutine context */
bdrv_get_block_status_co_entry(&data); bdrv_get_block_status_above_co_entry(&data);
} else { } else {
AioContext *aio_context = bdrv_get_aio_context(bs); AioContext *aio_context = bdrv_get_aio_context(bs);
co = qemu_coroutine_create(bdrv_get_block_status_co_entry); co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry);
qemu_coroutine_enter(co, &data); qemu_coroutine_enter(co, &data);
while (!data.done) { while (!data.done) {
aio_poll(aio_context, true); aio_poll(aio_context, true);
@ -1574,6 +1600,14 @@ int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
return data.ret; return data.ret;
} }
int64_t bdrv_get_block_status(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors, int *pnum)
{
return bdrv_get_block_status_above(bs, bs->backing_hd,
sector_num, nb_sectors, pnum);
}
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum)
{ {
@ -2378,8 +2412,6 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
return -EPERM; return -EPERM;
} }
bdrv_reset_dirty(bs, sector_num, nb_sectors);
/* Do nothing if disabled. */ /* Do nothing if disabled. */
if (!(bs->open_flags & BDRV_O_UNMAP)) { if (!(bs->open_flags & BDRV_O_UNMAP)) {
return 0; return 0;
@ -2389,6 +2421,8 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
return 0; return 0;
} }
bdrv_set_dirty(bs, sector_num, nb_sectors);
max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS); max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS);
while (nb_sectors > 0) { while (nb_sectors > 0) {
int ret; int ret;

View File

@ -70,6 +70,7 @@ typedef struct IscsiLun {
bool dpofua; bool dpofua;
bool has_write_same; bool has_write_same;
bool force_next_flush; bool force_next_flush;
bool request_timed_out;
} IscsiLun; } IscsiLun;
typedef struct IscsiTask { typedef struct IscsiTask {
@ -100,7 +101,8 @@ typedef struct IscsiAIOCB {
#endif #endif
} IscsiAIOCB; } IscsiAIOCB;
#define EVENT_INTERVAL 250 /* libiscsi uses time_t so its enough to process events every second */
#define EVENT_INTERVAL 1000
#define NOP_INTERVAL 5000 #define NOP_INTERVAL 5000
#define MAX_NOP_FAILURES 3 #define MAX_NOP_FAILURES 3
#define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times) #define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times)
@ -167,6 +169,19 @@ static inline unsigned exp_random(double mean)
return -mean * log((double)rand() / RAND_MAX); return -mean * log((double)rand() / RAND_MAX);
} }
/* SCSI_STATUS_TASK_SET_FULL and SCSI_STATUS_TIMEOUT were introduced
* in libiscsi 1.10.0 as part of an enum. The LIBISCSI_API_VERSION
* macro was introduced in 1.11.0. So use the API_VERSION macro as
* a hint that the macros are defined and define them ourselves
* otherwise to keep the required libiscsi version at 1.9.0 */
#if !defined(LIBISCSI_API_VERSION)
#define QEMU_SCSI_STATUS_TASK_SET_FULL 0x28
#define QEMU_SCSI_STATUS_TIMEOUT 0x0f000002
#else
#define QEMU_SCSI_STATUS_TASK_SET_FULL SCSI_STATUS_TASK_SET_FULL
#define QEMU_SCSI_STATUS_TIMEOUT SCSI_STATUS_TIMEOUT
#endif
static void static void
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *opaque) void *command_data, void *opaque)
@ -187,13 +202,19 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
iTask->do_retry = 1; iTask->do_retry = 1;
goto out; goto out;
} }
/* status 0x28 is SCSI_TASK_SET_FULL. It was first introduced if (status == SCSI_STATUS_BUSY ||
* in libiscsi 1.10.0. Hardcode this value here to avoid status == QEMU_SCSI_STATUS_TIMEOUT ||
* the need to bump the libiscsi requirement to 1.10.0 */ status == QEMU_SCSI_STATUS_TASK_SET_FULL) {
if (status == SCSI_STATUS_BUSY || status == 0x28) {
unsigned retry_time = unsigned retry_time =
exp_random(iscsi_retry_times[iTask->retries - 1]); exp_random(iscsi_retry_times[iTask->retries - 1]);
error_report("iSCSI Busy/TaskSetFull (retry #%u in %u ms): %s", if (status == QEMU_SCSI_STATUS_TIMEOUT) {
/* make sure the request is rescheduled AFTER the
* reconnect is initiated */
retry_time = EVENT_INTERVAL * 2;
iTask->iscsilun->request_timed_out = true;
}
error_report("iSCSI Busy/TaskSetFull/TimeOut"
" (retry #%u in %u ms): %s",
iTask->retries, retry_time, iTask->retries, retry_time,
iscsi_get_error(iscsi)); iscsi_get_error(iscsi));
aio_timer_init(iTask->iscsilun->aio_context, aio_timer_init(iTask->iscsilun->aio_context,
@ -277,20 +298,26 @@ iscsi_set_events(IscsiLun *iscsilun)
iscsilun); iscsilun);
iscsilun->events = ev; iscsilun->events = ev;
} }
/* newer versions of libiscsi may return zero events. In this
* case start a timer to ensure we are able to return to service
* once this situation changes. */
if (!ev) {
timer_mod(iscsilun->event_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
}
} }
static void iscsi_timed_set_events(void *opaque) static void iscsi_timed_check_events(void *opaque)
{ {
IscsiLun *iscsilun = opaque; IscsiLun *iscsilun = opaque;
/* check for timed out requests */
iscsi_service(iscsilun->iscsi, 0);
if (iscsilun->request_timed_out) {
iscsilun->request_timed_out = false;
iscsi_reconnect(iscsilun->iscsi);
}
/* newer versions of libiscsi may return zero events. Ensure we are able
* to return to service once this situation changes. */
iscsi_set_events(iscsilun); iscsi_set_events(iscsilun);
timer_mod(iscsilun->event_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
} }
static void static void
@ -1093,16 +1120,37 @@ static char *parse_initiator_name(const char *target)
return iscsi_name; return iscsi_name;
} }
static int parse_timeout(const char *target)
{
QemuOptsList *list;
QemuOpts *opts;
const char *timeout;
list = qemu_find_opts("iscsi");
if (list) {
opts = qemu_opts_find(list, target);
if (!opts) {
opts = QTAILQ_FIRST(&list->head);
}
if (opts) {
timeout = qemu_opt_get(opts, "timeout");
if (timeout) {
return atoi(timeout);
}
}
}
return 0;
}
static void iscsi_nop_timed_event(void *opaque) static void iscsi_nop_timed_event(void *opaque)
{ {
IscsiLun *iscsilun = opaque; IscsiLun *iscsilun = opaque;
if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) { if (iscsi_get_nops_in_flight(iscsilun->iscsi) >= MAX_NOP_FAILURES) {
error_report("iSCSI: NOP timeout. Reconnecting..."); error_report("iSCSI: NOP timeout. Reconnecting...");
iscsi_reconnect(iscsilun->iscsi); iscsilun->request_timed_out = true;
} } else if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages.");
return; return;
} }
@ -1260,10 +1308,13 @@ static void iscsi_attach_aio_context(BlockDriverState *bs,
timer_mod(iscsilun->nop_timer, timer_mod(iscsilun->nop_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
/* Prepare a timer for a delayed call to iscsi_set_events */ /* Set up a timer for periodic calls to iscsi_set_events and to
* scan for command timeout */
iscsilun->event_timer = aio_timer_new(iscsilun->aio_context, iscsilun->event_timer = aio_timer_new(iscsilun->aio_context,
QEMU_CLOCK_REALTIME, SCALE_MS, QEMU_CLOCK_REALTIME, SCALE_MS,
iscsi_timed_set_events, iscsilun); iscsi_timed_check_events, iscsilun);
timer_mod(iscsilun->event_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
} }
static void iscsi_modesense_sync(IscsiLun *iscsilun) static void iscsi_modesense_sync(IscsiLun *iscsilun)
@ -1318,7 +1369,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
QemuOpts *opts; QemuOpts *opts;
Error *local_err = NULL; Error *local_err = NULL;
const char *filename; const char *filename;
int i, ret = 0; int i, ret = 0, timeout = 0;
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);
@ -1388,6 +1439,16 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
goto out; goto out;
} }
/* timeout handling is broken in libiscsi before 1.15.0 */
timeout = parse_timeout(iscsi_url->target);
#if defined(LIBISCSI_API_VERSION) && LIBISCSI_API_VERSION >= 20150621
iscsi_set_timeout(iscsi, timeout);
#else
if (timeout) {
error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
}
#endif
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
error_setg(errp, "iSCSI: Failed to connect to LUN : %s", error_setg(errp, "iSCSI: Failed to connect to LUN : %s",
iscsi_get_error(iscsi)); iscsi_get_error(iscsi));
@ -1736,6 +1797,10 @@ static QemuOptsList qemu_iscsi_opts = {
.name = "initiator-name", .name = "initiator-name",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "Initiator iqn name to use when connecting", .help = "Initiator iqn name to use when connecting",
},{
.name = "timeout",
.type = QEMU_OPT_NUMBER,
.help = "Request timeout in seconds (default 0 = no timeout)",
}, },
{ /* end of list */ } { /* end of list */ }
}, },

View File

@ -58,6 +58,7 @@ typedef struct MirrorBlockJob {
int in_flight; int in_flight;
int sectors_in_flight; int sectors_in_flight;
int ret; int ret;
bool unmap;
} MirrorBlockJob; } MirrorBlockJob;
typedef struct MirrorOp { typedef struct MirrorOp {
@ -164,6 +165,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
uint64_t delay_ns = 0; uint64_t delay_ns = 0;
MirrorOp *op; MirrorOp *op;
int pnum;
int64_t ret;
s->sector_num = hbitmap_iter_next(&s->hbi); s->sector_num = hbitmap_iter_next(&s->hbi);
if (s->sector_num < 0) { if (s->sector_num < 0) {
@ -290,8 +293,22 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
s->in_flight++; s->in_flight++;
s->sectors_in_flight += nb_sectors; s->sectors_in_flight += nb_sectors;
trace_mirror_one_iteration(s, sector_num, nb_sectors); trace_mirror_one_iteration(s, sector_num, nb_sectors);
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
mirror_read_complete, op); ret = bdrv_get_block_status_above(source, NULL, sector_num,
nb_sectors, &pnum);
if (ret < 0 || pnum < nb_sectors ||
(ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) {
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
mirror_read_complete, op);
} else if (ret & BDRV_BLOCK_ZERO) {
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
mirror_write_complete, op);
} else {
assert(!(ret & BDRV_BLOCK_DATA));
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
mirror_write_complete, op);
}
return delay_ns; return delay_ns;
} }
@ -652,6 +669,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
int64_t buf_size, int64_t buf_size,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
void *opaque, Error **errp, void *opaque, Error **errp,
const BlockJobDriver *driver, const BlockJobDriver *driver,
@ -686,6 +704,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
s->base = base; s->base = base;
s->granularity = granularity; s->granularity = granularity;
s->buf_size = MAX(buf_size, granularity); s->buf_size = MAX(buf_size, granularity);
s->unmap = unmap;
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
if (!s->dirty_bitmap) { if (!s->dirty_bitmap) {
@ -704,21 +723,22 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
int64_t speed, uint32_t granularity, int64_t buf_size, int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
void *opaque, Error **errp) void *opaque, Error **errp)
{ {
bool is_none_mode; bool is_none_mode;
BlockDriverState *base; BlockDriverState *base;
if (mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) { if (mode == MIRROR_SYNC_MODE_INCREMENTAL) {
error_setg(errp, "Sync mode 'dirty-bitmap' not supported"); error_setg(errp, "Sync mode 'incremental' not supported");
return; return;
} }
is_none_mode = mode == MIRROR_SYNC_MODE_NONE; is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
mirror_start_job(bs, target, replaces, mirror_start_job(bs, target, replaces,
speed, granularity, buf_size, speed, granularity, buf_size,
on_source_error, on_target_error, cb, opaque, errp, on_source_error, on_target_error, unmap, cb, opaque, errp,
&mirror_job_driver, is_none_mode, base); &mirror_job_driver, is_none_mode, base);
} }
@ -766,7 +786,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
bdrv_ref(base); bdrv_ref(base);
mirror_start_job(bs, base, NULL, speed, 0, 0, mirror_start_job(bs, base, NULL, speed, 0, 0,
on_error, on_error, cb, opaque, &local_err, on_error, on_error, false, cb, opaque, &local_err,
&commit_active_job_driver, false, base); &commit_active_job_driver, false, base);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);

View File

@ -35,6 +35,8 @@
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include <nfsc/libnfs.h> #include <nfsc/libnfs.h>
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
typedef struct NFSClient { typedef struct NFSClient {
struct nfs_context *context; struct nfs_context *context;
struct nfsfh *fh; struct nfsfh *fh;
@ -327,6 +329,11 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
nfs_set_tcp_syncnt(client->context, val); nfs_set_tcp_syncnt(client->context, val);
#ifdef LIBNFS_FEATURE_READAHEAD #ifdef LIBNFS_FEATURE_READAHEAD
} else if (!strcmp(qp->p[i].name, "readahead")) { } else if (!strcmp(qp->p[i].name, "readahead")) {
if (val > QEMU_NFS_MAX_READAHEAD_SIZE) {
error_report("NFS Warning: Truncating NFS readahead"
" size to %d", QEMU_NFS_MAX_READAHEAD_SIZE);
val = QEMU_NFS_MAX_READAHEAD_SIZE;
}
nfs_set_readahead(client->context, val); nfs_set_readahead(client->context, val);
#endif #endif
} else { } else {

View File

@ -940,19 +940,21 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
} }
free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); free_in_cluster = s->cluster_size - offset_into_cluster(s, offset);
if (!offset || free_in_cluster < size) { do {
int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); if (!offset || free_in_cluster < size) {
if (new_cluster < 0) { int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size);
return new_cluster; if (new_cluster < 0) {
return new_cluster;
}
if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) {
offset = new_cluster;
}
} }
if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { assert(offset);
offset = new_cluster; ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER);
} } while (ret == -EAGAIN);
}
assert(offset);
ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }

View File

@ -2167,9 +2167,6 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
return; return;
} }
/* quiesce block driver; prevent further io */
bdrv_drain_all();
bdrv_flush(bs);
bdrv_close(bs); bdrv_close(bs);
/* if we have a device attached to this BlockDriverState /* if we have a device attached to this BlockDriverState
@ -2658,6 +2655,7 @@ void qmp_drive_mirror(const char *device, const char *target,
bool has_buf_size, int64_t buf_size, bool has_buf_size, int64_t buf_size,
bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_source_error, BlockdevOnError on_source_error,
bool has_on_target_error, BlockdevOnError on_target_error, bool has_on_target_error, BlockdevOnError on_target_error,
bool has_unmap, bool unmap,
Error **errp) Error **errp)
{ {
BlockBackend *blk; BlockBackend *blk;
@ -2689,6 +2687,9 @@ void qmp_drive_mirror(const char *device, const char *target,
if (!has_buf_size) { if (!has_buf_size) {
buf_size = DEFAULT_MIRROR_BUF_SIZE; buf_size = DEFAULT_MIRROR_BUF_SIZE;
} }
if (!has_unmap) {
unmap = true;
}
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
@ -2830,6 +2831,7 @@ void qmp_drive_mirror(const char *device, const char *target,
has_replaces ? replaces : NULL, has_replaces ? replaces : NULL,
speed, granularity, buf_size, sync, speed, granularity, buf_size, sync,
on_source_error, on_target_error, on_source_error, on_target_error,
unmap,
block_job_cb, bs, &local_err); block_job_cb, bs, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
bdrv_unref(target_bs); bdrv_unref(target_bs);

View File

@ -210,7 +210,7 @@ full backup as a backing image.
"bitmap": "bitmap0", "bitmap": "bitmap0",
"target": "incremental.0.img", "target": "incremental.0.img",
"format": "qcow2", "format": "qcow2",
"sync": "dirty-bitmap", "sync": "incremental",
"mode": "existing" "mode": "existing"
} }
} }
@ -235,7 +235,7 @@ full backup as a backing image.
"bitmap": "bitmap0", "bitmap": "bitmap0",
"target": "incremental.1.img", "target": "incremental.1.img",
"format": "qcow2", "format": "qcow2",
"sync": "dirty-bitmap", "sync": "incremental",
"mode": "existing" "mode": "existing"
} }
} }
@ -275,7 +275,7 @@ full backup as a backing image.
"bitmap": "bitmap0", "bitmap": "bitmap0",
"target": "incremental.0.img", "target": "incremental.0.img",
"format": "qcow2", "format": "qcow2",
"sync": "dirty-bitmap", "sync": "incremental",
"mode": "existing" "mode": "existing"
} }
} }
@ -308,7 +308,7 @@ full backup as a backing image.
"bitmap": "bitmap0", "bitmap": "bitmap0",
"target": "incremental.0.img", "target": "incremental.0.img",
"format": "qcow2", "format": "qcow2",
"sync": "dirty-bitmap", "sync": "incremental",
"mode": "existing" "mode": "existing"
} }
} }

2
hmp.c
View File

@ -1061,7 +1061,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
false, NULL, false, NULL, false, NULL, false, NULL,
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
true, mode, false, 0, false, 0, false, 0, true, mode, false, 0, false, 0, false, 0,
false, 0, false, 0, &err); false, 0, false, 0, false, true, &err);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);
} }

View File

@ -51,8 +51,6 @@
# define LOG_TB(...) do { } while (0) # define LOG_TB(...) do { } while (0)
#endif #endif
#define NSEC_PER_SEC 1000000000LL
static void cpu_ppc_tb_stop (CPUPPCState *env); static void cpu_ppc_tb_stop (CPUPPCState *env);
static void cpu_ppc_tb_start (CPUPPCState *env); static void cpu_ppc_tb_start (CPUPPCState *env);

View File

@ -26,6 +26,7 @@
* *
*/ */
#include "cpu.h" #include "cpu.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/ppc/spapr.h" #include "hw/ppc/spapr.h"
#include "qapi-event.h" #include "qapi-event.h"
@ -40,8 +41,6 @@ struct sPAPRRTCState {
int64_t ns_offset; int64_t ns_offset;
}; };
#define NSEC_PER_SEC 1000000000LL
void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns)
{ {
sPAPRRTCState *rtc = SPAPR_RTC(dev); sPAPRRTCState *rtc = SPAPR_RTC(dev);

View File

@ -48,7 +48,6 @@
# define DPRINTF_C(format, ...) do { } while (0) # define DPRINTF_C(format, ...) do { } while (0)
#endif #endif
#define NSEC_PER_SEC 1000000000LL
#define SEC_PER_MIN 60 #define SEC_PER_MIN 60
#define MIN_PER_HOUR 60 #define MIN_PER_HOUR 60
#define SEC_PER_HOUR 3600 #define SEC_PER_HOUR 3600

View File

@ -32,7 +32,7 @@
#include "trace.h" #include "trace.h"
#define FRAME_TIMER_FREQ 1000 #define FRAME_TIMER_FREQ 1000
#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define FRAME_TIMER_NS (NSEC_PER_SEC / FRAME_TIMER_FREQ)
#define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8) #define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8)
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints #define NB_MAXINTRATE 8 // Max rate at which controller issues ints

View File

@ -372,6 +372,10 @@ bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs);
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum); int nb_sectors, int *pnum);
int64_t bdrv_get_block_status_above(BlockDriverState *bs,
BlockDriverState *base,
int64_t sector_num,
int nb_sectors, int *pnum);
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
int *pnum); int *pnum);
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,

View File

@ -612,6 +612,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
* @mode: Whether to collapse all images in the chain to the target. * @mode: Whether to collapse all images in the chain to the target.
* @on_source_error: The action to take upon error reading from the source. * @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target. * @on_target_error: The action to take upon error writing to the target.
* @unmap: Whether to unmap target where source sectors only contain zeroes.
* @cb: Completion function for the job. * @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb. * @opaque: Opaque pointer value passed to @cb.
* @errp: Error object. * @errp: Error object.
@ -626,6 +627,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
int64_t speed, uint32_t granularity, int64_t buf_size, int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
void *opaque, Error **errp); void *opaque, Error **errp);
@ -635,7 +637,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
* @target: Block device to write to. * @target: Block device to write to.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @sync_mode: What parts of the disk image should be copied to the destination. * @sync_mode: What parts of the disk image should be copied to the destination.
* @sync_bitmap: The dirty bitmap if sync_mode is MIRROR_SYNC_MODE_DIRTY_BITMAP. * @sync_bitmap: The dirty bitmap if sync_mode is MIRROR_SYNC_MODE_INCREMENTAL.
* @on_source_error: The action to take upon error reading from the source. * @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target. * @on_target_error: The action to take upon error writing to the target.
* @cb: Completion function for the job. * @cb: Completion function for the job.
@ -660,7 +662,5 @@ bool blk_dev_is_medium_locked(BlockBackend *blk);
void blk_dev_resize_cb(BlockBackend *blk); void blk_dev_resize_cb(BlockBackend *blk);
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
int nr_sectors);
#endif /* BLOCK_INT_H */ #endif /* BLOCK_INT_H */

View File

@ -29,8 +29,6 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#define NANOSECONDS_PER_SECOND 1000000000.0
typedef enum { typedef enum {
THROTTLE_BPS_TOTAL, THROTTLE_BPS_TOTAL,
THROTTLE_BPS_READ, THROTTLE_BPS_READ,

View File

@ -5,6 +5,8 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu/notify.h" #include "qemu/notify.h"
#define NSEC_PER_SEC 1000000000LL
/* timers */ /* timers */
#define SCALE_MS 1000000 #define SCALE_MS 1000000

View File

@ -536,12 +536,12 @@
# #
# @none: only copy data written from now on # @none: only copy data written from now on
# #
# @dirty-bitmap: only copy data described by the dirty bitmap. Since: 2.4 # @incremental: only copy data described by the dirty bitmap. Since: 2.4
# #
# Since: 1.3 # Since: 1.3
## ##
{ 'enum': 'MirrorSyncMode', { 'enum': 'MirrorSyncMode',
'data': ['top', 'full', 'none', 'dirty-bitmap'] } 'data': ['top', 'full', 'none', 'incremental'] }
## ##
# @BlockJobType: # @BlockJobType:
@ -724,8 +724,8 @@
# #
# @speed: #optional the maximum speed, in bytes per second # @speed: #optional the maximum speed, in bytes per second
# #
# @bitmap: #optional the name of dirty bitmap if sync is "dirty-bitmap". # @bitmap: #optional the name of dirty bitmap if sync is "incremental".
# Must be present if sync is "dirty-bitmap", must NOT be present # Must be present if sync is "incremental", must NOT be present
# otherwise. (Since 2.4) # otherwise. (Since 2.4)
# #
# @on-source-error: #optional the action to take on an error on the source, # @on-source-error: #optional the action to take on an error on the source,
@ -975,6 +975,11 @@
# @on-target-error: #optional the action to take on an error on the target, # @on-target-error: #optional the action to take on an error on the target,
# default 'report' (no limitations, since this applies to # default 'report' (no limitations, since this applies to
# a different block device than @device). # a different block device than @device).
# @unmap: #optional Whether to try to unmap target sectors where source has
# only zero. If true, and target unallocated sectors will read as zero,
# target image sectors will be unmapped; otherwise, zeroes will be
# written. Both will result in identical contents.
# Default is true. (Since 2.4)
# #
# Returns: nothing on success # Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound # If @device is not a valid block device, DeviceNotFound
@ -987,7 +992,8 @@
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
'*speed': 'int', '*granularity': 'uint32', '*speed': 'int', '*granularity': 'uint32',
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } } '*on-target-error': 'BlockdevOnError',
'*unmap': 'bool' } }
## ##
# @BlockDirtyBitmap # @BlockDirtyBitmap

View File

@ -2294,6 +2294,10 @@ By default qemu will use the iSCSI initiator-name
'iqn.2008-11.org.linux-kvm[:<name>]' but this can also be set from the command 'iqn.2008-11.org.linux-kvm[:<name>]' but this can also be set from the command
line or a configuration file. line or a configuration file.
Since version Qemu 2.4 it is possible to specify a iSCSI request timeout to detect
stalled requests and force a reestablishment of the session. The timeout
is specified in seconds. The default is 0 which means no timeout. Libiscsi
1.15.0 or greater is required for this feature.
Example (without authentication): Example (without authentication):
@example @example
@ -2321,6 +2325,7 @@ DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi,
"-iscsi [user=user][,password=password]\n" "-iscsi [user=user][,password=password]\n"
" [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE\n" " [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE\n"
" [,initiator-name=initiator-iqn][,id=target-iqn]\n" " [,initiator-name=initiator-iqn][,id=target-iqn]\n"
" [,timeout=timeout]\n"
" iSCSI session parameters\n", QEMU_ARCH_ALL) " iSCSI session parameters\n", QEMU_ARCH_ALL)
STEXI STEXI

View File

@ -1137,10 +1137,10 @@ Arguments:
(json-string, optional) (json-string, optional)
- "sync": what parts of the disk image should be copied to the destination; - "sync": what parts of the disk image should be copied to the destination;
possibilities include "full" for all the disk, "top" for only the sectors possibilities include "full" for all the disk, "top" for only the sectors
allocated in the topmost image, "dirty-bitmap" for only the dirty sectors in allocated in the topmost image, "incremental" for only the dirty sectors in
the bitmap, or "none" to only replicate new I/O (MirrorSyncMode). the bitmap, or "none" to only replicate new I/O (MirrorSyncMode).
- "bitmap": dirty bitmap name for sync==dirty-bitmap. Must be present if sync - "bitmap": dirty bitmap name for sync==incremental. Must be present if sync
is "dirty-bitmap", must NOT be present otherwise. is "incremental", must NOT be present otherwise.
- "mode": whether and how QEMU should create a new image - "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths') (NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional) - "speed": the maximum speed, in bytes per second (json-int, optional)
@ -1503,6 +1503,7 @@ EQMP
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
"node-name:s?,replaces:s?," "node-name:s?,replaces:s?,"
"on-source-error:s?,on-target-error:s?," "on-source-error:s?,on-target-error:s?,"
"unmap:b?,"
"granularity:i?,buf-size:i?", "granularity:i?,buf-size:i?",
.mhandler.cmd_new = qmp_marshal_input_drive_mirror, .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
}, },
@ -1542,6 +1543,8 @@ Arguments:
(BlockdevOnError, default 'report') (BlockdevOnError, default 'report')
- "on-target-error": the action to take on an error on the target - "on-target-error": the action to take on an error on the target
(BlockdevOnError, default 'report') (BlockdevOnError, default 'report')
- "unmap": whether the target sectors should be discarded where source has only
zeroes. (json-bool, optional, default true)
The default value of the granularity is the image cluster size clamped The default value of the granularity is the image cluster size clamped
between 4096 and 65536, if the image format defines one. If the format between 4096 and 65536, if the image format defines one. If the format

View File

@ -34,38 +34,8 @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
class ImageMirroringTestCase(iotests.QMPTestCase):
'''Abstract base class for image mirroring test cases'''
def wait_ready(self, drive='drive0'): class TestSingleDrive(iotests.QMPTestCase):
'''Wait until a block job BLOCK_JOB_READY event'''
ready = False
while not ready:
for event in self.vm.get_qmp_events(wait=True):
if event['event'] == 'BLOCK_JOB_READY':
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/device', drive)
ready = True
def wait_ready_and_cancel(self, drive='drive0'):
self.wait_ready(drive=drive)
event = self.cancel_and_wait(drive=drive)
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/offset', event['data']['len'])
def complete_and_wait(self, drive='drive0', wait_ready=True):
'''Complete a block job and wait for it to finish'''
if wait_ready:
self.wait_ready(drive=drive)
result = self.vm.qmp('block-job-complete', device=drive)
self.assert_qmp(result, 'return', {})
event = self.wait_until_completed(drive=drive)
self.assert_qmp(event, 'data/type', 'mirror')
class TestSingleDrive(ImageMirroringTestCase):
image_len = 1 * 1024 * 1024 # MB image_len = 1 * 1024 * 1024 # MB
def setUp(self): def setUp(self):
@ -221,17 +191,9 @@ class TestSingleDriveUnalignedLength(TestSingleDrive):
test_small_buffer2 = None test_small_buffer2 = None
test_large_cluster = None test_large_cluster = None
class TestMirrorNoBacking(ImageMirroringTestCase): class TestMirrorNoBacking(iotests.QMPTestCase):
image_len = 2 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB
def complete_and_wait(self, drive='drive0', wait_ready=True):
iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready)
def compare_images(self, img1, img2):
iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
return iotests.compare_images(img1, img2)
def setUp(self): def setUp(self):
iotests.create_image(backing_img, TestMirrorNoBacking.image_len) iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
@ -242,7 +204,10 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
self.vm.shutdown() self.vm.shutdown()
os.remove(test_img) os.remove(test_img)
os.remove(backing_img) os.remove(backing_img)
os.remove(target_backing_img) try:
os.remove(target_backing_img)
except:
pass
os.remove(target_img) os.remove(target_img)
def test_complete(self): def test_complete(self):
@ -257,7 +222,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.assert_qmp(result, 'return[0]/inserted/file', target_img)
self.vm.shutdown() self.vm.shutdown()
self.assertTrue(self.compare_images(test_img, target_img), self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring') 'target image does not match source after mirroring')
def test_cancel(self): def test_cancel(self):
@ -272,7 +237,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/file', test_img) self.assert_qmp(result, 'return[0]/inserted/file', test_img)
self.vm.shutdown() self.vm.shutdown()
self.assertTrue(self.compare_images(test_img, target_img), self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring') 'target image does not match source after mirroring')
def test_large_cluster(self): def test_large_cluster(self):
@ -283,7 +248,6 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
%(TestMirrorNoBacking.image_len), target_backing_img) %(TestMirrorNoBacking.image_len), target_backing_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
% (TestMirrorNoBacking.image_len, target_backing_img), target_img) % (TestMirrorNoBacking.image_len, target_backing_img), target_img)
os.remove(target_backing_img)
result = self.vm.qmp('drive-mirror', device='drive0', sync='full', result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
mode='existing', target=target_img) mode='existing', target=target_img)
@ -293,10 +257,10 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.assert_qmp(result, 'return[0]/inserted/file', target_img)
self.vm.shutdown() self.vm.shutdown()
self.assertTrue(self.compare_images(test_img, target_img), self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring') 'target image does not match source after mirroring')
class TestMirrorResized(ImageMirroringTestCase): class TestMirrorResized(iotests.QMPTestCase):
backing_len = 1 * 1024 * 1024 # MB backing_len = 1 * 1024 * 1024 # MB
image_len = 2 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB
@ -344,7 +308,7 @@ class TestMirrorResized(ImageMirroringTestCase):
self.assertTrue(iotests.compare_images(test_img, target_img), self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring') 'target image does not match source after mirroring')
class TestReadErrors(ImageMirroringTestCase): class TestReadErrors(iotests.QMPTestCase):
image_len = 2 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB
# this should be a multiple of twice the default granularity # this should be a multiple of twice the default granularity
@ -498,7 +462,7 @@ new_state = "1"
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
self.vm.shutdown() self.vm.shutdown()
class TestWriteErrors(ImageMirroringTestCase): class TestWriteErrors(iotests.QMPTestCase):
image_len = 2 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB
# this should be a multiple of twice the default granularity # this should be a multiple of twice the default granularity
@ -624,7 +588,7 @@ new_state = "1"
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
self.vm.shutdown() self.vm.shutdown()
class TestSetSpeed(ImageMirroringTestCase): class TestSetSpeed(iotests.QMPTestCase):
image_len = 80 * 1024 * 1024 # MB image_len = 80 * 1024 * 1024 # MB
def setUp(self): def setUp(self):
@ -690,7 +654,7 @@ class TestSetSpeed(ImageMirroringTestCase):
self.wait_ready_and_cancel() self.wait_ready_and_cancel()
class TestUnbackedSource(ImageMirroringTestCase): class TestUnbackedSource(iotests.QMPTestCase):
image_len = 2 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB
def setUp(self): def setUp(self):
@ -731,7 +695,7 @@ class TestUnbackedSource(ImageMirroringTestCase):
self.complete_and_wait() self.complete_and_wait()
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
class TestRepairQuorum(ImageMirroringTestCase): class TestRepairQuorum(iotests.QMPTestCase):
""" This class test quorum file repair using drive-mirror. """ This class test quorum file repair using drive-mirror.
It's mostly a fork of TestSingleDrive """ It's mostly a fork of TestSingleDrive """
image_len = 1 * 1024 * 1024 # MB image_len = 1 * 1024 * 1024 # MB

View File

@ -187,7 +187,7 @@ class TestIncrementalBackup(iotests.QMPTestCase):
target = self.prepare_backup(bitmap, parent) target = self.prepare_backup(bitmap, parent)
res = self.do_qmp_backup(device=bitmap.drive['id'], res = self.do_qmp_backup(device=bitmap.drive['id'],
sync='dirty-bitmap', bitmap=bitmap.name, sync='incremental', bitmap=bitmap.name,
format=bitmap.drive['fmt'], target=target, format=bitmap.drive['fmt'], target=target,
mode='existing') mode='existing')
if not res: if not res:
@ -325,7 +325,7 @@ class TestIncrementalBackup(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
self.files.append(self.err_img) self.files.append(self.err_img)
result = self.vm.qmp('drive-backup', device=self.drives[0]['id'], result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
sync='dirty-bitmap', format=self.drives[0]['fmt'], sync='incremental', format=self.drives[0]['fmt'],
target=self.err_img) target=self.err_img)
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
@ -334,7 +334,7 @@ class TestIncrementalBackup(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
self.files.append(self.err_img) self.files.append(self.err_img)
result = self.vm.qmp('drive-backup', device=self.drives[0]['id'], result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
sync='dirty-bitmap', bitmap='unknown', sync='incremental', bitmap='unknown',
format=self.drives[0]['fmt'], target=self.err_img) format=self.drives[0]['fmt'], target=self.err_img)
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')

59
tests/qemu-iotests/132 Normal file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env python
#
# Test mirror with unmap
#
# Copyright (C) 2015 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import time
import os
import iotests
from iotests import qemu_img, qemu_io
test_img = os.path.join(iotests.test_dir, 'test.img')
target_img = os.path.join(iotests.test_dir, 'target.img')
class TestSingleDrive(iotests.QMPTestCase):
image_len = 2 * 1024 * 1024 # MB
def setUp(self):
# Write data to the image so we can compare later
qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleDrive.image_len))
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 2M', test_img)
self.vm = iotests.VM().add_drive(test_img, 'discard=unmap')
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(test_img)
try:
os.remove(target_img)
except OSError:
pass
def test_mirror_discard(self):
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
target=target_img)
self.assert_qmp(result, 'return', {})
self.vm.hmp_qemu_io('drive0', 'discard 0 64k')
self.complete_and_wait('drive0')
self.vm.shutdown()
self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring')
if __name__ == '__main__':
iotests.main(supported_fmts=['raw', 'qcow2'])

View File

@ -0,0 +1,5 @@
.
----------------------------------------------------------------------
Ran 1 tests
OK

View File

@ -131,4 +131,5 @@
129 rw auto quick 129 rw auto quick
130 rw auto quick 130 rw auto quick
131 rw auto quick 131 rw auto quick
132 rw auto quick
134 rw auto quick 134 rw auto quick

View File

@ -326,6 +326,29 @@ class QMPTestCase(unittest.TestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
return event return event
def wait_ready(self, drive='drive0'):
'''Wait until a block job BLOCK_JOB_READY event'''
f = {'data': {'type': 'mirror', 'device': drive } }
event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
def wait_ready_and_cancel(self, drive='drive0'):
self.wait_ready(drive=drive)
event = self.cancel_and_wait(drive=drive)
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/offset', event['data']['len'])
def complete_and_wait(self, drive='drive0', wait_ready=True):
'''Complete a block job and wait for it to finish'''
if wait_ready:
self.wait_ready(drive=drive)
result = self.vm.qmp('block-job-complete', device=drive)
self.assert_qmp(result, 'return', {})
event = self.wait_until_completed(drive=drive)
self.assert_qmp(event, 'data/type', 'mirror')
def notrun(reason): def notrun(reason):
'''Skip this test suite''' '''Skip this test suite'''
# Each test in qemu-iotests has a number ("seq") # Each test in qemu-iotests has a number ("seq")

View File

@ -12,6 +12,7 @@
#include "libqtest.h" #include "libqtest.h"
#include "libqos/pci-pc.h" #include "libqos/pci-pc.h"
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/timer.h"
#include "qemu-common.h" #include "qemu-common.h"
/* Tests only initialization so far. TODO: Replace with functional tests */ /* Tests only initialization so far. TODO: Replace with functional tests */
@ -20,7 +21,6 @@ static void nop(void)
} }
#define CLK 33000000 #define CLK 33000000
#define NS_PER_SEC 1000000000ULL
static QPCIBus *pcibus; static QPCIBus *pcibus;
static QPCIDevice *dev; static QPCIDevice *dev;
@ -86,7 +86,7 @@ static void test_timer(void)
fatal("time too big %u\n", curr); fatal("time too big %u\n", curr);
} }
for (cnt = 0; ; ) { for (cnt = 0; ; ) {
clock_step(1 * NS_PER_SEC); clock_step(1 * NSEC_PER_SEC);
prev = curr; prev = curr;
curr = in_Timer(); curr = in_Timer();
@ -125,7 +125,7 @@ static void test_timer(void)
out_IntrStatus(0x4000); out_IntrStatus(0x4000);
curr = in_Timer(); curr = in_Timer();
out_TimerInt(curr + 0.5 * CLK); out_TimerInt(curr + 0.5 * CLK);
clock_step(1 * NS_PER_SEC); clock_step(1 * NSEC_PER_SEC);
out_Timer(0); out_Timer(0);
if ((in_IntrStatus() & 0x4000) == 0) { if ((in_IntrStatus() & 0x4000) == 0) {
fatal("we should have an interrupt here!\n"); fatal("we should have an interrupt here!\n");
@ -137,7 +137,7 @@ static void test_timer(void)
out_IntrStatus(0x4000); out_IntrStatus(0x4000);
curr = in_Timer(); curr = in_Timer();
out_TimerInt(curr + 0.5 * CLK); out_TimerInt(curr + 0.5 * CLK);
clock_step(1 * NS_PER_SEC); clock_step(1 * NSEC_PER_SEC);
out_TimerInt(0); out_TimerInt(0);
if ((in_IntrStatus() & 0x4000) == 0) { if ((in_IntrStatus() & 0x4000) == 0) {
fatal("we should have an interrupt here!\n"); fatal("we should have an interrupt here!\n");
@ -148,7 +148,7 @@ static void test_timer(void)
next = curr + 5.0 * CLK; next = curr + 5.0 * CLK;
out_TimerInt(next); out_TimerInt(next);
for (cnt = 0; ; ) { for (cnt = 0; ; ) {
clock_step(1 * NS_PER_SEC); clock_step(1 * NSEC_PER_SEC);
prev = curr; prev = curr;
curr = in_Timer(); curr = in_Timer();
diff = (curr-prev) & 0xffffffffu; diff = (curr-prev) & 0xffffffffu;

View File

@ -40,19 +40,19 @@ static void test_leak_bucket(void)
bkt.level = 1.5; bkt.level = 1.5;
/* leak an op work of time */ /* leak an op work of time */
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); throttle_leak_bucket(&bkt, NSEC_PER_SEC / 150);
g_assert(bkt.avg == 150); g_assert(bkt.avg == 150);
g_assert(bkt.max == 15); g_assert(bkt.max == 15);
g_assert(double_cmp(bkt.level, 0.5)); g_assert(double_cmp(bkt.level, 0.5));
/* leak again emptying the bucket */ /* leak again emptying the bucket */
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); throttle_leak_bucket(&bkt, NSEC_PER_SEC / 150);
g_assert(bkt.avg == 150); g_assert(bkt.avg == 150);
g_assert(bkt.max == 15); g_assert(bkt.max == 15);
g_assert(double_cmp(bkt.level, 0)); g_assert(double_cmp(bkt.level, 0));
/* check that the bucket level won't go lower */ /* check that the bucket level won't go lower */
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); throttle_leak_bucket(&bkt, NSEC_PER_SEC / 150);
g_assert(bkt.avg == 150); g_assert(bkt.avg == 150);
g_assert(bkt.max == 15); g_assert(bkt.max == 15);
g_assert(double_cmp(bkt.level, 0)); g_assert(double_cmp(bkt.level, 0));
@ -90,7 +90,7 @@ static void test_compute_wait(void)
bkt.level = 15.5; bkt.level = 15.5;
wait = throttle_compute_wait(&bkt); wait = throttle_compute_wait(&bkt);
/* time required to do half an operation */ /* time required to do half an operation */
result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2; result = (int64_t) NSEC_PER_SEC / 150 / 2;
g_assert(wait == result); g_assert(wait == result);
} }

View File

@ -11,8 +11,7 @@
#include <string.h> #include <string.h>
#include "libqtest.h" #include "libqtest.h"
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/timer.h"
#define NS_PER_SEC 1000000000ULL
static void qmp_check_no_event(void) static void qmp_check_no_event(void)
{ {
@ -41,29 +40,29 @@ static QDict *qmp_get_event(const char *name)
static QDict *ib700_program_and_wait(QTestState *s) static QDict *ib700_program_and_wait(QTestState *s)
{ {
clock_step(NS_PER_SEC * 40); clock_step(NSEC_PER_SEC * 40);
qmp_check_no_event(); qmp_check_no_event();
/* 2 second limit */ /* 2 second limit */
outb(0x443, 14); outb(0x443, 14);
/* Ping */ /* Ping */
clock_step(NS_PER_SEC); clock_step(NSEC_PER_SEC);
qmp_check_no_event(); qmp_check_no_event();
outb(0x443, 14); outb(0x443, 14);
/* Disable */ /* Disable */
clock_step(NS_PER_SEC); clock_step(NSEC_PER_SEC);
qmp_check_no_event(); qmp_check_no_event();
outb(0x441, 1); outb(0x441, 1);
clock_step(3 * NS_PER_SEC); clock_step(3 * NSEC_PER_SEC);
qmp_check_no_event(); qmp_check_no_event();
/* Enable and let it fire */ /* Enable and let it fire */
outb(0x443, 13); outb(0x443, 13);
clock_step(3 * NS_PER_SEC); clock_step(3 * NSEC_PER_SEC);
qmp_check_no_event(); qmp_check_no_event();
clock_step(2 * NS_PER_SEC); clock_step(2 * NSEC_PER_SEC);
return qmp_get_event("WATCHDOG"); return qmp_get_event("WATCHDOG");
} }

View File

@ -36,7 +36,7 @@ void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta_ns)
double leak; double leak;
/* compute how much to leak */ /* compute how much to leak */
leak = (bkt->avg * (double) delta_ns) / NANOSECONDS_PER_SECOND; leak = (bkt->avg * (double) delta_ns) / NSEC_PER_SEC;
/* make the bucket leak */ /* make the bucket leak */
bkt->level = MAX(bkt->level - leak, 0); bkt->level = MAX(bkt->level - leak, 0);
@ -72,7 +72,7 @@ static void throttle_do_leak(ThrottleState *ts, int64_t now)
*/ */
static int64_t throttle_do_compute_wait(double limit, double extra) static int64_t throttle_do_compute_wait(double limit, double extra)
{ {
double wait = extra * NANOSECONDS_PER_SECOND; double wait = extra * NSEC_PER_SEC;
wait /= limit; wait /= limit;
return wait; return wait;
} }