mirror of
https://github.com/ostreedev/ostree.git
synced 2025-01-03 05:18:24 +03:00
bin/admin-upgrade: add kexec support
Adds a new `--kexec` flag to `ostree admin upgrade` which will cause the deployment to be loaded into kexec after the upgrade completes. It is particularly useful in conjunction with the `--reboot` flag to perform a reboot into the new deployment without waiting for the (often slow) firmware initialization to take place. (And in my case, allows me to avoid a normal reboot, which can be unreliable on my hardware). After an image has been loaded (using the `kexec_file_load` syscall), the `systemctl-reboot` command (which is called when the existing `-r` flag is included) will trigger a kexec on the loaded image rather than a normal reboot. From `systemctl(1)`: If a new kernel has been loaded via kexec --load, a kexec will be performed instead of a reboot, unless "SYSTEMCTL_SKIP_AUTO_KEXEC=1" has been set. If a new root file system has been set up on "/run/nextroot/", a soft-reboot will be performed instead of a reboot, unless "SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT=1" has been set. A good in-depth technical explanation of kexec can be found here: https://web.archive.org/web/20090505132901/http://www.ibm.com/developerworks/linux/library/l-kexec.html My implementation uses the `kexec_file_load` syscall rather than the older `kexec_load` syscall, which allows the kernel to verify the signatures of the new kernel. It is supported on Linux 3.17 and newer. I assume this probably won't be an issue, but if it is, it's not that hard to put a preprocessor directive around the kexec stuff to disable it for older kernels. Even RHEL is new enough now to not be an issue :) Closes: #435
This commit is contained in:
parent
64a38aec8c
commit
d5bd0e0035
@ -65,6 +65,22 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
<title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--reboot</option>,<option>-r</option></term>
|
||||
|
||||
<listitem><para>
|
||||
Reboot after a successful switch.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--kexec</option>,<option>-k</option></term>
|
||||
|
||||
<listitem><para>
|
||||
Load new deployment into kexec after a successful switch.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--os</option>="STATEROOT"</term>
|
||||
|
||||
|
@ -112,6 +112,14 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--kexec</option>,<option>-k</option></term>
|
||||
|
||||
<listitem><para>
|
||||
Load new deployment into kexec after a successful upgrade.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--allow-downgrade</option></term>
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <linux/kexec.h>
|
||||
|
||||
#ifdef HAVE_LIBMOUNT
|
||||
#include <libmount.h>
|
||||
@ -4265,3 +4266,63 @@ ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, OstreeDeployment *de
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_deployment_kexec_load
|
||||
* @self: Sysroot
|
||||
* @deployment: Deployment to prepare a kexec for
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Prepare the specified deployment for a kexec.
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_deployment_kexec_load (OstreeSysroot *self, OstreeDeployment *deployment,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
#ifdef SYS_kexec_file_load
|
||||
GLNX_AUTO_PREFIX_ERROR ("Loading kernel into kexec", error);
|
||||
OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
|
||||
const char *kargs = ostree_bootconfig_parser_get(bootconfig, "options");
|
||||
g_autofree char *deployment_dirpath = ostree_sysroot_get_deployment_dirpath (self, deployment);
|
||||
glnx_autofd int deployment_dfd = -1;
|
||||
if (!glnx_opendirat (self->sysroot_fd, deployment_dirpath, FALSE, &deployment_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
/* Find the kernel/initramfs in the tree */
|
||||
g_autoptr (OstreeKernelLayout) kernel_layout = NULL;
|
||||
if (!get_kernel_from_tree (self, deployment_dfd, &kernel_layout, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
unsigned long flags = 0;
|
||||
glnx_autofd int kernel_fd = -1;
|
||||
glnx_autofd int initrd_fd = -1;
|
||||
|
||||
if (!glnx_openat_rdonly (kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
|
||||
TRUE, &kernel_fd, error))
|
||||
return FALSE;
|
||||
|
||||
/* initramfs is optional */
|
||||
if (kernel_layout->initramfs_srcpath)
|
||||
{
|
||||
if (!glnx_openat_rdonly (kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath,
|
||||
TRUE, &initrd_fd, error))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flags |= KEXEC_FILE_NO_INITRAMFS;
|
||||
}
|
||||
|
||||
if (syscall (SYS_kexec_file_load, kernel_fd, initrd_fd, strlen (kargs) + 1, kargs, flags))
|
||||
return glnx_throw_errno_prefix(error, "kexec_file_load");
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
"This version of ostree is not compiled with kexec support");
|
||||
return FALSE;
|
||||
#endif // SYS_kexec_file_load
|
||||
}
|
||||
|
@ -601,6 +601,7 @@ ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, GCancellable *cance
|
||||
/* Experimental flag to enable staging */
|
||||
gboolean stage = (self->flags & OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE) > 0
|
||||
|| getenv ("OSTREE_EX_STAGE_DEPLOYMENTS") != NULL;
|
||||
OstreeSysrootSimpleWriteDeploymentFlags write_deployment_flags = OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE;
|
||||
if (stage)
|
||||
{
|
||||
if (!ostree_sysroot_stage_tree (self->sysroot, self->osname, self->new_revision, self->origin,
|
||||
@ -616,7 +617,12 @@ ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, GCancellable *cance
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_sysroot_simple_write_deployment (self->sysroot, self->osname, new_deployment,
|
||||
self->merge_deployment, 0, cancellable, error))
|
||||
self->merge_deployment, write_deployment_flags, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
|
||||
if ((self->flags & OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC) > 0)
|
||||
if (!ostree_sysroot_deployment_kexec_load(self->sysroot, new_deployment, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -635,6 +641,8 @@ ostree_sysroot_upgrader_flags_get_type (void)
|
||||
"OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED", "ignore-unconfigured" },
|
||||
{ OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE, "OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE",
|
||||
"stage" },
|
||||
{ OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC, "OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC",
|
||||
"kexec" },
|
||||
{ 0, NULL, NULL } };
|
||||
GType g_define_type_id
|
||||
= g_flags_register_static (g_intern_static_string ("OstreeSysrootUpgraderFlags"), values);
|
||||
|
@ -44,6 +44,7 @@ typedef enum
|
||||
OSTREE_SYSROOT_UPGRADER_FLAGS_NONE = (1 << 0),
|
||||
OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED = (1 << 1),
|
||||
OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE = (1 << 2),
|
||||
OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC = (1 << 3),
|
||||
} OstreeSysrootUpgraderFlags;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
|
@ -268,4 +268,9 @@ gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, const c
|
||||
OstreeSysrootSimpleWriteDeploymentFlags flags,
|
||||
GCancellable *cancellable, GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean
|
||||
ostree_sysroot_deployment_kexec_load (OstreeSysroot *self, OstreeDeployment *deployment,
|
||||
GCancellable *cancellable, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -29,10 +29,12 @@
|
||||
#include <unistd.h>
|
||||
|
||||
static gboolean opt_reboot;
|
||||
static gboolean opt_kexec;
|
||||
static char *opt_osname;
|
||||
|
||||
static GOptionEntry options[]
|
||||
= { { "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Reboot after switching trees", NULL },
|
||||
{ "kexec", 'k', 0, G_OPTION_ARG_NONE, &opt_kexec, "Stage new kernel in kexec", NULL },
|
||||
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname,
|
||||
"Use a different operating system root than the current one", "OSNAME" },
|
||||
{ NULL } };
|
||||
@ -56,8 +58,12 @@ ot_admin_builtin_switch (int argc, char **argv, OstreeCommandInvocation *invocat
|
||||
|
||||
const char *new_provided_refspec = argv[1];
|
||||
|
||||
OstreeSysrootUpgraderFlags flags = OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED;
|
||||
if (opt_kexec)
|
||||
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC;
|
||||
|
||||
g_autoptr (OstreeSysrootUpgrader) upgrader = ostree_sysroot_upgrader_new_for_os_with_flags (
|
||||
sysroot, opt_osname, OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED, cancellable, error);
|
||||
sysroot, opt_osname, flags, cancellable, error);
|
||||
if (!upgrader)
|
||||
return FALSE;
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
static gboolean opt_reboot;
|
||||
static gboolean opt_kexec;
|
||||
static gboolean opt_allow_downgrade;
|
||||
static gboolean opt_pull_only;
|
||||
static gboolean opt_deploy_only;
|
||||
@ -42,6 +43,7 @@ static GOptionEntry options[] = {
|
||||
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname,
|
||||
"Use a different operating system root than the current one", "OSNAME" },
|
||||
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Reboot after a successful upgrade", NULL },
|
||||
{ "kexec", 'k', 0, G_OPTION_ARG_NONE, &opt_kexec, "Stage new kernel in kexec", NULL },
|
||||
{ "allow-downgrade", 0, 0, G_OPTION_ARG_NONE, &opt_allow_downgrade,
|
||||
"Permit deployment of chronologically older trees", NULL },
|
||||
{ "override-commit", 0, 0, G_OPTION_ARG_STRING, &opt_override_commit,
|
||||
@ -72,16 +74,18 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeCommandInvocation *invoca
|
||||
"Cannot simultaneously specify --pull-only and --deploy-only");
|
||||
return FALSE;
|
||||
}
|
||||
else if (opt_pull_only && opt_reboot)
|
||||
else if (opt_pull_only && (opt_reboot || opt_kexec))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Cannot simultaneously specify --pull-only and --reboot");
|
||||
"Cannot simultaneously specify --pull-only and --reboot or --kexec");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
OstreeSysrootUpgraderFlags flags = 0;
|
||||
if (opt_stage)
|
||||
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE;
|
||||
if (opt_kexec)
|
||||
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC;
|
||||
|
||||
g_autoptr (OstreeSysrootUpgrader) upgrader = ostree_sysroot_upgrader_new_for_os_with_flags (
|
||||
sysroot, opt_osname, flags, cancellable, error);
|
||||
|
Loading…
Reference in New Issue
Block a user