1
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-06-18 15:39:46 +03:00
nixpkgs/nixos/modules/system/boot/systemd/repart.nix
Markus Sütter c6476ca119 repart: Enable discard option
systemd-repart can be configured to not automatically issue BLKDISCARD commands
to the underlying hardware.

This PR exposes this option in the repart module.
2025-04-11 12:46:50 +02:00

208 lines
6.7 KiB
Nix

{
config,
lib,
pkgs,
utils,
...
}:
let
cfg = config.systemd.repart;
initrdCfg = config.boot.initrd.systemd.repart;
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format (
lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions
);
partitionAssertions = lib.mapAttrsToList (
fileName: definition:
let
inherit (utils.systemdUtils.lib) GPTMaxLabelLength;
labelLength = builtins.stringLength definition.Label;
in
{
assertion = definition ? Label -> GPTMaxLabelLength >= labelLength;
message = ''
The partition label '${definition.Label}' defined for '${fileName}' is ${toString labelLength}
characters long, but the maximum label length supported by systemd is ${toString GPTMaxLabelLength}.
'';
}
) cfg.partitions;
in
{
options = {
boot.initrd.systemd.repart = {
enable = lib.mkEnableOption "systemd-repart" // {
description = ''
Grow and add partitions to a partition table at boot time in the initrd.
systemd-repart only works with GPT partition tables.
To run systemd-repart after the initrd, see
`options.systemd.repart.enable`.
'';
};
device = lib.mkOption {
type = with lib.types; nullOr str;
description = ''
The device to operate on.
If `device == null`, systemd-repart will operate on the device
backing the root partition. So in order to dynamically *create* the
root partition in the initrd you need to set a device.
'';
default = null;
example = "/dev/vda";
};
empty = lib.mkOption {
type = lib.types.enum [
"refuse"
"allow"
"require"
"force"
"create"
];
description = ''
Controls how to operate on empty devices that contain no partition table yet.
See {manpage}`systemd-repart(8)` for details.
'';
example = "require";
default = "refuse";
};
discard = lib.mkOption {
type = lib.types.bool;
description = ''
Controls whether to issue the BLKDISCARD I/O control command on the
space taken up by any added partitions or on the space in between them.
Usually, it's a good idea to issue this request since it tells the underlying
hardware that the covered blocks shall be considered empty, improving performance.
See {manpage}`systemd-repart(8)` for details.
'';
default = true;
};
};
systemd.repart = {
enable = lib.mkEnableOption "systemd-repart" // {
description = ''
Grow and add partitions to a partition table.
systemd-repart only works with GPT partition tables.
To run systemd-repart while in the initrd, see
`options.boot.initrd.systemd.repart.enable`.
'';
};
partitions = lib.mkOption {
type =
with lib.types;
attrsOf (
attrsOf (oneOf [
str
int
bool
(listOf str)
])
);
default = { };
example = {
"10-root" = {
Type = "root";
};
"20-home" = {
Type = "home";
SizeMinBytes = "512M";
SizeMaxBytes = "2G";
};
};
description = ''
Specify partitions as a set of the names of the definition files as the
key and the partition configuration as its value. The partition
configuration can use all upstream options. See {manpage}`repart.d(5)`
for all available options.
'';
};
};
};
config = lib.mkIf (cfg.enable || initrdCfg.enable) {
assertions = [
{
assertion = initrdCfg.enable -> config.boot.initrd.systemd.enable;
message = ''
'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled.
'';
}
] ++ partitionAssertions;
# systemd-repart uses loopback devices for partition creation
boot.initrd.availableKernelModules = lib.optional initrdCfg.enable "loop";
boot.initrd.systemd = lib.mkIf initrdCfg.enable {
additionalUpstreamUnits = [
"systemd-repart.service"
];
storePaths = [
"${config.boot.initrd.systemd.package}/bin/systemd-repart"
];
contents."/etc/repart.d".source = definitionsDirectory;
# Override defaults in upstream unit.
services.systemd-repart =
let
deviceUnit = "${utils.escapeSystemdPath initrdCfg.device}.device";
in
{
# systemd-repart tries to create directories in /var/tmp by default to
# store large temporary files that benefit from persistence on disk. In
# the initrd, however, /var/tmp does not provide more persistence than
# /tmp, so we re-use it here.
environment."TMPDIR" = "/tmp";
serviceConfig = {
ExecStart = [
" " # required to unset the previous value.
# When running in the initrd, systemd-repart by default searches
# for definition files in /sysroot or /sysusr. We tell it to look
# in the initrd itself.
''
${config.boot.initrd.systemd.package}/bin/systemd-repart \
--definitions=/etc/repart.d \
--dry-run=no \
--empty=${initrdCfg.empty} \
--discard=${lib.boolToString initrdCfg.discard} \
${lib.optionalString (initrdCfg.device != null) initrdCfg.device}
''
];
};
# systemd-repart needs to run after /sysroot (or /sysuser, but we
# don't have it) has been mounted because otherwise it cannot
# determine the device (i.e disk) to operate on. If you want to run
# systemd-repart without /sysroot (i.e. to create the root
# partition), you have to explicitly tell it which device to operate
# on. The service then needs to be ordered to run after this device
# is available.
requires = lib.mkIf (initrdCfg.device != null) [ deviceUnit ];
after = if initrdCfg.device == null then [ "sysroot.mount" ] else [ deviceUnit ];
};
};
environment.etc = lib.mkIf cfg.enable {
"repart.d".source = definitionsDirectory;
};
systemd = lib.mkIf cfg.enable {
additionalUpstreamSystemUnits = [
"systemd-repart.service"
];
};
};
meta.maintainers = with lib.maintainers; [ nikstur ];
}