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

* kwolf/for-anthony: (24 commits)
  block: Factor bdrv_read_unthrottled() out of guess_disk_lchs()
  qtest: Tidy up temporary files properly
  fdc: Drop broken code for user-defined floppy geometry
  fdc_test: introduce test_sense_interrupt
  fdc_test: update media_change test
  fdc: fix interrupt handling
  fdc: rewrite seek and DSKCHG bit handling
  block: introduce bdrv_swap, implement bdrv_append on top of it
  block: copy over job and dirty bitmap fields in bdrv_append
  raw: hook into blkdebug
  blkdebug: optionally tie errors to a specific sector
  blkdebug: store list of active rules
  blkdebug: pass getlength to underlying file
  blkdebug: tiny cleanup
  blkdebug: remove sync i/o events
  sheepdog: traverse pending_list from the first for each time
  sheepdog: split outstanding list into inflight and pending
  sheepdog: make sure we don't free aiocb before sending all requests
  sheepdog: use coroutine based socket functions in coroutine context
  sheepdog: restart I/O when socket becomes ready in do_co_req()
  ...
This commit is contained in:
Anthony Liguori 2012-07-09 10:29:40 -05:00
commit 715cc00ce1
13 changed files with 432 additions and 301 deletions

273
block.c
View File

@ -971,6 +971,107 @@ static void bdrv_rebind(BlockDriverState *bs)
} }
} }
static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
BlockDriverState *bs_src)
{
/* move some fields that need to stay attached to the device */
bs_dest->open_flags = bs_src->open_flags;
/* dev info */
bs_dest->dev_ops = bs_src->dev_ops;
bs_dest->dev_opaque = bs_src->dev_opaque;
bs_dest->dev = bs_src->dev;
bs_dest->buffer_alignment = bs_src->buffer_alignment;
bs_dest->copy_on_read = bs_src->copy_on_read;
bs_dest->enable_write_cache = bs_src->enable_write_cache;
/* i/o timing parameters */
bs_dest->slice_time = bs_src->slice_time;
bs_dest->slice_start = bs_src->slice_start;
bs_dest->slice_end = bs_src->slice_end;
bs_dest->io_limits = bs_src->io_limits;
bs_dest->io_base = bs_src->io_base;
bs_dest->throttled_reqs = bs_src->throttled_reqs;
bs_dest->block_timer = bs_src->block_timer;
bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
/* geometry */
bs_dest->cyls = bs_src->cyls;
bs_dest->heads = bs_src->heads;
bs_dest->secs = bs_src->secs;
bs_dest->translation = bs_src->translation;
/* r/w error */
bs_dest->on_read_error = bs_src->on_read_error;
bs_dest->on_write_error = bs_src->on_write_error;
/* i/o status */
bs_dest->iostatus_enabled = bs_src->iostatus_enabled;
bs_dest->iostatus = bs_src->iostatus;
/* dirty bitmap */
bs_dest->dirty_count = bs_src->dirty_count;
bs_dest->dirty_bitmap = bs_src->dirty_bitmap;
/* job */
bs_dest->in_use = bs_src->in_use;
bs_dest->job = bs_src->job;
/* keep the same entry in bdrv_states */
pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name),
bs_src->device_name);
bs_dest->list = bs_src->list;
}
/*
* Swap bs contents for two image chains while they are live,
* while keeping required fields on the BlockDriverState that is
* actually attached to a device.
*
* This will modify the BlockDriverState fields, and swap contents
* between bs_new and bs_old. Both bs_new and bs_old are modified.
*
* bs_new is required to be anonymous.
*
* This function does not create any image files.
*/
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
{
BlockDriverState tmp;
/* bs_new must be anonymous and shouldn't have anything fancy enabled */
assert(bs_new->device_name[0] == '\0');
assert(bs_new->dirty_bitmap == NULL);
assert(bs_new->job == NULL);
assert(bs_new->dev == NULL);
assert(bs_new->in_use == 0);
assert(bs_new->io_limits_enabled == false);
assert(bs_new->block_timer == NULL);
tmp = *bs_new;
*bs_new = *bs_old;
*bs_old = tmp;
/* there are some fields that should not be swapped, move them back */
bdrv_move_feature_fields(&tmp, bs_old);
bdrv_move_feature_fields(bs_old, bs_new);
bdrv_move_feature_fields(bs_new, &tmp);
/* bs_new shouldn't be in bdrv_states even after the swap! */
assert(bs_new->device_name[0] == '\0');
/* Check a few fields that should remain attached to the device */
assert(bs_new->dev == NULL);
assert(bs_new->job == NULL);
assert(bs_new->in_use == 0);
assert(bs_new->io_limits_enabled == false);
assert(bs_new->block_timer == NULL);
bdrv_rebind(bs_new);
bdrv_rebind(bs_old);
}
/* /*
* Add new bs contents at the top of an image chain while the chain is * Add new bs contents at the top of an image chain while the chain is
* live, while keeping required fields on the top layer. * live, while keeping required fields on the top layer.
@ -984,88 +1085,16 @@ static void bdrv_rebind(BlockDriverState *bs)
*/ */
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
{ {
BlockDriverState tmp; bdrv_swap(bs_new, bs_top);
/* bs_new must be anonymous */
assert(bs_new->device_name[0] == '\0');
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;
tmp.dev_opaque = bs_top->dev_opaque;
tmp.dev = bs_top->dev;
tmp.buffer_alignment = bs_top->buffer_alignment;
tmp.copy_on_read = bs_top->copy_on_read;
tmp.enable_write_cache = bs_top->enable_write_cache;
/* i/o timing parameters */
tmp.slice_time = bs_top->slice_time;
tmp.slice_start = bs_top->slice_start;
tmp.slice_end = bs_top->slice_end;
tmp.io_limits = bs_top->io_limits;
tmp.io_base = bs_top->io_base;
tmp.throttled_reqs = bs_top->throttled_reqs;
tmp.block_timer = bs_top->block_timer;
tmp.io_limits_enabled = bs_top->io_limits_enabled;
/* geometry */
tmp.cyls = bs_top->cyls;
tmp.heads = bs_top->heads;
tmp.secs = bs_top->secs;
tmp.translation = bs_top->translation;
/* r/w error */
tmp.on_read_error = bs_top->on_read_error;
tmp.on_write_error = bs_top->on_write_error;
/* i/o status */
tmp.iostatus_enabled = bs_top->iostatus_enabled;
tmp.iostatus = bs_top->iostatus;
/* keep the same entry in bdrv_states */
pstrcpy(tmp.device_name, sizeof(tmp.device_name), bs_top->device_name);
tmp.list = bs_top->list;
/* The contents of 'tmp' will become bs_top, as we are /* The contents of 'tmp' will become bs_top, as we are
* swapping bs_new and bs_top contents. */ * swapping bs_new and bs_top contents. */
tmp.backing_hd = bs_new; bs_top->backing_hd = bs_new;
pstrcpy(tmp.backing_file, sizeof(tmp.backing_file), bs_top->filename); bs_top->open_flags &= ~BDRV_O_NO_BACKING;
pstrcpy(tmp.backing_format, sizeof(tmp.backing_format), pstrcpy(bs_top->backing_file, sizeof(bs_top->backing_file),
bs_top->drv ? bs_top->drv->format_name : ""); bs_new->filename);
pstrcpy(bs_top->backing_format, sizeof(bs_top->backing_format),
/* swap contents of the fixed new bs and the current top */ bs_new->drv ? bs_new->drv->format_name : "");
*bs_new = *bs_top;
*bs_top = tmp;
/* device_name[] was carried over from the old bs_top. bs_new
* shouldn't be in bdrv_states, so we need to make device_name[]
* reflect the anonymity of bs_new
*/
bs_new->device_name[0] = '\0';
/* clear the copied fields in the new backing file */
bdrv_detach_dev(bs_new, bs_new->dev);
qemu_co_queue_init(&bs_new->throttled_reqs);
memset(&bs_new->io_base, 0, sizeof(bs_new->io_base));
memset(&bs_new->io_limits, 0, sizeof(bs_new->io_limits));
bdrv_iostatus_disable(bs_new);
/* we don't use bdrv_io_limits_disable() for this, because we don't want
* to affect or delete the block_timer, as it has been moved to bs_top */
bs_new->io_limits_enabled = false;
bs_new->block_timer = NULL;
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) void bdrv_delete(BlockDriverState *bs)
@ -1610,6 +1639,20 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num,
return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false); return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false);
} }
/* Just like bdrv_read(), but with I/O throttling temporarily disabled */
int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
bool enabled;
int ret;
enabled = bs->io_limits_enabled;
bs->io_limits_enabled = false;
ret = bdrv_read(bs, 0, buf, 1);
bs->io_limits_enabled = enabled;
return ret;
}
#define BITS_PER_LONG (sizeof(unsigned long) * 8) #define BITS_PER_LONG (sizeof(unsigned long) * 8)
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num, static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
@ -2107,11 +2150,10 @@ static int guess_disk_lchs(BlockDriverState *bs,
int *pcylinders, int *pheads, int *psectors) int *pcylinders, int *pheads, int *psectors)
{ {
uint8_t buf[BDRV_SECTOR_SIZE]; uint8_t buf[BDRV_SECTOR_SIZE];
int ret, i, heads, sectors, cylinders; int i, heads, sectors, cylinders;
struct partition *p; struct partition *p;
uint32_t nr_sects; uint32_t nr_sects;
uint64_t nb_sectors; uint64_t nb_sectors;
bool enabled;
bdrv_get_geometry(bs, &nb_sectors); bdrv_get_geometry(bs, &nb_sectors);
@ -2120,12 +2162,9 @@ static int guess_disk_lchs(BlockDriverState *bs,
* but also in async I/O mode. So the I/O throttling function has to * but also in async I/O mode. So the I/O throttling function has to
* be disabled temporarily here, not permanently. * be disabled temporarily here, not permanently.
*/ */
enabled = bs->io_limits_enabled; if (bdrv_read_unthrottled(bs, 0, buf, 1) < 0) {
bs->io_limits_enabled = false;
ret = bdrv_read(bs, 0, buf, 1);
bs->io_limits_enabled = enabled;
if (ret < 0)
return -1; return -1;
}
/* test msdos magic */ /* test msdos magic */
if (buf[510] != 0x55 || buf[511] != 0xaa) if (buf[510] != 0x55 || buf[511] != 0xaa)
return -1; return -1;
@ -2308,46 +2347,40 @@ void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
uint64_t nb_sectors, size; uint64_t nb_sectors, size;
int i, first_match, match; int i, first_match, match;
bdrv_get_geometry_hint(bs, nb_heads, max_track, last_sect); bdrv_get_geometry(bs, &nb_sectors);
if (*nb_heads != 0 && *max_track != 0 && *last_sect != 0) { match = -1;
/* User defined disk */ first_match = -1;
*rate = FDRIVE_RATE_500K; for (i = 0; ; i++) {
} else { parse = &fd_formats[i];
bdrv_get_geometry(bs, &nb_sectors); if (parse->drive == FDRIVE_DRV_NONE) {
match = -1; break;
first_match = -1; }
for (i = 0; ; i++) { if (drive_in == parse->drive ||
parse = &fd_formats[i]; drive_in == FDRIVE_DRV_NONE) {
if (parse->drive == FDRIVE_DRV_NONE) { size = (parse->max_head + 1) * parse->max_track *
parse->last_sect;
if (nb_sectors == size) {
match = i;
break; break;
} }
if (drive_in == parse->drive ||
drive_in == FDRIVE_DRV_NONE) {
size = (parse->max_head + 1) * parse->max_track *
parse->last_sect;
if (nb_sectors == size) {
match = i;
break;
}
if (first_match == -1) {
first_match = i;
}
}
}
if (match == -1) {
if (first_match == -1) { if (first_match == -1) {
match = 1; first_match = i;
} else {
match = first_match;
} }
parse = &fd_formats[match];
} }
*nb_heads = parse->max_head + 1;
*max_track = parse->max_track;
*last_sect = parse->last_sect;
*drive = parse->drive;
*rate = parse->rate;
} }
if (match == -1) {
if (first_match == -1) {
match = 1;
} else {
match = first_match;
}
parse = &fd_formats[match];
}
*nb_heads = parse->max_head + 1;
*max_track = parse->max_track;
*last_sect = parse->last_sect;
*drive = parse->drive;
*rate = parse->rate;
} }
int bdrv_get_translation_hint(BlockDriverState *bs) int bdrv_get_translation_hint(BlockDriverState *bs)

View File

@ -122,6 +122,7 @@ int bdrv_create(BlockDriver *drv, const char* filename,
int bdrv_create_file(const char* filename, QEMUOptionParameter *options); int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
BlockDriverState *bdrv_new(const char *device_name); BlockDriverState *bdrv_new(const char *device_name);
void bdrv_make_anon(BlockDriverState *bs); void bdrv_make_anon(BlockDriverState *bs);
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
void bdrv_delete(BlockDriverState *bs); void bdrv_delete(BlockDriverState *bs);
int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_parse_cache_flags(const char *mode, int *flags);
@ -141,6 +142,8 @@ bool bdrv_dev_is_tray_open(BlockDriverState *bs);
bool bdrv_dev_is_medium_locked(BlockDriverState *bs); bool bdrv_dev_is_medium_locked(BlockDriverState *bs);
int bdrv_read(BlockDriverState *bs, int64_t sector_num, int bdrv_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors); uint8_t *buf, int nb_sectors);
int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors);
int bdrv_write(BlockDriverState *bs, int64_t sector_num, int bdrv_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);
int bdrv_pread(BlockDriverState *bs, int64_t offset, int bdrv_pread(BlockDriverState *bs, int64_t offset,
@ -395,9 +398,7 @@ typedef enum {
BLKDBG_L2_ALLOC_COW_READ, BLKDBG_L2_ALLOC_COW_READ,
BLKDBG_L2_ALLOC_WRITE, BLKDBG_L2_ALLOC_WRITE,
BLKDBG_READ,
BLKDBG_READ_AIO, BLKDBG_READ_AIO,
BLKDBG_READ_BACKING,
BLKDBG_READ_BACKING_AIO, BLKDBG_READ_BACKING_AIO,
BLKDBG_READ_COMPRESSED, BLKDBG_READ_COMPRESSED,

View File

@ -26,24 +26,10 @@
#include "block_int.h" #include "block_int.h"
#include "module.h" #include "module.h"
typedef struct BlkdebugVars {
int state;
/* If inject_errno != 0, an error is injected for requests */
int inject_errno;
/* Decides if all future requests fail (false) or only the next one and
* after the next request inject_errno is reset to 0 (true) */
bool inject_once;
/* Decides if aio_readv/writev fails right away (true) or returns an error
* return value only in the callback (false) */
bool inject_immediately;
} BlkdebugVars;
typedef struct BDRVBlkdebugState { typedef struct BDRVBlkdebugState {
BlkdebugVars vars; int state;
QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
} BDRVBlkdebugState; } BDRVBlkdebugState;
typedef struct BlkdebugAIOCB { typedef struct BlkdebugAIOCB {
@ -73,12 +59,14 @@ typedef struct BlkdebugRule {
int error; int error;
int immediately; int immediately;
int once; int once;
int64_t sector;
} inject; } inject;
struct { struct {
int new_state; int new_state;
} set_state; } set_state;
} options; } options;
QLIST_ENTRY(BlkdebugRule) next; QLIST_ENTRY(BlkdebugRule) next;
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
} BlkdebugRule; } BlkdebugRule;
static QemuOptsList inject_error_opts = { static QemuOptsList inject_error_opts = {
@ -97,6 +85,10 @@ static QemuOptsList inject_error_opts = {
.name = "errno", .name = "errno",
.type = QEMU_OPT_NUMBER, .type = QEMU_OPT_NUMBER,
}, },
{
.name = "sector",
.type = QEMU_OPT_NUMBER,
},
{ {
.name = "once", .name = "once",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
@ -147,9 +139,7 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
[BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read", [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read",
[BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write", [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write",
[BLKDBG_READ] = "read",
[BLKDBG_READ_AIO] = "read_aio", [BLKDBG_READ_AIO] = "read_aio",
[BLKDBG_READ_BACKING] = "read_backing",
[BLKDBG_READ_BACKING_AIO] = "read_backing_aio", [BLKDBG_READ_BACKING_AIO] = "read_backing_aio",
[BLKDBG_READ_COMPRESSED] = "read_compressed", [BLKDBG_READ_COMPRESSED] = "read_compressed",
@ -228,6 +218,7 @@ static int add_rule(QemuOpts *opts, void *opaque)
rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0);
rule->options.inject.immediately = rule->options.inject.immediately =
qemu_opt_get_bool(opts, "immediately", 0); qemu_opt_get_bool(opts, "immediately", 0);
rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
break; break;
case ACTION_SET_STATE: case ACTION_SET_STATE:
@ -302,7 +293,7 @@ static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
filename = c + 1; filename = c + 1;
/* Set initial state */ /* Set initial state */
s->vars.state = 1; s->state = 1;
/* Open the backing file */ /* Open the backing file */
ret = bdrv_file_open(&bs->file, filename, flags); ret = bdrv_file_open(&bs->file, filename, flags);
@ -328,18 +319,18 @@ static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
} }
static BlockDriverAIOCB *inject_error(BlockDriverState *bs, static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque) BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
int error = s->vars.inject_errno; int error = rule->options.inject.error;
struct BlkdebugAIOCB *acb; struct BlkdebugAIOCB *acb;
QEMUBH *bh; QEMUBH *bh;
if (s->vars.inject_once) { if (rule->options.inject.once) {
s->vars.inject_errno = 0; QSIMPLEQ_INIT(&s->active_rules);
} }
if (s->vars.inject_immediately) { if (rule->options.inject.immediately) {
return NULL; return NULL;
} }
@ -358,14 +349,21 @@ static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque) BlockDriverCompletionFunc *cb, void *opaque)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
if (s->vars.inject_errno) { QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
return inject_error(bs, cb, opaque); if (rule->options.inject.sector == -1 ||
(rule->options.inject.sector >= sector_num &&
rule->options.inject.sector < sector_num + nb_sectors)) {
break;
}
} }
BlockDriverAIOCB *acb = if (rule && rule->options.inject.error) {
bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); return inject_error(bs, cb, opaque, rule);
return acb; }
return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
} }
static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs, static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
@ -373,14 +371,21 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque) BlockDriverCompletionFunc *cb, void *opaque)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
if (s->vars.inject_errno) { QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
return inject_error(bs, cb, opaque); if (rule->options.inject.sector == -1 ||
(rule->options.inject.sector >= sector_num &&
rule->options.inject.sector < sector_num + nb_sectors)) {
break;
}
} }
BlockDriverAIOCB *acb = if (rule && rule->options.inject.error) {
bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); return inject_error(bs, cb, opaque, rule);
return acb; }
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
} }
static void blkdebug_close(BlockDriverState *bs) static void blkdebug_close(BlockDriverState *bs)
@ -397,44 +402,53 @@ static void blkdebug_close(BlockDriverState *bs)
} }
} }
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
BlkdebugVars *old_vars) int old_state, bool injected)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
BlkdebugVars *vars = &s->vars;
/* Only process rules for the current state */ /* Only process rules for the current state */
if (rule->state && rule->state != old_vars->state) { if (rule->state && rule->state != old_state) {
return; return injected;
} }
/* Take the action */ /* Take the action */
switch (rule->action) { switch (rule->action) {
case ACTION_INJECT_ERROR: case ACTION_INJECT_ERROR:
vars->inject_errno = rule->options.inject.error; if (!injected) {
vars->inject_once = rule->options.inject.once; QSIMPLEQ_INIT(&s->active_rules);
vars->inject_immediately = rule->options.inject.immediately; injected = true;
}
QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
break; break;
case ACTION_SET_STATE: case ACTION_SET_STATE:
vars->state = rule->options.set_state.new_state; s->state = rule->options.set_state.new_state;
break; break;
} }
return injected;
} }
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
struct BlkdebugRule *rule; struct BlkdebugRule *rule;
BlkdebugVars old_vars = s->vars; int old_state = s->state;
bool injected;
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
injected = false;
QLIST_FOREACH(rule, &s->rules[event], next) { QLIST_FOREACH(rule, &s->rules[event], next) {
process_rule(bs, rule, &old_vars); injected = process_rule(bs, rule, old_state, injected);
} }
} }
static int64_t blkdebug_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file);
}
static BlockDriver bdrv_blkdebug = { static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug", .format_name = "blkdebug",
.protocol_name = "blkdebug", .protocol_name = "blkdebug",
@ -443,6 +457,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_file_open = blkdebug_open, .bdrv_file_open = blkdebug_open,
.bdrv_close = blkdebug_close, .bdrv_close = blkdebug_close,
.bdrv_getlength = blkdebug_getlength,
.bdrv_aio_readv = blkdebug_aio_readv, .bdrv_aio_readv = blkdebug_aio_readv,
.bdrv_aio_writev = blkdebug_aio_writev, .bdrv_aio_writev = blkdebug_aio_writev,

View File

@ -627,10 +627,11 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES);
assert(size > 0 && size <= s->cluster_size); assert(size > 0 && size <= s->cluster_size);
if (s->free_byte_offset == 0) { if (s->free_byte_offset == 0) {
s->free_byte_offset = qcow2_alloc_clusters(bs, s->cluster_size); offset = qcow2_alloc_clusters(bs, s->cluster_size);
if (s->free_byte_offset < 0) { if (offset < 0) {
return s->free_byte_offset; return offset;
} }
s->free_byte_offset = offset;
} }
redo: redo:
free_in_cluster = s->cluster_size - free_in_cluster = s->cluster_size -

