On Thu, Aug 4, 2011 at 9:07 PM, Stefan Hajnoczi <stefanha@xxxxxxxxx> wrote: > On Thu, Aug 4, 2011 at 5:34 AM, Zhi Yong Wu <wuzhy@xxxxxxxxxxxxxxxxxx> wrote: >> @@ -1387,6 +1422,11 @@ void bdrv_set_io_limits(BlockDriverState *bs, >> { >> memset(&bs->io_limits, 0, sizeof(BlockIOLimit)); >> bs->io_limits = *io_limits; >> + if (bdrv_io_limits_enabled(bs)) { >> + bs->io_limits_enabled = true; >> + } else { >> + bs->io_limits_enabled = false; >> + } > > Or in one line: > bs->io_limits_enabled = bdrv_io_limits_enabled(bs); nice. > >> } >> >> /* Recognize floppy formats */ >> @@ -1621,6 +1661,7 @@ BlockDriverState *bdrv_find(const char *name) >> return NULL; >> } >> >> +/* disk I/O throttling */ > > Looks like this should not be here.\ Let me check when i will go to office next week. > >> BlockDriverState *bdrv_next(BlockDriverState *bs) >> { >> if (!bs) { >> @@ -1784,6 +1825,16 @@ static void bdrv_print_dict(QObject *obj, void *opaque) >> qdict_get_bool(qdict, "ro"), >> qdict_get_str(qdict, "drv"), >> qdict_get_bool(qdict, "encrypted")); >> + >> + monitor_printf(mon, " bps=%" PRId64 " bps_rd=%" PRId64 >> + " bps_wr=%" PRId64 " iops=%" PRId64 >> + " iops_rd=%" PRId64 " iops_wr=%" PRId64, >> + qdict_get_int(qdict, "bps"), >> + qdict_get_int(qdict, "bps_rd"), >> + qdict_get_int(qdict, "bps_wr"), >> + qdict_get_int(qdict, "iops"), >> + qdict_get_int(qdict, "iops_rd"), >> + qdict_get_int(qdict, "iops_wr")); >> } else { >> monitor_printf(mon, " [not inserted]"); >> } >> @@ -1816,10 +1867,22 @@ void bdrv_info(Monitor *mon, QObject **ret_data) >> QDict *bs_dict = qobject_to_qdict(bs_obj); >> >> obj = qobject_from_jsonf("{ 'file': %s, 'ro': %i, 'drv': %s, " >> - "'encrypted': %i }", >> + "'encrypted': %i, " >> + "'bps': %" PRId64 "," >> + "'bps_rd': %" PRId64 "," >> + "'bps_wr': %" PRId64 "," >> + "'iops': %" PRId64 "," >> + "'iops_rd': %" PRId64 "," >> + "'iops_wr': %" PRId64 "}", >> bs->filename, bs->read_only, >> bs->drv->format_name, >> - bdrv_is_encrypted(bs)); >> + bdrv_is_encrypted(bs), >> + bs->io_limits.bps[2], >> + bs->io_limits.bps[0], >> + bs->io_limits.bps[1], >> + bs->io_limits.iops[2], >> + bs->io_limits.iops[0], >> + bs->io_limits.iops[1]); >> if (bs->backing_file[0] != '\0') { >> QDict *qdict = qobject_to_qdict(obj); >> qdict_put(qdict, "backing_file", >> @@ -2307,7 +2370,7 @@ static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors, >> } >> >> /* If a limit was exceeded, immediately queue this request */ >> - if ((bs->req_from_queue == false) >> + if (!bs->req_from_queue >> && !QTAILQ_EMPTY(&bs->block_queue->requests)) { >> if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL] >> || bs->io_limits.bps[is_write] || bs->io_limits.iops[is_write] >> @@ -2362,14 +2425,14 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, >> trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque); >> >> if (!drv || bdrv_check_request(bs, sector_num, nb_sectors)) { >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> bs->req_from_queue = false; >> } >> return NULL; >> } >> >> /* throttling disk read I/O */ >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> if (bdrv_exceed_io_limits(bs, nb_sectors, false, &wait_time)) { >> ret = qemu_block_queue_enqueue(bs->block_queue, bs, bdrv_aio_readv, >> sector_num, qiov, nb_sectors, cb, opaque); >> @@ -2388,14 +2451,14 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, >> bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE; >> bs->rd_ops ++; >> >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> bs->io_disps.bytes[BLOCK_IO_LIMIT_READ] += >> (unsigned) nb_sectors * BDRV_SECTOR_SIZE; >> bs->io_disps.ios[BLOCK_IO_LIMIT_READ]++; >> } >> } >> >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> bs->req_from_queue = false; >> } >> >> @@ -2451,7 +2514,7 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, >> >> if (!drv || bs->read_only >> || bdrv_check_request(bs, sector_num, nb_sectors)) { >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> bs->req_from_queue = false; >> } >> >> @@ -2466,7 +2529,7 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, >> } >> >> /* throttling disk write I/O */ >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> if (bdrv_exceed_io_limits(bs, nb_sectors, true, &wait_time)) { >> ret = qemu_block_queue_enqueue(bs->block_queue, bs, bdrv_aio_writev, >> sector_num, qiov, nb_sectors, cb, opaque); >> @@ -2488,14 +2551,14 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, >> bs->wr_highest_sector = sector_num + nb_sectors - 1; >> } >> >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> bs->io_disps.bytes[BLOCK_IO_LIMIT_WRITE] += >> (unsigned) nb_sectors * BDRV_SECTOR_SIZE; >> bs->io_disps.ios[BLOCK_IO_LIMIT_WRITE]++; >> } >> } >> >> - if (bdrv_io_limits_enable(&bs->io_limits)) { >> + if (bs->io_limits_enabled) { >> bs->req_from_queue = false; >> } >> >> diff --git a/block.h b/block.h >> index f0dac62..97d2177 100644 >> --- a/block.h >> +++ b/block.h >> @@ -57,6 +57,11 @@ void bdrv_info(Monitor *mon, QObject **ret_data); >> void bdrv_stats_print(Monitor *mon, const QObject *data); >> void bdrv_info_stats(Monitor *mon, QObject **ret_data); >> >> +/* disk I/O throttling */ >> +void bdrv_io_limits_res_init(BlockDriverState *bs); >> +void bdrv_io_limits_res_deinit(BlockDriverState *bs); >> +bool bdrv_io_limits_enabled(BlockDriverState *bs); >> + >> void bdrv_init(void); >> void bdrv_init_with_whitelist(void); >> BlockDriver *bdrv_find_protocol(const char *filename); >> diff --git a/block_int.h b/block_int.h >> index 1ca826b..7c96094 100644 >> --- a/block_int.h >> +++ b/block_int.h >> @@ -199,6 +199,8 @@ struct BlockDriverState { >> BlockIODisp io_disps; >> BlockQueue *block_queue; >> QEMUTimer *block_timer; >> + bool io_limits_enabled; >> + bool io_unlimit; >> bool req_from_queue; >> >> /* I/O stats (display with "info blockstats"). */ >> diff --git a/blockdev.c b/blockdev.c >> index aff6bb2..dc2e8a9 100644 >> --- a/blockdev.c >> +++ b/blockdev.c >> @@ -376,7 +376,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) >> return NULL; >> } >> >> - /* disk io limits */ >> + /* disk io throttling */ >> iol_flag = qemu_opt_io_limits_enable_flag(opts, iol_opts); >> if (iol_flag) { >> memset(&io_limits, 0, sizeof(BlockIOLimit)); >> @@ -387,6 +387,14 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) >> io_limits.iops[2] = qemu_opt_get_number(opts, "iops", 0); >> io_limits.iops[0] = qemu_opt_get_number(opts, "iops_rd", 0); >> io_limits.iops[1] = qemu_opt_get_number(opts, "iops_wr", 0); >> + >> + if (((io_limits.bps[2] != 0) >> + && ((io_limits.bps[0] != 0) || (io_limits.bps[1] != 0))) >> + || ((io_limits.iops[2] != 0) >> + && ((io_limits.iops[0] != 0) || (io_limits.iops[1] != 0)))) { > > Please define constants for read/write/total instead of using magic numbers. They have been defined. OK. let me replace those magic numbers with them. > >> + error_report("Some params for io throttling can not coexist"); > > This error message is vague. > > "bps and bps_rd/bps_wr cannot be used at the same time" > >> + return NULL; >> + } >> } >> >> on_write_error = BLOCK_ERR_STOP_ENOSPC; >> @@ -765,6 +773,82 @@ int do_change_block(Monitor *mon, const char *device, >> return monitor_read_bdrv_key_start(mon, bs, NULL, NULL); >> } >> >> +/* throttling disk I/O limits */ >> +int do_block_set_io_throttle(Monitor *mon, >> + const QDict *qdict, QObject **ret_data) >> +{ >> + const char *devname = qdict_get_str(qdict, "device"); >> + uint64_t bps = qdict_get_try_int(qdict, "bps", -1); >> + uint64_t bps_rd = qdict_get_try_int(qdict, "bps_rd", -1); >> + uint64_t bps_wr = qdict_get_try_int(qdict, "bps_wr", -1); >> + uint64_t iops = qdict_get_try_int(qdict, "iops", -1); >> + uint64_t iops_rd = qdict_get_try_int(qdict, "iops_rd", -1); >> + uint64_t iops_wr = qdict_get_try_int(qdict, "iops_wr", -1); >> + BlockDriverState *bs; >> + >> + bs = bdrv_find(devname); >> + if (!bs) { >> + qerror_report(QERR_DEVICE_NOT_FOUND, devname); >> + return -1; >> + } >> + >> + if (devname && (bps == -1) && (bps_rd == -1) && (bps_wr == -1) >> + && (iops == -1) && (iops_rd == -1) && (iops_wr == -1)) { > > No need for the devname check. It must be non-NULL since it is a > mandatory argument, also bdrv_find() would have crashed since devname > is used unchecked. OK. i will remove devname. > >> + qerror_report(QERR_IO_THROTTLE_NO_PARAM); >> + return -1; >> + } >> + >> + if (((bps != -1) && ((bps_rd != -1) || (bps_wr != -1))) >> + || ((iops != -1) && ((iops_rd != -1) || (iops_wr != -1)))) { >> + qerror_report(QERR_IO_THROTTLE_PARAM_ERROR); >> + return -1; >> + } >> + >> + if (bps != -1) { >> + bs->io_limits.bps[2] = bps; >> + bs->io_limits.bps[0] = 0; >> + bs->io_limits.bps[1] = 0; >> + } >> + >> + if ((bps_rd != -1) || (bps_wr != -1)) { >> + bs->io_limits.bps[0] = (bps_rd == -1) ? bs->io_limits.bps[0] : bps_rd; >> + bs->io_limits.bps[1] = (bps_wr == -1) ? bs->io_limits.bps[1] : bps_wr; >> + bs->io_limits.bps[2] = 0; >> + } >> + >> + if (iops != -1) { >> + bs->io_limits.iops[2] = iops; >> + bs->io_limits.iops[0] = 0; >> + bs->io_limits.iops[1] = 0; >> + } >> + >> + if ((iops_rd != -1) || (iops_wr != -1)) { >> + bs->io_limits.iops[0] = >> + (iops_rd == -1) ? bs->io_limits.iops[0] : iops_rd; >> + bs->io_limits.iops[1] = >> + (iops_wr == -1) ? bs->io_limits.iops[1] : iops_wr; >> + bs->io_limits.iops[2] = 0; >> + } >> + >> + if (bdrv_io_limits_enabled(bs)) { >> + if (!bs->io_limits_enabled) { >> + bdrv_io_limits_res_init(bs); >> + } >> + } else { >> + if (bs->io_limits_enabled) { >> + BlockQueue *queue = bs->block_queue; >> + if (!QTAILQ_EMPTY(&queue->requests)) { >> + bs->io_unlimit = true; >> + bs->io_limits_enabled = false; >> + } else { >> + bdrv_io_limits_res_deinit(bs); >> + } >> + } >> + } > > bdrv_io_limits_res_init() and bdrv_io_limits_res_deinit() are > basically enable/disable functions. I would change this to: > > if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) { > bdrv_io_limits_enable(bs); > } else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) { > bdrv_io_limits_disable(bs); > } Good point. > > And then remove the bs->io_unlimit field by immediately stopping I/O throttling: > > void bdrv_io_limits_disable(BlockDriverState *bs) > { > bs->io_limits_enabled = false; > bs->req_from_queue = false; > > if (bs->block_queue) { > qemu_block_queue_flush(bs->block_queue); /* <--- dispatch > queued requests */ > qemu_del_block_queue(bs->block_queue); > } > > if (bs->block_timer) { > qemu_del_timer(bs->block_timer); > qemu_free_timer(bs->block_timer); > } > > bs->slice_start[0] = 0; > bs->slice_start[1] = 0; > > bs->slice_end[0] = 0; > bs->slice_end[1] = 0; > } > > The queue flushing code from bdrv_block_timer() could be moved into > qemu_block_queue_flush(). bdrv_block_timer() would call that function > instead of looping over the queue itself. OK. > >> + >> + return 0; >> +} >> + >> int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) >> { >> const char *id = qdict_get_str(qdict, "id"); >> diff --git a/blockdev.h b/blockdev.h >> index 0a5144c..d0d0d77 100644 >> --- a/blockdev.h >> +++ b/blockdev.h >> @@ -63,6 +63,8 @@ int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data); >> int do_change_block(Monitor *mon, const char *device, >> const char *filename, const char *fmt); >> int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); >> +int do_block_set_io_throttle(Monitor *mon, >> + const QDict *qdict, QObject **ret_data); >> int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); >> int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); >> >> diff --git a/hmp-commands.hx b/hmp-commands.hx >> index aceba74..3ca496d 100644 >> --- a/hmp-commands.hx >> +++ b/hmp-commands.hx >> @@ -1210,6 +1210,21 @@ ETEXI >> }, >> >> STEXI >> +@item block_set_io_throttle @var{device} @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} >> +@findex block_set_io_throttle >> +Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} >> +ETEXI >> + >> + { >> + .name = "block_set_io_throttle", >> + .args_type = "device:B,bps:i?,bps_rd:i?,bps_wr:i?,iops:i?,iops_rd:i?,iops_wr:i?", >> + .params = "device [bps] [bps_rd] [bps_wr] [iops] [iops_rd] [iops_wr]", >> + .help = "change I/O throttle limts for a block drive", >> + .user_print = monitor_user_noop, >> + .mhandler.cmd_new = do_block_set_io_throttle, >> + }, >> + >> +STEXI >> @item block_passwd @var{device} @var{password} >> @findex block_passwd >> Set the encrypted device @var{device} password to @var{password} >> diff --git a/qerror.c b/qerror.c >> index d7fcd93..db04df7 100644 >> --- a/qerror.c >> +++ b/qerror.c >> @@ -213,6 +213,14 @@ static const QErrorStringTable qerror_table[] = { >> .error_fmt = QERR_VNC_SERVER_FAILED, >> .desc = "Could not start VNC server on %(target)", >> }, >> + { >> + .error_fmt = QERR_IO_THROTTLE_PARAM_ERROR, >> + .desc = "Some params for io throttling can not coexist", >> + }, >> + { >> + .error_fmt = QERR_IO_THROTTLE_NO_PARAM, >> + .desc = "No params for io throttling are specified", >> + }, >> {} >> }; >> >> diff --git a/qerror.h b/qerror.h >> index 16c830d..892abbb 100644 >> --- a/qerror.h >> +++ b/qerror.h >> @@ -181,4 +181,10 @@ QError *qobject_to_qerror(const QObject *obj); >> #define QERR_FEATURE_DISABLED \ >> "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }" >> >> +#define QERR_IO_THROTTLE_PARAM_ERROR \ >> + "{ 'class': 'ParamsCoexistNotPermitted', 'data': {} }" > > This error is not specific to I/O throttling. Please add a generic error: > > #define QERR_INVALID_PARAMETER_COMBINATION \ > "{ 'class': 'InvalidParameterCombination', 'data': { 'param1': %s, > 'param2': %s } }" > >> +#define QERR_IO_THROTTLE_NO_PARAM \ >> + "{ 'class': 'NoParamsSpecified', 'data': {} }" > > QERR_MISSING_PARAMETER already does this. > >> + >> #endif /* QERROR_H */ >> diff --git a/qmp-commands.hx b/qmp-commands.hx >> index 92c5c3a..0aaeae1 100644 >> --- a/qmp-commands.hx >> +++ b/qmp-commands.hx >> @@ -828,6 +828,44 @@ Example: >> EQMP >> >> { >> + .name = "block_set_io_throttle", >> + .args_type = "device:B,bps:i?,bps_rd:i?,bps_wr:i?,iops:i?,iops_rd:i?,iops_wr:i?", >> + .params = "device [bps[ [bps_rd] [bps_wr] [iops] [iops_rd] [iops_wr]", > > [bps] Good catch. > >> + .help = "change I/O throttle limts for a block drive", >> + .user_print = monitor_user_noop, >> + .mhandler.cmd_new = do_block_set_io_throttle, >> + }, >> + >> +SQMP >> +block_set_io_throttle >> +------------ >> + >> +Change I/O throttle limts for a block drive. >> + >> +Arguments: >> + >> +- "device": device name (json-string) >> +- "bps": total throughput value per second(json-int, optional) > > Please document the units of bps ("bps" can mean bits per second or > bytes per second): > > "total throughput limit in bytes per second (json-int, optional)" OK. > >> +- "bps_rd": read throughput value per second(json-int, optional) >> +- "bps_wr": read throughput value per second(json-int, optional) > > Stefan > Stefan, thanks for your good comments and spent time. -- Regards, Zhi Yong Wu -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html