batman-adv: iv: recover OGM scheduling after forward packet error

When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
packet for OGM transmission, the work item that drives periodic OGM
scheduling is never re-armed. This silently halts transmission of the
node's own OGMs on the affected interface — only OGMs from other peers
continue to be aggregated and forwarded.

Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
a dedicated recovery work item (reschedule_work) that fires after one
originator interval and calls batadv_iv_ogm_schedule() again.

Cc: stable@kernel.org
Fixes: c6c8fea297 ("net: Add batman-adv meshing protocol")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Sven Eckelmann 2026-05-15 22:00:40 +02:00
parent 20c2d6a20c
commit aa3153bd13
No known key found for this signature in database
GPG Key ID: 4D0F772BD314F5CB
2 changed files with 60 additions and 19 deletions

View File

@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
hard_iface->bat_iv.ogm_buff = NULL;
mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
}
static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
@ -536,8 +538,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
* @if_incoming: interface where the packet was received
* @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm
*
* Return: whether forward packet was scheduled
*/
static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
int packet_len, unsigned long send_time,
bool direct_link,
struct batadv_hard_iface *if_incoming,
@ -561,13 +565,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
skb = netdev_alloc_skb_ip_align(NULL, skb_size);
if (!skb)
return;
return false;
forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
queue_left, bat_priv, skb);
if (!forw_packet_aggr) {
kfree_skb(skb);
return;
return false;
}
forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
@ -590,6 +594,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
batadv_iv_send_outstanding_bat_ogm_packet);
batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
return true;
}
/* aggregate a new packet into the existing ogm packet */
@ -617,8 +623,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
* @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm
* @send_time: timestamp (jiffies) when the packet is to be sent
*
* Return: whether forward packet was scheduled
*/
static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
unsigned char *packet_buff,
int packet_len,
struct batadv_hard_iface *if_incoming,
@ -670,14 +678,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
send_time += max_aggregation_jiffies;
batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
send_time, direct_link,
if_incoming, if_outgoing,
own_packet);
return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
send_time, direct_link,
if_incoming, if_outgoing,
own_packet);
} else {
batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
packet_len, direct_link);
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
return true;
}
}
@ -790,6 +800,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
u32 seqno;
u16 tvlv_len = 0;
unsigned long send_time;
bool reschedule = false;
bool scheduled;
int ret;
lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
@ -818,11 +830,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
ogm_buff_len,
BATADV_OGM_HLEN);
if (ret < 0) {
/* OGMs must be queued even when the buffer allocation for
* TVLVs failed. just fall back to the non-TVLV version
*/
ret = 0;
*ogm_buff_len = BATADV_OGM_HLEN;
reschedule = true;
goto out;
}
tvlv_len = ret;
@ -844,8 +853,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
/* OGMs from secondary interfaces are only scheduled on their
* respective interfaces.
*/
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
hard_iface, hard_iface, 1, send_time);
scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
hard_iface, hard_iface, 1, send_time);
if (!scheduled)
reschedule = true;
goto out;
}
@ -857,15 +869,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
continue;
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
*ogm_buff_len, hard_iface,
tmp_hard_iface, 1, send_time);
scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
*ogm_buff_len, hard_iface,
tmp_hard_iface, 1, send_time);
batadv_hardif_put(tmp_hard_iface);
if (!scheduled && tmp_hard_iface == hard_iface)
reschedule = true;
}
rcu_read_unlock();
out:
if (reschedule) {
/* there was a failure scheduling the own forward packet.
* as result, the batadv_iv_send_outstanding_bat_ogm_packet()
* work item is no longer scheduled. it is therefore necessary
* to reschedule it manually
*/
queue_delayed_work(batadv_event_workqueue,
&hard_iface->bat_iv.reschedule_work,
msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
}
batadv_hardif_put(primary_if);
}
@ -880,6 +905,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
}
static void batadv_iv_ogm_reschedule(struct work_struct *work)
{
struct delayed_work *delayed_work = to_delayed_work(work);
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(delayed_work,
struct batadv_hard_iface,
bat_iv.reschedule_work);
batadv_iv_ogm_schedule(hard_iface);
}
/**
* batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
* @orig_node: originator which reproadcasted the OGMs directly
@ -2272,6 +2308,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
{
INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
/* begin scheduling originator messages on that interface */
batadv_iv_ogm_schedule(hard_iface);
}

View File

@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
/** @ogm_seqno: OGM sequence number - used to identify each OGM */
atomic_t ogm_seqno;
/** @reschedule_work: recover OGM schedule after schedule error */
struct delayed_work reschedule_work;
/** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
struct mutex ogm_buff_mutex;
};