Compare commits

...

21 Commits
v1.0.7 ... main

Author SHA1 Message Date
11e12a2f5e Add new scx scheds
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 27s
2025-03-12 22:25:55 +00:00
4806f7244a Add new scx scheds
Some checks failed
PikaOS Package Build & Release (amd64-v3) / build (push) Failing after 20s
2025-03-12 22:21:27 +00:00
387ee48881 Add new SCX scheds 2025-03-12 22:20:48 +00:00
ferreo
23520995bf Update falcond/debian/control 2025-03-04 23:48:12 +01:00
2b15cbede4 Update for the new zig version
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 26s
2025-03-04 22:15:59 +00:00
ferreo
a2c3d34fb2 Update .github/release-nest-v3
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 27s
2025-02-10 19:13:24 +01:00
ferreo
36a90a297d Update main.sh 2025-02-10 19:13:17 +01:00
54a7b7dc73 Fix memory leak on error, fix power ppd being required for falcond to launch.
Some checks failed
PikaOS Package Build & Release (amd64-v3) / build (push) Failing after 17s
2025-02-10 18:11:06 +00:00
ferreo
75d6d24d2f Update .github/release-nest-v3
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 27s
2025-02-09 01:03:57 +01:00
ferreo
67c3f96ae6 Update falcond/debian/changelog 2025-02-09 01:03:50 +01:00
bbef2d1607 Add better error logging and fix config parsing in newer zig versions
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 28s
2025-01-29 19:24:27 +00:00
d887536199 Add better error logging and fix config parsing in newer zig versions 2025-01-29 19:23:41 +00:00
ferreo
787bd99c06 Update .github/release-nest-v3
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 28s
2025-01-29 15:18:52 +01:00
ferreo
bb786d1419 Update falcond/debian/changelog 2025-01-29 15:18:45 +01:00
be0c2e28b2 Don't log ananicy failures - fix zig 0.14.0 build 2025-01-17 19:57:36 +00:00
af4ac1c167 Make scx optional at runtime in case the service is down 2025-01-17 13:57:10 +00:00
ea524fd764 Fix scx scheduler mode selection order
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 26s
2025-01-16 20:01:31 +00:00
d829af05f1 Contiributing correction 2025-01-16 17:21:52 +00:00
c6434f1a4e Add readme 2025-01-16 17:20:59 +00:00
ferreo
c3a26bb44e Update .github/release-nest-v3
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 26s
2025-01-16 17:08:34 +01:00
ferreo
c58b9fbe90 Rebuild 2025-01-16 17:08:27 +01:00
14 changed files with 351 additions and 84 deletions

View File

@ -1 +1 @@
1 2

145
README.md Normal file
View File

