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

Reply via email to