Skip to content

Commit 16cec0d

Browse files
soleenakpm00
authored andcommitted
liveupdate: luo_session: add ioctls for file preservation
Introducing the userspace interface and internal logic required to manage the lifecycle of file descriptors within a session. Previously, a session was merely a container; this change makes it a functional management unit. The following capabilities are added: A new set of ioctl commands are added, which operate on the file descriptor returned by CREATE_SESSION. This allows userspace to: - LIVEUPDATE_SESSION_PRESERVE_FD: Add a file descriptor to a session to be preserved across the live update. - LIVEUPDATE_SESSION_RETRIEVE_FD: Retrieve a preserved file in the new kernel using its unique token. - LIVEUPDATE_SESSION_FINISH: finish session The session's .release handler is enhanced to be state-aware. When a session's file descriptor is closed, it correctly unpreserves the session based on its current state before freeing all associated file resources. Link: https://lkml.kernel.org/r/20251125165850.3389713-8-pasha.tatashin@soleen.com Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com> Reviewed-by: Pratyush Yadav <pratyush@kernel.org> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Tested-by: David Matlack <dmatlack@google.com> Cc: Aleksander Lobakin <aleksander.lobakin@intel.com> Cc: Alexander Graf <graf@amazon.com> Cc: Alice Ryhl <aliceryhl@google.com> Cc: Andriy Shevchenko <andriy.shevchenko@linux.intel.com> Cc: anish kumar <yesanishhere@gmail.com> Cc: Anna Schumaker <anna.schumaker@oracle.com> Cc: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Borislav Betkov <bp@alien8.de> Cc: Chanwoo Choi <cw00.choi@samsung.com> Cc: Chen Ridong <chenridong@huawei.com> Cc: Chris Li <chrisl@kernel.org> Cc: Christian Brauner <brauner@kernel.org> Cc: Daniel Wagner <wagi@kernel.org> Cc: Danilo Krummrich <dakr@kernel.org> Cc: Dan Williams <dan.j.williams@intel.com> Cc: David Hildenbrand <david@redhat.com> Cc: David Jeffery <djeffery@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Guixin Liu <kanie@linux.alibaba.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Hugh Dickins <hughd@google.com> Cc: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Ira Weiny <ira.weiny@intel.com> Cc: Jann Horn <jannh@google.com> Cc: Jason Gunthorpe <jgg@nvidia.com> Cc: Jens Axboe <axboe@kernel.dk> Cc: Joanthan Cameron <Jonathan.Cameron@huawei.com> Cc: Joel Granados <joel.granados@kernel.org> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Lennart Poettering <lennart@poettering.net> Cc: Leon Romanovsky <leon@kernel.org> Cc: Leon Romanovsky <leonro@nvidia.com> Cc: Lukas Wunner <lukas@wunner.de> Cc: Marc Rutland <mark.rutland@arm.com> Cc: Masahiro Yamada <masahiroy@kernel.org> Cc: Matthew Maurer <mmaurer@google.com> Cc: Miguel Ojeda <ojeda@kernel.org> Cc: Myugnjoo Ham <myungjoo.ham@samsung.com> Cc: Parav Pandit <parav@nvidia.com> Cc: Pratyush Yadav <ptyadav@amazon.de> Cc: Randy Dunlap <rdunlap@infradead.org> Cc: Roman Gushchin <roman.gushchin@linux.dev> Cc: Saeed Mahameed <saeedm@nvidia.com> Cc: Samiullah Khawaja <skhawaja@google.com> Cc: Song Liu <song@kernel.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Stuart Hayes <stuart.w.hayes@gmail.com> Cc: Tejun Heo <tj@kernel.org> Cc: Thomas Gleinxer <tglx@linutronix.de> Cc: Thomas Weißschuh <linux@weissschuh.net> Cc: Vincent Guittot <vincent.guittot@linaro.org> Cc: William Tu <witu@nvidia.com> Cc: Yoann Congal <yoann.congal@smile.fr> Cc: Zhu Yanjun <yanjun.zhu@linux.dev> Cc: Zijun Hu <quic_zijuhu@quicinc.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 7c722a7 commit 16cec0d

2 files changed

Lines changed: 288 additions & 2 deletions

File tree

include/uapi/linux/liveupdate.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ enum {
5353
LIVEUPDATE_CMD_RETRIEVE_SESSION = 0x01,
5454
};
5555

