- Avoid rearmed bcast packet being missed by cancel_delayed_work_sync().
When a scheduled broadcast packet sending work executes and removes itself from the broadcast queue, it becomes invisible for the purging methods until it re-arms.
This means a to be re-armed broadcast work might slip through a cancel_delayed_work_sync() between the removal from and readdition to the broadcast queue.
To avoid this we perform the removal and readdition in one atomic step.
Signed-off-by: Linus Lüssing linus.luessing@web.de --- send.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/send.c b/send.c index 1ddfae7..34c54e9 100644 --- a/send.c +++ b/send.c @@ -217,12 +217,9 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet, unsigned long send_time) { - /* add new packet to packet list and start its timer */ - spin_lock_bh(&bat_priv->forw_bcast_list_lock); hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list); queue_delayed_work(batadv_event_workqueue, &forw_packet->delayed_work, send_time); - spin_unlock_bh(&bat_priv->forw_bcast_list_lock); }
/* add a broadcast packet to the queue and setup timers. broadcast packets @@ -272,7 +269,10 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, INIT_DELAYED_WORK(&forw_packet->delayed_work, batadv_send_outstanding_bcast_packet);
+ spin_lock_bh(&bat_priv->forw_bcast_list_lock); _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay); + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + return NETDEV_TX_OK;
packet_free: @@ -296,10 +296,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) soft_iface = forw_packet->if_incoming->soft_iface; bat_priv = netdev_priv(soft_iface);
- spin_lock_bh(&bat_priv->forw_bcast_list_lock); - hlist_del(&forw_packet->list); - spin_unlock_bh(&bat_priv->forw_bcast_list_lock); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out;
@@ -327,12 +323,21 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
/* if we still have some more bcasts to send */ if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) { + spin_lock_bh(&bat_priv->forw_bcast_list_lock); + hlist_del(&forw_packet->list); + _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, msecs_to_jiffies(5)); + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + return; }
out: + spin_lock_bh(&bat_priv->forw_bcast_list_lock); + hlist_del(&forw_packet->list); + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + batadv_forw_packet_free(forw_packet); }