View File

@ -405,7 +405,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
#ifdef DEBUG_ALLOC #ifdef DEBUG_ALLOC
{ {
BdrvCheckResult result = {0}; BdrvCheckResult result = {0};
qcow2_check_refcounts(bs, &result); qcow2_check_refcounts(bs, &result, 0);
} }
#endif #endif
return 0; return 0;
@ -522,7 +522,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
#ifdef DEBUG_ALLOC #ifdef DEBUG_ALLOC
{ {
BdrvCheckResult result = {0}; BdrvCheckResult result = {0};
qcow2_check_refcounts(bs, &result); qcow2_check_refcounts(bs, &result, 0);
} }
#endif #endif
return 0; return 0;
@ -582,7 +582,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
#ifdef DEBUG_ALLOC #ifdef DEBUG_ALLOC
{ {
BdrvCheckResult result = {0}; BdrvCheckResult result = {0};
qcow2_check_refcounts(bs, &result); qcow2_check_refcounts(bs, &result, 0);
} }
#endif #endif
return 0; return 0;

View File

@ -415,7 +415,7 @@ static int qcow2_open(BlockDriverState *bs, int flags)
#ifdef DEBUG_ALLOC #ifdef DEBUG_ALLOC
{ {
BdrvCheckResult result = {0}; BdrvCheckResult result = {0};
qcow2_check_refcounts(bs, &result); qcow2_check_refcounts(bs, &result, 0);
} }
#endif #endif
return ret; return ret;

