QEMU Live Migration
RFC 1/1 QEMU Live Migration
Howdy,
I wanted to post a draft of my QEMU live migration patch. I think there's a lingering memory allocation problem. I also need to implement ARP forwarding and a TCP transport stream.
However, I thought I'd post an initial version in case anyone wants to play around with it.
Once you apply the patch, make sure you've got it on both machines. For my setup, I've got a common NFS share mounted in /mnt. Then I run:
qemu -hda /mnt/win2k.img -monitor telnet:0.0.0.0:1025,server,nowait -vnc :2
Then, I connect to the monitor and execute:
(qemu) migrate ssh://woolly
When the migration completes, the monitor command will finish and then I can connect to the machine on woolly:2.
The ssh command will be derived from argc/argv so it's important to ensure that the -hda path is valid on both machines.
All three patches are required and should be applied in order of: qemu-apic-save-restore.diff qemu-file.diff qemu-migration.diff
Regards,
Anthony Liguori
Makefile.target +1 -1
@@ -300,7 +300,7 @@ endif # must use static linking to avoid leaving stuff in virtual address space VL_OBJS=vl.o osdep.o readline.o monitor.o pci.o console.o loader.o isa_mmio.o -VL_OBJS+=cutils.o +VL_OBJS+=cutils.o migration.o VL_OBJS+=block.o block-raw.o VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o block-qcow2.o ifdef CONFIG_WIN32
cpu-all.h +5 -0
@@ -884,6 +884,7 @@ int cpu_memory_rw_debug(CPUState *env, t#define VGA_DIRTY_FLAG 0x01#define CODE_DIRTY_FLAG 0x02 +#define MIGRATION_DIRTY_FLAG 0x04/* read dirty bit (return 0 or 1) */ static inline int cpu_physical_memory_is_dirty (ram_addr_t addr)
@@ -905,6 +906,10 @@ void cpu_physical_memory_reset_dirty(ramvoid cpu_physical_memory_reset_dirty (ram_addr_t start,ram_addr_t end,int dirty_flags);void cpu_tlb_update_dirty (CPUState *env); + +void cpu_physical_memory_set_dirty_tracking (int enable); + +int cpu_physical_memory_get_dirty_tracking (void );void dump_exec_info (FILE *f,int (*cpu_fprintf)(FILE *f,const char *fmt, ...));
cutils.c +40 -0
@@ -81,3 +81,43 @@ int stristart(const char *str, const cha *ptr = p;return 1 ; } + +int hex2bin (char ch) +{ +if (ch >='0' && ch <='9' ) +return ch -'0' ; +else if (ch >='A' && ch <='Z' ) +return 10 + ch -'A' ; +else if (ch >='a' && ch <='z' ) +return 10 + ch -'a' ; + +return -1 ; +} + +char *urldecode (const char *ptr) +{ +char *ret; +int i; + + ret =qemu_mallocz (strlen (ptr) +1 ); +if (ret ==NULL ) +return NULL ; + +for (i =0 ; *ptr; ptr++, i++) { +switch (*ptr) { +case '%' : +if (ptr[1 ] ==0 || ptr[2 ] ==0 ) +break ; + ret[i] =hex2bin (ptr[1 ]) <<4 |hex2bin (ptr[2 ]); + ptr +=2 ; +break ; +default : + ret[i] = *ptr; +break ; + } + } + ret[i] =0 ; + +return ret; +} +
exec.c +33 -2
@@ -82,6 +82,7 @@ int phys_ram_fd;int phys_ram_fd;uint8_t *phys_ram_base;uint8_t *phys_ram_dirty; +static int in_migration;CPUState *first_cpu;/* current CPU in the current thread. It is only valid inside
@@ -1406,6 +1407,16 @@ void cpu_physical_memory_reset_dirty(ram } }#endif +} + +void cpu_physical_memory_set_dirty_tracking (int enable) +{ + in_migration = enable; +} + +int cpu_physical_memory_get_dirty_tracking (void ) +{ +return in_migration; }static inline void tlb_update_dirty (CPUTLBEntry *tlb_entry)
@@ -2231,6 +2242,14 @@ uint32_t lduw_phys(target_phys_addr_t adreturn tswap16 (val); } +#ifdef __GNUC__ +#define likely (x) __builtin_expect(!!(x), 1) +#define unlikely (x) __builtin_expect(!!(x), 0) +#else +#define likely (x) x +#define unlikely (x) x +#endif +/* warning: addr must be aligned. The ram page is not masked as dirty and the code inside is not invalidated. It is useful if the dirty bits are used to track modified PTEs */
@@ -2252,9 +2271,21 @@ void stl_phys_notdirty(target_phys_addr_ io_index = (pd >>IO_MEM_SHIFT ) & (IO_MEM_NB_ENTRIES -1 ); io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr, val); }else { - ptr = phys_ram_base + (pd &TARGET_PAGE_MASK ) + - (addr & ~TARGET_PAGE_MASK ); +unsigned long addr1; + addr1 = (pd &TARGET_PAGE_MASK ) + (addr & ~TARGET_PAGE_MASK ); + + ptr = phys_ram_base + addr1;stl_p (ptr, val); + +if (unlikely (in_migration)) { +if (!cpu_physical_memory_is_dirty (addr1)) { +/* invalidate code */ +tb_invalidate_phys_page_range (addr1, addr1 +4 ,0 ); +/* set dirty bit */ + phys_ram_dirty[addr1 >>TARGET_PAGE_BITS ] |= + (0xff & ~CODE_DIRTY_FLAG ); + } + } } }
monitor.c +17 -1
@@ -1253,6 +1253,8 @@ static term_cmd_t term_cmds[] = { "capture index", "stop capture" }, {"memsave" ,"lis" , do_memory_save, "addr size file", "save to disk virtual memory dump starting at 'addr' of size 'size'", }, + { "migrate", "-ds", do_migrate, + "[-d] command", "migrate the VM using command (use -d to not wait for command to complete)" }, { NULL, NULL, }, };
@@ -2428,12 +2430,26 @@ static void term_read(void *opaque, consreadline_handle_byte (buf[i]); } +static int monitor_suspended; + +void monitor_suspend (void ) +{ + monitor_suspended =1 ; +} + +void monitor_resume (void ) +{ + monitor_suspended =0 ; +monitor_start_input (); +} +static void monitor_start_input (void );static void monitor_handle_command1 (void *opaque,const char *cmdline) {monitor_handle_command (cmdline); -monitor_start_input (); +if (!monitor_suspended) +monitor_start_input (); }static void monitor_start_input (void )
vl.c +165 -15
275 lines not on this page · raw message
vl.h +14 -1
@@ -107,8 +107,12 @@ char *pstrcat(char *buf, int buf_size, cchar *pstrcat (char *buf,int buf_size,const char *s);int strstart (const char *str,const char *val,const char **ptr);int stristart (const char *str,const char *val,const char **ptr); +int hex2bin (char ch); +char *urldecode (const char *ptr);/* vl.c */ +void qemu_get_launch_info (int *argc,char ***argv,int *opt_daemonize,int *opt_incoming); +uint64_t muldiv64 (uint64_t a,uint32_t b,uint32_t c);void hw_error (const char *fmt, ...);
@@ -436,8 +440,8 @@ typedef void (QEMUFileCloseFunc)(void *oQEMUFile *qemu_fopen (void *opaque,QEMUFilePutBufferFunc *put_buffer,QEMUFileGetBufferFunc *get_buffer,QEMUFileCloseFunc *close); -QEMUFile *qemu_fopen_file (const char *filename,const char *mode); +QEMUFile *qemu_fopen_fd (int fd);void qemu_fflush (QEMUFile *f);void qemu_fclose (QEMUFile *f);void qemu_put_buffer (QEMUFile *f,const uint8_t *buf,int size);
@@ -525,6 +529,9 @@ void do_loadvm(const char *name);void do_loadvm (const char *name);void do_delvm (const char *name);void do_info_snapshots (void ); + +int qemu_live_savevm_state (QEMUFile *f); +int qemu_live_loadvm_state (QEMUFile *f);/* bottom halves */ typedef void QEMUBHFunc (void *opaque);
@@ -1362,6 +1369,10 @@ pflash_t *pflash_register (target_ulong#endif /* defined(QEMU_TOOL) */ +/* migration.c */ +void do_migrate (int detach,const char *command); +int migrate_incoming (int fd); +/* monitor.c */ void monitor_init (CharDriverState *hd,int show_banner);void term_puts (const char *str);
@@ -1372,6 +1383,8 @@ void term_print_help(void);void term_print_help (void );void monitor_readline (const char *prompt,int is_password,char *buf,int buf_size); +void monitor_suspend (void ); +void monitor_resume (void );/* readline.c */ typedef void ReadLineFunc (void *opaque,const char *str);
migration.c new file +392 -0
392 lines not on this page · raw message
hw/apic.c +1 -1
@@ -831,7 +831,7 @@ int apic_init(CPUState *env) } s->timer =qemu_new_timer (vm_clock, apic_timer, s); -register_savevm ("apic" ,0 ,1 , apic_save, apic_load, s); +register_savevm ("apic" ,0 ,2 , apic_save, apic_load, s);qemu_register_reset (apic_reset, s); local_apics[s->id] = s;
audio/wavaudio.c +1 -1
@@ -151,7 +151,7 @@ static int wav_init_out (HWVoiceOut *hw,le_store (hdr +28 , hw->info.freq << (bits16 + stereo),4 );le_store (hdr +32 ,1 << (bits16 + stereo),2 ); - wav->f =qemu_fopen (conf.wav_path,"wb" ); + wav->f =qemu_fopen_file (conf.wav_path,"wb" );if (!wav->f) {dolog ("Failed to open wave file `%s'\nReason: %s\n" , conf.wav_path,strerror (errno));
audio/wavcapture.c +1 -1
@@ -132,7 +132,7 @@ int wav_start_capture (CaptureState *s,le_store (hdr +28 , freq << shift,4 );le_store (hdr +32 ,1 << shift,2 ); - wav->f =qemu_fopen (path,"wb" ); + wav->f =qemu_fopen_file (path,"wb" );if (!wav->f) {term_printf ("Failed to open wave file `%s'\nReason: %s\n" , path,strerror (errno));
vl.c +145 -63
251 lines not on this page · raw message
vl.h +8 -1
@@ -430,7 +430,14 @@ void cpu_disable_ticks(void);typedef struct QEMUFile QEMUFile ; -QEMUFile *qemu_fopen (const char *filename,const char *mode); +typedef void (QEMUFilePutBufferFunc )(void *opaque,const uint8_t *buf,int64_t pos,int size); +typedef int (QEMUFileGetBufferFunc )(void *opaque,uint8_t *buf,int64_t pos,int size); +typedef void (QEMUFileCloseFunc )(void *opaque); + +QEMUFile *qemu_fopen (void *opaque,QEMUFilePutBufferFunc *put_buffer, +QEMUFileGetBufferFunc *get_buffer,QEMUFileCloseFunc *close); + +QEMUFile *qemu_fopen_file (const char *filename,const char *mode);void qemu_fflush (QEMUFile *f);void qemu_fclose (QEMUFile *f);void qemu_put_buffer (QEMUFile *f,const uint8_t *buf,int size);
A message that is not in the archive (sent off-list or not yet received): 64F9B87B6B770947A9F8391472E0321603496B19@ehost011-8.exch011.intermedia.net
[Qemu-devel] QEMU Live Migration: arp-like packet patch
Hello,
We, kvm developers at Qumranet, have been developing a Live Migration solution
for qemu (and kvm) too (http://kvm.sourceforge.net/migration.html).
We are working with qemu-0.8.2 and are planning to upgrade to the current
qemu CVS.
Anthony Liguori sent patches implementing Live Migration
(http://lists.gnu.org/archive/html/qemu-devel/2007-01/msg00245.html).
Some differences are:
- Anthony's solution uses ssh our solution uses TCP sockets.
- Anthony's interface is a single qemu-monitor command accepting a uri, our interface is a single qemu command accepting a few subcommands.
- Anthony's solution supports migration start without any preparations, our solution requires starting a guest (qemu) on the destination host, and establish connection before starting the migration.
Naturally, Anthony's solution can be easily enhanced to support most of our interface.
Since there is no need for two different migration solutions, we'd like to
cooperate with Anthony (and with you) and contribute some patches which are additions to his solution.
Those patches would be based on Anthony's solution (and have to be applied after
his patches are applied).
The first patch (attached) is an arp-like packet sent (broadcast) when a
migration successfully completes.
The purpose of this packet is to inform network switches that the guest moved. Although the most known unsolicited packet is the famous "gratuitous arp",
we are not sending an arp packet since we do not know the guest's ip address, and we do not want to receive replies.
Instead we use an ethernet "experimental" packet.
The packet is sent only through tap interfaces.
Our implementation currently supports only linux hosts (adding support for
other operating systems is probably/hopefully as easy as changing the include preprocessor directive).
Regards,
Uri Lublin.
vl.c +48 -0
@@ -51,6 +51,7 @@#ifndef __sun__#include <linux/if.h> #include <linux/if_tun.h> +#include <linux/if_ether.h> #include <pty.h> #include <malloc.h> #include <linux/rtc.h>
@@ -3940,6 +3941,50 @@ } } +#if defined(__linux__) +#define SELF_ANNOUNCE_ROUNDS 5 +#define ETH_P_EXPERIMENTAL 0x01F1/* just a number in experimental range */ +#define EXPERIMENTAL_MAGIC 0xf1f23f4f +int announce_self_create (unsigned char *buf, +unsigned char mac_addr[ETH_ALEN ]) +{ +struct ethhdr *eh = (struct ethhdr *)buf; +uint32_t *magic = (uint32_t *)(eh+1 ); +unsigned char *p = (unsigned char *)(magic +1 ); + +/* ethernet header */ +memset (eh->h_dest,0xff ,ETH_ALEN ); +memcpy (eh->h_source, mac_addr,ETH_ALEN ); + eh->h_proto =htons (ETH_P_EXPERIMENTAL ); + +/* magic data */ + *magic =EXPERIMENTAL_MAGIC ; + +return p - buf;/* sizeof(*eh) + sizeof(*magic) */ +} + +void qemu_tap_announce_self (void ) +{ +int i, j, len; +VLANState *vlan; +VLANClientState *vc; +uint8_t buf[256 ]; + +for (i=0 ; i<nb_nics; i++) {/* for all nics */ + len =announce_self_create (buf, nd_table[i].macaddr); + vlan = nd_table[i].vlan; +for (vc = vlan->first_client; vc !=NULL ; vc = vc->next) { +if (vc->fd_read == tap_receive) {/* send only if tap */ +for (j=0 ; j<SELF_ANNOUNCE_ROUNDS ; j++) { + vc->fd_read (vc->opaque, buf, len); + } + } + }/* for vc -- look for tap_receive */ + }/* for i -- all nics */ +} +#else +void qemu_tap_announce_self (void ) {} +#endif /***********************************************************/ /* USB devices */
@@ -7480,6 +7525,9 @@printf ("Migration failed\n" );exit (1 ); } +else { +qemu_tap_announce_self (); + } } {
RE: [Qemu-devel] QEMU Live Migration: arp-like packet patch
By now (more than two months after I sent it) this patch is less relevant, as it is already included in Anthony's updated qemu-live-migration patch. Is live migration going to be accepted soon ?
Regards,
Uri.
-----Original Message-----
From: [email protected] on behalf of Uri Lublin
Sent: Wed 24/01/2007 18:36
To: [email protected]
Subject: [Qemu-devel] QEMU Live Migration: arp-like packet patch
Hello,
We, kvm developers at Qumranet, have been developing a Live Migration solution
for qemu (and kvm) too (http://kvm.sourceforge.net/migration.html).
We are working with qemu-0.8.2 and are planning to upgrade to the current
qemu CVS.
Anthony Liguori sent patches implementing Live Migration
(http://lists.gnu.org/archive/html/qemu-devel/2007-01/msg00245.html).
Some differences are:
- Anthony's solution uses ssh our solution uses TCP sockets.
- Anthony's interface is a single qemu-monitor command accepting a uri, our interface is a single qemu command accepting a few subcommands.
- Anthony's solution supports migration start without any preparations, our solution requires starting a guest (qemu) on the destination host, and establish connection before starting the migration.
Naturally, Anthony's solution can be easily enhanced to support most of our interface.
Since there is no need for two different migration solutions, we'd like to
cooperate with Anthony (and with you) and contribute some patches which are additions to his solution.
Those patches would be based on Anthony's solution (and have to be applied after
his patches are applied).
The first patch (attached) is an arp-like packet sent (broadcast) when a
migration successfully completes.
The purpose of this packet is to inform network switches that the guest moved. Although the most known unsolicited packet is the famous "gratuitous arp",
we are not sending an arp packet since we do not know the guest's ip address, and we do not want to receive replies.
Instead we use an ethernet "experimental" packet.
The packet is sent only through tap interfaces.
Our implementation currently supports only linux hosts (adding support for
other operating systems is probably/hopefully as easy as changing the include preprocessor directive).
Regards,
Uri Lublin.
I looked at your patch and it is OK for me. The 'ssh:' implementation is a nice trick. In order to be usable on win32, it is still needed to implement a more portable system with a TCP connection.
The dirty flag 0x04 is unfortunately used by kqemu - I should have documented it ! You can use 0x08 for the live migration.
Regards,
Fabrice.
Quoted 23 lines · Anthony Liguori
Howdy,
I wanted to post a draft of my QEMU live migration patch. I think there's a lingering memory allocation problem. I also need to implement ARP forwarding and a TCP transport stream.
However, I thought I'd post an initial version in case anyone wants to play around with it.
Once you apply the patch, make sure you've got it on both machines. For my setup, I've got a common NFS share mounted in /mnt. Then I run:
qemu -hda
/mnt/win2k.img-monitor telnet:0.0.0.0:1025,server,nowait -vnc :2Then, I connect to the monitor and execute:
(qemu) migrate ssh://woolly
When the migration completes, the monitor command will finish and then I can connect to the machine on woolly:2.
The ssh command will be derived from argc/argv so it's important to ensure that the -hda path is valid on both machines.
All three patches are required and should be applied in order of: qemu-apic-save-restore.diff qemu-file.diff qemu-migration.diff
Regards,
Anthony Liguori