* [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API
@ 2026-03-18 14:16 Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem Pasha Tatashin
` (7 more replies)
0 siblings, 8 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
This patch series addresses an issue with how LUO handles module
reference counting and unregistration during a module unload (e.g.,
via rmmod).
Currently, modules that register live update file handlers are pinned
for the entire duration they are registered. This prevents the modules
from being unloaded gracefully, even when no live update session is in
progress.
Furthermore, if a module is forcefully unloaded, the unregistration
functions return an error (e.g. -EBUSY) if a session is active, which
is ignored by the kernel's module unload path, leaving dangling
pointers in the LUO global lists.
As pointed out by Jason Gunthorpe and Alex Williamson during the review
of the VFIO PCI live update patches [1]:
> "destroy" functions that fail are evil. :)
> IMHO blow up the kernel or something in the core code, you can't stop
> module unloading once it starts so it is pointless to propagate this
To resolve these issues, this series introduces the following changes:
1. Adds read-write semaphores (luo_file_handler_lock, luo_flb_lock, and
a per-handler flb_lock) to protect the registration lists. This allows
concurrent access for file preservation without blocking, while still
preventing traversal races during module unload.
2. Defers FLB module reference counting (try_module_get / module_put)
so that modules are only pinned when their FLBs are actively used
in a live update session.
3. Removes module reference counting for file handlers, relying
on the VFS 'struct file' pinning (via f_op->owner) and safe
deserialization without concurrent unloads.
4. Removes the global luo_session_quiesce() mechanism since module
unload behavior now handles active sessions implicitly.
5. Introduces auto-unregistration of FLBs during file handler
unregistration to prevent leaving dangling resources.
6. Changes the unregistration functions to return void instead of
an error code.
[1] https://lore.kernel.org/all/20260303210733.GG972761@nvidia.com
Changelog since v1:
- Restructured the patch series to eliminate intermediate code changes.
- Replaced the "Defer file handler module refcounting" patch with
"Remove file handler module refcounting" since the VFS 'struct file'
inherently pins the file handler's module via f_op->owner during
active sessions, rendering dynamic reference counting unnecessary
for handlers, as suggested by David Matlack.
- Split the unregister API change into two logical patches: one for
auto-unregistration of FLBs and another to convert the unregister
functions to return void.
- Added a patch to clean up the redundant `liveupdate_test_unregister()`
function in `lib/tests/liveupdate.c`.
- Reordered the patches so that `liveupdate_test_unregister()` is cleanly
removed before the core API signatures change to return void.
Pasha Tatashin (8):
liveupdate: Protect file handler list with rwsem
liveupdate: Protect FLB lists with rwsem
liveupdate: Remove file handler module refcounting
liveupdate: Defer FLB module refcounting to active sessions
liveupdate: Remove luo_session_quiesce()
liveupdate: Auto unregister FLBs on file handler unregistration
liveupdate: Remove liveupdate_test_unregister()
liveupdate: Make unregister functions return void
include/linux/liveupdate.h | 17 +--
kernel/liveupdate/luo_file.c | 102 ++++++-----------
kernel/liveupdate/luo_flb.c | 182 +++++++++++++++++--------------
kernel/liveupdate/luo_internal.h | 5 +-
kernel/liveupdate/luo_session.c | 44 --------
lib/tests/liveupdate.c | 18 ---
6 files changed, 139 insertions(+), 229 deletions(-)
base-commit: 8e5a478b6d6a5bb0a3d52147862b15e4d826af19
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-22 13:13 ` Mike Rapoport
2026-03-18 14:16 ` [PATCH v2 2/8] liveupdate: Protect FLB lists " Pasha Tatashin
` (6 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
Because liveupdate file handlers will no longer hold a
module reference when registered, we must ensure that the access to
the handler list is protected against concurrent module unloading.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 61 +++++++++++++++++++++---------------
1 file changed, 35 insertions(+), 26 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index 5acee4174bf0..6a0ae29c6a24 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -112,6 +112,7 @@
#include <linux/string.h>
#include "luo_internal.h"
+static DECLARE_RWSEM(luo_file_handler_lock);
static LIST_HEAD(luo_file_handler_list);
/* 2 4K pages, give space for 128 files per file_set */
@@ -277,10 +278,12 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
goto err_fput;
err = -ENOENT;
- list_private_for_each_entry(fh, &luo_file_handler_list, list) {
- if (fh->ops->can_preserve(fh, file)) {
- err = 0;
- break;
+ scoped_guard(rwsem_read, &luo_file_handler_lock) {
+ list_private_for_each_entry(fh, &luo_file_handler_list, list) {
+ if (fh->ops->can_preserve(fh, file)) {
+ err = 0;
+ break;
+ }
}
}
@@ -777,10 +780,12 @@ int luo_file_deserialize(struct luo_file_set *file_set,
bool handler_found = false;
struct luo_file *luo_file;
- list_private_for_each_entry(fh, &luo_file_handler_list, list) {
- if (!strcmp(fh->compatible, file_ser[i].compatible)) {
- handler_found = true;
- break;
+ scoped_guard(rwsem_read, &luo_file_handler_lock) {
+ list_private_for_each_entry(fh, &luo_file_handler_list, list) {
+ if (!strcmp(fh->compatible, file_ser[i].compatible)) {
+ handler_found = true;
+ break;
+ }
}
}
@@ -850,25 +855,27 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
if (!luo_session_quiesce())
return -EBUSY;
- /* Check for duplicate compatible strings */
- list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
- if (!strcmp(fh_iter->compatible, fh->compatible)) {
- pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
- fh->compatible);
- err = -EEXIST;
+ scoped_guard(rwsem_write, &luo_file_handler_lock) {
+ /* Check for duplicate compatible strings */
+ list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
+ if (!strcmp(fh_iter->compatible, fh->compatible)) {
+ pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
+ fh->compatible);
+ err = -EEXIST;
+ goto err_resume;
+ }
+ }
+
+ /* Pin the module implementing the handler */
+ if (!try_module_get(fh->ops->owner)) {
+ err = -EAGAIN;
goto err_resume;
}
- }
- /* Pin the module implementing the handler */
- if (!try_module_get(fh->ops->owner)) {
- err = -EAGAIN;
- goto err_resume;
+ INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, flb_list));
+ INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list));
+ list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
}
-
- INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, flb_list));
- INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list));
- list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
luo_session_resume();
liveupdate_test_register(fh);
@@ -909,10 +916,12 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
if (!luo_session_quiesce())
goto err_register;
- if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
- goto err_resume;
+ scoped_guard(rwsem_write, &luo_file_handler_lock) {
+ if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
+ goto err_resume;
- list_del(&ACCESS_PRIVATE(fh, list));
+ list_del(&ACCESS_PRIVATE(fh, list));
+ }
module_put(fh->ops->owner);
luo_session_resume();
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 2/8] liveupdate: Protect FLB lists with rwsem
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-20 0:21 ` Samiullah Khawaja
2026-03-18 14:16 ` [PATCH v2 3/8] liveupdate: Remove file handler module refcounting Pasha Tatashin
` (5 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
Because liveupdate FLB objects will soon drop their persistent module
references when registered, list traversals must be protected against
concurrent module unloading.
Introduce two read-write semaphores to provide this protection:
1. A global luo_flb_lock protects the global registry of FLBs.
2. A per-handler flb_lock protects the handler's specific list of FLB
dependencies.
Read locks are used during concurrent list traversals (e.g., during
preservation and serialization). Write locks are taken during registration
and unregistration. When both locks are required, the global luo_flb_lock
is strictly acquired before the per-handler flb_lock to prevent deadlocks.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/liveupdate.h | 3 +++
kernel/liveupdate/luo_file.c | 1 +
kernel/liveupdate/luo_flb.c | 16 ++++++++++++++++
3 files changed, 20 insertions(+)
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index dd11fdc76a5f..8394fb2d8774 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -12,6 +12,7 @@
#include <linux/kho/abi/luo.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/rwsem.h>
#include <linux/types.h>
#include <uapi/linux/liveupdate.h>
@@ -107,6 +108,8 @@ struct liveupdate_file_handler {
struct list_head __private list;
/* A list of FLB dependencies. */
struct list_head __private flb_list;
+ /* Protects flb_list */
+ struct rw_semaphore __private flb_lock;
};
/**
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index 6a0ae29c6a24..96fdd5790dcc 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -873,6 +873,7 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
}
INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, flb_list));
+ init_rwsem(&ACCESS_PRIVATE(fh, flb_lock));
INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list));
list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
}
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index f52e8114837e..91910d806d1d 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -49,6 +49,7 @@
#include <linux/liveupdate.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/unaligned.h>
#include "luo_internal.h"
@@ -70,6 +71,7 @@ struct luo_flb_global {
long count;
};
+static DECLARE_RWSEM(luo_flb_lock);
static struct luo_flb_global luo_flb_global = {
.list = LIST_HEAD_INIT(luo_flb_global.list),
};
@@ -240,6 +242,8 @@ int luo_flb_file_preserve(struct liveupdate_file_handler *fh)
struct luo_flb_link *iter;
int err = 0;
+ guard(rwsem_read)(&ACCESS_PRIVATE(fh, flb_lock));
+
list_for_each_entry(iter, flb_list, list) {
err = luo_flb_file_preserve_one(iter->flb);
if (err)
@@ -272,6 +276,8 @@ void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh)
struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
struct luo_flb_link *iter;
+ guard(rwsem_read)(&ACCESS_PRIVATE(fh, flb_lock));
+
list_for_each_entry_reverse(iter, flb_list, list)
luo_flb_file_unpreserve_one(iter->flb);
}
@@ -292,6 +298,8 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh)
struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
struct luo_flb_link *iter;
+ guard(rwsem_read)(&ACCESS_PRIVATE(fh, flb_lock));
+
list_for_each_entry_reverse(iter, flb_list, list)
luo_flb_file_finish_one(iter->flb);
}
@@ -355,6 +363,9 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
if (!luo_session_quiesce())
return -EBUSY;
+ guard(rwsem_write)(&luo_flb_lock);
+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
+
/* Check that this FLB is not already linked to this file handler */
err = -EEXIST;
list_for_each_entry(iter, flb_list, list) {
@@ -444,6 +455,9 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
if (!luo_session_quiesce())
return -EBUSY;
+ guard(rwsem_write)(&luo_flb_lock);
+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
+
/* Find and remove the link from the file handler's list */
list_for_each_entry(iter, flb_list, list) {
if (iter->flb == flb) {
@@ -638,6 +652,8 @@ void luo_flb_serialize(void)
struct liveupdate_flb *gflb;
int i = 0;
+ guard(rwsem_read)(&luo_flb_lock);
+
list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) {
struct luo_flb_private *private = luo_flb_get_private(gflb);
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 3/8] liveupdate: Remove file handler module refcounting
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 2/8] liveupdate: Protect FLB lists " Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-24 21:15 ` David Matlack
2026-03-24 21:23 ` David Matlack
2026-03-18 14:16 ` [PATCH v2 4/8] liveupdate: Defer FLB module refcounting to active sessions Pasha Tatashin
` (4 subsequent siblings)
7 siblings, 2 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
File handlers do not need to pin modules indefinitely or during active
live update sessions. The VFS 'struct file' pins the file handler's module
via f_op->owner during active sessions, making dynamic reference counting
unnecessary for handlers.
When a file is preserved, the live update core obtains a 'struct file'
via fdget(). As long as the file is kept open within the live update
session, the module is pinned by the VFS and cannot be unloaded.
Similarly, during deserialization, file handlers are matched based on
the compatible string. Because the handler list is protected by
luo_file_handler_lock, there is no race that requires dynamic
module refcounting.
Removing these module references ensures that modules implementing file
handlers can be unloaded when no longer providing active files.
Suggested-by: David Matlack <dmatlack@google.com>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 28 +++++++---------------------
1 file changed, 7 insertions(+), 21 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index 96fdd5790dcc..b124fd747841 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -836,7 +836,6 @@ void luo_file_set_destroy(struct luo_file_set *file_set)
int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
{
struct liveupdate_file_handler *fh_iter;
- int err;
if (!liveupdate_enabled())
return -EOPNOTSUPP;
@@ -861,17 +860,11 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
if (!strcmp(fh_iter->compatible, fh->compatible)) {
pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
fh->compatible);
- err = -EEXIST;
- goto err_resume;
+ luo_session_resume();
+ return -EEXIST;
}
}
- /* Pin the module implementing the handler */
- if (!try_module_get(fh->ops->owner)) {
- err = -EAGAIN;
- goto err_resume;
- }
-
INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, flb_list));
init_rwsem(&ACCESS_PRIVATE(fh, flb_lock));
INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list));
@@ -882,10 +875,6 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
liveupdate_test_register(fh);
return 0;
-
-err_resume:
- luo_session_resume();
- return err;
}
/**
@@ -907,8 +896,6 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
*/
int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
{
- int err = -EBUSY;
-
if (!liveupdate_enabled())
return -EOPNOTSUPP;
@@ -918,19 +905,18 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
goto err_register;
scoped_guard(rwsem_write, &luo_file_handler_lock) {
- if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
- goto err_resume;
+ if (!list_empty(&ACCESS_PRIVATE(fh, flb_list))) {
+ luo_session_resume();
+ goto err_register;
+ }
list_del(&ACCESS_PRIVATE(fh, list));
}
- module_put(fh->ops->owner);
luo_session_resume();
return 0;
-err_resume:
- luo_session_resume();
err_register:
liveupdate_test_register(fh);
- return err;
+ return -EBUSY;
}
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 4/8] liveupdate: Defer FLB module refcounting to active sessions
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
` (2 preceding siblings ...)
2026-03-18 14:16 ` [PATCH v2 3/8] liveupdate: Remove file handler module refcounting Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 5/8] liveupdate: Remove luo_session_quiesce() Pasha Tatashin
` (3 subsequent siblings)
7 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
Stop pinning modules indefinitely upon FLB registration.
Instead, dynamically take a module reference when the FLB is actively
used in a session (e.g., during preserve and retrieve) and release it
when the session concludes.
This allows modules providing FLB operations to be cleanly unloaded
when not in active use by the live update orchestrator.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_flb.c | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 91910d806d1d..98336cd23aa5 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -112,10 +112,15 @@ static int luo_flb_file_preserve_one(struct liveupdate_flb *flb)
struct liveupdate_flb_op_args args = {0};
int err;
+ if (!try_module_get(flb->ops->owner))
+ return -ENODEV;
+
args.flb = flb;
err = flb->ops->preserve(&args);
- if (err)
+ if (err) {
+ module_put(flb->ops->owner);
return err;
+ }
private->outgoing.data = args.data;
private->outgoing.obj = args.obj;
}
@@ -143,6 +148,7 @@ static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb)
private->outgoing.data = 0;
private->outgoing.obj = NULL;
+ module_put(flb->ops->owner);
}
}
}
@@ -178,12 +184,17 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
if (!found)
return -ENOENT;
+ if (!try_module_get(flb->ops->owner))
+ return -ENODEV;
+
args.flb = flb;
args.data = private->incoming.data;
err = flb->ops->retrieve(&args);
- if (err)
+ if (err) {
+ module_put(flb->ops->owner);
return err;
+ }
private->incoming.obj = args.obj;
private->incoming.retrieved = true;
@@ -217,6 +228,7 @@ static void luo_flb_file_finish_one(struct liveupdate_flb *flb)
private->incoming.data = 0;
private->incoming.obj = NULL;
private->incoming.finished = true;
+ module_put(flb->ops->owner);
}
}
}
@@ -394,11 +406,6 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
goto err_resume;
}
- if (!try_module_get(flb->ops->owner)) {
- err = -EAGAIN;
- goto err_resume;
- }
-
list_add_tail(&private->list, &luo_flb_global.list);
luo_flb_global.count++;
}
@@ -474,12 +481,11 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
private->users--;
/*
* If this is the last file-handler with which we are registred, remove
- * from the global list, and relese module reference.
+ * from the global list.
*/
if (!private->users) {
list_del_init(&private->list);
luo_flb_global.count--;
- module_put(flb->ops->owner);
}
luo_session_resume();
@@ -506,7 +512,8 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
*
* Return: 0 on success, or a negative errno on failure. -ENODATA means no
* incoming FLB data, -ENOENT means specific flb not found in the incoming
- * data, and -EOPNOTSUPP when live update is disabled or not configured.
+ * data, -ENODEV if the FLB's module is unloading, and -EOPNOTSUPP when
+ * live update is disabled or not configured.
*/
int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp)
{
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 5/8] liveupdate: Remove luo_session_quiesce()
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
` (3 preceding siblings ...)
2026-03-18 14:16 ` [PATCH v2 4/8] liveupdate: Defer FLB module refcounting to active sessions Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 6/8] liveupdate: Auto unregister FLBs on file handler unregistration Pasha Tatashin
` (2 subsequent siblings)
7 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
Now that FLB module references are handled dynamically during active
sessions, we can safely remove the luo_session_quiesce() and
luo_session_resume() mechanism.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 21 ++-------------
kernel/liveupdate/luo_flb.c | 46 +++++---------------------------
kernel/liveupdate/luo_internal.h | 2 --
kernel/liveupdate/luo_session.c | 44 ------------------------------
4 files changed, 9 insertions(+), 104 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index b124fd747841..c0ce08d55747 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -846,21 +846,12 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
return -EINVAL;
}
- /*
- * Ensure the system is quiescent (no active sessions).
- * This prevents registering new handlers while sessions are active or
- * while deserialization is in progress.
- */
- if (!luo_session_quiesce())
- return -EBUSY;
-
scoped_guard(rwsem_write, &luo_file_handler_lock) {
/* Check for duplicate compatible strings */
list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
if (!strcmp(fh_iter->compatible, fh->compatible)) {
pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
fh->compatible);
- luo_session_resume();
return -EEXIST;
}
}
@@ -870,7 +861,6 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list));
list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
}
- luo_session_resume();
liveupdate_test_register(fh);
@@ -885,13 +875,12 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
* reverses the operations of liveupdate_register_file_handler().
*
* It ensures safe removal by checking that:
- * No live update session is currently in progress.
* No FLB registered with this file handler.
*
* If the unregistration fails, the internal test state is reverted.
*
* Return: 0 Success. -EOPNOTSUPP when live update is not enabled. -EBUSY A live
- * update is in progress, can't quiesce live update or FLB is registred with
+ * update is in progress, FLB is registred with
* this file handler.
*/
int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
@@ -901,18 +890,12 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
liveupdate_test_unregister(fh);
- if (!luo_session_quiesce())
- goto err_register;
-
scoped_guard(rwsem_write, &luo_file_handler_lock) {
- if (!list_empty(&ACCESS_PRIVATE(fh, flb_list))) {
- luo_session_resume();
+ if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
goto err_register;
- }
list_del(&ACCESS_PRIVATE(fh, list));
}
- luo_session_resume();
return 0;
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 98336cd23aa5..8bbe11a7286b 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -346,7 +346,6 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
struct luo_flb_link *link __free(kfree) = NULL;
struct liveupdate_flb *gflb;
struct luo_flb_link *iter;
- int err;
if (!liveupdate_enabled())
return -EOPNOTSUPP;
@@ -367,22 +366,13 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
if (!link)
return -ENOMEM;
- /*
- * Ensure the system is quiescent (no active sessions).
- * This acts as a global lock for registration: no other thread can
- * be in this section, and no sessions can be creating/using FDs.
- */
- if (!luo_session_quiesce())
- return -EBUSY;
-
guard(rwsem_write)(&luo_flb_lock);
guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
/* Check that this FLB is not already linked to this file handler */
- err = -EEXIST;
list_for_each_entry(iter, flb_list, list) {
if (iter->flb == flb)
- goto err_resume;
+ return -EEXIST;
}
/*
@@ -390,20 +380,16 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
* is registered
*/
if (!private->users) {
- if (WARN_ON(!list_empty(&private->list))) {
- err = -EINVAL;
- goto err_resume;
- }
+ if (WARN_ON(!list_empty(&private->list)))
+ return -EINVAL;
- if (luo_flb_global.count == LUO_FLB_MAX) {
- err = -ENOSPC;
- goto err_resume;
- }
+ if (luo_flb_global.count == LUO_FLB_MAX)
+ return -ENOSPC;
/* Check that compatible string is unique in global list */
list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) {
if (!strcmp(gflb->compatible, flb->compatible))
- goto err_resume;
+ return -EEXIST;
}
list_add_tail(&private->list, &luo_flb_global.list);
@@ -414,13 +400,8 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
private->users++;
link->flb = flb;
list_add_tail(&no_free_ptr(link)->list, flb_list);
- luo_session_resume();
return 0;
-
-err_resume:
- luo_session_resume();
- return err;
}
/**
@@ -455,13 +436,6 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
if (!liveupdate_enabled())
return -EOPNOTSUPP;
- /*
- * Ensure the system is quiescent (no active sessions).
- * This acts as a global lock for unregistration.
- */
- if (!luo_session_quiesce())
- return -EBUSY;
-
guard(rwsem_write)(&luo_flb_lock);
guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
@@ -476,7 +450,7 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
}
if (err)
- goto err_resume;
+ return err;
private->users--;
/*
@@ -488,13 +462,7 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
luo_flb_global.count--;
}
- luo_session_resume();
-
return 0;
-
-err_resume:
- luo_session_resume();
- return err;
}
/**
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 8083d8739b09..ec949f91c8c1 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -83,8 +83,6 @@ int __init luo_session_setup_outgoing(void *fdt);
int __init luo_session_setup_incoming(void *fdt);
int luo_session_serialize(void);
int luo_session_deserialize(void);
-bool luo_session_quiesce(void);
-void luo_session_resume(void);
int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd);
void luo_file_unpreserve_files(struct luo_file_set *file_set);
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 783677295640..067ffb54c36a 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -600,47 +600,3 @@ int luo_session_serialize(void)
return err;
}
-
-/**
- * luo_session_quiesce - Ensure no active sessions exist and lock session lists.
- *
- * Acquires exclusive write locks on both incoming and outgoing session lists.
- * It then validates no sessions exist in either list.
- *
- * This mechanism is used during file handler un/registration to ensure that no
- * sessions are currently using the handler, and no new sessions can be created
- * while un/registration is in progress.
- *
- * This prevents registering new handlers while sessions are active or
- * while deserialization is in progress.
- *
- * Return:
- * true - System is quiescent (0 sessions) and locked.
- * false - Active sessions exist. The locks are released internally.
- */
-bool luo_session_quiesce(void)
-{
- down_write(&luo_session_global.incoming.rwsem);
- down_write(&luo_session_global.outgoing.rwsem);
-
- if (luo_session_global.incoming.count ||
- luo_session_global.outgoing.count) {
- up_write(&luo_session_global.outgoing.rwsem);
- up_write(&luo_session_global.incoming.rwsem);
- return false;
- }
-
- return true;
-}
-
-/**
- * luo_session_resume - Unlock session lists and resume normal activity.
- *
- * Releases the exclusive locks acquired by a successful call to
- * luo_session_quiesce().
- */
-void luo_session_resume(void)
-{
- up_write(&luo_session_global.outgoing.rwsem);
- up_write(&luo_session_global.incoming.rwsem);
-}
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 6/8] liveupdate: Auto unregister FLBs on file handler unregistration
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
` (4 preceding siblings ...)
2026-03-18 14:16 ` [PATCH v2 5/8] liveupdate: Remove luo_session_quiesce() Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 7/8] liveupdate: Remove liveupdate_test_unregister() Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 8/8] liveupdate: Make unregister functions return void Pasha Tatashin
7 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
To ensure that unregistration is always successful and doesn't leave
dangling resources, introduce auto-unregistration of FLBs: when a file
handler is unregistered, all FLBs associated with it are automatically
unregistered.
Introduce a new helper luo_flb_unregister_all() which unregisters all
FLBs linked to the given file handler.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 17 +-----
kernel/liveupdate/luo_flb.c | 93 ++++++++++++++++++++------------
kernel/liveupdate/luo_internal.h | 1 +
3 files changed, 61 insertions(+), 50 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index c0ce08d55747..b9ba1b8dce84 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -873,15 +873,6 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
*
* Unregisters the file handler from the liveupdate core. This function
* reverses the operations of liveupdate_register_file_handler().
- *
- * It ensures safe removal by checking that:
- * No FLB registered with this file handler.
- *
- * If the unregistration fails, the internal test state is reverted.
- *
- * Return: 0 Success. -EOPNOTSUPP when live update is not enabled. -EBUSY A live
- * update is in progress, FLB is registred with
- * this file handler.
*/
int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
{
@@ -891,15 +882,9 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
liveupdate_test_unregister(fh);
scoped_guard(rwsem_write, &luo_file_handler_lock) {
- if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
- goto err_register;
-
+ luo_flb_unregister_all(fh);
list_del(&ACCESS_PRIVATE(fh, list));
}
return 0;
-
-err_register:
- liveupdate_test_register(fh);
- return -EBUSY;
}
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 8bbe11a7286b..5b61c0844a49 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -316,6 +316,64 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh)
luo_flb_file_finish_one(iter->flb);
}
+static void luo_flb_unregister_one(struct liveupdate_file_handler *fh,
+ struct liveupdate_flb *flb)
+{
+ struct luo_flb_private *private = luo_flb_get_private(flb);
+ struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
+ struct luo_flb_link *iter;
+ bool found = false;
+
+ /* Find and remove the link from the file handler's list */
+ list_for_each_entry(iter, flb_list, list) {
+ if (iter->flb == flb) {
+ list_del(&iter->list);
+ kfree(iter);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ pr_warn("Failed to unregister FLB '%s': not found in file handler '%s'\n",
+ flb->compatible, fh->compatible);
+ return;
+ }
+
+ private->users--;
+
+ /*
+ * If this is the last file-handler with which we are registred, remove
+ * from the global list.
+ */
+ if (!private->users) {
+ list_del_init(&private->list);
+ luo_flb_global.count--;
+ }
+}
+
+/**
+ * luo_flb_unregister_all - Unregister all FLBs associated with a file handler.
+ * @fh: The file handler whose FLBs should be unregistered.
+ *
+ * This function iterates through the list of FLBs associated with the given
+ * file handler and unregisters them all one by one.
+ */
+void luo_flb_unregister_all(struct liveupdate_file_handler *fh)
+{
+ struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
+ struct luo_flb_link *iter, *tmp;
+
+ if (!liveupdate_enabled())
+ return;
+
+ guard(rwsem_write)(&luo_flb_lock);
+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
+
+ list_for_each_entry_safe(iter, tmp, flb_list, list)
+ luo_flb_unregister_one(fh, iter->flb);
+}
+
/**
* liveupdate_register_flb - Associate an FLB with a file handler and register it globally.
* @fh: The file handler that will now depend on the FLB.
@@ -417,50 +475,17 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
* the FLB is removed from the global registry and the reference to its
* owner module (acquired during registration) is released.
*
- * Context: This function ensures the session is quiesced (no active FDs
- * being created) during the update. It is typically called from a
- * subsystem's module exit function.
- * Return: 0 on success.
- * -EOPNOTSUPP if live update is disabled.
- * -EBUSY if the live update session is active and cannot be quiesced.
- * -ENOENT if the FLB was not found in the file handler's list.
*/
int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
struct liveupdate_flb *flb)
{
- struct luo_flb_private *private = luo_flb_get_private(flb);
- struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
- struct luo_flb_link *iter;
- int err = -ENOENT;
-
if (!liveupdate_enabled())
return -EOPNOTSUPP;
guard(rwsem_write)(&luo_flb_lock);
guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
- /* Find and remove the link from the file handler's list */
- list_for_each_entry(iter, flb_list, list) {
- if (iter->flb == flb) {
- list_del(&iter->list);
- kfree(iter);
- err = 0;
- break;
- }
- }
-
- if (err)
- return err;
-
- private->users--;
- /*
- * If this is the last file-handler with which we are registred, remove
- * from the global list.
- */
- if (!private->users) {
- list_del_init(&private->list);
- luo_flb_global.count--;
- }
+ luo_flb_unregister_one(fh, flb);
return 0;
}
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index ec949f91c8c1..730c3faa7616 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -101,6 +101,7 @@ void luo_file_set_destroy(struct luo_file_set *file_set);
int luo_flb_file_preserve(struct liveupdate_file_handler *fh);
void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh);
void luo_flb_file_finish(struct liveupdate_file_handler *fh);
+void luo_flb_unregister_all(struct liveupdate_file_handler *fh);
int __init luo_flb_setup_outgoing(void *fdt);
int __init luo_flb_setup_incoming(void *fdt);
void luo_flb_serialize(void);
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 7/8] liveupdate: Remove liveupdate_test_unregister()
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
` (5 preceding siblings ...)
2026-03-18 14:16 ` [PATCH v2 6/8] liveupdate: Auto unregister FLBs on file handler unregistration Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 8/8] liveupdate: Make unregister functions return void Pasha Tatashin
7 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
Now that file handler unregistration automatically unregisters all
associated file handlers (FLBs), the liveupdate_test_unregister()
function is no longer needed. Remove it along with its usages
and declarations.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 2 --
kernel/liveupdate/luo_internal.h | 2 --
lib/tests/liveupdate.c | 18 ------------------
3 files changed, 22 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index b9ba1b8dce84..c9c26015020b 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -879,8 +879,6 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
if (!liveupdate_enabled())
return -EOPNOTSUPP;
- liveupdate_test_unregister(fh);
-
scoped_guard(rwsem_write, &luo_file_handler_lock) {
luo_flb_unregister_all(fh);
list_del(&ACCESS_PRIVATE(fh, list));
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 730c3faa7616..137ae317188a 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -108,10 +108,8 @@ void luo_flb_serialize(void);
#ifdef CONFIG_LIVEUPDATE_TEST
void liveupdate_test_register(struct liveupdate_file_handler *fh);
-void liveupdate_test_unregister(struct liveupdate_file_handler *fh);
#else
static inline void liveupdate_test_register(struct liveupdate_file_handler *fh) { }
-static inline void liveupdate_test_unregister(struct liveupdate_file_handler *fh) { }
#endif
#endif /* _LINUX_LUO_INTERNAL_H */
diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c
index 496d6ef91a30..e4b0ecbee32f 100644
--- a/lib/tests/liveupdate.c
+++ b/lib/tests/liveupdate.c
@@ -135,24 +135,6 @@ void liveupdate_test_register(struct liveupdate_file_handler *fh)
TEST_NFLBS, fh->compatible);
}
-void liveupdate_test_unregister(struct liveupdate_file_handler *fh)
-{
- int err, i;
-
- for (i = 0; i < TEST_NFLBS; i++) {
- struct liveupdate_flb *flb = &test_flbs[i];
-
- err = liveupdate_unregister_flb(fh, flb);
- if (err) {
- pr_err("Failed to unregister %s %pe\n",
- flb->compatible, ERR_PTR(err));
- }
- }
-
- pr_info("Unregistered %d FLBs from file handler: [%s]\n",
- TEST_NFLBS, fh->compatible);
-}
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pasha Tatashin <pasha.tatashin@soleen.com>");
MODULE_DESCRIPTION("In-kernel test for LUO mechanism");
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 8/8] liveupdate: Make unregister functions return void
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
` (6 preceding siblings ...)
2026-03-18 14:16 ` [PATCH v2 7/8] liveupdate: Remove liveupdate_test_unregister() Pasha Tatashin
@ 2026-03-18 14:16 ` Pasha Tatashin
7 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-18 14:16 UTC (permalink / raw)
To: rppt, akpm, linux-mm, linux-kernel, pasha.tatashin, dmatlack,
pratyush
Change liveupdate_unregister_file_handler and liveupdate_unregister_flb
to return void instead of an error code. This follows the design
principle that unregistration during module unload should not fail,
as the unload cannot be stopped at that point.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/liveupdate.h | 14 ++++++--------
kernel/liveupdate/luo_file.c | 6 ++----
kernel/liveupdate/luo_flb.c | 8 +++-----
3 files changed, 11 insertions(+), 17 deletions(-)
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index 8394fb2d8774..636175432d62 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -231,12 +231,12 @@ bool liveupdate_enabled(void);
int liveupdate_reboot(void);
int liveupdate_register_file_handler(struct liveupdate_file_handler *fh);
-int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh);
+void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh);
int liveupdate_register_flb(struct liveupdate_file_handler *fh,
struct liveupdate_flb *flb);
-int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
- struct liveupdate_flb *flb);
+void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
+ struct liveupdate_flb *flb);
int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp);
int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp);
@@ -258,9 +258,8 @@ static inline int liveupdate_register_file_handler(struct liveupdate_file_handle
return -EOPNOTSUPP;
}
-static inline int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
+static inline void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
{
- return -EOPNOTSUPP;
}
static inline int liveupdate_register_flb(struct liveupdate_file_handler *fh,
@@ -269,10 +268,9 @@ static inline int liveupdate_register_flb(struct liveupdate_file_handler *fh,
return -EOPNOTSUPP;
}
-static inline int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
- struct liveupdate_flb *flb)
+static inline void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
+ struct liveupdate_flb *flb)
{
- return -EOPNOTSUPP;
}
static inline int liveupdate_flb_get_incoming(struct liveupdate_flb *flb,
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index c9c26015020b..a38ea4975824 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -874,15 +874,13 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
* Unregisters the file handler from the liveupdate core. This function
* reverses the operations of liveupdate_register_file_handler().
*/
-int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
+void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
{
if (!liveupdate_enabled())
- return -EOPNOTSUPP;
+ return;
scoped_guard(rwsem_write, &luo_file_handler_lock) {
luo_flb_unregister_all(fh);
list_del(&ACCESS_PRIVATE(fh, list));
}
-
- return 0;
}
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 5b61c0844a49..874830f8e44d 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -476,18 +476,16 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
* owner module (acquired during registration) is released.
*
*/
-int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
- struct liveupdate_flb *flb)
+void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
+ struct liveupdate_flb *flb)
{
if (!liveupdate_enabled())
- return -EOPNOTSUPP;
+ return;
guard(rwsem_write)(&luo_flb_lock);
guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
luo_flb_unregister_one(fh, flb);
-
- return 0;
}
/**
--
2.53.0.851.ga537e3e6e9-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/8] liveupdate: Protect FLB lists with rwsem
2026-03-18 14:16 ` [PATCH v2 2/8] liveupdate: Protect FLB lists " Pasha Tatashin
@ 2026-03-20 0:21 ` Samiullah Khawaja
2026-03-20 1:04 ` Pasha Tatashin
0 siblings, 1 reply; 18+ messages in thread
From: Samiullah Khawaja @ 2026-03-20 0:21 UTC (permalink / raw)
To: Pasha Tatashin; +Cc: rppt, akpm, linux-mm, linux-kernel, dmatlack, pratyush
On Wed, Mar 18, 2026 at 10:16:40AM -0400, Pasha Tatashin wrote:
>Because liveupdate FLB objects will soon drop their persistent module
>references when registered, list traversals must be protected against
>concurrent module unloading.
>
>Introduce two read-write semaphores to provide this protection:
>1. A global luo_flb_lock protects the global registry of FLBs.
>2. A per-handler flb_lock protects the handler's specific list of FLB
> dependencies.
>
>Read locks are used during concurrent list traversals (e.g., during
>preservation and serialization). Write locks are taken during registration
>and unregistration. When both locks are required, the global luo_flb_lock
>is strictly acquired before the per-handler flb_lock to prevent deadlocks.
>
>Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
>---
> include/linux/liveupdate.h | 3 +++
> kernel/liveupdate/luo_file.c | 1 +
> kernel/liveupdate/luo_flb.c | 16 ++++++++++++++++
> 3 files changed, 20 insertions(+)
>
>diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
>index dd11fdc76a5f..8394fb2d8774 100644
>--- a/include/linux/liveupdate.h
>+++ b/include/linux/liveupdate.h
>@@ -12,6 +12,7 @@
> #include <linux/kho/abi/luo.h>
> #include <linux/list.h>
> #include <linux/mutex.h>
>+#include <linux/rwsem.h>
> #include <linux/types.h>
> #include <uapi/linux/liveupdate.h>
>
>@@ -107,6 +108,8 @@ struct liveupdate_file_handler {
> struct list_head __private list;
> /* A list of FLB dependencies. */
> struct list_head __private flb_list;
>+ /* Protects flb_list */
>+ struct rw_semaphore __private flb_lock;
> };
>
> /**
>diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
>index 6a0ae29c6a24..96fdd5790dcc 100644
>--- a/kernel/liveupdate/luo_file.c
>+++ b/kernel/liveupdate/luo_file.c
>@@ -873,6 +873,7 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
> }
>
> INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, flb_list));
>+ init_rwsem(&ACCESS_PRIVATE(fh, flb_lock));
> INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list));
> list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
> }
>diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
>index f52e8114837e..91910d806d1d 100644
>--- a/kernel/liveupdate/luo_flb.c
>+++ b/kernel/liveupdate/luo_flb.c
>@@ -49,6 +49,7 @@
> #include <linux/liveupdate.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
>+#include <linux/rwsem.h>
> #include <linux/slab.h>
> #include <linux/unaligned.h>
> #include "luo_internal.h"
>@@ -70,6 +71,7 @@ struct luo_flb_global {
> long count;
> };
>
>+static DECLARE_RWSEM(luo_flb_lock);
> static struct luo_flb_global luo_flb_global = {
> .list = LIST_HEAD_INIT(luo_flb_global.list),
> };
>@@ -240,6 +242,8 @@ int luo_flb_file_preserve(struct liveupdate_file_handler *fh)
> struct luo_flb_link *iter;
> int err = 0;
>
>+ guard(rwsem_read)(&ACCESS_PRIVATE(fh, flb_lock));
>+
> list_for_each_entry(iter, flb_list, list) {
> err = luo_flb_file_preserve_one(iter->flb);
> if (err)
>@@ -272,6 +276,8 @@ void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh)
> struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
> struct luo_flb_link *iter;
>
>+ guard(rwsem_read)(&ACCESS_PRIVATE(fh, flb_lock));
>+
> list_for_each_entry_reverse(iter, flb_list, list)
> luo_flb_file_unpreserve_one(iter->flb);
> }
>@@ -292,6 +298,8 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh)
> struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
> struct luo_flb_link *iter;
>
>+ guard(rwsem_read)(&ACCESS_PRIVATE(fh, flb_lock));
>+
> list_for_each_entry_reverse(iter, flb_list, list)
> luo_flb_file_finish_one(iter->flb);
> }
>@@ -355,6 +363,9 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
> if (!luo_session_quiesce())
> return -EBUSY;
>
>+ guard(rwsem_write)(&luo_flb_lock);
>+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
Since FLBs are linked with file handlers and the file_handler can be
unregistered/registered while this is running, should the luo_file_lock
write be taken here? I think maybe we don't need a separate luo_flb_lock
and the luo_file_lock should provide enough protection if we acquire it
here, as a file_handler is supposed to be registered first and then flb
needs to be registered against it?
Maybe we can have one luo_register_lock?
>+
> /* Check that this FLB is not already linked to this file handler */
> err = -EEXIST;
> list_for_each_entry(iter, flb_list, list) {
>@@ -444,6 +455,9 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
> if (!luo_session_quiesce())
> return -EBUSY;
>
>+ guard(rwsem_write)(&luo_flb_lock);
>+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
>+
> /* Find and remove the link from the file handler's list */
> list_for_each_entry(iter, flb_list, list) {
> if (iter->flb == flb) {
>@@ -638,6 +652,8 @@ void luo_flb_serialize(void)
> struct liveupdate_flb *gflb;
> int i = 0;
>
>+ guard(rwsem_read)(&luo_flb_lock);
>+
> list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) {
> struct luo_flb_private *private = luo_flb_get_private(gflb);
>
>--
>2.53.0.851.ga537e3e6e9-goog
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/8] liveupdate: Protect FLB lists with rwsem
2026-03-20 0:21 ` Samiullah Khawaja
@ 2026-03-20 1:04 ` Pasha Tatashin
0 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-20 1:04 UTC (permalink / raw)
To: Samiullah Khawaja; +Cc: rppt, akpm, linux-mm, linux-kernel, dmatlack, pratyush
> >@@ -355,6 +363,9 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh,
> > if (!luo_session_quiesce())
> > return -EBUSY;
> >
> >+ guard(rwsem_write)(&luo_flb_lock);
> >+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
>
> Since FLBs are linked with file handlers and the file_handler can be
> unregistered/registered while this is running, should the luo_file_lock
> write be taken here? I think maybe we don't need a separate luo_flb_lock
Hi Sami,
Yes, thank you for catching this. I thought we were safe since we are
taking fh->flb_lock, but you are correct. I am going to do what you
suggested below and use a single luo_registration_lock for both FLB
and File Handler registrations. Reads will also be shared; this will
simplify locking.
Pasha
> and the luo_file_lock should provide enough protection if we acquire it
> here, as a file_handler is supposed to be registered first and then flb
> needs to be registered against it?
>
> Maybe we can have one luo_register_lock?
> >+
> > /* Check that this FLB is not already linked to this file handler */
> > err = -EEXIST;
> > list_for_each_entry(iter, flb_list, list) {
> >@@ -444,6 +455,9 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
> > if (!luo_session_quiesce())
> > return -EBUSY;
> >
> >+ guard(rwsem_write)(&luo_flb_lock);
> >+ guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock));
> >+
> > /* Find and remove the link from the file handler's list */
> > list_for_each_entry(iter, flb_list, list) {
> > if (iter->flb == flb) {
> >@@ -638,6 +652,8 @@ void luo_flb_serialize(void)
> > struct liveupdate_flb *gflb;
> > int i = 0;
> >
> >+ guard(rwsem_read)(&luo_flb_lock);
> >+
> > list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) {
> > struct luo_flb_private *private = luo_flb_get_private(gflb);
> >
> >--
> >2.53.0.851.ga537e3e6e9-goog
> >
> >
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem
2026-03-18 14:16 ` [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem Pasha Tatashin
@ 2026-03-22 13:13 ` Mike Rapoport
2026-03-22 14:23 ` Pasha Tatashin
0 siblings, 1 reply; 18+ messages in thread
From: Mike Rapoport @ 2026-03-22 13:13 UTC (permalink / raw)
To: Pasha Tatashin; +Cc: akpm, linux-mm, linux-kernel, dmatlack, pratyush
Hi Pasha,
On Wed, Mar 18, 2026 at 10:16:39AM -0400, Pasha Tatashin wrote:
> Because liveupdate file handlers will no longer hold a
> module reference when registered, we must ensure that the access to
> the handler list is protected against concurrent module unloading.
>
> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> ---
> kernel/liveupdate/luo_file.c | 61 +++++++++++++++++++++---------------
> 1 file changed, 35 insertions(+), 26 deletions(-)
>
> diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
> index 5acee4174bf0..6a0ae29c6a24 100644
> --- a/kernel/liveupdate/luo_file.c
> +++ b/kernel/liveupdate/luo_file.c
> @@ -112,6 +112,7 @@
> #include <linux/string.h>
> #include "luo_internal.h"
>
> +static DECLARE_RWSEM(luo_file_handler_lock);
> static LIST_HEAD(luo_file_handler_list);
>
> /* 2 4K pages, give space for 128 files per file_set */
> @@ -277,10 +278,12 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
> goto err_fput;
>
> err = -ENOENT;
> - list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> - if (fh->ops->can_preserve(fh, file)) {
> - err = 0;
> - break;
> + scoped_guard(rwsem_read, &luo_file_handler_lock) {
I'm not sure scoped_guard() here is better that lock before the loop and
unlock after. scoped_guard() would be useful if we were returning from the
guarded scope.
> + list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> + if (fh->ops->can_preserve(fh, file)) {
> + err = 0;
> + break;
> + }
> }
> }
>
> @@ -777,10 +780,12 @@ int luo_file_deserialize(struct luo_file_set *file_set,
> bool handler_found = false;
> struct luo_file *luo_file;
>
> - list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> - if (!strcmp(fh->compatible, file_ser[i].compatible)) {
> - handler_found = true;
> - break;
> + scoped_guard(rwsem_read, &luo_file_handler_lock) {
> + list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> + if (!strcmp(fh->compatible, file_ser[i].compatible)) {
> + handler_found = true;
> + break;
> + }
Ditto.
> }
> }
>
> @@ -850,25 +855,27 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
> if (!luo_session_quiesce())
> return -EBUSY;
>
> - /* Check for duplicate compatible strings */
> - list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
> - if (!strcmp(fh_iter->compatible, fh->compatible)) {
> - pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
> - fh->compatible);
> - err = -EEXIST;
> + scoped_guard(rwsem_write, &luo_file_handler_lock) {
> + /* Check for duplicate compatible strings */
> + list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
> + if (!strcmp(fh_iter->compatible, fh->compatible)) {
> + pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
> + fh->compatible);
> + err = -EEXIST;
> + goto err_resume;
PeterZ advised against mixing guards() with goto, but unfortunately I can't
find lore link right now.
> + }
> + }
> +
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem
2026-03-22 13:13 ` Mike Rapoport
@ 2026-03-22 14:23 ` Pasha Tatashin
0 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-22 14:23 UTC (permalink / raw)
To: Mike Rapoport; +Cc: akpm, linux-mm, linux-kernel, dmatlack, pratyush
On Sun, Mar 22, 2026 at 9:13 AM Mike Rapoport <rppt@kernel.org> wrote:
>
> Hi Pasha,
>
> On Wed, Mar 18, 2026 at 10:16:39AM -0400, Pasha Tatashin wrote:
> > Because liveupdate file handlers will no longer hold a
> > module reference when registered, we must ensure that the access to
> > the handler list is protected against concurrent module unloading.
> >
> > Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > ---
> > kernel/liveupdate/luo_file.c | 61 +++++++++++++++++++++---------------
> > 1 file changed, 35 insertions(+), 26 deletions(-)
> >
> > diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
> > index 5acee4174bf0..6a0ae29c6a24 100644
> > --- a/kernel/liveupdate/luo_file.c
> > +++ b/kernel/liveupdate/luo_file.c
> > @@ -112,6 +112,7 @@
> > #include <linux/string.h>
> > #include "luo_internal.h"
> >
> > +static DECLARE_RWSEM(luo_file_handler_lock);
> > static LIST_HEAD(luo_file_handler_list);
> >
> > /* 2 4K pages, give space for 128 files per file_set */
> > @@ -277,10 +278,12 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
> > goto err_fput;
> >
> > err = -ENOENT;
> > - list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> > - if (fh->ops->can_preserve(fh, file)) {
> > - err = 0;
> > - break;
> > + scoped_guard(rwsem_read, &luo_file_handler_lock) {
>
> I'm not sure scoped_guard() here is better that lock before the loop and
> unlock after. scoped_guard() would be useful if we were returning from the
> guarded scope.
>
> > + list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> > + if (fh->ops->can_preserve(fh, file)) {
> > + err = 0;
> > + break;
> > + }
> > }
> > }
> >
> > @@ -777,10 +780,12 @@ int luo_file_deserialize(struct luo_file_set *file_set,
> > bool handler_found = false;
> > struct luo_file *luo_file;
> >
> > - list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> > - if (!strcmp(fh->compatible, file_ser[i].compatible)) {
> > - handler_found = true;
> > - break;
> > + scoped_guard(rwsem_read, &luo_file_handler_lock) {
> > + list_private_for_each_entry(fh, &luo_file_handler_list, list) {
> > + if (!strcmp(fh->compatible, file_ser[i].compatible)) {
> > + handler_found = true;
> > + break;
> > + }
>
> Ditto.
>
> > }
> > }
> >
> > @@ -850,25 +855,27 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
> > if (!luo_session_quiesce())
> > return -EBUSY;
> >
> > - /* Check for duplicate compatible strings */
> > - list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
> > - if (!strcmp(fh_iter->compatible, fh->compatible)) {
> > - pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
> > - fh->compatible);
> > - err = -EEXIST;
> > + scoped_guard(rwsem_write, &luo_file_handler_lock) {
> > + /* Check for duplicate compatible strings */
> > + list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) {
> > + if (!strcmp(fh_iter->compatible, fh->compatible)) {
> > + pr_err("File handler registration failed: Compatible string '%s' already registered.\n",
> > + fh->compatible);
> > + err = -EEXIST;
> > + goto err_resume;
>
> PeterZ advised against mixing guards() with goto, but unfortunately I can't
> find lore link right now.
Yeah, I was looking into this yesterday:
https://lore.kernel.org/all/CA+CK2bD2aNu-NByET4KkBP0n2j8WQE2zjxb4g-1cv2hYfjaRZQ@mail.gmail.com
Also, please wait for the next version before reviewing it. I am going
to use what Sami suggested:
https://lore.kernel.org/all/CA+CK2bDONAnry0n9qOsR7GSDTw4Xw3g4ZugRPwXD+NdMx7rpsQ@mail.gmail.com
Pasha
>
> > + }
> > + }
> > +
>
> --
> Sincerely yours,
> Mike.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/8] liveupdate: Remove file handler module refcounting
2026-03-18 14:16 ` [PATCH v2 3/8] liveupdate: Remove file handler module refcounting Pasha Tatashin
@ 2026-03-24 21:15 ` David Matlack
2026-03-24 21:23 ` David Matlack
1 sibling, 0 replies; 18+ messages in thread
From: David Matlack @ 2026-03-24 21:15 UTC (permalink / raw)
To: Pasha Tatashin; +Cc: rppt, akpm, linux-mm, linux-kernel, pratyush
On 2026-03-18 10:16 AM, Pasha Tatashin wrote:
> File handlers do not need to pin modules indefinitely or during active
> live update sessions. The VFS 'struct file' pins the file handler's module
> via f_op->owner during active sessions, making dynamic reference counting
> unnecessary for handlers.
>
> When a file is preserved, the live update core obtains a 'struct file'
> via fdget(). As long as the file is kept open within the live update
> session, the module is pinned by the VFS and cannot be unloaded.
>
> Similarly, during deserialization, file handlers are matched based on
> the compatible string. Because the handler list is protected by
> luo_file_handler_lock, there is no race that requires dynamic
> module refcounting.
Sashiko found a potential bug here when reviewing my VFIO patch series:
. If luo_file_deserialize() reconstructs preserved file structures and
. assigns the handler to luo_file->fh without calling try_module_get()
. to lock the module in memory, could the module be unloaded before the
. file descriptor is actually retrieved?
.
. This would cause liveupdate_unregister_file_handler() to run on module exit.
. If userspace subsequently calls luo_retrieve_file(), could it result
. in a use-after-free by dereferencing the dangling luo_file->fh->ops pointer?
https://sashiko.dev/#/patchset/20260323235817.1960573-1-dmatlack%40google.com?patch=7973
I think LUO would need to take a module reference in
luo_file_deserialize() and drop it once the file is retrieved. At that
point LUO can rely on the file's reference to the module to keep it from
being unloaded while LUO still has references to it.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/8] liveupdate: Remove file handler module refcounting
2026-03-18 14:16 ` [PATCH v2 3/8] liveupdate: Remove file handler module refcounting Pasha Tatashin
2026-03-24 21:15 ` David Matlack
@ 2026-03-24 21:23 ` David Matlack
2026-03-25 2:49 ` Pasha Tatashin
1 sibling, 1 reply; 18+ messages in thread
From: David Matlack @ 2026-03-24 21:23 UTC (permalink / raw)
To: Pasha Tatashin; +Cc: rppt, akpm, linux-mm, linux-kernel, pratyush
On Wed, Mar 18, 2026 at 7:17 AM Pasha Tatashin
<pasha.tatashin@soleen.com> wrote:
>
> File handlers do not need to pin modules indefinitely or during active
> live update sessions. The VFS 'struct file' pins the file handler's module
> via f_op->owner during active sessions, making dynamic reference counting
> unnecessary for handlers.
>
> When a file is preserved, the live update core obtains a 'struct file'
> via fdget(). As long as the file is kept open within the live update
> session, the module is pinned by the VFS and cannot be unloaded.
After invoking the file handler's retrieve(), LUO should probably
check that the created file's owner matches the file handler's owner,
since this scheme relies on that being true.
If there is a mismatch, LUO can put the file that was just created,
log a warning, and return an error up to the user.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/8] liveupdate: Remove file handler module refcounting
2026-03-24 21:23 ` David Matlack
@ 2026-03-25 2:49 ` Pasha Tatashin
2026-03-25 13:14 ` David Matlack
0 siblings, 1 reply; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-25 2:49 UTC (permalink / raw)
To: David Matlack; +Cc: rppt, akpm, linux-mm, linux-kernel, pratyush
On Tue, Mar 24, 2026 at 5:24 PM David Matlack <dmatlack@google.com> wrote:
>
> On Wed, Mar 18, 2026 at 7:17 AM Pasha Tatashin
> <pasha.tatashin@soleen.com> wrote:
> >
> > File handlers do not need to pin modules indefinitely or during active
> > live update sessions. The VFS 'struct file' pins the file handler's module
> > via f_op->owner during active sessions, making dynamic reference counting
> > unnecessary for handlers.
> >
> > When a file is preserved, the live update core obtains a 'struct file'
> > via fdget(). As long as the file is kept open within the live update
> > session, the module is pinned by the VFS and cannot be unloaded.
>
> After invoking the file handler's retrieve(), LUO should probably
> check that the created file's owner matches the file handler's owner,
> since this scheme relies on that being true.
>
> If there is a mismatch, LUO can put the file that was just created,
> log a warning, and return an error up to the user.
Is there a reason why taking a file handler module reference is
problematic for vfio or iommu? Could we take it while files are
present in incoming or outgoing sessions? Overall, it is because it
cover corener cases such as if the file struct owner is the same as
LUO file handler and also this approach covers the deserialziation
side nicely.
Pasha
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/8] liveupdate: Remove file handler module refcounting
2026-03-25 2:49 ` Pasha Tatashin
@ 2026-03-25 13:14 ` David Matlack
2026-03-25 13:43 ` Pasha Tatashin
0 siblings, 1 reply; 18+ messages in thread
From: David Matlack @ 2026-03-25 13:14 UTC (permalink / raw)
To: Pasha Tatashin; +Cc: rppt, akpm, linux-mm, linux-kernel, pratyush
On Tue, Mar 24, 2026 at 7:49 PM Pasha Tatashin
<pasha.tatashin@soleen.com> wrote:
>
> On Tue, Mar 24, 2026 at 5:24 PM David Matlack <dmatlack@google.com> wrote:
> >
> > On Wed, Mar 18, 2026 at 7:17 AM Pasha Tatashin
> > <pasha.tatashin@soleen.com> wrote:
> > >
> > > File handlers do not need to pin modules indefinitely or during active
> > > live update sessions. The VFS 'struct file' pins the file handler's module
> > > via f_op->owner during active sessions, making dynamic reference counting
> > > unnecessary for handlers.
> > >
> > > When a file is preserved, the live update core obtains a 'struct file'
> > > via fdget(). As long as the file is kept open within the live update
> > > session, the module is pinned by the VFS and cannot be unloaded.
> >
> > After invoking the file handler's retrieve(), LUO should probably
> > check that the created file's owner matches the file handler's owner,
> > since this scheme relies on that being true.
> >
> > If there is a mismatch, LUO can put the file that was just created,
> > log a warning, and return an error up to the user.
>
> Is there a reason why taking a file handler module reference is
> problematic for vfio or iommu? Could we take it while files are
> present in incoming or outgoing sessions? Overall, it is because it
> cover corener cases such as if the file struct owner is the same as
> LUO file handler and also this approach covers the deserialziation
> side nicely.
I see no problem with LUO taking a reference to the file handle module
owner while a file associated with it is preserved in an incoming or
outgoing session.
And I realized that VFIO can have different owners for the file (vfio
module) and file handler (vfio-pci module), as it is currently
implemented. It should still be safe to rely on the file reference,
but explicitly taking a reference to the file handler would be simpler
to reason about. So I am ok with going back to what you had in v1 [1].
[1] https://lore.kernel.org/lkml/20260317025049.494931-4-pasha.tatashin@soleen.com/
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/8] liveupdate: Remove file handler module refcounting
2026-03-25 13:14 ` David Matlack
@ 2026-03-25 13:43 ` Pasha Tatashin
0 siblings, 0 replies; 18+ messages in thread
From: Pasha Tatashin @ 2026-03-25 13:43 UTC (permalink / raw)
To: David Matlack; +Cc: rppt, akpm, linux-mm, linux-kernel, pratyush
On Wed, Mar 25, 2026 at 9:15 AM David Matlack <dmatlack@google.com> wrote:
>
> On Tue, Mar 24, 2026 at 7:49 PM Pasha Tatashin
> <pasha.tatashin@soleen.com> wrote:
> >
> > On Tue, Mar 24, 2026 at 5:24 PM David Matlack <dmatlack@google.com> wrote:
> > >
> > > On Wed, Mar 18, 2026 at 7:17 AM Pasha Tatashin
> > > <pasha.tatashin@soleen.com> wrote:
> > > >
> > > > File handlers do not need to pin modules indefinitely or during active
> > > > live update sessions. The VFS 'struct file' pins the file handler's module
> > > > via f_op->owner during active sessions, making dynamic reference counting
> > > > unnecessary for handlers.
> > > >
> > > > When a file is preserved, the live update core obtains a 'struct file'
> > > > via fdget(). As long as the file is kept open within the live update
> > > > session, the module is pinned by the VFS and cannot be unloaded.
> > >
> > > After invoking the file handler's retrieve(), LUO should probably
> > > check that the created file's owner matches the file handler's owner,
> > > since this scheme relies on that being true.
> > >
> > > If there is a mismatch, LUO can put the file that was just created,
> > > log a warning, and return an error up to the user.
> >
> > Is there a reason why taking a file handler module reference is
> > problematic for vfio or iommu? Could we take it while files are
> > present in incoming or outgoing sessions? Overall, it is because it
> > cover corener cases such as if the file struct owner is the same as
> > LUO file handler and also this approach covers the deserialziation
> > side nicely.
>
> I see no problem with LUO taking a reference to the file handle module
> owner while a file associated with it is preserved in an incoming or
> outgoing session.
>
> And I realized that VFIO can have different owners for the file (vfio
> module) and file handler (vfio-pci module), as it is currently
> implemented. It should still be safe to rely on the file reference,
> but explicitly taking a reference to the file handler would be simpler
> to reason about. So I am ok with going back to what you had in v1 [1].
Great, I will include this in v3, together with Sami's suggestion for
lock simplifications.
Pasha
>
> [1] https://lore.kernel.org/lkml/20260317025049.494931-4-pasha.tatashin@soleen.com/
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-03-25 13:44 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18 14:16 [PATCH v2 0/8] liveupdate: Fix module unloading and unregister API Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 1/8] liveupdate: Protect file handler list with rwsem Pasha Tatashin
2026-03-22 13:13 ` Mike Rapoport
2026-03-22 14:23 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 2/8] liveupdate: Protect FLB lists " Pasha Tatashin
2026-03-20 0:21 ` Samiullah Khawaja
2026-03-20 1:04 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 3/8] liveupdate: Remove file handler module refcounting Pasha Tatashin
2026-03-24 21:15 ` David Matlack
2026-03-24 21:23 ` David Matlack
2026-03-25 2:49 ` Pasha Tatashin
2026-03-25 13:14 ` David Matlack
2026-03-25 13:43 ` Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 4/8] liveupdate: Defer FLB module refcounting to active sessions Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 5/8] liveupdate: Remove luo_session_quiesce() Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 6/8] liveupdate: Auto unregister FLBs on file handler unregistration Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 7/8] liveupdate: Remove liveupdate_test_unregister() Pasha Tatashin
2026-03-18 14:16 ` [PATCH v2 8/8] liveupdate: Make unregister functions return void Pasha Tatashin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox