Skip to content

Commit 7f22559

Browse files
stgrabermihalicyn
authored andcommitted
lxd/instance/qemu: Use cluster CPU flags for migration.stateful
Signed-off-by: Stéphane Graber <[email protected]> Sponsored-by: Luizalabs (https://luizalabs.com) (cherry picked from commit 4d43430e13e6f5b0c0c665bde317f8243709c4d3) Signed-off-by: Kadin Sayani <[email protected]> Signed-off-by: Gabriel Mougard <[email protected]> License: Apache-2.0
1 parent c977616 commit 7f22559

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

lxd/instance/drivers/driver_qemu.go

+124
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,99 @@ func (d *qemu) getAgentClient() (*http.Client, error) {
377377
return agent, nil
378378
}
379379

380+
// getClusterCPUFlags identifies CPU flags that are consistently present across all
381+
// CPU cores in the cluster.
382+
//
383+
// 1. Retrieving all nodes in the cluster and their CPU information.
384+
// 2. Counting which flags are present on every core across all nodes.
385+
// 3. Building a set of flags common to all cores.
386+
//
387+
// This is useful in a migration scenario where we need to find a minimum common denominator
388+
// of CPU flags to properly start a VM on a different node in a cluster.
389+
func (d *qemu) getClusterCPUFlags() ([]string, error) {
390+
// Get the list of cluster members.
391+
var nodes []db.RaftNode
392+
err := d.state.DB.Node.Transaction(context.TODO(), func(ctx context.Context, tx *db.NodeTx) error {
393+
var err error
394+
nodes, err = tx.GetRaftNodes(ctx)
395+
return err
396+
})
397+
if err != nil {
398+
return nil, err
399+
}
400+
401+
// Get architecture name.
402+
arch, err := osarch.ArchitectureName(d.architecture)
403+
if err != nil {
404+
return nil, err
405+
}
406+
407+
// Get all the CPU flags for the architecture.
408+
flagMembers := map[string]int{}
409+
coreCount := 0
410+
411+
for _, node := range nodes {
412+
// Attempt to load the cached resources.
413+
resourcesPath := internalUtil.CachePath("resources", fmt.Sprintf("%s.yaml", node.Name))
414+
415+
data, err := os.ReadFile(resourcesPath)
416+
if err != nil {
417+
if os.IsNotExist(err) {
418+
continue
419+
}
420+
421+
return nil, err
422+
}
423+
424+
res := api.Resources{}
425+
err = json.Unmarshal(data, &res)
426+
if err != nil {
427+
return nil, err
428+
}
429+
430+
// Skip if not the correct architecture.
431+
if res.CPU.Architecture != arch {
432+
continue
433+
}
434+
435+
// Add the CPU flags to the map.
436+
for _, socket := range res.CPU.Sockets {
437+
for _, core := range socket.Cores {
438+
coreCount += 1
439+
for _, flag := range core.Flags {
440+
flagMembers[flag] += 1
441+
}
442+
}
443+
}
444+
}
445+
446+
// Get the host flags.
447+
info := DriverStatuses()[instancetype.VM].Info
448+
hostFlags, ok := info.Features["flags"].(map[string]bool)
449+
if !ok {
450+
// No CPU flags found.
451+
return nil, nil
452+
}
453+
454+
// Build a set of flags common to all cores.
455+
flags := []string{}
456+
457+
for k, v := range flagMembers {
458+
if v != coreCount {
459+
continue
460+
}
461+
462+
hostVal, ok := hostFlags[k]
463+
if !ok || hostVal {
464+
continue
465+
}
466+
467+
flags = append(flags, k)
468+
}
469+
470+
return flags, nil
471+
}
472+
380473
func (d *qemu) getMonitorEventHandler() func(event string, data map[string]any) {
381474
// Create local variables from instance properties we need so as not to keep references to instance around
382475
// after we have returned the callback function.
@@ -1480,6 +1573,19 @@ func (d *qemu) start(stateful bool, op *operationlock.InstanceOperation) error {
14801573
}
14811574

14821575
cpuType := "host"
1576+
1577+
// Get CPU flags if clustered and migration is enabled.
1578+
if d.state.ServerClustered && util.IsTrue(d.expandedConfig["migration.stateful"]) {
1579+
cpuFlags, err := d.getClusterCPUFlags()
1580+
if err != nil {
1581+
op.Done(err)
1582+
return err
1583+
}
1584+
1585+
cpuType = "kvm64"
1586+
cpuExtensions = append(cpuExtensions, cpuFlags...)
1587+
}
1588+
14831589
if len(cpuExtensions) > 0 {
14841590
cpuType += "," + strings.Join(cpuExtensions, ",")
14851591
}
@@ -8929,6 +9035,24 @@ func (d *qemu) checkFeatures(hostArch int, qemuPath string) (map[string]any, err
89299035
features["vhost_net"] = struct{}{}
89309036
}
89319037

9038+
// Get the host CPU model.
9039+
model, err := monitor.QueryCPUModel("kvm64")
9040+
if err != nil {
9041+
return nil, err
9042+
}
9043+
9044+
cpuFlags := map[string]bool{}
9045+
for k, v := range model.Flags {
9046+
value, ok := v.(bool)
9047+
if !ok {
9048+
continue
9049+
}
9050+
9051+
cpuFlags[k] = value
9052+
}
9053+
9054+
features["flags"] = cpuFlags
9055+
89329056
return features, nil
89339057
}
89349058

0 commit comments

Comments
 (0)