Compare commits

...

205 Commits
1.5 ... master

Author SHA1 Message Date
Ehren Bendler
af07e169d5 fix static analyzer error that is blocking CI 2025-05-02 08:30:35 +01:00
Ehren Bendler
6e5c950141 fix clang-format error that is blocking CI 2025-05-01 15:35:50 +01:00
MithicSpirit
499af4c7bb Prevent platform profile error on unsupported systems
If a system does not support setting the platform profile (i.e., does
not have the file /sys/firmware/acpi/platform_profile), then everything
that interacts with it is skipped to prevent errors. This situation is
more common than I expected.[1]

[1] https://github.com/FeralInteractive/gamemode/issues/524
2025-04-30 19:12:51 +01:00
Ehren Bendler
5f691c3171 Potential fix for code scanning alert no. 2: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-04-30 19:11:59 +01:00
Ehren Bendler
f5fbdcf014 Take advanted of imminent EOL of Ubuntu 20 to update GH runner config. Also make a pass over the development section of the README to reflect current reality. Update inih to r60. Update meson settings to eliminate deprecated functions. Set minimum Meson version to 1.3.1 to match Ubuntu 24 and OpenSUSE Leap 15.6. 2025-04-30 19:11:59 +01:00
Richard Martin
7c49d6e1db docs: a change was made to remove the supported games list, but it was still referenced from one location in the README.md 2025-03-14 10:23:06 +00:00
MithicSpirit
d84c5ded1c Extract split lock mitigation into common file
This reduces duplication of the split lock mitigation path.
Additionally, it allows extending the functionality of procsysctl into
also getting the split lock mitigation state.
2025-02-24 15:43:15 +00:00
MithicSpirit
2d71be6a94 Save all state before any of it is changed
The platform profile may restrict what values the governor can take, so
we need to save all state first to ensure the restored state is correct.
Then, the platform profile is the first thing we set (and restore) to
ensure all other changes are made to the best of our ability.
2025-02-24 15:43:15 +00:00
MithicSpirit
1e3e55c5d8 Add tests for platform profile
Note that the platform profile feature is considered required. Maybe
this shouldn't be the case, as I'm not sure how standard this feature is
yet.
2025-02-24 15:43:15 +00:00
MithicSpirit
e75f2c3942 Support setting the platform profile
The platform profile lives in /sys/firmware/acpi/platform_profile. The
desiredprof and defaultprof options correspond to the values for the
platform profile set when gamescope begins and ends, respectively.

HACK: The platform profile may restrict what values the governor can
take, so we choose to set the governor before the platform profile in
order to store the correct default governor, and restore the platform
profile before the governor. This is done to maximize correctness after
restoration, but it can cause issues if the previous platform profile
restricts the governor.

TODO: Save all the state we care about before any of it is changed. In
thsi case, we should set (and restore) the platform profile before the
governor.
2025-02-24 15:43:15 +00:00
Ahsan Fayaz
81d26c79b4 Remove list of supported apps
GameMode integration is widespread nowadays, and it works out of the box
for the most common use cases.

There is no longer a need for us to maintain a list of supported games
and applications, allowing us to streamline the readme file.
2025-02-24 15:15:17 +00:00
Ahsan Fayaz
5b3b4ae638 Add "GameMode contributors" to copyright notice
Attribute the contributions made by members of the community, but avoid
unweildy and inconsistent copyright notices at the top of each file.

