On Tue, Aug 09, 2011 at 12:17:51PM +0800, Zhi Yong Wu wrote: > Note: > 1.) When bps/iops limits are specified to a small value such as 511 bytes/s, this VM will hang up. We are considering how to handle this senario. > 2.) When "dd" command is issued in guest, if its option bs is set to a large value such as "bs=1024K", the result speed will slightly bigger than the limits. > > For these problems, if you have nice thought, pls let us know.:) > > Signed-off-by: Zhi Yong Wu <wuzhy@xxxxxxxxxxxxxxxxxx> > --- > block.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- > block.h | 6 +- > block_int.h | 30 +++++ > 3 files changed, 372 insertions(+), 11 deletions(-) > > diff --git a/block.c b/block.c > index 24a25d5..8fd6643 100644 > --- a/block.c > +++ b/block.c > @@ -29,6 +29,9 @@ > #include "module.h" > #include "qemu-objects.h" > > +#include "qemu-timer.h" > +#include "block/blk-queue.h" > + > #ifdef CONFIG_BSD > #include <sys/types.h> > #include <sys/stat.h> > @@ -58,6 +61,13 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, > static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, > const uint8_t *buf, int nb_sectors); > > +static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, > + bool is_write, double elapsed_time, uint64_t *wait); > +static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write, > + double elapsed_time, uint64_t *wait); > +static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors, > + bool is_write, uint64_t *wait); > + > static QTAILQ_HEAD(, BlockDriverState) bdrv_states = > QTAILQ_HEAD_INITIALIZER(bdrv_states); > > @@ -90,6 +100,68 @@ int is_windows_drive(const char *filename) > } > #endif > > +/* throttling disk I/O limits */ > +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); > + 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; > +} > + > +static void bdrv_block_timer(void *opaque) > +{ > + BlockDriverState *bs = opaque; > + BlockQueue *queue = bs->block_queue; > + > + qemu_block_queue_flush(queue); > +} > + > +void bdrv_io_limits_enable(BlockDriverState *bs) > +{ > + bs->req_from_queue = false; > + > + bs->block_queue = qemu_new_block_queue(); > + bs->block_timer = qemu_new_timer_ns(vm_clock, bdrv_block_timer, bs); > + > + bs->slice_start[BLOCK_IO_LIMIT_READ] = qemu_get_clock_ns(vm_clock); > + bs->slice_start[BLOCK_IO_LIMIT_WRITE] = qemu_get_clock_ns(vm_clock); a minor comment. better to keep the slice_start of the both the READ and WRITE side the same. bs->slice_start[BLOCK_IO_LIMIT_WRITE] = bs->slice_start[BLOCK_IO_LIMIT_READ]; saves a call to qemu_get_clock_ns(). > + > + bs->slice_end[BLOCK_IO_LIMIT_READ] = > + qemu_get_clock_ns(vm_clock) + BLOCK_IO_SLICE_TIME; bs->slice_end[BLOCK_IO_LIMIT_READ] = bs->slice_start[BLOCK_IO_LIMIT_READ] + BLOCK_IO_SLICE_TIME; saves one more call to qemu_get_clock_ns() > + bs->slice_end[BLOCK_IO_LIMIT_WRITE] = > + qemu_get_clock_ns(vm_clock) + BLOCK_IO_SLICE_TIME; bs->slice_end[BLOCK_IO_LIMIT_WRITE] = bs->slice_start[BLOCK_IO_LIMIT_WRITE] + BLOCK_IO_SLICE_TIME; yet another call saving. > +} > + > +bool bdrv_io_limits_enabled(BlockDriverState *bs) > +{ > + BlockIOLimit *io_limits = &bs->io_limits; > + if ((io_limits->bps[BLOCK_IO_LIMIT_READ] == 0) > + && (io_limits->bps[BLOCK_IO_LIMIT_WRITE] == 0) > + && (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] == 0) > + && (io_limits->iops[BLOCK_IO_LIMIT_READ] == 0) > + && (io_limits->iops[BLOCK_IO_LIMIT_WRITE] == 0) > + && (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] == 0)) { > + return false; > + } > + > + return true; > +} can be optimized to: return (io_limits->bps[BLOCK_IO_LIMIT_READ] || io_limits->bps[BLOCK_IO_LIMIT_WRITE] || io_limits->bps[BLOCK_IO_LIMIT_TOTAL] || io_limits->iops[BLOCK_IO_LIMIT_READ] || io_limits->iops[BLOCK_IO_LIMIT_WRITE] || io_limits->iops[BLOCK_IO_LIMIT_TOTAL]); > + > /* check if the path starts with "<protocol>:" */ > static int path_has_protocol(const char *path) > { > @@ -642,6 +714,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, > bs->change_cb(bs->change_opaque, CHANGE_MEDIA); > } > > + /* throttling disk I/O limits */ > + if (bs->io_limits_enabled) { > + bdrv_io_limits_enable(bs); > + } > + > return 0; > > unlink_and_fail: > @@ -680,6 +757,16 @@ void bdrv_close(BlockDriverState *bs) > if (bs->change_cb) > bs->change_cb(bs->change_opaque, CHANGE_MEDIA); > } > + > + /* throttling disk I/O limits */ > + if (bs->block_queue) { > + qemu_del_block_queue(bs->block_queue); > + } > + > + if (bs->block_timer) { > + qemu_del_timer(bs->block_timer); > + qemu_free_timer(bs->block_timer); > + } > } > > void bdrv_close_all(void) > @@ -1312,6 +1399,15 @@ void bdrv_get_geometry_hint(BlockDriverState *bs, > *psecs = bs->secs; > } > > +/* throttling disk io limits */ > +void bdrv_set_io_limits(BlockDriverState *bs, > + BlockIOLimit *io_limits) > +{ > + memset(&bs->io_limits, 0, sizeof(BlockIOLimit)); > + bs->io_limits = *io_limits; > + bs->io_limits_enabled = bdrv_io_limits_enabled(bs); > +} > + > /* Recognize floppy formats */ > typedef struct FDFormat { > FDriveType drive; > @@ -1707,6 +1803,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]"); > } > @@ -1739,10 +1845,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[BLOCK_IO_LIMIT_TOTAL], > + bs->io_limits.bps[BLOCK_IO_LIMIT_READ], > + bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE], > + bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL], > + bs->io_limits.iops[BLOCK_IO_LIMIT_READ], > + bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE]); > if (bs->backing_file[0] != '\0') { > QDict *qdict = qobject_to_qdict(obj); > qdict_put(qdict, "backing_file", > @@ -2111,6 +2229,165 @@ char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) > return buf; > } > > +static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, > + bool is_write, double elapsed_time, uint64_t *wait) { > + uint64_t bps_limit = 0; > + double bytes_limit, bytes_disp, bytes_res; > + double slice_time, wait_time; > + > + if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) { > + bps_limit = bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; > + } else if (bs->io_limits.bps[is_write]) { > + bps_limit = bs->io_limits.bps[is_write]; > + } else { > + if (wait) { > + *wait = 0; > + } > + > + return false; > + } > + > + slice_time = bs->slice_end[is_write] - bs->slice_start[is_write]; > + slice_time /= (BLOCK_IO_SLICE_TIME * 10.0); > + bytes_limit = bps_limit * slice_time; > + bytes_disp = bs->io_disps.bytes[is_write]; > + if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) { > + bytes_disp += bs->io_disps.bytes[!is_write]; > + } > + > + bytes_res = (unsigned) nb_sectors * BDRV_SECTOR_SIZE; > + > + if (bytes_disp + bytes_res <= bytes_limit) { > + if (wait) { > + *wait = 0; > + } > + > + return false; > + } > + > + /* Calc approx time to dispatch */ > + wait_time = (bytes_disp + bytes_res) / bps_limit - elapsed_time; > + > + if (wait) { > + *wait = wait_time * BLOCK_IO_SLICE_TIME * 10; > + } > + > + return true; > +} > + > +static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write, > + double elapsed_time, uint64_t *wait) { > + uint64_t iops_limit = 0; > + double ios_limit, ios_disp; > + double slice_time, wait_time; > + > + if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) { > + iops_limit = bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]; > + } else if (bs->io_limits.iops[is_write]) { > + iops_limit = bs->io_limits.iops[is_write]; > + } else { > + if (wait) { > + *wait = 0; > + } > + > + return false; > + } > + > + slice_time = bs->slice_end[is_write] - bs->slice_start[is_write]; > + slice_time /= (BLOCK_IO_SLICE_TIME * 10.0); > + ios_limit = iops_limit * slice_time; > + ios_disp = bs->io_disps.ios[is_write]; > + if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) { > + ios_disp += bs->io_disps.ios[!is_write]; > + } > + > + if (ios_disp + 1 <= ios_limit) { > + if (wait) { > + *wait = 0; > + } > + > + return false; > + } > + > + /* Calc approx time to dispatch */ > + wait_time = (ios_disp + 1) / iops_limit; > + if (wait_time > elapsed_time) { > + wait_time = wait_time - elapsed_time; > + } else { > + wait_time = 0; > + } > + > + if (wait) { > + *wait = wait_time * BLOCK_IO_SLICE_TIME * 10; > + } > + > + return true; > +} bdrv_exceed_bps_limits() and bdrv_exceed_iops_limits() has almost similar logic. Probably can be abstracted into a single function. RP -- 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