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.

Fixes: d893c5d00f63 ("[batman-adv] Cleanup aggregation.[ch]")
Signed-off-by: Sven Eckelmann <[email protected]>
---
See https://www.open-mesh.org/issues/433
---
Changes in v2:
- move reschedule to central place at end of function
- remove RFC prefix
- Link to v1: 
https://patch.msgid.link/20260515-bat_iv-own-fwd-failure-recover-v1-1-caa171e7d...@narfation.org
---
 net/batman-adv/bat_iv_ogm.c | 70 ++++++++++++++++++++++++++++++++++++---------
 net/batman-adv/types.h      |  3 ++
 2 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 74ef7dc2..7115eaed 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -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;
 
        lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
 
@@ -834,8 +846,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;
        }
 
@@ -847,15 +862,29 @@ 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);
 }
 
@@ -870,6 +899,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
@@ -2262,6 +2302,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);
 }
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index c8c3e806..6138f63a 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -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;
 };

---
base-commit: ff182fc0c666754e3f35f7b63c9ccf0dff239e52
change-id: 20260515-bat_iv-own-fwd-failure-recover-01c6a7e48491

Best regards,
--  
Sven Eckelmann <[email protected]>

Reply via email to