Migration errors & cleanup (the integrated version)
v5 cover Migration errors & cleanup (the integrated version)
Hi
v5:
- addressed review comments:
* Amit
- move "return real error code" after has_error has got a negative value (i.e. from patch 3 to patch 11)
* Kevin
- return -errno, no errno.
* Orit
- return errno instead of EINVAL
- once there, introduce some comments (more needed)
- Make sure that *save_live() return negative on error, and adjust all callers.
- make migration_state enum anonymous (Anthony)
v4:
- rebase on top of new qemu and new migration-errors series
- integrate back erros & cleanup series
- s/MIG_STATE_NONE/MIG_STATE_SETUP/ (Orit suggestion)
- s/migrate_create_state/migrate_new/ (Anthony suggestion)
- Add migrate_get_current() accessor.
- make has_error contain the errno instead of a bool
- rename qemu_file_has_error() -> qemu_file_get_error()
- rename has_error field into last_error
- migration_state_notifiers now pass MigrationState pointer
v3:
this patch applies on top of my previous "migration error" patches. All error handling has been moved to that series, except for "propagate error correctly", without this refactoring, it is quite complicated to apply it.
Please, review.
Later, Juan.
v3:
- more checkpatch.pl happines
- split error handling in a previous series
- make Anthony happy. current_migration is still a pointer, but points to a static variable. We can change current_migration when we integrate kemari.
v2:
- make Jan^Wcheckpatch.pl happy
- Yoshiaki Tamura suggestions:
- include its two patches to clean things
- MAX_THROTTLE define
- migration_state enum
- I removed spurious differences between migration-{tcp,unix}
- better error propagation, after this patch:
migrate -d "tcp:name_don_exist:port"
migrate -d "tcp:name:port_dont_exist"
migrate -d "exec: prog_dont_exist"
migrate -d "exec: gzip > /path/dont/exist"
fail as expected. Last two used to enter an infinite loop.
The fixes part should be backported to 0.14, waiting for the review to do that.
Later, Juan.
v1:
This series:
- Fold MigrationState into FdMigrationState (and then rename)
- Factorize migration statec creation in a single place
- Make use of MIG_STATE_*, setup through helpers and make them local
- remove relase & cancel callbacks (where used only one in same file than defined)
- get_status() is no more, just access directly to .state
- current_migration use cleanup, and make variable static
- max_throotle is gone, now inside current_migration
- change get_migration_status() to migration_has_finished() and actualize single user.
Please review.
Later, Juan.
Juan Quintela (36):
ds1225y: Use stdio instead of QEMUFile
migration: simplify state assignmente
migration: Check that migration is active before cancel it
migration: If there is one error, it makes no sense to continue
buffered_file: Use right "opaque"
buffered_file: reuse QEMUFile has_error field
migration: don't "write" when migration is not active
migration: set error if select return one error
migration: change has_error to contain errno values
migration: return real error code
migration: rename qemu_file_has_error to qemu_file_get_error
savevm: Rename has_error to last_error field
migration: use qemu_file_get_error() return value when possible
migration: make *save_live return errors
migration: Make *start_outgoing_migration return FdMigrationState
migration: Use FdMigrationState instead of MigrationState when
possible
migration: Fold MigrationState into FdMigrationState
migration: Rename FdMigrationState MigrationState
migration: Refactor MigrationState creation
migration: Make all posible migration functions static
migration: move migrate_new to do_migrate
migration: Introduce MIG_STATE_SETUP
migration: Refactor and simplify error checking in
migrate_fd_put_ready
migration: Introduce migrate_fd_completed() for consistency
migration: Our release callback was just free
migration: Remove get_status() accessor
migration: Remove migration cancel() callback
migration: Move exported functions to the end of the file
migration: create accessor for current_migration
migration: Use bandwidth_limit directly
migration: Pass MigrationState in migration notifiers
migration: Export a function that tells if the migration has finished
correctly
migration: Make state definitions local
migration: Don't use callback on file defining it
migration: propagate error correctly
migration: make migration-{tcp,unix} consistent
Yoshiaki Tamura (1):
migration: add error handling to migrate_fd_put_notify().
arch_init.c | 17 ++- block-migration.c | 35 +++-- buffered_file.c | 44 ++++-- hw/ds1225y.c | 28 ++-- hw/hw.h | 4 +- migration-exec.c | 39 +---- migration-fd.c | 42 +---- migration-tcp.c | 76 ++++------ migration-unix.c | 113 ++++++--------- migration.c | 439 +++++++++++++++++++++++++++-------------------------- migration.h | 85 ++--------- savevm.c | 78 ++++++---- ui/spice-core.c | 4 +- 13 files changed, 456 insertions(+), 548 deletions(-)
Signature · 1 line
1.7.6.4
PATCH v5 5/37 migration: If there is one error, it makes no sense to continue
Once there, add a comment about what each error mean.
--- buffered_file.c | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-)
buffered_file.c +9 -3
@@ -189,13 +189,19 @@ static int buffered_close(void *opaque)return ret; } +/* +* The meaning of the return values is: +* 0: We can continue sending +* 1: Time to stop +* -1: There has been an error +*/ static int buffered_rate_limit (void *opaque) {QEMUFileBuffered *s = opaque; -if (s->has_error) -return 0 ; - +if (s->has_error) { +return -1 ; + }if (s->freeze_output)return 1 ;
Signature · 1 line
1.7.6.4
PATCH v5 10/37 migration: change has_error to contain errno values
In reply to Juan QuintelaWe normally already have an errno value. When not, abuse EIO.
--- arch_init.c | 2 +- block-migration.c | 11 ++++++----- buffered_file.c | 4 ++-- hw/hw.h | 2 +- migration.c | 2 +- savevm.c | 8 ++++---- 6 files changed, 15 insertions(+), 14 deletions(-)
arch_init.c +1 -1
@@ -263,7 +263,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) }if (cpu_physical_sync_dirty_bitmap (0 ,TARGET_PHYS_ADDR_MAX ) !=0 ) { -qemu_file_set_error (f); +qemu_file_set_error (f, -EINVAL );return 0 ; }
block-migration.c +6 -5
@@ -263,7 +263,7 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, error:monitor_printf (mon,"Error reading sector %" PRId64"\n" , cur_sector); -qemu_file_set_error (f); +qemu_file_set_error (f, -EIO );g_free (blk->buf);g_free (blk);return 0 ;
@@ -383,6 +383,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,int64_t total_sectors = bmds->total_sectors;int64_t sector;int nr_sectors; +int ret = -EIO ;for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {if (bmds_aio_inflight (bmds, sector)) {
@@ -418,8 +419,8 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, block_mig_state.submitted++;bmds_set_aio_inflight (bmds, sector, nr_sectors,1 ); }else { -if (bdrv_read (bmds->bs, sector, blk->buf, - nr_sectors) <0 ) { + ret =bdrv_read (bmds->bs, sector, blk->buf, nr_sectors); +if (ret <0 ) { goto error; }blk_send (f, blk);
@@ -439,7 +440,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, error:monitor_printf (mon,"Error reading sector %" PRId64"\n" , sector); -qemu_file_set_error (f); +qemu_file_set_error (f, ret);g_free (blk->buf);g_free (blk);return 0 ;
@@ -473,7 +474,7 @@ static void flush_blks(QEMUFile* f)break ; }if (blk->ret <0 ) { -qemu_file_set_error (f); +qemu_file_set_error (f, blk->ret);break ; }blk_send (f, blk);
buffered_file.c +2 -2
@@ -92,7 +92,7 @@ static void buffered_flush(QEMUFileBuffered *s)if (ret <=0 ) {DPRINTF ("error flushing data, %zd\n" , ret); -qemu_file_set_error (s->file); +qemu_file_set_error (s->file, ret);break ; }else {DPRINTF ("flushed %zd byte(s)\n" , ret);
@@ -138,7 +138,7 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, inif (ret <=0 ) {DPRINTF ("error putting\n" ); -qemu_file_set_error (s->file); +qemu_file_set_error (s->file, ret); offset = -EINVAL ;break ; }
hw/hw.h +1 -1
@@ -86,7 +86,7 @@ int qemu_file_rate_limit(QEMUFile *f);int64_t qemu_file_set_rate_limit (QEMUFile *f,int64_t new_rate);int64_t qemu_file_get_rate_limit (QEMUFile *f);int qemu_file_has_error (QEMUFile *f); -void qemu_file_set_error (QEMUFile *f); +void qemu_file_set_error (QEMUFile *f,int error);/* Try to send any outstanding data. This function is useful when output is * halted due to rate limiting or EAGAIN errors occur as it can be used to
migration.c +1 -1
@@ -455,7 +455,7 @@ void migrate_fd_wait_for_unfreeze(void *opaque) }while (ret ==-1 && (s->get_error (s)) ==EINTR );if (ret ==-1 ) { -qemu_file_set_error (s->file); +qemu_file_set_error (s->file, -s->get_error (s)); } }
savevm.c +4 -4
@@ -430,9 +430,9 @@ int qemu_file_has_error(QEMUFile *f)return f->has_error; } -void qemu_file_set_error (QEMUFile *f) +void qemu_file_set_error (QEMUFile *f,int ret) { - f->has_error =1 ; + f->has_error = ret; }void qemu_fflush (QEMUFile *f)
@@ -447,7 +447,7 @@ void qemu_fflush(QEMUFile *f)if (len >0 ) f->buf_offset += f->buf_index;else - f->has_error =1 ; + f->has_error = -EINVAL ; f->buf_index =0 ; } }
@@ -468,7 +468,7 @@ static void qemu_fill_buffer(QEMUFile *f) f->buf_size = len; f->buf_offset += len; }else if (len != -EAGAIN ) - f->has_error =1 ; + f->has_error = len; }int qemu_fclose (QEMUFile *f)
Signature · 1 line
1.7.6.4
PATCH v5 14/37 migration: use qemu_file_get_error() return value when possible
In reply to Juan Quintela--- arch_init.c | 6 ++++-- block-migration.c | 7 ++++--- buffered_file.c | 23 ++++++++++++++--------- savevm.c | 4 ++-- 4 files changed, 24 insertions(+), 16 deletions(-)
arch_init.c +4 -2
@@ -371,6 +371,7 @@ int ram_load(QEMUFile *f, void *opaque, int version_id) {ram_addr_t addr;int flags; +int error;if (version_id <3 || version_id >4 ) {return -EINVAL ;
@@ -451,8 +452,9 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)qemu_get_buffer (f, host,TARGET_PAGE_SIZE ); } -if (qemu_file_get_error (f)) { -return -EIO ; + error =qemu_file_get_error (f); +if (error) { +return error; } }while (!(flags &RAM_SAVE_FLAG_EOS ));
block-migration.c +4 -3
@@ -647,6 +647,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)uint8_t *buf;int64_t total_sectors =0 ;int nr_sectors; +int ret; do { addr = qemu_get_be64(f);
@@ -655,7 +656,6 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) addr >>=BDRV_SECTOR_BITS ;if (flags &BLK_MIG_FLAG_DEVICE_BLOCK ) { -int ret;/* get device name */ len =qemu_get_byte (f);qemu_get_buffer (f, (uint8_t *)device_name, len);
@@ -705,8 +705,9 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)fprintf (stderr,"Unknown flags\n" );return -EINVAL ; } -if (qemu_file_get_error (f)) { -return -EIO ; + ret =qemu_file_get_error (f); +if (ret !=0 ) { +return ret; } }while (!(flags &BLK_MIG_FLAG_EOS ));
buffered_file.c +14 -9
@@ -71,9 +71,11 @@ static void buffered_append(QEMUFileBuffered *s,static void buffered_flush (QEMUFileBuffered *s) {size_t offset =0 ; +int error; -if (qemu_file_get_error (s->file)) { -DPRINTF ("flush when error, bailing\n" ); + error =qemu_file_get_error (s->file); +if (error !=0 ) { +DPRINTF ("flush when error, bailing: %s\n" ,strerror (-error));return ; }
@@ -108,14 +110,15 @@ static void buffered_flush(QEMUFileBuffered *s)static int buffered_put_buffer (void *opaque,const uint8_t *buf,int64_t pos,int size) {QEMUFileBuffered *s = opaque; -int offset =0 ; +int offset =0 , error;ssize_t ret;DPRINTF ("putting %d bytes at %" PRId64"\n" , size, pos); -if (qemu_file_get_error (s->file)) { -DPRINTF ("flush when error, bailing\n" ); -return -EINVAL ; + error =qemu_file_get_error (s->file); +if (error) { +DPRINTF ("flush when error, bailing: %s\n" ,strerror (-error)); +return error; }DPRINTF ("unfreezing output\n" );
@@ -192,14 +195,16 @@ static int buffered_close(void *opaque)* The meaning of the return values is: * 0: We can continue sending * 1: Time to stop -* -1: There has been an error +* negative: There has been an error */ static int buffered_rate_limit (void *opaque) {QEMUFileBuffered *s = opaque; +int ret; -if (qemu_file_get_error (s->file)) { -return -1 ; + ret =qemu_file_get_error (s->file); +if (ret) { +return ret; }if (s->freeze_output)return 1 ;
savevm.c +2 -2
@@ -1841,8 +1841,8 @@ out:g_free (le); } -if (qemu_file_get_error (f)) { - ret = -EIO ; +if (ret ==0 ) { + ret =qemu_file_get_error (f); }return ret;
Signature · 1 line
1.7.6.4
PATCH v5 15/37 migration: make *save_live return errors
In reply to Juan QuintelaMake *save_live() return negative values when there is one error, and updates all callers to check for the error.
--- arch_init.c | 9 +++++++-- block-migration.c | 17 +++++++++++------ savevm.c | 14 +++++++++++--- 3 files changed, 29 insertions(+), 11 deletions(-)
arch_init.c +7 -2
@@ -256,6 +256,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)uint64_t bytes_transferred_last;double bwidth =0 ;uint64_t expected_time =0 ; +int ret;if (stage <0 ) {cpu_physical_memory_set_dirty_tracking (0 );
@@ -264,7 +265,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)if (cpu_physical_sync_dirty_bitmap (0 ,TARGET_PHYS_ADDR_MAX ) !=0 ) {qemu_file_set_error (f, -EINVAL ); -return 0 ; +return -EINVAL ; }if (stage ==1 ) {
@@ -300,7 +301,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) bytes_transferred_last = bytes_transferred; bwidth =qemu_get_clock_ns (rt_clock); -while (!qemu_file_rate_limit (f)) { +while ((ret =qemu_file_rate_limit (f)) ==0 ) {int bytes_sent; bytes_sent =ram_save_block (f);
@@ -310,6 +311,10 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) } } +if (ret <0 ) { +return ret; + } + bwidth =qemu_get_clock_ns (rt_clock) - bwidth; bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
block-migration.c +11 -6
@@ -557,6 +557,8 @@ static void blk_mig_cleanup(Monitor *mon)static int block_save_live (Monitor *mon,QEMUFile *f,int stage,void *opaque) { +int ret; +DPRINTF ("Enter save live stage %d submitted %d transferred %d\n" , stage, block_mig_state.submitted, block_mig_state.transferred);
@@ -580,9 +582,10 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)flush_blks (f); -if (qemu_file_get_error (f)) { + ret =qemu_file_get_error (f); +if (ret) {blk_mig_cleanup (mon); -return 0 ; +return ret; }blk_mig_reset_dirty_cursor ();
@@ -608,9 +611,10 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)flush_blks (f); -if (qemu_file_get_error (f)) { + ret =qemu_file_get_error (f); +if (ret) {blk_mig_cleanup (mon); -return 0 ; +return ret; } }
@@ -625,8 +629,9 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)/* report completion */ qemu_put_be64 (f, (100 <<BDRV_SECTOR_BITS ) |BLK_MIG_FLAG_PROGRESS ); -if (qemu_file_get_error (f)) { -return 0 ; + ret =qemu_file_get_error (f); +if (ret) { +return ret; }monitor_printf (mon,"Block migration completed\n" );
savevm.c +11 -3
@@ -1496,7 +1496,11 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,qemu_put_be32 (f, se->instance_id);qemu_put_be32 (f, se->version_id); - se->save_live_state (mon, f,QEMU_VM_SECTION_START , se->opaque); + ret = se->save_live_state (mon, f,QEMU_VM_SECTION_START , se->opaque); +if (ret <0 ) { +qemu_savevm_state_cancel (mon, f); +return ret; + } } ret =qemu_file_get_error (f);if (ret !=0 ) {
@@ -1527,7 +1531,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)qemu_put_be32 (f, se->section_id); ret = se->save_live_state (mon, f,QEMU_VM_SECTION_PART , se->opaque); -if (!ret) { +if (ret <=0 ) {/* Do not proceed to the next vmstate before this one reported completion of the current stage. This serializes the migration and reduces the probability that a faster changing state is
@@ -1548,6 +1552,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)int qemu_savevm_state_complete (Monitor *mon,QEMUFile *f) {SaveStateEntry *se; +int ret;cpu_synchronize_all_states ();
@@ -1559,7 +1564,10 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)qemu_put_byte (f,QEMU_VM_SECTION_END );qemu_put_be32 (f, se->section_id); - se->save_live_state (mon, f,QEMU_VM_SECTION_END , se->opaque); + ret = se->save_live_state (mon, f,QEMU_VM_SECTION_END , se->opaque); +if (ret <0 ) { +return ret; + } } QTAILQ_FOREACH(se, &savevm_handlers, entry) {
Signature · 1 line
1.7.6.4
PATCH v5 23/37 migration: Introduce MIG_STATE_SETUP
In reply to Juan QuintelaUse MIG_STATE_ACTIVE only when migration has really started. Use this new state to setup migration parameters. Change defines for an anonymous struct.
--- migration.c | 6 +++++- migration.h | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-)
migration.c +5 -1
@@ -239,6 +239,9 @@ void do_info_migrate(Monitor *mon, QObject **ret_data)MigrationState *s = current_migration;switch (s->get_status (current_migration)) { +case MIG_STATE_SETUP : +/* no migration has happened ever */ +break ;case MIG_STATE_ACTIVE : qdict =qdict_new ();qdict_put (qdict,"status" ,qstring_from_str ("active" ));
@@ -478,6 +481,7 @@ void migrate_fd_connect(MigrationState *s) {int ret; + s->state =MIG_STATE_ACTIVE ; s->file = qemu_fopen_ops_buffered(s, s->bandwidth_limit, migrate_fd_put_buffer,
@@ -507,7 +511,7 @@ static MigrationState *migrate_new(Monitor *mon, int64_t bandwidth_limit, s->shared = inc; s->mon =NULL ; s->bandwidth_limit = bandwidth_limit; - s->state =MIG_STATE_ACTIVE ; + s->state =MIG_STATE_SETUP ;if (!detach) {migrate_fd_monitor_suspend (s, mon);
migration.h +7 -4
@@ -18,10 +18,13 @@#include "qemu-common.h" #include "notify.h" -#define MIG_STATE_ERROR -1 -#define MIG_STATE_COMPLETED 0 -#define MIG_STATE_CANCELLED 1 -#define MIG_STATE_ACTIVE 2 +enum { +MIG_STATE_ERROR , +MIG_STATE_SETUP , +MIG_STATE_CANCELLED , +MIG_STATE_ACTIVE , +MIG_STATE_COMPLETED , +};typedef struct MigrationState MigrationState ;
Signature · 1 line
1.7.6.4