mirror of
https://github.com/FeralInteractive/gamemode.git
synced 2025-06-04 22:57:21 +02:00
Compare commits
205 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
af07e169d5 | ||
![]() |
6e5c950141 | ||
![]() |
499af4c7bb | ||
![]() |
5f691c3171 | ||
![]() |
f5fbdcf014 | ||
![]() |
7c49d6e1db | ||
![]() |
d84c5ded1c | ||
![]() |
2d71be6a94 | ||
![]() |
1e3e55c5d8 | ||
![]() |
e75f2c3942 | ||
![]() |
81d26c79b4 | ||
![]() |
5b3b4ae638 | ||
![]() |
1d9c1e6a72 | ||
![]() |
2eecd513ac | ||
![]() |
086b3fe5b4 | ||
![]() |
2f69f7c023 | ||
![]() |
c54d6d4243 | ||
![]() |
715a1389b7 | ||
![]() |
c0516059ed | ||
![]() |
a875008d8e | ||
![]() |
ec10c591ff | ||
![]() |
fef538ba92 | ||
![]() |
a2fe0108b5 | ||
![]() |
c854772369 | ||
![]() |
7d55ef856b | ||
![]() |
1c293d7818 | ||
![]() |
9646f2bd93 | ||
![]() |
8e0a71a0bc | ||
![]() |
3fa41891cf | ||
![]() |
8cea4c2418 | ||
![]() |
4a82094c98 | ||
![]() |
5180d89e66 | ||
![]() |
e82e9dafd0 | ||
![]() |
bb40374067 | ||
![]() |
c3b9d4ce8b | ||
![]() |
070216ad81 | ||
![]() |
6b03bc9354 | ||
![]() |
5867058d19 | ||
![]() |
49dc7ee49b | ||
![]() |
41191dc607 | ||
![]() |
22001c56b1 | ||
![]() |
088639e8b2 | ||
![]() |
9e21ac3924 | ||
![]() |
e2e8e981a2 | ||
![]() |
7389d5ed1d | ||
![]() |
162374c026 | ||
![]() |
4700089325 | ||
![]() |
c7a4572d73 | ||
![]() |
7381d93c13 | ||
![]() |
97e588a5ad | ||
![]() |
9a7ee00bbf | ||
![]() |
b2bd7e5919 | ||
![]() |
97cee92d94 | ||
![]() |
b9e8448afe | ||
![]() |
6c197f9b70 | ||
![]() |
485a7f920a | ||
![]() |
f48c58f34c | ||
![]() |
fad889db45 | ||
![]() |
775c93001c | ||
![]() |
160bf91665 | ||
![]() |
138ad384e3 | ||
![]() |
9614ceca9b | ||
![]() |
865ffb2588 | ||
![]() |
b83fb8f83e | ||
![]() |
e882505881 | ||
![]() |
88a15aba86 | ||
![]() |
9cb119be62 | ||
![]() |
fd226e46ba | ||
![]() |
91eb57574c | ||
![]() |
25a99af4a1 | ||
![]() |
740a82a761 | ||
![]() |
173a8594b4 | ||
![]() |
81dc3f95e5 | ||
![]() |
912d4bdc19 | ||
![]() |
85b9abd654 | ||
![]() |
2f7075b9c6 | ||
![]() |
2e26331d97 | ||
![]() |
303a5a9734 | ||
![]() |
a9042199c6 | ||
![]() |
3cabf9859d | ||
![]() |
51ee251efb | ||
![]() |
ba1c593d0e | ||
![]() |
7d00c1f0a4 | ||
![]() |
c070604a22 | ||
![]() |
dc2f7bbcd0 | ||
![]() |
437a129900 | ||
![]() |
4997adef8d | ||
![]() |
1b10b679cd | ||
![]() |
495a659895 | ||
![]() |
2dbd565340 | ||
![]() |
01024927d2 | ||
![]() |
32a0b5b636 | ||
![]() |
520dc85ac3 | ||
![]() |
82206534dc | ||
![]() |
1f28880509 | ||
![]() |
8cfc345312 | ||
![]() |
7cf59587ce | ||
![]() |
f5882d5158 | ||
![]() |
06f01938a9 | ||
![]() |
71f4b875ce | ||
![]() |
b103bfdd60 | ||
![]() |
4cffdef805 | ||
![]() |
3f969bbf1f | ||
![]() |
1ca2daf47f | ||
![]() |
179d5432e4 | ||
![]() |
4934191b19 | ||
![]() |
55b799e3df | ||
![]() |
337f1b8a8e | ||
![]() |
898ab01924 | ||
![]() |
1e24067430 | ||
![]() |
e34e9c5a43 | ||
![]() |
aee9703872 | ||
![]() |
4dc99dff76 | ||
![]() |
5fff74598a | ||
![]() |
c3d638d7ce | ||
![]() |
86591acdf2 | ||
![]() |
618466005e | ||
![]() |
f5a715e124 | ||
![]() |
f498f2a20a | ||
![]() |
9b44fc6def | ||
![]() |
b58b32072a | ||
![]() |
f50e7fffe7 | ||
![]() |
fac33baa09 | ||
![]() |
e7dcf8792d | ||
![]() |
db1a0e3ace | ||
![]() |
01b849a518 | ||
![]() |
b8fa857b35 | ||
![]() |
23493e5cc3 | ||
![]() |
c96f7379b4 | ||
![]() |
898feb9c52 | ||
![]() |
ab06ba419e | ||
![]() |
6c60565f33 | ||
![]() |
5e366bd55d | ||
![]() |
126e67fb6b | ||
![]() |
4000a32584 | ||
![]() |
b11d2912e2 | ||
![]() |
0344e7c311 | ||
![]() |
993857d0e8 | ||
![]() |
5fd0065df6 | ||
![]() |
6592c2229c | ||
![]() |
05a4c0c152 | ||
![]() |
fb7062bd9c | ||
![]() |
49a0ef8c0b | ||
![]() |
cfa8b9a2c4 | ||
![]() |
62659edd2b | ||
![]() |
9037b730f6 | ||
![]() |
5f71f57db1 | ||
![]() |
17b2b6201b | ||
![]() |
832f9ab7e0 | ||
![]() |
5163c01d24 | ||
![]() |
3fcff0d75f | ||
![]() |
510a0a6ae2 | ||
![]() |
f6c68cd6de | ||
![]() |
7a68a178ac | ||
![]() |
8b408694b0 | ||
![]() |
d8337aeb05 | ||
![]() |
d4536c62af | ||
![]() |
785df22274 | ||
![]() |
8810e4f158 | ||
![]() |
db7d52dbac | ||
![]() |
3033867fc9 | ||
![]() |
a48272ae61 | ||
![]() |
39f93e4000 | ||
![]() |
d27f8caecc | ||
![]() |
3d49f87308 | ||
![]() |
d47dea92de | ||
![]() |
16ebc794c5 | ||
![]() |
082b1c6619 | ||
![]() |
937bcf3c73 | ||
![]() |
e6355d91f4 | ||
![]() |
9ecff8d5d3 | ||
![]() |
9f676632c3 | ||
![]() |
115f1ecdbd | ||
![]() |
ce6485ef97 | ||
![]() |
953792b4a5 | ||
![]() |
1e8312f7e3 | ||
![]() |
1709810707 | ||
![]() |
c36019a9aa | ||
![]() |
6453a123ab | ||
![]() |
f95470c94a | ||
![]() |
a1660fc37a | ||
![]() |
43911a8919 | ||
![]() |
e1ae78e346 | ||
![]() |
f7a4a6ccfe | ||
![]() |
90d05eef42 | ||
![]() |
2fb62e34fa | ||
![]() |
c755f7e539 | ||
![]() |
1ca9b727c3 | ||
![]() |
329f7b4cee | ||
![]() |
695f7e565f | ||
![]() |
e3c24f34f1 | ||
![]() |
29b4148a00 | ||
![]() |
94444cb76f | ||
![]() |
57bc3e26ba | ||
![]() |
0ba1551293 | ||
![]() |
094905f7f8 | ||
![]() |
c8492ca28f | ||
![]() |
faea358023 | ||
![]() |
bfa20975ca | ||
![]() |
40702d3fed | ||
![]() |
616dca659a | ||
![]() |
d3e309b23b | ||
![]() |
1a598f53d2 | ||
![]() |
f0943ff431 | ||
![]() |
065454bb4e |
@ -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
41
.github/workflows/build-and-test.yml
vendored
Normal 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
5
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
build/
|
||||
*.swp
|
||||
/builddir
|
||||
/subprojects/packagecache/
|
||||
/subprojects/inih-r*
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "subprojects/inih"]
|
||||
path = subprojects/inih
|
||||
url = https://github.com/FeralInteractive/inih.git
|
28
.travis.yml
28
.travis.yml
@ -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
|
120
CHANGELOG.md
120
CHANGELOG.md
@ -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
|
||||
|
||||
|
@ -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
100
README.md
@ -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 [](https://travis-ci.org/FeralInteractive/gamemode)
|
||||
## Development [](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)
|
||||
|
||||
|
27
bootstrap.sh
27
bootstrap.sh
@ -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
71
common/common-cpu.c
Normal 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
51
common/common-cpu.h
Normal 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);
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
86
common/common-profile.c
Normal 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
50
common/common-profile.h
Normal 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
64
common/common-splitlock.c
Normal 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
44
common/common-splitlock.h
Normal 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);
|
@ -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',
|
||||
]
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
540
daemon/gamemode-cpu.c
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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'],
|
||||
)
|
||||
|
@ -1 +0,0 @@
|
||||
@@LIMITSGROUP@ - nice -10
|
@ -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>
|
33
data/gamemode-simulate-game.1.in
Normal file
33
data/gamemode-simulate-game.1.in
Normal 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)
|
@ -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
32
data/gamemodelist
Executable 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
68
data/gamemodelist.1.in
Normal 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
9
data/gamemoderun
Executable 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
50
data/gamemoderun.1.in
Normal 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)
|
@ -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}" "$@"
|
41
data/io.github.feralinteractive.gamemode.metainfo.xml
Normal file
41
data/io.github.feralinteractive.gamemode.metainfo.xml
Normal 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>
|
152
data/meson.build
152
data/meson.build
@ -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
|
||||
|
1
data/pam_limits/10-gamemode.conf.in
Normal file
1
data/pam_limits/10-gamemode.conf.in
Normal file
@ -0,0 +1 @@
|
||||
@@GAMEMODE_PRIVILEGED_GROUP@ - nice -10
|
73
data/polkit/actions/com.feralinteractive.GameMode.policy.in
Normal file
73
data/polkit/actions/com.feralinteractive.GameMode.policy.in
Normal 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>
|
15
data/polkit/rules.d/gamemode.rules.in
Normal file
15
data/polkit/rules.d/gamemode.rules.in
Normal 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;
|
||||
}
|
||||
});
|
1
data/systemd/sysusers.d/gamemode.conf.in
Normal file
1
data/systemd/sysusers.d/gamemode.conf.in
Normal file
@ -0,0 +1 @@
|
||||
g @GAMEMODE_PRIVILEGED_GROUP@ - -
|
@ -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
|
@ -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
|
||||
}
|
@ -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
|
@ -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
|
||||
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=gamemoded
|
||||
Exec=systemctl --user restart gamemoded.service
|
||||
Type=Application
|
||||
Terminal=false
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
)
|
||||
|
100
meson.build
100
meson.build
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
9
subprojects/inih.wrap
Normal 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
141
util/cpucorectl.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
84
util/platprofctl.c
Normal 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
75
util/procsysctl.c
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user