View File

@ -748,7 +748,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
/* If the read straddles the end of the backing file, shorten it */ /* If the read straddles the end of the backing file, shorten it */
size = MIN((uint64_t)backing_length - pos, qiov->size); size = MIN((uint64_t)backing_length - pos, qiov->size);
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING); BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
qiov, size / BDRV_SECTOR_SIZE, cb, opaque); qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
} }

View File

@ -12,12 +12,14 @@ static int raw_open(BlockDriverState *bs, int flags)
static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
{ {
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov); return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov);
} }
static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
{ {
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
} }

View File

@ -259,8 +259,7 @@ typedef struct AIOReq {
uint8_t flags; uint8_t flags;
uint32_t id; uint32_t id;
QLIST_ENTRY(AIOReq) outstanding_aio_siblings; QLIST_ENTRY(AIOReq) aio_siblings;
QLIST_ENTRY(AIOReq) aioreq_siblings;
} AIOReq; } AIOReq;
enum AIOCBState { enum AIOCBState {
@ -283,8 +282,7 @@ struct SheepdogAIOCB {
void (*aio_done_func)(SheepdogAIOCB *); void (*aio_done_func)(SheepdogAIOCB *);
int canceled; int canceled;
int nr_pending;
QLIST_HEAD(aioreq_head, AIOReq) aioreq_head;
}; };
typedef struct BDRVSheepdogState { typedef struct BDRVSheepdogState {
@ -307,7 +305,8 @@ typedef struct BDRVSheepdogState {
Coroutine *co_recv; Coroutine *co_recv;
uint32_t aioreq_seq_num; uint32_t aioreq_seq_num;
QLIST_HEAD(outstanding_aio_head, AIOReq) outstanding_aio_head; QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head;
QLIST_HEAD(pending_aio_head, AIOReq) pending_aio_head;
} BDRVSheepdogState; } BDRVSheepdogState;
static const char * sd_strerror(int err) static const char * sd_strerror(int err)
@ -358,7 +357,7 @@ static const char * sd_strerror(int err)
* Sheepdog I/O handling: * Sheepdog I/O handling:
* *
* 1. In sd_co_rw_vector, we send the I/O requests to the server and * 1. In sd_co_rw_vector, we send the I/O requests to the server and
* link the requests to the outstanding_list in the * link the requests to the inflight_list in the
* BDRVSheepdogState. The function exits without waiting for * BDRVSheepdogState. The function exits without waiting for
* receiving the response. * receiving the response.
* *
@ -386,21 +385,18 @@ static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
aio_req->flags = flags; aio_req->flags = flags;
aio_req->id = s->aioreq_seq_num++; aio_req->id = s->aioreq_seq_num++;
QLIST_INSERT_HEAD(&s->outstanding_aio_head, aio_req, acb->nr_pending++;
outstanding_aio_siblings);
QLIST_INSERT_HEAD(&acb->aioreq_head, aio_req, aioreq_siblings);
return aio_req; return aio_req;
} }
static inline int free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req) static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
{ {
SheepdogAIOCB *acb = aio_req->aiocb; SheepdogAIOCB *acb = aio_req->aiocb;
QLIST_REMOVE(aio_req, outstanding_aio_siblings);
QLIST_REMOVE(aio_req, aioreq_siblings); QLIST_REMOVE(aio_req, aio_siblings);
g_free(aio_req); g_free(aio_req);
return !QLIST_EMPTY(&acb->aioreq_head); acb->nr_pending--;
} }
static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb) static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
@ -446,7 +442,7 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
acb->canceled = 0; acb->canceled = 0;
acb->coroutine = qemu_coroutine_self(); acb->coroutine = qemu_coroutine_self();
acb->ret = 0; acb->ret = 0;
QLIST_INIT(&acb->aioreq_head); acb->nr_pending = 0;
return acb; return acb;
} }
@ -541,11 +537,18 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
return ret; return ret;
} }
static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen, unsigned int *rlen);
static int do_req(int sockfd, SheepdogReq *hdr, void *data, static int do_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen, unsigned int *rlen) unsigned int *wlen, unsigned int *rlen)
{ {
int ret; int ret;
if (qemu_in_coroutine()) {
return do_co_req(sockfd, hdr, data, wlen, rlen);
}
socket_set_block(sockfd); socket_set_block(sockfd);
ret = send_req(sockfd, hdr, data, wlen); ret = send_req(sockfd, hdr, data, wlen);
if (ret < 0) { if (ret < 0) {
@ -577,10 +580,21 @@ out:
return ret; return ret;
} }
static void restart_co_req(void *opaque)
{
Coroutine *co = opaque;
qemu_coroutine_enter(co, NULL);
}
static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data, static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen, unsigned int *rlen) unsigned int *wlen, unsigned int *rlen)
{ {
int ret; int ret;
Coroutine *co;
co = qemu_coroutine_self();
qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, NULL, co);
socket_set_block(sockfd); socket_set_block(sockfd);
ret = send_co_req(sockfd, hdr, data, wlen); ret = send_co_req(sockfd, hdr, data, wlen);
@ -588,6 +602,8 @@ static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data,
goto out; goto out;
} }
qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, NULL, co);
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
if (ret < sizeof(*hdr)) { if (ret < sizeof(*hdr)) {
error_report("failed to get a rsp, %s", strerror(errno)); error_report("failed to get a rsp, %s", strerror(errno));
@ -609,6 +625,7 @@ static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data,
} }
ret = 0; ret = 0;
out: out:
qemu_aio_set_fd_handler(sockfd, NULL, NULL, NULL, NULL);
socket_set_nonblock(sockfd); socket_set_nonblock(sockfd);
return ret; return ret;
} }
@ -617,32 +634,41 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
struct iovec *iov, int niov, int create, struct iovec *iov, int niov, int create,
enum AIOCBState aiocb_type); enum AIOCBState aiocb_type);
static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid)
{
AIOReq *aio_req;
QLIST_FOREACH(aio_req, &s->pending_aio_head, aio_siblings) {
if (aio_req->oid == oid) {
return aio_req;
}
}
return NULL;
}
/* /*
* This function searchs pending requests to the object `oid', and * This function searchs pending requests to the object `oid', and
* sends them. * sends them.
*/ */
static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid, uint32_t id) static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid)
{ {
AIOReq *aio_req, *next; AIOReq *aio_req;
SheepdogAIOCB *acb; SheepdogAIOCB *acb;
int ret; int ret;
QLIST_FOREACH_SAFE(aio_req, &s->outstanding_aio_head, while ((aio_req = find_pending_req(s, oid)) != NULL) {
outstanding_aio_siblings, next) {
if (id == aio_req->id) {
continue;
}
if (aio_req->oid != oid) {
continue;
}
acb = aio_req->aiocb; acb = aio_req->aiocb;
/* move aio_req from pending list to inflight one */
QLIST_REMOVE(aio_req, aio_siblings);
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
ret = add_aio_request(s, aio_req, acb->qiov->iov, ret = add_aio_request(s, aio_req, acb->qiov->iov,
acb->qiov->niov, 0, acb->aiocb_type); acb->qiov->niov, 0, acb->aiocb_type);
if (ret < 0) { if (ret < 0) {
error_report("add_aio_request is failed"); error_report("add_aio_request is failed");
free_aio_req(s, aio_req); free_aio_req(s, aio_req);
if (QLIST_EMPTY(&acb->aioreq_head)) { if (!acb->nr_pending) {
sd_finish_aiocb(acb); sd_finish_aiocb(acb);
} }
} }
@ -663,10 +689,9 @@ static void coroutine_fn aio_read_response(void *opaque)
int ret; int ret;
AIOReq *aio_req = NULL; AIOReq *aio_req = NULL;
SheepdogAIOCB *acb; SheepdogAIOCB *acb;
int rest;
unsigned long idx; unsigned long idx;
if (QLIST_EMPTY(&s->outstanding_aio_head)) { if (QLIST_EMPTY(&s->inflight_aio_head)) {
goto out; goto out;
} }
@ -677,8 +702,8 @@ static void coroutine_fn aio_read_response(void *opaque)
goto out; goto out;
} }
/* find the right aio_req from the outstanding_aio list */ /* find the right aio_req from the inflight aio list */
QLIST_FOREACH(aio_req, &s->outstanding_aio_head, outstanding_aio_siblings) { QLIST_FOREACH(aio_req, &s->inflight_aio_head, aio_siblings) {
if (aio_req->id == rsp.id) { if (aio_req->id == rsp.id) {
break; break;
} }
@ -716,7 +741,7 @@ static void coroutine_fn aio_read_response(void *opaque)
* create requests are not allowed, so we search the * create requests are not allowed, so we search the
* pending requests here. * pending requests here.
*/ */
send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx), rsp.id); send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx));
} }
break; break;
case AIOCB_READ_UDATA: case AIOCB_READ_UDATA:
@ -734,8 +759,8 @@ static void coroutine_fn aio_read_response(void *opaque)
error_report("%s", sd_strerror(rsp.result)); error_report("%s", sd_strerror(rsp.result));
} }
rest = free_aio_req(s, aio_req); free_aio_req(s, aio_req);
if (!rest) { if (!acb->nr_pending) {
/* /*
* We've finished all requests which belong to the AIOCB, so * We've finished all requests which belong to the AIOCB, so
* we can switch back to sd_co_readv/writev now. * we can switch back to sd_co_readv/writev now.
@ -768,7 +793,8 @@ static int aio_flush_request(void *opaque)
{ {
BDRVSheepdogState *s = opaque; BDRVSheepdogState *s = opaque;
return !QLIST_EMPTY(&s->outstanding_aio_head); return !QLIST_EMPTY(&s->inflight_aio_head) ||
!QLIST_EMPTY(&s->pending_aio_head);
} }
static int set_nodelay(int fd) static int set_nodelay(int fd)
@ -1085,7 +1111,8 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
strstart(filename, "sheepdog:", (const char **)&filename); strstart(filename, "sheepdog:", (const char **)&filename);
QLIST_INIT(&s->outstanding_aio_head); QLIST_INIT(&s->inflight_aio_head);
QLIST_INIT(&s->pending_aio_head);
s->fd = -1; s->fd = -1;
memset(vdi, 0, sizeof(vdi)); memset(vdi, 0, sizeof(vdi));
@ -1447,6 +1474,7 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
iov.iov_len = sizeof(s->inode); iov.iov_len = sizeof(s->inode);
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
data_len, offset, 0, 0, offset); data_len, offset, 0, 0, offset);
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
ret = add_aio_request(s, aio_req, &iov, 1, 0, AIOCB_WRITE_UDATA); ret = add_aio_request(s, aio_req, &iov, 1, 0, AIOCB_WRITE_UDATA);
if (ret) { if (ret) {
free_aio_req(s, aio_req); free_aio_req(s, aio_req);
@ -1515,7 +1543,7 @@ out:
* Send I/O requests to the server. * Send I/O requests to the server.
* *
* This function sends requests to the server, links the requests to * This function sends requests to the server, links the requests to
* the outstanding_list in BDRVSheepdogState, and exits without * the inflight_list in BDRVSheepdogState, and exits without
* waiting the response. The responses are received in the * waiting the response. The responses are received in the
* `aio_read_response' function which is called from the main loop as * `aio_read_response' function which is called from the main loop as
* a fd handler. * a fd handler.
@ -1547,6 +1575,12 @@ static int coroutine_fn sd_co_rw_vector(void *p)
} }
} }
/*
* Make sure we don't free the aiocb before we are done with all requests.
* This additional reference is dropped at the end of this function.
*/
acb->nr_pending++;
while (done != total) { while (done != total) {
uint8_t flags = 0; uint8_t flags = 0;
uint64_t old_oid = 0; uint64_t old_oid = 0;
@ -1571,22 +1605,18 @@ static int coroutine_fn sd_co_rw_vector(void *p)
} }
if (create) { if (create) {
dprintf("update ino (%" PRIu32") %" PRIu64 " %" PRIu64 dprintf("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n",
" %" PRIu64 "\n", inode->vdi_id, oid, inode->vdi_id, oid,
vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); vid_to_data_oid(inode->data_vdi_id[idx], idx), idx);
oid = vid_to_data_oid(inode->vdi_id, idx); oid = vid_to_data_oid(inode->vdi_id, idx);
dprintf("new oid %lx\n", oid); dprintf("new oid %" PRIx64 "\n", oid);
} }
aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done); aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done);
if (create) { if (create) {
AIOReq *areq; AIOReq *areq;
QLIST_FOREACH(areq, &s->outstanding_aio_head, QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) {
outstanding_aio_siblings) {
if (areq == aio_req) {
continue;
}
if (areq->oid == oid) { if (areq->oid == oid) {
/* /*
* Sheepdog cannot handle simultaneous create * Sheepdog cannot handle simultaneous create
@ -1596,11 +1626,14 @@ static int coroutine_fn sd_co_rw_vector(void *p)
*/ */
aio_req->flags = 0; aio_req->flags = 0;
aio_req->base_oid = 0; aio_req->base_oid = 0;
QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req,
aio_siblings);
goto done; goto done;
} }
} }
} }
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
create, acb->aiocb_type); create, acb->aiocb_type);
if (ret < 0) { if (ret < 0) {
@ -1615,7 +1648,7 @@ static int coroutine_fn sd_co_rw_vector(void *p)
done += len; done += len;
} }
out: out:
if (QLIST_EMPTY(&acb->aioreq_head)) { if (!--acb->nr_pending) {
return acb->ret; return acb->ret;
} }
return 1; return 1;
@ -1628,7 +1661,6 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
int ret; int ret;
if (bs->growable && sector_num + nb_sectors > bs->total_sectors) { if (bs->growable && sector_num + nb_sectors > bs->total_sectors) {
/* TODO: shouldn't block here */
ret = sd_truncate(bs, (sector_num + nb_sectors) * SECTOR_SIZE); ret = sd_truncate(bs, (sector_num + nb_sectors) * SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -1696,7 +1728,7 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
hdr.opcode = SD_OP_FLUSH_VDI; hdr.opcode = SD_OP_FLUSH_VDI;
hdr.oid = vid_to_vdi_oid(inode->vdi_id); hdr.oid = vid_to_vdi_oid(inode->vdi_id);
ret = do_co_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen); ret = do_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen);
if (ret) { if (ret) {
error_report("failed to send a request to the sheep"); error_report("failed to send a request to the sheep");
return ret; return ret;
@ -1726,7 +1758,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
SheepdogInode *inode; SheepdogInode *inode;
unsigned int datalen; unsigned int datalen;
dprintf("sn_info: name %s id_str %s s: name %s vm_state_size %d " dprintf("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " "
"is_snapshot %d\n", sn_info->name, sn_info->id_str, "is_snapshot %d\n", sn_info->name, sn_info->id_str,
s->name, sn_info->vm_state_size, s->is_snapshot); s->name, sn_info->vm_state_size, s->is_snapshot);

View File

@ -609,6 +609,10 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
bdrv_flags |= ro ? 0 : BDRV_O_RDWR; bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
if (ro && copy_on_read) {
error_report("warning: disabling copy_on_read on readonly drive");
}
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
if (ret < 0) { if (ret < 0) {
error_report("could not open disk image %s: %s", error_report("could not open disk image %s: %s",

116
hw/fdc.c
View File

@ -153,8 +153,12 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
} }
#endif #endif
drv->head = head; drv->head = head;
if (drv->track != track) if (drv->track != track) {
if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
drv->media_changed = 0;
}
ret = 1; ret = 1;
}
drv->track = track; drv->track = track;
drv->sect = sect; drv->sect = sect;
} }
@ -170,9 +174,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
static void fd_recalibrate(FDrive *drv) static void fd_recalibrate(FDrive *drv)
{ {
FLOPPY_DPRINTF("recalibrate\n"); FLOPPY_DPRINTF("recalibrate\n");
drv->head = 0; fd_seek(drv, 0, 0, 1, 1);
drv->track = 0;
drv->sect = 1;
} }
/* Revalidate a disk drive after a disk change */ /* Revalidate a disk drive after a disk change */
@ -189,9 +191,6 @@ static void fd_revalidate(FDrive *drv)
&last_sect, drv->drive, &drive, &rate); &last_sect, drv->drive, &drive, &rate);
if (!bdrv_is_inserted(drv->bs)) { if (!bdrv_is_inserted(drv->bs)) {
FLOPPY_DPRINTF("No disk in drive\n"); FLOPPY_DPRINTF("No disk in drive\n");
} else if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
FLOPPY_DPRINTF("User defined disk (%d %d %d)\n",
nb_heads - 1, max_track, last_sect);
} else { } else {
FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads, FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
max_track, last_sect, ro ? "ro" : "rw"); max_track, last_sect, ro ? "ro" : "rw");
@ -305,6 +304,9 @@ enum {
}; };
enum { enum {
FD_SR0_DS0 = 0x01,
FD_SR0_DS1 = 0x02,
FD_SR0_HEAD = 0x04,
FD_SR0_EQPMT = 0x10, FD_SR0_EQPMT = 0x10,
FD_SR0_SEEK = 0x20, FD_SR0_SEEK = 0x20,
FD_SR0_ABNTERM = 0x40, FD_SR0_ABNTERM = 0x40,
@ -711,14 +713,6 @@ static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0)
qemu_set_irq(fdctrl->irq, 1); qemu_set_irq(fdctrl->irq, 1);
fdctrl->sra |= FD_SRA_INTPEND; fdctrl->sra |= FD_SRA_INTPEND;
} }
if (status0 & FD_SR0_SEEK) {
FDrive *cur_drv;
/* A seek clears the disk change line (if a disk is inserted) */
cur_drv = get_cur_drv(fdctrl);
if (cur_drv->bs != NULL && bdrv_is_inserted(cur_drv->bs)) {
cur_drv->media_changed = 0;
}
}
fdctrl->reset_sensei = 0; fdctrl->reset_sensei = 0;
fdctrl->status0 = status0; fdctrl->status0 = status0;
@ -978,14 +972,15 @@ static void fdctrl_reset_fifo(FDCtrl *fdctrl)
} }
/* Set FIFO status for the host to read */ /* Set FIFO status for the host to read */
static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len, int do_irq) static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len, uint8_t status0)
{ {
fdctrl->data_dir = FD_DIR_READ; fdctrl->data_dir = FD_DIR_READ;
fdctrl->data_len = fifo_len; fdctrl->data_len = fifo_len;
fdctrl->data_pos = 0; fdctrl->data_pos = 0;
fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO; fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
if (do_irq) if (status0) {
fdctrl_raise_irq(fdctrl, 0x00); fdctrl_raise_irq(fdctrl, status0);
}
} }
/* Set an error: unimplemented/unknown command */ /* Set an error: unimplemented/unknown command */
@ -997,7 +992,10 @@ static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
fdctrl_set_fifo(fdctrl, 1, 0); fdctrl_set_fifo(fdctrl, 1, 0);
} }
/* Seek to next sector */ /* Seek to next sector
* returns 0 when end of track reached (for DBL_SIDES on head 1)
* otherwise returns 1
*/
static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv) static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
{ {
FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n", FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
@ -1005,30 +1003,39 @@ static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
fd_sector(cur_drv)); fd_sector(cur_drv));
/* XXX: cur_drv->sect >= cur_drv->last_sect should be an /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
error in fact */ error in fact */
if (cur_drv->sect >= cur_drv->last_sect || uint8_t new_head = cur_drv->head;
cur_drv->sect == fdctrl->eot) { uint8_t new_track = cur_drv->track;
cur_drv->sect = 1; uint8_t new_sect = cur_drv->sect;
int ret = 1;
if (new_sect >= cur_drv->last_sect ||
new_sect == fdctrl->eot) {
new_sect = 1;
if (FD_MULTI_TRACK(fdctrl->data_state)) { if (FD_MULTI_TRACK(fdctrl->data_state)) {
if (cur_drv->head == 0 && if (new_head == 0 &&
(cur_drv->flags & FDISK_DBL_SIDES) != 0) { (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
cur_drv->head = 1; new_head = 1;
} else { } else {
cur_drv->head = 0; new_head = 0;
cur_drv->track++; new_track++;
if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) {
return 0; ret = 0;
}
} }
} else { } else {
cur_drv->track++; new_track++;
return 0; ret = 0;
}
if (ret == 1) {
FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
new_head, new_track, new_sect, fd_sector(cur_drv));
} }
FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
cur_drv->head, cur_drv->track,
cur_drv->sect, fd_sector(cur_drv));
} else { } else {
cur_drv->sect++; new_sect++;
} }
return 1; fd_seek(cur_drv, new_head, new_track, new_sect, 1);
return ret;
} }
/* Callback for transfer end (stop or abort) */ /* Callback for transfer end (stop or abort) */
@ -1038,10 +1045,12 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
FDrive *cur_drv; FDrive *cur_drv;
cur_drv = get_cur_drv(fdctrl); cur_drv = get_cur_drv(fdctrl);
fdctrl->status0 = status0 | FD_SR0_SEEK | (cur_drv->head << 2) |
GET_CUR_DRV(fdctrl);
FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
status0, status1, status2, status0, status1, status2, fdctrl->status0);
status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl)); fdctrl->fifo[0] = fdctrl->status0;
fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
fdctrl->fifo[1] = status1; fdctrl->fifo[1] = status1;
fdctrl->fifo[2] = status2; fdctrl->fifo[2] = status2;
fdctrl->fifo[3] = cur_drv->track; fdctrl->fifo[3] = cur_drv->track;
@ -1054,7 +1063,7 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
} }
fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO; fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
fdctrl->msr &= ~FD_MSR_NONDMA; fdctrl->msr &= ~FD_MSR_NONDMA;
fdctrl_set_fifo(fdctrl, 7, 1); fdctrl_set_fifo(fdctrl, 7, fdctrl->status0);
} }
/* Prepare a data transfer (either DMA or FIFO) */ /* Prepare a data transfer (either DMA or FIFO) */
@ -1169,7 +1178,7 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
if (direction != FD_DIR_WRITE) if (direction != FD_DIR_WRITE)
fdctrl->msr |= FD_MSR_DIO; fdctrl->msr |= FD_MSR_DIO;
/* IO based transfer: calculate len */ /* IO based transfer: calculate len */
fdctrl_raise_irq(fdctrl, 0x00); fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
return; return;
} }
@ -1598,16 +1607,18 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
{ {
FDrive *cur_drv = get_cur_drv(fdctrl); FDrive *cur_drv = get_cur_drv(fdctrl);
if(fdctrl->reset_sensei > 0) { if (fdctrl->reset_sensei > 0) {
fdctrl->fifo[0] = fdctrl->fifo[0] =
FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei; FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
fdctrl->reset_sensei--; fdctrl->reset_sensei--;
} else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
fdctrl->fifo[0] = FD_SR0_INVCMD;
fdctrl_set_fifo(fdctrl, 1, 0);
return;
} else { } else {
/* XXX: status0 handling is broken for read/write
commands, so we do this hack. It should be suppressed
ASAP */
fdctrl->fifo[0] = fdctrl->fifo[0] =
FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0))
| GET_CUR_DRV(fdctrl);
} }
fdctrl->fifo[1] = cur_drv->track; fdctrl->fifo[1] = cur_drv->track;
@ -1626,11 +1637,7 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
/* The seek command just sends step pulses to the drive and doesn't care if /* The seek command just sends step pulses to the drive and doesn't care if
* there is a medium inserted of if it's banging the head against the drive. * there is a medium inserted of if it's banging the head against the drive.
*/ */
if (fdctrl->fifo[2] > cur_drv->max_track) { fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
cur_drv->track = cur_drv->max_track;
} else {
cur_drv->track = fdctrl->fifo[2];
}
/* Raise Interrupt */ /* Raise Interrupt */
fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
} }
@ -1695,9 +1702,10 @@ static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl); cur_drv = get_cur_drv(fdctrl);
if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
cur_drv->track = cur_drv->max_track - 1; fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1,
cur_drv->sect, 1);
} else { } else {
cur_drv->track += fdctrl->fifo[2]; fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
} }
fdctrl_reset_fifo(fdctrl); fdctrl_reset_fifo(fdctrl);
/* Raise Interrupt */ /* Raise Interrupt */
@ -1711,9 +1719,9 @@ static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl); cur_drv = get_cur_drv(fdctrl);
if (fdctrl->fifo[2] > cur_drv->track) { if (fdctrl->fifo[2] > cur_drv->track) {
cur_drv->track = 0; fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1);
} else { } else {
cur_drv->track -= fdctrl->fifo[2]; fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
} }
fdctrl_reset_fifo(fdctrl); fdctrl_reset_fifo(fdctrl);
/* Raise Interrupt */ /* Raise Interrupt */

