From b9abb301d0911e33747e59bb61bb9858f0cb4a77 Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Thu, 5 Nov 2020 11:03:21 +0100 Subject: linux-boot: Resume from hibernation. * gnu/build/linux-boot.scm (resume-if-hibernated): New procedure. (boot-system): Call it. --- gnu/build/linux-boot.scm | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm index 32e3536039..bfaac9ec1f 100644 --- a/gnu/build/linux-boot.scm +++ b/gnu/build/linux-boot.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès +;;; Copyright © 2016, 2017, 2019, 2020 Tobias Geerinckx-Rice ;;; Copyright © 2017 Mathieu Othacehe ;;; Copyright © 2019 Guillaume Le Vaillant ;;; @@ -110,6 +111,58 @@ OPTION doesn't appear in ARGUMENTS." (substring arg (+ 1 (string-index arg #\=))))) arguments))) +(define (resume-if-hibernated device) + "Resume from hibernation if possible. This is safe ONLY if no on-disk file +systems have been mounted; calling it later risks severe file system corruption! +See in the kernel source directory. This is the +caller's responsibility, as is catching exceptions if resumption was supposed to +happen but didn't. + +Resume only from DEVICE if it's a string. If it's #f, use the kernel's default +hibernation device (CONFIG_PM_STD_PARTITION). Never return if resumption +succeeds. Return nothing otherwise. The kernel logs any details to dmesg." + + (define (string->major:minor string) + "Return a string with MAJOR:MINOR numbers of the device specified by STRING" + + ;; The "resume=" kernel command-line option always provides a string, which + ;; can represent a device, a UUID, or a label. Check for all three. + (let* ((spec (cond ((string-prefix? "/" string) string) + ((uuid string) => identity) + (else (file-system-label string)))) + ;; XXX The kernel's swsusp_resume_can_resume() waits if ‘resumewait’ + ;; is found on the command line; our canonicalize-device-spec gives + ;; up after 20 seconds. We could emulate the former by looping… + (device (canonicalize-device-spec spec)) + (rdev (stat:rdev (stat device))) + ;; For backwards compatibility, device numbering is a baroque affair. + ;; This is the full 64-bit scheme used by glibc's . + (major (logior (ash (logand #x00000000000fff00 rdev) -8) + (ash (logand #xfffff00000000000 rdev) -32))) + (minor (logior (logand #x00000000000000ff rdev) + (ash (logand #x00000ffffff00000 rdev) -12)))) + (format #f "~a:~a" major minor))) + + ;; Write the resume DEVICE to this magic file, using the MAJOR:MINOR device + ;; numbers if possible. The kernel will immediately try to resume from it. + (let ((resume "/sys/power/resume")) + (when (file-exists? resume) ; this kernel supports hibernation + ;; Honour the kernel's default device (only) if none other was given. + (let ((major:minor (if device + (or (false-if-exception (string->major:minor + device)) + ;; We can't parse it. Maybe the kernel can. + device) + (let ((default (call-with-input-file resume + read-line))) + ;; Don't waste time echoing 0:0 to /sys. + (if (string=? "0:0" default) + #f + default))))) + (when major:minor + (call-with-output-file resume ; may throw an ‘Invalid argument’ + (cut display major:minor <>))))))) ; may never return + (define* (make-disk-device-nodes base major #:optional (minor 0)) "Make the block device nodes around BASE (something like \"/root/dev/sda\") with the given MAJOR number, starting with MINOR." @@ -507,6 +560,12 @@ upon error." (load-linux-modules-from-directory linux-modules linux-module-directory) + (unless (member "noresume" args) + ;; Try to resume immediately after loading (storage) modules + ;; but before any on-disk file systems have been mounted. + (false-if-exception ; failure is not fatal + (resume-if-hibernated (find-long-option "resume" args)))) + (when keymap-file (let ((status (system* "loadkeys" keymap-file))) (unless (zero? status) -- cgit v1.2.3