56+
/* ioctl commands for session file descriptors */
57+
enum {
58+
LIVEUPDATE_CMD_SESSION_BASE = 0x40,
59+
LIVEUPDATE_CMD_SESSION_PRESERVE_FD = LIVEUPDATE_CMD_SESSION_BASE,
60+
LIVEUPDATE_CMD_SESSION_RETRIEVE_FD = 0x41,
61+
LIVEUPDATE_CMD_SESSION_FINISH = 0x42,
62+
};
63+
5664
/**
5765
* struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_SESSION)
5866
* @size: Input; sizeof(struct liveupdate_ioctl_create_session)
@@ -110,4 +118,99 @@ struct liveupdate_ioctl_retrieve_session {
110118
#define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \
111119
_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION)
112120

121+
/* Session specific IOCTLs */
122+
123+
/**
124+
* struct liveupdate_session_preserve_fd - ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)
125+
* @size: Input; sizeof(struct liveupdate_session_preserve_fd)
126+
* @fd: Input; The user-space file descriptor to be preserved.
127+
* @token: Input; An opaque, unique token for preserved resource.
128+
*
129+
* Holds parameters for preserving a file descriptor.
130+
*
131+
* User sets the @fd field identifying the file descriptor to preserve
132+
* (e.g., memfd, kvm, iommufd, VFIO). The kernel validates if this FD type
133+
* and its dependencies are supported for preservation. If validation passes,
134+
* the kernel marks the FD internally and *initiates the process* of preparing
135+
* its state for saving. The actual snapshotting of the state typically occurs
136+
* during the subsequent %LIVEUPDATE_IOCTL_PREPARE execution phase, though
137+
* some finalization might occur during freeze.
138+
* On successful validation and initiation, the kernel uses the @token
139+
* field with an opaque identifier representing the resource being preserved.
140+
* This token confirms the FD is targeted for preservation and is required for
141+
* the subsequent %LIVEUPDATE_SESSION_RETRIEVE_FD call after the live update.
142+
*
143+
* Return: 0 on success (validation passed, preservation initiated), negative
144+
* error code on failure (e.g., unsupported FD type, dependency issue,
145+
* validation failed).
146+
*/
147+
struct liveupdate_session_preserve_fd {
148+
__u32 size;
149+
__s32 fd;
150+
__aligned_u64 token;
151+
};
152+
153+
#define LIVEUPDATE_SESSION_PRESERVE_FD \
154+
_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_PRESERVE_FD)
155+
156+
/**
157+
* struct liveupdate_session_retrieve_fd - ioctl(LIVEUPDATE_SESSION_RETRIEVE_FD)
158+
* @size: Input; sizeof(struct liveupdate_session_retrieve_fd)
159+
* @fd: Output; The new file descriptor representing the fully restored
160+
* kernel resource.
161+
* @token: Input; An opaque, token that was used to preserve the resource.
162+
*
163+
* Retrieve a previously preserved file descriptor.
164+
*
165+
* User sets the @token field to the value obtained from a successful
166+
* %LIVEUPDATE_IOCTL_FD_PRESERVE call before the live update. On success,
167+
* the kernel restores the state (saved during the PREPARE/FREEZE phases)
168+
* associated with the token and populates the @fd field with a new file
169+
* descriptor referencing the restored resource in the current (new) kernel.
170+
* This operation must be performed *before* signaling completion via
171+
* %LIVEUPDATE_IOCTL_FINISH.
172+
*
173+
* Return: 0 on success, negative error code on failure (e.g., invalid token).
174+
*/
175+
struct liveupdate_session_retrieve_fd {
176+
__u32 size;
177+
__s32 fd;
178+
__aligned_u64 token;
179+
};
180+
181+
#define LIVEUPDATE_SESSION_RETRIEVE_FD \
182+
_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_RETRIEVE_FD)
183+
184+
/**
185+
* struct liveupdate_session_finish - ioctl(LIVEUPDATE_SESSION_FINISH)
186+
* @size: Input; sizeof(struct liveupdate_session_finish)
187+
* @reserved: Input; Must be zero. Reserved for future use.
188+
*
189+
* Signals the completion of the restoration process for a retrieved session.
190+
* This is the final operation that should be performed on a session file
191+
* descriptor after a live update.
192+
*
193+
* This ioctl must be called once all required file descriptors for the session
194+
* have been successfully retrieved (using %LIVEUPDATE_SESSION_RETRIEVE_FD) and
195+
* are fully restored from the userspace and kernel perspective.
196+
*
197+
* Upon success, the kernel releases its ownership of the preserved resources
198+
* associated with this session. This allows internal resources to be freed,
199+
* typically by decrementing reference counts on the underlying preserved
200+
* objects.
201+
*
202+
* If this operation fails, the resources remain preserved in memory. Userspace
203+
* may attempt to call finish again. The resources will otherwise be reset
204+
* during the next live update cycle.
205+
*
206+
* Return: 0 on success, negative error code on failure.
207+
*/
208+
struct liveupdate_session_finish {
209+
__u32 size;
210+
__u32 reserved;
211+
};
212+
213+
#define LIVEUPDATE_SESSION_FINISH \
214+
_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FINISH)
215+
113216
#endif /* _UAPI_LIVEUPDATE_H */