View File

@ -142,7 +142,7 @@ static uint8_t send_read_command(void)
} }
st0 = floppy_recv(); st0 = floppy_recv();
if (st0 != 0x40) { if (st0 != 0x60) {
ret = 1; ret = 1;
} }
@ -156,19 +156,16 @@ static uint8_t send_read_command(void)
return ret; return ret;
} }
static void send_step_pulse(void) static void send_step_pulse(int cyl)
{ {
int drive = 0; int drive = 0;
int head = 0; int head = 0;
static int cyl = 0;
floppy_send(CMD_SEEK); floppy_send(CMD_SEEK);
floppy_send(head << 2 | drive); floppy_send(head << 2 | drive);
g_assert(!get_irq(FLOPPY_IRQ)); g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(cyl); floppy_send(cyl);
ack_irq(); ack_irq();
cyl = (cyl + 1) % 4;
} }
static uint8_t cmos_read(uint8_t reg) static uint8_t cmos_read(uint8_t reg)
@ -195,8 +192,7 @@ static void test_no_media_on_start(void)
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
send_step_pulse(); send_step_pulse(1);
send_step_pulse();
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
@ -227,7 +223,14 @@ static void test_media_change(void)
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
send_step_pulse(); send_step_pulse(0);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
/* Step to next track should clear DSKCHG bit. */
send_step_pulse(1);
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_clear(dir, DSKCHG); assert_bit_clear(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
@ -243,11 +246,39 @@ static void test_media_change(void)
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
send_step_pulse(); send_step_pulse(0);
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
send_step_pulse(1);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
}
static void test_sense_interrupt(void)
{
int drive = 0;
int head = 0;
int cyl = 0;
int ret = 0;
floppy_send(CMD_SENSE_INT);
ret = floppy_recv();
g_assert(ret == 0x80);
floppy_send(CMD_SEEK);
floppy_send(head << 2 | drive);
g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(cyl);
floppy_send(CMD_SENSE_INT);
ret = floppy_recv();
g_assert(ret == 0x20);
floppy_recv();
} }
/* success if no crash or abort */ /* success if no crash or abort */
@ -297,6 +328,7 @@ int main(int argc, char **argv)
qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start); qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
qtest_add_func("/fdc/read_without_media", test_read_without_media); qtest_add_func("/fdc/read_without_media", test_read_without_media);
qtest_add_func("/fdc/media_change", test_media_change); qtest_add_func("/fdc/media_change", test_media_change);
qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
qtest_add_func("/fdc/fuzz-registers", fuzz_registers); qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
ret = g_test_run(); ret = g_test_run();

