reboot: use kexec where possible

This doesn’t work on the Raspberry Pi yet (due to missing kexec_file_load), but
I tested it in qemu.
This commit is contained in:
Michael Stapelberg 2018-06-17 08:54:44 +02:00
parent 91da7026f8
commit 4f0e427025
3 changed files with 77 additions and 2 deletions

9
reboot.go Normal file
View File

@ -0,0 +1,9 @@
// +build !amd64
package gokrazy
import "golang.org/x/sys/unix"
func reboot() error {
return unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
}

65
reboot_amd64.go Normal file
View File

@ -0,0 +1,65 @@
package gokrazy
import (
"io/ioutil"
"log"
"os"
"path/filepath"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
// TODO: get these constants into sys/unix
const (
KEXEC_ARCH_DEFAULT = (0 << 16)
KEXEC_FILE_NO_INITRAMFS = 0x00000004
)
func kexecReboot() error {
tmpdir, err := ioutil.TempDir("", "kexec")
if err != nil {
return err
}
if err := syscall.Mount(mustFindRootDevice()+"1", tmpdir, "vfat", 0, ""); err != nil {
return err
}
kernel, err := os.Open(filepath.Join(tmpdir, "vmlinuz"))
if err != nil {
return err
}
defer kernel.Close()
cmdline, err := ioutil.ReadFile("/proc/cmdline")
if err != nil {
return err
}
rep := rootRe.ReplaceAllLiteral(cmdline, []byte("root="+mustFindRootDevice()+inactiveRootPartition))
// NUL-terminate cmdline
cmdlinebuf := make([]byte, len(rep)+1)
copy(cmdlinebuf, rep)
_, _, errno := unix.Syscall6(
unix.SYS_KEXEC_FILE_LOAD,
uintptr(kernel.Fd()), // kernel_fd
0, // initrd_fd
uintptr(len(cmdline)+1), // cmdline_len
uintptr(unsafe.Pointer(&cmdlinebuf[0])), // cmdline
KEXEC_ARCH_DEFAULT|KEXEC_FILE_NO_INITRAMFS, // flags
0)
if errno != 0 {
// errno is syscall.ENOSYS on kernels without CONFIG_KEXEC_FILE_LOAD=y
return errno
}
return unix.Reboot(unix.LINUX_REBOOT_CMD_KEXEC)
}
func reboot() error {
if err := kexecReboot(); err != nil {
log.Printf("kexec reboot failed: %v", err)
return unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
}
return nil
}

View File

@ -21,6 +21,8 @@ import (
var (
rootRe = regexp.MustCompile(`root=/dev/(?:mmcblk0p|sda)([2-3])`)
rootDeviceRe = regexp.MustCompile(`root=(/dev/(?:mmcblk0p|sda))`)
inactiveRootPartition string
)
// mustFindRootDevice returns the device from which gokrazy was booted. It is
@ -136,7 +138,6 @@ func initUpdate() error {
}
rootPartition := matches[1]
var inactiveRootPartition string
switch rootPartition {
case "2":
inactiveRootPartition = "3"
@ -176,7 +177,7 @@ func initUpdate() error {
log.Printf("unmounting /perm failed: %v", err)
}
if err := unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART); err != nil {
if err := reboot(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}()