Because liveupdate file handlers will eventually 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 --- 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 #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 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 --- include/linux/liveupdate.h | 3 +++ kernel/liveupdate/luo_flb.c | 16 ++++++++++++++++ 2 files changed, 19 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 #include #include +#include #include #include @@ -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_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 #include #include +#include #include #include #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 Stop pinning modules indefinitely upon file handler registration. Instead, dynamically increment the module reference count only when a live update session actively uses the file handler (e.g., during preservation or deserialization), and release it when the session ends. This allows modules providing live update handlers to be gracefully unloaded when no live update is in progress. Signed-off-by: Pasha Tatashin --- kernel/liveupdate/luo_file.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 6a0ae29c6a24..6b2b49cb375e 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -253,6 +253,7 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token) * -ENOSPC if the file_set is full. * -ENOENT if no compatible handler is found. * -ENOMEM on memory allocation failure. + * -ENODEV if the file handler's module is unloading. * Other erros might be returned by .preserve(). */ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) @@ -281,7 +282,10 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) 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; + if (try_module_get(fh->ops->owner)) + err = 0; + else + err = -ENODEV; break; } } @@ -293,7 +297,7 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) err = luo_flb_file_preserve(fh); if (err) - goto err_free_files_mem; + goto err_module_put; luo_file = kzalloc_obj(*luo_file); if (!luo_file) { @@ -323,6 +327,8 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) kfree(luo_file); err_flb_unpreserve: luo_flb_file_unpreserve(fh); +err_module_put: + module_put(fh->ops->owner); err_free_files_mem: luo_free_files_mem(file_set); err_fput: @@ -365,6 +371,7 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set) args.private_data = luo_file->private_data; luo_file->fh->ops->unpreserve(&args); luo_flb_file_unpreserve(luo_file->fh); + module_put(luo_file->fh->ops->owner); list_del(&luo_file->list); file_set->count--; @@ -649,6 +656,7 @@ static void luo_file_finish_one(struct luo_file_set *file_set, luo_file->fh->ops->finish(&args); luo_flb_file_finish(luo_file->fh); + module_put(luo_file->fh->ops->owner); } /** @@ -783,7 +791,8 @@ int luo_file_deserialize(struct luo_file_set *file_set, 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; + if (try_module_get(fh->ops->owner)) + handler_found = true; break; } } @@ -796,8 +805,10 @@ int luo_file_deserialize(struct luo_file_set *file_set, } luo_file = kzalloc_obj(*luo_file); - if (!luo_file) + if (!luo_file) { + module_put(fh->ops->owner); return -ENOMEM; + } luo_file->fh = fh; luo_file->file = NULL; @@ -873,6 +884,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); } @@ -922,7 +934,6 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) list_del(&ACCESS_PRIVATE(fh, list)); } - module_put(fh->ops->owner); luo_session_resume(); return 0; -- 2.53.0.851.ga537e3e6e9-goog 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 --- kernel/liveupdate/luo_flb.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 91910d806d1d..daa852abdedd 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++; } @@ -479,7 +486,6 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh, 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 Change liveupdate_unregister_file_handler and liveupdate_unregister_flb to return void instead of an error code. If they fail to unregister due to dangling references, print a warning instead of aborting. As pointed out by Jason Gunthorpe and Alex Williamson, returning an error from a "destroy" or unregister function during a module unload is a poor API choice since the unload cannot be stopped at that point. Signed-off-by: Pasha Tatashin --- include/linux/liveupdate.h | 13 ++++++------ kernel/liveupdate/luo_file.c | 27 +++++++++++------------- kernel/liveupdate/luo_flb.c | 40 ++++++++++-------------------------- lib/tests/liveupdate.c | 8 ++------ 4 files changed, 31 insertions(+), 57 deletions(-) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 8394fb2d8774..293df26b9e7f 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,8 +268,8 @@ 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; } diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 6b2b49cb375e..0fe2f8be8bd1 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -907,21 +907,17 @@ 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 - * this file handler. */ -int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) +void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) { - int err = -EBUSY; + bool is_empty; if (!liveupdate_enabled()) - return -EOPNOTSUPP; + return; liveupdate_test_unregister(fh); @@ -929,18 +925,19 @@ 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; - - list_del(&ACCESS_PRIVATE(fh, list)); + is_empty = list_empty(&ACCESS_PRIVATE(fh, flb_list)); + if (is_empty) + list_del(&ACCESS_PRIVATE(fh, list)); } luo_session_resume(); - return 0; + if (!is_empty) { + pr_warn("Failed to unregister file handler '%s': FLB list not empty\n", + fh->compatible); + liveupdate_test_register(fh); + } + return; -err_resume: - luo_session_resume(); err_register: liveupdate_test_register(fh); - return err; } diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index daa852abdedd..23fa6e0c6083 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -436,31 +436,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) +void 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; + bool found = false; 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; + return; guard(rwsem_write)(&luo_flb_lock); guard(rwsem_write)(&ACCESS_PRIVATE(fh, flb_lock)); @@ -470,15 +456,19 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh, if (iter->flb == flb) { list_del(&iter->list); kfree(iter); - err = 0; + found = true; break; } } - if (err) - goto err_resume; + 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, and relese module reference. @@ -487,14 +477,6 @@ int liveupdate_unregister_flb(struct liveupdate_file_handler *fh, list_del_init(&private->list); luo_flb_global.count--; } - - luo_session_resume(); - - return 0; - -err_resume: - luo_session_resume(); - return err; } /** diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c index 496d6ef91a30..5b6abf779f87 100644 --- a/lib/tests/liveupdate.c +++ b/lib/tests/liveupdate.c @@ -137,16 +137,12 @@ void liveupdate_test_register(struct liveupdate_file_handler *fh) void liveupdate_test_unregister(struct liveupdate_file_handler *fh) { - int err, i; + int 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)); - } + liveupdate_unregister_flb(fh, flb); } pr_info("Unregistered %d FLBs from file handler: [%s]\n", -- 2.53.0.851.ga537e3e6e9-goog Now that module references properly handle the usage of file handlers and FLBs during active sessions, we can safely remove the luo_session_quiesce() and luo_session_resume() mechanism. Signed-off-by: Pasha Tatashin --- kernel/liveupdate/luo_file.c | 31 +++------------------- kernel/liveupdate/luo_flb.c | 31 +++++----------------- kernel/liveupdate/luo_internal.h | 2 -- kernel/liveupdate/luo_session.c | 44 -------------------------------- 4 files changed, 9 insertions(+), 99 deletions(-) diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 0fe2f8be8bd1..bfa0b4868746 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -847,7 +847,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; @@ -858,45 +857,29 @@ 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); - err = -EEXIST; - goto err_resume; + return -EEXIST; } } /* Pin the module implementing the handler */ - if (!try_module_get(fh->ops->owner)) { - err = -EAGAIN; - goto err_resume; - } + if (!try_module_get(fh->ops->owner)) + return -EAGAIN; 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); } - luo_session_resume(); liveupdate_test_register(fh); return 0; - -err_resume: - luo_session_resume(); - return err; } /** @@ -921,23 +904,15 @@ void 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) { is_empty = list_empty(&ACCESS_PRIVATE(fh, flb_list)); if (is_empty) list_del(&ACCESS_PRIVATE(fh, list)); } - luo_session_resume(); if (!is_empty) { pr_warn("Failed to unregister file handler '%s': FLB list not empty\n", fh->compatible); liveupdate_test_register(fh); } - return; - -err_register: - liveupdate_test_register(fh); } diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 23fa6e0c6083..8810595c672d 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; } /** 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