@ -0,0 +1,145 @@
# falcond - Advanced Linux Gaming Performance Daemon
falcond is a powerful system daemon designed to automatically optimize your Linux gaming experience. It intelligently manages system resources and performance settings on a per-game basis, eliminating the need to manually configure settings for each game.
## Features
- **Automatic Game Detection**: Automatically detects running games and applies optimized settings
- **Per-Game Profiles**: Customizable profiles for different games
- **Performance Mode**: Enables system-wide performance optimizations when gaming
- **3D vcache Management**: Smart management of AMD 3D vcache settings
- **SCX Scheduler Integration**: Dynamically pick a scheduler that is best for the specific game
- **Proton Compatibility**: Full support for Steam Proton games, with a global profile for excellent game coverage
- **Low Overhead**: Minimal system resource usage
- **Different Device Modes**: Profiles for desktops, handhelds, HTPC etc
## Installation
falcond is available on PikaOS via the falcond package or via the Pika Gameutils Metapackage. For other distributions, please check your package manager or build from source.
## Configuration
The configuration file is located at `/etc/falcond/config.conf`. Here's an example configuration, it will be generated automatically on first run:
```
enable_performance_mode = true
scx_sched = none
scx_sched_props = default
vcache_mode = none
profile_mode = none
```
This is global configuration and all options other than profile_mode override individual game profiles.
There is also a list of proton/wine system processes in `/usr/share/falcond/system.conf`. This list can be updated if for example a crash handler is intercepting your profile and needs to be ignored.
### Configuration Options
- `enable_performance_mode`: Enable/disable performance mode (default: true)
- `scx_sched`: SCX scheduler (options: none, bpfland, lavd, rusty, flash)
- `scx_sched_props`: SCX scheduler mode (options: default, gaming, power, latency, server)
- `vcache_mode`: VCache management mode (options: none, cache, freq)
- `profile_mode`: What type of device is in use, none means desktop (options: none, handheld, htpc)
## Profile Modes
Falcond now supports different profile modes for different device types:
- Default (none): Uses profiles from `/usr/share/falcond/profiles`
- Handheld: Uses profiles from `/usr/share/falcond/profiles/handheld`
- HTPC: Uses profiles from `/usr/share/falcond/profiles/htpc`
To set a profile mode, add this to your config file:
```
profile_mode = "handheld" # or "htpc" or "none"
```
## Game Profiles
Game profiles are stored in `/usr/share/falcond/profiles/` and define specific optimizations for individual games. You can contribute new profiles via PR at:
[Falcond Profiles Repository](https://github.com/PikaOS-Linux/falcond-profiles)
Example profile:
```
name = "game.exe"
performance_mode = true
scx_sched = bpfland
scx_sched_props = gaming
vcache_mode = cache
```
### Available Options
- `name`: The exe name (examples: cs2, PathOfExileSteam.exe)
- `performance_mode`: Enable/disable performance mode (default: true)
- `scx_sched`: SCX scheduler (options: none, bpfland, lavd, rusty, flash)
- `scx_sched_props`: SCX scheduler mode (options: none, gaming, power, latency, server)
- `vcache_mode`: VCache management mode (options: none, cache, freq)
## Service Management
To restart the daemon:
```bash
sudo systemctl restart falcond
```
To check the status:
```
sudo systemctl status falcond
```
## Source Code
The source code for falcond can be found at:
[Falcond Source Repository](https://git.pika-os.com/general-packages/falcond)
## Important Notes
⚠️ **Compatibility Warning** ⚠️ falcond should not be used alongside Feral GameMode or Falcon GameMode as they may conflict with each other. falcond provides similar functionality with additional features and optimizations.
## Why falcond?
Traditional gaming on Linux often requires manual optimization for each game - tweaking CPU governors, scheduling priorities, and cache settings. falcond automates this entire process by:
1. Automatically detecting when games are running
2. Applying optimized settings based on pre-configured profiles
3. Reverting settings when games are closed
4. Providing a centralized way to manage gaming performance
5. Easy switching of options for different device types
This means you can focus on gaming while falcond handles all the technical optimizations in the background!
## License
falcond is released under the [MIT License](http://git.pika-os.com/general-packages/falcond/raw/branch/main/LICENSE).
## Contributing
Please fork the [PikaOS-Linux/falcond](https://github.com/PikaOS-Linux/falcond) repository and submit a pull request.
## Build Dependencies
zig 0.14.0+
## Building from Source
```
git clone https://git.pika-os.com/general-packages/falcond.git
cd falcond
cd falcond
zig build -Doptimize=ReleaseFast
```
## Runtime Dependencies
These should be feature detected by falcond so if not present that specific feature will not be used.
power-profiles-daemon or tuned + tuned-ppd
scx-sched
Linux kernel patched with AMD 3D vcache support
## Packaging
falcond should be placed in /usr/bin/falcond and run via a service file. There is an example systemd service file in ./falcond/debian/falcond.service
falcond needs profiles to be useful, these should be placed in /usr/share/falcond/profiles alongside the system.conf in /usr/share/falcond/system.conf. Upto date profiles can be found in the [PikaOS-Linux/falcond-profiles](https://github.com/PikaOS-Linux/falcond-profiles) repository. We currently pull the latest profiles from there on building of this package but you could also package seperately and depend on that package.
There is a config file in /etc/falcond/config.conf which is generated automatically on first run. You could also package that if you need different default settings.

View File

@ -4,13 +4,17 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{ const main_module = b.createModule(.{
.name = "falcond",
.root_source_file = .{ .cwd_relative = "src/main.zig" }, .root_source_file = .{ .cwd_relative = "src/main.zig" },
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const exe = b.addExecutable(.{
.name = "falcond",
.root_module = main_module,
});
exe.bundle_compiler_rt = true; exe.bundle_compiler_rt = true;
b.installArtifact(exe); b.installArtifact(exe);

View File

@ -6,12 +6,12 @@
// //
// It is redundant to include "zig" in this name because it is already // It is redundant to include "zig" in this name because it is already
// within the Zig package namespace. // within the Zig package namespace.
.name = "falcond", .name = .falcond,
// This is a [Semantic Version](https://semver.org/). // This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication. // In a future version of Zig it will be used for package deduplication.
.version = "0.1.0", .version = "0.1.0",
.fingerprint = 0x31d9d78a54cab47b,
// This field is optional. // This field is optional.
// This is currently advisory only; Zig does not yet do anything // This is currently advisory only; Zig does not yet do anything
// with this value. // with this value.

View File

@ -1,4 +1,41 @@
falcond (1.0.7-101pika1) pika; urgency=low falcond (1.1.2-101pika1) pika; urgency=low
* Add new scx sched options
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
falcond (1.1.1-101pika1) pika; urgency=low
* Update to support latest zig
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
falcond (1.1.0-101pika1) pika; urgency=low
* Fix memory leak on error, fix power ppd being required for falcond to launch.
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
falcond (1.0.9-101pika2) pika; urgency=low
* Config parsing fixes
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
falcond (1.0.8-101pika3) pika; urgency=low
* Update profiles
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
falcond (1.0.8-101pika2) pika; urgency=low
* Fix scx scheduler mode selection order
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
falcond (1.0.7-101pika2) pika; urgency=low
* Don't load profiles in subfolders * Don't load profiles in subfolders

View File

@ -4,7 +4,7 @@ Priority: optional
Maintainer: ferreo <ferreo@pika-os.com> Maintainer: ferreo <ferreo@pika-os.com>
Rules-Requires-Root: no Rules-Requires-Root: no
Build-Depends: Build-Depends:
debhelper-compat (= 13), zig-nightly, git debhelper-compat (= 13), zig, git
Standards-Version: 4.6.1 Standards-Version: 4.6.1
Homepage: https://pika-os.com Homepage: https://pika-os.com

View File

@ -13,7 +13,7 @@ pub const PowerProfiles = struct {
original_profile: ?[]const u8, original_profile: ?[]const u8,
has_performance: bool, has_performance: bool,
pub fn init(allocator: std.mem.Allocator, config: Config) !*PowerProfiles { pub fn init(allocator: std.mem.Allocator, config: Config) !?*PowerProfiles {
var self = try allocator.create(PowerProfiles); var self = try allocator.create(PowerProfiles);
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@ -37,7 +37,11 @@ pub const PowerProfiles = struct {
.has_performance = false, .has_performance = false,
}; };
const profiles = try self.getAvailableProfiles(allocator); const profiles = self.getAvailableProfiles(allocator) catch |err| {
std.log.err("Failed to get power profiles: {}, power profiles will be disabled", .{err});
allocator.destroy(self);
return null;
};
defer { defer {
for (profiles) |profile| { for (profiles) |profile| {
allocator.free(profile); allocator.free(profile);

View File

@ -20,6 +20,8 @@ pub const ScxScheduler = enum {
simple, simple,
userland, userland,
vder, vder,
p2dq,
tickless,
none, none,
pub fn toScxName(self: ScxScheduler) []const u8 { pub fn toScxName(self: ScxScheduler) []const u8 {
@ -49,6 +51,8 @@ pub const ScxScheduler = enum {
if (std.mem.eql(u8, str, "scx_simple")) return .simple; if (std.mem.eql(u8, str, "scx_simple")) return .simple;
if (std.mem.eql(u8, str, "scx_userland")) return .userland; if (std.mem.eql(u8, str, "scx_userland")) return .userland;
if (std.mem.eql(u8, str, "scx_vder")) return .vder; if (std.mem.eql(u8, str, "scx_vder")) return .vder;
if (std.mem.eql(u8, str, "scx_p2dq")) return .p2dq;
if (std.mem.eql(u8, str, "scx_tickless")) return .tickless;
return error.InvalidValue; return error.InvalidValue;
} }
@ -75,7 +79,11 @@ pub fn init(alloc: std.mem.Allocator) !void {
allocator = alloc; allocator = alloc;
std.log.info("Initializing scheduler state", .{}); std.log.info("Initializing scheduler state", .{});
const sched_list = try getSupportedSchedulers(alloc); const sched_list = getSupportedSchedulers(alloc) catch |err| {
std.log.warn("Failed to get supported schedulers: {}, scx_loader may not be available", .{err});
supported_schedulers = &[_]ScxScheduler{};
return;
};
defer alloc.free(sched_list); defer alloc.free(sched_list);
std.log.info("Supported schedulers:", .{}); std.log.info("Supported schedulers:", .{});
@ -102,8 +110,8 @@ const SCX_IFACE = "org.scx.Loader";
fn modeToInt(mode: ScxSchedModes) u32 { fn modeToInt(mode: ScxSchedModes) u32 {
return switch (mode) { return switch (mode) {
.default => 0, .default => 0,
.power => 1, .gaming => 1,
.gaming => 2, .power => 2,
.latency => 3, .latency => 3,
.server => 4, .server => 4,
}; };
@ -112,8 +120,8 @@ fn modeToInt(mode: ScxSchedModes) u32 {
fn intToMode(value: u32) ScxError!ScxSchedModes { fn intToMode(value: u32) ScxError!ScxSchedModes {
return switch (value) { return switch (value) {
0 => .default, 0 => .default,
1 => .power, 1 => .gaming,
2 => .gaming, 2 => .power,
3 => .latency, 3 => .latency,
4 => .server, 4 => .server,
else => error.InvalidValue, else => error.InvalidValue,
@ -123,23 +131,38 @@ fn intToMode(value: u32) ScxError!ScxSchedModes {
pub fn getCurrentScheduler(alloc: std.mem.Allocator) !?ScxScheduler { pub fn getCurrentScheduler(alloc: std.mem.Allocator) !?ScxScheduler {
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE); var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
const current = try dbus_conn.getProperty("CurrentScheduler"); const current = dbus_conn.getProperty("CurrentScheduler") catch |err| {
std.log.warn("Failed to get current scheduler: {}", .{err});
return .none;
};
defer alloc.free(current); defer alloc.free(current);
if (current.len == 0) return null; if (current.len == 0) return .none;
return try ScxScheduler.fromString(current); return ScxScheduler.fromString(current) catch |err| {
std.log.warn("Invalid scheduler value: {}", .{err});
return .none;
};
} }
pub fn getCurrentMode(alloc: std.mem.Allocator) !ScxSchedModes { pub fn getCurrentMode(alloc: std.mem.Allocator) !ScxSchedModes {
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE); var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
const mode_str = try dbus_conn.getProperty("SchedulerMode"); const mode_str = dbus_conn.getProperty("SchedulerMode") catch |err| {
std.log.warn("Failed to get scheduler mode: {}", .{err});
return .default;
};
defer alloc.free(mode_str); defer alloc.free(mode_str);
if (mode_str.len == 0) return .default; if (mode_str.len == 0) return .default;
const mode = try std.fmt.parseInt(u32, mode_str, 10); const mode = std.fmt.parseInt(u32, mode_str, 10) catch |err| {
return intToMode(mode); std.log.warn("Invalid scheduler mode value: {}", .{err});
return .default;
};
return intToMode(mode) catch |err| {
std.log.warn("Invalid mode value: {}", .{err});
return .default;
};
} }
pub fn getSupportedSchedulers(alloc: std.mem.Allocator) ![]ScxScheduler { pub fn getSupportedSchedulers(alloc: std.mem.Allocator) ![]ScxScheduler {
@ -247,23 +270,21 @@ fn runSystemCtl(alloc: std.mem.Allocator, command: []const u8, service: []const
.allocator = alloc, .allocator = alloc,
.argv = &argv, .argv = &argv,
.max_output_bytes = max_output_size, .max_output_bytes = max_output_size,
}) catch |err| { }) catch {
std.log.warn("Failed to run systemctl {s} {s}: {}", .{ command, service, err });
return; return;
}; };
defer alloc.free(result.stderr); defer alloc.free(result.stderr);
defer alloc.free(result.stdout); defer alloc.free(result.stdout);
if (result.term.Exited != 0) { if (result.term.Exited != 0) {
std.log.warn("systemctl failed: {s}", .{result.stderr});
return; return;
} }
} }
pub fn deactivateScheduler(alloc: std.mem.Allocator) ScxError!void { pub fn deactivateScheduler(alloc: std.mem.Allocator) ScxError!void {
runSystemCtl(alloc, "start", "ananicy-cpp");
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE); var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
try dbus_conn.callMethod("StopScheduler", &[_][]const u8{}); try dbus_conn.callMethod("StopScheduler", &[_][]const u8{});
runSystemCtl(alloc, "start", "ananicy-cpp");
} }
pub fn restorePreviousState(alloc: std.mem.Allocator) void { pub fn restorePreviousState(alloc: std.mem.Allocator) void {

View File

@ -20,7 +20,10 @@ pub fn loadConf(comptime T: type, allocator: std.mem.Allocator, path: []const u8
if (bytes_read != size) return error.UnexpectedEOF; if (bytes_read != size) return error.UnexpectedEOF;
var parser = Parser(T).init(allocator, buffer); var parser = Parser(T).init(allocator, buffer);
return try parser.parse(); return parser.parse() catch |err| {
std.log.err("Failed to parse config file '{s}': {s}", .{ path, @errorName(err) });
return err;
};
} }
pub fn loadConfDir(comptime T: type, allocator: std.mem.Allocator, dir_path: []const u8) !std.ArrayList(T) { pub fn loadConfDir(comptime T: type, allocator: std.mem.Allocator, dir_path: []const u8) !std.ArrayList(T) {
@ -43,7 +46,10 @@ pub fn loadConfDir(comptime T: type, allocator: std.mem.Allocator, dir_path: []c
defer allocator.free(content); defer allocator.free(content);
var parser = Parser(T).init(allocator, content); var parser = Parser(T).init(allocator, content);
const parsed = try parser.parse(); const parsed = parser.parse() catch |err| {
std.log.err("Failed to parse config file '{s}': {s}", .{ path, @errorName(err) });
return err;
};
try result.append(parsed); try result.append(parsed);
} }
} }

View File

@ -81,17 +81,33 @@ pub fn Parser(comptime T: type) type {
return error.InvalidSyntax; return error.InvalidSyntax;
self.pos += 1; self.pos += 1;
const start = self.pos; var escaped = false;
var result = std.ArrayList(u8).init(self.allocator);
errdefer result.deinit();
while (self.pos < self.content.len) : (self.pos += 1) { while (self.pos < self.content.len) : (self.pos += 1) {
switch (self.content[self.pos]) { const c = self.content[self.pos];
if (escaped) {
switch (c) {
'"' => try result.append('"'),
'\\' => try result.append('\\'),
'n' => try result.append('\n'),
'r' => try result.append('\r'),
't' => try result.append('\t'),
else => return error.InvalidSyntax,
}
escaped = false;
continue;
}
switch (c) {
'"' => { '"' => {
const str = try self.allocator.dupe(u8, self.content[start..self.pos]);
self.pos += 1; self.pos += 1;
return str; return result.toOwnedSlice();
}, },
'\\' => return error.InvalidSyntax, '\\' => {
else => {}, escaped = true;
},
else => try result.append(c),
} }
} }
return error.UnterminatedString; return error.UnterminatedString;
@ -162,7 +178,10 @@ pub fn Parser(comptime T: type) type {
return values.toOwnedSlice(); return values.toOwnedSlice();
} }
const str = try self.parseString(); const str = self.parseString() catch |err| {
std.log.err("Failed to parse string at position {}: {s}", .{ self.pos, @errorName(err) });
return err;
};
try values.append(str); try values.append(str);
self.skipWhitespace(); self.skipWhitespace();
@ -174,8 +193,10 @@ pub fn Parser(comptime T: type) type {
self.pos += 1; self.pos += 1;
return values.toOwnedSlice(); return values.toOwnedSlice();
} }
std.log.err("Expected ',' or ']' but found '{c}' at position {}", .{ self.content[self.pos], self.pos });
return error.InvalidSyntax; return error.InvalidSyntax;
} }
std.log.err("Unterminated array at position {}", .{self.pos});
return error.InvalidSyntax; return error.InvalidSyntax;
} }
@ -258,11 +279,16 @@ pub fn Parser(comptime T: type) type {
@field(result, field.name) = @intCast(try self.parseNumber()); @field(result, field.name) = @intCast(try self.parseNumber());
}, },
.array => |array_info| { .array => |array_info| {
const array = try self.parseArray(); switch (@typeInfo(array_info.child)) {
if (array.len > array_info.len) return error.InvalidSyntax; i64 => {
@field(result, field.name) = undefined; const array = try self.parseArray();
var dest = &@field(result, field.name); if (array.len > array_info.len) return error.InvalidSyntax;
@memcpy(dest[0..array.len], array); @field(result, field.name) = undefined;
var dest = &@field(result, field.name);
@memcpy(dest[0..array.len], array);
},
else => return error.InvalidSyntax,
}
}, },
.@"enum" => { .@"enum" => {
const ident = try self.parseIdentifier(); const ident = try self.parseIdentifier();
@ -288,18 +314,22 @@ pub fn Parser(comptime T: type) type {
} }
}, },
.pointer => |ptr_info| { .pointer => |ptr_info| {
if (ptr_info.size != .Slice) return error.InvalidSyntax; if (ptr_info.size != .slice) {
switch (ptr_info.child) { return error.InvalidSyntax;
}
if (ptr_info.child == []const u8) {
@field(result, field.name) = try self.parseStringArray();
} else switch (ptr_info.child) {
u8 => { u8 => {
@field(result, field.name) = try self.parseString(); @field(result, field.name) = try self.parseString();
}, },
i64 => { i64 => {
@field(result, field.name) = try self.parseArray(); @field(result, field.name) = try self.parseArray();
}, },
[]const u8 => { else => {
@field(result, field.name) = try self.parseStringArray(); return error.InvalidSyntax;
}, },
else => return error.InvalidSyntax,
} }
}, },
else => return error.InvalidSyntax, else => return error.InvalidSyntax,

View File

@ -15,7 +15,7 @@ pub const Daemon = struct {
profile_manager: ProfileManager, profile_manager: ProfileManager,
oneshot: bool, oneshot: bool,
known_pids: ?std.AutoHashMap(u32, *const Profile), known_pids: ?std.AutoHashMap(u32, *const Profile),
power_profiles: *PowerProfiles, power_profiles: ?*PowerProfiles,
performance_mode: bool, performance_mode: bool,
last_profiles_check: i128, last_profiles_check: i128,
last_config_check: i128, last_config_check: i128,
@ -30,13 +30,17 @@ pub const Daemon = struct {
const system_conf_path_owned = try allocator.dupe(u8, system_conf_path); const system_conf_path_owned = try allocator.dupe(u8, system_conf_path);
errdefer allocator.free(system_conf_path_owned); errdefer allocator.free(system_conf_path_owned);
const config = try Config.load(allocator, config_path, system_conf_path); var config = try Config.load(allocator, config_path, system_conf_path);
const power_profiles = try PowerProfiles.init(allocator, config); errdefer config.deinit();
errdefer power_profiles.deinit();
const performance_mode = power_profiles.isPerformanceAvailable(); const power_profiles = try PowerProfiles.init(allocator, config);
if (performance_mode) { var performance_mode = false;
std.log.info("Performance profile available - power profile management enabled", .{});
if (power_profiles) |pp| {
performance_mode = pp.isPerformanceAvailable();
if (performance_mode) {
std.log.info("Performance profile available - power profile management enabled", .{});
}
} }
var profile_manager = ProfileManager.init(allocator, power_profiles, config); var profile_manager = ProfileManager.init(allocator, power_profiles, config);
@ -66,7 +70,9 @@ pub const Daemon = struct {
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
scx_scheds.deinit(); scx_scheds.deinit();
self.profile_manager.deinit(); self.profile_manager.deinit();
self.power_profiles.deinit(); if (self.power_profiles) |pp| {
pp.*.deinit();
}
if (self.known_pids) |*map| { if (self.known_pids) |*map| {
map.deinit(); map.deinit();
} }
@ -86,7 +92,9 @@ pub const Daemon = struct {
} }
self.profile_manager.deinit(); self.profile_manager.deinit();
self.power_profiles.deinit(); if (self.power_profiles) |pp| {
pp.*.deinit();
}
self.config.deinit(); self.config.deinit();
const config = try Config.load(self.allocator, self.config_path, self.system_conf_path); const config = try Config.load(self.allocator, self.config_path, self.system_conf_path);
@ -96,7 +104,7 @@ pub const Daemon = struct {
self.config = config; self.config = config;
self.power_profiles = power_profiles; self.power_profiles = power_profiles;
self.performance_mode = self.power_profiles.isPerformanceAvailable(); self.performance_mode = if (power_profiles) |pp| pp.isPerformanceAvailable() else false;
self.profile_manager = profile_manager; self.profile_manager = profile_manager;
} }

View File

@ -1,6 +1,6 @@
const std = @import("std"); const std = @import("std");
const Daemon = @import("daemon.zig").Daemon; const Daemon = @import("daemon.zig").Daemon;
const builtin = @import("builtin");
pub const std_options = std.Options{ pub const std_options = std.Options{
.log_level = .debug, .log_level = .debug,
.log_scope_levels = &[_]std.log.ScopeLevel{ .log_scope_levels = &[_]std.log.ScopeLevel{
@ -29,19 +29,25 @@ const AllocTracker = struct {
const config_path: []const u8 = "/etc/falcond/config.conf"; const config_path: []const u8 = "/etc/falcond/config.conf";
const system_conf_path: []const u8 = "/usr/share/falcond/system.conf"; const system_conf_path: []const u8 = "/usr/share/falcond/system.conf";
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 { fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
var t: *AllocTracker = @ptrCast(@alignCast(ctx)); var t: *AllocTracker = @ptrCast(@alignCast(ctx));
t.trackAlloc(); t.trackAlloc();
return gpa_vtable.alloc(gpa_ptr, len, ptr_align, ret_addr); return gpa_vtable.alloc(gpa_ptr, len, ptr_align, ret_addr);
} }
fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool { fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
var t: *AllocTracker = @ptrCast(@alignCast(ctx)); var t: *AllocTracker = @ptrCast(@alignCast(ctx));
t.trackResize(); t.trackResize();
return gpa_vtable.resize(gpa_ptr, buf, log2_buf_align, new_len, ret_addr); return gpa_vtable.resize(gpa_ptr, buf, log2_buf_align, new_len, ret_addr);
} }
fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void { fn remap(ctx: *anyopaque, buf: []u8, log2_buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
var t: *AllocTracker = @ptrCast(@alignCast(ctx));
t.trackResize();
return gpa_vtable.remap(gpa_ptr, buf, log2_buf_align, new_len, ret_addr);
}
fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: std.mem.Alignment, ret_addr: usize) void {
var t: *AllocTracker = @ptrCast(@alignCast(ctx)); var t: *AllocTracker = @ptrCast(@alignCast(ctx));
t.trackDealloc(); t.trackDealloc();
gpa_vtable.free(gpa_ptr, buf, log2_buf_align, ret_addr); gpa_vtable.free(gpa_ptr, buf, log2_buf_align, ret_addr);
@ -49,16 +55,20 @@ fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void {
var gpa_vtable: *const std.mem.Allocator.VTable = undefined; var gpa_vtable: *const std.mem.Allocator.VTable = undefined;
var gpa_ptr: *anyopaque = undefined; var gpa_ptr: *anyopaque = undefined;
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
pub fn main() !void { pub fn main() !void {
std.log.info("Starting falcond...", .{}); std.log.info("Starting falcond...", .{});
var allocator: std.mem.Allocator = undefined;
var tracker = AllocTracker{}; var tracker = AllocTracker{};
var gpa = std.heap.GeneralPurposeAllocator(.{ const gpa, const is_debug = gpa: {
.verbose_log = false, break :gpa switch (builtin.mode) {
.enable_memory_limit = true, .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
}){}; .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
defer { };
const leaked = gpa.deinit(); };
allocator = gpa;
defer if (is_debug) {
const leaked = debug_allocator.deinit();
if (leaked == .leak) { if (leaked == .leak) {
std.log.err("Memory leaks detected!", .{}); std.log.err("Memory leaks detected!", .{});
} }
@ -67,18 +77,20 @@ pub fn main() !void {
tracker.deallocs, tracker.deallocs,
tracker.resizes, tracker.resizes,
}); });
}
gpa_vtable = gpa.allocator().vtable;
gpa_ptr = gpa.allocator().ptr;
const allocator = std.mem.Allocator{
.ptr = &tracker,
.vtable = &std.mem.Allocator.VTable{
.alloc = alloc,
.resize = resize,
.free = free,
},
}; };
if (is_debug) {
gpa_vtable = gpa.vtable;
gpa_ptr = gpa.ptr;
allocator = std.mem.Allocator{
.ptr = &tracker,
.vtable = &std.mem.Allocator.VTable{
.alloc = alloc,
.resize = resize,
.free = free,
.remap = remap,
},
};
}
const args = try std.process.argsAlloc(allocator); const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args); defer std.process.argsFree(allocator, args);

View File

@ -14,10 +14,10 @@ pub const ProfileManager = struct {
proton_profile: ?*const Profile, proton_profile: ?*const Profile,
active_profile: ?*const Profile = null, active_profile: ?*const Profile = null,
queued_profiles: std.ArrayList(*const Profile), queued_profiles: std.ArrayList(*const Profile),
power_profiles: *PowerProfiles, power_profiles: ?*PowerProfiles,
config: Config, config: Config,
pub fn init(allocator: std.mem.Allocator, power_profiles: *PowerProfiles, config: Config) ProfileManager { pub fn init(allocator: std.mem.Allocator, power_profiles: ?*PowerProfiles, config: Config) ProfileManager {
return .{ return .{
.allocator = allocator, .allocator = allocator,
.profiles = std.ArrayList(Profile).init(allocator), .profiles = std.ArrayList(Profile).init(allocator),
@ -47,9 +47,9 @@ pub const ProfileManager = struct {
std.log.info("Activating profile: {s}", .{profile.name}); std.log.info("Activating profile: {s}", .{profile.name});
self.active_profile = profile; self.active_profile = profile;
if (profile.performance_mode and self.power_profiles.isPerformanceAvailable()) { if (profile.performance_mode and self.power_profiles != null and self.power_profiles.?.isPerformanceAvailable()) {
std.log.info("Enabling performance mode for profile: {s}", .{profile.name}); std.log.info("Enabling performance mode for profile: {s}", .{profile.name});
self.power_profiles.enablePerformanceMode(); self.power_profiles.?.enablePerformanceMode();
} }
const effective_mode = if (self.config.vcache_mode != .none) const effective_mode = if (self.config.vcache_mode != .none)
@ -80,9 +80,9 @@ pub const ProfileManager = struct {
if (active == profile) { if (active == profile) {
std.log.info("Deactivating profile: {s}", .{profile.name}); std.log.info("Deactivating profile: {s}", .{profile.name});
if (profile.performance_mode and self.power_profiles.isPerformanceAvailable()) { if (profile.performance_mode and self.power_profiles != null and self.power_profiles.?.isPerformanceAvailable()) {
std.log.info("Disabling performance mode for profile: {s}", .{profile.name}); std.log.info("Disabling performance mode for profile: {s}", .{profile.name});
self.power_profiles.disablePerformanceMode(); self.power_profiles.?.disablePerformanceMode();
} }
vcache_setting.applyVCacheMode(.none); vcache_setting.applyVCacheMode(.none);
@ -102,9 +102,9 @@ pub const ProfileManager = struct {
if (active == profile) { if (active == profile) {
std.log.info("Unloading profile: {s}", .{profile.name}); std.log.info("Unloading profile: {s}", .{profile.name});
if (profile.performance_mode and self.power_profiles.isPerformanceAvailable()) { if (profile.performance_mode and self.power_profiles != null and self.power_profiles.?.isPerformanceAvailable()) {
std.log.info("Disabling performance mode for profile: {s}", .{profile.name}); std.log.info("Disabling performance mode for profile: {s}", .{profile.name});
self.power_profiles.disablePerformanceMode(); self.power_profiles.?.disablePerformanceMode();
} }
vcache_setting.applyVCacheMode(.none); vcache_setting.applyVCacheMode(.none);

View File

@ -2,7 +2,7 @@
set -e set -e
VERSION="1.0.7" VERSION="1.1.2"
source ./pika-build-config.sh source ./pika-build-config.sh