Merge remote-tracking branch 'kwolf/for-anthony' into staging

* kwolf/for-anthony: (30 commits)
  declare ECANCELED on all machines
  tests/Makefile: Add missing $(EXESUF)
  stream: do not copy unallocated sectors from the base
  stream: fix ratelimiting corner case
  stream: fix HMP block_job_set_speed
  stream: pass new base image format to bdrv_change_backing_file
  stream: add testcase for partial streaming
  stream: fix sectors not allocated test
  qemu-io: fix the alloc command
  qemu-io: correctly print non-integer values as decimals
  qemu-img: make "info" backing file output correct and easier to use
  block: move field reset from bdrv_open_common to bdrv_close
  block: protect path_has_protocol from filenames with colons
  block: simplify path_is_absolute
  block: wait for job callback in block_job_cancel_sync
  block: add block_job_sleep_ns
  block: fully delete bs->file when closing
  block: do not reuse the backing file across bdrv_close/bdrv_open
  block: another bdrv_append fix
  block: fix snapshot on QED
  ...
This commit is contained in:
Anthony Liguori
2012-05-10 08:30:34 -05:00
33 changed files with 4787 additions and 4357 deletions
+134 -42
View File
@@ -198,33 +198,31 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
/* check if the path starts with "<protocol>:" */
static int path_has_protocol(const char *path)
{
const char *p;
#ifdef _WIN32
if (is_windows_drive(path) ||
is_windows_drive_prefix(path)) {
return 0;
}
p = path + strcspn(path, ":/\\");
#else
p = path + strcspn(path, ":/");
#endif
return strchr(path, ':') != NULL;
return *p == ':';
}
int path_is_absolute(const char *path)
{
const char *p;
#ifdef _WIN32
/* specific case for names like: "\\.\d:" */
if (*path == '/' || *path == '\\')
if (is_windows_drive(path) || is_windows_drive_prefix(path)) {
return 1;
#endif
p = strchr(path, ':');
if (p)
p++;
else
p = path;
#ifdef _WIN32
return (*p == '/' || *p == '\\');
}
return (*path == '/' || *path == '\\');
#else
return (*p == '/');
return (*path == '/');
#endif
}
@@ -272,6 +270,15 @@ void path_combine(char *dest, int dest_size,
}
}
void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz)
{
if (bs->backing_file[0] == '\0' || path_has_protocol(bs->backing_file)) {
pstrcpy(dest, sz, bs->backing_file);
} else {
path_combine(dest, sz, bs->filename, bs->backing_file);
}
}
void bdrv_register(BlockDriver *bdrv)
{
/* Block drivers without coroutine functions need emulation */
@@ -612,16 +619,11 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
int ret, open_flags;
assert(drv != NULL);
assert(bs->file == NULL);
trace_bdrv_open_common(bs, filename, flags, drv->format_name);
bs->file = NULL;
bs->total_sectors = 0;
bs->encrypted = 0;
bs->valid_key = 0;
bs->sg = 0;
bs->open_flags = flags;
bs->growable = 0;
bs->buffer_alignment = 512;
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
@@ -630,7 +632,6 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
}
pstrcpy(bs->filename, sizeof(bs->filename), filename);
bs->backing_file[0] = '\0';
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
return -ENOTSUP;
@@ -804,14 +805,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
BlockDriver *back_drv = NULL;
bs->backing_hd = bdrv_new("");
if (path_has_protocol(bs->backing_file)) {
pstrcpy(backing_filename, sizeof(backing_filename),
bs->backing_file);
} else {
path_combine(backing_filename, sizeof(backing_filename),
filename, bs->backing_file);
}
bdrv_get_full_backing_filename(bs, backing_filename,
sizeof(backing_filename));
if (bs->backing_format[0] != '\0') {
back_drv = bdrv_find_format(bs->backing_format);
@@ -878,9 +873,17 @@ void bdrv_close(BlockDriverState *bs)
bs->opaque = NULL;
bs->drv = NULL;
bs->copy_on_read = 0;
bs->backing_file[0] = '\0';
bs->backing_format[0] = '\0';
bs->total_sectors = 0;
bs->encrypted = 0;
bs->valid_key = 0;
bs->sg = 0;
bs->growable = 0;
if (bs->file != NULL) {
bdrv_close(bs->file);
bdrv_delete(bs->file);
bs->file = NULL;
}
bdrv_dev_change_media_cb(bs, false);
@@ -906,12 +909,31 @@ void bdrv_close_all(void)
*
* This function does not flush data to disk, use bdrv_flush_all() for that
* after calling this function.
*
* Note that completion of an asynchronous I/O operation can trigger any
* number of other I/O operations on other devices---for example a coroutine
* can be arbitrarily complex and a constant flow of I/O can come until the
* coroutine is complete. Because of this, it is not possible to have a
* function to drain a single device's I/O queue.
*/
void bdrv_drain_all(void)
{
BlockDriverState *bs;
bool busy;
qemu_aio_flush();
do {
busy = qemu_aio_wait();
/* FIXME: We do not have timer support here, so this is effectively
* a busy wait.
*/
QTAILQ_FOREACH(bs, &bdrv_states, list) {
if (!qemu_co_queue_empty(&bs->throttled_reqs)) {
qemu_co_queue_restart_all(&bs->throttled_reqs);
busy = true;
}
}
} while (busy);
/* If requests are still pending there is a bug somewhere */
QTAILQ_FOREACH(bs, &bdrv_states, list) {
@@ -930,6 +952,13 @@ void bdrv_make_anon(BlockDriverState *bs)
bs->device_name[0] = '\0';
}
static void bdrv_rebind(BlockDriverState *bs)
{
if (bs->drv && bs->drv->bdrv_rebind) {
bs->drv->bdrv_rebind(bs);
}
}
/*
* Add new bs contents at the top of an image chain while the chain is
* live, while keeping required fields on the top layer.
@@ -951,6 +980,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
tmp = *bs_new;
/* there are some fields that need to stay on the top layer: */
tmp.open_flags = bs_top->open_flags;
/* dev info */
tmp.dev_ops = bs_top->dev_ops;
@@ -1018,6 +1048,9 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
bs_new->slice_time = 0;
bs_new->slice_start = 0;
bs_new->slice_end = 0;
bdrv_rebind(bs_new);
bdrv_rebind(bs_top);
}
void bdrv_delete(BlockDriverState *bs)
@@ -1030,9 +1063,6 @@ void bdrv_delete(BlockDriverState *bs)
bdrv_make_anon(bs);
bdrv_close(bs);
if (bs->file != NULL) {
bdrv_delete(bs->file);
}
assert(bs != bs_snapshots);
g_free(bs);
@@ -1440,12 +1470,24 @@ int bdrv_change_backing_file(BlockDriverState *bs,
const char *backing_file, const char *backing_fmt)
{
BlockDriver *drv = bs->drv;
int ret;
/* Backing file format doesn't make sense without a backing file */
if (backing_fmt && !backing_file) {
return -EINVAL;
}
if (drv->bdrv_change_backing_file != NULL) {
return drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
ret = drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
} else {
return -ENOTSUP;
ret = -ENOTSUP;
}
if (ret == 0) {
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
}
return ret;
}
static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
@@ -1553,6 +1595,8 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num,
return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false);
}
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int dirty)
{
@@ -1563,8 +1607,8 @@ static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
for (; start <= end; start++) {
idx = start / (sizeof(unsigned long) * 8);
bit = start % (sizeof(unsigned long) * 8);
idx = start / BITS_PER_LONG;
bit = start % BITS_PER_LONG;
val = bs->dirty_bitmap[idx];
if (dirty) {
if (!(val & (1UL << bit))) {
@@ -3869,10 +3913,10 @@ void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
if (enable) {
if (!bs->dirty_bitmap) {
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG - 1;
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
bs->dirty_bitmap = g_malloc0(bitmap_size);
bs->dirty_bitmap = g_new0(unsigned long, bitmap_size);
}
} else {
if (bs->dirty_bitmap) {
@@ -4072,10 +4116,15 @@ int bdrv_img_create(const char *filename, const char *fmt,
if (backing_file && backing_file->value.s) {
uint64_t size;
char buf[32];
int back_flags;
/* backing files always opened read-only */
back_flags =
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
bs = bdrv_new("");
ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
ret = bdrv_open(bs, backing_file->value.s, back_flags, backing_drv);
if (ret < 0) {
error_report("Could not open '%s'", backing_file->value.s);
goto out;
@@ -4139,6 +4188,7 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
job->bs = bs;
job->cb = cb;
job->opaque = opaque;
job->busy = true;
bs->job = job;
/* Only set speed when necessary to avoid NotSupported error */
@@ -4188,6 +4238,9 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
void block_job_cancel(BlockJob *job)
{
job->cancelled = true;
if (job->co && !job->busy) {
qemu_coroutine_enter(job->co, NULL);
}
}
bool block_job_is_cancelled(BlockJob *job)
@@ -4195,13 +4248,52 @@ bool block_job_is_cancelled(BlockJob *job)
return job->cancelled;
}
void block_job_cancel_sync(BlockJob *job)
struct BlockCancelData {
BlockJob *job;
BlockDriverCompletionFunc *cb;
void *opaque;
bool cancelled;
int ret;
};
static void block_job_cancel_cb(void *opaque, int ret)
{
struct BlockCancelData *data = opaque;
data->cancelled = block_job_is_cancelled(data->job);
data->ret = ret;
data->cb(data->opaque, ret);
}
int block_job_cancel_sync(BlockJob *job)
{
struct BlockCancelData data;
BlockDriverState *bs = job->bs;
assert(bs->job == job);
/* Set up our own callback to store the result and chain to
* the original callback.
*/
data.job = job;
data.cb = job->cb;
data.opaque = job->opaque;
data.ret = -EINPROGRESS;
job->cb = block_job_cancel_cb;
job->opaque = &data;
block_job_cancel(job);
while (bs->job != NULL && bs->job->busy) {
while (data.ret == -EINPROGRESS) {
qemu_aio_wait();
}
return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
}
void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns)
{
/* Check cancellation *before* setting busy = false, too! */
if (!block_job_is_cancelled(job)) {
job->busy = false;
co_sleep_ns(clock, ns);
job->busy = true;
}
}
+2
View File
@@ -303,6 +303,8 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size);
void bdrv_get_full_backing_filename(BlockDriverState *bs,
char *dest, size_t sz);
int bdrv_can_snapshot(BlockDriverState *bs);
int bdrv_is_snapshot(BlockDriverState *bs);
BlockDriverState *bdrv_snapshots(void);
-5
View File
@@ -1011,11 +1011,6 @@ fail:
static int qcow2_change_backing_file(BlockDriverState *bs,
const char *backing_file, const char *backing_fmt)
{
/* Backing file format doesn't make sense without a backing file */
if (backing_fmt && !backing_file) {
return -EINVAL;
}
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
+7
View File
@@ -367,6 +367,12 @@ static void qed_cancel_need_check_timer(BDRVQEDState *s)
qemu_del_timer(s->need_check_timer);
}
static void bdrv_qed_rebind(BlockDriverState *bs)
{
BDRVQEDState *s = bs->opaque;
s->bs = bs;
}
static int bdrv_qed_open(BlockDriverState *bs, int flags)
{
BDRVQEDState *s = bs->opaque;
@@ -1550,6 +1556,7 @@ static BlockDriver bdrv_qed = {
.create_options = qed_create_options,
.bdrv_probe = bdrv_qed_probe,
.bdrv_rebind = bdrv_qed_rebind,
.bdrv_open = bdrv_qed_open,
.bdrv_close = bdrv_qed_close,
.bdrv_create = bdrv_qed_create,
+26 -50
View File
@@ -33,19 +33,19 @@ typedef struct {
static int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
{
int64_t delay_ns = 0;
int64_t now = qemu_get_clock_ns(rt_clock);
if (limit->next_slice_time < now) {
limit->next_slice_time = now + SLICE_TIME;
limit->dispatched = 0;
}
if (limit->dispatched + n > limit->slice_quota) {
delay_ns = limit->next_slice_time - now;
} else {
if (limit->dispatched == 0 || limit->dispatched + n <= limit->slice_quota) {
limit->dispatched += n;
return 0;
} else {
limit->dispatched = n;
return limit->next_slice_time - now;
}
return delay_ns;
}
static void ratelimit_set_speed(RateLimit *limit, uint64_t speed)
@@ -96,17 +96,6 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
bdrv_delete(unused);
}
top->backing_hd = base;
pstrcpy(top->backing_file, sizeof(top->backing_file), "");
pstrcpy(top->backing_format, sizeof(top->backing_format), "");
if (base_id) {
pstrcpy(top->backing_file, sizeof(top->backing_file), base_id);
if (base->drv) {
pstrcpy(top->backing_format, sizeof(top->backing_format),
base->drv->format_name);
}
}
}
/*
@@ -141,14 +130,9 @@ static int coroutine_fn is_allocated_base(BlockDriverState *top,
*/
intermediate = top->backing_hd;
while (intermediate) {
while (intermediate != base) {
int pnum_inter;
/* reached base */
if (intermediate == base) {
*pnum = n;
return 1;
}
ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors,
&pnum_inter);
if (ret < 0) {
@@ -171,6 +155,7 @@ static int coroutine_fn is_allocated_base(BlockDriverState *top,
intermediate = intermediate->backing_hd;
}
*pnum = n;
return 1;
}
@@ -203,30 +188,25 @@ static void coroutine_fn stream_run(void *opaque)
}
for (sector_num = 0; sector_num < end; sector_num += n) {
retry:
uint64_t delay_ns = 0;
wait:
/* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that qemu_aio_flush() returns.
*/
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
if (block_job_is_cancelled(&s->common)) {
break;
}
s->common.busy = true;
if (base) {
ret = is_allocated_base(bs, base, sector_num,
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
} else {
ret = bdrv_co_is_allocated(bs, sector_num,
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE,
&n);
}
ret = is_allocated_base(bs, base, sector_num,
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
trace_stream_one_iteration(s, sector_num, n, ret);
if (ret == 0) {
if (s->common.speed) {
uint64_t delay_ns = ratelimit_calculate_delay(&s->limit, n);
delay_ns = ratelimit_calculate_delay(&s->limit, n);
if (delay_ns > 0) {
s->common.busy = false;
co_sleep_ns(rt_clock, delay_ns);
/* Recheck cancellation and that sectors are unallocated */
goto retry;
goto wait;
}
}
ret = stream_populate(bs, sector_num, n, buf);
@@ -238,12 +218,6 @@ retry:
/* Publish progress */
s->common.offset += n * BDRV_SECTOR_SIZE;
/* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that qemu_aio_flush() returns.
*/
s->common.busy = false;
co_sleep_ns(rt_clock, 0);
}
if (!base) {
@@ -251,11 +225,14 @@ retry:
}
if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
const char *base_id = NULL;
const char *base_id = NULL, *base_fmt = NULL;
if (base) {
base_id = s->backing_file_id;
if (base->drv) {
base_fmt = base->drv->format_name;
}
}
ret = bdrv_change_backing_file(bs, base_id, NULL);
ret = bdrv_change_backing_file(bs, base_id, base_fmt);
close_unused_images(bs, base, base_id);
}
@@ -286,7 +263,6 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
void *opaque, Error **errp)
{
StreamBlockJob *s;
Coroutine *co;
s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp);
if (!s) {
@@ -298,7 +274,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
}
co = qemu_coroutine_create(stream_run);
trace_stream_start(bs, base, s, co, opaque);
qemu_coroutine_enter(co, s);
s->common.co = qemu_coroutine_create(stream_run);
trace_stream_start(bs, base, s, s->common.co, opaque);
qemu_coroutine_enter(s->common.co, s);
}
+7
View File
@@ -982,6 +982,12 @@ static BDRVVVFATState *vvv = NULL;
static int enable_write_target(BDRVVVFATState *s);
static int is_consistent(BDRVVVFATState *s);
static void vvfat_rebind(BlockDriverState *bs)
{
BDRVVVFATState *s = bs->opaque;
s->bs = bs;
}
static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
{
BDRVVVFATState *s = bs->opaque;
@@ -2855,6 +2861,7 @@ static BlockDriver bdrv_vvfat = {
.format_name = "vvfat",
.instance_size = sizeof(BDRVVVFATState),
.bdrv_file_open = vvfat_open,
.bdrv_rebind = vvfat_rebind,
.bdrv_read = vvfat_co_read,
.bdrv_write = vvfat_co_write,
.bdrv_close = vvfat_close,
+26 -8
View File
@@ -94,21 +94,24 @@ struct BlockJob {
/** The block device on which the job is operating. */
BlockDriverState *bs;
/**
* The coroutine that executes the job. If not NULL, it is
* reentered when busy is false and the job is cancelled.
*/
Coroutine *co;
/**
* Set to true if the job should cancel itself. The flag must
* always be tested just before toggling the busy flag from false
* to true. After a job has detected that the cancelled flag is
* true, it should not anymore issue any I/O operation to the
* block device.
* to true. After a job has been cancelled, it should only yield
* if #qemu_aio_wait will ("sooner or later") reenter the coroutine.
*/
bool cancelled;
/**
* Set to false by the job while it is in a quiescent state, where
* no I/O is pending and cancellation can be processed without
* issuing new I/O. The busy flag must be set to false when the
* job goes to sleep on any condition that is not detected by
* #qemu_aio_wait, such as a timer.
* no I/O is pending and the job has yielded on any condition
* that is not detected by #qemu_aio_wait, such as a timer.
*/
bool busy;
@@ -140,6 +143,7 @@ struct BlockDriver {
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
void (*bdrv_close)(BlockDriverState *bs);
void (*bdrv_rebind)(BlockDriverState *bs);
int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
int (*bdrv_make_empty)(BlockDriverState *bs);
@@ -362,6 +366,17 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
int64_t speed, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp);
/**
* block_job_sleep_ns:
* @job: The job that calls the function.
* @clock: The clock to sleep on.
* @ns: How many nanoseconds to stop for.
*
* Put the job to sleep (assuming that it wasn't canceled) for @ns
* nanoseconds. Canceling the job will interrupt the wait immediately.
*/
void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns);
/**
* block_job_complete:
* @job: The job being completed.
@@ -409,8 +424,11 @@ bool block_job_is_cancelled(BlockJob *job);
* immediately after #block_job_cancel_sync. Users of block jobs
* will usually protect the BlockDriverState objects with a reference
* count, should this be a concern.
*
* Returns the return value from the job if the job actually completed
* during the call, or -ECANCELED if it was canceled.
*/
void block_job_cancel_sync(BlockJob *job);
int block_job_cancel_sync(BlockJob *job);
/**
* stream_start:
+6 -3
View File
@@ -756,14 +756,17 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
goto delete_and_fail;
}
if (!bdrv_is_inserted(states->old_bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
goto delete_and_fail;
}
if (bdrv_in_use(states->old_bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
goto delete_and_fail;
}
if (!bdrv_is_read_only(states->old_bs) &&
bdrv_is_inserted(states->old_bs)) {
if (!bdrv_is_read_only(states->old_bs)) {
if (bdrv_flush(states->old_bs)) {
error_set(errp, QERR_IO_ERROR);
goto delete_and_fail;
+23 -17
View File
@@ -418,31 +418,37 @@ cvtstr(
char *str,
size_t size)
{
const char *fmt;
int precise;
precise = ((double)value * 1000 == (double)(int)value * 1000);
char *trim;
const char *suffix;
if (value >= EXABYTES(1)) {
fmt = precise ? "%.f EiB" : "%.3f EiB";
snprintf(str, size, fmt, TO_EXABYTES(value));
suffix = " EiB";
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
} else if (value >= PETABYTES(1)) {
fmt = precise ? "%.f PiB" : "%.3f PiB";
snprintf(str, size, fmt, TO_PETABYTES(value));
suffix = " PiB";
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
} else if (value >= TERABYTES(1)) {
fmt = precise ? "%.f TiB" : "%.3f TiB";
snprintf(str, size, fmt, TO_TERABYTES(value));
suffix = " TiB";
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
} else if (value >= GIGABYTES(1)) {
fmt = precise ? "%.f GiB" : "%.3f GiB";
snprintf(str, size, fmt, TO_GIGABYTES(value));
suffix = " GiB";
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
} else if (value >= MEGABYTES(1)) {
fmt = precise ? "%.f MiB" : "%.3f MiB";
snprintf(str, size, fmt, TO_MEGABYTES(value));
suffix = " MiB";
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
} else if (value >= KILOBYTES(1)) {
fmt = precise ? "%.f KiB" : "%.3f KiB";
snprintf(str, size, fmt, TO_KILOBYTES(value));
suffix = " KiB";
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
} else {
snprintf(str, size, "%f bytes", value);
suffix = " bytes";
snprintf(str, size - 6, "%f", value);
}
trim = strstr(str, ".000");
if (trim) {
strcpy(trim, suffix);
} else {
strcat(str, suffix);
}
}
+1 -1
View File
@@ -849,7 +849,7 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
int64_t value = qdict_get_int(qdict, "value");
int64_t value = qdict_get_int(qdict, "speed");
qmp_block_job_set_speed(device, value, &error);
+3
View File
@@ -61,6 +61,9 @@ typedef struct Monitor Monitor;
#if !defined(ENOTSUP)
#define ENOTSUP 4096
#endif
#if !defined(ECANCELED)
#define ECANCELED 4097
#endif
#ifndef TIME_MAX
#define TIME_MAX LONG_MAX
#endif
+7 -5
View File
@@ -1138,11 +1138,13 @@ static int img_info(int argc, char **argv)
}
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
if (backing_filename[0] != '\0') {
path_combine(backing_filename2, sizeof(backing_filename2),
filename, backing_filename);
printf("backing file: %s (actual path: %s)\n",
backing_filename,
backing_filename2);
bdrv_get_full_backing_filename(bs, backing_filename2,
sizeof(backing_filename2));
printf("backing file: %s", backing_filename);
if (strcmp(backing_filename, backing_filename2) != 0) {
printf(" (actual path: %s)", backing_filename2);
}
putchar('\n');
}
dump_snapshots(bs);
bdrv_delete(bs);
+8 -2
View File
@@ -1560,7 +1560,7 @@ out:
static int alloc_f(int argc, char **argv)
{
int64_t offset;
int64_t offset, sector_num;
int nb_sectors, remaining;
char s1[64];
int num, sum_alloc;
@@ -1581,12 +1581,18 @@ static int alloc_f(int argc, char **argv)
remaining = nb_sectors;
sum_alloc = 0;
sector_num = offset >> 9;
while (remaining) {
ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
ret = bdrv_is_allocated(bs, sector_num, remaining, &num);
sector_num += num;
remaining -= num;
if (ret) {
sum_alloc += num;
}
if (num == 0) {
nb_sectors -= remaining;
remaining = 0;
}
}
cvtstr(offset, s1, sizeof(s1));
+1 -1
View File
@@ -759,7 +759,7 @@ EQMP
{
.name = "blockdev-snapshot-sync",
.args_type = "device:B,snapshot-file:s,format:s?",
.args_type = "device:B,snapshot-file:s,format:s?,mode:s?",
.mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync,
},
+3 -1
View File
@@ -18,7 +18,8 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
# All QTests for now are POSIX-only, but the dependencies are
# really in libqtest, not in the testcases themselves.
check-qtest-i386-y = tests/rtc-test
check-qtest-i386-y = tests/rtc-test$(EXESUF)
check-qtest-i386-y = tests/fdc-test$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y)
check-qtest-sparc-y = tests/m48t59-test$(EXESUF)
check-qtest-sparc64-y = tests/m48t59-test$(EXESUF)
@@ -67,6 +68,7 @@ tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marsh
tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y)
tests/fdc-test$(EXESUF): tests/fdc-test.o tests/libqtest.o
# QTest rules
+195
View File
@@ -0,0 +1,195 @@
/*
* Floppy test cases.
*
* Copyright (c) 2012 Kevin Wolf <kwolf@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include "libqtest.h"
#include "qemu-common.h"
#define TEST_IMAGE_SIZE 1440 * 1024
#define FLOPPY_BASE 0x3f0
#define FLOPPY_IRQ 6
enum {
reg_sra = 0x0,
reg_srb = 0x1,
reg_dor = 0x2,
reg_msr = 0x4,
reg_dsr = 0x4,
reg_fifo = 0x5,
reg_dir = 0x7,
};
enum {
CMD_SENSE_INT = 0x08,
CMD_SEEK = 0x0f,
};
enum {
RQM = 0x80,
DIO = 0x40,
DSKCHG = 0x80,
};
char test_image[] = "/tmp/qtest.XXXXXX";
#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
static void floppy_send(uint8_t byte)
{
uint8_t msr;
msr = inb(FLOPPY_BASE + reg_msr);
assert_bit_set(msr, RQM);
assert_bit_clear(msr, DIO);
outb(FLOPPY_BASE + reg_fifo, byte);
}
static uint8_t floppy_recv(void)
{
uint8_t msr;
msr = inb(FLOPPY_BASE + reg_msr);
assert_bit_set(msr, RQM | DIO);
return inb(FLOPPY_BASE + reg_fifo);
}
static void ack_irq(void)
{
g_assert(get_irq(FLOPPY_IRQ));
floppy_send(CMD_SENSE_INT);
floppy_recv();
floppy_recv();
g_assert(!get_irq(FLOPPY_IRQ));
}
static void send_step_pulse(void)
{
int drive = 0;
int head = 0;
static int cyl = 0;
floppy_send(CMD_SEEK);
floppy_send(head << 2 | drive);
g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(cyl);
ack_irq();
cyl = (cyl + 1) % 4;
}
static void test_media_change(void)
{
uint8_t dir;
/* Media changed bit must be up-to-date after step pulse. Do two SEEKs
* because we may already happen to be on the right cylinder initially. */
send_step_pulse();
send_step_pulse();
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_clear(dir, DSKCHG);
/* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
* reset the bit. */
qmp("{'execute':'eject', 'arguments':{ 'device':'floppy0' }}");
qmp(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
send_step_pulse();
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
/* And then insert it again. DSKCHK should not be reset until a step pulse
* is sent. */
qmp("{'execute':'change', 'arguments':{ 'device':'floppy0', "
"'target': '%s' }}", test_image);
qmp(""); /* ignore event (FIXME open -> open transition?!) */
qmp(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
send_step_pulse();
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_clear(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_clear(dir, DSKCHG);
}
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
char *cmdline;
int fd;
int ret;
/* Check architecture */
if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
g_test_message("Skipping test for non-x86\n");
return 0;
}
/* Create a temporary raw image */
fd = mkstemp(test_image);
g_assert(fd >= 0);
ret = ftruncate(fd, TEST_IMAGE_SIZE);
g_assert(ret == 0);
close(fd);
/* Run the tests */
g_test_init(&argc, &argv, NULL);
cmdline = g_strdup_printf("-vnc none "
"-drive file=%s,if=floppy,cache=writeback ",
test_image);
qtest_start(cmdline);
qtest_irq_intercept_in(global_qtest, "ioapic");
qtest_add_func("/fdc/media_change", test_media_change);
ret = g_test_run();
/* Cleanup */
qtest_quit(global_qtest);
unlink(test_image);
return ret;
}
+106 -37
View File
@@ -36,6 +36,7 @@ QTestState *global_qtest;
struct QTestState
{
int fd;
int qmp_fd;
bool irq_level[MAX_IRQ];
GString *rx;
gchar *pid_file;
@@ -45,25 +46,11 @@ struct QTestState
g_assert_cmpint(ret, !=, -1); \
} while (0)
QTestState *qtest_init(const char *extra_args)
static int init_socket(const char *socket_path)
{
QTestState *s;
struct sockaddr_un addr;
int sock, ret, i;
gchar *socket_path;
gchar *pid_file;
gchar *command;
const char *qemu_binary;
pid_t pid;
socklen_t addrlen;
qemu_binary = getenv("QTEST_QEMU_BINARY");
g_assert(qemu_binary != NULL);
socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
s = g_malloc(sizeof(*s));
int sock;
int ret;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
g_assert_no_errno(sock);
@@ -78,21 +65,14 @@ QTestState *qtest_init(const char *extra_args)
g_assert_no_errno(ret);
listen(sock, 1);
pid = fork();
if (pid == 0) {
command = g_strdup_printf("%s "
"-qtest unix:%s,nowait "
"-qtest-log /dev/null "
"-pidfile %s "
"-machine accel=qtest "
"%s", qemu_binary, socket_path,
pid_file,
extra_args ?: "");
return sock;
}
ret = system(command);
exit(ret);
g_free(command);
}
static int socket_accept(int sock)
{
struct sockaddr_un addr;
socklen_t addrlen;
int ret;
do {
ret = accept(sock, (struct sockaddr *)&addr, &addrlen);
@@ -100,7 +80,52 @@ QTestState *qtest_init(const char *extra_args)
g_assert_no_errno(ret);
close(sock);
s->fd = ret;
return ret;
}
QTestState *qtest_init(const char *extra_args)
{
QTestState *s;
int sock, qmpsock, ret, i;
gchar *socket_path;
gchar *qmp_socket_path;
gchar *pid_file;
gchar *command;
const char *qemu_binary;
pid_t pid;
qemu_binary = getenv("QTEST_QEMU_BINARY");
g_assert(qemu_binary != NULL);
socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
s = g_malloc(sizeof(*s));
sock = init_socket(socket_path);
qmpsock = init_socket(qmp_socket_path);
pid = fork();
if (pid == 0) {
command = g_strdup_printf("%s "
"-qtest unix:%s,nowait "
"-qtest-log /dev/null "
"-qmp unix:%s,nowait "
"-pidfile %s "
"-machine accel=qtest "
"%s", qemu_binary, socket_path,
qmp_socket_path, pid_file,
extra_args ?: "");
ret = system(command);
exit(ret);
g_free(command);
}
s->fd = socket_accept(sock);
s->qmp_fd = socket_accept(qmpsock);
s->rx = g_string_new("");
s->pid_file = pid_file;
for (i = 0; i < MAX_IRQ; i++) {
@@ -108,6 +133,11 @@ QTestState *qtest_init(const char *extra_args)
}
g_free(socket_path);
g_free(qmp_socket_path);
/* Read the QMP greeting and then do the handshake */
qtest_qmp(s, "");
qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }");
return s;
}
@@ -131,22 +161,19 @@ void qtest_quit(QTestState *s)
}
}
static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
static void socket_sendf(int fd, const char *fmt, va_list ap)
{
va_list ap;
gchar *str;
size_t size, offset;
va_start(ap, fmt);
str = g_strdup_vprintf(fmt, ap);
va_end(ap);
size = strlen(str);
offset = 0;
while (offset < size) {
ssize_t len;
len = write(s->fd, str + offset, size - offset);
len = write(fd, str + offset, size - offset);
if (len == -1 && errno == EINTR) {
continue;
}
@@ -158,6 +185,15 @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
}
}
static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
socket_sendf(s->fd, fmt, ap);
va_end(ap);
}
static GString *qtest_recv_line(QTestState *s)
{
GString *line;
@@ -233,6 +269,39 @@ redo:
return words;
}
void qtest_qmp(QTestState *s, const char *fmt, ...)
{
va_list ap;
bool has_reply = false;
int nesting = 0;
/* Send QMP request */
va_start(ap, fmt);
socket_sendf(s->qmp_fd, fmt, ap);
va_end(ap);
/* Receive reply */
while (!has_reply || nesting > 0) {
ssize_t len;
char c;
len = read(s->qmp_fd, &c, 1);
if (len == -1 && errno == EINTR) {
continue;
}
switch (c) {
case '{':
nesting++;
has_reply = true;
break;
case '}':
nesting--;
break;
}
}
}
const char *qtest_get_arch(void)
{
const char *qemu = getenv("QTEST_QEMU_BINARY");
+17
View File
@@ -37,6 +37,15 @@ QTestState *qtest_init(const char *extra_args);
*/
void qtest_quit(QTestState *s);
/**
* qtest_qmp:
* @s: QTestState instance to operate on.
* @fmt...: QMP message to send to qemu
*
* Sends a QMP message to QEMU
*/
void qtest_qmp(QTestState *s, const char *fmt, ...);
/**
* qtest_get_irq:
* @s: QTestState instance to operate on.
@@ -206,6 +215,14 @@ void qtest_add_func(const char *str, void (*fn));
global_qtest = qtest_init((args)) \
)
/**
* qmp:
* @fmt...: QMP message to send to qemu
*
* Sends a QMP message to QEMU
*/
#define qmp(fmt, ...) qtest_qmp(global_qtest, fmt, ## __VA_ARGS__)
/**
* get_irq:
* @num: Interrupt to observe.
+2 -2
View File
@@ -15,9 +15,9 @@ read 134217728/134217728 bytes at offset 0
unaligned pwrite
wrote 42/42 bytes at offset 66
42.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
verify pattern
read 42/42 bytes at offset 66
42.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
+1 -1
View File
@@ -5,5 +5,5 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
== read from read-only image
read 512/512 bytes at offset 0
512.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done

Some files were not shown because too many files have changed in this diff Show More