kernel/liveupdate/luo_session.c

Lines changed: 185 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ static struct luo_session *luo_session_alloc(const char *name)
125125
return ERR_PTR(-ENOMEM);
126126

127127
strscpy(session->name, name, sizeof(session->name));
128+
INIT_LIST_HEAD(&session->file_set.files_list);
129+
luo_file_set_init(&session->file_set);
128130
INIT_LIST_HEAD(&session->list);
129131
mutex_init(&session->mutex);
130132

@@ -133,6 +135,7 @@ static struct luo_session *luo_session_alloc(const char *name)
133135

134136
static void luo_session_free(struct luo_session *session)
135137
{
138+
luo_file_set_destroy(&session->file_set);
136139
mutex_destroy(&session->mutex);
137140
kfree(session);
138141
}
@@ -177,26 +180,187 @@ static void luo_session_remove(struct luo_session_header *sh,
177180
sh->count--;
178181
}
179182

183+
static int luo_session_finish_one(struct luo_session *session)
184+
{
185+
guard(mutex)(&session->mutex);
186+
return luo_file_finish(&session->file_set);
187+
}
188+
189+
static void luo_session_unfreeze_one(struct luo_session *session,
190+
struct luo_session_ser *ser)
191+
{
192+
guard(mutex)(&session->mutex);
193+
luo_file_unfreeze(&session->file_set, &ser->file_set_ser);
194+
}
195+
196+
static int luo_session_freeze_one(struct luo_session *session,
197+
struct luo_session_ser *ser)
198+
{
199+
guard(mutex)(&session->mutex);
200+
return luo_file_freeze(&session->file_set, &ser->file_set_ser);
201+
}
202+
180203
static int luo_session_release(struct inode *inodep, struct file *filep)
181204
{
182205
struct luo_session *session = filep->private_data;
183206
struct luo_session_header *sh;
184207

185208
/* If retrieved is set, it means this session is from incoming list */
186-
if (session->retrieved)
209+
if (session->retrieved) {
210+
int err = luo_session_finish_one(session);
211+
212+
if (err) {
213+
pr_warn("Unable to finish session [%s] on release\n",
214+
session->name);
215+
return err;
216+
}
187217
sh = &luo_session_global.incoming;
188-
else
218+
} else {
219+
scoped_guard(mutex, &session->mutex)
220+
luo_file_unpreserve_files(&session->file_set);
189221
sh = &luo_session_global.outgoing;
222+
}
190223

191224
luo_session_remove(sh, session);
192225
luo_session_free(session);
193226

194227
return 0;
195228
}
196229

