From nobody Tue Dec 2 01:08:01 2025 Received: from mail-yx1-f54.google.com (mail-yx1-f54.google.com [74.125.224.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 647E72D5C61 for ; Sat, 22 Nov 2025 22:24:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763850250; cv=none; b=mymoHB7ZPYbrA4Mw+C5EsNCYeuxrQb4zN/vrje38vuNsCfeXNsNNkRqBmaUKOI3BvOgPHY47NYbVFIssTtKv7oyNfGDJ0QDmokfiENz4ZCy13zBEPRrXgKfsaokg9YZTEKWx5rX3zPpOFs/tz5d+EDYOuGjfKPQKE5+pIS/nzLk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763850250; c=relaxed/simple; bh=VdsRKJOPMGdFTmpnYFOjezgc7KmhFSw5nB5qgGoDEP4=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PZoy+8kDTgck7sxVwJ5riBfHyFF1jryBHmQiN2y39ZpiPA3xxm8g7jl/GHe9eclZJWvupZoAjfre3WGaBaIttxXIUTljrhj8VyiQbwxY50XCPPLKTfjQl745EqKqEUnuOz89BJ/LMBCXg9lbH8e2jr/03/6ygilYSQEs+S1GS1k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=dWjh9IVJ; arc=none smtp.client-ip=74.125.224.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="dWjh9IVJ" Received: by mail-yx1-f54.google.com with SMTP id 956f58d0204a3-640c857ce02so2962933d50.0 for ; Sat, 22 Nov 2025 14:24:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1763850246; x=1764455046; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=g/BoEmiu9zsrdP23VvEElLAx2sbpX8ex9ZCQjmSfNmg=; b=dWjh9IVJyy0qExkUBZl4x9+J5wzJ//lZEVnkFkFTv5nlafxNpMKt0qhqd8zqbH/zAl DDQwth40RLf0GmzyUOseeo4wE0Aik3OfPfW2DbJ7yVdUSvO9W3cCSdCbsghjnTWEWSR+ 5Zjap3PSPt/h4ObAmLlT34PU+cRPA3tKxyDuo/iUeEhjGV9ZSPR3dW8bwnPhmz2jezpg r+y5s1xJFJ6yK7IrZ/8q9cRhMx6Nbjw+9xk+1mnm0JcCIJgvPt9ECqOcX0loJowDfMWH aGS6+QPsHQAkwJm+tjVni500Pm/pzIsXm4jpPsUD8ASCstcLZjKPbfyDCQ4M2PixTENk LkyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763850246; x=1764455046; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=g/BoEmiu9zsrdP23VvEElLAx2sbpX8ex9ZCQjmSfNmg=; b=djvcHhL1pzg4Ez+iWurRAVwDxkVmd1JLCEPNrOmVNgQFiuMiD+kZ1Ezm1wdakTGVcS z1dgteSd/lt4EwHz2KXaUYeCqZYJd9liZyf9rIF4OGZhMMY95fabm8CY904YfDUHoizl g0c0TlhEgSzYW/qpKDmwBHz2zQSEeYNBCQqC6KAYwjjaOLSOo9jMMmdDp4fnprXV/qke VLejFBROOwhxQP12qz2jt1Y6cLieDd0YzhzszvBVddK3dXB20t4eg/RJ5KZ0Nb5rPri5 zefNQtAp/SxQ+oBVSF+JX205izwvS+aB2haGanrauCWzj8fjkyah1E03GMGsNOkF6oHz vujQ== X-Forwarded-Encrypted: i=1; AJvYcCUuGAQCCVK5zaG9pK5pVvgSRR0CSbq1B7aXXFKrNIt+ICqeRivw2qbE/LwpzS3ugqTq41cNLOYpBWyaEz0=@vger.kernel.org X-Gm-Message-State: AOJu0YyLZaI0FlY1BY57r5x4oqMg33WEou8prPmKnGJg9S7+Bfkbyp9M L3j2nNkPyp9ecDrqUephkvj4j5U1KI0nUOnDoRuP6hP/kKIb/zb1izwOK4MJ+C6jVAU= X-Gm-Gg: ASbGnctWS/+FykDWx7oqSFArGz11qXHjfdOGfUUBBBbdCUbXXLds398JmgPfRiHJDcK cYpagP5m6sjCEyerNwmUhBPApPu7OR917V/SKnzBTToJfySRI3ZkCHYge82m6DA3cKx9VQ8ECYT 2VGFv7f81rYq1+Ix4Ah8qF9apdwX4aaOdRU+LjYY/zAmksXn5h/bGaj8D1wEPuoHCMzpxkQbDYs f7bpTlUyVOg1Y6PRGJfrhdjKsj2mHARO8VyCISR8baPQTpqryK0FsPTnr+jXnPjF8maYfIBHroQ g9Egj57KTT2H2+QfoOoNxi3mSi5D2PJOlQ25r+NNTB5CBy5uX+6ngLHYk6XJ6J8Qu/tmkWTW2/O ewrO8ldLSSrGCrIyeKMITeHFr+o8Vs668wKBxO9Otd4NDMTEHPH22XSOIoMGV5BF7wgYRxnXtYm cmkwsg+345jnhRKWXQieStf7V7w6PkgD1MxL5T5lBpThmECBhNewaMLru+2J9B6woakmOAI1iSr WZBTKM= X-Google-Smtp-Source: AGHT+IGsBnZ08fwT+U7ONzjkXoJcTJprClMLwp+I60RQ2LQQg/g7R6+ZP/xtQp7fP65yvXwFKSrR1g== X-Received: by 2002:a05:690e:848:b0:63f:b590:2e5 with SMTP id 956f58d0204a3-64302a7cb9emr3717590d50.22.1763850246138; Sat, 22 Nov 2025 14:24:06 -0800 (PST) Received: from soleen.c.googlers.com.com (182.221.85.34.bc.googleusercontent.com. [34.85.221.182]) by smtp.gmail.com with ESMTPSA id 00721157ae682-78a79779a4esm28858937b3.0.2025.11.22.14.24.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Nov 2025 14:24:05 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.com, pasha.tatashin@soleen.com, rppt@kernel.org, dmatlack@google.com, rientjes@google.com, corbet@lwn.net, rdunlap@infradead.org, ilpo.jarvinen@linux.intel.com, kanie@linux.alibaba.com, ojeda@kernel.org, aliceryhl@google.com, masahiroy@kernel.org, akpm@linux-foundation.org, tj@kernel.org, yoann.congal@smile.fr, mmaurer@google.com, roman.gushchin@linux.dev, chenridong@huawei.com, axboe@kernel.dk, mark.rutland@arm.com, jannh@google.com, vincent.guittot@linaro.org, hannes@cmpxchg.org, dan.j.williams@intel.com, david@redhat.com, joel.granados@kernel.org, rostedt@goodmis.org, anna.schumaker@oracle.com, song@kernel.org, linux@weissschuh.net, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-mm@kvack.org, gregkh@linuxfoundation.org, tglx@linutronix.de, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com, rafael@kernel.org, dakr@kernel.org, bartosz.golaszewski@linaro.org, cw00.choi@samsung.com, myungjoo.ham@samsung.com, yesanishhere@gmail.com, Jonathan.Cameron@huawei.com, quic_zijuhu@quicinc.com, aleksander.lobakin@intel.com, ira.weiny@intel.com, andriy.shevchenko@linux.intel.com, leon@kernel.org, lukas@wunner.de, bhelgaas@google.com, wagi@kernel.org, djeffery@redhat.com, stuart.w.hayes@gmail.com, ptyadav@amazon.de, lennart@poettering.net, brauner@kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, saeedm@nvidia.com, ajayachandra@nvidia.com, jgg@nvidia.com, parav@nvidia.com, leonro@nvidia.com, witu@nvidia.com, hughd@google.com, skhawaja@google.com, chrisl@kernel.org Subject: [PATCH v7 05/22] liveupdate: luo_core: add user interface Date: Sat, 22 Nov 2025 17:23:32 -0500 Message-ID: <20251122222351.1059049-6-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.52.0.rc2.455.g230fcf2819-goog In-Reply-To: <20251122222351.1059049-1-pasha.tatashin@soleen.com> References: <20251122222351.1059049-1-pasha.tatashin@soleen.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce the user-space interface for the Live Update Orchestrator via ioctl commands, enabling external control over the live update process and management of preserved resources. The idea is that there is going to be a single userspace agent driving the live update, therefore, only a single process can ever hold this device opened at a time. The following ioctl commands are introduced: LIVEUPDATE_IOCTL_CREATE_SESSION Provides a way for userspace to create a named session for grouping file descriptors that need to be preserved. It returns a new file descriptor representing the session. LIVEUPDATE_IOCTL_RETRIEVE_SESSION Allows the userspace agent in the new kernel to reclaim a preserved session by its name, receiving a new file descriptor to manage the restored resources. Signed-off-by: Pasha Tatashin Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav --- include/uapi/linux/liveupdate.h | 64 +++++++++++ kernel/liveupdate/luo_core.c | 179 ++++++++++++++++++++++++++++++- kernel/liveupdate/luo_internal.h | 21 ++++ 3 files changed, 263 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.h index 40578ae19668..1183cf984b5f 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -46,4 +46,68 @@ /* The maximum length of session name including null termination */ #define LIVEUPDATE_SESSION_NAME_LENGTH 64 =20 +/* The /dev/liveupdate ioctl commands */ +enum { + LIVEUPDATE_CMD_BASE =3D 0x00, + LIVEUPDATE_CMD_CREATE_SESSION =3D LIVEUPDATE_CMD_BASE, + LIVEUPDATE_CMD_RETRIEVE_SESSION =3D 0x01, +}; + +/** + * struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_= SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_create_session) + * @fd: Output; The new file descriptor for the created session. + * @name: Input; A null-terminated string for the session name, max + * length %LIVEUPDATE_SESSION_NAME_LENGTH including termination + * character. + * + * Creates a new live update session for managing preserved resources. + * This ioctl can only be called on the main /dev/liveupdate device. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_create_session { + __u32 size; + __s32 fd; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_IOCTL_CREATE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_CREATE_SESSION) + +/** + * struct liveupdate_ioctl_retrieve_session - ioctl(LIVEUPDATE_IOCTL_RETRI= EVE_SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_retrieve_session) + * @fd: Output; The new file descriptor for the retrieved session. + * @name: Input; A null-terminated string identifying the session to re= trieve. + * The name must exactly match the name used when the session was + * created in the previous kernel. + * + * Retrieves a handle (a new file descriptor) for a preserved session by i= ts + * name. This is the primary mechanism for a userspace agent to regain con= trol + * of its preserved resources after a live update. + * + * The userspace application provides the null-terminated `name` of a sess= ion + * it created before the live update. If a preserved session with a matchi= ng + * name is found, the kernel instantiates it and returns a new file descri= ptor + * in the `fd` field. This new session FD can then be used for all file-sp= ecific + * operations, such as restoring individual file descriptors with + * LIVEUPDATE_SESSION_RETRIEVE_FD. + * + * It is the responsibility of the userspace application to know the names= of + * the sessions it needs to retrieve. If no session with the given name is + * found, the ioctl will fail with -ENOENT. + * + * This ioctl can only be called on the main /dev/liveupdate device when t= he + * system is in the LIVEUPDATE_STATE_UPDATED state. + */ +struct liveupdate_ioctl_retrieve_session { + __u32 size; + __s32 fd; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index a0f7788cd003..bc90954252a3 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -41,7 +41,13 @@ =20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt =20 +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -52,7 +58,6 @@ #include #include #include - #include "kexec_handover_internal.h" #include "luo_internal.h" =20 @@ -246,12 +251,183 @@ bool liveupdate_enabled(void) return luo_global.enabled; } =20 +/** + * DOC: LUO ioctl Interface + * + * The IOCTL user-space control interface for the LUO subsystem. + * It registers a character device, typically found at ``/dev/liveupdate``, + * which allows a userspace agent to manage the LUO state machine and its + * associated resources, such as preservable file descriptors. + * + * To ensure that the state machine is controlled by a single entity, acce= ss + * to this device is exclusive: only one process is permitted to have + * ``/dev/liveupdate`` open at any given time. Subsequent open attempts wi= ll + * fail with -EBUSY until the first process closes its file descriptor. + * This singleton model simplifies state management by preventing conflict= ing + * commands from multiple userspace agents. + */ + struct luo_device_state { struct miscdevice miscdev; + atomic_t in_use; +}; + +static int luo_ioctl_create_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_create_session *argp =3D ucmd->cmd; + struct file *file; + int err; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + err =3D luo_session_create(argp->name, &file); + if (err) + goto err_put_fd; + + err =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + goto err_put_file; + + fd_install(argp->fd, file); + + return 0; + +err_put_file: + fput(file); +err_put_fd: + put_unused_fd(argp->fd); + + return err; +} + +static int luo_ioctl_retrieve_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_retrieve_session *argp =3D ucmd->cmd; + struct file *file; + int err; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + err =3D luo_session_retrieve(argp->name, &file); + if (err < 0) + goto err_put_fd; + + err =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + goto err_put_file; + + fd_install(argp->fd, file); + + return 0; + +err_put_file: + fput(file); +err_put_fd: + put_unused_fd(argp->fd); + + return err; +} + +static int luo_open(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev =3D container_of(filep->private_data, + struct luo_device_state, + miscdev); + + if (atomic_cmpxchg(&ldev->in_use, 0, 1)) + return -EBUSY; + + /* Always return -EIO to user if deserialization fail */ + if (luo_session_deserialize()) { + atomic_set(&ldev->in_use, 0); + return -EIO; + } + + return 0; +} + +static int luo_release(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev =3D container_of(filep->private_data, + struct luo_device_state, + miscdev); + atomic_set(&ldev->in_use, 0); + + return 0; +} + +union ucmd_buffer { + struct liveupdate_ioctl_create_session create; + struct liveupdate_ioctl_retrieve_session retrieve; +}; + +struct luo_ioctl_op { + unsigned int size; + unsigned int min_size; + unsigned int ioctl_num; + int (*execute)(struct luo_ucmd *ucmd); +}; + +#define IOCTL_OP(_ioctl, _fn, _struct, _last) = \ + [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_BASE] =3D { \ + .size =3D sizeof(_struct) + \ + BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ + sizeof(_struct)), \ + .min_size =3D offsetofend(_struct, _last), \ + .ioctl_num =3D _ioctl, \ + .execute =3D _fn, \ + } + +static const struct luo_ioctl_op luo_ioctl_ops[] =3D { + IOCTL_OP(LIVEUPDATE_IOCTL_CREATE_SESSION, luo_ioctl_create_session, + struct liveupdate_ioctl_create_session, name), + IOCTL_OP(LIVEUPDATE_IOCTL_RETRIEVE_SESSION, luo_ioctl_retrieve_session, + struct liveupdate_ioctl_retrieve_session, name), }; =20 +static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long = arg) +{ + const struct luo_ioctl_op *op; + struct luo_ucmd ucmd =3D {}; + union ucmd_buffer buf; + unsigned int nr; + int err; + + nr =3D _IOC_NR(cmd); + if (nr < LIVEUPDATE_CMD_BASE || + (nr - LIVEUPDATE_CMD_BASE) >=3D ARRAY_SIZE(luo_ioctl_ops)) { + return -EINVAL; + } + + ucmd.ubuffer =3D (void __user *)arg; + err =3D get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); + if (err) + return err; + + op =3D &luo_ioctl_ops[nr - LIVEUPDATE_CMD_BASE]; + if (op->ioctl_num !=3D cmd) + return -ENOIOCTLCMD; + if (ucmd.user_size < op->min_size) + return -EINVAL; + + ucmd.cmd =3D &buf; + err =3D copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, + ucmd.user_size); + if (err) + return err; + + return op->execute(&ucmd); +} + static const struct file_operations luo_fops =3D { .owner =3D THIS_MODULE, + .open =3D luo_open, + .release =3D luo_release, + .unlocked_ioctl =3D luo_ioctl, }; =20 static struct luo_device_state luo_dev =3D { @@ -260,6 +436,7 @@ static struct luo_device_state luo_dev =3D { .name =3D "liveupdate", .fops =3D &luo_fops, }, + .in_use =3D ATOMIC_INIT(0), }; =20 static int __init liveupdate_ioctl_init(void) diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 05ae91695ec6..1292ac47eef8 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -9,6 +9,27 @@ #define _LINUX_LUO_INTERNAL_H =20 #include +#include + +struct luo_ucmd { + void __user *ubuffer; + u32 user_size; + void *cmd; +}; + +static inline int luo_ucmd_respond(struct luo_ucmd *ucmd, + size_t kernel_cmd_size) +{ + /* + * Copy the minimum of what the user provided and what we actually + * have. + */ + if (copy_to_user(ucmd->ubuffer, ucmd->cmd, + min_t(size_t, ucmd->user_size, kernel_cmd_size))) { + return -EFAULT; + } + return 0; +} =20 /* * Handles a deserialization failure: devices and memory is in unpredictab= le --=20 2.52.0.rc2.455.g230fcf2819-goog