Learn more about these different git repos.
Other Git URLs
There is a lot of movement in this area so I'd like to bring together people working on all related things here.
GRUB: @jwrdegoede @javierm @pjones systemd: @lennart atomic/coreOS/IoT: @walters @dustymabe @jlebon @pbrobinson
systemd is introducing boot counting in its bootloader, systemd-boot, putting the counter in the boot entry filename as a suffix (see PR: https://github.com/systemd/systemd/pull/9437)
systemd also introduces a bootloader-agnostic boot-complete.target with this change, which may be similar to our greenboot.target(Doc: https://github.com/poettering/systemd/blob/5ccca2145dec23340e4c60e93d57f771a3180509/doc/AUTOMATIC_BOOT_ASSESSMENT.md)
boot-complete.target
greenboot.target
GRUB recently introduced a boot_succes grubenv var and an increment function for grubenv vars. This will be used in >F29 to hide the boot menu on single OS installs by default. This variable can be set from userspace (see: https://github.com/rhboot/grub2/commit/80d64d4fe206d03b7ebadcf32dab92349d57ba70#diff-54a02322910a35ab515b71dde0b9de86)
GRUB does not allow for changing boot entry filenames
Add a new grubenv var boot_counter (or similar), a decrement function and logic to switch saved_entry upon reaching 0 in the boot_counter to implement boot counting.
boot_counter
saved_entry
Use greenboot for determining boot success (setting the boot_success grubenv var to 1 on green boot status)
boot_success
Can we agree on not putting the counter in the bootentry filename when using GRUB?
Will we be able to reuse systemd's boot-complete.target?
Where should the logic to switch saved_entry live?
Please add your corrections of the above as well as any thoughts and questions you have regarding the implementation. Guidance on any of this is also highly appreciated.
GRUB recently introduced a boot_success grubenv var and an increment function for grubenv vars.
Neat!
How does resetting the counter work? E.g. with the filename approach, this is done implicitly when creating a new entry. If we keep a single variable separately, then e.g. would OSTree need to learn to reset the counter when creating a new deployment?
Would it be possible to keep the counter in the BLS entry file itself instead? Then new files implicitly start from the default count.
Yeah, it'd would be nice to standardize on it! It seems like that's mostly orthogonal to the bootloader details of the equation?
another question via IRC: <dustymabe> where are grubenv vars stored? and how/when can/do they get modified/accessed ?
<dustymabe> where are grubenv vars stored? and how/when can/do they get modified/accessed ?
Hi All,
I think we cannot only agree on that, I think that we must do that since the grub filesystem drivers don't support renames AFAIK.
I think that is something which should be strived for. The workstation case is a bit special because we want to signal success from within the user-session (we want to be sure the display output and keyboard input work, iow we want to be sure the user can interact with the system). So we have a systemd timer activated user systemd unit which sets boot_success in the grubenv.
But as said, this is a bit special, for non interactive systems having some target which when reached signals success seems to be the way to go and standardizing that seems like a good idea.
grub.cfg syntax is quite bash like, I think you can probably do all this with a grub.cfg snippet. currently grub.cfg files is generated by sh scripts living under /etc/grub.d we are thinking about moving to doing this build-time though, since once we have bls there will be no more need to modify grub.cfg
where are grubenv vars stored? and how/when can/do they get modified/accessed ?
These are stored in /boot/grub2/grubenv, which on EFI systems is a symlink to /boot/efi/EFI/fedora/grubenv. This is a simply text file, but there are some special rules about its contents. It should only be modified through grub2-editenv.
That is a good question, I think the counter should be put in the grubenv and indeed needs to be reset after installing an update.
That would require 2 things:
1) Modifying the bls spec to allow for this 2) Modifying grubs bls parser to support not only reading, but also modifying and writing back the bls file. Note that grub's filesystem code does not allow resizing files, so it would only be able to modify the count if it is already there (so there is pre-existing space for it)
All in all I don't think this is a good idea.
Regards,
Hans
p.s.
They can be modified from within grub during boot (e.g. the menu auto hide support clears the boot_success flag at boot before doing anything else) and from within the running os using grub2-editenv.
@jwrdegoede @jlebon
Where should the logic to switch saved_entry live? grub.cfg syntax is quite bash like, I think you can probably do all this with a grub.cfg snippet. currently grub.cfg files is generated by sh scripts living under /etc/grub.d we are thinking about moving to doing this build-time though, since once we have bls there will be no more need to modify grub.cfg
Changing grub.cfg is not needed for this, though...or is it? Doesn't grub.cfg just point to the saved_entry var from grubenv for its current entry?
I'm guessing OSTree should change the entry from userspace, along with resetting the counter when doing the rollback. And when should the counter decrementaion happen? At boot, if boot_successful != 1? Or also from userspace? I think if we do it at boot time, we could in userspace do if [boot_counter=0 && boot_successful=0] then rpm-ostree rollback
boot_successful
if [boot_counter=0 && boot_successful=0] then rpm-ostree rollback
@jwrdegoede @jlebon Where should the logic to switch saved_entry live? grub.cfg syntax is quite bash like, I think you can probably do all this with a grub.cfg snippet. currently grub.cfg files is generated by sh scripts living under /etc/grub.d we are thinking about moving to doing this build-time though, since once we have bls there will be no more need to modify grub.cfg Changing grub.cfg is not needed for this, though...or is it?
Changing grub.cfg is not needed for this, though...or is it?
Even when you plan to reuse the saved_entry variable (or just set default) to fallback to the previous boot entry, you need some logic in grub.cfg to set to the previous entry in the case of a boot_successful=0.
default
grub.cfg
boot_successful=0
Doesn't grub.cfg just point to the saved_entry var from grubenv for its current entry?
Yes, default is set to saved_entry. But it also depends on other variables set on grubenv and /etc/default/grub when the grub.cfg is generated. You can take a look to its logic in /etc/grub.d/00_header.
grubenv
/etc/default/grub
/etc/grub.d/00_header
I'm guessing OSTree should change the entry from userspace, along with resetting the counter when doing the rollback.
Currently it doens't AFAICT, so in this case default is 0 which means that the first entry will be booted.
And when should the counter decrementaion happen? At boot, if boot_successful != 1? Or also from userspace?
The boot counter decrement should be done by grub before attempting to boot the current default entry, user-space should just reset that in the case of a successful boot.
I think if we do it at boot time, we could in userspace do if [boot_counter=0 && boot_successful=0] then rpm-ostree rollback
You can't do it from user-space if the default entry failed to boot (at least without user intervention, a user could choose to do it).
Another thing to take into account is that we should probably remove the failed entry, otherwise a ostree upgrade will leave the system with a known-to-not-boot entry and a new one that wasn't tested yet. IOW, the known-to-boot entry will be not be available anymore.
So we do if (boot_successful=0 && boot_counter=0) then saved_entry++ at boot time
if (boot_successful=0 && boot_counter=0) then saved_entry++
@wjt pointed me out that I was wrong on this. On deploy ostree doesn't use the last two deployments but the booted one + the new deploy, so the failed BLS won't be present in the new BLS entries directory for the latest deployment.
so the failed BLS won't be present in the new BLS entries directory for the latest deployment.
Right...I'm thinking any time ostree changes the bootloader configuration, the saved entry state should be reset and the first entry should be the default.
One thing that came out of a meeting today is that ostree should probably learn how to parse the grubenv data around boot counting, so we can render it in rpm-ostree status. It's really important to me that rpm-ostree status describes "truth" of system state. It'd be very confusing if e.g. when I reboot the first deployment isn't what I boot into because the try counter is zero for it because it failed.
rpm-ostree status
And following up on my previous comment; is grub responsible for resetting the grubenv somehow any time the bootloader configuration changes, or does ostree need to learn how to do that?
I think ostree would need to learn how to do that. i.e. if I rpm-ostree upgrade ostree needs to set up the grubenv appropriately before (or during) reboot. right?
ostree
rpm-ostree upgrade
Right, grub never changes grubenv itself, unless you tell it so in grub.cfg e.g. for the auto-hide-menu stuff landing in F29 I've added:
set boot_success=0 save_env boot_success
Which resets the boot_success variable to 0 on every boot, changes which only need to happen on an upgrade / update are probably best done from the upgrade scripts. But you could have some logic detecting an update has happened in grub.cfg (it is a sh-like script) and make grub do it there.
Doing it from the upgrade scripts seems better though.
I drew up a state machine diagram, with 2 boot attempts and 2 boot loader entries. Legend: s: boot_success c: boot_counter e: saved_entry
Note that the rollback edge is assumed to take place during boot in GRUB here, as opposed to in userspace by rpm-ostree (which AFAIKT rewrites the zero entry instead of incrementing it)
rollback
Edit: Note 2: boot_counter counts down here. Are there any reasons to have a second counter counting up?
<img alt="grub_boot_counter_states.png" src="/fedora-iot/issue/raw/files/99fed418e2454270c449247db84ed505b0d2ad3552ab9d605c0b99b4bbd25dd6-grub_boot_counter_states.png" />
Questions:
What has to happen in reset e? We want to move up the entry x (here 1) to 0 in the boot loader menu entry index.
reset e
When attempting to increment e during rollback (GRUB), how can be determined how many entries there are, ie. when to stop?
e
If we add a var in grubenv, e.g. boot_counter=0, up to what number can we increment it without needing to resize the block (which GRUB doesn't support)
boot_counter=0
What are the fs limitations for grubenv? Any at all? VFAT only?
i think that point is where we want to have rpm-ostree/ostree do some logic: i.e. perform rollback (not just boot old deployment) and fixup the grub env vars.
rpm-ostree
I think we only want to consider the0 and 1 entries. 1 is where we were at before the upgrade so it should boot since it booted before.
0
1
don't know. @jwrdegoede @javierm @pjones ?
same: @jwrdegoede @javierm @pjones ?
@lorbus - a few questions for you:
On 18-07-18 14:42, Dusty Mabe wrote:
If we add a var in grubenv, e.g. boot_counter=0, up to what number can we increment it without needing to resize the block (which GRUB doesn't support) don't know. @jwrdegoede @javierm @pjones ?
It gets stored as a text string, grubenv itself is 1024 bytes in size and typically has plenty of free space, I guess the limit will more be the size of the integer used then the grubenv size, although that could be a problem too if there are lots of variables in there.
What are the fs limitations for grubenv? Any at all? VFAT only? same: @jwrdegoede @javierm @pjones ?
ext2/3/4 and vfat should all be fine, I do not know about xfs, last time I checked GRUB's btrfs code did not support writing but that may have changed since.
@dustymabe diagram source is a LibreOffice Draw odg file (attached). I tried doing it with graphviz, which would've made making changes to it easier, but I couldn't get it to position the nodes where I wanted them and thus made it harder to grasp visually. <img alt="grub_boot_counter_states.odg" src="/fedora-iot/issue/raw/files/def44fff958cd09eb012a0307c66c6a0d7ad42d87eea16a4f943f32f25f09dc2-grub_boot_counter_states.odg" />
Can we safely assume we reach userspace on a failed boot, though? If that is the case we wouldn't even need to increment saved_entry, as ostree will rewrite 0th entry in rpm-ostree rollback. The current proposal wouldn't call rpm-ostree rollback at all and instead call some new logic from userspace to move entry 1 to 0 after the fact.
rpm-ostree rollback
Pros I see for this:
Can we safely assume we reach userspace on a failed boot, though?
i think i was talking about the case where we reach userspace after we've chosen entry 1 because entry 0 failed to boot. This is not the same as a rollback. We would still need to tell ostree to do a rollback I believe. @jlebon would be able to weigh in.
We discussed this in a meeting today, though for posterity:
This is not the same as a rollback. We would still need to tell ostree to do a rollback I believe.
That's correct, we'd need to change the bootloader order to make sure entry 1 was back to being the default again.
Metadata Update from @pbrobinson: - Issue assigned to lorbus - Issue tagged with: GSoC
We came up with a simplified state diagram for this. we might be able to not use saved_entry but boot_once instead:
boot_once
Legend: s: boot_success c: boot_counter
<img alt="lorbus_grub_boot_counter_states.png" src="/fedora-iot/issue/raw/files/8f9bdee7f5f2c9ce583befa095c9854d205b0d653a21a78963b23bccf0ae7974-lorbus_grub_boot_counter_states.png" />
Metadata Update from @lorbus: - Issue status updated to: Closed (was: Open)
Hi all,
I realize this is closed now, but I think the following systemd RFE: https://github.com/systemd/systemd/issues/9897 "RFE: system-boot-success target"
Is of interest for those involved in this, the purpose here is to offer a standard framework decoupling how boot-success is detected vs how it is signaled to the boot-loader, the idea being that success leads to reaching a new "system-boot-success" target in systemd and that once that target is reached a boot-loader specific oneshot service will be run which communicates this to the bootloader.
Please read the above systemd RFE and add any comments you may have there.
hey @jwrdegoede, thanks. @lorbus, do you mind taking a look?
Login to comment on this ticket.