Existing contributor copyright notices have been left as-is.
2025-02-24 15:14:21 +00:00
Henrik Holst
1d9c1e6a72
Use better kernel api for P/E-cores (#515)
There's a proper kernel way to check for the presence of P- and E-cores so we don't have to check the frequency differences.

Kept the frequency check in case the kernel way does not exist on the users kernel but upped the fail-safe to 10% from 5% to close #498
2025-01-27 08:25:32 +00:00
Kaydax
2eecd513ac Update README.md
PolyMC seems to have been removed for the reason "PolyMC died" yet the project is still active. Requesting this be added back as it still supports gamemode even on the latest version
2025-01-13 11:43:31 +00:00
afayaz-feral
086b3fe5b4
Update build-and-test.yml
Update to upload-artifact@v4
2024-11-06 11:54:08 +00:00
Andrew Koroluk
2f69f7c023 Add suggestion message to failed CPU governernor test 2024-11-04 11:04:05 +00:00
Gilles Schintgen
c54d6d4243 fix project version in meson.build (1.8.1->1.8.2) 2024-09-03 12:53:21 +01:00
Ahsan Fayaz
715a1389b7 Update version to 1.8.2 2024-08-19 11:18:51 +01:00
Ahsan Fayaz
c0516059ed Update copyright year 2024-08-09 13:14:27 +01:00
Ahsan Fayaz
a875008d8e Standardise formatting of "Other Apps" section 2024-08-09 13:12:16 +01:00
Mahmut Sacit Meydanal
ec10c591ff
README: Add new gamemode extension link (#484)
* README: Add new gamemode extension link

Since the old extension is no longer maintained by the original author and has stopped working in the latest releases of GNOME, I recommend including my extension in the README.
2024-08-09 11:59:49 +01:00
mhmarf
fef538ba92
Update gamemode.ini (#490)
Clarify that user must be added to the gamemode group in order to make Core Parking work.
2024-08-01 14:22:16 +01:00
peelz
a2fe0108b5 Drop duplicate clang-format rule 2024-03-27 16:44:45 +00:00
peelz
c854772369 Fix idle inhibitor closing bus connection too early 2024-03-27 16:44:45 +00:00
Kira Bruneau
7d55ef856b add homepage url to metainfo 2024-03-27 10:22:58 +00:00
Daniel Martinez
1c293d7818 use posix basename to fix build on musl
glibc provides a nonstandard basename implementation,
this can be overriden and posix basename can be used
by includeing libgen.h, however musl only has posix
basename, and must always include libgen.h

In this particular case, it doesn't appear that using
the posix version of basename will cause any issues,
as it is simply being used to match a hardcoded config
file name.
2024-03-27 10:17:17 +00:00
Reilly Brogan
9646f2bd93 gamemodelist: Fix unreadable process maps
Systemd commit [bf1b9ae487b65b1cb1639b222724fab95e508cf5](bf1b9ae487) (present in Systemd v254 and later) effectively broke gamemodelist as the process map for the `systemd --user` process became unreadable. After this change gamemodelist would exit with an error like the following:

```
awk: fatal: cannot open file `/proc/2281/maps' for reading: Permission denied
```

To work around this let's add a hook to the awk statement to skip any files that can't be read.

Closes FeralInteractive/gamemode#456
2024-03-27 10:16:28 +00:00
Jrelvas
8e0a71a0bc README: Add Vinegar to apps list
Vinegar is a bootstrapper for Roblox Player and Roblox Studio. It uses gamemode for both by default.
2024-02-14 10:37:00 +00:00
Trial97
3fa41891cf Fixed crash if dbus is not accesible
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-02-14 10:33:22 +00:00
patatahooligan
8cea4c2418 Fix hybrid CPU core pinning
Previously the condition would always evaluate to `false`. With the fix
any core with >=95% of `max_freq` will be added to the set of cores to
keep.
2024-01-03 11:09:13 +00:00
Kostadin Shishmanov
4a82094c98 Fix build with musl
Fixes the following build failure:
In file included from ../common/common-cpu.c:32:
../common/common-cpu.h:44:9: error: unknown type name 'cpu_set_t'
   44 |         cpu_set_t *online;
      |         ^~~~~~~~~
../common/common-cpu.h:45:9: error: unknown type name 'cpu_set_t'
   45 |         cpu_set_t *to_keep;
      |         ^~~~~~~~~

Signed-off-by: Kostadin Shishmanov <kocelfc@tutanota.com>
2024-01-03 10:54:40 +00:00
Ahsan Fayaz
5180d89e66 Update version to 1.8.1 2023-12-12 16:02:49 +00:00
Vamist
e82e9dafd0 [fix] polkit parse error caused by an extra ) 2023-12-11 09:14:05 +00:00
Ahsan Fayaz
bb40374067 Update version number in README.md 2023-12-06 17:11:07 +00:00
Ahsan Fayaz
c3b9d4ce8b Update changelog and version for 1.8 2023-12-06 17:06:52 +00:00
Ahsan Fayaz
070216ad81 Update copyright year 2023-12-06 17:06:51 +00:00
Ahsan Fayaz
6b03bc9354 Sort list of supported apps 2023-12-06 17:06:51 +00:00
Ahsan Fayaz
5867058d19 Add packagecache to .gitignore 2023-12-06 17:06:51 +00:00
Ahsan Fayaz
49dc7ee49b Add --reset flag to meson subprojects update
This prevents us from exiting early, as meson will return non zero.
2023-12-06 17:06:51 +00:00
Henrik Holst
41191dc607 Update gamemode-context.c
check format wined again, looks like I'm really bad at this
2023-12-05 11:31:16 +00:00
Henrik Holst
22001c56b1 Update gamemode-config.c
fixed format to match the proper codingstyle
2023-12-05 11:31:16 +00:00
Henrik Holst
088639e8b2 Update gamemode-context.c
fixed format to match the proper codingstyle
2023-12-05 11:31:16 +00:00
Henrik Holst
9e21ac3924 Update gamemode-context.c
fixed format to match the proper codingstyle
2023-12-05 11:31:16 +00:00
Henrik Holst
e2e8e981a2 Update gamemode-config.c
fixed format to match the proper codingstyle
2023-12-05 11:31:16 +00:00
Henrik Holst
7389d5ed1d Update procsysctl.c
fixed format to match the proper codingstyle
2023-12-05 11:31:16 +00:00
Henrik Holst
162374c026 Create procsysctl.c
added a new pkexec helper to modify values in /proc/sys, currently only can do writes to /proc/sys/kernel/split_lock_mitigate
2023-12-05 11:31:16 +00:00
Henrik Holst
4700089325 Update meson.build
added build info for the new helper to modify /proc/sys values
2023-12-05 11:31:16 +00:00
Henrik Holst
c7a4572d73 Update gamemode.ini
added info about and default value for the setting to enable and disable split lock mitigation
2023-12-05 11:31:16 +00:00
Henrik Holst
7381d93c13 Update gamemode.rules.in
added policykit rules for a new helper to modify values in /proc/sys
2023-12-05 11:31:16 +00:00
Henrik Holst
97e588a5ad Update com.feralinteractive.GameMode.policy.in
added plociykit support for a new helper to write to /proc/sys
2023-12-05 11:31:16 +00:00
Henrik Holst
9a7ee00bbf Update gamemode-context.c
added support for disabling the kernel split lock mitigation when entering game mode
2023-12-05 11:31:16 +00:00
Henrik Holst
b2bd7e5919 Update gamemode-config.h
added config values for disabling split lock mitigation
2023-12-05 11:31:16 +00:00
Henrik Holst
97cee92d94 Update gamemode-config.c
added config value for disabling split lock mitigation
2023-12-05 11:31:16 +00:00
Ahsan Fayaz
b9e8448afe Replace crypto mining reference with folding@home
Based on the change originally made by @joelsgp in PR #417, but there
were issues with the original commit message:
- Used an emoji which may cause rendering issues in CLI tools
- Mentioned Bitcoin mining which is inaccurate and contained
unnecessary censorship
2023-12-04 18:12:22 +00:00
Ahsan Fayaz
6c197f9b70 Apply clang-format with the new include order 2023-12-04 17:00:20 +00:00
Ahsan Fayaz
485a7f920a Move includes to the file that actually uses them
This was spotted thanks to clang-format reordering the includes.
Even with the new config, it will include the header file for the
current source file first. Nevertheless, it is best to always include
header files where they are needed.
2023-12-04 16:48:07 +00:00
Ahsan Fayaz
f48c58f34c Update include order in .clang-format
We generally want system headers included above project ones.
In one case <linux/limits.h> needs to be above other system headers.
2023-12-04 16:42:15 +00:00
Ahsan Fayaz
fad889db45 Apply clang-format to CPU pinning and parking code
The original PR #416 failed the format check, but this wasn't apparent
until after merging.
2023-12-04 15:33:15 +00:00
begin-theadventure
775c93001c [gamemode.ini] Add renice information about the user group. 2023-12-04 14:57:22 +00:00
wantija
160bf91665 Update README.md
Added game mode support for cemu (1bcdb35e42)
2023-12-04 14:54:05 +00:00
Hugo Locurcio
138ad384e3 Set the default GPU device ID to 0
This avoids confusion with the default configuration value being
`gpu_device=0` (as commented out in the `gamemode.ini` example),
but the actual default value being `-1`.

The alternative is to document this behavior and set the commented
out value to `-1`, but this makes the setup procedure more complex.
The new default behavior suits the 90% use case where there's
only one dedicated GPU available.
2023-12-04 14:21:32 +00:00
Henrik Holst
9614ceca9b pin every thread of the process
walk through /proc/pid/task to make sure that we set the thread affinity for every single thread in the process
2023-12-04 14:18:38 +00:00
Henrik Holst
865ffb2588 reapply the core pinning from the reaper thread
Reapply the core pinning from the reaper thread to catch cases where the game launches threads after initial start
2023-12-04 14:18:38 +00:00
Henrik Holst
b83fb8f83e made pinning optionally silent
made core pinning optionally silent, used for when the reaper thread calls us repeatable so we don't create tons of unnecessary logs
2023-12-04 14:18:38 +00:00
Henrik Holst
e882505881 properly support config reloading 2023-12-04 14:18:38 +00:00
Henrik Holst
88a15aba86 properly support config reloading 2023-12-04 14:18:38 +00:00
Henrik Holst
9cb119be62 properly support config reloading 2023-12-04 14:18:38 +00:00
Henrik Holst
fd226e46ba added info about the parking/pinning capability 2023-12-04 14:18:38 +00:00
Henrik Holst
91eb57574c fixed double free on exit from non use
if cpu core parking/pinning was disabled by the logic then there would be a double free at exit
2023-12-04 14:18:38 +00:00
Henrik Holst
25a99af4a1 use define instead of value
use a define instead of values for the park_or_pin variable
2023-12-04 14:18:38 +00:00
Henrik Holst
740a82a761 use defines instead of value
use defines instead of values for the park_or_pin variable
2023-12-04 14:18:38 +00:00
Henrik Holst
173a8594b4 fixed more coding style errors 2023-12-04 14:18:38 +00:00
Henrik Holst
81dc3f95e5 fixed more coding style errors 2023-12-04 14:18:38 +00:00
Henrik Holst
912d4bdc19 fixed more coding style errors 2023-12-04 14:18:38 +00:00
Henrik Holst
85b9abd654 fixed more coding style errors 2023-12-04 14:18:38 +00:00
Henrik Holst
2f7075b9c6 fix for clang-format 2023-12-04 14:18:38 +00:00
Henrik Holst
2e26331d97 fix to match clang-format 2023-12-04 14:18:38 +00:00
Henrik Holst
303a5a9734 fix to match clang-format 2023-12-04 14:18:38 +00:00
Henrik Holst
a9042199c6 fixed to match clang-format 2023-12-04 14:18:38 +00:00
Henrik Holst
3cabf9859d added info about suport for Intel E- and P-cores 2023-12-04 14:18:38 +00:00
Henrik Holst
51ee251efb Added detection for big.LITTLE
Added detection for big.LITTLE aka cpu:s where not all cores have the same frequency like on Intel Alder Lake and newer. The current logic allows a 5% difference in the max frequency due to some reports that those cpu:s doesn't always give back the exact same value (possible due to boosting capability).
2023-12-04 14:18:38 +00:00
Henrik Holst
ba1c593d0e added build info for cpucorectl
added build info for the new cpucorectl utility to meson
2023-12-04 14:18:38 +00:00
Henrik Holst
7d00c1f0a4 added cpucorectl.c
Added a utility to enable and disable cpu cores (aka core parking) since this requires root privileges
2023-12-04 14:18:38 +00:00
Henrik Holst
c070604a22 added cpu core parking/pinning settings to gamemode.ini
added some info about the new cpu core parking and pinning settings in gamemode.ini
2023-12-04 14:18:38 +00:00
Henrik Holst
dc2f7bbcd0 added polkit rules for cpucorectl
added polkit rules so we can run the cpucorectl utility without being root
2023-12-04 14:18:38 +00:00
Henrik Holst
437a129900 added polkit policy for cpucorectl
added the polkit policy for the cpucorectl utility so we can call it without being root
2023-12-04 14:18:38 +00:00
Henrik Holst
4997adef8d build gamemode-cpu
Added build info for gamemade-cpu.c to meson
2023-12-04 14:18:38 +00:00
Henrik Holst
1b10b679cd added the cpu core parking/pinning definitions
added the cpu core parking/pinning definitions to gamemode.h
2023-12-04 14:18:38 +00:00
Henrik Holst
495a659895 Added gamemode-cpu.c
Added gamemode-cpu.c which contains the functions for cpu core parking and pinning
2023-12-04 14:18:38 +00:00
Henrik Holst
2dbd565340 call the cpu core parking/pinning from context
call the cpu core parking/pinning from gamemode-context.c
2023-12-04 14:18:38 +00:00
Henrik Holst
01024927d2 added cpu core parking/pinning settings
added cpu core parking/pinning settings to gamemode-config.h
2023-12-04 14:18:38 +00:00
Henrik Holst
32a0b5b636 added the cpu core parking/pinning settings
added the cpu core parking/pinning settings to gamemode-config.c
2023-12-04 14:18:38 +00:00
Henrik Holst
520dc85ac3 updated meson.build to build common-cpu
added build info for common-cpu to meson
2023-12-04 14:18:38 +00:00
Henrik Holst
82206534dc Added common-cpu.c and common-cpu.h
Added common files for the cpu core parking/pinning functionality
2023-12-04 14:18:38 +00:00
Ahsan Fayaz
1f28880509 Update copyright year and project list
PolyMC died and most of the active devs created a fork, Prism Launcher
2023-05-23 11:37:01 +01:00
8lendzior
8cfc345312 issue #384, #399
I have fixed the issues by implementing this change before running the script
2023-03-02 11:27:44 +00:00
Hugo Locurcio
7cf59587ce Document some configuration files not allowing [gpu] changes
This also reorders the list from highest to lowest priority,
which is generally more commonly used.
2023-03-02 11:25:52 +00:00
MrAkells
f5882d5158 Update README.md 2023-03-02 11:25:25 +00:00
James Le Cuirot
06f01938a9 Fix typo in gamemodelist man page 2022-10-04 15:44:23 +01:00
James Le Cuirot
71f4b875ce Only install gamemoderun if shared libgamemodeauto is built
Otherwise emit a warning.
2022-10-04 15:44:23 +01:00
James Le Cuirot
b103bfdd60 Don't force installation of static libgamemodeauto
Defining the library with `library` rather than `both_libraries` allows the user
to choose which type they want to install using `-Ddefault_library`.

Closes: https://github.com/FeralInteractive/gamemode/issues/287
2022-10-04 15:44:23 +01:00
ashuntu
4cffdef805 Removed blank line 2022-09-14 17:57:49 +01:00
ashuntu
3f969bbf1f Fix env edgecase 2022-09-14 17:57:49 +01:00
ashuntu
1ca2daf47f Make more readable 2022-09-14 17:57:49 +01:00
ashuntu
179d5432e4 Add snap support 2022-09-14 17:57:49 +01:00
Nyikos Zoltán
4934191b19 Fix building when pidfd_open is available
On glibc2.36 pidfd_open was made available, but it needs an include
2022-08-12 15:54:57 +01:00
Kira Bruneau
55b799e3df Add option to specific PAM limits.d directory 2022-08-05 11:35:24 +01:00
Kira Bruneau
337f1b8a8e Scope polkit actions to require the privileged group 2022-08-05 11:35:24 +01:00
Kira Bruneau
898ab01924 Organize data files that hook into other packages 2022-08-05 11:35:24 +01:00
Kira Bruneau
1e24067430 Add option to specify privileged gamemode group 2022-08-05 11:35:24 +01:00
Kira Bruneau
e34e9c5a43 Add options to disable installing systemd specific files 2022-08-05 11:35:24 +01:00
Kira Bruneau
aee9703872 Add option to set systemd sysuser dir 2022-08-05 11:35:24 +01:00
Ahsan Fayaz
4dc99dff76 Specify correct inih directory after update to r54 2022-07-21 15:24:10 +01:00
Ahsan Fayaz
5fff74598a Use inih r54 from wrapdb
Updated using the command 'meson subprojects update'.
2022-07-21 15:21:25 +01:00
Ahsan Fayaz
c3d638d7ce Update version to 1.7 2022-07-21 15:03:58 +01:00
Ahsan Fayaz
86591acdf2 Update version references to 1.7 2022-07-21 15:03:34 +01:00
Ahsan Fayaz
618466005e Add changes for 1.7 2022-07-21 15:03:05 +01:00
Ahsan Fayaz
f5a715e124 Add recent Feral titles to the list of games 2022-07-20 11:50:53 +01:00
Ahsan Fayaz
f498f2a20a Update copyright year to 2022 2022-07-20 11:48:56 +01:00
Stephan Lachnit
9b44fc6def ci: fix static analyser with assertions
Signed-off-by: Stephan Lachnit <stephanlachnit@debian.org>
2022-07-15 14:17:38 +01:00
Stephan Lachnit
b58b32072a meson: fix warning in run_command
Fixes this warning:
```
WARNING: You should add the boolean check kwarg to the run_command call.
         It currently defaults to false,
         but it will default to true in future releases of meson.
         See also: https://github.com/mesonbuild/meson/issues/9300
```

Signed-off-by: Stephan Lachnit <stephanlachnit@debian.org>
2022-07-15 14:16:09 +01:00
Alfred Persson Forsberg
f50e7fffe7 Fix build on musl libc
This simple patch includes signal.h in daemon/gamemode-context.c to fix building gamemode on musl
libc.

This has been tested Gentoo musl and Alpine (also Gentoo glibc to
ensure no multiple defined symbols/other errors for glibc).

> ../daemon/gamemode-context.c: In function 'game_mode_context_auto_expire':
> ../daemon/gamemode-context.c:421:29: error: implicit declaration of function 'kill' [-Werror=implicit-function-declaration]
>   421 |                         if (kill(client->pid, 0) != 0) {
>       |                             ^~~~
> ../daemon/gamemode-context.c:421:29: warning: nested extern declaration of 'kill' [-Wnested-externs]

Signed-off-by: Alfred Persson Forsberg <cat@catcream.org>
2022-07-15 14:14:16 +01:00
Issam Maghni
fac33baa09 Simplify the offload command
The POSIX env [1] supports multiple `name=value` operands.
So I merged them into one.

[1] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
2022-07-15 14:13:09 +01:00
Gregory keenan
e7dcf8792d Update README.md
Minor update on language
2022-07-15 14:12:14 +01:00
Gregory keenan
db1a0e3ace Update README.md
Added a section in the Build and Install section to test if its working correctly.
2022-07-15 14:12:14 +01:00
gouchi
01b849a518 README.md: add RetroArch to other apps section
[Source](https://github.com/libretro/RetroArch/pull/13339)
2022-07-15 14:11:23 +01:00
Sam Gleske
b8fa857b35 Fix GitHub Actions Static Analyzer Check
Dockerfile
----------

This dockerfile was used to emulate GitHub actions.

<details><summary>Dockerfile source (click to expand)</summary>

```dockerfile
FROM ubuntu:20.04

ENV DEBIAN_FRONTEND=noninteractive

RUN set -ex; \
apt-get update; \
apt-get install -y build-essential meson appstream clang clang-format clang-tools libdbus-1-dev libinih-dev libsystemd-dev git

RUN set -ex; \
yes | adduser ci-user

USER ci-user
```

</details>

Commands to reproduce
---------------------

I simulated GitHub actions with a local docker environment.

    docker build -t ci .

I ran the following commands to reproduce the issue so that I could bugfix the static analyzer script.

```bash
docker run -e CI=true --rm -v "$PWD:$PWD" -w "$PWD" --init ci ./scripts/static-analyser-check.sh
docker run -e CI=true --rm -v "$PWD:$PWD" -w "$PWD" --init ci ./bootstrap.sh -Dwith-examples=true
docker run --rm -v "$PWD:$PWD" -w "$PWD" --init ci meson test -C builddir
docker run --rm -v "$PWD:$PWD" -w "$PWD" --init ci dbus-run-session -- gamemode-simulate-game
docker run --rm -v "$PWD:$PWD" -w "$PWD" --init ci ./scripts/static-analyser-check.sh
```

All commands reuse the same workspace which is how it works in GH actions.

Legitimate bugs
---------------

Now the error displayed in build failure appears to be a legitimate bug.  Four were found related to null pointer dereference.  I'm not fixing the bug but the check should work properly for those who do.

If you following my docker setup in this description you can view the bug report by starting the report server.

```bash
docker run -p 127.0.0.1:8181:8181 -it --rm -v "$PWD:$PWD" -w "$PWD" --init ci scan-view --host 0.0.0.0 --allow-all-hosts builddir/meson-logs/scanbuild/*
```

After the report server is started you can visit `http://127.0.0.1:8181/` to view the bugs.

Screenshot of report
--------------------

![Screenshot of bug report](https://user-images.githubusercontent.com/875669/155830028-473db1ea-7c98-4a82-bc3a-290d5c155108.png)
2022-03-10 10:40:45 +00:00
Sam Gleske
23493e5cc3 Fix GitHub Actions Format Check
This change fixes the format check in GitHub actions ran by `clang-format`.

Dockerfile

```dockerfile
FROM ubuntu:20.04

ENV DEBIAN_FRONTEND=noninteractive

RUN set -ex; \
apt-get update; \
apt-get install -y build-essential meson appstream clang clang-format clang-tools libdbus-1-dev libinih-dev libsystemd-dev git

RUN set -ex; \
yes | adduser ci-user

USER ci-user
```

Environment setup

```bash
sudo su -g docker $USER
docker build -t ci .
```

clang-format fix

```bash
docker run -e CI=true --rm -v "$PWD:$PWD" -w "$PWD" --init ci ./scripts/format-check.sh
```
2022-02-21 09:51:59 +00:00
Sam Gleske
c96f7379b4
New utility: gamemodelist (#346)
* New utility: gamemodelist

While trying out gamemode on Ubuntu 18.04 I had trouble figuring out
whether or not my games were running with gamemode enabled.  I wrote this
utility which prints all processes loaded with the gamemode shared
library.

- [x] Added utility to `data/` folder.
- [x] Update meson installer.
- [x] Included section 1 manual.
- [x] Updated README for Ubuntu 18.04 build instructions.  Steam supports
  Ubuntu 18.04.

I'm open to feedback and generally this should work for any distrobution
since it makes use of the Linux `/proc` filesystem.  [Learn more about
`/proc`][1].

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/filesystems/proc.rst?h=v5.15.12
2022-02-21 09:50:57 +00:00
Emil Velikov
898feb9c52 data: add and install sysusers.d gamemode.conf
Add a trivial gamemode.conf file, which creates the gamemode group.

v2: git add gamemode.conf (d'oh)

Signed-off-by: Emil Velikov <emil.velikov@collabora.com>
2022-02-03 16:50:29 +00:00
Stephan Lachnit
ab06ba419e Switch to GitHub Actions
Signed-off-by: Stephan Lachnit <stephanlachnit@debian.org>
2022-02-03 16:40:02 +00:00
Stephan Lachnit
6c60565f33 Format files according to clang-format
Signed-off-by: Stephan Lachnit <stephanlachnit@debian.org>
2022-02-03 16:40:02 +00:00
Kira Bruneau
5e366bd55d Fix loading shipped config when using a prefix other than /usr
Also remove recommendation to modify `/usr/share/gamemode/gamemode.ini`.
`/usr/share` is intended to contain read-only data files.

See https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s11.html.
2022-02-03 16:02:18 +00:00
Kira Bruneau
126e67fb6b Run executables from PATH instead of /usr/bin
Not all distributions install non-system binaries into /usr/bin. For
example, NixOS installs packages to /nix/store using a unique hash
generated from the inputs used to build it:

/nix/store/jld7jh3ilvbg91zvn1bdyawfc55b9jk8-polkit-0.118-bin/bin/pkexec
2022-02-03 15:42:22 +00:00
Patrick Pulfer
4000a32584 Documentation: Add GCC dependency to Ubuntu installation guide 2021-05-24 11:27:27 +01:00
Ahsan Fayaz
b11d2912e2 Update inih version in CHANGELOG.md 2021-02-18 19:00:12 +00:00
Ahsan Fayaz
0344e7c311 Specify the correct inih subproject directory. 2021-02-18 18:52:59 +00:00
Ahsan Fayaz
993857d0e8 Use inih r53 from wrapdb. Updated using the command 'meson subprojects update'. 2021-02-18 18:42:08 +00:00
Ahsan Fayaz
5fd0065df6 Update version to 1.6.1 2021-02-18 18:39:53 +00:00
Ahsan Fayaz
6592c2229c Update version references to 1.6.1 2021-02-16 16:12:29 +00:00
Ahsan Fayaz
05a4c0c152 Add changes for 1.6.1 2021-02-16 16:11:59 +00:00
Ahsan Fayaz
fb7062bd9c Update copyright year to 2021. 2021-02-04 13:24:59 +00:00
Stephan Lachnit
49a0ef8c0b minor metainfo improvements
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-11-18 12:33:38 +00:00
Stephan Lachnit
cfa8b9a2c4 Use inih r52 from wrapdb
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-11-06 11:30:26 +00:00
Ivan Tham
62659edd2b Gamemode is now available on Arch community repo
https://www.archlinux.org/packages/community/x86_64/gamemode/
2020-09-30 09:09:06 +01:00
otreblan
9037b730f6 Add libinih pacman dependency 2020-09-30 09:08:50 +01:00
Ahsan Fayaz
5f71f57db1 Update CHANGELOG.md for 1.6. 2020-09-18 15:03:00 +01:00
Michael Butler
17b2b6201b Update README.md to point to release 1.6 in instructions 2020-09-18 14:56:14 +01:00
Rémi Verschelde
832f9ab7e0 Fix installation of man pages in proper man dirs
See https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s11.html#usrsharemanManualPages
2020-09-14 10:18:53 +01:00
sgleizes
5163c01d24 Allow LD_PRELOAD overrides from GAMEMODERUNEXEC commands 2020-09-01 16:21:05 +01:00
Niels Thykier
3fcff0d75f Align open_fdinfo_dir with pidfds_to_pids
The function `pidfds_to_pids` expected `-1` if `open_fdinfo_dir` failed but the latter returned `errno` which is hard to distinguish from a valid file handle.  Correct that by making `open_fdinfo_dir` a wrapper around `open`.
2020-08-12 09:41:28 +01:00
Ahsan Fayaz
510a0a6ae2 Fix issues found by Coverity, closes #206. 2020-07-17 18:55:00 +01:00
Stephan Lachnit
f6c68cd6de change location for the shipped default config
Before it was installed to /etc, but according the daemon/gamemode-config.c line 381 the shipped config should be in /usr/share/gamemode

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-07-09 08:14:09 +01:00
Stephan Lachnit
7a68a178ac simplify libgamemodeauto preloading
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-07-09 08:13:17 +01:00
Stephan Lachnit
8b408694b0 improve libgamemodeauto pkg-config file
Rename the pkg-config entry for libgamemodeauto, to it make it more that this will link against a library, compared to what the `gamemode` pkg-config does. This also removes the manual addition of the libdl dependency, this is done by Meson automatically.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-23 09:18:14 +01:00
Stephan Lachnit
d8337aeb05 various variable naming improvements
This makes it more clear that libgamemode and libgamemodeauto are indeed libraries. Also, the misleading name `libgamemode_dep` has been renamed to `gamemode_dep`, which now also includes the dependency on libdl. The misleading `libgamemode_includes` variable name has also been changed.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-23 09:18:14 +01:00
Stephan Lachnit
d4536c62af build gamemodeauto also as static library
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-23 09:18:14 +01:00
Stephan Lachnit
785df22274 travis: fix some config warnings
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-23 09:14:42 +01:00
Ahsan Hussain
8810e4f158 README.md: Update note for Nvidia Optimus proprietary driver users
Add a note for Nvidia hybrid graphics users, not having the opensource
nouveau driver, which can save them some time for searching the right
solution

Signed-off-by: Ahsan Hussain <ahsan_hussain@mentor.com>
2020-06-08 10:24:52 +01:00
RickAndTired
db7d52dbac Spelling / typo fix 2020-05-27 10:50:17 +01:00
Stephan Lachnit
3033867fc9 add man page for example game
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-27 10:49:36 +01:00
Stephan Lachnit
a48272ae61 fix incorrect name in gamemoderun man page
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-27 10:49:36 +01:00
Ahsan Fayaz
39f93e4000 Fix incorrect merge conflict resolution. 2020-05-26 17:43:23 +01:00
Stephan Lachnit
d27f8caecc improve path names for man pages and metainfo
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-26 12:53:21 +01:00
Stephan Lachnit
3d49f87308 configure version in man pages
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-26 12:53:21 +01:00
Ahsan Fayaz
d47dea92de Merge branch 'stephanlachnit-patch/mkrelease' 2020-05-26 12:50:10 +01:00
afayaz-feral
16ebc794c5 Merge branch 'master' into patch/mkrelease 2020-05-26 12:35:58 +01:00
Stephan Lachnit
082b1c6619 mkrelease.sh: get version via git
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-26 12:32:19 +01:00
Stephan Lachnit
937bcf3c73 remove gitignore
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-26 12:32:01 +01:00
Stephan Lachnit
e6355d91f4 simplify archive creation
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-20 18:05:37 +02:00
Stephan Lachnit
9ecff8d5d3 Meson: explicitly set include path
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-20 10:00:18 +01:00
Stephan Lachnit
9f676632c3 meson: ensure subprojects are up-to-date
... when creating the release tarball.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-19 16:35:00 +01:00
Stephan Lachnit
115f1ecdbd meson: use builddir instead of build
Use the -C option instead of changing the dir, change the build folder to builddir.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-19 16:35:00 +01:00
Stephan Lachnit
ce6485ef97 combine no-daemon, elogind and systemd option
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-19 16:27:51 +01:00
Stephan Lachnit
953792b4a5 Add option to use elogind
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-19 16:27:51 +01:00
Stephan Lachnit
1e8312f7e3 Add option to change lib dir in gamemoderun
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-13 10:27:15 +01:00
Stephan Lachnit
1709810707 Use distro provided git-clang-format
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-13 09:52:01 +01:00
Stephan Lachnit
c36019a9aa travis: run gamemoded -v as meson test
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
6453a123ab add metainfo test
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
f95470c94a travis: add libinih-dev as dependency
This will compile gamemode against the shared lib version of inih, as it should be in most distros.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
a1660fc37a add gamemode-simulate-game to travis
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
43911a8919 install example config
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
e1ae78e346 install example
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
f7a4a6ccfe expose dependency objects for libs
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
90d05eef42 travis: update to focal
This updates to Ubuntu 20.04 (focal) and uses it's provided Meson version.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
2fb62e34fa fix double spaces in example
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
c755f7e539 fix wrong prefix variable in data
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-12 19:25:57 +01:00
Stephan Lachnit
1ca9b727c3 remove some old example files
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-11 12:30:15 +01:00
Stephan Lachnit
329f7b4cee give gamemoderun an own man page
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-11 09:41:17 +01:00
Stephan Lachnit
695f7e565f Add return values to example
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-06 14:04:06 +01:00
Stephan Lachnit
e3c24f34f1 provide metainfo
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-06 12:34:06 +01:00
Anjune
29b4148a00 Make system revert to previous governor instead of "powersave"
See issue 214.
2020-05-06 11:32:10 +01:00
qarmin
94444cb76f Check index before using 2020-05-06 11:05:02 +01:00
Alan Jenkins
57bc3e26ba Add ATLauncher to gamemode compatible list
Adds ATLauncher to the list of applications that integrate with
Gamemode.
2020-05-06 11:04:29 +01:00
Alan Jenkins
0ba1551293 Sort the games list alphabetically in README
Sorts the games list alphabetically in the README.md.
2020-05-06 11:04:29 +01:00
Stephan Lachnit
094905f7f8 rename example to gamemode-simulate-game
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-06 11:02:29 +01:00
Ari Breitkreuz
c8492ca28f Fix typo in README and help message 2020-05-06 11:01:37 +01:00
Stephan Lachnit
faea358023 Use wrap instead of git submodule
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-05-06 11:01:13 +01:00
Ahsan Fayaz
bfa20975ca Add git-clang-format, copied from llvm-project GitHub repository. This is preferable to downloading it each time which often fails and causes errors with continuous integration. 2020-03-03 16:54:09 +00:00
Ahsan Fayaz
40702d3fed Update version back to 1.6 post-release 2020-03-03 16:35:37 +00:00
Ahsan Fayaz
616dca659a Update version to 1.5.1 2020-03-03 16:18:48 +00:00
Stephan Lachnit
d3e309b23b use upstream inih r48
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-03-03 14:38:35 +00:00
seb128
1a598f53d2 Don't default to syslog logging
Let the preferred/configured logging system to be used instead
2020-02-26 18:26:09 +00:00
Stephan Lachnit
f0943ff431 prefer system installation of inih
Closes #195.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-02-26 18:24:50 +00:00
Ahsan Fayaz
065454bb4e Update version to 1.6 post-release 2020-01-22 14:51:00 +00:00
85 changed files with 2710 additions and 794 deletions

View File

@ -19,7 +19,6 @@ AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
@ -39,16 +38,14 @@ SortIncludes: true
# IncludeBlocksStyle changed to IncludeBlocks, between xenial and disco, so we can't use it for consistency
# IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^"gamemode.h"'
- Regex: '^<linux\/'
Priority: 0
- Regex: '^"build-'
Priority: 1
- Regex: '^"common-'
Priority: 2
- Regex: '^"gamemode-'
Priority: 3
- Regex: '^<'
Priority: 4
Priority: 1
- Regex: '^"gamemode.h"'
Priority: 2
- Regex: '^"'
Priority: 3
# IncludeIsMainRegex: (project doesn't use a main includes that can add other includes via regex)
IndentCaseLabels: false
IndentWidth: 4

41
.github/workflows/build-and-test.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Build and test
on: [push, pull_request]
permissions:
contents: read
actions: write
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential meson appstream clang clang-format clang-tools libdbus-1-dev libinih-dev libsystemd-dev systemd-dev
- name: Check format
env:
CI: "true"
run: |
./scripts/format-check.sh
- name: Build and install
env:
CI: "true"
run: |
./bootstrap.sh -Dwith-examples=true
- name: Tests
run: |
meson test -C builddir
- name: Simulate game
run: |
dbus-run-session -- gamemode-simulate-game
- name: Static analyser check
run: |
./scripts/static-analyser-check.sh
- name: Upload logs
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: logs
path: |
builddir/meson-logs/

5
.gitignore vendored
View File

@ -1,2 +1,3 @@
build/
*.swp
/builddir
/subprojects/packagecache/
/subprojects/inih-r*

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "subprojects/inih"]
path = subprojects/inih
url = https://github.com/FeralInteractive/inih.git

View File

@ -1,28 +0,0 @@
dist: xenial
language: c
compiler: gcc
sudo: false
addons:
apt:
packages:
- clang
- clang-format
- python3-pip
- python3-setuptools
- libsystemd-dev
- ninja-build
- libdbus-1-dev
artifacts:
paths:
- $(git ls-files -o | tr "\n" ":")
before_script:
- pip3 install wheel
- pip3 install --upgrade 'meson==0.52.0'
- meson --version
script:
- ./scripts/format-check.sh
- ./bootstrap.sh -Dwith-examples=true
- gamemoded -v
- ./scripts/static-analyser-check.sh

View File

@ -1,3 +1,121 @@
## 1.8.2
### Changes
* Fix idle inhibitor closing bus connection too early (#466)
* Fix hybrid CPU core pinning (#455)
* Fix unreadable process maps in gamemodelist (#463)
* Fixed crash if dbus is not accesible (#458)
* Various bugfixes and improvements to documentation
### Contributors
* @notpeelz
* @patatahooligan
* Reilly Brogan @ReillyBrogan
* Alexandru Ionut Tripon @Trial97
* Kostadin @kostadinsh
* Daniel Martinez @Calandracas606
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2023-12-13&to=2024-08-19&type=c)
## 1.8.1
### Changes
* Fix polkit parse error (#449)
### Contributors
* @Vam-Jam
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2022-07-22&to=2023-12-12&type=c)
## 1.8
### Changes
* Add CPU core pinning and parking capability (#416)
* Allow disabling the Linux kernel split lock mitigation (#446)
* Fix building when pidfd_open is available (Fixes build with glibc 2.36) (#379)
* Unify privileged group configuration between pam, systemd, & polkit (#375)
* Various other bugfixes and improved default configuration
### Contributors
* Henrik Holst @HenrikHolst
* Kira Bruneau @kira-bruneau
* James Le Cuirot @chewi
* Hugo Locurcio @Calinou
* Zoltán Nyikos @nyz93
* @ashuntu
* @szymon-gniado
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2022-07-22&to=2023-12-06&type=c)
## 1.7
### Changes
* Added new utility: `gamemodelist` (#346)
* Run executables from `PATH` instead of `/usr/bin` (#323)
* Add a trivial `gamemode.conf` file, which creates the gamemode group (#339)
* Various minor bugfixes and updates to documentation
### Contributors
* Sam Gleske @samrocketman
* Kira Bruneau @kira-bruneau
* Stephan Lachnit @stephanlachnit
* Emil Velikov @evelikov-work
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2021-02-19&to=2022-07-21&type=c)
## 1.6.1
### Changes
* Use inih r53
* Packaging changes for Arch
* Minor metainfo improvements
### Contributors
* Stephan Lachnit @stephanlachnit
* Alberto Oporto Ames @otreblan
## 1.6
### Changes
* Created new manpages for `gamemoderun` and the example, now called `gamemode-simulate-game`
* Add ability to change lib directory of `gamemoderun`
* Add option to use `elogind`
* Copy default config file to the correct location
* Allow `LD_PRELOAD` to be overridden in `$GAMEMODERUNEXEC`
* Various minor bugfixes
* Improvements to dependency management
### Contributors
* Stephan Lachnit @stephanlachnit
* Rafał Mikrut @qarmin
* Niels Thykier @nthykier
* Stéphane Gleizes @sgleizes
## 1.5.1
### Changes
Minor changes for Debian and Ubuntu packaging:
* Use the preferred logging system rather than defaulting to syslog.
* Prefer the system installation of inih.
### Contributors
* Sebastien Bacher @seb128
* Stephan Lachnit @stephanlachnit
## 1.5
### Changes
@ -10,7 +128,7 @@
* Alex Smith @aejsmith
* Christian Kellner @gicmo
* Jason Ekstrand @jekstrand
* Faith Ekstrand @gfxstrand
## 1.4

View File

@ -1,4 +1,4 @@
Copyright (c) 2017-2020, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

100
README.md
View File

@ -10,16 +10,17 @@ Currently GameMode includes support for optimisations including:
* Kernel scheduler (`SCHED_ISO`)
* Screensaver inhibiting
* GPU performance mode (NVIDIA and AMD), GPU overclocking (NVIDIA)
* CPU core pinning or parking
* Custom scripts
GameMode packages are available for Ubuntu, Debian, Solus, the AUR, Gentoo, Fedora, OpenSUSE, Mageia and possibly more.
GameMode packages are available for Ubuntu, Debian, Solus, Arch, Gentoo, Fedora, OpenSUSE, Mageia and possibly more.
Issues with GameMode should be reported here in the issues section, and not reported to Feral directly.
---
## Requesting GameMode
For games/launchers which integrate GameMode support (see list later on), simply running the game will automatically activate GameMode.
For games/launchers which integrate GameMode support, simply running the game will automatically activate GameMode.
For others, you must manually request GameMode when running the game. This can be done by launching the game through `gamemoderun`:
```bash
@ -41,60 +42,78 @@ LD_PRELOAD="$LD_PRELOAD:/usr/\$LIB/libgamemodeauto.so.0"
The daemon is configured with a `gamemode.ini` file. [example/gamemode.ini](https://github.com/FeralInteractive/gamemode/blob/master/example/gamemode.ini) is an example of what this file would look like, with explanations for all the variables.
Config files are loaded and merged from the following directories, in order:
1. `/usr/share/gamemode/`
2. `/etc/`
3. `$XDG_CONFIG_HOME` or `$HOME/.config/`
4. `$PWD`
Configuration files are loaded and merged from the following directories, from highest to lowest priority:
1. `$PWD` ("unsafe" - **`[gpu]` settings take no effect in this file**)
2. `$XDG_CONFIG_HOME` or `$HOME/.config/` ("unsafe" - **`[gpu]` settings take no effect in this file**)
3. `/etc/`
4. `/usr/share/gamemode/`
---
## Note for Hybrid GPU users
It's not possible to integrate commands like optirun automatically inside GameMode, since the GameMode request is made once the game has already started. However it is possible to use a hybrid GPU wrapper like optirun by starting the game with `gamemoderun`.
You can do this by setting the environment variable `GAMEMODERUNEXEC` to your wrapper's launch command, so for example `GAMEMODERUNEXEC=optirun` or `GAMEMODERUNEXEC="env DRI_PRIME=1"`. This environment variable can be set globally, so that the same prefix command does not have to be duplicated everywhere you want to use `gamemoderun`.
You can do this by setting the environment variable `GAMEMODERUNEXEC` to your wrapper's launch command, so for example `GAMEMODERUNEXEC=optirun`, `GAMEMODERUNEXEC="env DRI_PRIME=1"`, or `GAMEMODERUNEXEC="env __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia __VK_LAYER_NV_optimus=NVIDIA_only"`. This environment variable can be set globally (e.g. in /etc/environment), so that the same prefix command does not have to be duplicated everywhere you want to use `gamemoderun`.
GameMode will not be injected to the wrapper.
---
## Apps with GameMode integration
### Games
The following games are known to integrate GameMode support (meaning they don't require any additional configuration to activate GameMode while running):
* DiRT 4
* Rise of the Tomb Raider
* Shadow of the Tomb Raider
* Total War: Three Kingdoms
* Total War: WARHAMMER II
* Total War Saga: Thrones of Britannia
### Others
Other apps which can integrate with GameMode include:
* GNOME Shell ([via extension](https://github.com/gicmo/gamemode-extension)) - indicates when GameMode is active in the top panel.
* Lutris - Enables GameMode for all games by default if available (must have both 32- and 64-bit GameMode libraries installed), configurable in preferences.
---
## Development [![Build Status](https://travis-ci.org/FeralInteractive/gamemode.svg?branch=master)](https://travis-ci.org/FeralInteractive/gamemode)
## Development [![Build and test](https://github.com/FeralInteractive/gamemode/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/FeralInteractive/gamemode/actions/workflows/build-and-test.yml)
The design of GameMode has a clear-cut abstraction between the host daemon and library (`gamemoded` and `libgamemode`), and the client loaders (`libgamemodeauto` and `gamemode_client.h`) that allows for safe use without worrying about whether the daemon is installed or running. This design also means that while the host library currently relies on `systemd` for exchanging messages with the daemon, it's entirely possible to implement other internals that still work with the same clients.
See repository subdirectories for information on each component.
### Install Dependencies
GameMode depends on `meson` for building and `systemd` for internal communication. This repo contains a `bootstrap.sh` script to allow for quick install to the user bus, but check `meson_options.txt` for custom settings.
GameMode depends on `meson` for building and `systemd` for internal communication. This repo contains a `bootstrap.sh` script to allow for quick install to the user bus, but check `meson_options.txt` for custom settings. These instructions all assume that you
already have a C development environment (gcc or clang, libc-devel, etc) installed.
#### Ubuntu/Debian (you may also need `dbus-user-session`)
#### Ubuntu/Debian
Note: Debian 13 and Ubuntu 25.04 (and later) need to install `systemd-dev` in addition to the dependencies below.
```bash
apt install meson libsystemd-dev pkg-config ninja-build git libdbus-1-dev
apt update && apt install meson libsystemd-dev pkg-config ninja-build git dbus-user-session libdbus-1-dev libinih-dev build-essential
```
On Debian 12 and Ubuntu 22 (and earlier), you'll need to install `python3` and `python3-venv` packages to install the latest meson version from `pip`.
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install meson
```
Later you can deactivate the virtual environment and remove it.
```bash
deactivate
rm -rf .venv
```
#### Arch
```bash
pacman -S meson systemd git dbus
pacman -S meson systemd git dbus libinih gcc pkgconf
```
#### RHEL 10 and variants
Note: Older versions of RHEL (and variants) cannot build gamemode due to not exposing libdbus-1 to pkg-config.
(also - don't try and play games on RHEL, come on)
You must have [EPEL](https://docs.fedoraproject.org/en-US/epel/getting-started/) enabled to install all dependencies.
```bash
dnf install meson systemd-devel pkg-config git dbus-devel inih-devel
```
#### Fedora
```bash
dnf install meson systemd-devel pkg-config git dbus-devel
dnf install meson systemd-devel pkg-config git dbus-devel inih-devel
```
#### OpenSUSE Leap/Tumbleweed
```bash
zypper install meson systemd-devel git dbus-1-devel libgcc_s1 libstdc++-devel libinih-devel
```
#### Gentoo
Gentoo has an ebuild which builds a stable release from sources. It will also pull in all the dependencies so you can work on the source code.
```bash
@ -105,21 +124,30 @@ You can also install using the latest sources from git:
ACCEPT_KEYWORDS="**" emerge --ask ~games-util/gamemode-9999
```
#### Nix
Similar to Gentoo, nixOS already has a package for gamemode, so we can use that to setup an environment:
```bash
nix-shell -p pkgs.gamemode.buildInputs pkgs.gamemode.nativeBuildInputs
```
### Build and Install GameMode
Then clone, build and install a release version of GameMode at 1.5:
Then clone, build and install a release version of GameMode at 1.8.2:
```bash
git clone https://github.com/FeralInteractive/gamemode.git
cd gamemode
git checkout 1.5 # omit to build the master branch
git checkout 1.8.2 # omit to build the master branch
./bootstrap.sh
```
To test GameMode installed and will run correctly:
```bash
gamemoded -t
```
To uninstall:
```bash
systemctl --user stop gamemoded.service
cd build/
ninja uninstall
ninja uninstall -C builddir
```
### Pull Requests
@ -136,7 +164,7 @@ See the [contributors](https://github.com/FeralInteractive/gamemode/graphs/contr
---
## License
Copyright © 2017-2020 Feral Interactive
Copyright © 2017-2025 Feral Interactive and the GameMode contributors
GameMode is available under the terms of the BSD 3-Clause License (Revised)

View File

@ -14,9 +14,9 @@ if [ ! -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" ]; then
echo "This probably means that you have disabled processor scheduling features in your BIOS. See README.md (or GitHub issue #44) for more information."
echo "This means GameMode's CPU governor control feature will not work (other features will still work)."
if [ "$TRAVIS" != "true" ]; then
if [ "$CI" != "true" ]; then
# Allow to continue the install, as gamemode has other useful features
read -p "Would you like to continue anyway [Y/N]? " -r
read -p "Would you like to continue anyway [y/N]? " -r
[[ $REPLY =~ ^[Yy]$ ]]
fi
fi
@ -26,24 +26,25 @@ fi
# Echo the rest so it's obvious
set -x
meson --prefix=$prefix build --buildtype debugoptimized -Dwith-systemd-user-unit-dir=/etc/systemd/user "$@"
cd build
ninja
meson setup builddir --prefix=$prefix --buildtype debugoptimized -Dwith-systemd-user-unit-dir=/etc/systemd/user "$@"
ninja -C builddir
# Verify user wants to install
set +x
if [ "$TRAVIS" != "true" ]; then
if [ "$CI" != "true" ]; then
read -p "Install to $prefix? [y/N] " -r
[[ $REPLY =~ ^[Yy]$ ]]
fi
set -x
sudo ninja install
sudo ninja install -C builddir
# Restart polkit so we don't get pop-ups whenever we pkexec
if systemctl list-unit-files |grep -q polkit.service; then
sudo systemctl try-restart polkit
if [ "$CI" != "true" ]; then
# Restart polkit so we don't get pop-ups whenever we pkexec
if systemctl list-unit-files | grep -q polkit.service; then
sudo systemctl try-restart polkit
fi
# Reload systemd configuration so that it picks up the new service.
systemctl --user daemon-reload
fi
# Reload systemd configuration so that it picks up the new service.
systemctl --user daemon-reload

71
common/common-cpu.c Normal file
View File

@ -0,0 +1,71 @@
/*
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "common-cpu.h"
#include "common-logging.h"
char *parse_cpulist(char *cpulist, long *from, long *to)
{
if (!cpulist || *cpulist == '\0')
return NULL;
char *endp;
*from = strtol(cpulist, &endp, 10);
if (endp == cpulist)
return NULL;
if (*endp == '\0' || *endp == ',') {
*to = *from;
if (*endp == '\0')
return endp;
return endp + 1;
}
if (*endp != '-')
return NULL;
cpulist = endp + 1;
*to = strtol(cpulist, &endp, 10);
if (endp == cpulist)
return NULL;
if (*to < *from)
return NULL;
if (*endp == '\0')
return endp;
return endp + 1;
}

51
common/common-cpu.h Normal file
View File

@ -0,0 +1,51 @@
/*
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>
#define IS_CPU_PARK 0
#define IS_CPU_PIN 1
/* Storage for CPU info*/
struct GameModeCPUInfo {
size_t num_cpu;
int park_or_pin;
cpu_set_t *online;
cpu_set_t *to_keep;
};
/* parses a list of cpu cores in the format "a,b-c,d-e,f" */
char *parse_cpulist(char *cpulist, long *from, long *to);

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -140,7 +140,7 @@ int run_external_process(const char *const *exec_args, char buffer[EXTERNAL_BUFF
* bindings that these objects are completely constant.
* http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
*/
if (execv(exec_args[0], (char *const *)exec_args) != 0) {
if (execvp(exec_args[0], (char *const *)exec_args) != 0) {
LOG_ERROR("Failed to execute external process: %s %s\n", exec_args[0], strerror(errno));
exit(EXIT_FAILURE);
}

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -122,22 +122,28 @@ const char *get_gov_state(void)
long length = ftell(f);
fseek(f, 0, SEEK_SET);
char contents[length];
if (fread(contents, 1, (size_t)length, f) > 0) {
/* Files have a newline */
strtok(contents, "\n");
if (strlen(governor) > 0 && strncmp(governor, contents, 64) != 0) {
/* Don't handle the mixed case, this shouldn't ever happen
* But it is a clear sign we shouldn't carry on */
LOG_ERROR("Governors malformed: got \"%s\", expected \"%s\"", contents, governor);
fclose(f);
return "malformed";
}
strncpy(governor, contents, sizeof(governor) - 1);
if (length == -1) {
LOG_ERROR("Failed to seek file %s\n", gov);
} else {
LOG_ERROR("Failed to read contents of %s\n", gov);
char contents[length];
if (fread(contents, 1, (size_t)length, f) > 0) {
/* Files have a newline */
strtok(contents, "\n");
if (strlen(governor) > 0 && strncmp(governor, contents, 64) != 0) {
/* Don't handle the mixed case, this shouldn't ever happen
* But it is a clear sign we shouldn't carry on */
LOG_ERROR("Governors malformed: got \"%s\", expected \"%s\"",
contents,
governor);
fclose(f);
return "malformed";
}
strncpy(governor, contents, sizeof(governor) - 1);
} else {
LOG_ERROR("Failed to read contents of %s\n", gov);
}
}
fclose(f);

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Red Hat
All rights reserved.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Red Hat
All rights reserved.
@ -58,6 +58,8 @@ static int pidfd_open(pid_t pid, unsigned int flags)
{
return (int)syscall(__NR_pidfd_open, pid, flags);
}
#else
#include <sys/pidfd.h>
#endif
/* pidfd functions */
@ -196,12 +198,5 @@ int pidfds_to_pids(int *fds, pid_t *pids, int count)
/* misc directory helpers */
int open_fdinfo_dir(void)
{
int fd;
fd = open("/proc/self/fdinfo", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
if (fd == -1)
return errno;
return fd;
return open("/proc/self/fdinfo", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
}

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Red Hat
All rights reserved.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Intel Corporation
All rights reserved.
@ -35,10 +35,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-power.h"
#include "common-logging.h"
#include <linux/limits.h>
#include <assert.h>
#include <ctype.h>
#include <glob.h>
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

86
common/common-profile.c Normal file
View File

@ -0,0 +1,86 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "common-profile.h"
#include "common-logging.h"
/**
* Path for platform profile
*/
const char *profile_path = "/sys/firmware/acpi/platform_profile";
/**
* Check if platform profile file exists
*/
int profile_exists(void)
{
return !access(profile_path, F_OK);
}
/**
* Return the current platform profile state
*/
const char *get_profile_state(void)
{
/* Persistent profile state */
static char profile[64] = { 0 };
memset(profile, 0, sizeof(profile));
FILE *f = fopen(profile_path, "r");
if (!f) {
LOG_ERROR("Failed to open file for read %s\n", profile_path);
return "none";
}
/* Grab the file length */
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
if (length == -1) {
LOG_ERROR("Failed to seek file %s\n", profile_path);
} else {
char contents[length + 1];
if (fread(contents, 1, (size_t)length, f) > 0) {
strtok(contents, "\n");
strncpy(profile, contents, sizeof(profile) - 1);
} else {
LOG_ERROR("Failed to read contents of %s\n", profile_path);
}
}
fclose(f);
return profile;
}

50
common/common-profile.h Normal file
View File

@ -0,0 +1,50 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <linux/limits.h>
#include <unistd.h>
/**
* Path for platform profile
*/
extern const char *profile_path;
/**
* Check if platform profile file exists
*/
int profile_exists(void);
/**
* Get the current platform profile state
*/
const char *get_profile_state(void);

64
common/common-splitlock.c Normal file
View File

@ -0,0 +1,64 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "common-splitlock.h"
#include "common-logging.h"
/**
* Path for the split lock mitigation state
*/
const char *splitlock_path = "/proc/sys/kernel/split_lock_mitigate";
/**
* Return the current split lock mitigation state
*/
long get_splitlock_state(void)
{
FILE *f = fopen(splitlock_path, "r");
if (!f) {
LOG_ERROR("Failed to open file for read %s\n", splitlock_path);
return -1;
}
char contents[41] = { 0 };
long value = -1;
if (fread(contents, 1, sizeof contents - 1, f) > 0) {
value = strtol(contents, NULL, 10);
} else {
LOG_ERROR("Failed to read contents of %s\n", splitlock_path);
}
fclose(f);
return value;
}

44
common/common-splitlock.h Normal file
View File

@ -0,0 +1,44 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <linux/limits.h>
/**
* Path for the split lock mitagation state
*/
extern const char *splitlock_path;
/**
* Get the current split lock mitigation state
*/
long get_splitlock_state(void);

View File

@ -2,9 +2,12 @@
common_sources = [
'common-logging.c',
'common-governors.c',
'common-profile.c',
'common-splitlock.c',
'common-external.c',
'common-helpers.c',
'common-gpu.c',
'common-cpu.c',
'common-pidfds.c',
'common-power.c',
]

View File

@ -11,7 +11,7 @@ Usage: gamemoded [-d] [-l] [-r] [-t] [-h] [-v]
When no PID given, queries the status globally
-d, --daemonize Daemonize self after launch
-l, --log-to-syslog Log to syslog
-r, --test Run tests
-t, --test Run tests
-h, --help Print this help
-v, --version Print version
```

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -35,10 +35,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-helpers.h"
#include "common-logging.h"
#include "build-config.h"
/* Ben Hoyt's inih library */
#include "ini.h"
#include <ini.h>
#include <dirent.h>
#include <libgen.h>
#include <math.h>
#include <pthread.h>
#include <pwd.h>
@ -85,6 +88,9 @@ struct GameModeConfig {
char defaultgov[CONFIG_VALUE_MAX];
char desiredgov[CONFIG_VALUE_MAX];
char defaultprof[CONFIG_VALUE_MAX];
char desiredprof[CONFIG_VALUE_MAX];
char igpu_desiredgov[CONFIG_VALUE_MAX];
float igpu_power_threshold;
@ -95,6 +101,8 @@ struct GameModeConfig {
long inhibit_screensaver;
long disable_splitlock;
long reaper_frequency;
char apply_gpu_optimisations[CONFIG_VALUE_MAX];
@ -104,6 +112,9 @@ struct GameModeConfig {
long nv_powermizer_mode;
char amd_performance_level[CONFIG_VALUE_MAX];
char cpu_park_cores[CONFIG_VALUE_MAX];
char cpu_pin_cores[CONFIG_VALUE_MAX];
long require_supervisor;
char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
char supervisor_blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
@ -257,6 +268,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
valid = get_string_value(value, self->values.defaultgov);
} else if (strcmp(name, "desiredgov") == 0) {
valid = get_string_value(value, self->values.desiredgov);
} else if (strcmp(name, "defaultprof") == 0) {
valid = get_string_value(value, self->values.defaultprof);
} else if (strcmp(name, "desiredprof") == 0) {
valid = get_string_value(value, self->values.desiredprof);
} else if (strcmp(name, "igpu_desiredgov") == 0) {
valid = get_string_value(value, self->values.igpu_desiredgov);
} else if (strcmp(name, "igpu_power_threshold") == 0) {
@ -269,6 +284,8 @@ static int inih_handler(void *user, const char *section, const char *name, const
valid = get_string_value(value, self->values.ioprio);
} else if (strcmp(name, "inhibit_screensaver") == 0) {
valid = get_long_value(name, value, &self->values.inhibit_screensaver);
} else if (strcmp(name, "disable_splitlock") == 0) {
valid = get_long_value(name, value, &self->values.disable_splitlock);
}
} else if (strcmp(section, "gpu") == 0) {
/* Protect the user - don't allow these config options from unsafe config locations */
@ -277,9 +294,7 @@ static int inih_handler(void *user, const char *section, const char *name, const
"The [gpu] config section is not configurable from unsafe config files! Option %s "
"will be ignored!\n",
name);
LOG_ERROR(
"Consider moving this option to /etc/gamemode.ini or "
"/usr/share/gamemode/gamemode.ini\n");
LOG_ERROR("Consider moving this option to /etc/gamemode.ini\n");
}
/* GPU subsection */
@ -296,6 +311,12 @@ static int inih_handler(void *user, const char *section, const char *name, const
} else if (strcmp(name, "amd_performance_level") == 0) {
valid = get_string_value(value, self->values.amd_performance_level);
}
} else if (strcmp(section, "cpu") == 0) {
if (strcmp(name, "park_cores") == 0) {
valid = get_string_value(value, self->values.cpu_park_cores);
} else if (strcmp(name, "pin_cores") == 0) {
valid = get_string_value(value, self->values.cpu_pin_cores);
}
} else if (strcmp(section, "supervisor") == 0) {
/* Supervisor subsection */
if (strcmp(name, "supervisor_whitelist") == 0) {
@ -362,8 +383,9 @@ static void load_config_files(GameModeConfig *self)
/* Set some non-zero defaults */
self->values.igpu_power_threshold = DEFAULT_IGPU_POWER_THRESHOLD;
self->values.inhibit_screensaver = 1; /* Defaults to on */
self->values.disable_splitlock = 1; /* Defaults to on */
self->values.reaper_frequency = DEFAULT_REAPER_FREQ;
self->values.gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */
self->values.gpu_device = 0;
self->values.nv_powermizer_mode = -1;
self->values.nv_core_clock_mhz_offset = -1;
self->values.nv_mem_clock_mhz_offset = -1;
@ -378,7 +400,7 @@ static void load_config_files(GameModeConfig *self)
bool protected;
};
struct ConfigLocation locations[CONFIG_NUM_LOCATIONS] = {
{ "/usr/share/gamemode", true }, /* shipped default config */
{ SYSCONFDIR, true }, /* shipped default config */
{ "/etc", true }, /* administrator config */
{ config_location_home, false }, /* $XDG_CONFIG_HOME or $HOME/.config/ */
{ config_location_local, false } /* local data eg. $PWD */
@ -631,6 +653,16 @@ bool config_get_inhibit_screensaver(GameModeConfig *self)
return val == 1;
}
/*
* Gets the disable splitlock setting
*/
bool config_get_disable_splitlock(GameModeConfig *self)
{
long val;
memcpy_locked_config(self, &val, &self->values.disable_splitlock, sizeof(long));
return val == 1;
}
/*
* Get a set of scripts to call when gamemode starts
*/
@ -666,13 +698,29 @@ void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALU
}
/*
* Get the chosen desired governor
* Get the chosen desired platform profile
*/
void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self, governor, self->values.desiredgov, sizeof(self->values.desiredgov));
}
/*
* Get the chosen default platform profile
*/
void config_get_default_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self, profile, self->values.defaultprof, sizeof(self->values.defaultprof));
}
/*
* Get the chosen desired governor
*/
void config_get_desired_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self, profile, self->values.desiredprof, sizeof(self->values.desiredprof));
}
/*
* Get the chosen iGPU desired governor
*/
@ -794,6 +842,25 @@ void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VA
*/
DEFINE_CONFIG_GET(require_supervisor)
/*
* Get various config info for cpu optimisations
*/
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
value,
&self->values.cpu_park_cores,
sizeof(self->values.cpu_park_cores));
}
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
value,
&self->values.cpu_pin_cores,
sizeof(self->values.cpu_pin_cores));
}
/*
* Checks if the supervisor is whitelisted
*/

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -102,11 +102,14 @@ bool config_get_inhibit_screensaver(GameModeConfig *self);
long config_get_script_timeout(GameModeConfig *self);
void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
void config_get_default_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX]);
void config_get_desired_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX]);
void config_get_igpu_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
float config_get_igpu_power_threshold(GameModeConfig *self);
void config_get_soft_realtime(GameModeConfig *self, char softrealtime[CONFIG_VALUE_MAX]);
long config_get_renice_value(GameModeConfig *self);
long config_get_ioprio_value(GameModeConfig *self);
bool config_get_disable_splitlock(GameModeConfig *self);
/*
* Get various config info for gpu optimisations
@ -118,6 +121,12 @@ long config_get_nv_mem_clock_mhz_offset(GameModeConfig *self);
long config_get_nv_powermizer_mode(GameModeConfig *self);
void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
/*
* Get various config info for cpu optimisations
*/
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
/**
* Functions to get supervisor config permissions
*/

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -36,6 +36,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-helpers.h"
#include "common-logging.h"
#include "common-power.h"
#include "common-profile.h"
#include "common-splitlock.h"
#include "gamemode.h"
#include "gamemode-config.h"
@ -45,6 +47,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <sys/time.h>
@ -70,6 +73,11 @@ enum GameModeGovernor {
GAME_MODE_GOVERNOR_IGPU_DESIRED,
};
enum GameModeProfile {
GAME_MODE_PROFILE_DEFAULT,
GAME_MODE_PROFILE_DESIRED,
};
struct GameModeContext {
pthread_rwlock_t rwlock; /**<Guard access to the client list */
_Atomic int refcount; /**<Allow cycling the game mode */
@ -81,13 +89,22 @@ struct GameModeContext {
enum GameModeGovernor current_govenor;
char initial_profile[64];
enum GameModeProfile current_profile;
struct GameModeGPUInfo *stored_gpu; /**<Stored GPU info for the current GPU */
struct GameModeGPUInfo *target_gpu; /**<Target GPU info for the current GPU */
struct GameModeCPUInfo *cpu; /**<Stored CPU info for the current CPU */
GameModeIdleInhibitor *idle_inhibitor;
bool igpu_optimization_enabled;
uint32_t last_cpu_energy_uj;
uint32_t last_igpu_energy_uj;
long initial_split_lock_mitigate;
/* Reaper control */
struct {
pthread_t thread;
@ -111,6 +128,7 @@ static void game_mode_context_enter(GameModeContext *self);
static void game_mode_context_leave(GameModeContext *self);
static char *game_mode_context_find_exe(pid_t pid);
static void game_mode_execute_scripts(char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX], int timeout);
static int game_mode_disable_splitlock(GameModeContext *self, bool disable);
static void start_reaper_thread(GameModeContext *self)
{
@ -145,6 +163,11 @@ void game_mode_context_init(GameModeContext *self)
game_mode_initialise_gpu(self->config, &self->stored_gpu);
game_mode_initialise_gpu(self->config, &self->target_gpu);
/* Initialise the current CPU info */
game_mode_initialise_cpu(self->config, &self->cpu);
self->initial_split_lock_mitigate = -1;
pthread_rwlock_init(&self->rwlock, NULL);
/* Get the reaper thread going */
@ -187,31 +210,72 @@ void game_mode_context_destroy(GameModeContext *self)
game_mode_free_gpu(&self->stored_gpu);
game_mode_free_gpu(&self->target_gpu);
/* Destroy the cpu object */
game_mode_free_cpu(&self->cpu);
/* Destroy the config object */
config_destroy(self->config);
pthread_rwlock_destroy(&self->rwlock);
}
static void game_mode_store_splitlock(GameModeContext *self)
{
long initial_state = get_splitlock_state();
self->initial_split_lock_mitigate = initial_state;
LOG_MSG("split lock mitigation was initially set to [%ld]\n", initial_state);
}
static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
{
if (!config_get_disable_splitlock(self->config))
return 0;
long value_num = self->initial_split_lock_mitigate;
char value_str[40];
if (disable && value_num == 0)
return 0;
if (value_num == -1)
return 0;
sprintf(value_str, "%ld", value_num);
const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/procsysctl", "split_lock_mitigate", value_str, NULL,
};
LOG_MSG("Requesting update of split_lock_mitigate to %s\n", value_str);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to update split_lock_mitigate\n");
return ret;
}
return 0;
}
static void game_mode_store_governor(GameModeContext *self)
{
if (self->current_govenor != GAME_MODE_GOVERNOR_DEFAULT)
return;
const char *initial_state = get_gov_state();
if (initial_state == NULL)
return;
strncpy(self->initial_cpu_mode, initial_state, sizeof(self->initial_cpu_mode) - 1);
self->initial_cpu_mode[sizeof(self->initial_cpu_mode) - 1] = '\0';
LOG_MSG("governor was initially set to [%s]\n", initial_state);
}
static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor gov)
{
if (self->current_govenor == gov) {
return 0;
}
if (self->current_govenor == GAME_MODE_GOVERNOR_DEFAULT) {
/* Read the initial governor state so we can revert it correctly */
const char *initial_state = get_gov_state();
if (initial_state == NULL) {
return 0;
}
/* store the initial cpu governor mode */
strncpy(self->initial_cpu_mode, initial_state, sizeof(self->initial_cpu_mode) - 1);
self->initial_cpu_mode[sizeof(self->initial_cpu_mode) - 1] = '\0';
LOG_MSG("governor was initially set to [%s]\n", initial_state);
}
char *gov_str = NULL;
char gov_config_str[CONFIG_VALUE_MAX] = { 0 };
switch (gov) {
@ -235,7 +299,7 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
}
const char *const exec_args[] = {
"/usr/bin/pkexec", LIBEXECDIR "/cpugovctl", "set", gov_str, NULL,
"pkexec", LIBEXECDIR "/cpugovctl", "set", gov_str, NULL,
};
LOG_MSG("Requesting update of governor policy to %s\n", gov_str);
@ -251,6 +315,65 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
return 0;
}
static void game_mode_store_profile(GameModeContext *self)
{
if (!profile_exists() || self->current_profile != GAME_MODE_PROFILE_DEFAULT)
return;
const char *initial_state = get_profile_state();
if (initial_state == NULL)
return;
strncpy(self->initial_profile, initial_state, sizeof(self->initial_profile) - 1);
self->initial_profile[sizeof(self->initial_profile) - 1] = '\0';
LOG_MSG("platform profile was initially set to [%s]\n", initial_state);
}
static int game_mode_set_profile(GameModeContext *self, enum GameModeProfile prof)
{
if (self->current_profile == prof) {
return 0;
}
if (!profile_exists()) {
LOG_MSG("Setting platform profile unsupported; skipping\n");
return 0;
}
const char *prof_str = NULL;
char prof_config_str[CONFIG_VALUE_MAX] = { 0 };
switch (prof) {
case GAME_MODE_PROFILE_DEFAULT:
config_get_default_profile(self->config, prof_config_str);
prof_str = prof_config_str[0] != '\0' ? prof_config_str : self->initial_profile;
break;
case GAME_MODE_PROFILE_DESIRED:
config_get_desired_profile(self->config, prof_config_str);
prof_str = prof_config_str[0] != '\0' ? prof_config_str : "performance";
break;
default:
assert(!"Invalid platform profile requested");
}
const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/platprofctl", "set", prof_str, NULL,
};
LOG_MSG("Requesting update of platform profile to %s\n", prof_str);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to update platform profile\n");
return ret;
}
/* Update the current govenor only if we succeed at setting govenors. */
self->current_profile = prof;
return 0;
}
static void game_mode_enable_igpu_optimization(GameModeContext *self)
{
float threshold = config_get_igpu_power_threshold(self->config);
@ -338,6 +461,15 @@ unlock:
pthread_rwlock_unlock(&self->rwlock);
}
static void game_mode_context_store_defaults(GameModeContext *self)
{
game_mode_store_profile(self);
game_mode_store_governor(self);
game_mode_store_splitlock(self);
}
/**
* Pivot into game mode.
*
@ -349,6 +481,14 @@ static void game_mode_context_enter(GameModeContext *self)
LOG_MSG("Entering Game Mode...\n");
sd_notifyf(0, "STATUS=%sGameMode is now active.%s\n", "\x1B[1;32m", "\x1B[0m");
/* Store the default value for everything before anything changes. */
game_mode_context_store_defaults(self);
/* Set the profile before anything else since it can restrict things
* like the governor.
*/
game_mode_set_profile(self, GAME_MODE_PROFILE_DESIRED);
if (game_mode_set_governor(self, GAME_MODE_GOVERNOR_DESIRED) == 0) {
/* We just switched to a non-default governor. Enable the iGPU
* optimization.
@ -357,13 +497,19 @@ static void game_mode_context_enter(GameModeContext *self)
}
/* Inhibit the screensaver */
if (config_get_inhibit_screensaver(self->config))
game_mode_inhibit_screensaver(true);
if (config_get_inhibit_screensaver(self->config)) {
game_mode_destroy_idle_inhibitor(self->idle_inhibitor);
self->idle_inhibitor = game_mode_create_idle_inhibitor();
}
game_mode_disable_splitlock(self, true);
/* Apply GPU optimisations by first getting the current values, and then setting the target */
game_mode_get_gpu(self->stored_gpu);
game_mode_apply_gpu(self->target_gpu);
game_mode_park_cpu(self->cpu);
/* Run custom scripts last - ensures the above are applied first and these scripts can react to
* them if needed */
char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
@ -384,12 +530,23 @@ static void game_mode_context_leave(GameModeContext *self)
LOG_MSG("Leaving Game Mode...\n");
sd_notifyf(0, "STATUS=%sGameMode is currently deactivated.%s\n", "\x1B[1;36m", "\x1B[0m");
/* Restore profile before anything else since it can restrict things
* like the governor.
*/
game_mode_set_profile(self, GAME_MODE_PROFILE_DEFAULT);
/* Remove GPU optimisations */
game_mode_apply_gpu(self->stored_gpu);
game_mode_unpark_cpu(self->cpu);
/* UnInhibit the screensaver */
if (config_get_inhibit_screensaver(self->config))
game_mode_inhibit_screensaver(false);
if (config_get_inhibit_screensaver(self->config)) {
game_mode_destroy_idle_inhibitor(self->idle_inhibitor);
self->idle_inhibitor = NULL;
}
game_mode_disable_splitlock(self, false);
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DEFAULT);
@ -521,6 +678,9 @@ static int game_mode_apply_client_optimisations(GameModeContext *self, pid_t cli
/* Apply scheduler policies */
game_mode_apply_scheduling(self, client);
/* Apply core pinning */
game_mode_apply_core_pinning(self->cpu, client, false);
return 0;
}
@ -633,6 +793,8 @@ static int game_mode_remove_client_optimisations(GameModeContext *self, pid_t cl
/* Restore the renice value for the process, expecting it to be our config value */
game_mode_apply_renice(self, client, (int)config_get_renice_value(self->config));
/* Restore the process affinity to all online cores */
game_mode_undo_core_pinning(self->cpu, client);
return 0;
}
@ -870,6 +1032,16 @@ uint64_t game_mode_client_get_timestamp(GameModeClient *client)
return (uint64_t)client->timestamp;
}
static void game_mode_reapply_core_pinning_internal(GameModeContext *self)
{
pthread_rwlock_wrlock(&self->rwlock);
if (game_mode_context_num_clients(self)) {
for (GameModeClient *cl = self->client; cl; cl = cl->next)
game_mode_apply_core_pinning(self->cpu, cl->pid, true);
}
pthread_rwlock_unlock(&self->rwlock);
}
/* Internal refresh config function (assumes no contention with reaper thread) */
static void game_mode_reload_config_internal(GameModeContext *self)
{
@ -888,6 +1060,7 @@ static void game_mode_reload_config_internal(GameModeContext *self)
/* Reload the config */
config_reload(self->config);
game_mode_reconfig_cpu(self->config, &self->cpu);
/* Re-apply all current optimisations */
if (game_mode_context_num_clients(self)) {
@ -933,6 +1106,9 @@ static void *game_mode_context_reaper(void *userdata)
/* Expire remaining entries */
game_mode_context_auto_expire(self);
/* Re apply the thread affinity mask (aka core pinning) */
game_mode_reapply_core_pinning_internal(self);
/* Check if we should be reloading the config, and do so if needed */
if (config_needs_reload(self->config)) {
LOG_MSG("Detected config file changes\n");
@ -1006,7 +1182,7 @@ fail:
static void game_mode_execute_scripts(char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX], int timeout)
{
unsigned int i = 0;
while (*scripts[i] != '\0' && i < CONFIG_LIST_MAX) {
while (i < CONFIG_LIST_MAX && *scripts[i] != '\0') {
LOG_MSG("Executing script [%s]\n", scripts[i]);
int err;
const char *args[] = { "/bin/sh", "-c", scripts[i], NULL };

540
daemon/gamemode-cpu.c Normal file
View File

@ -0,0 +1,540 @@
/*
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <linux/limits.h>
#include <dirent.h>
#include <sched.h>
#include "common-cpu.h"
#include "common-external.h"
#include "common-helpers.h"
#include "common-logging.h"
#include "gamemode.h"
#include "gamemode-config.h"
#include "build-config.h"
static int read_small_file(char *path, char **buf, size_t *buflen)
{
FILE *f = fopen(path, "r");
if (!f) {
LOG_ERROR("Couldn't open file at %s : %s\n", path, strerror(errno));
return 0;
}
ssize_t nread = getline(buf, buflen, f);
if (nread == -1) {
LOG_ERROR("Couldn't read file at %s : %s\n", path, strerror(errno));
fclose(f);
return 0;
}
fclose(f);
while (nread > 0 && ((*buf)[nread - 1] == '\n' || (*buf)[nread - 1] == '\r'))
nread--;
(*buf)[nread] = '\0';
return 1;
}
static void set_online_from_list(char *cpulist, GameModeCPUInfo *info)
{
long from, to;
while ((cpulist = parse_cpulist(cpulist, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online);
}
}
}
static int check_pe_cores(char **buf, size_t *buflen, GameModeCPUInfo *info)
{
if (!read_small_file("/sys/devices/cpu_core/cpus", buf, buflen))
return 0;
LOG_MSG("found kernel support for checking P/E-cores\n");
long from, to;
char *list = *buf;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
}
}
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0)
LOG_MSG("kernel did not indicate that this was an P/E-cores system\n");
return 1;
}
static int walk_sysfs(char *cpulist, char **buf, size_t *buflen, GameModeCPUInfo *info)
{
char path[PATH_MAX];
unsigned long long max_cache = 0, max_freq = 0;
long from, to;
cpu_set_t *freq_cores = CPU_ALLOC(info->num_cpu);
char *list = cpulist;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
/* check for L3 cache non-uniformity among the cores */
int ret =
snprintf(path, PATH_MAX, "/sys/devices/system/cpu/cpu%ld/cache/index3/size", cpu);
if (ret > 0 && ret < PATH_MAX) {
if (read_small_file(path, buf, buflen)) {
char *endp;
unsigned long long cache_size = strtoull(*buf, &endp, 10);
if (*endp == 'K') {
cache_size *= 1024;
} else if (*endp == 'M') {
cache_size *= 1024 * 1024;
} else if (*endp == 'G') {
cache_size *= 1024 * 1024 * 1024;
} else if (*endp != '\0') {
LOG_MSG("cpu L3 cache size (%s) on core #%ld is silly\n", *buf, cpu);
cache_size = 0;
}
if (cache_size > max_cache) {
max_cache = cache_size;
CPU_ZERO_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
}
if (cache_size == max_cache)
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
}
}
/* check for frequency non-uniformity among the cores */
ret = snprintf(path,
PATH_MAX,
"/sys/devices/system/cpu/cpu%ld/cpufreq/cpuinfo_max_freq",
cpu);
if (ret > 0 && ret < PATH_MAX) {
if (read_small_file(path, buf, buflen)) {
unsigned long long freq = strtoull(*buf, NULL, 10);
unsigned long long cutoff = (freq * 10) / 100;
if (freq > max_freq) {
if (max_freq < freq - cutoff)
CPU_ZERO_S(CPU_ALLOC_SIZE(info->num_cpu), freq_cores);
max_freq = freq;
}
if (freq + cutoff >= max_freq)
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), freq_cores);
}
}
}
}
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0) {
LOG_MSG("cpu L3 cache was uniform, this is not a x3D with multiple chiplets\n");
CPU_FREE(info->to_keep);
info->to_keep = freq_cores;
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0)
LOG_MSG("cpu frequency was uniform, this is not a big.LITTLE type of system\n");
} else {
CPU_FREE(freq_cores);
}
return 1;
}
static int walk_string(char *cpulist, char *config_cpulist, GameModeCPUInfo *info)
{
long from, to;
char *list = cpulist;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online);
if (info->park_or_pin == IS_CPU_PARK)
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
}
}
list = config_cpulist;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
if (CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online)) {
if (info->park_or_pin == IS_CPU_PARK)
CPU_CLR_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
else
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
}
}
}
return 1;
}
void game_mode_reconfig_cpu(GameModeConfig *config, GameModeCPUInfo **info)
{
game_mode_unpark_cpu(*info);
game_mode_free_cpu(info);
game_mode_initialise_cpu(config, info);
}
int game_mode_initialise_cpu(GameModeConfig *config, GameModeCPUInfo **info)
{
/* Verify input, this is programmer error */
if (!info || *info)
FATAL_ERROR("Invalid GameModeCPUInfo passed to %s", __func__);
/* Early out if we have this feature turned off */
char park_cores[CONFIG_VALUE_MAX];
char pin_cores[CONFIG_VALUE_MAX];
config_get_cpu_park_cores(config, park_cores);
config_get_cpu_pin_cores(config, pin_cores);
int park_or_pin = -1;
if (pin_cores[0] != '\0') {
if (strcasecmp(pin_cores, "no") == 0 || strcasecmp(pin_cores, "false") == 0 ||
strcmp(pin_cores, "0") == 0) {
park_or_pin = -2;
} else if (strcasecmp(pin_cores, "yes") == 0 || strcasecmp(pin_cores, "true") == 0 ||
strcmp(pin_cores, "1") == 0) {
pin_cores[0] = '\0';
park_or_pin = IS_CPU_PIN;
} else {
park_or_pin = IS_CPU_PIN;
}
}
if (park_or_pin != IS_CPU_PIN && park_cores[0] != '\0') {
if (strcasecmp(park_cores, "no") == 0 || strcasecmp(park_cores, "false") == 0 ||
strcmp(park_cores, "0") == 0) {
if (park_or_pin == -2)
return 0;
park_or_pin = -1;
} else if (strcasecmp(park_cores, "yes") == 0 || strcasecmp(park_cores, "true") == 0 ||
strcmp(park_cores, "1") == 0) {
park_cores[0] = '\0';
park_or_pin = IS_CPU_PARK;
} else {
park_or_pin = IS_CPU_PARK;
}
}
/* always default to pin */
if (park_or_pin != IS_CPU_PARK)
park_or_pin = IS_CPU_PIN;
char *buf = NULL, *buf2 = NULL;
size_t buflen = 0, buf2len = 0;
/* first we find which cores are online, this also helps us to determine the max
* cpu core number that we need to allocate the cpulist later */
if (!read_small_file("/sys/devices/system/cpu/online", &buf, &buflen))
goto error_exit;
long from, to, max = 0;
char *s = buf;
while ((s = parse_cpulist(s, &from, &to))) {
if (to > max)
max = to;
}
/* either parsing failed or we have only a single core, in either case
* we cannot optimize anyway */
if (max == 0)
goto early_exit;
GameModeCPUInfo *new_info = malloc(sizeof(GameModeCPUInfo));
memset(new_info, 0, sizeof(GameModeCPUInfo));
new_info->num_cpu = (size_t)(max + 1);
new_info->park_or_pin = park_or_pin;
new_info->online = CPU_ALLOC(new_info->num_cpu);
new_info->to_keep = CPU_ALLOC(new_info->num_cpu);
CPU_ZERO_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->online);
CPU_ZERO_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->to_keep);
if (park_or_pin == IS_CPU_PARK && park_cores[0] != '\0') {
if (!walk_string(buf, park_cores, new_info))
goto error_exit;
} else if (park_or_pin == IS_CPU_PIN && pin_cores[0] != '\0') {
if (!walk_string(buf, pin_cores, new_info))
goto error_exit;
} else {
set_online_from_list(buf, new_info);
if (!check_pe_cores(&buf2, &buf2len, new_info)) {
if (!walk_sysfs(buf, &buf2, &buf2len, new_info))
goto error_exit;
}
}
if (park_or_pin == IS_CPU_PARK &&
CPU_EQUAL_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->online, new_info->to_keep)) {
game_mode_free_cpu(&new_info);
LOG_MSG("I can find no reason to perform core parking on this system!\n");
goto error_exit;
}
if (CPU_COUNT_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->to_keep) == 0) {
game_mode_free_cpu(&new_info);
LOG_MSG("I can find no reason to perform core pinning on this system!\n");
goto error_exit;
}
if (CPU_COUNT_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->to_keep) < 4) {
game_mode_free_cpu(&new_info);
LOG_MSG(
"logic or config would result in less than 4 active cores, will not apply cpu core "
"parking/pinning!\n");
goto error_exit;
}
*info = new_info;
early_exit:
free(buf);
free(buf2);
return 0;
error_exit:
free(buf);
free(buf2);
return -1;
}
static int log_state(char *cpulist, int *pos, const long first, const long last)
{
int ret;
if (*pos != 0) {
ret = snprintf(cpulist + *pos, ARG_MAX - (size_t)*pos, ",");
if (ret < 0 || (size_t)ret >= (ARG_MAX - (size_t)*pos)) {
LOG_ERROR("snprintf failed, will not apply cpu core parking!\n");
return 0;
}
*pos += ret;
}
if (first == last)
ret = snprintf(cpulist + *pos, ARG_MAX - (size_t)*pos, "%ld", first);
else
ret = snprintf(cpulist + *pos, ARG_MAX - (size_t)*pos, "%ld-%ld", first, last);
if (ret < 0 || (size_t)ret >= (ARG_MAX - (size_t)*pos)) {
LOG_ERROR("snprintf failed, will not apply cpu core parking!\n");
return 0;
}
*pos += ret;
return 1;
}
int game_mode_park_cpu(const GameModeCPUInfo *info)
{
if (!info || info->park_or_pin == IS_CPU_PIN)
return 0;
long first = -1, last = -1;
char cpulist[ARG_MAX];
int pos = 0;
for (long cpu = 0; cpu < (long)(info->num_cpu); cpu++) {
if (CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online) &&
!CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep)) {
if (first == -1) {
first = cpu;
last = cpu;
} else if (last + 1 == cpu) {
last = cpu;
} else {
if (!log_state(cpulist, &pos, first, last))
return 0;
first = cpu;
last = cpu;
}
}
}
if (first != -1)
log_state(cpulist, &pos, first, last);
const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/cpucorectl", "offline", cpulist, NULL,
};
LOG_MSG("Requesting parking of cores %s\n", cpulist);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to park cpu cores\n");
return ret;
}
return 0;
}
int game_mode_unpark_cpu(const GameModeCPUInfo *info)
{
if (!info || info->park_or_pin == IS_CPU_PIN)
return 0;
long first = -1, last = -1;
char cpulist[ARG_MAX];
int pos = 0;
for (long cpu = 0; cpu < (long)(info->num_cpu); cpu++) {
if (CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online) &&
!CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep)) {
if (first == -1) {
first = cpu;
last = cpu;
} else if (last + 1 == cpu) {
last = cpu;
} else {
if (!log_state(cpulist, &pos, first, last))
return 0;
first = cpu;
last = cpu;
}
}
}
if (first != -1)
log_state(cpulist, &pos, first, last);
const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/cpucorectl", "online", cpulist, NULL,
};
LOG_MSG("Requesting unparking of cores %s\n", cpulist);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to unpark cpu cores\n");
return ret;
}
return 0;
}
static void apply_affinity_mask(pid_t pid, size_t cpusetsize, const cpu_set_t *mask,
const bool be_silent)
{
char buffer[PATH_MAX];
char *proc_path = NULL;
DIR *proc_dir = NULL;
if (!(proc_path = buffered_snprintf(buffer, "/proc/%d/task", pid))) {
if (!be_silent) {
LOG_ERROR("Unable to find executable for PID %d: %s\n", pid, strerror(errno));
}
return;
}
if (!(proc_dir = opendir(proc_path))) {
if (!be_silent) {
LOG_ERROR("Unable to find executable for PID %d: %s\n", pid, strerror(errno));
}
return;
}
struct dirent *entry;
while ((entry = readdir(proc_dir))) {
if (entry->d_name[0] == '.')
continue;
int tid = atoi(entry->d_name);
if (sched_setaffinity(tid, cpusetsize, mask) != 0 && !be_silent)
LOG_ERROR("Failed to pin thread %d: %s\n", tid, strerror(errno));
}
closedir(proc_dir);
}
void game_mode_apply_core_pinning(const GameModeCPUInfo *info, const pid_t client,
const bool be_silent)
{
if (!info || info->park_or_pin == IS_CPU_PARK)
return;
if (!be_silent)
LOG_MSG("Pinning process...\n");
apply_affinity_mask(client, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep, be_silent);
}
void game_mode_undo_core_pinning(const GameModeCPUInfo *info, const pid_t client)
{
if (!info || info->park_or_pin == IS_CPU_PARK)
return;
LOG_MSG("Pinning process back to all online cores...\n");
apply_affinity_mask(client, CPU_ALLOC_SIZE(info->num_cpu), info->online, false);
}
void game_mode_free_cpu(GameModeCPUInfo **info)
{
if ((*info)) {
CPU_FREE((*info)->online);
(*info)->online = NULL;
CPU_FREE((*info)->to_keep);
(*info)->to_keep = NULL;
free(*info);
*info = NULL;
}
}

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -36,8 +36,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-logging.h"
#include "common-pidfds.h"
#ifdef USE_ELOGIND
#include <elogind/sd-bus.h>
#include <elogind/sd-daemon.h>
#else
#include <systemd/sd-bus.h>
#include <systemd/sd-daemon.h>
#endif
#include <assert.h>
#include <errno.h>
@ -703,81 +708,106 @@ void game_mode_context_loop(GameModeContext *context)
}
}
struct GameModeIdleInhibitor {
sd_bus *bus;
unsigned int cookie;
};
/**
* Attempts to inhibit the screensaver
* Uses the "org.freedesktop.ScreenSaver" interface
*/
static unsigned int screensaver_inhibit_cookie = 0;
int game_mode_inhibit_screensaver(bool inhibit)
GameModeIdleInhibitor *game_mode_create_idle_inhibitor(void)
{
const char *service = "org.freedesktop.ScreenSaver";
const char *object_path = "/org/freedesktop/ScreenSaver";
const char *interface = "org.freedesktop.ScreenSaver";
const char *function = inhibit ? "Inhibit" : "UnInhibit";
sd_bus_message *msg = NULL;
sd_bus *bus_local = NULL;
sd_bus_error err;
memset(&err, 0, sizeof(sd_bus_error));
int result = -1;
sd_bus_error err = SD_BUS_ERROR_NULL;
// Open the user bus
int ret = sd_bus_open_user(&bus_local);
if (ret < 0) {
LOG_ERROR("Could not connect to user bus: %s\n", strerror(-ret));
return -1;
return NULL;
}
if (inhibit) {
ret = sd_bus_call_method(bus_local,
service,
object_path,
interface,
function,
&err,
&msg,
"ss",
"com.feralinteractive.GameMode",
"GameMode Activated");
} else {
ret = sd_bus_call_method(bus_local,
service,
object_path,
interface,
function,
&err,
&msg,
"u",
screensaver_inhibit_cookie);
}
ret = sd_bus_call_method(bus_local,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"Inhibit",
&err,
&msg,
"ss",
"com.feralinteractive.GameMode",
"GameMode Activated");
if (ret < 0) {
LOG_ERROR(
"Could not call %s on %s: %s\n"
"Failed to call Inhibit on org.freedesktop.ScreenSaver: %s\n"
"\t%s\n"
"\t%s\n",
function,
service,
strerror(-ret),
err.name,
err.message);
} else if (inhibit) {
// Read the reply
ret = sd_bus_message_read(msg, "u", &screensaver_inhibit_cookie);
if (ret < 0) {
LOG_ERROR("Failure to parse response from %s on %s: %s\n",
function,
service,
strerror(-ret));
} else {
result = 0;
}
} else {
result = 0;
sd_bus_close(bus_local);
sd_bus_unrefp(&bus_local);
return NULL;
}
sd_bus_unref(bus_local);
// Read the reply
unsigned int cookie = 0;
ret = sd_bus_message_read(msg, "u", &cookie);
if (ret < 0) {
LOG_ERROR("Invalid response from Inhibit on org.freedesktop.ScreenSaver: %s\n",
strerror(-ret));
sd_bus_close(bus_local);
sd_bus_unrefp(&bus_local);
return NULL;
}
return result;
GameModeIdleInhibitor *inhibitor = malloc(sizeof(GameModeIdleInhibitor));
if (inhibitor == NULL) {
sd_bus_close(bus_local);
sd_bus_unrefp(&bus_local);
return NULL;
}
inhibitor->bus = bus_local;
inhibitor->cookie = cookie;
return inhibitor;
}
void game_mode_destroy_idle_inhibitor(GameModeIdleInhibitor *inhibitor)
{
sd_bus_message *msg = NULL;
sd_bus_error err = SD_BUS_ERROR_NULL;
if (inhibitor == NULL) {
return;
}
int ret = sd_bus_call_method(inhibitor->bus,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"UnInhibit",
&err,
&msg,
"u",
inhibitor->cookie);
if (ret < 0) {
LOG_ERROR(
"Failed to call UnInhibit on org.freedesktop.ScreenSaver: %s\n"
"\t%s\n"
"\t%s\n",
strerror(-ret),
err.name,
err.message);
}
sd_bus_close(inhibitor->bus);
sd_bus_unrefp(&inhibitor->bus);
free(inhibitor);
}

View File

@ -1,7 +1,7 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -167,7 +167,7 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info)
// Set up our command line to pass to gpuclockctl
const char *const exec_args[] = {
"/usr/bin/pkexec",
"pkexec",
LIBEXECDIR "/gpuclockctl",
device,
"set",

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -60,7 +60,7 @@ POSSIBILITY OF SUCH DAMAGE.
#endif
#ifndef IOPRIO_PRIO_DATA
#define IOPRIO_PRIO_DATA(mask) ((mask)&IOPRIO_PRIO_MASK)
#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
#endif
#ifndef IOPRIO_PRIO_VALUE

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-gpu.h"
#include "common-helpers.h"
#include "common-logging.h"
#include "common-profile.h"
#include "gamemode.h"
#include "gamemode-config.h"
@ -268,8 +269,7 @@ static int run_gamemoderun_and_reaper_tests(struct GameModeConfig *config)
/* Close stdout, we don't care if sh prints anything */
fclose(stdout);
/* Preload into sh and then kill it */
if (execl("/usr/bin/gamemoderun", "/usr/bin/gamemoderun", "sleep", "5", (char *)NULL) ==
-1) {
if (execlp("gamemoderun", "gamemoderun", "sleep", "5", (char *)NULL) == -1) {
LOG_ERROR("failed to launch gamemoderun with execl: %s\n", strerror(errno));
return -1;
}
@ -358,6 +358,62 @@ static int run_cpu_governor_tests(struct GameModeConfig *config)
return 0;
}
/* Check the platform profile setting works */
static int run_platform_profile_tests(struct GameModeConfig *config)
{
if (!profile_exists())
return 1;
/* get the two config parameters we care about */
char desiredprof[CONFIG_VALUE_MAX] = { 0 };
config_get_desired_profile(config, desiredprof);
if (desiredprof[0] == '\0')
strcpy(desiredprof, "performance");
char defaultprof[CONFIG_VALUE_MAX] = { 0 };
config_get_default_profile(config, defaultprof);
if (defaultprof[0] == '\0') {
const char *currentprof = get_profile_state();
if (currentprof) {
strncpy(defaultprof, currentprof, CONFIG_VALUE_MAX - 1);
} else {
LOG_ERROR(
"Could not get current platform profile state, this indicates an error! See rest "
"of log.\n");
return -1;
}
}
/* Start gamemode */
gamemode_request_start();
/* Verify the platform profile is the desired one */
const char *currentprof = get_profile_state();
if (strncmp(currentprof, desiredprof, CONFIG_VALUE_MAX) != 0) {
LOG_ERROR("Platform profile was not set to %s (was actually %s)!\n",
desiredprof,
currentprof);
gamemode_request_end();
return -1;
}
/* End gamemode */
gamemode_request_end();
/* Verify the platform profile has been set back */
currentprof = get_profile_state();
if (strncmp(currentprof, defaultprof, CONFIG_VALUE_MAX) != 0) {
LOG_ERROR("Platform profile was not set back to %s (was actually %s)!\n",
defaultprof,
currentprof);
return -1;
}
return 0;
}
static int run_custom_scripts_tests(struct GameModeConfig *config)
{
int scriptstatus = 0;
@ -370,7 +426,7 @@ static int run_custom_scripts_tests(struct GameModeConfig *config)
if (startscripts[0][0] != '\0') {
int i = 0;
while (*startscripts[i] != '\0' && i < CONFIG_LIST_MAX) {
while (i < CONFIG_LIST_MAX && *startscripts[i] != '\0') {
LOG_MSG(":::: Running start script [%s]\n", startscripts[i]);
const char *args[] = { "/bin/sh", "-c", startscripts[i], NULL };
@ -393,7 +449,7 @@ static int run_custom_scripts_tests(struct GameModeConfig *config)
if (endscripts[0][0] != '\0') {
int i = 0;
while (*endscripts[i] != '\0' && i < CONFIG_LIST_MAX) {
while (i < CONFIG_LIST_MAX && *endscripts[i] != '\0') {
LOG_MSG(":::: Running end script [%s]\n", endscripts[i]);
const char *args[] = { "/bin/sh", "-c", endscripts[i], NULL };
@ -802,11 +858,32 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
LOG_MSG(" -- You may need to add your user to the gamemode group:");
LOG_MSG(" -- $ sudo usermod -aG gamemode $(whoami)");
// Consider the CPU governor feature required
status = -1;
}
}
/* Does the platform profile get set properly? */
{
LOG_MSG("::: Verifying platform profile setting\n");
int profstatus = run_platform_profile_tests(config);
if (profstatus == 1)
LOG_MSG("::: Passed (platform profile not supported)\n");
else if (profstatus == 0)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
LOG_MSG(" -- You may need to add your user to the gamemode group:");
LOG_MSG(" -- $ sudo usermod -aG gamemode $(whoami)");
// If available, setting the platform profile should work
status = -1;
}
}
/* Do custom scripts run? */
{
LOG_MSG("::: Verifying Scripts\n");

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -224,7 +224,8 @@ char *game_mode_resolve_wine_preloader(const char *exe, const pid_t pid)
goto fail;
error_cleanup:
game_mode_close_proc(proc_fd);
if (proc_fd != INVALID_PROCFD)
game_mode_close_proc(proc_fd);
free(wineprefix);
return wine_exe;

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -203,10 +203,25 @@ void game_mode_free_gpu(GameModeGPUInfo **info);
int game_mode_apply_gpu(const GameModeGPUInfo *info);
int game_mode_get_gpu(GameModeGPUInfo *info);
/** gamemode-cpu.c
* Provides internal functions to apply optimisations to cpus
*/
typedef struct GameModeCPUInfo GameModeCPUInfo;
int game_mode_initialise_cpu(GameModeConfig *config, GameModeCPUInfo **info);
void game_mode_free_cpu(GameModeCPUInfo **info);
void game_mode_reconfig_cpu(GameModeConfig *config, GameModeCPUInfo **info);
int game_mode_park_cpu(const GameModeCPUInfo *info);
int game_mode_unpark_cpu(const GameModeCPUInfo *info);
void game_mode_apply_core_pinning(const GameModeCPUInfo *info, const pid_t client,
const bool be_silent);
void game_mode_undo_core_pinning(const GameModeCPUInfo *info, const pid_t client);
/** gamemode-dbus.c
* Provides an API interface for using dbus
*/
typedef struct GameModeIdleInhibitor GameModeIdleInhibitor;
void game_mode_context_loop(GameModeContext *context) __attribute__((noreturn));
int game_mode_inhibit_screensaver(bool inhibit);
GameModeIdleInhibitor *game_mode_create_idle_inhibitor(void);
void game_mode_destroy_idle_inhibitor(GameModeIdleInhibitor *inhibitor);
void game_mode_client_registered(pid_t);
void game_mode_client_unregistered(pid_t);

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -72,7 +72,7 @@ POSSIBILITY OF SUCH DAMAGE.
" When no PID given, queries the status globally\n" \
" -d, --daemonize Daemonize self after launch\n" \
" -l, --log-to-syslog Log to syslog\n" \
" -r, --test Run tests\n" \
" -t, --test Run tests\n" \
" -h, --help Print this help\n" \
" -v, --version Print version\n" \
"\n" \
@ -132,11 +132,16 @@ static void daemonize(const char *name)
/* replace standard file descriptors by /dev/null */
int devnull_r = open("/dev/null", O_RDONLY);
int devnull_w = open("/dev/null", O_WRONLY);
dup2(devnull_r, STDIN_FILENO);
dup2(devnull_w, STDOUT_FILENO);
dup2(devnull_w, STDERR_FILENO);
close(devnull_r);
close(devnull_w);
if (devnull_r == -1 || devnull_w == -1) {
LOG_ERROR("Failed to redirect standard input and output to /dev/null\n");
} else {
dup2(devnull_r, STDIN_FILENO);
dup2(devnull_w, STDOUT_FILENO);
dup2(devnull_w, STDERR_FILENO);
close(devnull_r);
close(devnull_w);
}
}
/**

View File

@ -7,20 +7,22 @@ daemon_sources = [
'gamemode-wine.c',
'gamemode-tests.c',
'gamemode-gpu.c',
'gamemode-cpu.c',
'gamemode-dbus.c',
'gamemode-config.c',
]
gamemoded_includes = libgamemode_includes
gamemoded_includes = gamemode_headers_includes
gamemoded_includes += config_h_dir
executable(
gamemoded = executable(
'gamemoded',
sources: daemon_sources,
c_args: sd_bus_args,
dependencies: [
link_daemon_common,
dep_threads,
dep_systemd,
sd_bus_dep,
inih_dependency,
libdl,
],
@ -28,4 +30,11 @@ executable(
gamemoded_includes,
],
install: true,
)
)
# verify gamemoded compiled properly
test(
'validate gamemoded compiled properly',
gamemoded,
args: ['-v'],
)

View File

@ -1 +0,0 @@
@@LIMITSGROUP@ - nice -10

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<!--
Copyright (c) 2017-2019, Feral Interactive
All rights reserved.
-->
<vendor>Feral GameMode Activation</vendor>
<vendor_url>http://www.feralinteractive.com</vendor_url>
<action id="com.feralinteractive.GameMode.governor-helper">
<description>Modify the CPU governor</description>
<message>Authentication is required to modify the CPU governor</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/cpugovctl</annotate>
</action>
<action id="com.feralinteractive.GameMode.gpu-helper">
<description>Modify the GPU clock states</description>
<message>Authentication is required to modify the GPU clock states</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/gpuclockctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>

View File

@ -0,0 +1,33 @@
.\" Manpage for gamemode-simulate-game.
.\" Contact linux-contact@feralinteractive.com to correct errors or typos.
.TH gamemode-simulate-game 1 "26 May 2020" "@GAMEMODE_VERSION@" "gamemode-simulate-game man page"
.SH NAME
gamemode-simulate-game \- simulate a game using gamemode
.SH SYNOPSIS
\fBgamemode-simulate-game\fR
.SH DESCRIPTION
\fBGameMode\fR is a daemon/lib combo for Linux that allows games to request a set of optimisations be temporarily applied to the host OS.
The design has a clear cut abstraction between the host daemon and library (\fBgamemoded\fR and \fBlibgamemode\fR), and the client loaders (\fBlibgamemodeauto\fR and \fBgamemode_client.h\fR) that allows for safe usage without worrying about whether the daemon is installed or running. This design also means that while the host library currently relies on systemd for exchanging messages with the daemon, it's entirely possible to implement other internals that still work with the same clients.
\fBGameMode\fR was designed primarily as a stop-gap solution to problems with the Intel and AMD CPU powersave or ondemand governors, but is intended to be expanded beyond just CPU governor states, as there are a wealth of automation tasks one might want to apply.
.SH USAGE
The executable starts gamemode, sleeps for 10 seconds and stops it. It will exit with zero if everything works fine, else it will print an error and exit with one.
To use this with a CI you might need to start a dbus session by hand. This can be done with:
.RS 4
dbus-run-session -- gamemode-simulate-game
.RE
Note that this might output to stderr, even if it exits with zero.
.SH SEE ALSO
gamemoded(8), gamemoderun(1), dbus-run-session(1)
.SH ABOUT
GameMode source can be found at \fIhttps://github.com/FeralInteractive/gamemode.git\fR
.SH AUTHOR
Feral Interactive (linux-contact@feralinteractive.com)

View File

@ -1,8 +1,8 @@
.\" Manpage for gamemoded.
.\" Contact linux-contact@feralinteractive.com to correct errors or typos.
.TH gamemoded 8 "22 Jan 2020" "1.5" "gamemoded man page"
.TH gamemoded 8 "4 May 2020" "@GAMEMODE_VERSION@" "gamemoded man page"
.SH NAME
gamemoded \- optimises system performance on demand
gamemoded \- daemon that optimises system performance on demand
.SH SYNOPSIS
\fBgamemoded\fR [OPTIONS...]
.SH DESCRIPTION
@ -38,31 +38,7 @@ Run diagnostic tests on the current installation
Print the version
.SH USAGE
\fBlibgamemodeauto.so.0\fR can be pre-loaded into any program to request \fBgamemoded\fR begin or end the mode, like so:
.RS 4
gamemoderun \./game
.RE
Or by setting the Steam launch options for a game:
.RS 4
gamemoderun %command%
.RE
The library can be manually preloaded if needed:
.RS 4
LD_PRELOAD=$LD_PRELOAD:/usr/\e$LIB/libgamemodeauto.so.0 ./game
.RE
It is possible to set additional start commands to gamemoderun by setting the environment variable:
.RS 4
GAMEMODERUNEXEC="command"
.RE
When this is set, gamemoderun will execute the command given by that environment variable, and the command line passed to gamemoderun will be passed as arguments to that command. GameMode will not be applied to the wrapper command, just the game itself.
\fBlibgamemodeauto.so.0\fR can be pre-loaded into any program to request \fBgamemoded\fR begin or end the mode. See gamemoderun(1) for details.
The \fBgamemode_client.h\fR header can be used by developers to build the requests into a program:
@ -97,7 +73,7 @@ Or, distribute \fBlibgamemodeauto.so.0\fR and either link with \fB\-lgamemodeaut
\fBgamemoded\fR will load and merge \fBgamemode.ini\fR config files from these directories in the following order:
.RS 4
/usr/share/gamemode/
@SYSCONFDIR@/
.RE
.RS 4
/etc/
@ -118,7 +94,7 @@ Behaviour of the config file can be explained by presenting a commented example:
.RE
.SH SEE ALSO
systemd(1)
gamemoderun(1), systemd(1)
.SH ABOUT
GameMode source can be found at \fIhttps://github.com/FeralInteractive/gamemode.git\fR

32
data/gamemodelist Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# Created by Sam Gleske
# Created Sat Jan 1 16:56:54 EST 2022
# MIT License - https://github.com/samrocketman/home
# DESCRIPTION
# Find all running processes which have loaded Feral Interactive gamemode
# via libgamemodeauto.so. This script will not detect processes which load
# gamemode without libgamemodeauto.so.
# DEVELOPMENT ENVIRONMENT
# Ubuntu 18.04.6 LTS
# Linux 5.4.0-91-generic x86_64
# GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
# find (GNU findutils) 4.7.0-git
# GNU Awk 4.1.4, API: 1.1 (GNU MPFR 4.0.1, GNU MP 6.1.2)
# xargs (GNU findutils) 4.7.0-git
# ps from procps-ng 3.3.12
if [ -z "${USER:-}" ]; then
echo '$USER variable not defined.' >&2
exit 1
fi
if [ ! -d /proc ]; then
echo 'ERROR: /proc filesystem missing. We do not appear to be running on Linux.' >&2
exit 1
fi
find /proc -maxdepth 2 -type f -user "${USER}" -readable -name maps -exec \
awk -- 'BEGINFILE { if (ERRNO) nextfile } $0 ~ /libgamemodeauto\.so\.0/ {pid=FILENAME; gsub("[^0-9]", "", pid); print pid;nextfile}' {} + \
| xargs | xargs -I{} -- ps -o pid,ppid,user,ni,psr,comm --pid '{}'

68
data/gamemodelist.1.in Normal file
View File

@ -0,0 +1,68 @@
.\" Manpage for gamemodelist.
.\" Contact linux-contact@feralinteractive.com to correct errors or typos.
.TH gamemodelist 1 "4 May 2020" "@GAMEMODE_VERSION@" "gamemodelist man page"
.SH NAME
gamemodelist \- search for processes running with gamemode
.SH SYNOPSIS
\fBgamemodelist\fR
.SH DESCRIPTION
\fBgamemodelist\fR will search the runtime of all processes running which started \fBGameMode\fR via \fBlibgamemodeauto.so\fR and print them with
.BR ps (1)
command output. This helper script makes it easy to find which processes are utilizing \fBGameMode\fR via \fBlibgamemodeauto.so\fR when troubleshooting potential game issues.
.SH USAGE
\fBlibgamemodeauto.so.0\fR will be found in the shared object maps of running processes utilizing \fBGameMode\fR. Run the following command to print processes loaded with \fBlibgamemodeauto.so.0\fR. \fBGameMode\fR can be started other ways but this script will only detect processes utilizing \fBGameMode\fR via \fBlibgamemodeauto.so\fR.
.RS 4
gamemodelist
.RE
.SH OUTPUT
The output is a process table from
.BR ps (1)
command.
.RS 4
PID PPID USER NI PSR COMMAND
.RE
Where each of these fields are defined in
.BR ps (1)
manual. For your convenience here's a definition for each field.
.RS 4
.TS
l lw(3i).
\fBCOLUMN DESCRIPTION\fR
PID Process ID
PPID Parent process ID
USER User name owning the process.
NI T{
Nice value. This ranges from 19 (nicest) to \-20 (not nice to others),
See
.IR nice (1).
T}
PSR T{
Processor that process is currently assigned to. Useful when setting process affinity using
.IR taskset (1)
utility.
T}
COMMAND Command name (only the executable name).
.TE
.RE
.SH SEE ALSO
.BR gamemodrun (1),
.BR nice (1),
.BR ps (1),
.BR taskset (1).
.SH ABOUT
GameMode source can be found at \fIhttps://github.com/FeralInteractive/gamemode.git\fR
.SH AUTHOR
.BR gamemodelist
was authored by Sam Gleske (https://github.com/samrocketman/)
.BR GameMode
was authored by Feral Interactive (linux-contact@feralinteractive.com)

9
data/gamemoderun Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# Helper script to launch games with gamemode
GAMEMODEAUTO_NAME="libgamemodeauto.so.0"
# ld will find the right path to load the library, including for 32-bit apps.
LD_PRELOAD="${GAMEMODEAUTO_NAME}${LD_PRELOAD:+:$LD_PRELOAD}"
exec env LD_PRELOAD="${LD_PRELOAD}" $GAMEMODERUNEXEC "$@"

50
data/gamemoderun.1.in Normal file
View File

@ -0,0 +1,50 @@
.\" Manpage for gamemoderun.
.\" Contact linux-contact@feralinteractive.com to correct errors or typos.
.TH gamemoderun 1 "4 May 2020" "@GAMEMODE_VERSION@" "gamemoderun man page"
.SH NAME
gamemoderun \- invoke gamemode into any program
.SH SYNOPSIS
\fBgamemoderun\fR PROGRAM
.SH DESCRIPTION
\fBGameMode\fR is a daemon/lib combo for Linux that allows games to request a set of optimisations be temporarily applied to the host OS.
The design has a clear cut abstraction between the host daemon and library (\fBgamemoded\fR and \fBlibgamemode\fR), and the client loaders (\fBlibgamemodeauto\fR and \fBgamemode_client.h\fR) that allows for safe usage without worrying about whether the daemon is installed or running. This design also means that while the host library currently relies on systemd for exchanging messages with the daemon, it's entirely possible to implement other internals that still work with the same clients.
\fBGameMode\fR was designed primarily as a stop-gap solution to problems with the Intel and AMD CPU powersave or ondemand governors, but is intended to be expanded beyond just CPU governor states, as there are a wealth of automation tasks one might want to apply.
.SH USAGE
\fBlibgamemodeauto.so.0\fR can be pre-loaded into any program to request \fBgamemoded\fR begin or end the mode, like so:
.RS 4
gamemoderun \./game
.RE
Or by setting the Steam launch options for a game:
.RS 4
gamemoderun %command%
.RE
The library can be manually preloaded if needed:
.RS 4
LD_PRELOAD=$LD_PRELOAD:/usr/\e$LIB/libgamemodeauto.so.0 ./game
.RE
.SH CONFIG
It is possible to set additional start commands to gamemoderun by setting the environment variable:
.RS 4
GAMEMODERUNEXEC="command"
.RE
When this is set, gamemoderun will execute the command given by that environment variable, and the command line passed to gamemoderun will be passed as arguments to that command. GameMode will not be applied to the wrapper command, just the game itself.
.SH SEE ALSO
gamemoded(8)
.SH ABOUT
GameMode source can be found at \fIhttps://github.com/FeralInteractive/gamemode.git\fR
.SH AUTHOR
Feral Interactive (linux-contact@feralinteractive.com)

View File

@ -1,10 +0,0 @@
#!/bin/bash
# Helper script to launch games with gamemode
# Path to installed libgamemodeauto. ld.so will substitute "\$LIB" to get the
# appropriate path depending on whether the app is 32- or 64-bit.
GAMEMODEAUTO="@GAMEMODE_PREFIX@/\$LIB/libgamemodeauto.so.0"
LD_PRELOAD="${GAMEMODEAUTO}${LD_PRELOAD:+:$LD_PRELOAD}"
exec $GAMEMODERUNEXEC env LD_PRELOAD="${LD_PRELOAD}" "$@"

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<component>
<id>io.github.feralinteractive.gamemode</id>
<name>gamemode</name>
<summary>daemon that allows games to request a set of optimizations be temporarily applied</summary>
<developer_name>Feral Interactive</developer_name>
<!-- <icon type="stock">io.github.feralinteractive.gamemode</icon> -->
<metadata_license>FSFAP</metadata_license>
<project_license>BSD-3-Clause</project_license>
<description>
<p>
GameMode is a daemon/lib combo for Linux that allows games to request a set of optimisations be temporarily applied to the host OS and/or a game process.
</p>
<p>
It was designed primarily as a stop-gap solution to problems with the Intel and AMD CPU powersave or ondemand governors, but is now host to a range of optimisation features and configurations.
</p>
<p>Currently GameMode includes support for optimisations including:</p>
<ul>
<li>CPU governor</li>
<li>I/O priority</li>
<li>Process niceness</li>
<li>Kernel scheduler (SCHED_ISO)</li>
<li>Screensaver inhibiting</li>
<li>GPU performance mode (NVIDIA and AMD)</li>
<li>GPU overclocking (NVIDIA)</li>
<li>Custom scripts</li>
</ul>
</description>
<content_rating type="oars-1.1" />
<categories>
<category>Utility</category>
<category>Game</category>
</categories>
<url type="homepage">https://feralinteractive.github.io/gamemode</url>
</component>

View File

@ -1,55 +1,157 @@
data_conf = configuration_data()
data_conf.set('BINDIR', path_bindir)
data_conf.set('LIBEXECDIR', path_libexecdir)
data_conf.set('SYSCONFDIR', path_sysconfdir)
data_conf.set('GAMEMODE_PREFIX', path_prefix)
data_conf.set('GAMEMODE_VERSION', meson.project_version())
data_conf.set('GAMEMODE_PRIVILEGED_GROUP', with_privileged_group)
# Pull in the example config
config_example = run_command(
'cat',
join_paths(meson.source_root(), 'example', 'gamemode.ini')
join_paths(meson.project_source_root(), 'example', 'gamemode.ini'),
check: true,
).stdout().strip()
data_conf.set('GAMEMODE_EXAMPLE_CONFIG', config_example)
if with_systemd == true
# Install systemd user unit
configure_file(
input: 'gamemoded.service.in',
output: 'gamemoded.service',
configuration: data_conf,
install_dir: path_systemd_unit_dir,
)
if sd_bus_provider == 'systemd'
if with_systemd_unit
# Install systemd user unit
configure_file(
input: 'systemd/user/gamemoded.service.in',
output: 'gamemoded.service',
configuration: data_conf,
install_dir: path_systemd_unit_dir,
)
endif
if with_systemd_group
# Install the sysusers.d file
configure_file(
input: 'systemd/sysusers.d/gamemode.conf.in',
output: 'gamemode.conf',
configuration: data_conf,
install_dir: path_systemd_group_dir,
)
endif
endif
if with_pam_renicing
# Install the limits.d configuration file
configure_file(
input: 'pam_limits/10-gamemode.conf.in',
output: '10-gamemode.conf',
configuration: data_conf,
install_dir: path_pam_limits_dir,
)
endif
# Install the D-BUS service file
configure_file(
input: 'com.feralinteractive.GameMode.service.in',
input: 'dbus/com.feralinteractive.GameMode.service.in',
output: 'com.feralinteractive.GameMode.service',
configuration: data_conf,
install_dir: path_dbus_service_dir,
)
# Install the Polkit action file in all cases
configure_file(
input: 'com.feralinteractive.GameMode.policy.in',
output: 'com.feralinteractive.GameMode.policy',
configuration: data_conf,
install_dir: path_polkit_action_dir,
)
# Install the Polkit action & rule files for the privileged gamemode group
if with_privileged_group != ''
configure_file(
input: 'polkit/actions/com.feralinteractive.GameMode.policy.in',
output: 'com.feralinteractive.GameMode.policy',
configuration: data_conf,
install_dir: path_polkit_action_dir,
)
configure_file(
input: 'polkit/rules.d/gamemode.rules.in',
output: 'gamemode.rules',
configuration: data_conf,
install_dir: path_polkit_rule_dir,
)
endif
# Install the helper run script
configure_file(
input: 'gamemoderun.in',
output: 'gamemoderun',
configuration: data_conf,
# Install the helper run script and man page
if get_option('default_library') == 'static'
warning('gamemoderun will not be installed as a shared libgamemodeauto library is required')
else
install_data(
files('gamemoderun'),
install_dir: path_bindir,
install_mode: 'rwxr-xr-x',
)
gamemoderun_manpage = configure_file(
input: files('gamemoderun.1.in'),
output: 'gamemoderun.1',
configuration: data_conf,
)
install_man(
gamemoderun_manpage,
install_dir: join_paths(path_mandir, 'man1')
)
endif
# Install script to find processes with gamemode lib in runtime
install_data(
files('gamemodelist'),
install_dir: path_bindir,
install_mode: 'rwxr-xr-x',
)
# Configure and install the man page
manpage = configure_file(
# Configure and install man pages
gamemoded_manpage = configure_file(
input: files('gamemoded.8.in'),
output: 'gamemoded.8',
configuration: data_conf,
)
install_man(manpage)
install_man(
gamemoded_manpage,
install_dir: join_paths(path_mandir, 'man8')
)
gamemodelist_manpage = configure_file(
input: files('gamemodelist.1.in'),
output: 'gamemodelist.1',
configuration: data_conf,
)
install_man(
gamemodelist_manpage,
install_dir: join_paths(path_mandir, 'man1')
)
if with_examples
example_manpage = configure_file(
input: files('gamemode-simulate-game.1.in'),
output: 'gamemode-simulate-game.1',
configuration: data_conf,
)
install_man(
example_manpage,
install_dir: join_paths(path_mandir, 'man1')
)
endif
# Install metainfo
metainfo_file = files('io.github.feralinteractive.gamemode.metainfo.xml')
install_data(
metainfo_file,
install_dir: path_metainfo,
)
# Validate metainfo
appstreamcli = find_program(
'appstreamcli',
required: false
)
if appstreamcli.found()
test(
'validate metainfo file',
appstreamcli,
args: ['validate', '--no-net', '--pedantic', metainfo_file],
)
endif

View File

@ -0,0 +1 @@
@@GAMEMODE_PRIVILEGED_GROUP@ - nice -10

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<!--
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
-->
<vendor>Feral GameMode Activation</vendor>
<vendor_url>http://www.feralinteractive.com</vendor_url>
<action id="com.feralinteractive.GameMode.governor-helper">
<description>Modify the CPU governor</description>
<message>Authentication is required to modify the CPU governor</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/cpugovctl</annotate>
</action>
<action id="com.feralinteractive.GameMode.gpu-helper">
<description>Modify the GPU clock states</description>
<message>Authentication is required to modify the GPU clock states</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/gpuclockctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
<action id="com.feralinteractive.GameMode.cpu-helper">
<description>Modify the CPU core states</description>
<message>Authentication is required to modify the CPU core states</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/cpucorectl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
<action id="com.feralinteractive.GameMode.procsys-helper">
<description>Modify the /proc/sys values</description>
<message>Authentication is required to modify the /proc/sys/ values</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/procsysctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
<action id="com.feralinteractive.GameMode.profile-helper">
<description>Modify the platform profile</description>
<message>Authentication is required to modify platform profile</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/platprofctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>

View File

@ -0,0 +1,15 @@
/*
* Allow users in privileged gamemode group to run cpugovctl &
* gpuclockctl without authentication
*/
polkit.addRule(function (action, subject) {
if ((action.id == "com.feralinteractive.GameMode.governor-helper" ||
action.id == "com.feralinteractive.GameMode.gpu-helper" ||
action.id == "com.feralinteractive.GameMode.cpu-helper" ||
action.id == "com.feralinteractive.GameMode.procsys-helper" ||
action.id == "com.feralinteractive.GameMode.profile-helper") &&
subject.isInGroup("@GAMEMODE_PRIVILEGED_GROUP@"))
{
return polkit.Result.YES;
}
});

View File

@ -0,0 +1 @@
g @GAMEMODE_PRIVILEGED_GROUP@ - -

View File

@ -5,7 +5,7 @@ Description=gamemoded
Type=dbus
BusName=com.feralinteractive.GameMode
NotifyAccess=main
ExecStart=@BINDIR@/gamemoded -l
ExecStart=@BINDIR@/gamemoded
[Install]
WantedBy=default.target

View File

@ -1,30 +0,0 @@
# Maintainer: Ysblokje <ysblokje at gmail dot com>
pkgname=('gamemode-git')
pkgver='1.5'
pkgrel=1
pkgdesc="GameMode is a daemon/lib combo for Linux that allows games to request a set of optimisations be temporarily applied to the host OS."
arch=('x86_64')
url="https://github.com/FeralInteractive/gamemode.git"
license=('MIT')
depends=('systemd' 'polkit')
makedepends=('meson' 'pkg-config')
provides=('gamemode')
source=("git+https://github.com/FeralInteractive/gamemode.git")
sha256sums=('SKIP')
pkgver() {
cd gamemode
echo $(git rev-parse --short HEAD)
}
build() {
cd gamemode
arch-meson build
cd build
ninja
}
package() {
cd gamemode/build
DESTDIR=$pkgdir ninja install
}

View File

@ -1,4 +0,0 @@
The following folders contain PKGBUILD file for arch(like) distro's. You can use those as starting point for your own packages.
Regards,
Minze Zwerver

View File

@ -4,8 +4,13 @@ reaper_freq=5
; The desired governor is used when entering GameMode instead of "performance"
desiredgov=performance
; The default governer is used when leaving GameMode instead of restoring the original value
defaultgov=powersave
; The default governor is used when leaving GameMode instead of restoring the original value
;defaultgov=powersave
; The desired platform profile is used when entering GameMode instead of "performance"
desiredprof=performance
; The default platform profile is used when leaving GameMode instead of restoring the original value
;defaultgov=low-power
; The iGPU desired governor is used when the integrated GPU is under heavy load
igpu_desiredgov=powersave
@ -23,6 +28,8 @@ softrealtime=off
; GameMode can renice game processes. You can put any value between 0 and 20 here, the value
; will be negated and applied as a nice value (0 means no change). Defaults to 0.
; To use this feature, the user must be added to the gamemode group (and then rebooted):
; sudo usermod -aG gamemode $(whoami)
renice=0
; By default, GameMode adjusts the iopriority of clients to BE/0, you can put any value
@ -35,6 +42,10 @@ ioprio=0
; Defaults to 1
inhibit_screensaver=1
; Sets whether gamemode will disable split lock mitigation when active
; Defaults to 1
disable_splitlock=1
[filter]
; If "whitelist" entry has a value(s)
; gamemode will reject anything not in the whitelist
@ -75,6 +86,17 @@ inhibit_screensaver=1
; This corresponds to power_dpm_force_performance_level, "manual" is not supported for now
;amd_performance_level=high
[cpu]
; Parking or Pinning can be enabled with either "yes", "true" or "1" and disabled with "no", "false" or "0".
; Either can also be set to a specific list of cores to park or pin, comma separated list where "-" denotes
; a range. E.g "park_cores=1,8-15" would park cores 1 and 8 to 15.
; The default is uncommented is to disable parking but enable pinning. If either is enabled the code will
; currently only properly autodetect Ryzen 7900x3d, 7950x3d and Intel CPU:s with E- and P-cores.
; For Core Parking, user must be added to the gamemode group (not required for Core Pinning):
; sudo usermod -aG gamemode $(whoami)
;park_cores=no
;pin_cores=yes
[supervisor]
; This section controls the new gamemode functions gamemode_request_start_for and gamemode_request_end_for
; The whilelist and blacklist control which supervisor programs are allowed to make the above requests
@ -88,10 +110,10 @@ inhibit_screensaver=1
[custom]
; Custom scripts (executed using the shell) when gamemode starts and ends
;start=notify-send "GameMode started"
; /home/me/bin/stop_ethmining.sh
; /home/me/bin/stop_foldingathome.sh
;end=notify-send "GameMode ended"
; /home/me/bin/start_ethmining.sh
; /home/me/bin/start_foldingathome.sh
; Timeout for scripts (seconds). Scripts will be killed if they do not complete within this time.
;script_timeout=10

View File

@ -1,5 +0,0 @@
[Desktop Entry]
Name=gamemoded
Exec=systemctl --user restart gamemoded.service
Type=Application
Terminal=false

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "gamemode_client.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
@ -39,6 +40,7 @@ int main(void)
/* Request we start game mode */
if (gamemode_request_start() != 0) {
fprintf(stderr, "Failed to request gamemode start: %s...\n", gamemode_error_string());
return EXIT_FAILURE;
}
/* Simulate running a game */
@ -47,5 +49,8 @@ int main(void)
/* Request we end game mode (optional) */
if (gamemode_request_end() != 0) {
fprintf(stderr, "Failed to request gamemode end: %s...\n", gamemode_error_string());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -1,11 +1,18 @@
# An example game
executable(
'example',
'gamemode-simulate-game',
sources: [
'main.c',
],
include_directories: libgamemode_includes,
dependencies: [
libdl,
gamemode_dep,
],
install: true,
install_dir: path_bindir,
)
# An example configuration
install_data(
files('gamemode.ini'),
install_dir: path_sysconfdir,
)

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -109,8 +109,8 @@ static inline int *alloc_fd_array(int n)
return fds;
}
// Helper to check if we are running inside a flatpak
static int in_flatpak(void)
// Helper to check if we are running inside a sandboxed framework like Flatpak or Snap
static int in_sandbox(void)
{
static int status = -1;
@ -120,6 +120,10 @@ static int in_flatpak(void)
r = lstat("/.flatpak-info", &sb);
status = r == 0 && sb.st_size > 0;
if (getenv("SNAP") != NULL) {
status = 1;
}
}
return status;
@ -146,7 +150,7 @@ static int log_error(const char *fmt, ...)
static void hop_off_the_bus(DBusConnection **bus)
{
if (bus == NULL)
if (bus == NULL || *bus == NULL)
return;
dbus_connection_unref(*bus);
@ -228,7 +232,7 @@ static int make_request(DBusConnection *bus, int native, int use_pidfds, const c
native,
use_pidfds);
// If we are inside a flatpak we need to talk to the portal instead
// If we are inside a Flatpak or Snap we need to talk to the portal instead
const char *dest = native ? DAEMON_DBUS_NAME : PORTAL_DBUS_NAME;
const char *path = native ? DAEMON_DBUS_PATH : PORTAL_DBUS_PATH;
const char *iface = native ? DAEMON_DBUS_IFACE : PORTAL_DBUS_IFACE;
@ -302,7 +306,7 @@ static int gamemode_request(const char *method, pid_t for_pid)
int native;
int res = -1;
native = !in_flatpak();
native = !in_sandbox();
/* pid[0] is the client, i.e. the game
* pid[1] is the requestor, i.e. this process

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -80,6 +80,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <dlfcn.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
static char internal_gamemode_client_error_string[512] = { 0 };
@ -231,6 +233,9 @@ __attribute__((always_inline)) static inline const char *gamemode_error_string(v
return internal_gamemode_client_error_string;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_error_string != NULL);
return REAL_internal_gamemode_error_string();
}
@ -254,6 +259,9 @@ int gamemode_request_start(void)
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_start != NULL);
if (REAL_internal_gamemode_request_start() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
@ -280,6 +288,9 @@ int gamemode_request_end(void)
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_end != NULL);
if (REAL_internal_gamemode_request_end() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());

View File

@ -6,7 +6,7 @@ lt_age = '0'
lt_version = '@0@.@1@.@2@'.format(lt_current, lt_age, lt_revision)
# Main client library to message the daemon
gamemode = shared_library(
libgamemode = shared_library(
'gamemode',
sources: [
'client_impl.c',
@ -20,12 +20,12 @@ gamemode = shared_library(
version: lt_version,
)
libgamemode_includes = [
gamemode_headers_includes = [
include_directories('.'),
]
# Small library to automatically use gamemode
gamemodeauto = shared_library(
libgamemodeauto = library(
'gamemodeauto',
sources: [
'client_loader.c',
@ -43,7 +43,10 @@ gamemode_headers = [
'gamemode_client.h',
]
install_headers(gamemode_headers)
install_headers(
gamemode_headers,
install_dir: path_includedir,
)
# Generate a pkg-config files
pkg = import('pkgconfig')
@ -59,13 +62,18 @@ pkg.generate(
)
pkg.generate(
name: 'gamemode',
name: 'libgamemodeauto',
description: desc,
filebase: 'gamemode-auto',
libraries: gamemodeauto,
filebase: 'libgamemodeauto',
libraries: libgamemodeauto,
version: meson.project_version(),
libraries_private: [
libdl
],
)
# Dependency objects
gamemode_dep = declare_dependency(
include_directories: gamemode_headers_includes,
dependencies: libdl,
)
libgamemodeauto_dep = declare_dependency(
link_with: libgamemodeauto,
)

View File

@ -2,8 +2,9 @@ project(
'gamemode',
'c',
default_options : ['c_std=c11', 'warning_level=3'],
version: '1.5',
version: '1.8.2',
license: 'BSD',
meson_version: '>= 1.3.1',
)
am_cflags = [
@ -77,10 +78,21 @@ path_datadir = join_paths(path_prefix, get_option('datadir'))
path_includedir = join_paths(path_prefix, get_option('includedir'))
path_libdir = join_paths(path_prefix, get_option('libdir'))
path_libexecdir = join_paths(path_prefix, get_option('libexecdir'))
path_mandir = join_paths(path_prefix, get_option('mandir'))
path_metainfo = join_paths(path_datadir, 'metainfo')
path_sysconfdir = join_paths(path_datadir, 'gamemode')
# Find systemd via pkgconfig
with_systemd = get_option('with-systemd')
dep_systemd = dependency('libsystemd')
# Find systemd / elogind via pkgconfig
sd_bus_provider = get_option('with-sd-bus-provider')
sd_bus_args = []
sd_bus_dep = []
if sd_bus_provider == 'systemd'
sd_bus_dep = dependency('libsystemd')
elif sd_bus_provider == 'elogind'
sd_bus_args += ['-DUSE_ELOGIND']
sd_bus_dep = dependency('libelogind')
endif
# For the client, libdbus is used
dep_dbus = dependency('dbus-1')
@ -91,28 +103,41 @@ dep_threads = dependency('threads')
# On non glibc systems this might be a stub, i.e. for musl
libdl = cc.find_library('dl', required: false)
with_privileged_group = get_option('with-privileged-group')
# Determine the location for the systemd unit
if with_systemd == true
# If the path isn't explicitly set, ask systemd for the systemd user unit directory
path_systemd_unit_dir = get_option('with-systemd-user-unit-dir')
if path_systemd_unit_dir == ''
message('Asking pkg-config for systemd\'s directories')
pkgconfig_systemd = dependency('systemd')
path_systemd_unit_dir = pkgconfig_systemd.get_pkgconfig_variable('systemduserunitdir')
if sd_bus_provider == 'systemd'
with_systemd_unit = get_option('with-systemd-user-unit')
if with_systemd_unit
path_systemd_unit_dir = get_option('with-systemd-user-unit-dir')
if path_systemd_unit_dir == ''
message('Asking pkg-config for systemd\'s \'systemduserunitdir\' directory')
pkgconfig_systemd = dependency('systemd')
path_systemd_unit_dir = pkgconfig_systemd.get_variable(pkgconfig: 'systemduserunitdir')
endif
endif
if with_privileged_group != ''
with_systemd_group = get_option('with-systemd-group')
if with_systemd_group
path_systemd_group_dir = get_option('with-systemd-group-dir')
if path_systemd_group_dir == ''
message('Asking pkg-config for systemd\'s \'sysusersdir\' directory')
pkgconfig_systemd = dependency('systemd')
path_systemd_group_dir = pkgconfig_systemd.get_variable(pkgconfig: 'sysusersdir')
endif
endif
else
with_systemd_group = false
endif
endif
with_limits_conf = get_option('with-pam-group')
if with_limits_conf != ''
ldata = configuration_data()
ldata.set('LIMITSGROUP', with_limits_conf)
# Install the limits.d configuration file
configure_file(
input: 'data/10-gamemode.conf.in',
output: '10-gamemode.conf',
configuration: ldata,
install_dir: '/etc/security/limits.d',
)
if with_privileged_group != ''
with_pam_renicing = get_option('with-pam-renicing')
if with_pam_renicing
path_pam_limits_dir = get_option('with-pam-limits-dir')
endif
else
with_pam_renicing = false
endif
# Set the dbus path as appropriate.
@ -121,9 +146,10 @@ if path_dbus_service_dir == ''
path_dbus_service_dir = join_paths(path_datadir, 'dbus-1', 'services')
endif
path_polkit_action_dir = join_paths(path_datadir, 'polkit-1', 'actions')
path_polkit_dir = join_paths(path_datadir, 'polkit-1')
path_polkit_action_dir = join_paths(path_polkit_dir, 'actions')
path_polkit_rule_dir = join_paths(path_polkit_dir, 'rules.d')
with_daemon = get_option('with-daemon')
with_examples = get_option('with-examples')
with_util = get_option('with-util')
@ -132,6 +158,7 @@ pidfd_open = cc.has_function('pidfd_open', args: '-D_GNU_SOURCE')
cdata = configuration_data()
cdata.set_quoted('LIBEXECDIR', path_libexecdir)
cdata.set_quoted('SYSCONFDIR', path_sysconfdir)
cdata.set_quoted('GAMEMODE_VERSION', meson.project_version())
cdata.set10('HAVE_FN_PIDFD_OPEN', pidfd_open)
@ -155,10 +182,12 @@ endif
# The daemon can be disabled if necessary, allowing multilib builds of the
# main library
if with_daemon == true
if sd_bus_provider != 'no-daemon'
# inih currently only needed by the daemon
inih = subproject('inih')
inih_dependency = inih.get_variable('inih_dependency')
inih_dependency = dependency(
'inih',
fallback : ['inih', 'inih_dep']
)
subdir('daemon')
@ -184,11 +213,23 @@ report = [
' includedir: @0@'.format(path_includedir),
]
if with_systemd == true
if with_pam_renicing
report += [
' PAM limits.d directory: @0@'.format(path_pam_limits_dir),
]
endif
if sd_bus_provider == 'systemd'
if with_systemd_unit
report += [
' systemd user unit directory: @0@'.format(path_systemd_unit_dir),
]
endif
if with_systemd_group
report += [
' systemd group directory: @0@'.format(path_systemd_group_dir),
]
endif
endif
report += [
' D-BUS service directory: @0@'.format(path_dbus_service_dir),
]
@ -200,10 +241,9 @@ report += [
' Options:',
' ========',
'',
' daemon: @0@'.format(with_daemon),
' sd-bus provier: @0@'.format(sd_bus_provider),
' examples: @0@'.format(with_examples),
' util: @0@'.format(with_util),
' systemd: @0@'.format(with_systemd),
]
# Output some stuff to validate the build config

View File

@ -1,15 +1,20 @@
option('with-systemd', type: 'boolean', description: 'Use systemd support (unit, etc)', value: 'true')
# limits.d
option('with-pam-group', type: 'string', description: 'Install the limits.d configuration file to allow renicing as an unpriviledged user being part of the specified group')
option('with-pam-renicing', type: 'boolean', description: 'Install the limits.d configuration file to allow renicing as a user being part of the privileged gamemode group', value: true)
option('with-pam-limits-dir', type: 'string', description: 'Explicitly set the PAM limits.d directory', value: '/etc/security/limits.d')
# sd-bus provider
option('with-sd-bus-provider', type: 'combo', choices: ['systemd', 'elogind', 'no-daemon'], value: 'systemd')
# systemd specific
option('with-systemd-user-unit', type: 'boolean', description: 'Install systemd user unit', value: true)
option('with-systemd-user-unit-dir', type: 'string', description: 'Explicitly set the systemd user unit directory')
option('with-systemd-group', type: 'boolean', description: 'Install privileged gamemode group with systemd', value: true)
option('with-systemd-group-dir', type: 'string', description: 'Explicitly set the systemd group directory')
# Not using systemd
option('with-dbus-service-dir', type: 'string', description: 'Explicitly set the D-BUS session directory')
# General options
option('with-examples', type: 'boolean', description: 'Build sample programs', value: 'true')
option('with-daemon', type: 'boolean', description: 'Build the daemon', value: 'true')
option('with-util', type: 'boolean', description: 'Build the utilities', value: 'true')
option('with-examples', type: 'boolean', description: 'Build sample programs', value: true)
option('with-util', type: 'boolean', description: 'Build the utilities', value: true)
option('with-privileged-group', type: 'string', description: 'Group that has access to privileged gamemode features', value: 'gamemode')

View File

@ -4,24 +4,33 @@
# Ensure we are at the project root
cd "$(dirname $0)"/..
wget -Nq -T3 -t1 https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/git-clang-format
if [[ "$1" == "--pre-commit" ]]; then
# used via .git/hooks/pre-commit:
# exec "$(dirname $0)"/../../scripts/format-check.sh --pre-commit
git-clang-format
exit
fi
if chmod +x git-clang-format; then
if [[ "$1" == "--pre-commit" ]]; then
# used via .git/hooks/pre-commit:
# exec "$(dirname $0)"/../../scripts/format-check.sh --pre-commit
./git-clang-format
exit
fi
CLANG_FORMAT_OUTPUT=$(./git-clang-format HEAD^ HEAD --diff)
if [[ ! ${CLANG_FORMAT_OUTPUT} == "no modified files to format" ]] && [[ ! -z ${CLANG_FORMAT_OUTPUT} ]]; then
if [[ "$CI" == "true" ]]; then
# used in ci, assumes clean repo
clang-format -i $(find . -name '*.[ch]' -not -path "*subprojects/*")
GIT_DIFF_OUTPUT=$(git diff)
if [[ ! -z ${GIT_DIFF_OUTPUT} ]]; then
echo "Failed clang format check:"
echo "${CLANG_FORMAT_OUTPUT}"
echo "${GIT_DIFF_OUTPUT}"
exit 1
else
echo "Passed clang format check"
exit 0
fi
else
echo "git-clang-format not downloaded"
exit 1
fi
CLANG_FORMAT_OUTPUT=$(git-clang-format HEAD^ HEAD --diff)
if [[ ! ${CLANG_FORMAT_OUTPUT} == "no modified files to format" ]] && [[ ! -z ${CLANG_FORMAT_OUTPUT} ]]; then
echo "Failed clang format check:"
echo "${CLANG_FORMAT_OUTPUT}"
exit 1
else
echo "Passed clang format check"
exit 0
fi

View File

@ -1,313 +0,0 @@
#!/bin/bash -
#
# File: git-archive-all.sh
#
# Description: A utility script that builds an archive file(s) of all
# git repositories and submodules in the current path.
# Useful for creating a single tarfile of a git super-
# project that contains other submodules.
#
# Examples: Use git-archive-all.sh to create archive distributions
# from git repositories. To use, simply do:
#
# cd $GIT_DIR; git-archive-all.sh
#
# where $GIT_DIR is the root of your git superproject.
#
# License: GPL3+
#
###############################################################################
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
###############################################################################
# DEBUGGING
set -e
set -C # noclobber
# TRAP SIGNALS
trap 'cleanup' QUIT EXIT
# For security reasons, explicitly set the internal field separator
# to newline, space, tab
OLD_IFS=$IFS
IFS="$(printf '\n \t')"
function cleanup () {
rm -f $TMPFILE
rm -f $TMPLIST
rm -f $TOARCHIVE
IFS="$OLD_IFS"
}
function usage () {
echo "Usage is as follows:"
echo
echo "$PROGRAM <--version>"
echo " Prints the program version number on a line by itself and exits."
echo
echo "$PROGRAM <--usage|--help|-?>"
echo " Prints this usage output and exits."
echo
echo "$PROGRAM [--format <fmt>] [--prefix <path>] [--verbose|-v] [--separate|-s]"
echo " [--worktree-attributes] [--tree-ish|-t <tree-ish>] [output_file]"
echo " Creates an archive for the entire git superproject, and its submodules"
echo " using the passed parameters, described below."
echo
echo " If '--format' is specified, the archive is created with the named"
echo " git archiver backend. Obviously, this must be a backend that git archive"
echo " understands. The format defaults to 'tar' if not specified."
echo
echo " If '--prefix' is specified, the archive's superproject and all submodules"
echo " are created with the <path> prefix named. The default is to not use one."
echo
echo " If '--worktree-attributes' is specified, the invidual archive commands will"
echo " look for attributes in .gitattributes in the working directory too."
echo
echo " If '--separate' or '-s' is specified, individual archives will be created"
echo " for each of the superproject itself and its submodules. The default is to"
echo " concatenate individual archives into one larger archive."
echo
echo " If '--tree-ish' is specified, the archive will be created based on whatever"
echo " you define the tree-ish to be. Branch names, commit hash, etc. are acceptable."
echo " Defaults to HEAD if not specified. See git archive's documentation for more"
echo " information on what a tree-ish is."
echo
echo " If 'output_file' is specified, the resulting archive is created as the"
echo " file named. This parameter is essentially a path that must be writeable."
echo " When combined with '--separate' ('-s') this path must refer to a directory."
echo " Without this parameter or when combined with '--separate' the resulting"
echo " archive(s) are named with a dot-separated path of the archived directory and"
echo " a file extension equal to their format (e.g., 'superdir.submodule1dir.tar')."
echo
echo " The special value '-' (single dash) is treated as STDOUT and, when used, the"
echo " --separate option is ignored. Use a double-dash to separate the outfile from"
echo " the value of previous options. For example, to write a .zip file to STDOUT:"
echo
echo " ./$PROGRAM --format zip -- -"
echo
echo " If '--verbose' or '-v' is specified, progress will be printed."
}
function version () {
echo "$PROGRAM version $VERSION"
}
# Internal variables and initializations.
readonly PROGRAM=`basename "$0"`
readonly VERSION=0.3
SEPARATE=0
VERBOSE=0
TARCMD=`command -v gtar || command -v gnutar || command -v tar`
FORMAT=tar
PREFIX=
TREEISH=HEAD
ARCHIVE_OPTS=
# RETURN VALUES/EXIT STATUS CODES
readonly E_BAD_OPTION=254
readonly E_UNKNOWN=255
# Process command-line arguments.
while test $# -gt 0; do
if [ x"$1" == x"--" ]; then
# detect argument termination
shift
break
fi
case $1 in
--format )
shift
FORMAT="$1"
shift
;;
--prefix )
shift
PREFIX="$1"
shift
;;
--worktree-attributes )
ARCHIVE_OPTS+=" $1"
shift
;;
--separate | -s )
shift
SEPARATE=1
;;
--tree-ish | -t )
shift
TREEISH="$1"
shift
;;
--version )
version
exit
;;
--verbose | -v )
shift
VERBOSE=1
;;
-? | --usage | --help )
usage
exit
;;
-* )
echo "Unrecognized option: $1" >&2
usage
exit $E_BAD_OPTION
;;
* )
break
;;
esac
done
OLD_PWD="`pwd`"
TMPDIR=${TMPDIR:-/tmp}
TMPFILE=`mktemp "$TMPDIR/$PROGRAM.XXXXXX"` # Create a place to store our work's progress
TMPLIST=`mktemp "$TMPDIR/$PROGRAM.submodules.XXXXXX"`
TOARCHIVE=`mktemp "$TMPDIR/$PROGRAM.toarchive.XXXXXX"`
OUT_FILE=$OLD_PWD # assume "this directory" without a name change by default
if [ ! -z "$1" ]; then
OUT_FILE="$1"
if [ "-" == "$OUT_FILE" ]; then
SEPARATE=0
fi
shift
fi
# Validate parameters; error early, error often.
if [ "-" == "$OUT_FILE" -o $SEPARATE -ne 1 ] && [ "$FORMAT" == "tar" -a `$TARCMD --help | grep -q -- "--concatenate"; echo $?` -ne 0 ]; then
echo "Your 'tar' does not support the '--concatenate' option, which we need"
echo "to produce a single tarfile. Either install a compatible tar (such as"
echo "gnutar), or invoke $PROGRAM with the '--separate' option."
exit
elif [ $SEPARATE -eq 1 -a ! -d "$OUT_FILE" ]; then
echo "When creating multiple archives, your destination must be a directory."
echo "If it's not, you risk being surprised when your files are overwritten."
exit
elif [ `git config -l | grep -q '^core\.bare=true'; echo $?` -eq 0 ]; then
echo "$PROGRAM must be run from a git working copy (i.e., not a bare repository)."
exit
fi
# Create the superproject's git-archive
if [ $VERBOSE -eq 1 ]; then
echo -n "creating superproject archive..."
fi
git archive --format=$FORMAT --prefix="$PREFIX" $ARCHIVE_OPTS $TREEISH > $TMPDIR/$(basename "$(pwd)").$FORMAT
if [ $VERBOSE -eq 1 ]; then
echo "done"
fi
echo $TMPDIR/$(basename "$(pwd)").$FORMAT >| $TMPFILE # clobber on purpose
superfile=`head -n 1 $TMPFILE`
if [ $VERBOSE -eq 1 ]; then
echo -n "looking for subprojects..."
fi
# find all '.git' dirs, these show us the remaining to-be-archived dirs
# we only want directories that are below the current directory
find . -mindepth 2 -name '.git' -type d -print | sed -e 's/^\.\///' -e 's/\.git$//' >> $TOARCHIVE
# as of version 1.7.8, git places the submodule .git directories under the superprojects .git dir
# the submodules get a .git file that points to their .git dir. we need to find all of these too
find . -mindepth 2 -name '.git' -type f -print | xargs grep -l "gitdir" | sed -e 's/^\.\///' -e 's/\.git$//' >> $TOARCHIVE
if [ $VERBOSE -eq 1 ]; then
echo "done"
echo " found:"
cat $TOARCHIVE | while read arch
do
echo " $arch"
done
fi
if [ $VERBOSE -eq 1 ]; then
echo -n "archiving submodules..."
fi
git submodule >>"$TMPLIST"
while read path; do
TREEISH=$(grep "^ .*${path%/} " "$TMPLIST" | cut -d ' ' -f 2) # git submodule does not list trailing slashes in $path
cd "$path"
git archive --format=$FORMAT --prefix="${PREFIX}$path" $ARCHIVE_OPTS ${TREEISH:-HEAD} > "$TMPDIR"/"$(echo "$path" | sed -e 's/\//./g')"$FORMAT
if [ $FORMAT == 'zip' ]; then
# delete the empty directory entry; zipped submodules won't unzip if we don't do this
zip -d "$(tail -n 1 $TMPFILE)" "${PREFIX}${path%/}" >/dev/null # remove trailing '/'
fi
echo "$TMPDIR"/"$(echo "$path" | sed -e 's/\//./g')"$FORMAT >> $TMPFILE
cd "$OLD_PWD"
done < $TOARCHIVE
if [ $VERBOSE -eq 1 ]; then
echo "done"
fi
if [ $VERBOSE -eq 1 ]; then
echo -n "concatenating archives into single archive..."
fi
# Concatenate archives into a super-archive.
if [ $SEPARATE -eq 0 -o "-" == "$OUT_FILE" ]; then
if [ $FORMAT == 'tar.gz' ]; then
gunzip $superfile
superfile=${superfile:0: -3} # Remove '.gz'
sed -e '1d' $TMPFILE | while read file; do
gunzip $file
file=${file:0: -3}
$TARCMD --concatenate -f "$superfile" "$file" && rm -f "$file"
done
gzip $superfile
superfile=$superfile.gz
elif [ $FORMAT == 'tar' ]; then
sed -e '1d' $TMPFILE | while read file; do
$TARCMD --concatenate -f "$superfile" "$file" && rm -f "$file"
done
elif [ $FORMAT == 'zip' ]; then
sed -e '1d' $TMPFILE | while read file; do
# zip incorrectly stores the full path, so cd and then grow
cd `dirname "$file"`
zip -g "$superfile" `basename "$file"` && rm -f "$file"
done
cd "$OLD_PWD"
fi
echo "$superfile" >| $TMPFILE # clobber on purpose
fi
if [ $VERBOSE -eq 1 ]; then
echo "done"
fi
if [ $VERBOSE -eq 1 ]; then
echo -n "moving archive to $OUT_FILE..."
fi
while read file; do
if [ "-" == "$OUT_FILE" ]; then
cat "$file" && rm -f "$file"
else
mv "$file" "$OUT_FILE"
fi
done < $TMPFILE
if [ $VERBOSE -eq 1 ]; then
echo "done"
fi

View File

@ -2,19 +2,19 @@
set -e
# Simple script to construct a redistributable and complete tarball of the
# gamemode tree, including the git submodules, so that it can be trivially
# gamemode tree, including the subprojects, so that it can be trivially
# packaged by distributions banning networking during build.
#
# Modified from Ikey Doherty's release scripts for use within
# Feral Interactive's gamemode project.
git submodule init
git submodule update
# Bump in tandem with meson.build, run script once new tag is up.
VERSION="1.5"
NAME="gamemode"
./scripts/git-archive-all.sh --format tar --prefix ${NAME}-${VERSION}/ --verbose -t HEAD ${NAME}-${VERSION}.tar
VERSION=$(git describe --tags --dirty)
# get code in this repo
git archive HEAD --format=tar --prefix=${NAME}-${VERSION}/ --output=${NAME}-${VERSION}.tar
# get code from subprojects
meson subprojects download
meson subprojects update --reset
tar -rf ${NAME}-${VERSION}.tar --exclude-vcs --transform="s,^subprojects,${NAME}-$VERSION/subprojects," subprojects/inih-r54/
# compress archive
xz -9 "${NAME}-${VERSION}.tar"
# Automatically sign the tarball with GPG key of user running this script

View File

@ -1,14 +1,12 @@
#!/bin/bash
# Exit on failure
set -e
set -exo pipefail
# Build directly
cd build/
# Ensure we are at the project root
cd "$(dirname $0)"/..
# Collect scan-build output
ninja scan-build | tee /tmp/scan-build-results.txt
ninja scan-build -C builddir | tee builddir/meson-logs/scan-build.txt
# Invert the output - if this string exists it's a fail
! grep -E '[0-9]+ bugs? found.' /tmp/scan-build-results.txt
! grep -E '[0-9]+ bugs? found.' builddir/meson-logs/scan-build.txt

@ -1 +0,0 @@
Subproject commit 745ada6724038cde32ff6390b32426cbdd5e532b

9
subprojects/inih.wrap Normal file
View File

@ -0,0 +1,9 @@
[wrap-file]
directory = inih-r60
source_url = https://github.com/benhoyt/inih/archive/r60.tar.gz
source_filename = inih-r60.tar.gz
source_hash = 706aa05c888b53bd170e5d8aa8f8a9d9ccf5449dfed262d5103d1f292af26774
[provide]
inih = inih_dep
inireader = INIReader_dep

141
util/cpucorectl.c Normal file
View File

@ -0,0 +1,141 @@
/*
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <linux/limits.h>
#include <sched.h>
#include <unistd.h>
#include "common-cpu.h"
#include "common-logging.h"
static int write_state(char *path, int state)
{
FILE *f = fopen(path, "w");
if (!f) {
LOG_ERROR("Couldn't open file at %s (%s)\n", path, strerror(errno));
return 0;
}
if (putc(state, f) == EOF) {
LOG_ERROR("Couldn't write to file at %s (%s)\n", path, strerror(errno));
fclose(f);
return 0;
}
fclose(f);
return 1;
}
static void log_state(const int state, const long first, const long last)
{
if (state == '0') {
if (first == last)
LOG_MSG("parked core %ld\n", first);
else
LOG_MSG("parked cores %ld - %ld\n", first, last);
} else {
if (first == last)
LOG_MSG("unparked core %ld\n", first);
else
LOG_MSG("unparked cores %ld - %ld\n", first, last);
}
}
static int set_state(char *cpulist, int state)
{
char path[PATH_MAX];
long from, to;
char *list = cpulist;
long first = -1, last = -1;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
if (snprintf(path, PATH_MAX, "/sys/devices/system/cpu/cpu%ld/online", cpu) < 0) {
LOG_ERROR("snprintf failed, will not apply cpu core parking!\n");
return 0;
}
if (!write_state(path, state)) {
/* on some systems one cannot park core #0 */
if (cpu != 0) {
if (state == '0') {
LOG_ERROR("unable to park core #%ld, will not apply cpu core parking!\n",
cpu);
return -1;
}
LOG_ERROR("unable to unpark core #%ld\n", cpu);
}
} else {
if (first == -1) {
first = cpu;
last = cpu;
} else if (last + 1 == cpu) {
last = cpu;
} else {
log_state(state, first, last);
first = cpu;
last = cpu;
}
}
}
}
if (first != -1)
log_state(state, first, last);
return 1;
}
int main(int argc, char *argv[])
{
if (geteuid() != 0) {
LOG_ERROR("This program must be run as root\n");
return EXIT_FAILURE;
}
if (argc == 3 && strncmp(argv[1], "online", 6) == 0) {
if (!set_state(argv[2], '1'))
return EXIT_FAILURE;
} else if (argc == 3 && strncmp(argv[1], "offline", 7) == 0) {
if (!set_state(argv[2], '0'))
return EXIT_FAILURE;
} else {
fprintf(stderr, "usage: cpucorectl [online]|[offline] VALUE]\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -70,7 +70,7 @@ static void print_usage_and_exit(void)
static const char *get_nv_attr(const char *attr)
{
static char out[EXTERNAL_BUFFER_MAX];
const char *exec_args[] = { "/usr/bin/nvidia-settings", "-q", attr, "-t", NULL };
const char *exec_args[] = { "nvidia-settings", "-q", attr, "-t", NULL };
if (run_external_process(exec_args, out, -1) != 0) {
LOG_ERROR("Failed to get %s!\n", attr);
out[0] = 0;
@ -82,7 +82,7 @@ static const char *get_nv_attr(const char *attr)
static int set_nv_attr(const char *attr)
{
const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", attr, NULL };
const char *exec_args_core[] = { "nvidia-settings", "-a", attr, NULL };
if (run_external_process(exec_args_core, NULL, -1) != 0) {
LOG_ERROR("Failed to set %s!\n", attr);
return -1;

View File

@ -28,3 +28,48 @@ gpuclockctl = executable(
install: true,
install_dir: path_libexecdir,
)
# Small target util to park and unpark cores
cpucorectl_sources = [
'cpucorectl.c',
]
cpucorectl = executable(
'cpucorectl',
sources: cpucorectl_sources,
dependencies: [
link_daemon_common,
],
install: true,
install_dir: path_libexecdir,
)
# Small target util to set values in /proc/sys/
procsysctl_sources = [
'procsysctl.c',
]
procsysctl = executable(
'procsysctl',
sources: procsysctl_sources,
dependencies: [
link_daemon_common,
],
install: true,
install_dir: path_libexecdir,
)
# Small target util to get and set platform profile
platprofctl_sources = [
'platprofctl.c',
]
platprofctl = executable(
'platprofctl',
sources: platprofctl_sources,
dependencies: [
link_daemon_common,
],
install: true,
install_dir: path_libexecdir,
)

84
util/platprofctl.c Normal file
View File

@ -0,0 +1,84 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "common-logging.h"
#include "common-profile.h"
#include <unistd.h>
/**
* Sets platform profile to a value
*/
static int set_profile_state(const char *value)
{
int retval = EXIT_SUCCESS;
FILE *f = fopen(profile_path, "w");
if (!f) {
LOG_ERROR("Failed to open file for write %s\n", profile_path);
return EXIT_FAILURE;
}
if (fprintf(f, "%s\n", value) < 0) {
LOG_ERROR("Failed to set platform profile to %s: %s", value, strerror(errno));
retval = EXIT_FAILURE;
}
fclose(f);
return retval;
}
/**
* Main entry point, dispatch to the appropriate helper
*/
int main(int argc, char *argv[])
{
if (argc == 2 && strncmp(argv[1], "get", 3) == 0) {
printf("%s", get_profile_state());
} else if (argc == 3 && strncmp(argv[1], "set", 3) == 0) {
const char *value = argv[2];
/* Must be root to set the state */
if (geteuid() != 0) {
LOG_ERROR("This program must be run as root\n");
return EXIT_FAILURE;
}
return set_profile_state(value);
} else {
fprintf(stderr, "usage: platprofctl [get] [set VALUE]\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

75
util/procsysctl.c Normal file
View File

@ -0,0 +1,75 @@
/*
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include "common-logging.h"
#include "common-splitlock.h"
static bool write_value(const char *key, const char *value)
{
FILE *f = fopen(key, "w");
if (!f) {
if (errno != ENOENT)
LOG_ERROR("Couldn't open file at %s (%s)\n", key, strerror(errno));
return false;
}
if (fputs(value, f) == EOF) {
LOG_ERROR("Couldn't write to file at %s (%s)\n", key, strerror(errno));
fclose(f);
return false;
}
fclose(f);
return true;
}
int main(int argc, char *argv[])
{
if (geteuid() != 0) {
LOG_ERROR("This program must be run as root\n");
return EXIT_FAILURE;
}
if (argc == 3) {
if (strcmp(argv[1], "split_lock_mitigate") == 0) {
if (!write_value(splitlock_path, argv[2]))
return EXIT_FAILURE;
return EXIT_SUCCESS;
} else {
fprintf(stderr, "unsupported key: '%s'\n", argv[1]);
return EXIT_FAILURE;
}
}
fprintf(stderr, "usage: procsysctl KEY VALUE\n");
fprintf(stderr, "where KEY can by any of 'split_lock_mitigate'\n");
return EXIT_FAILURE;
}