at master 5.6 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 concatLines 11 concatStringsSep 12 mapAttrsToList 13 optional 14 optionals 15 16 mkIf 17 mkOption 18 types 19 ; 20 21 cfg = config.hardware.block; 22 escape = lib.strings.escape [ ''"'' ]; 23 24 udevValue = types.addCheck types.nonEmptyStr (x: builtins.match "[^\n\r]*" x != null) // { 25 name = "udevValue"; 26 description = "udev rule value"; 27 descriptionClass = "noun"; 28 }; 29 30 udevRule = 31 { 32 rotational ? null, 33 include ? null, 34 exclude ? null, 35 scheduler, 36 }: 37 concatStringsSep ", " ( 38 [ 39 ''SUBSYSTEM=="block"'' 40 ''ACTION=="add|change"'' 41 ''TEST=="queue/scheduler"'' 42 ] 43 ++ optionals (rotational != null) [ 44 ''ATTR{queue/rotational}=="${if rotational then "1" else "0"}"'' 45 ] 46 ++ optionals (include != null) [ 47 ''KERNEL=="${escape include}"'' 48 ] 49 ++ optionals (exclude != null) [ 50 ''KERNEL!="${escape exclude}"'' 51 ] 52 ++ [ 53 ''ATTR{queue/scheduler}="${escape scheduler}"'' 54 ] 55 ); 56in 57{ 58 options.hardware.block = { 59 defaultScheduler = mkOption { 60 type = types.nullOr udevValue; 61 default = null; 62 description = '' 63 Default block I/O scheduler. 64 65 Unless `null`, the value is assigned through a udev rule matching all 66 block devices. 67 ''; 68 example = "kyber"; 69 }; 70 71 defaultSchedulerRotational = mkOption { 72 type = types.nullOr udevValue; 73 default = null; 74 description = '' 75 Default block I/O scheduler for rotational drives (e.g. hard disks). 76 77 Unless `null`, the value is assigned through a udev rule matching all 78 rotational block devices. 79 80 This option takes precedence over 81 {option}`config.hardware.block.defaultScheduler`. 82 ''; 83 example = "bfq"; 84 }; 85 86 defaultSchedulerExclude = mkOption { 87 type = types.nullOr udevValue; 88 default = "loop[0-9]*"; 89 description = '' 90 Device name pattern to exclude from default scheduler assignment 91 through {option}`config.hardware.block.defaultScheduler` and 92 {option}`config.hardware.block.defaultSchedulerRotational`. 93 94 By default this excludes loop devices which generally do not benefit 95 from extra I/O scheduling in addition to the scheduling already 96 performed for their backing devices. 97 98 This setting does not affect {option}`config.hardware.block.scheduler`. 99 ''; 100 }; 101 102 scheduler = mkOption { 103 type = types.attrsOf udevValue; 104 default = { }; 105 description = '' 106 Assign block I/O scheduler by device name pattern. 107 108 Names are matched using the {manpage}`udev(7)` pattern syntax: 109 110 `*` 111 : Matches zero or more characters. 112 113 `?` 114 : Matches any single character. 115 116 `[]` 117 : Matches any single character specified in the brackets. Ranges are 118 supported via the `-` character. 119 120 `|` 121 : Separates alternative patterns. 122 123 124 Please note that overlapping patterns may produce unexpected results. 125 More complex configurations requiring these should instead be specified 126 directly through custom udev rules, for example via 127 [{option}`config.services.udev.extraRules`](#opt-services.udev.extraRules), 128 to ensure correct ordering. 129 130 Available schedulers depend on the kernel configuration but modern 131 Linux systems typically support: 132 133 `none` 134 : Nooperation scheduler with no reordering of requests. Suitable 135 for devices with fast random I/O such as NVMe SSDs. 136 137 [`mq-deadline`](https://www.kernel.org/doc/html/latest/block/deadline-iosched.html) 138 : Simple latencyoriented generalpurpose scheduler. 139 140 [`kyber`](https://www.kernel.org/doc/html/latest/block/kyber-iosched.html) 141 : Simple latencyoriented scheduler for fast multiqueue devices 142 like NVMe SSDs. 143 144 [`bfq`](https://www.kernel.org/doc/html/latest/block/bfq-iosched.html) 145 : Complex fairnessoriented scheduler. Higher processing overhead, 146 but good interactive response, especially with slower devices. 147 148 149 Schedulers assigned through this option take precedence over 150 {option}`config.hardware.block.defaultScheduler` and 151 {option}`config.hardware.block.defaultSchedulerRotational` but may be 152 overridden by other udev rules. 153 ''; 154 example = { 155 "mmcblk[0-9]*" = "bfq"; 156 "nvme[0-9]*" = "kyber"; 157 }; 158 }; 159 }; 160 161 config = 162 mkIf 163 (cfg.defaultScheduler != null || cfg.defaultSchedulerRotational != null || cfg.scheduler != { }) 164 { 165 services.udev.packages = [ 166 (pkgs.writeTextDir "etc/udev/rules.d/98-block-io-scheduler.rules" ( 167 concatLines ( 168 optional (cfg.defaultScheduler != null) (udevRule { 169 exclude = cfg.defaultSchedulerExclude; 170 scheduler = cfg.defaultScheduler; 171 }) 172 ++ optional (cfg.defaultSchedulerRotational != null) (udevRule { 173 rotational = true; 174 exclude = cfg.defaultSchedulerExclude; 175 scheduler = cfg.defaultSchedulerRotational; 176 }) 177 ++ mapAttrsToList ( 178 include: scheduler: 179 udevRule { 180 inherit include scheduler; 181 } 182 ) cfg.scheduler 183 ) 184 )) 185 ]; 186 }; 187 188 meta.maintainers = with lib.maintainers; [ mvs ]; 189}