Fix interaction between CPU pinning and KVM migration
authorAndrea Spadaccini <spadaccio@google.com>
Wed, 21 Sep 2011 13:38:57 +0000 (14:38 +0100)
committerAndrea Spadaccini <spadaccio@google.com>
Fri, 23 Sep 2011 08:57:03 +0000 (09:57 +0100)
CPU pinning requires the KVM hypervisor to start in the paused state, in
order to retrieve information, and immediately unpauses it.

This does not play well with live migration, as the unpausing was done
before the migration started and so the receiving kvm process left the
migrated instance in the stopped status.

This patch fixes this behavior, by not launching the KVM process in
stopped state while on the receiving side of a migration.

Also, the stopping is now done outside _ExecuteCpuAffinity.

Signed-off-by: Andrea Spadaccini <spadaccio@google.com>
Reviewed-by: Tsachy Shacham <tsachy@google.com>

lib/hypervisor/hv_kvm.py

index 3fd2b4d..8ea1d71 100644 (file)
@@ -839,33 +839,21 @@ class KVMHypervisor(hv_base.BaseHypervisor):
 
     return result
 
-  def _ExecuteCpuAffinity(self, instance_name, cpu_mask, startup_paused):
-    """Complete CPU pinning and resume instance execution if needed.
+  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
+    """Complete CPU pinning.
 
     @type instance_name: string
     @param instance_name: name of instance
     @type cpu_mask: string
     @param cpu_mask: CPU pinning mask as entered by user
-    @type startup_paused: bool
-    @param startup_paused: was instance requested to pause before startup
 
     """
-    try:
-      # Get KVM process ID, to be used if need to pin entire VM
-      _, pid, _ = self._InstancePidAlive(instance_name)
-      # Get vCPU thread IDs, to be used if need to pin vCPUs separately
-      thread_dict = self._GetVcpuThreadIds(instance_name)
-      # Run CPU pinning, based on configured mask
-      self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
-
-    finally:
-      # To control CPU pinning, the VM was started frozen, so we need
-      # to resume its execution, but only if freezing was not
-      # explicitly requested.
-      # Note: this is done even when an exception occurred so the VM
-      # is not unintentionally frozen.
-      if not startup_paused:
-        self._CallMonitorCommand(instance_name, self._CONT_CMD)
+    # Get KVM process ID, to be used if need to pin entire VM
+    _, pid, _ = self._InstancePidAlive(instance_name)
+    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
+    thread_dict = self._GetVcpuThreadIds(instance_name)
+    # Run CPU pinning, based on configured mask
+    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
 
   def ListInstances(self):
     """Get the list of running instances.
@@ -1388,10 +1376,10 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         continue
       self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
 
-    # Before running the KVM command, capture wether the instance is
-    # supposed to start paused. This is used later when changing CPU
-    # affinity in order to know whether to resume instance execution.
-    startup_paused = _KVM_START_PAUSED_FLAG in kvm_cmd
+    # CPU affinity requires kvm to start paused, so we set this flag if the
+    # instance is not already paused and if we are not going to accept a
+    # migrating instance. In the latter case, pausing is not needed.
+    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
 
     # Note: CPU pinning is using up_hvp since changes take effect
     # during instance startup anyway, and to avoid problems when soft
@@ -1399,7 +1387,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     cpu_pinning = False
     if up_hvp.get(constants.HV_CPU_MASK, None):
       cpu_pinning = True
-      if not startup_paused:
+      if start_kvm_paused:
         kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
 
     if security_model == constants.HT_SM_POOL:
@@ -1455,8 +1443,16 @@ class KVMHypervisor(hv_base.BaseHypervisor):
 
     # If requested, set CPU affinity and resume instance execution
     if cpu_pinning:
-      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK],
-                               startup_paused)
+      try:
+        self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
+      finally:
+        if start_kvm_paused:
+          # To control CPU pinning, the VM was started frozen, so we need
+          # to resume its execution, but only if freezing was not
+          # explicitly requested.
+          # Note: this is done even when an exception occurred so the VM
+          # is not unintentionally frozen.
+          self._CallMonitorCommand(instance.name, self._CONT_CMD)
 
   def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.