-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
nixos/config/sysfs: init module #391329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
nixos/config/sysfs: init module #391329
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,266 @@ | ||
| { | ||
| lib, | ||
| config, | ||
| utils, | ||
| pkgs, | ||
| ... | ||
| }: | ||
|
|
||
| let | ||
| inherit (lib) | ||
| all | ||
| any | ||
| concatLines | ||
| concatStringsSep | ||
| escapeShellArg | ||
| flatten | ||
| floatToString | ||
| foldl' | ||
| head | ||
| isAttrs | ||
| isDerivation | ||
| isFloat | ||
| isList | ||
| length | ||
| listToAttrs | ||
| match | ||
| mapAttrsToList | ||
| nameValuePair | ||
| removePrefix | ||
| tail | ||
| throwIf | ||
| ; | ||
|
|
||
| inherit (lib.options) | ||
| showDefs | ||
| showOption | ||
| ; | ||
|
|
||
| inherit (lib.strings) | ||
| escapeC | ||
| isConvertibleWithToString | ||
| ; | ||
|
|
||
| inherit (lib.path.subpath) join; | ||
|
|
||
| inherit (utils) escapeSystemdPath; | ||
|
|
||
| cfg = config.boot.kernel.sysfs; | ||
|
|
||
| sysfsAttrs = with lib.types; nullOr (either sysfsValue (attrsOf sysfsAttrs)); | ||
| sysfsValue = lib.mkOptionType { | ||
| name = "sysfs value"; | ||
| description = "sysfs attribute value"; | ||
| descriptionClass = "noun"; | ||
| check = v: isConvertibleWithToString v; | ||
| merge = | ||
| loc: defs: | ||
| if length defs == 1 then | ||
| (head defs).value | ||
| else | ||
| (foldl' ( | ||
| first: def: | ||
| # merge definitions if they produce the same value string | ||
| throwIf (mkValueString first.value != mkValueString def.value) | ||
| "The option \"${showOption loc}\" has conflicting definition values:${ | ||
| showDefs [ | ||
| first | ||
| def | ||
| ] | ||
| }" | ||
| first | ||
| ) (head defs) (tail defs)).value; | ||
| }; | ||
|
|
||
| mapAttrsToListRecursive = | ||
| fn: set: | ||
| let | ||
| recurse = | ||
| p: v: | ||
| if isAttrs v && !isDerivation v then mapAttrsToList (n: v: recurse (p ++ [ n ]) v) v else fn p v; | ||
| in | ||
| flatten (recurse [ ] set); | ||
|
|
||
| mkPath = p: "/sys" + removePrefix "." (join p); | ||
| hasGlob = p: any (n: match ''(.*[^\\])?[*?[].*'' n != null) p; | ||
|
|
||
| mkValueString = | ||
| v: | ||
| # true will be converted to "1" by toString, saving one branch | ||
| if v == false then | ||
| "0" | ||
| else if isFloat v then | ||
| floatToString v # warn about loss of precision | ||
| else if isList v then | ||
| concatStringsSep "," (map mkValueString v) | ||
| else | ||
| toString v; | ||
|
|
||
| # escape whitespace and linebreaks, as well as the escape character itself, | ||
| # to ensure that field boundaries are always preserved | ||
| escapeTmpfiles = escapeC [ | ||
| "\t" | ||
| "\n" | ||
| "\r" | ||
| " " | ||
| "\\" | ||
| ]; | ||
|
|
||
| tmpfiles = pkgs.runCommand "nixos-sysfs-tmpfiles.d" { } ( | ||
| '' | ||
| mkdir "$out" | ||
| '' | ||
| + concatLines ( | ||
| mapAttrsToListRecursive ( | ||
| p: v: | ||
| let | ||
| path = mkPath p; | ||
| in | ||
| if v == null then | ||
| [ ] | ||
| else | ||
| '' | ||
| printf 'w %s - - - - %s\n' \ | ||
| ${escapeShellArg (escapeTmpfiles path)} \ | ||
| ${escapeShellArg (escapeTmpfiles (mkValueString v))} \ | ||
| >"$out"/${escapeShellArg (escapeSystemdPath path)}.conf | ||
| '' | ||
| ) cfg | ||
| ) | ||
| ); | ||
| in | ||
| { | ||
| options = { | ||
| boot.kernel.sysfs = lib.mkOption { | ||
| type = lib.types.submodule { | ||
| freeformType = lib.types.attrsOf sysfsAttrs // { | ||
| description = "nested attribute set of null or sysfs attribute values"; | ||
| }; | ||
| }; | ||
|
|
||
| description = '' | ||
illdefined marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| sysfs attributes to be set as soon as they become available. | ||
|
|
||
| Attribute names represent path components in the sysfs filesystem and | ||
| cannot be `.` or `..` nor contain any slash character (`/`). | ||
|
|
||
| Names may contain shell‐style glob patterns (`*`, `?` and `[…]`) | ||
| matching a single path component, these should however be used with | ||
| caution, as they may produce unexpected results if attribute paths | ||
| overlap. | ||
|
|
||
| Values will be converted to strings, with list elements concatenated | ||
| with commata and booleans converted to numeric values (`0` or `1`). | ||
|
|
||
| `null` values are ignored, allowing removal of values defined in other | ||
| modules, as are empty attribute sets. | ||
|
|
||
| List values defined in different modules will _not_ be concatenated. | ||
|
|
||
| This option may only be used for attributes which can be set | ||
| idempotently, as the configured values might be written more than once. | ||
| ''; | ||
|
|
||
| default = { }; | ||
|
|
||
| example = lib.literalExpression '' | ||
| { | ||
| # enable transparent hugepages with deferred defragmentaion | ||
| kernel.mm.transparent_hugepage = { | ||
| enabled = "always"; | ||
| defrag = "defer"; | ||
| shmem_enabled = "within_size"; | ||
| }; | ||
|
|
||
| devices.system.cpu = { | ||
| # configure powesave frequency governor for all CPUs | ||
| # the [0-9]* glob pattern ensures that other paths | ||
| # like cpufreq or cpuidle are not matched | ||
| "cpu[0-9]*" = { | ||
| scaling_governor = "powersave"; | ||
| energy_performance_preference = 8; | ||
| }; | ||
|
|
||
| # disable frequency boost | ||
| intel_pstate.no_turbo = true; | ||
| }; | ||
| } | ||
| ''; | ||
| }; | ||
| }; | ||
|
|
||
| config = lib.mkIf (cfg != { }) { | ||
| systemd = { | ||
| paths = | ||
| { | ||
| "nixos-sysfs@" = { | ||
| description = "/%I attribute watcher"; | ||
| pathConfig.PathExistsGlob = "/%I"; | ||
| unitConfig.DefaultDependencies = false; | ||
| }; | ||
| } | ||
| // listToAttrs ( | ||
| mapAttrsToListRecursive ( | ||
| p: v: | ||
| if v == null then | ||
| [ ] | ||
| else | ||
| nameValuePair "nixos-sysfs@${escapeSystemdPath (mkPath p)}" { | ||
| overrideStrategy = "asDropin"; | ||
| wantedBy = [ "sysinit.target" ]; | ||
| before = [ "sysinit.target" ]; | ||
| } | ||
| ) cfg | ||
| ); | ||
|
|
||
| services."nixos-sysfs@" = { | ||
| description = "/%I attribute setter"; | ||
|
|
||
| unitConfig = { | ||
| DefaultDependencies = false; | ||
| AssertPathIsMountPoint = "/sys"; | ||
| AssertPathExistsGlob = "/%I"; | ||
| }; | ||
|
|
||
| serviceConfig = { | ||
| Type = "oneshot"; | ||
| RemainAfterExit = true; | ||
|
|
||
| # while we could be tempted to use simple shell script to set the | ||
| # sysfs attributes specified by the path or glob pattern, it is | ||
| # almost impossible to properly escape a glob pattern so that it | ||
| # can be used safely in a shell script | ||
| ExecStart = "${lib.getExe' config.systemd.package "systemd-tmpfiles"} --prefix=/sys --create ${tmpfiles}/%i.conf"; | ||
|
|
||
| # hardening may be overkill for such a simple and short‐lived | ||
| # service, the following settings would however be suitable to deny | ||
| # access to anything but /sys | ||
| #ProtectProc = "noaccess"; | ||
| #ProcSubset = "pid"; | ||
| #ProtectSystem = "strict"; | ||
| #PrivateDevices = true; | ||
| #SystemCallErrorNumber = "EPERM"; | ||
| #SystemCallFilter = [ | ||
| # "@basic-io" | ||
| # "@file-system" | ||
| #]; | ||
| }; | ||
| }; | ||
| }; | ||
|
|
||
| warnings = mapAttrsToListRecursive ( | ||
| p: v: | ||
| if hasGlob p then | ||
| "Attribute path \"${concatStringsSep "." p}\" contains glob patterns. Please ensure that it does not overlap with other paths." | ||
| else | ||
| [ ] | ||
| ) cfg; | ||
|
|
||
| assertions = mapAttrsToListRecursive (p: v: { | ||
| assertion = all (n: match ''(\.\.?|.*/.*)'' n == null) p; | ||
| message = "Attribute path \"${concatStringsSep "." p}\" has invalid components."; | ||
| }) cfg; | ||
| }; | ||
|
|
||
| meta.maintainers = with lib.maintainers; [ mvs ]; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { lib, ... }: | ||
|
|
||
| { | ||
| name = "sysfs"; | ||
| meta.maintainers = with lib.maintainers; [ mvs ]; | ||
|
|
||
| nodes.machine = { | ||
| boot.kernel.sysfs = { | ||
| kernel.mm.transparent_hugepage = { | ||
| enabled = "always"; | ||
| defrag = "defer"; | ||
| shmem_enabled = "within_size"; | ||
| }; | ||
|
|
||
| block."*".queue.scheduler = "none"; | ||
| }; | ||
| }; | ||
|
|
||
| testScript = | ||
| { nodes, ... }: | ||
| let | ||
| inherit (nodes.machine.boot.kernel) sysfs; | ||
| in | ||
| '' | ||
| from shlex import quote | ||
|
|
||
| def check(filename, contents): | ||
| machine.succeed('grep -F -q {} {}'.format(quote(contents), quote(filename))) | ||
|
|
||
| check('/sys/kernel/mm/transparent_hugepage/enabled', | ||
| '[${sysfs.kernel.mm.transparent_hugepage.enabled}]') | ||
| check('/sys/kernel/mm/transparent_hugepage/defrag', | ||
| '[${sysfs.kernel.mm.transparent_hugepage.defrag}]') | ||
| check('/sys/kernel/mm/transparent_hugepage/shmem_enabled', | ||
| '[${sysfs.kernel.mm.transparent_hugepage.shmem_enabled}]') | ||
| ''; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
types have descriptions? That's new to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description is usually generated automatically, but due to the nesting this would lead to an infinite recursion. I therefore override the
descriptionattribute and describe the type manually.This description is included in the generated option documentation.