Skip to content

Commit cad55c0

Browse files
authored
Merge pull request #4 from hshiina/cpu-quota-round
systemd: Write rounded CPU quota to cgroupfs
2 parents 4ba5b2a + 83f2519 commit cad55c0

File tree

4 files changed

+75
-7
lines changed

4 files changed

+75
-7
lines changed

systemd/common.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ func systemdVersionAtoi(str string) (int, error) {
280280
return ver, nil
281281
}
282282

283-
func addCpuQuota(cm *dbusConnManager, properties *[]systemdDbus.Property, quota int64, period uint64) {
283+
// addCPUQuota adds CPUQuotaPeriodUSec and CPUQuotaPerSecUSec to the properties. The passed quota may be modified
284+
// along with round-up during calculation in order to write the same value to cgroupfs later.
285+
func addCPUQuota(cm *dbusConnManager, properties *[]systemdDbus.Property, quota *int64, period uint64) {
284286
if period != 0 {
285287
// systemd only supports CPUQuotaPeriodUSec since v242
286288
sdVer := systemdVersion(cm)
@@ -292,10 +294,10 @@ func addCpuQuota(cm *dbusConnManager, properties *[]systemdDbus.Property, quota
292294
" (setting will still be applied to cgroupfs)", sdVer)
293295
}
294296
}
295-
if quota != 0 || period != 0 {
297+
if *quota != 0 || period != 0 {
296298
// corresponds to USEC_INFINITY in systemd
297299
cpuQuotaPerSecUSec := uint64(math.MaxUint64)
298-
if quota > 0 {
300+
if *quota > 0 {
299301
if period == 0 {
300302
// assume the default
301303
period = defCPUQuotaPeriod
@@ -304,9 +306,11 @@ func addCpuQuota(cm *dbusConnManager, properties *[]systemdDbus.Property, quota
304306
// (integer percentage of CPU) internally. This means that if a fractional percent of
305307
// CPU is indicated by Resources.CpuQuota, we need to round up to the nearest
306308
// 10ms (1% of a second) such that child cgroups can set the cpu.cfs_quota_us they expect.
307-
cpuQuotaPerSecUSec = uint64(quota*1000000) / period
309+
cpuQuotaPerSecUSec = uint64(*quota*1000000) / period
308310
if cpuQuotaPerSecUSec%10000 != 0 {
309311
cpuQuotaPerSecUSec = ((cpuQuotaPerSecUSec / 10000) + 1) * 10000
312+
// Update the requested quota along with the round-up in order to write the same value to cgroupfs.
313+
*quota = int64(cpuQuotaPerSecUSec) * int64(period) / 1000000
310314
}
311315
}
312316
*properties = append(*properties,

systemd/systemd_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,61 @@ func TestUnifiedResToSystemdProps(t *testing.T) {
178178
})
179179
}
180180
}
181+
182+
func TestAddCPUQuota(t *testing.T) {
183+
if !IsRunningSystemd() {
184+
t.Skip("Test requires systemd.")
185+
}
186+
187+
cm := newDbusConnManager(os.Geteuid() != 0)
188+
189+
testCases := []struct {
190+
name string
191+
quota int64
192+
period uint64
193+
expectedCPUQuotaPerSecUSec uint64
194+
expectedQuota int64
195+
}{
196+
{
197+
name: "No round up",
198+
quota: 500000,
199+
period: 1000000,
200+
expectedCPUQuotaPerSecUSec: 500000,
201+
expectedQuota: 500000,
202+
},
203+
{
204+
name: "With fraction",
205+
quota: 123456,
206+
expectedCPUQuotaPerSecUSec: 1240000,
207+
expectedQuota: 124000,
208+
},
209+
{
210+
name: "Round up at division",
211+
quota: 500000,
212+
period: 900000,
213+
expectedCPUQuotaPerSecUSec: 560000,
214+
expectedQuota: 504000,
215+
},
216+
}
217+
218+
for _, tc := range testCases {
219+
t.Run(tc.name, func(t *testing.T) {
220+
props := []systemdDbus.Property{}
221+
addCPUQuota(cm, &props, &tc.quota, tc.period)
222+
var cpuQuotaPerSecUSec uint64
223+
for _, p := range props {
224+
if p.Name == "CPUQuotaPerSecUSec" {
225+
if err := p.Value.Store(&cpuQuotaPerSecUSec); err != nil {
226+
t.Errorf("failed to parse CPUQuotaPerSecUSec: %v", err)
227+
}
228+
}
229+
}
230+
if cpuQuotaPerSecUSec != tc.expectedCPUQuotaPerSecUSec {
231+
t.Errorf("CPUQuotaPerSecUSec is not set as expected (exp: %v, got: %v)", tc.expectedCPUQuotaPerSecUSec, cpuQuotaPerSecUSec)
232+
}
233+
if tc.quota != tc.expectedQuota {
234+
t.Errorf("quota is not updated as expected (exp: %v, got: %v)", tc.expectedQuota, tc.quota)
235+
}
236+
})
237+
}
238+
}

systemd/v1.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func genV1ResourcesProperties(r *cgroups.Resources, cm *dbusConnManager) ([]syst
9090
newProp("CPUShares", r.CpuShares))
9191
}
9292

93-
addCpuQuota(cm, &properties, r.CpuQuota, r.CpuPeriod)
93+
addCPUQuota(cm, &properties, &r.CpuQuota, r.CpuPeriod)
9494

9595
if r.BlkioWeight != 0 {
9696
properties = append(properties,
@@ -334,6 +334,9 @@ func (m *LegacyManager) Set(r *cgroups.Resources) error {
334334
if r.Unified != nil {
335335
return cgroups.ErrV1NoUnified
336336
}
337+
// Use a copy since CpuQuota in r may be modified.
338+
rCopy := *r
339+
r = &rCopy
337340
properties, err := genV1ResourcesProperties(r, m.dbus)
338341
if err != nil {
339342
return err

systemd/v2.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func unifiedResToSystemdProps(cm *dbusConnManager, res map[string]string) (props
113113
return nil, fmt.Errorf("unified resource %q quota value conversion error: %w", k, err)
114114
}
115115
}
116-
addCpuQuota(cm, &props, quota, period)
116+
addCPUQuota(cm, &props, &quota, period)
117117

118118
case "cpu.weight":
119119
if shouldSetCPUIdle(cm, strings.TrimSpace(res["cpu.idle"])) {
@@ -254,7 +254,7 @@ func genV2ResourcesProperties(dirPath string, r *cgroups.Resources, cm *dbusConn
254254
}
255255
}
256256

257-
addCpuQuota(cm, &properties, r.CpuQuota, r.CpuPeriod)
257+
addCPUQuota(cm, &properties, &r.CpuQuota, r.CpuPeriod)
258258

259259
if r.PidsLimit > 0 || r.PidsLimit == -1 {
260260
properties = append(properties,
@@ -480,6 +480,9 @@ func (m *UnifiedManager) Set(r *cgroups.Resources) error {
480480
if r == nil {
481481
return nil
482482
}
483+
// Use a copy since CpuQuota in r may be modified.
484+
rCopy := *r
485+
r = &rCopy
483486
properties, err := genV2ResourcesProperties(m.fsMgr.Path(""), r, m.dbus)
484487
if err != nil {
485488
return err

0 commit comments

Comments
 (0)