cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
// Cgroup management code
|
|
|
|
|
// this sucks, ill mess with it later
|
|
|
|
|
|
2024-11-02 11:58:35 -04:00
|
|
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, rmdirSync, writeFileSync } from 'node:fs';
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
import path from 'node:path';
|
2024-11-02 18:01:07 -04:00
|
|
|
import pino from 'pino';
|
|
|
|
|
|
|
|
|
|
let logger = pino({ name: 'CVMTS/CGroup' });
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
|
|
|
|
|
export class CGroupController {
|
|
|
|
|
private controller;
|
|
|
|
|
private cg: CGroup;
|
|
|
|
|
|
|
|
|
|
constructor(controller: string, cg: CGroup) {
|
|
|
|
|
this.controller = controller;
|
|
|
|
|
this.cg = cg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteValue(key: string, value: string) {
|
2024-11-02 18:01:07 -04:00
|
|
|
try {
|
|
|
|
|
writeFileSync(path.join(this.cg.Path(), `${this.controller}.${key}`), value);
|
|
|
|
|
} catch(e) {
|
|
|
|
|
logger.error({error: e, controller_key: `${this.controller}.${key}`, value: value }, 'Failed to set CGroup controller value')
|
|
|
|
|
}
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class CGroup {
|
|
|
|
|
private path;
|
|
|
|
|
|
|
|
|
|
constructor(path: string) {
|
|
|
|
|
this.path = path;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 18:01:07 -04:00
|
|
|
InitControllers(wants_cpuset: boolean) {
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
// Configure this "root" cgroup to provide cpu and cpuset controllers to the leaf
|
|
|
|
|
// QEMU cgroups. A bit iffy but whatever.
|
2024-11-02 18:01:07 -04:00
|
|
|
if(wants_cpuset) {
|
|
|
|
|
try {
|
|
|
|
|
writeFileSync(path.join(this.path, 'cgroup.subtree_control'), '+cpu +cpuset');
|
|
|
|
|
} catch(err) {
|
|
|
|
|
logger.error({error: err}, 'Could not provide cpuset controller to subtree');
|
|
|
|
|
// just give up if this fails
|
|
|
|
|
writeFileSync(path.join(this.path, 'cgroup.subtree_control'), '+cpu');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
writeFileSync(path.join(this.path, 'cgroup.subtree_control'), '+cpu');
|
|
|
|
|
}
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GetController(controller: string) {
|
|
|
|
|
return new CGroupController(controller, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Path(): string {
|
|
|
|
|
return this.path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HasSubgroup(name: string): boolean {
|
|
|
|
|
let subgroup_root = path.join(this.path, name);
|
|
|
|
|
if (existsSync(subgroup_root)) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 11:58:35 -04:00
|
|
|
DeleteSubgroup(name: string): void {
|
|
|
|
|
let subgroup_root = path.join(this.path, name);
|
|
|
|
|
if (!this.HasSubgroup(name)) {
|
|
|
|
|
throw new Error(`Subgroup ${name} does not exist`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//console.log("Deleting subgroup", name);
|
|
|
|
|
rmdirSync(subgroup_root);
|
|
|
|
|
}
|
|
|
|
|
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
// Gets a CGroup inside of this cgroup.
|
|
|
|
|
GetSubgroup(name: string): CGroup {
|
|
|
|
|
// make the subgroup if it doesn't already exist
|
|
|
|
|
let subgroup_root = path.join(this.path, name);
|
|
|
|
|
if (!this.HasSubgroup(name)) {
|
|
|
|
|
mkdirSync(subgroup_root);
|
|
|
|
|
// We need to make the subgroup threaded before we can attach a process to it.
|
|
|
|
|
// It's a bit weird, but oh well. Blame linux people, not me.
|
|
|
|
|
writeFileSync(path.join(subgroup_root, 'cgroup.type'), 'threaded');
|
|
|
|
|
}
|
|
|
|
|
return new CGroup(subgroup_root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Attaches a process to this cgroup.
|
|
|
|
|
AttachProcess(pid: number) {
|
|
|
|
|
appendFileSync(path.join(this.path, 'cgroup.procs'), pid.toString());
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 07:46:59 -04:00
|
|
|
// Attaches a thread to this cgroup. (The CGroup is a threaded one. See above)
|
|
|
|
|
AttachThread(tid: number) {
|
|
|
|
|
appendFileSync(path.join(this.path, 'cgroup.threads'), tid.toString());
|
|
|
|
|
}
|
|
|
|
|
|
cvmts: Add support for cgroup process resource limits on Linux
Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff.
This commit adds support for doing so and configuring the resource limits in config.toml.
For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated.
NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
2024-11-02 06:08:26 -04:00
|
|
|
// Returns a CGroup instance for the process' current cgroup, prepared for subgroup usage.
|
|
|
|
|
// This will only fail if you are not using systemd or elogind,
|
|
|
|
|
// since even logind user sessions are run inside of a user@[UID] slice.
|
|
|
|
|
// NOTE: This only supports cgroups2-only systems. Systemd practically enforces that so /shrug
|
|
|
|
|
static Self(): CGroup {
|
|
|
|
|
const kCgroupSelfPath = '/proc/self/cgroup';
|
|
|
|
|
if (!existsSync(kCgroupSelfPath)) throw new Error('This process is not in a CGroup.');
|
|
|
|
|
let res = readFileSync(kCgroupSelfPath, { encoding: 'utf-8' });
|
|
|
|
|
|
|
|
|
|
// Make sure the first/only line is a cgroups2 0::/path/to/cgroup entry.
|
|
|
|
|
// Legacy cgroups1 is not supported.
|
|
|
|
|
if (res[0] != '0') throw new Error('CGroup.Self() does not work with cgroups 1 systems. Please do not the cgroups 1.');
|
|
|
|
|
let cg_path = res.substring(3, res.indexOf('\n'));
|
|
|
|
|
|
|
|
|
|
let cg = new CGroup(path.join('/sys/fs/cgroup', cg_path));
|
|
|
|
|
|
|
|
|
|
return cg;
|
|
|
|
|
}
|
|
|
|
|
}
|