230+
static int luo_session_preserve_fd(struct luo_session *session,
231+
struct luo_ucmd *ucmd)
232+
{
233+
struct liveupdate_session_preserve_fd *argp = ucmd->cmd;
234+
int err;
235+
236+
guard(mutex)(&session->mutex);
237+
err = luo_preserve_file(&session->file_set, argp->token, argp->fd);
238+
if (err)
239+
return err;
240+
241+
err = luo_ucmd_respond(ucmd, sizeof(*argp));
242+
if (err)
243+
pr_warn("The file was successfully preserved, but response to user failed\n");
244+
245+
return err;
246+
}
247+
248+
static int luo_session_retrieve_fd(struct luo_session *session,
249+
struct luo_ucmd *ucmd)
250+
{
251+
struct liveupdate_session_retrieve_fd *argp = ucmd->cmd;
252+
struct file *file;
253+
int err;
254+
255+
argp->fd = get_unused_fd_flags(O_CLOEXEC);
256+
if (argp->fd < 0)
257+
return argp->fd;
258+
259+
guard(mutex)(&session->mutex);
260+
err = luo_retrieve_file(&session->file_set, argp->token, &file);
261+
if (err < 0)
262+
goto err_put_fd;
263+
264+
err = luo_ucmd_respond(ucmd, sizeof(*argp));
265+
if (err)
266+
goto err_put_file;
267+
268+
fd_install(argp->fd, file);
269+
270+
return 0;
271+
272+
err_put_file:
273+
fput(file);
274+
err_put_fd:
275+
put_unused_fd(argp->fd);
276+
277+
return err;
278+
}
279+
280+
static int luo_session_finish(struct luo_session *session,
281+
struct luo_ucmd *ucmd)
282+
{
283+
struct liveupdate_session_finish *argp = ucmd->cmd;
284+
int err = luo_session_finish_one(session);
285+
286+
if (err)
287+
return err;
288+
289+
return luo_ucmd_respond(ucmd, sizeof(*argp));
290+
}
291+
292+
union ucmd_buffer {
293+
struct liveupdate_session_finish finish;
294+
struct liveupdate_session_preserve_fd preserve;
295+
struct liveupdate_session_retrieve_fd retrieve;
296+
};
297+
298+
struct luo_ioctl_op {
299+
unsigned int size;
300+
unsigned int min_size;
301+
unsigned int ioctl_num;
302+
int (*execute)(struct luo_session *session, struct luo_ucmd *ucmd);
303+
};
304+
305+
#define IOCTL_OP(_ioctl, _fn, _struct, _last) \
306+
[_IOC_NR(_ioctl) - LIVEUPDATE_CMD_SESSION_BASE] = { \
307+
.size = sizeof(_struct) + \
308+
BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \
309+
sizeof(_struct)), \
310+
.min_size = offsetofend(_struct, _last), \
311+
.ioctl_num = _ioctl, \
312+
.execute = _fn, \
313+
}
314+
315+
static const struct luo_ioctl_op luo_session_ioctl_ops[] = {
316+
IOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish,
317+
struct liveupdate_session_finish, reserved),
318+
IOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd,
319+
struct liveupdate_session_preserve_fd, token),
320+
IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd,
321+
struct liveupdate_session_retrieve_fd, token),
322+
};
323+
324+
static long luo_session_ioctl(struct file *filep, unsigned int cmd,
325+
unsigned long arg)
326+
{
327+
struct luo_session *session = filep->private_data;
328+
const struct luo_ioctl_op *op;
329+
struct luo_ucmd ucmd = {};
330+
union ucmd_buffer buf;
331+
unsigned int nr;
332+
int ret;
333+
334+
nr = _IOC_NR(cmd);
335+
if (nr < LIVEUPDATE_CMD_SESSION_BASE || (nr - LIVEUPDATE_CMD_SESSION_BASE) >=
336+
ARRAY_SIZE(luo_session_ioctl_ops)) {
337+
return -EINVAL;
338+
}
339+
340+
ucmd.ubuffer = (void __user *)arg;
341+
ret = get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer);
342+
if (ret)
343+
return ret;
344+
345+
op = &luo_session_ioctl_ops[nr - LIVEUPDATE_CMD_SESSION_BASE];
346+
if (op->ioctl_num != cmd)
347+
return -ENOIOCTLCMD;
348+
if (ucmd.user_size < op->min_size)
349+
return -EINVAL;
350+
351+
ucmd.cmd = &buf;
352+
ret = copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer,
353+
ucmd.user_size);
354+
if (ret)
355+
return ret;
356+
357+
return op->execute(session, &ucmd);
358+
}
359+
197360
static const struct file_operations luo_session_fops = {
198361
.owner = THIS_MODULE,
199362
.release = luo_session_release,
363+
.unlocked_ioctl = luo_session_ioctl,
200364
};
201365

202366
/* Create a "struct file" for session */
@@ -392,6 +556,11 @@ int luo_session_deserialize(void)
392556
luo_session_free(session);
393557
return err;
394558
}
559+
560+
scoped_guard(mutex, &session->mutex) {
561+
luo_file_deserialize(&session->file_set,
562+
&sh->ser[i].file_set_ser);
563+
}
395564
}
396565

397566
kho_restore_free(sh->header_ser);
@@ -406,16 +575,30 @@ int luo_session_serialize(void)
406575
struct luo_session_header *sh = &luo_session_global.outgoing;
407576
struct luo_session *session;
408577
int i = 0;
578+
int err;
409579

410580
guard(rwsem_write)(&sh->rwsem);
411581
list_for_each_entry(session, &sh->list, list) {
582+
err = luo_session_freeze_one(session, &sh->ser[i]);
583+
if (err)
584+
goto err_undo;
585+
412586
strscpy(sh->ser[i].name, session->name,
413587
sizeof(sh->ser[i].name));
414588
i++;
415589
}
416590
sh->header_ser->count = sh->count;
417591

418592
return 0;
593+
594+
err_undo:
595+
list_for_each_entry_continue_reverse(session, &sh->list, list) {
596+
i--;
597+
luo_session_unfreeze_one(session, &sh->ser[i]);
598+
memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name));
599+
}
600+
601+
return err;
419602
}
420603

421604
/**

0 commit comments

Comments
 (0)