diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ddc9c7b1a1..1baea16c96 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2166,7 +2166,7 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, break; } } - } while ((index < htabslots) && !qemu_file_rate_limit(f)); + } while ((index < htabslots) && !migration_rate_exceeded(f)); if (index >= htabslots) { assert(index == htabslots); @@ -2237,7 +2237,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, assert(index == htabslots); index = 0; } - } while ((examined < htabslots) && (!qemu_file_rate_limit(f) || final)); + } while ((examined < htabslots) && (!migration_rate_exceeded(f) || final)); if (index >= htabslots) { assert(index == htabslots); diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index aed919ad7d..220e845d12 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -209,7 +209,7 @@ static int cmma_save(QEMUFile *f, void *opaque, int final) return -ENOMEM; } - while (final ? 1 : qemu_file_rate_limit(f) == 0) { + while (final ? 1 : migration_rate_exceeded(f) == 0) { reallen = sac->get_stattr(sas, &start_gfn, buflen, buf); if (reallen < 0) { g_free(buf); diff --git a/include/migration/qemu-file-types.h b/include/migration/qemu-file-types.h index 1436f9ce92..9ba163f333 100644 --- a/include/migration/qemu-file-types.h +++ b/include/migration/qemu-file-types.h @@ -165,6 +165,16 @@ size_t coroutine_mixed_fn qemu_get_counted_string(QEMUFile *f, char buf[256]); void qemu_put_counted_string(QEMUFile *f, const char *name); -int qemu_file_rate_limit(QEMUFile *f); +/** + * migration_rate_exceeded: Check if we have exceeded rate for this interval + * + * Checks if we have already transferred more data that we are allowed + * in the current interval. + * + * @f: QEMUFile used for main migration channel + * + * Returns if we should stop sending data for this interval. + */ +bool migration_rate_exceeded(QEMUFile *f); #endif diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 20f36e6bd8..032fc5f405 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -706,7 +706,7 @@ static void bulk_phase(QEMUFile *f, DBMSaveState *s, bool limit) QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) { while (!dbms->bulk_completed) { bulk_phase_send_chunk(f, s, dbms); - if (limit && qemu_file_rate_limit(f)) { + if (limit && migration_rate_exceeded(f)) { return; } } diff --git a/migration/block.c b/migration/block.c index 12617b4152..b9580a6c7e 100644 --- a/migration/block.c +++ b/migration/block.c @@ -23,6 +23,7 @@ #include "block/dirty-bitmap.h" #include "migration/misc.h" #include "migration.h" +#include "migration-stats.h" #include "migration/register.h" #include "qemu-file.h" #include "migration/vmstate.h" @@ -625,7 +626,7 @@ static int flush_blks(QEMUFile *f) blk_mig_lock(); while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { - if (qemu_file_rate_limit(f)) { + if (migration_rate_exceeded(f)) { break; } if (blk->ret < 0) { @@ -762,7 +763,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque) /* control the rate of transfer */ blk_mig_lock(); while (block_mig_state.read_done * BLK_MIG_BLOCK_SIZE < - qemu_file_get_rate_limit(f) && + migration_rate_get() && block_mig_state.submitted < MAX_PARALLEL_IO && (block_mig_state.submitted + block_mig_state.read_done) < MAX_IO_BUFFERS) { diff --git a/migration/meson.build b/migration/meson.build index dc8b1daef5..b3d0c537c8 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -1,5 +1,6 @@ # Files needed by unit tests migration_files = files( + 'migration-stats.c', 'page_cache.c', 'xbzrle.c', 'vmstate-types.c', @@ -18,7 +19,6 @@ softmmu_ss.add(files( 'fd.c', 'global_state.c', 'migration-hmp-cmds.c', - 'migration-stats.c', 'migration.c', 'multifd.c', 'multifd-zlib.c', diff --git a/migration/migration-stats.c b/migration/migration-stats.c index 2f2cea965c..0890220ba5 100644 --- a/migration/migration-stats.c +++ b/migration/migration-stats.c @@ -12,6 +12,50 @@ #include "qemu/osdep.h" #include "qemu/stats64.h" +#include "qemu-file.h" #include "migration-stats.h" MigrationAtomicStats mig_stats; + +bool migration_rate_exceeded(QEMUFile *f) +{ + if (qemu_file_get_error(f)) { + return true; + } + + uint64_t rate_limit_used = stat64_get(&mig_stats.rate_limit_used); + uint64_t rate_limit_max = stat64_get(&mig_stats.rate_limit_max); + + if (rate_limit_max == RATE_LIMIT_DISABLED) { + return false; + } + if (rate_limit_max > 0 && rate_limit_used > rate_limit_max) { + return true; + } + return false; +} + +uint64_t migration_rate_get(void) +{ + return stat64_get(&mig_stats.rate_limit_max); +} + +#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY) + +void migration_rate_set(uint64_t limit) +{ + /* + * 'limit' is per second. But we check it each BUFER_DELAY miliseconds. + */ + stat64_set(&mig_stats.rate_limit_max, limit / XFER_LIMIT_RATIO); +} + +void migration_rate_reset(void) +{ + stat64_set(&mig_stats.rate_limit_used, 0); +} + +void migration_rate_account(uint64_t len) +{ + stat64_add(&mig_stats.rate_limit_used, len); +} diff --git a/migration/migration-stats.h b/migration/migration-stats.h index e7f1269769..7b64dc7cc2 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -15,6 +15,12 @@ #include "qemu/stats64.h" +/* + * Amount of time to allocate to each "chunk" of bandwidth-throttled + * data. + */ +#define BUFFER_DELAY 100 + /* * If rate_limit_max is 0, there is special code to remove the rate * limit. @@ -75,6 +81,14 @@ typedef struct { * Number of bytes sent during precopy stage. */ Stat64 precopy_bytes; + /* + * Maximum amount of data we can send in a cycle. + */ + Stat64 rate_limit_max; + /* + * Amount of data we have sent in the current cycle. + */ + Stat64 rate_limit_used; /* * Total number of bytes transferred. */ @@ -87,4 +101,36 @@ typedef struct { extern MigrationAtomicStats mig_stats; +/** + * migration_rate_account: Increase the number of bytes transferred. + * + * Report on a number of bytes the have been transferred that need to + * be applied to the rate limiting calcuations. + * + * @len: amount of bytes transferred + */ +void migration_rate_account(uint64_t len); + +/** + * migration_rate_get: Get the maximum amount that can be transferred. + * + * Returns the maximum number of bytes that can be transferred in a cycle. + */ +uint64_t migration_rate_get(void); + +/** + * migration_rate_reset: Reset the rate limit counter. + * + * This is called when we know we start a new transfer cycle. + */ +void migration_rate_reset(void); + +/** + * migration_rate_set: Set the maximum amount that can be transferred. + * + * Sets the maximum amount of bytes that can be transferred in one cycle. + * + * @new_rate: new maximum amount + */ +void migration_rate_set(uint64_t new_rate); #endif diff --git a/migration/migration.c b/migration/migration.c index 3ceaf29798..fc8974e274 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2120,7 +2120,7 @@ static int postcopy_start(MigrationState *ms) * will notice we're in POSTCOPY_ACTIVE and not actually * wrap their state up here */ - qemu_file_set_rate_limit(ms->to_dst_file, bandwidth); + migration_rate_set(bandwidth); if (migrate_postcopy_ram()) { /* Ping just for debugging, helps line traces up */ qemu_savevm_send_ping(ms->to_dst_file, 2); @@ -2304,7 +2304,7 @@ static void migration_completion(MigrationState *s) * them if migration fails or is cancelled. */ s->block_inactive = !migrate_colo(); - qemu_file_set_rate_limit(s->to_dst_file, RATE_LIMIT_DISABLED); + migration_rate_set(RATE_LIMIT_DISABLED); ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false, s->block_inactive); } @@ -2699,7 +2699,7 @@ static void migration_update_counters(MigrationState *s, stat64_get(&mig_stats.dirty_bytes_last_sync) / bandwidth; } - qemu_file_reset_rate_limit(s->to_dst_file); + migration_rate_reset(); update_iteration_initial_status(s); @@ -2852,7 +2852,7 @@ bool migration_rate_limit(void) bool urgent = false; migration_update_counters(s, now); - if (qemu_file_rate_limit(s->to_dst_file)) { + if (migration_rate_exceeded(s->to_dst_file)) { if (qemu_file_get_error(s->to_dst_file)) { return false; @@ -2974,7 +2974,7 @@ static void *migration_thread(void *opaque) trace_migration_thread_setup_complete(); while (migration_is_active(s)) { - if (urgent || !qemu_file_rate_limit(s->to_dst_file)) { + if (urgent || !migration_rate_exceeded(s->to_dst_file)) { MigIterateState iter_state = migration_iteration_run(s); if (iter_state == MIG_ITERATE_SKIP) { continue; @@ -3048,7 +3048,7 @@ static void *bg_migration_thread(void *opaque) rcu_register_thread(); object_ref(OBJECT(s)); - qemu_file_set_rate_limit(s->to_dst_file, RATE_LIMIT_DISABLED); + migration_rate_set(RATE_LIMIT_DISABLED); setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); /* @@ -3220,7 +3220,7 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) notifier_list_notify(&migration_state_notifiers, s); } - qemu_file_set_rate_limit(s->to_dst_file, rate_limit); + migration_rate_set(rate_limit); qemu_file_set_blocking(s->to_dst_file, true); /* diff --git a/migration/multifd.c b/migration/multifd.c index 5c4298eadf..5052091ce2 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -431,7 +431,7 @@ static int multifd_send_pages(QEMUFile *f) multifd_send_state->pages = p->pages; p->pages = pages; transferred = ((uint64_t) pages->num) * p->page_size + p->packet_len; - qemu_file_acct_rate_limit(f, transferred); + migration_rate_account(transferred); qemu_mutex_unlock(&p->mutex); stat64_add(&mig_stats.transferred, transferred); stat64_add(&mig_stats.multifd_bytes, transferred); diff --git a/migration/options.c b/migration/options.c index c2a278ee2d..b62ab30cd5 100644 --- a/migration/options.c +++ b/migration/options.c @@ -23,6 +23,7 @@ #include "migration/colo.h" #include "migration/misc.h" #include "migration.h" +#include "migration-stats.h" #include "qemu-file.h" #include "ram.h" #include "options.h" @@ -1242,8 +1243,7 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_max_bandwidth) { s->parameters.max_bandwidth = params->max_bandwidth; if (s->to_dst_file && !migration_in_postcopy()) { - qemu_file_set_rate_limit(s->to_dst_file, - s->parameters.max_bandwidth); + migration_rate_set(s->parameters.max_bandwidth); } } @@ -1272,8 +1272,7 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_max_postcopy_bandwidth) { s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; if (s->to_dst_file && migration_in_postcopy()) { - qemu_file_set_rate_limit(s->to_dst_file, - s->parameters.max_postcopy_bandwidth); + migration_rate_set(s->parameters.max_postcopy_bandwidth); } } if (params->has_max_cpu_throttle) { diff --git a/migration/options.h b/migration/options.h index 5cca3326d6..45991af3c2 100644 --- a/migration/options.h +++ b/migration/options.h @@ -17,13 +17,6 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -/* constants */ - -/* Amount of time to allocate to each "chunk" of bandwidth-throttled - * data. */ -#define BUFFER_DELAY 100 -#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY) - /* migration properties */ extern Property migration_properties[]; diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 3d66c5c512..9c67b52fe0 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -41,17 +41,6 @@ struct QEMUFile { QIOChannel *ioc; bool is_writable; - /* - * Maximum amount of data in bytes to transfer during one - * rate limiting time window - */ - uint64_t rate_limit_max; - /* - * Total amount of data in bytes queued for transfer - * during this rate limiting time window - */ - uint64_t rate_limit_used; - /* The sum of bytes transferred on the wire */ uint64_t total_transferred; @@ -303,7 +292,7 @@ void qemu_fflush(QEMUFile *f) qemu_file_set_error_obj(f, -EIO, local_error); } else { uint64_t size = iov_size(f->iov, f->iovcnt); - qemu_file_acct_rate_limit(f, size); + migration_rate_account(size); f->total_transferred += size; } @@ -356,7 +345,7 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, int ret = f->hooks->save_page(f, block_offset, offset, size, bytes_sent); if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - qemu_file_acct_rate_limit(f, size); + migration_rate_account(size); } if (ret != RAM_SAVE_CONTROL_DELAYED && @@ -727,43 +716,6 @@ uint64_t qemu_file_transferred(QEMUFile *f) return f->total_transferred; } -int qemu_file_rate_limit(QEMUFile *f) -{ - if (qemu_file_get_error(f)) { - return 1; - } - if (f->rate_limit_max == RATE_LIMIT_DISABLED) { - return 0; - } - if (f->rate_limit_used > f->rate_limit_max) { - return 1; - } - return 0; -} - -uint64_t qemu_file_get_rate_limit(QEMUFile *f) -{ - return f->rate_limit_max; -} - -void qemu_file_set_rate_limit(QEMUFile *f, uint64_t limit) -{ - /* - * 'limit' is per second. But we check it each 100 miliseconds. - */ - f->rate_limit_max = limit / XFER_LIMIT_RATIO; -} - -void qemu_file_reset_rate_limit(QEMUFile *f) -{ - f->rate_limit_used = 0; -} - -void qemu_file_acct_rate_limit(QEMUFile *f, uint64_t len) -{ - f->rate_limit_used += len; -} - void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); diff --git a/migration/qemu-file.h b/migration/qemu-file.h index bcc39081f2..e649718492 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -130,17 +130,6 @@ void qemu_file_skip(QEMUFile *f, int size); * accounting information tracks the total migration traffic. */ void qemu_file_credit_transfer(QEMUFile *f, size_t size); -void qemu_file_reset_rate_limit(QEMUFile *f); -/* - * qemu_file_acct_rate_limit: - * - * Report on a number of bytes the have been transferred - * out of band from the main file object I/O methods, and - * need to be applied to the rate limiting calcuations - */ -void qemu_file_acct_rate_limit(QEMUFile *f, uint64_t len); -void qemu_file_set_rate_limit(QEMUFile *f, uint64_t new_rate); -uint64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_get_error_obj(QEMUFile *f, Error **errp); int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp); void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); diff --git a/migration/ram.c b/migration/ram.c index f69d8d42b0..15cb15bceb 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3116,7 +3116,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); i = 0; - while ((ret = qemu_file_rate_limit(f)) == 0 || + while ((ret = migration_rate_exceeded(f)) == 0 || postcopy_has_request(rs)) { int pages; diff --git a/migration/savevm.c b/migration/savevm.c index e33788343a..03795ce8dc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1338,7 +1338,7 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) !(se->ops->has_postcopy && se->ops->has_postcopy(se->opaque))) { continue; } - if (qemu_file_rate_limit(f)) { + if (migration_rate_exceeded(f)) { return 0; } trace_savevm_section_start(se->idstr, se->section_id);