mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches (rebased Stefan's pull request) # gpg: Signature made Thu 12 Nov 2015 15:34:16 GMT using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: (43 commits) block: Update copyright of the accounting code scsi-disk: Account for failed operations macio: Account for failed operations ide: Account for failed and invalid operations atapi: Account for failed and invalid operations xen_disk: Account for failed and invalid operations virtio-blk: Account for failed and invalid operations nvme: Account for failed and invalid operations iotests: Add test for the block device statistics block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode qemu-io: Account for failed, invalid and flush operations block: New option to define the intervals for collecting I/O statistics block: Add average I/O queue depth to BlockDeviceTimedStats block: Compute minimum, maximum and average I/O latencies block: Allow configuring whether to account failed and invalid ops block: Add statistics for failed and invalid I/O operations block: Add idle_time_ns to BlockDeviceStats util: Infrastructure for computing recent averages block: define 'clock_type' for the accounting code ide: Account for write operations correctly ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -3404,10 +3404,25 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_reset_all(bitmap->bitmap);
|
||||
if (!out) {
|
||||
hbitmap_reset_all(bitmap->bitmap);
|
||||
} else {
|
||||
HBitmap *backup = bitmap->bitmap;
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap->size,
|
||||
hbitmap_granularity(backup));
|
||||
*out = backup;
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||
{
|
||||
HBitmap *tmp = bitmap->bitmap;
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
bitmap->bitmap = in;
|
||||
hbitmap_free(tmp);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
|
||||
+120
-3
@@ -2,6 +2,7 @@
|
||||
* QEMU System Emulator block accounting
|
||||
*
|
||||
* Copyright (c) 2011 Christoph Hellwig
|
||||
* Copyright (c) 2015 Igalia, S.L.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,6 +26,54 @@
|
||||
#include "block/accounting.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
||||
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
||||
|
||||
void block_acct_init(BlockAcctStats *stats, bool account_invalid,
|
||||
bool account_failed)
|
||||
{
|
||||
stats->account_invalid = account_invalid;
|
||||
stats->account_failed = account_failed;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_cleanup(BlockAcctStats *stats)
|
||||
{
|
||||
BlockAcctTimedStats *s, *next;
|
||||
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
|
||||
g_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
unsigned i;
|
||||
|
||||
s = g_new0(BlockAcctTimedStats, 1);
|
||||
s->interval_length = interval_length;
|
||||
QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
timed_average_init(&s->latency[i], clock_type,
|
||||
(uint64_t) interval_length * NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
||||
BlockAcctTimedStats *s)
|
||||
{
|
||||
if (s == NULL) {
|
||||
return QSLIST_FIRST(&stats->intervals);
|
||||
} else {
|
||||
return QSLIST_NEXT(s, entries);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
int64_t bytes, enum BlockAcctType type)
|
||||
@@ -32,20 +81,71 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
cookie->bytes = bytes;
|
||||
cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
cookie->start_time_ns = qemu_clock_get_ns(clock_type);
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
latency_ns = qtest_latency_ns;
|
||||
}
|
||||
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
stats->total_time_ns[cookie->type] +=
|
||||
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns;
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->failed_ops[cookie->type]++;
|
||||
|
||||
if (stats->account_failed) {
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
latency_ns = qtest_latency_ns;
|
||||
}
|
||||
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
|
||||
{
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
/* block_acct_done() and block_acct_failed() update
|
||||
* total_time_ns[], but this one does not. The reason is that
|
||||
* invalid requests are accounted during their submission,
|
||||
* therefore there's no actual I/O involved. */
|
||||
|
||||
stats->invalid_ops[type]++;
|
||||
|
||||
if (stats->account_invalid) {
|
||||
stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
int num_requests)
|
||||
@@ -53,3 +153,20 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
stats->merged[type] += num_requests;
|
||||
}
|
||||
|
||||
int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
|
||||
{
|
||||
return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns;
|
||||
}
|
||||
|
||||
double block_acct_queue_depth(BlockAcctTimedStats *stats,
|
||||
enum BlockAcctType type)
|
||||
{
|
||||
uint64_t sum, elapsed;
|
||||
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
sum = timed_average_sum(&stats->latency[type], &elapsed);
|
||||
|
||||
return (double) sum / elapsed;
|
||||
}
|
||||
|
||||
+36
-14
@@ -221,11 +221,45 @@ static void backup_iostatus_reset(BlockJob *job)
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDriverState *bs = job->common.bs;
|
||||
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
} else {
|
||||
/* Everything is fine, delete this bitmap and install the backup. */
|
||||
bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_commit(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_abort(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static const BlockJobDriver backup_job_driver = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.set_speed = backup_set_speed,
|
||||
.iostatus_reset = backup_iostatus_reset,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
};
|
||||
|
||||
static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
@@ -441,19 +475,6 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
|
||||
if (job->sync_bitmap) {
|
||||
BdrvDirtyBitmap *bm;
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
} else {
|
||||
/* Everything is fine, delete this bitmap and install the backup. */
|
||||
bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
}
|
||||
}
|
||||
hbitmap_free(job->bitmap);
|
||||
|
||||
if (target->blk) {
|
||||
@@ -472,7 +493,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
Error **errp)
|
||||
BlockJobTxn *txn, Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
|
||||
@@ -554,6 +575,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
sync_bitmap : NULL;
|
||||
job->common.len = len;
|
||||
job->common.co = qemu_coroutine_create(backup_run);
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
qemu_coroutine_enter(job->common.co, job);
|
||||
return;
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ static void blk_delete(BlockBackend *blk)
|
||||
}
|
||||
g_free(blk->name);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
g_free(blk);
|
||||
}
|
||||
|
||||
|
||||
+130
-20
@@ -237,8 +237,21 @@ bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bdrv_drain_recurse(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
if (bs->drv && bs->drv->bdrv_drain) {
|
||||
bs->drv->bdrv_drain(bs);
|
||||
}
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_drain_recurse(child->bs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for pending requests to complete on a single BlockDriverState subtree
|
||||
* Wait for pending requests to complete on a single BlockDriverState subtree,
|
||||
* and suspend block driver's internal I/O until next request arrives.
|
||||
*
|
||||
* Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState
|
||||
* AioContext.
|
||||
@@ -251,6 +264,7 @@ void bdrv_drain(BlockDriverState *bs)
|
||||
{
|
||||
bool busy = true;
|
||||
|
||||
bdrv_drain_recurse(bs);
|
||||
while (busy) {
|
||||
/* Keep iterating */
|
||||
bdrv_flush_io_queue(bs);
|
||||
@@ -348,13 +362,14 @@ static void tracked_request_end(BdrvTrackedRequest *req)
|
||||
static void tracked_request_begin(BdrvTrackedRequest *req,
|
||||
BlockDriverState *bs,
|
||||
int64_t offset,
|
||||
unsigned int bytes, bool is_write)
|
||||
unsigned int bytes,
|
||||
enum BdrvTrackedRequestType type)
|
||||
{
|
||||
*req = (BdrvTrackedRequest){
|
||||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.is_write = is_write,
|
||||
.type = type,
|
||||
.co = qemu_coroutine_self(),
|
||||
.serialising = false,
|
||||
.overlap_offset = offset,
|
||||
@@ -971,7 +986,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
|
||||
bytes = ROUND_UP(bytes, align);
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, offset, bytes, false);
|
||||
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
|
||||
ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align,
|
||||
use_local_qiov ? &local_qiov : qiov,
|
||||
flags);
|
||||
@@ -1292,7 +1307,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
||||
* Pad qiov with the read parts and be sure to have a tracked request not
|
||||
* only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle.
|
||||
*/
|
||||
tracked_request_begin(&req, bs, offset, bytes, true);
|
||||
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE);
|
||||
|
||||
if (!qiov) {
|
||||
ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req);
|
||||
@@ -2317,18 +2332,20 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
|
||||
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
int ret;
|
||||
BdrvTrackedRequest req;
|
||||
|
||||
if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) ||
|
||||
bdrv_is_sg(bs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, 0, 0, BDRV_TRACKED_FLUSH);
|
||||
/* Write back cached data to the OS even with cache=unsafe */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS);
|
||||
if (bs->drv->bdrv_co_flush_to_os) {
|
||||
ret = bs->drv->bdrv_co_flush_to_os(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2368,14 +2385,17 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
ret = 0;
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH
|
||||
* in the case of cache=unsafe, so there are no useless flushes.
|
||||
*/
|
||||
flush_parent:
|
||||
return bs->file ? bdrv_co_flush(bs->file->bs) : 0;
|
||||
ret = bs->file ? bdrv_co_flush(bs->file->bs) : 0;
|
||||
out:
|
||||
tracked_request_end(&req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_flush(BlockDriverState *bs)
|
||||
@@ -2418,6 +2438,7 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque)
|
||||
int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
BdrvTrackedRequest req;
|
||||
int max_discard, ret;
|
||||
|
||||
if (!bs->drv) {
|
||||
@@ -2440,6 +2461,8 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, sector_num, nb_sectors,
|
||||
BDRV_TRACKED_DISCARD);
|
||||
bdrv_set_dirty(bs, sector_num, nb_sectors);
|
||||
|
||||
max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS);
|
||||
@@ -2473,20 +2496,24 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
if (acb == NULL) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
} else {
|
||||
qemu_coroutine_yield();
|
||||
ret = co.ret;
|
||||
}
|
||||
}
|
||||
if (ret && ret != -ENOTSUP) {
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sector_num += num;
|
||||
nb_sectors -= num;
|
||||
}
|
||||
return 0;
|
||||
ret = 0;
|
||||
out:
|
||||
tracked_request_end(&req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
||||
@@ -2515,26 +2542,109 @@ int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
||||
return rwco.ret;
|
||||
}
|
||||
|
||||
/* needed for generic scsi interface */
|
||||
typedef struct {
|
||||
CoroutineIOCompletion *co;
|
||||
QEMUBH *bh;
|
||||
} BdrvIoctlCompletionData;
|
||||
|
||||
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
static void bdrv_ioctl_bh_cb(void *opaque)
|
||||
{
|
||||
BdrvIoctlCompletionData *data = opaque;
|
||||
|
||||
bdrv_co_io_em_complete(data->co, -ENOTSUP);
|
||||
qemu_bh_delete(data->bh);
|
||||
}
|
||||
|
||||
static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
BdrvTrackedRequest tracked_req;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
BlockAIOCB *acb;
|
||||
|
||||
if (drv && drv->bdrv_ioctl)
|
||||
return drv->bdrv_ioctl(bs, req, buf);
|
||||
return -ENOTSUP;
|
||||
tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL);
|
||||
if (!drv || !drv->bdrv_aio_ioctl) {
|
||||
co.ret = -ENOTSUP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co);
|
||||
if (!acb) {
|
||||
BdrvIoctlCompletionData *data = g_new(BdrvIoctlCompletionData, 1);
|
||||
data->bh = aio_bh_new(bdrv_get_aio_context(bs),
|
||||
bdrv_ioctl_bh_cb, data);
|
||||
data->co = &co;
|
||||
qemu_bh_schedule(data->bh);
|
||||
}
|
||||
qemu_coroutine_yield();
|
||||
out:
|
||||
tracked_request_end(&tracked_req);
|
||||
return co.ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BlockDriverState *bs;
|
||||
int req;
|
||||
void *buf;
|
||||
int ret;
|
||||
} BdrvIoctlCoData;
|
||||
|
||||
static void coroutine_fn bdrv_co_ioctl_entry(void *opaque)
|
||||
{
|
||||
BdrvIoctlCoData *data = opaque;
|
||||
data->ret = bdrv_co_do_ioctl(data->bs, data->req, data->buf);
|
||||
}
|
||||
|
||||
/* needed for generic scsi interface */
|
||||
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
BdrvIoctlCoData data = {
|
||||
.bs = bs,
|
||||
.req = req,
|
||||
.buf = buf,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_co_ioctl_entry(&data);
|
||||
} else {
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
}
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
return data.ret;
|
||||
}
|
||||
|
||||
static void coroutine_fn bdrv_co_aio_ioctl_entry(void *opaque)
|
||||
{
|
||||
BlockAIOCBCoroutine *acb = opaque;
|
||||
acb->req.error = bdrv_co_do_ioctl(acb->common.bs,
|
||||
acb->req.req, acb->req.buf);
|
||||
bdrv_co_complete(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
BlockAIOCBCoroutine *acb = qemu_aio_get(&bdrv_em_co_aiocb_info,
|
||||
bs, cb, opaque);
|
||||
Coroutine *co;
|
||||
|
||||
if (drv && drv->bdrv_aio_ioctl)
|
||||
return drv->bdrv_aio_ioctl(bs, req, buf, cb, opaque);
|
||||
return NULL;
|
||||
acb->need_bh = true;
|
||||
acb->req.error = -EINPROGRESS;
|
||||
acb->req.req = req;
|
||||
acb->req.buf = buf;
|
||||
co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
bdrv_co_maybe_schedule_bh(acb);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
void *qemu_blockalign(BlockDriverState *bs, size_t size)
|
||||
|
||||
+38
-35
@@ -97,6 +97,7 @@ typedef struct IscsiAIOCB {
|
||||
int status;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
int ret;
|
||||
#ifdef __linux__
|
||||
sg_io_hdr_t *ioh;
|
||||
#endif
|
||||
@@ -779,6 +780,38 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
||||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static void iscsi_ioctl_bh_completion(void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static void iscsi_ioctl_handle_emulated(IscsiAIOCB *acb, int req, void *buf)
|
||||
{
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int ret = 0;
|
||||
|
||||
switch (req) {
|
||||
case SG_GET_VERSION_NUM:
|
||||
*(int *)buf = 30000;
|
||||
break;
|
||||
case SG_GET_SCSI_ID:
|
||||
((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
assert(!acb->bh);
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(bs),
|
||||
iscsi_ioctl_bh_completion, acb);
|
||||
acb->ret = ret;
|
||||
qemu_bh_schedule(acb->bh);
|
||||
}
|
||||
|
||||
static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
@@ -788,8 +821,6 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
struct iscsi_data data;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
assert(req == SG_IO);
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
@@ -798,6 +829,11 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
acb->buf = NULL;
|
||||
acb->ioh = buf;
|
||||
|
||||
if (req != SG_IO) {
|
||||
iscsi_ioctl_handle_emulated(acb, req, buf);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
||||
@@ -862,38 +898,6 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void ioctl_cb(void *opaque, int status)
|
||||
{
|
||||
int *p_status = opaque;
|
||||
*p_status = status;
|
||||
}
|
||||
|
||||
static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int status;
|
||||
|
||||
switch (req) {
|
||||
case SG_GET_VERSION_NUM:
|
||||
*(int *)buf = 30000;
|
||||
break;
|
||||
case SG_GET_SCSI_ID:
|
||||
((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
|
||||
break;
|
||||
case SG_IO:
|
||||
status = -EINPROGRESS;
|
||||
iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status);
|
||||
|
||||
while (status == -EINPROGRESS) {
|
||||
aio_poll(iscsilun->aio_context, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int64_t
|
||||
@@ -1824,7 +1828,6 @@ static BlockDriver bdrv_iscsi = {
|
||||
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||
|
||||
#ifdef __linux__
|
||||
.bdrv_ioctl = iscsi_ioctl,
|
||||
.bdrv_aio_ioctl = iscsi_aio_ioctl,
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -742,7 +742,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
||||
if (!s->dirty_bitmap) {
|
||||
g_free(s->replaces);
|
||||
block_job_release(bs);
|
||||
block_job_unref(&s->common);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -346,17 +346,68 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
if (bs->blk) {
|
||||
BlockAcctStats *stats = blk_get_stats(bs->blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
|
||||
s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (s->stats->has_idle_time_ns) {
|
||||
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
|
||||
}
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
|
||||
dev_stats->avg_rd_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||
|
||||
+13
@@ -375,6 +375,18 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_qed_drain(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
/* Cancel timer and start doing I/O that were meant to happen as if it
|
||||
* fired, that way we get bdrv_drain() taking care of the ongoing requests
|
||||
* correctly. */
|
||||
qed_cancel_need_check_timer(s);
|
||||
qed_plug_allocating_write_reqs(s);
|
||||
bdrv_aio_flush(s->bs, qed_clear_need_check, s);
|
||||
}
|
||||
|
||||
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -1676,6 +1688,7 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_check = bdrv_qed_check,
|
||||
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
|
||||
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
|
||||
.bdrv_drain = bdrv_qed_drain,
|
||||
};
|
||||
|
||||
static void bdrv_qed_init(void)
|
||||
|
||||
@@ -2175,12 +2175,6 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
return ioctl(s->fd, req, buf);
|
||||
}
|
||||
|
||||
static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
@@ -2338,7 +2332,6 @@ static BlockDriver bdrv_host_device = {
|
||||
|
||||
/* generic scsi device */
|
||||
#ifdef __linux__
|
||||
.bdrv_ioctl = hdev_ioctl,
|
||||
.bdrv_aio_ioctl = hdev_aio_ioctl,
|
||||
#endif
|
||||
};
|
||||
@@ -2471,7 +2464,6 @@ static BlockDriver bdrv_host_cdrom = {
|
||||
.bdrv_lock_medium = cdrom_lock_medium,
|
||||
|
||||
/* generic scsi device */
|
||||
.bdrv_ioctl = hdev_ioctl,
|
||||
.bdrv_aio_ioctl = hdev_aio_ioctl,
|
||||
};
|
||||
#endif /* __linux__ */
|
||||
|
||||
@@ -169,11 +169,6 @@ static void raw_lock_medium(BlockDriverState *bs, bool locked)
|
||||
bdrv_lock_medium(bs->file->bs, locked);
|
||||
}
|
||||
|
||||
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
return bdrv_ioctl(bs->file->bs, req, buf);
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb,
|
||||
@@ -262,7 +257,6 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_media_changed = &raw_media_changed,
|
||||
.bdrv_eject = &raw_eject,
|
||||
.bdrv_lock_medium = &raw_lock_medium,
|
||||
.bdrv_ioctl = &raw_ioctl,
|
||||
.bdrv_aio_ioctl = &raw_aio_ioctl,
|
||||
.create_opts = &raw_create_opts,
|
||||
.bdrv_has_zero_init = &raw_has_zero_init
|
||||
|
||||
+383
-106
File diff suppressed because it is too large
Load Diff
+158
-31
@@ -37,6 +37,19 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
/* Transactional group of block jobs */
|
||||
struct BlockJobTxn {
|
||||
|
||||
/* Is this txn being cancelled? */
|
||||
bool aborting;
|
||||
|
||||
/* List of jobs */
|
||||
QLIST_HEAD(, BlockJob) jobs;
|
||||
|
||||
/* Reference count */
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
@@ -60,6 +73,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
job->cb = cb;
|
||||
job->opaque = opaque;
|
||||
job->busy = true;
|
||||
job->refcnt = 1;
|
||||
bs->job = job;
|
||||
|
||||
/* Only set speed when necessary to avoid NotSupported error */
|
||||
@@ -68,7 +82,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
block_job_release(bs);
|
||||
block_job_unref(job);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
@@ -76,15 +90,101 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
return job;
|
||||
}
|
||||
|
||||
void block_job_release(BlockDriverState *bs)
|
||||
void block_job_ref(BlockJob *job)
|
||||
{
|
||||
BlockJob *job = bs->job;
|
||||
++job->refcnt;
|
||||
}
|
||||
|
||||
bs->job = NULL;
|
||||
bdrv_op_unblock_all(bs, job->blocker);
|
||||
error_free(job->blocker);
|
||||
g_free(job->id);
|
||||
g_free(job);
|
||||
void block_job_unref(BlockJob *job)
|
||||
{
|
||||
if (--job->refcnt == 0) {
|
||||
job->bs->job = NULL;
|
||||
bdrv_op_unblock_all(job->bs, job->blocker);
|
||||
bdrv_unref(job->bs);
|
||||
error_free(job->blocker);
|
||||
g_free(job->id);
|
||||
g_free(job);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_completed_single(BlockJob *job)
|
||||
{
|
||||
if (!job->ret) {
|
||||
if (job->driver->commit) {
|
||||
job->driver->commit(job);
|
||||
}
|
||||
} else {
|
||||
if (job->driver->abort) {
|
||||
job->driver->abort(job);
|
||||
}
|
||||
}
|
||||
job->cb(job->opaque, job->ret);
|
||||
if (job->txn) {
|
||||
block_job_txn_unref(job->txn);
|
||||
}
|
||||
block_job_unref(job);
|
||||
}
|
||||
|
||||
static void block_job_completed_txn_abort(BlockJob *job)
|
||||
{
|
||||
AioContext *ctx;
|
||||
BlockJobTxn *txn = job->txn;
|
||||
BlockJob *other_job, *next;
|
||||
|
||||
if (txn->aborting) {
|
||||
/*
|
||||
* We are cancelled by another job, which will handle everything.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
txn->aborting = true;
|
||||
/* We are the first failed job. Cancel other jobs. */
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
ctx = bdrv_get_aio_context(other_job->bs);
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
if (other_job == job || other_job->completed) {
|
||||
/* Other jobs are "effectively" cancelled by us, set the status for
|
||||
* them; this job, however, may or may not be cancelled, depending
|
||||
* on the caller, so leave it. */
|
||||
if (other_job != job) {
|
||||
other_job->cancelled = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
block_job_cancel_sync(other_job);
|
||||
assert(other_job->completed);
|
||||
}
|
||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
||||
ctx = bdrv_get_aio_context(other_job->bs);
|
||||
block_job_completed_single(other_job);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_completed_txn_success(BlockJob *job)
|
||||
{
|
||||
AioContext *ctx;
|
||||
BlockJobTxn *txn = job->txn;
|
||||
BlockJob *other_job, *next;
|
||||
/*
|
||||
* Successful completion, see if there are other running jobs in this
|
||||
* txn.
|
||||
*/
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
if (!other_job->completed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* We are the last completed job, commit the transaction. */
|
||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
||||
ctx = bdrv_get_aio_context(other_job->bs);
|
||||
aio_context_acquire(ctx);
|
||||
assert(other_job->ret == 0);
|
||||
block_job_completed_single(other_job);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_completed(BlockJob *job, int ret)
|
||||
@@ -92,8 +192,16 @@ void block_job_completed(BlockJob *job, int ret)
|
||||
BlockDriverState *bs = job->bs;
|
||||
|
||||
assert(bs->job == job);
|
||||
job->cb(job->opaque, ret);
|
||||
block_job_release(bs);
|
||||
assert(!job->completed);
|
||||
job->completed = true;
|
||||
job->ret = ret;
|
||||
if (!job->txn) {
|
||||
block_job_completed_single(job);
|
||||
} else if (ret < 0 || block_job_is_cancelled(job)) {
|
||||
block_job_completed_txn_abort(job);
|
||||
} else {
|
||||
block_job_completed_txn_success(job);
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -178,43 +286,29 @@ struct BlockFinishData {
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void block_job_finish_cb(void *opaque, int ret)
|
||||
{
|
||||
struct BlockFinishData *data = opaque;
|
||||
|
||||
data->cancelled = block_job_is_cancelled(data->job);
|
||||
data->ret = ret;
|
||||
data->cb(data->opaque, ret);
|
||||
}
|
||||
|
||||
static int block_job_finish_sync(BlockJob *job,
|
||||
void (*finish)(BlockJob *, Error **errp),
|
||||
Error **errp)
|
||||
{
|
||||
struct BlockFinishData data;
|
||||
BlockDriverState *bs = job->bs;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
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_finish_cb;
|
||||
job->opaque = &data;
|
||||
block_job_ref(job);
|
||||
finish(job, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
block_job_unref(job);
|
||||
return -EBUSY;
|
||||
}
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
while (!job->completed) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
|
||||
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
||||
block_job_unref(job);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
|
||||
@@ -406,3 +500,36 @@ void block_job_defer_to_main_loop(BlockJob *job,
|
||||
|
||||
qemu_bh_schedule(data->bh);
|
||||
}
|
||||
|
||||
BlockJobTxn *block_job_txn_new(void)
|
||||
{
|
||||
BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
|
||||
QLIST_INIT(&txn->jobs);
|
||||
txn->refcnt = 1;
|
||||
return txn;
|
||||
}
|
||||
|
||||
static void block_job_txn_ref(BlockJobTxn *txn)
|
||||
{
|
||||
txn->refcnt++;
|
||||
}
|
||||
|
||||
void block_job_txn_unref(BlockJobTxn *txn)
|
||||
{
|
||||
if (txn && --txn->refcnt == 0) {
|
||||
g_free(txn);
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
|
||||
{
|
||||
if (!txn) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!job->txn);
|
||||
job->txn = txn;
|
||||
|
||||
QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
|
||||
block_job_txn_ref(txn);
|
||||
}
|
||||
|
||||
+1
-5
@@ -97,11 +97,7 @@ which is included at the end of this document.
|
||||
}
|
||||
```
|
||||
|
||||
## Transactions (Not yet implemented)
|
||||
|
||||
* Transactional commands are forthcoming in a future version,
|
||||
and are not yet available for use. This section serves as
|
||||
documentation of intent for their design and usage.
|
||||
## Transactions
|
||||
|
||||
### Justification
|
||||
|
||||
|
||||
@@ -522,6 +522,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
|
||||
" flush_total_time_ns=%" PRId64
|
||||
" rd_merged=%" PRId64
|
||||
" wr_merged=%" PRId64
|
||||
" idle_time_ns=%" PRId64
|
||||
"\n",
|
||||
stats->value->stats->rd_bytes,
|
||||
stats->value->stats->wr_bytes,
|
||||
@@ -532,7 +533,8 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
|
||||
stats->value->stats->rd_total_time_ns,
|
||||
stats->value->stats->flush_total_time_ns,
|
||||
stats->value->stats->rd_merged,
|
||||
stats->value->stats->wr_merged);
|
||||
stats->value->stats->wr_merged,
|
||||
stats->value->stats->idle_time_ns);
|
||||
}
|
||||
|
||||
qapi_free_BlockStatsList(stats_list);
|
||||
|
||||
+8
-3
@@ -201,10 +201,11 @@ static void nvme_rw_cb(void *opaque, int ret)
|
||||
NvmeCtrl *n = sq->ctrl;
|
||||
NvmeCQueue *cq = n->cq[sq->cqid];
|
||||
|
||||
block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
|
||||
if (!ret) {
|
||||
block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
|
||||
req->status = NVME_SUCCESS;
|
||||
} else {
|
||||
block_acct_failed(blk_get_stats(n->conf.blk), &req->acct);
|
||||
req->status = NVME_INTERNAL_DEV_ERROR;
|
||||
}
|
||||
if (req->has_sg) {
|
||||
@@ -238,18 +239,22 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
|
||||
uint64_t data_size = (uint64_t)nlb << data_shift;
|
||||
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
|
||||
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
|
||||
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
|
||||
|
||||
if ((slba + nlb) > ns->id_ns.nsze) {
|
||||
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
|
||||
return NVME_LBA_RANGE | NVME_DNR;
|
||||
}
|
||||
|
||||
if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) {
|
||||
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
|
||||
assert((nlb << data_shift) == req->qsg.size);
|
||||
|
||||
req->has_sg = true;
|
||||
dma_acct_start(n->conf.blk, &req->acct, &req->qsg,
|
||||
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
|
||||
dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct);
|
||||
req->aiocb = is_write ?
|
||||
dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) :
|
||||
dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req);
|
||||
|
||||
@@ -76,7 +76,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
|
||||
s->rq = req;
|
||||
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
|
||||
block_acct_done(blk_get_stats(s->blk), &req->acct);
|
||||
block_acct_failed(blk_get_stats(s->blk), &req->acct);
|
||||
virtio_blk_free_request(req);
|
||||
}
|
||||
|
||||
@@ -536,6 +536,8 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
|
||||
if (!virtio_blk_sect_range_ok(req->dev, req->sector_num,
|
||||
req->qiov.size)) {
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
|
||||
block_acct_invalid(blk_get_stats(req->dev->blk),
|
||||
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
|
||||
virtio_blk_free_request(req);
|
||||
return;
|
||||
}
|
||||
|
||||
+25
-2
@@ -537,7 +537,11 @@ static void qemu_aio_complete(void *opaque, int ret)
|
||||
break;
|
||||
}
|
||||
case BLKIF_OP_READ:
|
||||
block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
|
||||
if (ioreq->status == BLKIF_RSP_OKAY) {
|
||||
block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
|
||||
} else {
|
||||
block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
|
||||
}
|
||||
break;
|
||||
case BLKIF_OP_DISCARD:
|
||||
default:
|
||||
@@ -576,7 +580,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
|
||||
}
|
||||
|
||||
block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
|
||||
ioreq->v.size, BLOCK_ACCT_WRITE);
|
||||
ioreq->v.size,
|
||||
ioreq->req.operation == BLKIF_OP_WRITE ?
|
||||
BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
|
||||
ioreq->aio_inflight++;
|
||||
blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE,
|
||||
&ioreq->v, ioreq->v.size / BLOCK_SIZE,
|
||||
@@ -720,6 +726,23 @@ static void blk_handle_requests(struct XenBlkDev *blkdev)
|
||||
|
||||
/* parse them */
|
||||
if (ioreq_parse(ioreq) != 0) {
|
||||
|
||||
switch (ioreq->req.operation) {
|
||||
case BLKIF_OP_READ:
|
||||
block_acct_invalid(blk_get_stats(blkdev->blk),
|
||||
BLOCK_ACCT_READ);
|
||||
break;
|
||||
case BLKIF_OP_WRITE:
|
||||
block_acct_invalid(blk_get_stats(blkdev->blk),
|
||||
BLOCK_ACCT_WRITE);
|
||||
break;
|
||||
case BLKIF_OP_FLUSH_DISKCACHE:
|
||||
block_acct_invalid(blk_get_stats(blkdev->blk),
|
||||
BLOCK_ACCT_FLUSH);
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if (blk_send_response_one(ioreq)) {
|
||||
xen_be_send_notify(&blkdev->xendev);
|
||||
}
|
||||
|
||||
+19
-12
@@ -108,27 +108,30 @@ static void cd_data_to_raw(uint8_t *buf, int lba)
|
||||
static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size)
|
||||
{
|
||||
int ret;
|
||||
block_acct_start(blk_get_stats(s->blk), &s->acct,
|
||||
4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
|
||||
|
||||
switch(sector_size) {
|
||||
case 2048:
|
||||
block_acct_start(blk_get_stats(s->blk), &s->acct,
|
||||
4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
|
||||
ret = blk_read(s->blk, (int64_t)lba << 2, buf, 4);
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
break;
|
||||
case 2352:
|
||||
block_acct_start(blk_get_stats(s->blk), &s->acct,
|
||||
4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
|
||||
ret = blk_read(s->blk, (int64_t)lba << 2, buf + 16, 4);
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
cd_data_to_raw(buf, lba);
|
||||
if (ret >= 0) {
|
||||
cd_data_to_raw(buf, lba);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EIO;
|
||||
break;
|
||||
block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
block_acct_failed(blk_get_stats(s->blk), &s->acct);
|
||||
} else {
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -357,7 +360,11 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
|
||||
return;
|
||||
|
||||
eot:
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
if (ret < 0) {
|
||||
block_acct_failed(blk_get_stats(s->blk), &s->acct);
|
||||
} else {
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
}
|
||||
ide_set_inactive(s, false);
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -574,7 +574,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
if (ret != 0) {
|
||||
if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO |
|
||||
IDE_RETRY_READ)) {
|
||||
@@ -582,6 +581,8 @@ static void ide_sector_read_cb(void *opaque, int ret)
|
||||
}
|
||||
}
|
||||
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
|
||||
n = s->nsector;
|
||||
if (n > s->req_nb_sectors) {
|
||||
n = s->req_nb_sectors;
|
||||
@@ -621,6 +622,7 @@ static void ide_sector_read(IDEState *s)
|
||||
|
||||
if (!ide_sect_range_ok(s, sector_num, n)) {
|
||||
ide_rw_error(s);
|
||||
block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -672,6 +674,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
|
||||
assert(s->bus->retry_unit == s->unit);
|
||||
s->bus->error_status = op;
|
||||
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
block_acct_failed(blk_get_stats(s->blk), &s->acct);
|
||||
if (op & IDE_RETRY_DMA) {
|
||||
ide_dma_error(s);
|
||||
} else {
|
||||
@@ -750,6 +753,7 @@ static void ide_dma_cb(void *opaque, int ret)
|
||||
if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) &&
|
||||
!ide_sect_range_ok(s, sector_num, n)) {
|
||||
ide_dma_error(s);
|
||||
block_acct_invalid(blk_get_stats(s->blk), s->acct.type);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -826,7 +830,6 @@ static void ide_sector_write_cb(void *opaque, int ret)
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
|
||||
s->pio_aiocb = NULL;
|
||||
s->status &= ~BUSY_STAT;
|
||||
@@ -837,6 +840,8 @@ static void ide_sector_write_cb(void *opaque, int ret)
|
||||
}
|
||||
}
|
||||
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
|
||||
n = s->nsector;
|
||||
if (n > s->req_nb_sectors) {
|
||||
n = s->req_nb_sectors;
|
||||
@@ -887,6 +892,7 @@ static void ide_sector_write(IDEState *s)
|
||||
|
||||
if (!ide_sect_range_ok(s, sector_num, n)) {
|
||||
ide_rw_error(s);
|
||||
block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -895,7 +901,7 @@ static void ide_sector_write(IDEState *s)
|
||||
qemu_iovec_init_external(&s->qiov, &s->iov, 1);
|
||||
|
||||
block_acct_start(blk_get_stats(s->blk), &s->acct,
|
||||
n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
|
||||
n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
|
||||
s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n,
|
||||
ide_sector_write_cb, s);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user