| 1 | |
| 2 |
|
| 3 | |
| 4 | * Copyright (c) 2025, Google LLC.
|
| 5 | * Pasha Tatashin <[email protected]>
|
| 6 |
|
| 7 |
|
| 8 | |
| 9 | * DOC: Live Update Orchestrator (LUO)
|
| 10 | *
|
| 11 | * Live Update is a specialized, kexec-based reboot process that allows a
|
| 12 | * running kernel to be updated from one version to another while preserving
|
| 13 | * the state of selected resources and keeping designated hardware devices
|
| 14 | * operational. For these devices, DMA activity may continue throughout the
|
| 15 | * kernel transition.
|
| 16 | *
|
| 17 | * While the primary use case driving this work is supporting live updates of
|
| 18 | * the Linux kernel when it is used as a hypervisor in cloud environments, the
|
| 19 | * LUO framework itself is designed to be workload-agnostic. Live Update
|
| 20 | * facilitates a full kernel version upgrade for any type of system.
|
| 21 | *
|
| 22 | * For example, a non-hypervisor system running an in-memory cache like
|
| 23 | * memcached with many gigabytes of data can use LUO. The userspace service
|
| 24 | * can place its cache into a memfd, have its state preserved by LUO, and
|
| 25 | * restore it immediately after the kernel kexec.
|
| 26 | *
|
| 27 | * Whether the system is running virtual machines, containers, a
|
| 28 | * high-performance database, or networking services, LUO's primary goal is to
|
| 29 | * enable a full kernel update by preserving critical userspace state and
|
| 30 | * keeping essential devices operational.
|
| 31 | *
|
| 32 | * The core of LUO is a mechanism that tracks the progress of a live update,
|
| 33 | * along with a callback API that allows other kernel subsystems to participate
|
| 34 | * in the process. Example subsystems that can hook into LUO include: kvm,
|
| 35 | * iommu, interrupts, vfio, participating filesystems, and memory management.
|
| 36 | *
|
| 37 | * LUO uses Kexec Handover to transfer memory state from the current kernel to
|
| 38 | * the next kernel. For more details see Documentation/core-api/kho/index.rst.
|
| 39 | *
|
| 40 | * .. note::
|
| 41 | * To enable LUO, boot the kernel with the ``liveupdate=on`` command line
|
| 42 | * parameter.
|
| 43 |
|
| 44 |
|
| 45 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 46 |
|
| 47 | #include <linux/atomic.h>
|
| 48 | #include <linux/errno.h>
|
| 49 | #include <linux/file.h>
|
| 50 | #include <linux/fs.h>
|
| 51 | #include <linux/init.h>
|
| 52 | #include <linux/io.h>
|
| 53 | #include <linux/kernel.h>
|
| 54 | #include <linux/kexec_handover.h>
|
| 55 | #include <linux/kho/abi/luo.h>
|
| 56 | #include <linux/kobject.h>
|
| 57 | #include <linux/liveupdate.h>
|
| 58 | #include <linux/miscdevice.h>
|
| 59 | #include <linux/mm.h>
|
| 60 | #include <linux/rwsem.h>
|
| 61 | #include <linux/sizes.h>
|
| 62 | #include <linux/string.h>
|
| 63 |
|
| 64 | #include "kexec_handover_internal.h"
|
| 65 | #include "luo_internal.h"
|
| 66 |
|
| 67 | static struct {
|
| 68 | bool enabled;
|
| 69 | struct luo_ser *luo_ser_out;
|
| 70 | u64 liveupdate_num;
|
| 71 | } luo_global;
|
| 72 |
|
| 73 | |
| 74 | * luo_register_rwlock - Protects registration of file handlers and FLBs.
|
| 75 |
|
| 76 | DECLARE_RWSEM(luo_register_rwlock);
|
| 77 |
|
| 78 | static int __init early_liveupdate_param(char *buf)
|
| 79 | {
|
| 80 | return kstrtobool(buf, &luo_global.enabled);
|
| 81 | }
|
| 82 | early_param("liveupdate", early_liveupdate_param);
|
| 83 |
|
| 84 | static int __init luo_early_startup(void)
|
| 85 | {
|
| 86 | phys_addr_t luo_ser_phys;
|
| 87 | struct luo_ser *luo_ser;
|
| 88 | size_t len;
|
| 89 | int err;
|
| 90 |
|
| 91 | if (!kho_is_enabled()) {
|
| 92 | if (liveupdate_enabled())
|
| 93 | pr_warn("Disabling liveupdate because KHO is disabled\n");
|
| 94 | luo_global.enabled = false;
|
| 95 | return 0;
|
| 96 | }
|
| 97 |
|
| 98 |
|
| 99 | err = kho_retrieve_subtree(LUO_KHO_ENTRY_NAME, &luo_ser_phys, &len);
|
| 100 | if (err) {
|
| 101 | if (err != -ENOENT) {
|
| 102 | pr_err("failed to retrieve LUO state '%s' from KHO: %pe\n",
|
| 103 | LUO_KHO_ENTRY_NAME, ERR_PTR(err));
|
| 104 | return err;
|
| 105 | }
|
| 106 |
|
| 107 | return 0;
|
| 108 | }
|
| 109 |
|
| 110 | if (len < sizeof(*luo_ser)) {
|
| 111 | pr_err("LUO state is too small (%zu < %zu)\n", len, sizeof(*luo_ser));
|
| 112 | return -EINVAL;
|
| 113 | }
|
| 114 |
|
| 115 | luo_ser = phys_to_virt(luo_ser_phys);
|
| 116 | if (strncmp(luo_ser->compatible, LUO_ABI_COMPATIBLE, LUO_ABI_COMPAT_LEN)) {
|
| 117 | pr_err("LUO state is incompatible with '%s'\n", LUO_ABI_COMPATIBLE);
|
| 118 | return -EINVAL;
|
| 119 | }
|
| 120 |
|
| 121 | luo_global.liveupdate_num = luo_ser->liveupdate_num;
|
| 122 | pr_info("Retrieved live update data, liveupdate number: %lld\n",
|
| 123 | luo_global.liveupdate_num);
|
| 124 |
|
| 125 | err = luo_session_setup_incoming(luo_ser->sessions_pa);
|
| 126 | if (err)
|
| 127 | goto out_free_ser;
|
| 128 |
|
| 129 | luo_flb_setup_incoming(luo_ser->flbs_pa);
|
| 130 |
|
| 131 | err = 0;
|
| 132 |
|
| 133 | out_free_ser:
|
| 134 | kho_restore_free(luo_ser);
|
| 135 | return err;
|
| 136 | }
|
| 137 |
|
| 138 | static int __init liveupdate_early_init(void)
|
| 139 | {
|
| 140 | int err;
|
| 141 |
|
| 142 | err = luo_early_startup();
|
| 143 | if (err) {
|
| 144 | luo_global.enabled = false;
|
| 145 | luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
|
| 146 | ERR_PTR(err));
|
| 147 | }
|
| 148 |
|
| 149 | return err;
|
| 150 | }
|
| 151 | early_initcall(liveupdate_early_init);
|
| 152 |
|
| 153 |
|
| 154 | static int __init luo_state_setup(void)
|
| 155 | {
|
| 156 | struct luo_ser *luo_ser;
|
| 157 | int err;
|
| 158 |
|
| 159 | luo_ser = kho_alloc_preserve(sizeof(*luo_ser));
|
| 160 | if (IS_ERR(luo_ser)) {
|
| 161 | pr_err("failed to allocate/preserve LUO state memory\n");
|
| 162 | return PTR_ERR(luo_ser);
|
| 163 | }
|
| 164 |
|
| 165 | strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible));
|
| 166 | luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
|
| 167 |
|
| 168 | luo_session_setup_outgoing(&luo_ser->sessions_pa);
|
| 169 |
|
| 170 | err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
|
| 171 | if (err)
|
| 172 | goto exit_free_luo_ser;
|
| 173 |
|
| 174 | err = kho_add_subtree(LUO_KHO_ENTRY_NAME, luo_ser, sizeof(*luo_ser));
|
| 175 | if (err)
|
| 176 | goto exit_free_luo_ser;
|
| 177 |
|
| 178 | luo_global.luo_ser_out = luo_ser;
|
| 179 |
|
| 180 | return 0;
|
| 181 |
|
| 182 | exit_free_luo_ser:
|
| 183 | kho_unpreserve_free(luo_ser);
|
| 184 | pr_err("failed to prepare LUO state: %d\n", err);
|
| 185 |
|
| 186 | return err;
|
| 187 | }
|
| 188 |
|
| 189 | |
| 190 | * late initcall because it initializes the outgoing tree that is needed only
|
| 191 | * once userspace starts using /dev/liveupdate.
|
| 192 |
|
| 193 | static int __init luo_late_startup(void)
|
| 194 | {
|
| 195 | int err;
|
| 196 |
|
| 197 | if (!liveupdate_enabled())
|
| 198 | return 0;
|
| 199 |
|
| 200 | err = luo_state_setup();
|
| 201 | if (err)
|
| 202 | luo_global.enabled = false;
|
| 203 |
|
| 204 | return err;
|
| 205 | }
|
| 206 | late_initcall(luo_late_startup);
|
| 207 |
|
| 208 |
|
| 209 |
|
| 210 | |
| 211 | * liveupdate_reboot() - Kernel reboot notifier for live update final
|
| 212 | * serialization.
|
| 213 | *
|
| 214 | * This function is invoked directly from the reboot() syscall pathway
|
| 215 | * if kexec is in progress.
|
| 216 | *
|
| 217 | * If any callback fails, this function aborts KHO, undoes the freeze()
|
| 218 | * callbacks, and returns an error.
|
| 219 |
|
| 220 | int liveupdate_reboot(void)
|
| 221 | {
|
| 222 | int err;
|
| 223 |
|
| 224 | if (!liveupdate_enabled())
|
| 225 | return 0;
|
| 226 |
|
| 227 | err = luo_session_serialize();
|
| 228 | if (err)
|
| 229 | return err;
|
| 230 |
|
| 231 | luo_flb_serialize();
|
| 232 |
|
| 233 | return 0;
|
| 234 | }
|
| 235 |
|
| 236 | |
| 237 | * liveupdate_enabled - Check if the live update feature is enabled.
|
| 238 | *
|
| 239 | * This function returns the state of the live update feature flag, which
|
| 240 | * can be controlled via the ``liveupdate`` kernel command-line parameter.
|
| 241 | *
|
| 242 | * @return true if live update is enabled, false otherwise.
|
| 243 |
|
| 244 | bool liveupdate_enabled(void)
|
| 245 | {
|
| 246 | return luo_global.enabled;
|
| 247 | }
|
| 248 |
|
| 249 | |
| 250 | * DOC: LUO ioctl Interface
|
| 251 | *
|
| 252 | * The IOCTL user-space control interface for the LUO subsystem.
|
| 253 | * It registers a character device, typically found at ``/dev/liveupdate``,
|
| 254 | * which allows a userspace agent to manage the LUO state machine and its
|
| 255 | * associated resources, such as preservable file descriptors.
|
| 256 | *
|
| 257 | * To ensure that the state machine is controlled by a single entity, access
|
| 258 | * to this device is exclusive: only one process is permitted to have
|
| 259 | * ``/dev/liveupdate`` open at any given time. Subsequent open attempts will
|
| 260 | * fail with -EBUSY until the first process closes its file descriptor.
|
| 261 | * This singleton model simplifies state management by preventing conflicting
|
| 262 | * commands from multiple userspace agents.
|
| 263 |
|
| 264 |
|
| 265 | struct luo_device_state {
|
| 266 | struct miscdevice miscdev;
|
| 267 | atomic_t in_use;
|
| 268 | };
|
| 269 |
|
| 270 | static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
|
| 271 | {
|
| 272 | struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
|
| 273 | struct file *file;
|
| 274 | int err;
|
| 275 |
|
| 276 | argp->fd = get_unused_fd_flags(O_CLOEXEC);
|
| 277 | if (argp->fd < 0)
|
| 278 | return argp->fd;
|
| 279 |
|
| 280 | err = luo_session_create(argp->name, &file);
|
| 281 | if (err)
|
| 282 | goto err_put_fd;
|
| 283 |
|
| 284 | err = luo_ucmd_respond(ucmd, sizeof(*argp));
|
| 285 | if (err)
|
| 286 | goto err_put_file;
|
| 287 |
|
| 288 | fd_install(argp->fd, file);
|
| 289 |
|
| 290 | return 0;
|
| 291 |
|
| 292 | err_put_file:
|
| 293 | fput(file);
|
| 294 | err_put_fd:
|
| 295 | put_unused_fd(argp->fd);
|
| 296 |
|
| 297 | return err;
|
| 298 | }
|
| 299 |
|
| 300 | static int luo_ioctl_retrieve_session(struct luo_ucmd *ucmd)
|
| 301 | {
|
| 302 | struct liveupdate_ioctl_retrieve_session *argp = ucmd->cmd;
|
| 303 | struct file *file;
|
| 304 | int err;
|
| 305 |
|
| 306 | argp->fd = get_unused_fd_flags(O_CLOEXEC);
|
| 307 | if (argp->fd < 0)
|
| 308 | return argp->fd;
|
| 309 |
|
| 310 | err = luo_session_retrieve(argp->name, &file);
|
| 311 | if (err < 0)
|
| 312 | goto err_put_fd;
|
| 313 |
|
| 314 | err = luo_ucmd_respond(ucmd, sizeof(*argp));
|
| 315 | if (err)
|
| 316 | goto err_put_file;
|
| 317 |
|
| 318 | fd_install(argp->fd, file);
|
| 319 |
|
| 320 | return 0;
|
| 321 |
|
| 322 | err_put_file:
|
| 323 | fput(file);
|
| 324 | err_put_fd:
|
| 325 | put_unused_fd(argp->fd);
|
| 326 |
|
| 327 | return err;
|
| 328 | }
|
| 329 |
|
| 330 | static int luo_open(struct inode *inodep, struct file *filep)
|
| 331 | {
|
| 332 | struct luo_device_state *ldev = container_of(filep->private_data,
|
| 333 | struct luo_device_state,
|
| 334 | miscdev);
|
| 335 |
|
| 336 | if (atomic_cmpxchg(&ldev->in_use, 0, 1))
|
| 337 | return -EBUSY;
|
| 338 |
|
| 339 |
|
| 340 | if (luo_session_deserialize()) {
|
| 341 | atomic_set(&ldev->in_use, 0);
|
| 342 | return -EIO;
|
| 343 | }
|
| 344 |
|
| 345 | return 0;
|
| 346 | }
|
| 347 |
|
| 348 | static int luo_release(struct inode *inodep, struct file *filep)
|
| 349 | {
|
| 350 | struct luo_device_state *ldev = container_of(filep->private_data,
|
| 351 | struct luo_device_state,
|
| 352 | miscdev);
|
| 353 | atomic_set(&ldev->in_use, 0);
|
| 354 |
|
| 355 | return 0;
|
| 356 | }
|
| 357 |
|
| 358 | union ucmd_buffer {
|
| 359 | struct liveupdate_ioctl_create_session create;
|
| 360 | struct liveupdate_ioctl_retrieve_session retrieve;
|
| 361 | };
|
| 362 |
|
| 363 | struct luo_ioctl_op {
|
| 364 | unsigned int size;
|
| 365 | unsigned int min_size;
|
| 366 | unsigned int ioctl_num;
|
| 367 | int (*execute)(struct luo_ucmd *ucmd);
|
| 368 | };
|
| 369 |
|
| 370 | #define IOCTL_OP(_ioctl, _fn, _struct, _last) \
|
| 371 | [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_BASE] = { \
|
| 372 | .size = sizeof(_struct) + \
|
| 373 | BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \
|
| 374 | sizeof(_struct)), \
|
| 375 | .min_size = offsetofend(_struct, _last), \
|
| 376 | .ioctl_num = _ioctl, \
|
| 377 | .execute = _fn, \
|
| 378 | }
|
| 379 |
|
| 380 | static const struct luo_ioctl_op luo_ioctl_ops[] = {
|
| 381 | IOCTL_OP(LIVEUPDATE_IOCTL_CREATE_SESSION, luo_ioctl_create_session,
|
| 382 | struct liveupdate_ioctl_create_session, name),
|
| 383 | IOCTL_OP(LIVEUPDATE_IOCTL_RETRIEVE_SESSION, luo_ioctl_retrieve_session,
|
| 384 | struct liveupdate_ioctl_retrieve_session, name),
|
| 385 | };
|
| 386 |
|
| 387 | static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
| 388 | {
|
| 389 | const struct luo_ioctl_op *op;
|
| 390 | struct luo_ucmd ucmd = {};
|
| 391 | union ucmd_buffer buf;
|
| 392 | unsigned int nr;
|
| 393 | int err;
|
| 394 |
|
| 395 | nr = _IOC_NR(cmd);
|
| 396 | if (nr - LIVEUPDATE_CMD_BASE >= ARRAY_SIZE(luo_ioctl_ops))
|
| 397 | return -EINVAL;
|
| 398 |
|
| 399 | ucmd.ubuffer = (void __user *)arg;
|
| 400 | err = get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer);
|
| 401 | if (err)
|
| 402 | return err;
|
| 403 |
|
| 404 | op = &luo_ioctl_ops[nr - LIVEUPDATE_CMD_BASE];
|
| 405 | if (op->ioctl_num != cmd)
|
| 406 | return -ENOIOCTLCMD;
|
| 407 | if (ucmd.user_size < op->min_size)
|
| 408 | return -EINVAL;
|
| 409 |
|
| 410 | ucmd.cmd = &buf;
|
| 411 | err = copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer,
|
| 412 | ucmd.user_size);
|
| 413 | if (err)
|
| 414 | return err;
|
| 415 |
|
| 416 | return op->execute(&ucmd);
|
| 417 | }
|
| 418 |
|
| 419 | static const struct file_operations luo_fops = {
|
| 420 | .owner = THIS_MODULE,
|
| 421 | .open = luo_open,
|
| 422 | .release = luo_release,
|
| 423 | .unlocked_ioctl = luo_ioctl,
|
| 424 | };
|
| 425 |
|
| 426 | static struct luo_device_state luo_dev = {
|
| 427 | .miscdev = {
|
| 428 | .minor = MISC_DYNAMIC_MINOR,
|
| 429 | .name = "liveupdate",
|
| 430 | .fops = &luo_fops,
|
| 431 | },
|
| 432 | .in_use = ATOMIC_INIT(0),
|
| 433 | };
|
| 434 |
|
| 435 | static int __init liveupdate_ioctl_init(void)
|
| 436 | {
|
| 437 | if (!liveupdate_enabled())
|
| 438 | return 0;
|
| 439 |
|
| 440 | return misc_register(&luo_dev.miscdev);
|
| 441 | }
|
| 442 | late_initcall(liveupdate_ioctl_init);
|