With incremental kho_block publication in place, LUO sessions can be published into sh->block_set individually as they are committed, rather than rebuilding the entire session serialization chain during reboot. Currently, luo_session_serialize() resets *sh->sessions_pa and serializes all outgoing sessions from scratch in a single pass at reboot time. Introduce luo_session_publish_one() to append a session's luo_session_ser entry via kho_block_set_it_append() and move the session onto sh->published_list. Update luo_session_serialize() to freeze all remaining unpublished sessions first (rolling back via luo_session_unfreeze_one() on failure) and then publish them in a second pass. Signed-off-by: Pasha Tatashin --- kernel/liveupdate/luo_session.c | 92 ++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 6dea35911181..caf0e85081e8 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -103,20 +103,30 @@ static DECLARE_RWSEM(luo_session_serialize_rwsem); /** * struct luo_session_header - Header struct for managing LUO sessions. - * @count: The number of sessions currently tracked in the @list. + * @count: The number of sessions currently tracked in the header. + * @published_count: The number of outgoing sessions currently published in + * @block_set. * @list: The head of the linked list of `struct luo_session` instances. + * @published_list: The head of the linked list of published outgoing + * sessions, ordered identically to their entries in + * @block_set. * @rwsem: A read-write semaphore providing synchronized access to the * session list and other fields in this structure. * @block_set: The set of serialization blocks. + * @it: Iterator tracking the next slot in @block_set for outgoing + * session serialization. * @sessions_pa: Points to the location of sessions_pa within struct luo_ser. * @active: Set to true when first initialized. If previous kernel did not * send session data, active stays false for incoming. */ struct luo_session_header { long count; + long published_count; struct list_head list; + struct list_head published_list; struct rw_semaphore rwsem; struct kho_block_set block_set; + struct kho_block_set_it it; u64 *sessions_pa; bool active; }; @@ -134,12 +144,14 @@ struct luo_session_global { static struct luo_session_global luo_session_global = { .incoming = { .list = LIST_HEAD_INIT(luo_session_global.incoming.list), + .published_list = LIST_HEAD_INIT(luo_session_global.incoming.published_list), .rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem), .block_set = KHO_BLOCK_SET_INIT(luo_session_global.incoming.block_set, sizeof(struct luo_session_ser)), }, .outgoing = { .list = LIST_HEAD_INIT(luo_session_global.outgoing.list), + .published_list = LIST_HEAD_INIT(luo_session_global.outgoing.published_list), .rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem), .block_set = KHO_BLOCK_SET_INIT(luo_session_global.outgoing.block_set, sizeof(struct luo_session_ser)), @@ -186,6 +198,10 @@ static int luo_session_insert(struct luo_session_header *sh, if (!strncmp(it->name, session->name, sizeof(it->name))) return -EEXIST; } + list_for_each_entry(it, &sh->published_list, list) { + if (!strncmp(it->name, session->name, sizeof(it->name))) + return -EEXIST; + } /* * For outgoing we should make sure there is room in serialization array @@ -203,6 +219,42 @@ static int luo_session_insert(struct luo_session_header *sh, return 0; } +static void luo_session_publish_one(struct luo_session *session) +{ + struct luo_session_header *sh = &luo_session_global.outgoing; + struct luo_session_ser entry = {}; + + lockdep_assert_held_write(&sh->rwsem); + lockdep_assert_held(&session->mutex); + + if (!sh->active || !sh->sessions_pa) + return; + + if (!sh->published_count) + kho_block_set_it_init(&sh->it, &sh->block_set); + + strscpy(entry.name, session->name, sizeof(entry.name)); + entry.file_set_ser.count = session->file_set.count; + entry.file_set_ser.files = + kho_block_set_head_pa(&session->file_set.block_set); + + /* This should not fail normally as blocks were pre-allocated */ + if (WARN_ON_ONCE(!kho_block_set_it_append(&sh->it, &entry))) + return; + + if (!sh->published_count) { + /* + * Ensure the first block header and entry are visible before + * publishing sessions_pa to concurrent KHO readers. + */ + smp_store_release(sh->sessions_pa, + kho_block_set_head_pa(&sh->block_set)); + } + + list_move_tail(&session->list, &sh->published_list); + sh->published_count++; +} + static void luo_session_remove(struct luo_session_header *sh, struct luo_session *session) { @@ -636,50 +688,32 @@ int luo_session_deserialize(void) int luo_session_serialize(void) { struct luo_session_header *sh = &luo_session_global.outgoing; - struct luo_session *session; - struct kho_block_set_it it; + struct luo_session *session, *tmp; int err; down_write(&luo_session_serialize_rwsem); down_write(&sh->rwsem); - *sh->sessions_pa = 0; - - kho_block_set_it_init(&it, &sh->block_set); list_for_each_entry(session, &sh->list, list) { - struct luo_session_ser *ser = kho_block_set_it_reserve_entry(&it); - - /* This should not fail normally as blocks were pre-allocated */ - if (WARN_ON_ONCE(!ser)) { - err = -ENOSPC; - goto err_undo; - } - err = luo_session_freeze_one(session); if (err) { - kho_block_set_it_prev(&it); - goto err_undo; + list_for_each_entry_continue_reverse(session, &sh->list, list) + luo_session_unfreeze_one(session); + goto err_unlock; } + } - strscpy(ser->name, session->name, sizeof(ser->name)); - ser->file_set_ser.count = session->file_set.count; - ser->file_set_ser.files = - kho_block_set_head_pa(&session->file_set.block_set); + list_for_each_entry_safe(session, tmp, &sh->list, list) { + mutex_lock(&session->mutex); + luo_session_publish_one(session); + mutex_unlock(&session->mutex); } - if (sh->count > 0) - *sh->sessions_pa = kho_block_set_head_pa(&sh->block_set); up_write(&sh->rwsem); return 0; -err_undo: - list_for_each_entry_continue_reverse(session, &sh->list, list) { - struct luo_session_ser *ser = kho_block_set_it_prev(&it); - - luo_session_unfreeze_one(session); - memset(ser, 0, sizeof(*ser)); - } +err_unlock: up_write(&sh->rwsem); up_write(&luo_session_serialize_rwsem); -- 2.56.0.398.g882cbad275-goog