]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
batman-adv: iv: recover OGM scheduling after forward packet error
authorSven Eckelmann <sven@narfation.org>
Fri, 15 May 2026 20:00:40 +0000 (22:00 +0200)
committerSven Eckelmann <sven@narfation.org>
Tue, 19 May 2026 07:09:29 +0000 (09:09 +0200)
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: c6c8fea29769 ("net: Add batman-adv meshing protocol")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
net/batman-adv/bat_iv_ogm.c
net/batman-adv/types.h

index 7ad26128b5f7cafa277f1e737c16ba3422664c55..b8b1b997960a96c33e4afbf3d649420d05f2d1be 100644 (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 @@ out:
  * @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);
 }
index fb0e4cb89d79a8c0f5c1ac673c532500cc3ebcec..821ada05d86a7873505f7a73e2e3cb4e922616b9 100644 (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;
 };