I found a fix for plymouth not displaying but I'm not sure who to tell

boot.initrd.systemd.services.plymouth-start = {
  after = [ “systemd-modules-load.service” ];
  requires = [ “systemd-modules-load.service” ];
};

So on my Desktop, without this, with systemd based init (which iirc is about to become the default), ends up starting plymouth before the nVidia Drivers have a chance to kick in - plymouth will start up in about 0.6 seconds, but the nVidia driver won’t finish loading for almost 2 seconds - So what happens is plymouth starts up, grabs the screen, and them almost immediately nVidia loads and I just get a text boot instead because the screen buffer that plymouth grabbed doesn’t exist or at least isn’t primary any more.

This makes plymouth wait for the modules to finish starting, and fixed the issue.

So… Who do I poke on nixpkgs github to get this included in nixpkgs? This could fall under nVidia, or Plymouth, or even Systemd.

(Also if someone relevant sees this, and decides to just add it, that’d be fantastic)

6 Likes

When I look at the GitHub history, I see that acid-bong has been working on the last three commits to plymouth.nix

You can just submit a PR yourself, presuming you’re willing to discuss the reasoning there.

1 Like

Hrm… could this be happening not only with NVIDIA GPUs?

I’m currently testing it on Intel and AMD systems, and so far it looks promising!

Hrm… I’ve been experiencing inconsistent behavior on this front. Some boots I’d get plymouth and other I wouldn’t. Either with AMD or with some integrated Intel. For possibly months on unstable. Never looked into it. Just accepted it. So, I hope that this can be fixed.

3 Likes

Yeah, that’s exactly how it was for me, too!

1 Like

@Krutonium please link the PR I hope you’re submitting soon here :wink: :crossed_fingers:

1 Like

Data point: I have this problem on a Surface Pro 3 with integrated graphics, but not on my desktop with an Nvidia card.

This is really not the kind of solution I’d want to see, for a couple of reasons.

  1. Graphics drivers in initrd are not a good idea if they can be avoided. They’re generally not actually necessary (simple-drm will handle graphics well enough for this stage, and your EFI should be picking a good-looking resolution to start with (which you can fix with systemd-boot’s console mode if it isn’t)) and they bloat the size of your initrd by a lot (taking up a lot of /boot / ESP space).
  2. If plymouth isn’t figuring out what to do later when the GPU driver takes over, that’s a plymouth bug, not a NixOS bug. Delaying the start of plymouth for the GPU driver is just a really wrong solution; plymouth should be handling the transition correctly, not starting later.
1 Like

Is this because of adding points of failure, perf or something else?

It’s because it’s (usually) pointless, and as I said it makes the initrd on your ESP way bigger, wasting space. I imagine it would also be bad for performance, since it’s more data for the kernel to decompress as it unpacks the initrd, and since loading something like amdgpu will probably take time in stage 1 that would be better done in the background of stage 2. But performance isn’t really my primary motivation; it’s just adding something big and messy to stage 1 that doesn’t need to be there.

That’s true, it’s definitely worse; however plymouth doesn’t support changing framebuffers as far as I can tell, and nVidia’s drivers are primarily the only ones doing this - And you basically have to put them initrd to even come close to a “seamless” boot.

Hm, I’m not sure that’s right? If I boot with modprobe.blacklist=amdgpu and plymouth:debug, then I’ll boot without the GPU driver and plymouth will do debug logging. I can boot up, delete /var/log/plymouth-debug.log, and then do systemctl restart plymouth-start.service. What this does is put me into a state where I have interactive control over SSH and plymouth is running with a clean log and for sure with simpledrm as its display backend (I can even tell visually since I can cause it to boot with a worse resolution for the EFI framebuffer, which amdgpu would modeset away). Once I visually confirm it’s running, over SSH I can run modprobe amdgpu to load the GPU driver and see how it handles the change over. After a few seconds, amdgpu takes over the display and does modesetting, and plymouth visibly takes over the amdgpu output with the improved resolution. Here’s what I get in the debug log: plymouth-debug.log · GitHub

When amdgpu takes over, the simpledrm device is removed, which causes plymouth to stop trying to use it for display output. Then the amdgpu DRM outputs come up, and after a moment it finds card1 to use, and plymouth successfully switches to the new video output.

So unless you mean nvidia does something other than bringing down the simpledrm device and bringing up an nvidia DRM device, I’m not sure why nvidia wouldn’t work just fine the same way.

1 Like

From my understanding nVidia leaves the simpledrm up and just replaces it - Either way plymouth hates nVidia during boot.

Warning: I would avoid adding this unless it is actually needed:

requires = [ "systemd-modules-load.service" ];

On my system, this prevented modules from boot.kernelModules from being loaded at boot.

Removing the requires directive fixed the issue, and the service still works correctly without it.

Ahh, yea actually that’s a good point I hadn’t realized about this workaround, and that’s something we should probably be able to issue a build failure / warning about: When something is a dependency of initrd-switch-root.target, it can cause pretty extreme confusion about what happens with units in stage 2, because the stage 1 unit can actually survive into stage 2 and take its place.

In this case, plymouth-start.service has WantedBy=initrd-switch-root.target, which allows the unit state that systemd keeps track of to persist from stage 1 to stage 2. For plymouth-start.service, this is correct, because plymouth is meant to continue running across the transition from stage 1 to 2. So when stage 2 starts, it sees that plymouth-start.service is already active, and it doesn’t queue a new plymouth-start.service/start job. Since plymouth is already running, that’s correct.

But that would not be correct for systemd-modules-load.service. If your /etc/modules-load.d/ differs between stage 1 and stage 2 (which is very normal), you need this service to start again in stage 2. But since it has RemainAfterExit=yes, it needs to be stopped before transition, or else it’ll be active at transition, and stage 2 systemd will think it doesn’t need to be started again. The way it’s usually stopped is stage 1’s initrd-cleanup.service, which does systemctl isolate initrd-switch-root.target; the isolate command says to stop everything except for the dependencies of the specified unit, so that’s what normally stops systemd-modules-load.service before transition.

If you add boot.initrd.systemd.services.plymouth-start.requires = [ "systemd-modules-load.service" ];, that makes systemd-modules-load.service a transitive dependency of initrd-switch-root.target, which means it will survive transition and it won’t start again in stage 2. There’s an even worse version of this problem, where you accidentally make sysinit.target a dependency of initrd-switch-root.target if you add your own service to initrd-switch-root.target’s dependencies and don’t have DefaultDependencies=no to break the default dependency on sysinit.target; in that case, sysinit.target begins stage 2 already active, which breaks all sorts of unit ordering rules in stage 2 and becomes extremely confusing.

We should probably add logic to detect these errant dependencies and fail the build if you have them, because it’s extremely unusual for a unit surviving the stage 2 transition to be intended, and the consequences can be awfully hard to diagnose. Then we can just have an option like boot.initrd.systemd.<type>.<name>.allowStage2Persistence to explicitly permit the units that we know should do this, like initrd-fs.target and plymouth-start.service.

2 Likes