Skip to content

Commit a70a9aa

Browse files
committed
batman-adv: lock around TT operations to avoid sending inconsistent data
A TT response may be prepared and sent while the local or global translation table is getting updated. The worst case is when one of the tables is accessed after its content has been recently updated but the metadata (TTVN/CRC) has not yet. In this case the reader will get a table content which does not match the TTVN/CRC. This will lead to an inconsistent state and so to a TT recovery. To avoid entering this situation, put a lock around those TT operations recomputing the metadata and around the TT Response creation (the latter is the only reader that accesses the metadata together with the table). Signed-off-by: Antonio Quartulli <antonio@open-mesh.com> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
1 parent e75de4f commit a70a9aa

File tree

4 files changed

+34
-4
lines changed

4 files changed

+34
-4
lines changed

net/batman-adv/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ int batadv_mesh_init(struct net_device *soft_iface)
110110
spin_lock_init(&bat_priv->tt.req_list_lock);
111111
spin_lock_init(&bat_priv->tt.roam_list_lock);
112112
spin_lock_init(&bat_priv->tt.last_changeset_lock);
113+
spin_lock_init(&bat_priv->tt.commit_lock);
113114
spin_lock_init(&bat_priv->gw.list_lock);
114115
spin_lock_init(&bat_priv->tvlv.container_list_lock);
115116
spin_lock_init(&bat_priv->tvlv.handler_list_lock);

net/batman-adv/originator.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
239239
spin_lock_init(&orig_node->bcast_seqno_lock);
240240
spin_lock_init(&orig_node->neigh_list_lock);
241241
spin_lock_init(&orig_node->tt_buff_lock);
242+
spin_lock_init(&orig_node->tt_lock);
242243

243244
batadv_nc_init_orig(orig_node);
244245

net/batman-adv/translation-table.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
20192019
req_src, tt_data->ttvn,
20202020
(tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
20212021

2022+
spin_lock_bh(&bat_priv->tt.commit_lock);
20222023

20232024
my_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
20242025
req_ttvn = tt_data->ttvn;
@@ -2091,6 +2092,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
20912092
unlock:
20922093
spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
20932094
out:
2095+
spin_unlock_bh(&bat_priv->tt.commit_lock);
20942096
if (orig_node)
20952097
batadv_orig_node_free_ref(orig_node);
20962098
if (primary_if)
@@ -2259,6 +2261,8 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
22592261
if (!orig_node)
22602262
goto out;
22612263

2264+
spin_lock_bh(&orig_node->tt_lock);
2265+
22622266
if (tt_data->flags & BATADV_TT_FULL_TABLE) {
22632267
batadv_tt_fill_gtable(bat_priv, tt_data, resp_src, num_entries);
22642268
} else {
@@ -2267,6 +2271,11 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
22672271
tt_data->ttvn, tt_change);
22682272
}
22692273

2274+
/* Recalculate the CRC for this orig_node and store it */
2275+
orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
2276+
2277+
spin_unlock_bh(&orig_node->tt_lock);
2278+
22702279
/* Delete the tt_req_node from pending tt_requests list */
22712280
spin_lock_bh(&bat_priv->tt.req_list_lock);
22722281
list_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
@@ -2276,9 +2285,6 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
22762285
kfree(node);
22772286
}
22782287
spin_unlock_bh(&bat_priv->tt.req_list_lock);
2279-
2280-
/* Recalculate the CRC for this orig_node and store it */
2281-
orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
22822288
out:
22832289
if (orig_node)
22842290
batadv_orig_node_free_ref(orig_node);
@@ -2532,10 +2538,12 @@ void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
25322538
{
25332539
uint16_t changed_num = 0;
25342540

2541+
spin_lock_bh(&bat_priv->tt.commit_lock);
2542+
25352543
if (atomic_read(&bat_priv->tt.local_changes) < 1) {
25362544
if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
25372545
batadv_tt_tvlv_container_update(bat_priv);
2538-
return;
2546+
goto out;
25392547
}
25402548

25412549
changed_num = batadv_tt_set_flags(bat_priv->tt.local_hash,
@@ -2555,6 +2563,9 @@ void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
25552563
/* reset the sending counter */
25562564
atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
25572565
batadv_tt_tvlv_container_update(bat_priv);
2566+
2567+
out:
2568+
spin_unlock_bh(&bat_priv->tt.commit_lock);
25582569
}
25592570

25602571
bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
@@ -2631,6 +2642,8 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
26312642
goto request_table;
26322643
}
26332644

2645+
spin_lock_bh(&orig_node->tt_lock);
2646+
26342647
tt_change = (struct batadv_tvlv_tt_change *)tt_buff;
26352648
batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes,
26362649
ttvn, tt_change);
@@ -2641,6 +2654,8 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
26412654
*/
26422655
orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
26432656

2657+
spin_unlock_bh(&orig_node->tt_lock);
2658+
26442659
/* The ttvn alone is not enough to guarantee consistency
26452660
* because a single value could represent different states
26462661
* (due to the wrap around). Thus a node has to check whether

net/batman-adv/types.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ struct batadv_frag_list_entry {
128128
* @tt_size: number of global TT entries announced by the orig node
129129
* @tt_initialised: bool keeping track of whether or not this node have received
130130
* any translation table information from the orig node yet
131+
* @tt_lock: prevents from updating the table while reading it. Table update is
132+
* made up by two operations (data structure update and metdata -CRC/TTVN-
133+
* recalculation) and they have to be executed atomically in order to avoid
134+
* another thread to read the table/metadata between those.
131135
* @last_real_seqno: last and best known sequence number
132136
* @last_ttl: ttl of last received packet
133137
* @bcast_bits: bitfield containing the info which payload broadcast originated
@@ -171,6 +175,8 @@ struct batadv_orig_node {
171175
spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */
172176
atomic_t tt_size;
173177
bool tt_initialised;
178+
/* prevents from changing the table while reading it */
179+
spinlock_t tt_lock;
174180
uint32_t last_real_seqno;
175181
uint8_t last_ttl;
176182
DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
@@ -388,6 +394,11 @@ enum batadv_counters {
388394
* @last_changeset: last tt changeset this host has generated
389395
* @last_changeset_len: length of last tt changeset this host has generated
390396
* @last_changeset_lock: lock protecting last_changeset & last_changeset_len
397+
* @commit_lock: prevents from executing a local TT commit while reading the
398+
* local table. The local TT commit is made up by two operations (data
399+
* structure update and metdata -CRC/TTVN- recalculation) and they have to be
400+
* executed atomically in order to avoid another thread to read the
401+
* table/metadata between those.
391402
* @work: work queue callback item for translation table purging
392403
*/
393404
struct batadv_priv_tt {
@@ -408,6 +419,8 @@ struct batadv_priv_tt {
408419
int16_t last_changeset_len;
409420
/* protects last_changeset & last_changeset_len */
410421
spinlock_t last_changeset_lock;
422+
/* prevents from executing a commit while reading the table */
423+
spinlock_t commit_lock;
411424
struct delayed_work work;
412425
};
413426

0 commit comments

Comments
 (0)