Loading nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -444,6 +444,7 @@ ./services/desktops/pipewire/wireplumber.nix ./services/desktops/profile-sync-daemon.nix ./services/desktops/system-config-printer.nix ./services/desktops/system76-scheduler.nix ./services/desktops/telepathy.nix ./services/desktops/tumbler.nix ./services/desktops/zeitgeist.nix Loading nixos/modules/services/desktops/system76-scheduler.nix 0 → 100644 +296 −0 Original line number Diff line number Diff line { config, lib, pkgs, ... }: let cfg = config.services.system76-scheduler; inherit (builtins) concatStringsSep map toString attrNames; inherit (lib) boolToString types mkOption literalExpression mdDoc optional mkIf mkMerge; inherit (types) nullOr listOf bool int ints float str enum; withDefaults = optionSpecs: defaults: lib.genAttrs (attrNames optionSpecs) (name: mkOption (optionSpecs.${name} // { default = optionSpecs.${name}.default or defaults.${name} or null; })); latencyProfile = withDefaults { latency = { type = int; description = mdDoc "`sched_latency_ns`."; }; nr-latency = { type = int; description = mdDoc "`sched_nr_latency`."; }; wakeup-granularity = { type = float; description = mdDoc "`sched_wakeup_granularity_ns`."; }; bandwidth-size = { type = int; description = mdDoc "`sched_cfs_bandwidth_slice_us`."; }; preempt = { type = enum [ "none" "voluntary" "full" ]; description = mdDoc "Preemption mode."; }; }; schedulerProfile = withDefaults { nice = { type = nullOr (ints.between (-20) 19); description = mdDoc "Niceness."; }; class = { type = nullOr (enum [ "idle" "batch" "other" "rr" "fifo" ]); example = literalExpression "\"batch\""; description = mdDoc "CPU scheduler class."; }; prio = { type = nullOr (ints.between 1 99); example = literalExpression "49"; description = mdDoc "CPU scheduler priority."; }; ioClass = { type = nullOr (enum [ "idle" "best-effort" "realtime" ]); example = literalExpression "\"best-effort\""; description = mdDoc "IO scheduler class."; }; ioPrio = { type = nullOr (ints.between 0 7); example = literalExpression "4"; description = mdDoc "IO scheduler priority."; }; matchers = { type = nullOr (listOf str); default = []; example = literalExpression '' [ "include cgroup=\"/user.slice/*.service\" parent=\"systemd\"" "emacs" ] ''; description = mdDoc "Process matchers."; }; }; cfsProfileToString = name: let p = cfg.settings.cfsProfiles.${name}; in "${name} latency=${toString p.latency} nr-latency=${toString p.nr-latency} wakeup-granularity=${toString p.wakeup-granularity} bandwidth-size=${toString p.bandwidth-size} preempt=\"${p.preempt}\""; prioToString = class: prio: if prio == null then "\"${class}\"" else "(${class})${toString prio}"; schedulerProfileToString = name: a: indent: concatStringsSep " " (["${indent}${name}"] ++ (optional (a.nice != null) "nice=${toString a.nice}") ++ (optional (a.class != null) "sched=${prioToString a.class a.prio}") ++ (optional (a.ioClass != null) "io=${prioToString a.ioClass a.ioPrio}") ++ (optional ((builtins.length a.matchers) != 0) ("{\n${concatStringsSep "\n" (map (m: " ${indent}${m}") a.matchers)}\n${indent}}"))); in { options = { services.system76-scheduler = { enable = lib.mkEnableOption (lib.mdDoc "system76-scheduler"); package = mkOption { type = types.package; default = config.boot.kernelPackages.system76-scheduler; defaultText = literalExpression "config.boot.kernelPackages.system76-scheduler"; description = mdDoc "Which System76-Scheduler package to use."; }; useStockConfig = mkOption { type = bool; default = true; description = mdDoc '' Use the (reasonable and featureful) stock configuration. When this option is `true`, `services.system76-scheduler.settings` are ignored. ''; }; settings = { cfsProfiles = { enable = mkOption { type = bool; default = true; description = mdDoc "Tweak CFS latency parameters when going on/off battery"; }; default = latencyProfile { latency = 6; nr-latency = 8; wakeup-granularity = 1.0; bandwidth-size = 5; preempt = "voluntary"; }; responsive = latencyProfile { latency = 4; nr-latency = 10; wakeup-granularity = 0.5; bandwidth-size = 3; preempt = "full"; }; }; processScheduler = { enable = mkOption { type = bool; default = true; description = mdDoc "Tweak scheduling of individual processes in real time."; }; useExecsnoop = mkOption { type = bool; default = true; description = mdDoc "Use execsnoop (otherwise poll the precess list periodically)."; }; refreshInterval = mkOption { type = int; default = 60; description = mdDoc "Process list poll interval, in seconds"; }; foregroundBoost = { enable = mkOption { type = bool; default = true; description = mdDoc '' Boost foreground process priorities. (And de-boost background ones). Note that this option needs cooperation from the desktop environment to work. On Gnome the client side is implemented by the "System76 Scheduler" shell extension. ''; }; foreground = schedulerProfile { nice = 0; ioClass = "best-effort"; ioPrio = 0; }; background = schedulerProfile { nice = 6; ioClass = "idle"; }; }; pipewireBoost = { enable = mkOption { type = bool; default = true; description = mdDoc "Boost Pipewire client priorities."; }; profile = schedulerProfile { nice = -6; ioClass = "best-effort"; ioPrio = 0; }; }; }; }; assignments = mkOption { type = types.attrsOf (types.submodule { options = schedulerProfile { }; }); default = {}; example = literalExpression '' { nix-builds = { nice = 15; class = "batch"; ioClass = "idle"; matchers = [ "nix-daemon" ]; }; } ''; description = mdDoc "Process profile assignments."; }; exceptions = mkOption { type = types.listOf str; default = []; example = literalExpression '' [ "include descends=\"schedtool\"" "schedtool" ] ''; description = mdDoc "Processes that are left alone."; }; }; }; config = { environment.systemPackages = [ cfg.package ]; services.dbus.packages = [ cfg.package ]; systemd.services.system76-scheduler = { description = "Manage process priorities and CFS scheduler latencies for improved responsiveness on the desktop"; wantedBy = [ "multi-user.target" ]; path = [ # execsnoop needs those to extract kernel headers: pkgs.kmod pkgs.gnutar pkgs.xz ]; serviceConfig = { Type = "dbus"; BusName= "com.system76.Scheduler"; ExecStart = "${cfg.package}/bin/system76-scheduler daemon"; ExecReload = "${cfg.package}/bin/system76-scheduler daemon reload"; }; }; environment.etc = mkMerge [ (mkIf cfg.useStockConfig { # No custom settings: just use stock configuration with a fix for Pipewire "system76-scheduler/config.kdl".source = "${cfg.package}/data/config.kdl"; "system76-scheduler/process-scheduler/00-dist.kdl".source = "${cfg.package}/data/pop_os.kdl"; "system76-scheduler/process-scheduler/01-fix-pipewire-paths.kdl".source = ../../../../pkgs/os-specific/linux/system76-scheduler/01-fix-pipewire-paths.kdl; }) (let settings = cfg.settings; cfsp = settings.cfsProfiles; ps = settings.processScheduler; in mkIf (!cfg.useStockConfig) { "system76-scheduler/config.kdl".text = '' version "2.0" autogroup-enabled false cfs-profiles enable=${boolToString cfsp.enable} { ${cfsProfileToString "default"} ${cfsProfileToString "responsive"} } process-scheduler enable=${boolToString ps.enable} { execsnoop ${boolToString ps.useExecsnoop} refresh-rate ${toString ps.refreshInterval} assignments { ${if ps.foregroundBoost.enable then (schedulerProfileToString "foreground" ps.foregroundBoost.foreground " ") else ""} ${if ps.foregroundBoost.enable then (schedulerProfileToString "background" ps.foregroundBoost.background " ") else ""} ${if ps.pipewireBoost.enable then (schedulerProfileToString "pipewire" ps.pipewireBoost.profile " ") else ""} } } ''; }) { "system76-scheduler/process-scheduler/02-config.kdl".text = "exceptions {\n${concatStringsSep "\n" (map (e: " ${e}") cfg.exceptions)}\n}\n" + "assignments {\n" + (concatStringsSep "\n" (map (name: schedulerProfileToString name cfg.assignments.${name} " ") (attrNames cfg.assignments))) + "\n}\n"; } ]; }; meta = { maintainers = [ lib.maintainers.cmm ]; }; } pkgs/os-specific/linux/system76-scheduler/01-fix-pipewire-paths.kdl 0 → 100644 +8 −0 Original line number Diff line number Diff line assignments { sound-server { // original config matches on /usr/bin/..., but this is NixOS pipewire pipewire-pulse jackd } } pkgs/os-specific/linux/system76-scheduler/default.nix 0 → 100644 +47 −0 Original line number Diff line number Diff line { lib , fetchFromGitHub , rustPlatform , llvm , clang , libclang , pipewire , pkg-config , bcc , dbus }: let version = "2.0.1"; in rustPlatform.buildRustPackage { pname = "system76-scheduler"; inherit version; src = fetchFromGitHub { owner = "pop-os"; repo = "system76-scheduler"; rev = version; hash = "sha256-o4noaLBXHDe7pMBHfQ85uzKJzwbBE5mkWq8h9l6iIZs="; }; cargoSha256 = "sha256-hpFDAhOzm4v3lBWwAl/10pS5xvKCScdKsp5wpCeQ+FE="; nativeBuildInputs = [ pkg-config llvm clang ]; buildInputs = [ dbus pipewire ]; LIBCLANG_PATH = "${libclang.lib}/lib"; EXECSNOOP_PATH = "${bcc}/bin/execsnoop"; # tests don't build doCheck = false; postInstall = '' mkdir -p $out/data install -D -m 0644 data/com.system76.Scheduler.conf $out/etc/dbus-1/system.d/com.system76.Scheduler.conf install -D -m 0644 data/*.kdl $out/data/ ''; meta = with lib; { description = "System76 Scheduler"; homepage = "https://github.com/pop-os/system76-scheduler"; license = licenses.mpl20; platforms = [ "x86_64-linux" "x86-linux" "aarch64-linux" ]; maintainers = [ maintainers.cmm ]; }; } pkgs/top-level/linux-kernels.nix +2 −0 Original line number Diff line number Diff line Loading @@ -502,6 +502,8 @@ in { system76-io = callPackage ../os-specific/linux/system76-io { }; system76-scheduler = callPackage ../os-specific/linux/system76-scheduler { }; tmon = callPackage ../os-specific/linux/tmon { }; tp_smapi = callPackage ../os-specific/linux/tp_smapi { }; Loading Loading
nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -444,6 +444,7 @@ ./services/desktops/pipewire/wireplumber.nix ./services/desktops/profile-sync-daemon.nix ./services/desktops/system-config-printer.nix ./services/desktops/system76-scheduler.nix ./services/desktops/telepathy.nix ./services/desktops/tumbler.nix ./services/desktops/zeitgeist.nix Loading
nixos/modules/services/desktops/system76-scheduler.nix 0 → 100644 +296 −0 Original line number Diff line number Diff line { config, lib, pkgs, ... }: let cfg = config.services.system76-scheduler; inherit (builtins) concatStringsSep map toString attrNames; inherit (lib) boolToString types mkOption literalExpression mdDoc optional mkIf mkMerge; inherit (types) nullOr listOf bool int ints float str enum; withDefaults = optionSpecs: defaults: lib.genAttrs (attrNames optionSpecs) (name: mkOption (optionSpecs.${name} // { default = optionSpecs.${name}.default or defaults.${name} or null; })); latencyProfile = withDefaults { latency = { type = int; description = mdDoc "`sched_latency_ns`."; }; nr-latency = { type = int; description = mdDoc "`sched_nr_latency`."; }; wakeup-granularity = { type = float; description = mdDoc "`sched_wakeup_granularity_ns`."; }; bandwidth-size = { type = int; description = mdDoc "`sched_cfs_bandwidth_slice_us`."; }; preempt = { type = enum [ "none" "voluntary" "full" ]; description = mdDoc "Preemption mode."; }; }; schedulerProfile = withDefaults { nice = { type = nullOr (ints.between (-20) 19); description = mdDoc "Niceness."; }; class = { type = nullOr (enum [ "idle" "batch" "other" "rr" "fifo" ]); example = literalExpression "\"batch\""; description = mdDoc "CPU scheduler class."; }; prio = { type = nullOr (ints.between 1 99); example = literalExpression "49"; description = mdDoc "CPU scheduler priority."; }; ioClass = { type = nullOr (enum [ "idle" "best-effort" "realtime" ]); example = literalExpression "\"best-effort\""; description = mdDoc "IO scheduler class."; }; ioPrio = { type = nullOr (ints.between 0 7); example = literalExpression "4"; description = mdDoc "IO scheduler priority."; }; matchers = { type = nullOr (listOf str); default = []; example = literalExpression '' [ "include cgroup=\"/user.slice/*.service\" parent=\"systemd\"" "emacs" ] ''; description = mdDoc "Process matchers."; }; }; cfsProfileToString = name: let p = cfg.settings.cfsProfiles.${name}; in "${name} latency=${toString p.latency} nr-latency=${toString p.nr-latency} wakeup-granularity=${toString p.wakeup-granularity} bandwidth-size=${toString p.bandwidth-size} preempt=\"${p.preempt}\""; prioToString = class: prio: if prio == null then "\"${class}\"" else "(${class})${toString prio}"; schedulerProfileToString = name: a: indent: concatStringsSep " " (["${indent}${name}"] ++ (optional (a.nice != null) "nice=${toString a.nice}") ++ (optional (a.class != null) "sched=${prioToString a.class a.prio}") ++ (optional (a.ioClass != null) "io=${prioToString a.ioClass a.ioPrio}") ++ (optional ((builtins.length a.matchers) != 0) ("{\n${concatStringsSep "\n" (map (m: " ${indent}${m}") a.matchers)}\n${indent}}"))); in { options = { services.system76-scheduler = { enable = lib.mkEnableOption (lib.mdDoc "system76-scheduler"); package = mkOption { type = types.package; default = config.boot.kernelPackages.system76-scheduler; defaultText = literalExpression "config.boot.kernelPackages.system76-scheduler"; description = mdDoc "Which System76-Scheduler package to use."; }; useStockConfig = mkOption { type = bool; default = true; description = mdDoc '' Use the (reasonable and featureful) stock configuration. When this option is `true`, `services.system76-scheduler.settings` are ignored. ''; }; settings = { cfsProfiles = { enable = mkOption { type = bool; default = true; description = mdDoc "Tweak CFS latency parameters when going on/off battery"; }; default = latencyProfile { latency = 6; nr-latency = 8; wakeup-granularity = 1.0; bandwidth-size = 5; preempt = "voluntary"; }; responsive = latencyProfile { latency = 4; nr-latency = 10; wakeup-granularity = 0.5; bandwidth-size = 3; preempt = "full"; }; }; processScheduler = { enable = mkOption { type = bool; default = true; description = mdDoc "Tweak scheduling of individual processes in real time."; }; useExecsnoop = mkOption { type = bool; default = true; description = mdDoc "Use execsnoop (otherwise poll the precess list periodically)."; }; refreshInterval = mkOption { type = int; default = 60; description = mdDoc "Process list poll interval, in seconds"; }; foregroundBoost = { enable = mkOption { type = bool; default = true; description = mdDoc '' Boost foreground process priorities. (And de-boost background ones). Note that this option needs cooperation from the desktop environment to work. On Gnome the client side is implemented by the "System76 Scheduler" shell extension. ''; }; foreground = schedulerProfile { nice = 0; ioClass = "best-effort"; ioPrio = 0; }; background = schedulerProfile { nice = 6; ioClass = "idle"; }; }; pipewireBoost = { enable = mkOption { type = bool; default = true; description = mdDoc "Boost Pipewire client priorities."; }; profile = schedulerProfile { nice = -6; ioClass = "best-effort"; ioPrio = 0; }; }; }; }; assignments = mkOption { type = types.attrsOf (types.submodule { options = schedulerProfile { }; }); default = {}; example = literalExpression '' { nix-builds = { nice = 15; class = "batch"; ioClass = "idle"; matchers = [ "nix-daemon" ]; }; } ''; description = mdDoc "Process profile assignments."; }; exceptions = mkOption { type = types.listOf str; default = []; example = literalExpression '' [ "include descends=\"schedtool\"" "schedtool" ] ''; description = mdDoc "Processes that are left alone."; }; }; }; config = { environment.systemPackages = [ cfg.package ]; services.dbus.packages = [ cfg.package ]; systemd.services.system76-scheduler = { description = "Manage process priorities and CFS scheduler latencies for improved responsiveness on the desktop"; wantedBy = [ "multi-user.target" ]; path = [ # execsnoop needs those to extract kernel headers: pkgs.kmod pkgs.gnutar pkgs.xz ]; serviceConfig = { Type = "dbus"; BusName= "com.system76.Scheduler"; ExecStart = "${cfg.package}/bin/system76-scheduler daemon"; ExecReload = "${cfg.package}/bin/system76-scheduler daemon reload"; }; }; environment.etc = mkMerge [ (mkIf cfg.useStockConfig { # No custom settings: just use stock configuration with a fix for Pipewire "system76-scheduler/config.kdl".source = "${cfg.package}/data/config.kdl"; "system76-scheduler/process-scheduler/00-dist.kdl".source = "${cfg.package}/data/pop_os.kdl"; "system76-scheduler/process-scheduler/01-fix-pipewire-paths.kdl".source = ../../../../pkgs/os-specific/linux/system76-scheduler/01-fix-pipewire-paths.kdl; }) (let settings = cfg.settings; cfsp = settings.cfsProfiles; ps = settings.processScheduler; in mkIf (!cfg.useStockConfig) { "system76-scheduler/config.kdl".text = '' version "2.0" autogroup-enabled false cfs-profiles enable=${boolToString cfsp.enable} { ${cfsProfileToString "default"} ${cfsProfileToString "responsive"} } process-scheduler enable=${boolToString ps.enable} { execsnoop ${boolToString ps.useExecsnoop} refresh-rate ${toString ps.refreshInterval} assignments { ${if ps.foregroundBoost.enable then (schedulerProfileToString "foreground" ps.foregroundBoost.foreground " ") else ""} ${if ps.foregroundBoost.enable then (schedulerProfileToString "background" ps.foregroundBoost.background " ") else ""} ${if ps.pipewireBoost.enable then (schedulerProfileToString "pipewire" ps.pipewireBoost.profile " ") else ""} } } ''; }) { "system76-scheduler/process-scheduler/02-config.kdl".text = "exceptions {\n${concatStringsSep "\n" (map (e: " ${e}") cfg.exceptions)}\n}\n" + "assignments {\n" + (concatStringsSep "\n" (map (name: schedulerProfileToString name cfg.assignments.${name} " ") (attrNames cfg.assignments))) + "\n}\n"; } ]; }; meta = { maintainers = [ lib.maintainers.cmm ]; }; }
pkgs/os-specific/linux/system76-scheduler/01-fix-pipewire-paths.kdl 0 → 100644 +8 −0 Original line number Diff line number Diff line assignments { sound-server { // original config matches on /usr/bin/..., but this is NixOS pipewire pipewire-pulse jackd } }
pkgs/os-specific/linux/system76-scheduler/default.nix 0 → 100644 +47 −0 Original line number Diff line number Diff line { lib , fetchFromGitHub , rustPlatform , llvm , clang , libclang , pipewire , pkg-config , bcc , dbus }: let version = "2.0.1"; in rustPlatform.buildRustPackage { pname = "system76-scheduler"; inherit version; src = fetchFromGitHub { owner = "pop-os"; repo = "system76-scheduler"; rev = version; hash = "sha256-o4noaLBXHDe7pMBHfQ85uzKJzwbBE5mkWq8h9l6iIZs="; }; cargoSha256 = "sha256-hpFDAhOzm4v3lBWwAl/10pS5xvKCScdKsp5wpCeQ+FE="; nativeBuildInputs = [ pkg-config llvm clang ]; buildInputs = [ dbus pipewire ]; LIBCLANG_PATH = "${libclang.lib}/lib"; EXECSNOOP_PATH = "${bcc}/bin/execsnoop"; # tests don't build doCheck = false; postInstall = '' mkdir -p $out/data install -D -m 0644 data/com.system76.Scheduler.conf $out/etc/dbus-1/system.d/com.system76.Scheduler.conf install -D -m 0644 data/*.kdl $out/data/ ''; meta = with lib; { description = "System76 Scheduler"; homepage = "https://github.com/pop-os/system76-scheduler"; license = licenses.mpl20; platforms = [ "x86_64-linux" "x86-linux" "aarch64-linux" ]; maintainers = [ maintainers.cmm ]; }; }
pkgs/top-level/linux-kernels.nix +2 −0 Original line number Diff line number Diff line Loading @@ -502,6 +502,8 @@ in { system76-io = callPackage ../os-specific/linux/system76-io { }; system76-scheduler = callPackage ../os-specific/linux/system76-scheduler { }; tmon = callPackage ../os-specific/linux/tmon { }; tp_smapi = callPackage ../os-specific/linux/tp_smapi { }; Loading