When running wine games, there's good chance the executable is already
gone when GameMode decided to add it to the list. Let's silently bail
out here. It probably grabs a non-matching PID anyway in this moment.
Signed-off-by: Kai Krakow <kai@kaishome.de>
During finding the cause of an issue, we discovered bad interactions
with other resource management daemon that may be running at the same
time and may interfere with GameMode's ability to properly set
scheduling parameters of the process. Usually, this results in EPERM
(instead of EINVAL for lacking kernel support). It also means that the
other daemon is leaking wrong scheduling settings into the games with
negative performance impacts (quite the opposite of what the user wanted
to achieve). So we should give some valuable hints.
This commit improves logging by detecting the error situation and giving
context-based hints on what to do next. To keep logging verbosity low,
the hint is only logged once per error code.
Closes: https://github.com/FeralInteractive/gamemode/issues/68
Signed-off-by: Kai Krakow <kai@kaishome.de>
This commit allows changing the io priority of the client to a value
specified in the configuration file. That can possibly reduce lags or
latency when a game has to load assets on demand and you have background
IO activity running (or other concurrent IO).
Signed-off-by: Kai Krakow <kai@kaishome.de>
Fix what was reported by valgrind:
```
==8458==
==8458== HEAP SUMMARY:
==8458== in use at exit: 11,677 bytes in 27 blocks
==8458== total heap usage: 768 allocs, 741 frees, 397,008 bytes
allocated
==8458==
==8458== Searching for pointers to 27 not-freed blocks
==8458== Checked 206,624 bytes
==8458==
==8458== LEAK SUMMARY:
==8458== definitely lost: 0 bytes in 0 blocks
==8458== indirectly lost: 0 bytes in 0 blocks
==8458== possibly lost: 0 bytes in 0 blocks
==8458== still reachable: 11,677 bytes in 27 blocks
==8458== suppressed: 0 bytes in 0 blocks
==8458== Reachable blocks (those to which a pointer was found) are not
shown.
==8458== To see them, rerun with: --leak-check=full
--show-leak-kinds=all
==8458==
==8458== Use --track-origins=yes to see where uninitialised values come
from
==8458== ERROR SUMMARY: 200 errors from 10 contexts (suppressed: 0 from
0)
==8458==
==8458== 20 errors in context 1 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x10BD18: game_mode_apply_scheduler (gamemode.c:237)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 2 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x4A0C4DC: strcmp (vg_replace_strmem.c:846)
==8458== by 0x10BD15: game_mode_apply_scheduler (gamemode.c:237)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 3 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x10BCDB: game_mode_apply_scheduler (gamemode.c:232)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 4 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x4A0C4DC: strcmp (vg_replace_strmem.c:846)
==8458== by 0x10BCD8: game_mode_apply_scheduler (gamemode.c:232)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 5 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x53AB44C: vfprintf (vfprintf.c:1642)
==8458== by 0x53B2C5D: printf (printf.c:33)
==8458== by 0x10BBA2: game_mode_apply_scheduler (gamemode.c:201)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 6 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x53AA9DA: vfprintf (vfprintf.c:1642)
==8458== by 0x53B2C5D: printf (printf.c:33)
==8458== by 0x10BBA2: game_mode_apply_scheduler (gamemode.c:201)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 7 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x53A6FF5: _itoa_word (_itoa.c:179)
==8458== by 0x53AA922: vfprintf (vfprintf.c:1642)
==8458== by 0x53B2C5D: printf (printf.c:33)
==8458== by 0x10BBA2: game_mode_apply_scheduler (gamemode.c:201)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 8 of 10:
==8458== Use of uninitialised value of size 8
==8458== at 0x53A6FE8: _itoa_word (_itoa.c:179)
==8458== by 0x53AA922: vfprintf (vfprintf.c:1642)
==8458== by 0x53B2C5D: printf (printf.c:33)
==8458== by 0x10BBA2: game_mode_apply_scheduler (gamemode.c:201)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 9 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x53AB20A: vfprintf (vfprintf.c:1642)
==8458== by 0x53B2C5D: printf (printf.c:33)
==8458== by 0x10BBA2: game_mode_apply_scheduler (gamemode.c:201)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458==
==8458== 20 errors in context 10 of 10:
==8458== Conditional jump or move depends on uninitialised value(s)
==8458== at 0x10BB44: game_mode_apply_scheduler (gamemode.c:200)
==8458== by 0x10C7D7: game_mode_context_register (gamemode.c:445)
==8458== by 0x10D4E6: method_register_game (dbus_messaging.c:79)
==8458== by 0x4CB795B: method_callbacks_run (bus-objects.c:404)
==8458== by 0x4CB795B: object_find_and_run (bus-objects.c:1262)
==8458== by 0x4CB8D38: bus_process_object (bus-objects.c:1378)
==8458== by 0x4CC9251: process_message (sd-bus.c:2663)
==8458== by 0x4CC9251: process_running (sd-bus.c:2705)
==8458== by 0x4CC9251: bus_process_internal (sd-bus.c:2924)
==8458== by 0x10D8B9: game_mode_context_loop (dbus_messaging.c:173)
==8458== by 0x10B7C4: main (main.c:186)
==8458==
==8458== ERROR SUMMARY: 200 errors from 10 contexts (suppressed: 0 from
0)
```
Signed-off-by: Kai Krakow <kai@kaishome.de>
We should not leak `SCHED_ISO` into children processes. This is obviously
a no-op if launchers use the `LD_PRELOAD` method because every child would
also preload the gamemode library (except they patch the environment
before forking).
But for games supporting gamemode natively, this prevents leaking the
scheduler settings into child processes which is important because it
children may create high CPU usage which counterfeits the original idea
of this.
Apparently, this won't work for the nice value except we would intercept
the libc forking functions (which would be possible but not very
transparent and prone to compatibility problems). However, if we
implemented it, it shouldn't be part of this commit anyway.
Signed-off-by: Kai Krakow <kai@kaishome.de>
Out of the box on most distros, both of these steps will fail (renicing
requires permission which may need adjustment to limits.conf, and the
upstream kernel does not support SCHED_ISO).
Explicitly state this in the error messages to hopefully reduce user
confusion as to why these might be failing.
When there are not valid standard file descriptors then strange things
can happen. When new file descriptors are opened, they will take the
place of the former standard file descriptors and when e.g. somebody
calls printf() they will write to some file descriptor that is not
prepared for it. This would already happen during PLOG_MSG() in
gamemoded.
Actually this also causes a SIGABRT when calling gamemoded like this:
```bash
gamemoded -d
```
This is due to a bug [1] in systemd that causes an assertion to be
triggered. This shows that file descriptor zero is in this case being
replaced by a UNIX domain socket representing the connection to the
D-Bus session bus.
Therefore instead of just closing the standard file descriptors, replace
them by appropriate file descriptors refering to /dev/null.
[1]: https://github.com/systemd/systemd/issues/8080
The reason for setting umask in a daemon is to get a defined umask value
instead of whatever the calling user had configured. A umask of zero is
dangerous, however, because it can easily cause world-readable and
world-writeable files when oblivious code is involved that specified
0777 during open() calls, wanting to grant the user full control of the
resulting file mode.
Currently the daemon shouldn't be creating any new files so this is not
a matter. This could change in the future, however.
This commit applies the configured nice value to the client. It accepts
values from 1 to 20, the negated value is applied as a nice value.
Negation was chosen due to limits of the configuration parser. Since low
priority values (0 to 19) make no sense in the scope of GameMode, this
is a safe approach.
Signed-off-by: Kai Krakow <kai@kaishome.de>
This commit adds configuration support for the renice value and amends
documentation and examples. This commit by itself does nothing, the
following commit is needed to actually apply the new settings.
Signed-off-by: Kai Krakow <kai@kaishome.de>
This commit adds a simple heuristic to auto detect whether to use
SCHED_ISO or not. It does this by looking at the number of cores:
According to some reports by users of SCHED_ISO and an explanation by
Con Kolivas, games running busy loops and running on too few cores might
start fighting with scheduling of the graphic driver, leading to
priority inversion. This results in actually lower performance.
So let's only enable SCHED_ISO on at least four cores.
The user can still force SCHED_ISO on or off by setting the
configuration value. All other values (or none) will apply heuristics.
Signed-off-by: Kai Krakow <kai@kaishome.de>
This adds support for a new configuration option "softrealtime" to be
read from the general section. This commit alone does nothing, the
following commit adds actually making use of the value.
Signed-off-by: Kai Krakow <kai@kaishome.de>
Kernels that support SCHED_ISO scheduling policy can give processes soft
real time support. This improves latency without compromising system
stability. See https://lwn.net/Articles/720227/.
This commit adds support for setting this policy with a safe fall back if
kernel support is lacking by just ignoring the error condition.
Additionally, it also tries to raise the nice priority of the game to -4
to give it a slight IO and CPU priority over other background processes.
This needs PAM adjustments to allow users raising priority to certain
levels. If it doesn't work, the fall back strategy is also ignoring the
error condition. See /etc/security/limits.conf.
Kernels that currently support SCHED_ISO include kernels with Con
Kolivas MuQSS patchset (likely the CK patchset). This patchset is
generally recommended for desktop machines but usually not found in
standard distribution kernels due to lack of widespread stability tests.
Signed-off-by: Kai Krakow <kai@kaishome.de>
Rather than when gamemoded is started. As see in Issue #52.
A small refactor here to ensure things stay consistent, especially
to stop storing a pointer from get_gov_state that might have had
it's memory changed at some point, confusingly.
gamemoded will now load and merge settings from the following locations - arrays will merge and single settings will overwrite.
1. /usr/share/gamemode/
2. /etc/
3. $XDG_CONFIG_HOME or $HOME/.config/
4. $PWD
This allows the client to query the daemon about the status of gamemode.
Returns the following:
0 if gamemode is inactive
1 if gamemode is active
2 if gamemode is active and this client is registered
-1 if the query failed
Passing -s to gamemoded will simply query and print the current status.
Allows for more comprehensive testing when using 'gamemoded -r' as well as more reactionary program behaviour
This allows for easy requesting of gamemode and pausing for any signal
Primarily useful for simple tests, but also functionally to allow enterning and exiting gamemode manually
A much requested feature, this allows for providing custom scripts in the config file. An example in the man page is below and would trigger both a system notification, and allow control over a background crypto mining script automatically in gamemode.
[custom]
; Custom scripts (executed using the shell) when gamemode starts and ends
start=notify-send "GameMode started"
/home/me/bin/stop_ethmining.sh
end=notify-send "GameMode ended"
/home/me/bin/start_ethmining.sh
Scripts are run with system() and do not have any special privilages, as with the rest of the daemon, custom scripts that require root will need their own permissions set up externally.
This commit also renames two defines as they needed to be moved to the public interface.
This moves adding to lists and getting long values into resuable functions.
It also fixes a couple other issues in the process:
1. Logging now goes to the error channel and is labelled
2. >Max length lines are properly handled, although this should never happen since inih has a 200 limit
* Minor C cleanup
- some symbols can be made static:
1. set_gov_state
2. everything in gamemode_client.h
- daemonize() can also take a const char*, since the name is only
passed to printf() or syslog()
- prevent shadowing of variables
- use explicit (void) as parameter-list more consistently
- use some more const.
Move cast to more appropriate place and document that execv() behaves
as if args where of type const *char and we trust on that.
- example: Just use main(void), which is also an acceptable ISO-C decl
- example: Use stderr for errors
* Fix -Wold-style-declaration issue
The systemd bus headers aren't setting a POSIX compliance level so we
define the catch-all to ensure the build doesn't error out, as seen with
issue #3. This is due to the reliance of `siginfo_t`, which requires a
minimum POSIX level of `199309L` when using glibc.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
Checks for a gamemode.ini in /usr/share/gamemode/ (or in the cwd for debugging)
Currently allows for blacklisting and whitelisting clients based on rudimentary needle-haystack executable name checks
See the example/gamemode.ini file for expected syntax
Using the BSD licensed inih library (with additional meson.build file)
The spec says the results are undefined if an uninitialised
read-write lock is used without being initialised, though
evidently things were working out alright
This exposed a bunch of issues that needed dealing with to ensure the
code is clean and sane. Notably the dlopen/dlsym routine has been altered
to closer match the LSI approach of safe symbol binding, by not attempting
to directly cast the result of a dlsym operation. Instead, if we succeed
in getting the dlsym() pointer, we memcpy this to the target and ensure
we have the correct constraints.
Note that in sanitizing the log helpers, I opted to remove the varargs
ability from FATAL_ERRNO given this is used exactly like perror() and
there are no examples currently using varargs with this in the tree.
This allowed me to keep the log helpers as macros and not have to implement
wrapper functions.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
Primarily we convert the service into a thread safe one that isn't reliant
on signaling for control flow, eliminating data race conditions. We also
enable interleaving by separating game mode pivoting from explicit client
registration.
The static pid list is now converted into a dynamic list that is OOM safe
to store all registered clients (with a reasonable upper limit of 256 clients)
to better handle cases where LD_PRELOAD is used for a large process group.
Additionally we begin storing some metadata on the connected clients such
as their executable path, which will enable us to perform some basic
whitelisting in future.
The cpugovctl binary is now moved into the libexecdir as an explicit helper
of the D-BUS service, using the shared library to merge some code back into
the daemon. This saves having to execute a process to query the state of the
governors, as we don't need a privileged client to do this.
In order to sanely set the governors, we require that the binary is running
as euid 0, and execute this using `pkexec`. A PolKit policy definition is
provided which allows active/logged in users to execute this helper through
a path whitelist. As such we can convert the daemon into user-mode only, with
the privileged helper being dispatched exclusively via polkit. This removes
the need for a setuid helper or having a system mode daemon.
Lastly we clean up the codebase a bit to be consistent with modern C code
conventions, using pragmas where available. The library component still uses
the older ifdef approach to support older compilers, but the daemon portion
uses the directive to simplify intent and speed up compilation. Additionally
we move all comments to C style comments for consistency, instead of mixing
in C++ style single line comments, in order to establish a formal coding
style.
The net result is a more robust service which can be D-BUS activated when
clients need it, that can perform scaling automatically without harassing
the user with authentication popups.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
Defining _GNU_SOURCE will set the appropriate POSIX levels required to
use the `_MAX`, `kill`, `realpath`, etc, set of POSIX family functions
and defines.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
This also explicitly constructs reusable library components to make it
easier to extend the project over time. Notably we make less assumptions
about the host system and use pkgconfig where appropriate to give us
system details, such as the systemd system unit directory for packaging.
This can be overriden with the meson option.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>