View File

@ -40,6 +40,7 @@ struct QTestState
bool irq_level[MAX_IRQ]; bool irq_level[MAX_IRQ];
GString *rx; GString *rx;
gchar *pid_file; gchar *pid_file;
char *socket_path, *qmp_socket_path;
}; };
#define g_assert_no_errno(ret) do { \ #define g_assert_no_errno(ret) do { \
@ -88,8 +89,6 @@ QTestState *qtest_init(const char *extra_args)
{ {
QTestState *s; QTestState *s;
int sock, qmpsock, ret, i; int sock, qmpsock, ret, i;
gchar *socket_path;
gchar *qmp_socket_path;
gchar *pid_file; gchar *pid_file;
gchar *command; gchar *command;
const char *qemu_binary; const char *qemu_binary;
@ -98,14 +97,14 @@ QTestState *qtest_init(const char *extra_args)
qemu_binary = getenv("QTEST_QEMU_BINARY"); qemu_binary = getenv("QTEST_QEMU_BINARY");
g_assert(qemu_binary != NULL); 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)); s = g_malloc(sizeof(*s));
sock = init_socket(socket_path); s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
qmpsock = init_socket(qmp_socket_path); s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
sock = init_socket(s->socket_path);
qmpsock = init_socket(s->qmp_socket_path);
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
@ -115,8 +114,8 @@ QTestState *qtest_init(const char *extra_args)
"-qmp unix:%s,nowait " "-qmp unix:%s,nowait "
"-pidfile %s " "-pidfile %s "
"-machine accel=qtest " "-machine accel=qtest "
"%s", qemu_binary, socket_path, "%s", qemu_binary, s->socket_path,
qmp_socket_path, pid_file, s->qmp_socket_path, pid_file,
extra_args ?: ""); extra_args ?: "");
ret = system(command); ret = system(command);
@ -133,9 +132,6 @@ QTestState *qtest_init(const char *extra_args)
s->irq_level[i] = false; s->irq_level[i] = false;
} }
g_free(socket_path);
g_free(qmp_socket_path);
/* Read the QMP greeting and then do the handshake */ /* Read the QMP greeting and then do the handshake */
qtest_qmp(s, ""); qtest_qmp(s, "");
qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"); qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }");
@ -160,6 +156,13 @@ void qtest_quit(QTestState *s)
fclose(f); fclose(f);
} }
unlink(s->pid_file);
unlink(s->socket_path);
unlink(s->qmp_socket_path);
g_free(s->pid_file);
g_free(s->socket_path);
g_free(s->qmp_socket_path);
} }
static void socket_sendf(int fd, const char *fmt, va_list ap) static void socket_sendf(int fd, const char *fmt, va_list ap)