The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6872
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From 641a9f9bfc73c8ffe42154f6b364cc5ffd8597b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 11 Feb 2020 15:32:17 -0800 Subject: [PATCH 1/3] lxd/instance: Move ParseCpuset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/devices.go | 44 ++++------------------------------ lxd/instance/instance_utils.go | 37 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/lxd/devices.go b/lxd/devices.go index 497960f2af..4ce79fa9ae 100644 --- a/lxd/devices.go +++ b/lxd/devices.go @@ -285,42 +285,6 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent return chCPU, chNetwork, chUSB, chUnix, nil } -func parseCpuset(cpu string) ([]int, error) { - cpus := []int{} - chunks := strings.Split(cpu, ",") - for _, chunk := range chunks { - if strings.Contains(chunk, "-") { - // Range - fields := strings.SplitN(chunk, "-", 2) - if len(fields) != 2 { - return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) - } - - low, err := strconv.Atoi(fields[0]) - if err != nil { - return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) - } - - high, err := strconv.Atoi(fields[1]) - if err != nil { - return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) - } - - for i := low; i <= high; i++ { - cpus = append(cpus, i) - } - } else { - // Simple entry - nr, err := strconv.Atoi(chunk) - if err != nil { - return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) - } - cpus = append(cpus, nr) - } - } - return cpus, nil -} - func deviceTaskBalance(s *state.State) { min := func(x, y int) int { if x < y { @@ -345,7 +309,7 @@ func deviceTaskBalance(s *state.State) { } } - effectiveCpusInt, err := parseCpuset(effectiveCpus) + effectiveCpusInt, err := instance.ParseCpuset(effectiveCpus) if err != nil { logger.Errorf("Error parsing effective CPU set") return @@ -362,7 +326,7 @@ func deviceTaskBalance(s *state.State) { // File might exist even though there are no isolated cpus. isolatedCpus := strings.TrimSpace(string(buf)) if isolatedCpus != "" { - isolatedCpusInt, err = parseCpuset(isolatedCpus) + isolatedCpusInt, err = instance.ParseCpuset(isolatedCpus) if err != nil { logger.Errorf("Error parsing isolated CPU set: %s", string(isolatedCpus)) return @@ -385,7 +349,7 @@ func deviceTaskBalance(s *state.State) { if err != nil && shared.PathExists("/sys/fs/cgroup/cpuset/lxc") { logger.Warn("Error setting lxd's cpuset.cpus", log.Ctx{"err": err}) } - cpus, err := parseCpuset(effectiveCpus) + cpus, err := instance.ParseCpuset(effectiveCpus) if err != nil { logger.Error("Error parsing host's cpu set", log.Ctx{"cpuset": effectiveCpus, "err": err}) return @@ -418,7 +382,7 @@ func deviceTaskBalance(s *state.State) { balancedInstances[c] = count } else { // Pinned - containerCpus, err := parseCpuset(cpulimit) + containerCpus, err := instance.ParseCpuset(cpulimit) if err != nil { return } diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go index 7dcc748da9..ae707c1258 100644 --- a/lxd/instance/instance_utils.go +++ b/lxd/instance/instance_utils.go @@ -958,3 +958,40 @@ func ValidName(instanceName string, isSnapshot bool) error { return nil } + +// ParseCpuset parses a limits.cpu range into a list of CPU ids. +func ParseCpuset(cpu string) ([]int, error) { + cpus := []int{} + chunks := strings.Split(cpu, ",") + for _, chunk := range chunks { + if strings.Contains(chunk, "-") { + // Range + fields := strings.SplitN(chunk, "-", 2) + if len(fields) != 2 { + return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) + } + + low, err := strconv.Atoi(fields[0]) + if err != nil { + return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) + } + + high, err := strconv.Atoi(fields[1]) + if err != nil { + return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) + } + + for i := low; i <= high; i++ { + cpus = append(cpus, i) + } + } else { + // Simple entry + nr, err := strconv.Atoi(chunk) + if err != nil { + return nil, fmt.Errorf("Invalid cpuset value: %s", cpu) + } + cpus = append(cpus, nr) + } + } + return cpus, nil +} From 744a23bb6eeaee7850bf701d938c70a0772fe11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 11 Feb 2020 15:53:24 -0800 Subject: [PATCH 2/3] lxd/vm/qmp: Allow retrieving vCPU pids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/instance/drivers/qmp/monitor.go | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index 4b96312228..1d81f5b8ab 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -284,3 +284,39 @@ func (m *Monitor) Quit() error { func (m *Monitor) AgentReady() bool { return m.agentReady } + +// GetCPUs fetches the vCPU information for pinning. +func (m *Monitor) GetCPUs() ([]int, error) { + // Check if disconnected + if m.disconnected { + return nil, ErrMonitorDisconnect + } + + // Query the consoles. + respRaw, err := m.qmp.Run([]byte("{'execute': 'query-cpus'}")) + if err != nil { + m.Disconnect() + return nil, ErrMonitorDisconnect + } + + // Process the response. + var respDecoded struct { + Return []struct { + CPU int `json:"CPU"` + PID int `json:"thread_id"` + } `json:"return"` + } + + err = json.Unmarshal(respRaw, &respDecoded) + if err != nil { + return nil, ErrMonitorBadReturn + } + + // Make a slice of PIDs. + pids := []int{} + for _, cpu := range respDecoded.Return { + pids = append(pids, cpu.PID) + } + + return pids, nil +} From 1d0ad4eb79d594c305eb7b9ccb4669ebd3f3bc3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 11 Feb 2020 15:53:40 -0800 Subject: [PATCH 3/3] lxd/vm: Implement CPU pinning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/instance/drivers/driver_qemu.go | 47 ++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index efb2276fbb..28c6f3256d 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -819,6 +819,46 @@ func (vm *qemu) Start(stateful bool) error { return err } + // Apply CPU pinning. + cpuLimit, ok := vm.expandedConfig["limits.cpu"] + if ok && cpuLimit != "" { + _, err := strconv.Atoi(cpuLimit) + if err != nil { + // Expand to a set of CPU identifiers. + pins, err := instance.ParseCpuset(cpuLimit) + if err != nil { + op.Done(err) + return err + } + + // Get the list of PIDs from the VM. + pids, err := monitor.GetCPUs() + if err != nil { + op.Done(err) + return err + } + + // Confirm nothing weird is going on. + if len(pins) != len(pids) { + return fmt.Errorf("QEMU has less vCPUs than configured") + } + + for i, pin := range pins { + pid := pids[i] + + set := unix.CPUSet{} + set.Set(pin) + + // Apply the pin. + err := unix.SchedSetaffinity(pid, &set) + if err != nil { + op.Done(err) + return err + } + } + } + } + // Start the VM. err = monitor.Start() if err != nil { @@ -1386,7 +1426,12 @@ func (vm *qemu) addCPUConfig(sb *strings.Builder) error { cpuCount, err := strconv.Atoi(cpus) if err != nil { - return fmt.Errorf("limits.cpu invalid: %v", err) + pins, err := instance.ParseCpuset(cpus) + if err != nil { + return fmt.Errorf("limits.cpu invalid: %v", err) + } + + cpuCount = len(pins) } return qemuCPU.Execute(sb, map[string]interface{}{
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel