From 2c786d0cb9f93c52c8929822cdfd0f795bcf64be Mon Sep 17 00:00:00 2001
From: zhong-jiawei-1 <zhongjiawei1@huawei.com>
Date: Tue, 25 Oct 2022 10:58:03 +0800
Subject: [PATCH] runc:support set cpuset.prefer_cpus using --cpuset-cpus

---
 runc-1.1.3/libcontainer/cgroups/fs/cpuset.go | 118 ++++++++++++++++++-
 1 file changed, 114 insertions(+), 4 deletions(-)

diff --git a/runc-1.1.3/libcontainer/cgroups/fs/cpuset.go b/runc-1.1.3/libcontainer/cgroups/fs/cpuset.go
index ef1f633..989f4ec 100644
--- a/runc-1.1.3/libcontainer/cgroups/fs/cpuset.go
+++ b/runc-1.1.3/libcontainer/cgroups/fs/cpuset.go
@@ -24,16 +24,126 @@ func (s *CpusetGroup) Apply(path string, r *configs.Resources, pid int) error {
 	return s.ApplyDir(path, r, pid)
 }
 
-func (s *CpusetGroup) Set(path string, r *configs.Resources) error {
-	var ret error
-	if r.CpusetCpus != "" {
-		if err := cgroups.WriteFile(path, "cpuset.cpus", r.CpusetCpus); err != nil {
+func parseCpus(cpus string) (map[int]bool, error) {
+	persedCpus := make(map[int]bool)
+	for _, cpu := range strings.Split(cpus, ",") {
+		invalidFormatError := fmt.Errorf("invalid cpuset format: %s", cpu)
+		if cpuInt, err := strconv.Atoi(cpu); err == nil {
+			if cpuInt < 0 {
+				return nil, invalidFormatError
+			}
+			persedCpus[cpuInt] = true
+			continue
+		}
+
+		ranges := strings.Split(cpu, "-")
+		if len(ranges) != 2 {
+			return nil, invalidFormatError
+		}
+		lower, err1 := strconv.Atoi(ranges[0])
+		upper, err2 := strconv.Atoi(ranges[1])
+		if err1 != nil || err2 != nil {
+			return nil, invalidFormatError
+		}
+		if lower < 0 || upper < 0 {
+			return nil, invalidFormatError
+		}
+		if lower > upper {
+			return nil, invalidFormatError
+		}
+		const max = 8192
+		if upper > max {
+			return nil, fmt.Errorf("value of out range, maximum is %d", max)
+		}
+		for i := lower; i <= upper; i++ {
+			persedCpus[i] = true
+		}
+	}
+
+	return persedCpus, nil
+}
+
+func checkInCpuset(cpuset, prefer map[int]bool) bool {
+	for k := range prefer {
+		if _, ok := cpuset[k]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+func parseCpuset(cpuset string) (string, string, error) {
+	var cpusetStr, preferCpusStr string
+	invalidFormatError := fmt.Errorf("invalid cpuset format: %s", cpuset)
+
+	splits := strings.Split(cpuset, "+")
+	if len(splits) == 1 {
+		cpusetStr = cpuset
+	} else if len(splits) == 2 {
+		if !strings.HasSuffix(splits[0], ",") {
+			return "", "", invalidFormatError
+		}
+		cpusetStr = strings.TrimSuffix(splits[0], ",")
+		preferCpusStr = splits[1]
+
+		cpusetMap, err := parseCpus(cpusetStr)
+		if err != nil {
+			return "", "", err
+		}
+		preferCpusMap, err := parseCpus(preferCpusStr)
+		if err != nil {
+			return "", "", err
+		}
+		if !checkInCpuset(cpusetMap, preferCpusMap) {
+			return "", "", fmt.Errorf("invalid preferred_cpus: %s not in cpuset: %s", preferCpusStr, cpusetStr)
+		}
+	} else {
+		return "", "", invalidFormatError
+	}
+
+	return cpusetStr, preferCpusStr, nil
+}
+
+func (s *CpusetGroup) setCpuset(path, cpuset string) error {
+	if _, err := os.Stat(filepath.Join(path, "cpuset.preferred_cpus")); err != nil {
+		var ret error
+		if err := writeFile(path, "cpuset.cpus", cpuset); err != nil {
 			ret = fmt.Errorf("failed to set cpuset.cpus, %v", err)
 			if _, err := os.Stat(path); err != nil {
 				ret = fmt.Errorf("%v, failed to stat %v, %v", ret, path, err)
 			}
 			return ret
 		}
+		return nil
+	}
+
+	cpusetStr, preferCpusStr, err := parseCpuset(cpuset)
+	if err != nil {
+		return err
+	}
+
+	if _, err := os.Stat(path); err != nil {
+		return fmt.Errorf("failed to stat %v, %v", path, err)
+	}
+	if err := writeFile(path, "cpuset.preferred_cpus", ""); err != nil {
+		return fmt.Errorf("failed to set cpuset.preferred_cpus to nil, %v", err)
+	}
+	if err := writeFile(path, "cpuset.cpus", cpusetStr); err != nil {
+		return fmt.Errorf("failed to set cpuset.cpus, %v", err)
+	}
+	if preferCpusStr != "" {
+		if err := writeFile(path, "cpuset.preferred_cpus", preferCpusStr); err != nil {
+			return fmt.Errorf("failed to set cpuset.preferred_cpus, %v", err)
+		}
+	}
+
+	return nil
+}
+
+func (s *CpusetGroup) Set(path string, r *configs.Resources) error {
+	var ret error
+	if r.CpusetCpus != "" {
+		return s.setCpuset(path, cgroup.Resources.CpusetCpus)
 	}
 	if r.CpusetMems != "" {
 		if err := cgroups.WriteFile(path, "cpuset.mems", r.CpusetMems); err != nil {
-- 
2.30.0

