From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=23.88.38.48 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793368282; b=t+UQepocGSB5bTT7XRTA42D3gQwJRKdh+tffxkvIQKQ+yAzTCLDluBhfm8zymhEnPeGd hZh2y3uhzihCKCfvPO6WQaEfaDwkrx/O+42KYXz88vI537XD3I1V9+99x7jo7SoehnqK1 j3kGO95NB2tFrwXIzvpQwldxtjZjmP/tFPGFPCp7QP4Xt53H+FfZAsJ0RzgLL/aw9HJMS HMpvcpI7qjsXXuE6llZkSZHN0ZepOGgFC2FlxNtUjmRJhKkO7lKFuum7b4LcSrWO3ORlE CQOEA8ErKg4QYwvY4nnqgyfer3v+GQL1k/0CWbNRlAcqDA6OBOGyTcVAnE+gveb6GOQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793368282; h=DMARC-Filter:Received:DKIM-Signature:From:To:Cc:Subject:Date: Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=UFxRLs5vW2tsghKtP5gycgKT1qnvgM0WlTrEQHjOoas=; b=sQwApkHUzCsUSVtz3J9h8D/deFxjzhcSI3XfBd5AnMUT8f32pzqGEKoiSFjT3aqtx54M l8Um/qEiRqMu37GRjts3a9ouvEsow9O0oAMm76tardkLfwdqWE/jIz8ScnUUJawNsA3Hb h0R40hozMiHxdeNQeykuq6WLPOkvlgRvbXeSpK4ks0l+QwbtHTJ1Sjm9zA/XoOPieJdQK fJgiOZoFB9Kk5WxiUcDRvpgd+zYLmgnXSt24OPn00uFW9ppBrFWx/8T1D17lRksIsNQoJ CXexfonvp58Tvc2UXMgAd9YkkBnICMyXlTKyFweoZ5paKOASzJw1NWkqrFHY8l5NpWQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=simonwunderlich.de; spf=pass smtp.mailfrom=simonwunderlich.de; dkim=pass header.d=simonwunderlich.de header.i=@simonwunderlich.de header.a=rsa-sha256 header.s=09092022 header.b=ytb8whlv; arc=none smtp.remote-ip=23.88.38.48 Received: from mail.simonwunderlich.de (mail.simonwunderlich.de [23.88.38.48]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with UTF8SMTPS id CFC4360A2601; Fri, 30 Oct 2026 13:51:21 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org CFC4360A2601 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=simonwunderlich.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=simonwunderlich.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=simonwunderlich.de header.i=@simonwunderlich.de header.a=rsa-sha256 header.s=09092022 header.b=ytb8whlv Received: from kero.packetmixer.de (p200300C597124fd839580c360d3ed6f6.dip0.t-ipconnect.de [IPv6:2003:c5:9712:4fd8:3958:c36:d3e:d6f6]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange secp256r1 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mail.simonwunderlich.de (Postfix) with UTF8SMTPSA id 1BDA5FA006; Fri, 30 Oct 2026 14:51:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=simonwunderlich.de; s=09092022; t=1793368280; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UFxRLs5vW2tsghKtP5gycgKT1qnvgM0WlTrEQHjOoas=; b=ytb8whlvwET1ZOa7sOau/pD5WhJsaHvM+YCfXF46KrSrLm8+s4uO4USxI+hdJpWBkHExE5 aS80MRVtVSeNGUjFuVaCWtk9nEAhZJclx5ZwlVEn/c2FfqhsQfFWqN61/a2E2F0jaL7kqD JE8Zj0yJL5hPnqslmf9M+86DpTyJZsLodj1ySvLLmS2KzQfBf3FsvMHbZT6XEZFKoisOMq B33npiMYvzmT45Stpu9lXv4j9sbE+wxRdyngdlO9ba0bulEBhg1zFVN8l0iewhBpSvuLFV oMvNjB1ue9BwgpXAtK5+5fJxu2NPX3AqqvaGhfpgs2z//fjF3Go+so15uXQyNQ== From: Simon Wunderlich To: netdev@vger.kernel.org Cc: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , b.a.t.m.a.n@lists.open-mesh.org, Sven Eckelmann , stable@vger.kernel.org, Sashiko , Simon Wunderlich Subject: [PATCH net v2 13/14] batman-adv: tt: don't drop local events queued during TVLV update Date: Fri, 30 Oct 2026 14:51:04 +0100 Message-ID: <20261030135105.2555887-14-sw@simonwunderlich.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261030135105.2555887-1-sw@simonwunderlich.de> References: <20261030135105.2555887-1-sw@simonwunderlich.de> X-Mailing-List: netdev@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Sven Eckelmann batadv_tt_tvlv_container_update() read the number of local changes without holding changes_list_lock. batadv_tt_local_event() is not serialized by the commit_lock and can queue new events before the changes_list is flushed: CPU0 CPU1 batadv_tt_tvlv_container_update() num = ...local_changes; batadv_tt_local_event() list_add_tail(...); ...local_changes++; spin_lock_bh(&...changes_list_lock); /* only num events copied but all freed */ The additional events were freed without ever being sent. Other nodes will only notice this via a CRC mismatch and must request the full table. The number of changes and the changes_list must therefore be taken atomically. Events queued afterwards then stay in the tt.changes_list for the next TT round. The changes can no longer be kept for the next round when the TVLV buffer allocation fails. But they would be sent with a skipped TTVN anyway, so the other nodes have to request the full table in this case either way. The last changeset is only dropped in this case when the TTVN was incremented. Without new changes, it still describes the current TTVN. Previously, the unconsumed changes caused a retry of the TVLV container update in the next commit round. Since the changes are now consumed, the next round would only take the path without changes and wait for BATADV_TT_OGM_APPEND_MAX rounds before updating the container again. Until then, the outdated container with the previous TTVN would still be announced. The ogm_append_cnt is therefore reset on failure to retry the update in the next OGM interval. This doesn't make the CRC consistent with the announced changeset. The local table modifications are still not serialized with the commit and batadv_tt_local_transition_new() can still make a client part of the CRC for which the event is only part of the next TT round. Such an inconsistency is detected by the CRC check of the other nodes and solved via a full table request. Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/bug/linux-fc286ddd-5bed-4683-b842-ee7a307eeb19 Fixes: a73105b8d4c7 ("batman-adv: improved client announcement mechanism") Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/translation-table.c | 61 +++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index fa5ead248f192..f0100ccf93deb 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1323,22 +1323,24 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, * batadv_tt_tvlv_container_update() - update the translation table tvlv * container after local tt changes have been committed * @bat_priv: the bat priv with all the mesh interface information + * @changes_list: list of committed changes which will be consumed + * @tt_diff_entries_num: number of entries in @changes_list */ -static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) +static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv, + struct list_head *changes_list, + int tt_diff_entries_num) { struct batadv_tvlv_tt_change *tt_change; struct batadv_tt_change_node *entry; struct batadv_tvlv_tt_data *tt_data; struct batadv_tt_change_node *safe; int tt_diff_entries_count = 0; - int tt_diff_entries_num = 0; bool drop_changes = false; size_t tt_extra_len = 0; int tt_change_len = 0; int tt_diff_len; u16 tvlv_len; - tt_diff_entries_num = READ_ONCE(bat_priv->tt.local_changes); tt_diff_len = batadv_tt_len(tt_diff_entries_num); /* if we have too many changes for one packet don't send any @@ -1356,19 +1358,39 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data, &tt_change, &tt_diff_len); - if (!tvlv_len) + if (!tvlv_len) { + /* the changes are lost and the other nodes have to request the + * full table. Don't answer TT requests for the new TTVN with + * the stale changeset of the previous TTVN. Without changes, the + * TTVN was not incremented and the last changeset is still valid + */ + if (drop_changes || tt_diff_entries_num > 0) { + list_for_each_entry_safe(entry, safe, changes_list, + list) { + list_del(&entry->list); + kmem_cache_free(batadv_tt_change_cache, entry); + } + + spin_lock_bh(&bat_priv->tt.last_changeset_lock); + kfree(bat_priv->tt.last_changeset); + bat_priv->tt.last_changeset = NULL; + bat_priv->tt.last_changeset_len = 0; + spin_unlock_bh(&bat_priv->tt.last_changeset_lock); + } + + /* announce the already committed TTVN in the next OGM interval + * instead of waiting for BATADV_TT_OGM_APPEND_MAX idle rounds + */ + atomic_set(&bat_priv->tt.ogm_append_cnt, 0); return; + } tt_data->flags = BATADV_TT_OGM_DIFF; if (!drop_changes && tt_diff_len == 0) goto container_register; - spin_lock_bh(&bat_priv->tt.changes_list_lock); - WRITE_ONCE(bat_priv->tt.local_changes, 0); - - list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list, - list) { + list_for_each_entry_safe(entry, safe, changes_list, list) { if (tt_diff_entries_count < tt_diff_entries_num) { memcpy(tt_change + tt_diff_entries_count, &entry->change, @@ -1378,7 +1400,6 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) list_del(&entry->list); kmem_cache_free(batadv_tt_change_cache, entry); } - spin_unlock_bh(&bat_priv->tt.changes_list_lock); tt_extra_len = batadv_tt_len(tt_diff_entries_num - tt_diff_entries_count); @@ -4429,11 +4450,25 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) */ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) { + LIST_HEAD(changes_list); + int changes_num; + lockdep_assert_held(&bat_priv->tt.commit_lock); - if (READ_ONCE(bat_priv->tt.local_changes) == 0) { + /* atomically get the list of changes. The TTVN decision and the + * announced changeset must cover the same events. Events queued + * afterwards stay in the tt.changes_list for the next TT round + */ + spin_lock_bh(&bat_priv->tt.changes_list_lock); + list_splice_init(&bat_priv->tt.changes_list, &changes_list); + changes_num = READ_ONCE(bat_priv->tt.local_changes); + WRITE_ONCE(bat_priv->tt.local_changes, 0); + spin_unlock_bh(&bat_priv->tt.changes_list_lock); + + if (changes_num == 0) { if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt)) - batadv_tt_tvlv_container_update(bat_priv); + batadv_tt_tvlv_container_update(bat_priv, &changes_list, + changes_num); return; } @@ -4450,7 +4485,7 @@ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) /* reset the sending counter */ atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX); - batadv_tt_tvlv_container_update(bat_priv); + batadv_tt_tvlv_container_update(bat_priv, &changes_list, changes_num); } /** -- 2.47.3