diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index fa5ead248f19..f0100ccf93de 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); } /**