|
@@ -19,6 +19,7 @@
|
|
|
#include "main.h"
|
|
|
|
|
|
#include <linux/atomic.h>
|
|
|
+#include <linux/bug.h>
|
|
|
#include <linux/byteorder/generic.h>
|
|
|
#include <linux/errno.h>
|
|
|
#include <linux/etherdevice.h>
|
|
@@ -520,6 +521,8 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming,
|
|
|
if (if_outgoing)
|
|
|
kref_get(&if_outgoing->refcount);
|
|
|
|
|
|
+ INIT_HLIST_NODE(&forw_packet->list);
|
|
|
+ INIT_HLIST_NODE(&forw_packet->cleanup_list);
|
|
|
forw_packet->skb = NULL;
|
|
|
forw_packet->queue_left = queue_left;
|
|
|
forw_packet->if_incoming = if_incoming;
|
|
@@ -535,19 +538,191 @@ err:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_was_stolen - check whether someone stole this packet
|
|
|
+ * @forw_packet: the forwarding packet to check
|
|
|
+ *
|
|
|
+ * This function checks whether the given forwarding packet was claimed by
|
|
|
+ * someone else for free().
|
|
|
+ *
|
|
|
+ * Return: True if someone stole it, false otherwise.
|
|
|
+ */
|
|
|
+static bool
|
|
|
+batadv_forw_packet_was_stolen(struct batadv_forw_packet *forw_packet)
|
|
|
+{
|
|
|
+ return !hlist_unhashed(&forw_packet->cleanup_list);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_steal - claim a forw_packet for free()
|
|
|
+ * @forw_packet: the forwarding packet to steal
|
|
|
+ * @lock: a key to the store to steal from (e.g. forw_{bat,bcast}_list_lock)
|
|
|
+ *
|
|
|
+ * This function tries to steal a specific forw_packet from global
|
|
|
+ * visibility for the purpose of getting it for free(). That means
|
|
|
+ * the caller is *not* allowed to requeue it afterwards.
|
|
|
+ *
|
|
|
+ * Return: True if stealing was successful. False if someone else stole it
|
|
|
+ * before us.
|
|
|
+ */
|
|
|
+bool batadv_forw_packet_steal(struct batadv_forw_packet *forw_packet,
|
|
|
+ spinlock_t *lock)
|
|
|
+{
|
|
|
+ /* did purging routine steal it earlier? */
|
|
|
+ spin_lock_bh(lock);
|
|
|
+ if (batadv_forw_packet_was_stolen(forw_packet)) {
|
|
|
+ spin_unlock_bh(lock);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ hlist_del_init(&forw_packet->list);
|
|
|
+
|
|
|
+ /* Just to spot misuse of this function */
|
|
|
+ hlist_add_fake(&forw_packet->cleanup_list);
|
|
|
+
|
|
|
+ spin_unlock_bh(lock);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_list_steal - claim a list of forward packets for free()
|
|
|
+ * @forw_list: the to be stolen forward packets
|
|
|
+ * @cleanup_list: a backup pointer, to be able to dispose the packet later
|
|
|
+ * @hard_iface: the interface to steal forward packets from
|
|
|
+ *
|
|
|
+ * This function claims responsibility to free any forw_packet queued on the
|
|
|
+ * given hard_iface. If hard_iface is NULL forwarding packets on all hard
|
|
|
+ * interfaces will be claimed.
|
|
|
+ *
|
|
|
+ * The packets are being moved from the forw_list to the cleanup_list and
|
|
|
+ * by that allows already running threads to notice the claiming.
|
|
|
+ */
|
|
|
static void
|
|
|
-_batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
|
|
- struct batadv_forw_packet *forw_packet,
|
|
|
- unsigned long send_time)
|
|
|
+batadv_forw_packet_list_steal(struct hlist_head *forw_list,
|
|
|
+ struct hlist_head *cleanup_list,
|
|
|
+ const struct batadv_hard_iface *hard_iface)
|
|
|
{
|
|
|
- /* add new packet to packet list */
|
|
|
- spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
|
|
- hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list);
|
|
|
- spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
|
|
+ struct batadv_forw_packet *forw_packet;
|
|
|
+ struct hlist_node *safe_tmp_node;
|
|
|
+
|
|
|
+ hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
|
|
+ forw_list, list) {
|
|
|
+ /* if purge_outstanding_packets() was called with an argument
|
|
|
+ * we delete only packets belonging to the given interface
|
|
|
+ */
|
|
|
+ if (hard_iface &&
|
|
|
+ (forw_packet->if_incoming != hard_iface) &&
|
|
|
+ (forw_packet->if_outgoing != hard_iface))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ hlist_del(&forw_packet->list);
|
|
|
+ hlist_add_head(&forw_packet->cleanup_list, cleanup_list);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_list_free - free a list of forward packets
|
|
|
+ * @head: a list of to be freed forw_packets
|
|
|
+ *
|
|
|
+ * This function cancels the scheduling of any packet in the provided list,
|
|
|
+ * waits for any possibly running packet forwarding thread to finish and
|
|
|
+ * finally, safely frees this forward packet.
|
|
|
+ *
|
|
|
+ * This function might sleep.
|
|
|
+ */
|
|
|
+static void batadv_forw_packet_list_free(struct hlist_head *head)
|
|
|
+{
|
|
|
+ struct batadv_forw_packet *forw_packet;
|
|
|
+ struct hlist_node *safe_tmp_node;
|
|
|
+
|
|
|
+ hlist_for_each_entry_safe(forw_packet, safe_tmp_node, head,
|
|
|
+ cleanup_list) {
|
|
|
+ cancel_delayed_work_sync(&forw_packet->delayed_work);
|
|
|
|
|
|
- /* start timer for this packet */
|
|
|
- queue_delayed_work(batadv_event_workqueue, &forw_packet->delayed_work,
|
|
|
- send_time);
|
|
|
+ hlist_del(&forw_packet->cleanup_list);
|
|
|
+ batadv_forw_packet_free(forw_packet, true);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_queue - try to queue a forwarding packet
|
|
|
+ * @forw_packet: the forwarding packet to queue
|
|
|
+ * @lock: a key to the store (e.g. forw_{bat,bcast}_list_lock)
|
|
|
+ * @head: the shelve to queue it on (e.g. forw_{bat,bcast}_list)
|
|
|
+ * @send_time: timestamp (jiffies) when the packet is to be sent
|
|
|
+ *
|
|
|
+ * This function tries to (re)queue a forwarding packet. Requeuing
|
|
|
+ * is prevented if the according interface is shutting down
|
|
|
+ * (e.g. if batadv_forw_packet_list_steal() was called for this
|
|
|
+ * packet earlier).
|
|
|
+ *
|
|
|
+ * Calling batadv_forw_packet_queue() after a call to
|
|
|
+ * batadv_forw_packet_steal() is forbidden!
|
|
|
+ *
|
|
|
+ * Caller needs to ensure that forw_packet->delayed_work was initialized.
|
|
|
+ */
|
|
|
+static void batadv_forw_packet_queue(struct batadv_forw_packet *forw_packet,
|
|
|
+ spinlock_t *lock, struct hlist_head *head,
|
|
|
+ unsigned long send_time)
|
|
|
+{
|
|
|
+ spin_lock_bh(lock);
|
|
|
+
|
|
|
+ /* did purging routine steal it from us? */
|
|
|
+ if (batadv_forw_packet_was_stolen(forw_packet)) {
|
|
|
+ /* If you got it for free() without trouble, then
|
|
|
+ * don't get back into the queue after stealing...
|
|
|
+ */
|
|
|
+ WARN_ONCE(hlist_fake(&forw_packet->cleanup_list),
|
|
|
+ "Requeuing after batadv_forw_packet_steal() not allowed!\n");
|
|
|
+
|
|
|
+ spin_unlock_bh(lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ hlist_del_init(&forw_packet->list);
|
|
|
+ hlist_add_head(&forw_packet->list, head);
|
|
|
+
|
|
|
+ queue_delayed_work(batadv_event_workqueue,
|
|
|
+ &forw_packet->delayed_work,
|
|
|
+ send_time - jiffies);
|
|
|
+ spin_unlock_bh(lock);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_bcast_queue - try to queue a broadcast packet
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @forw_packet: the forwarding packet to queue
|
|
|
+ * @send_time: timestamp (jiffies) when the packet is to be sent
|
|
|
+ *
|
|
|
+ * This function tries to (re)queue a broadcast packet.
|
|
|
+ *
|
|
|
+ * Caller needs to ensure that forw_packet->delayed_work was initialized.
|
|
|
+ */
|
|
|
+static void
|
|
|
+batadv_forw_packet_bcast_queue(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_forw_packet *forw_packet,
|
|
|
+ unsigned long send_time)
|
|
|
+{
|
|
|
+ batadv_forw_packet_queue(forw_packet, &bat_priv->forw_bcast_list_lock,
|
|
|
+ &bat_priv->forw_bcast_list, send_time);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_forw_packet_ogmv1_queue - try to queue an OGMv1 packet
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @forw_packet: the forwarding packet to queue
|
|
|
+ * @send_time: timestamp (jiffies) when the packet is to be sent
|
|
|
+ *
|
|
|
+ * This function tries to (re)queue an OGMv1 packet.
|
|
|
+ *
|
|
|
+ * Caller needs to ensure that forw_packet->delayed_work was initialized.
|
|
|
+ */
|
|
|
+void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_forw_packet *forw_packet,
|
|
|
+ unsigned long send_time)
|
|
|
+{
|
|
|
+ batadv_forw_packet_queue(forw_packet, &bat_priv->forw_bat_list_lock,
|
|
|
+ &bat_priv->forw_bat_list, send_time);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -600,7 +775,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
|
|
INIT_DELAYED_WORK(&forw_packet->delayed_work,
|
|
|
batadv_send_outstanding_bcast_packet);
|
|
|
|
|
|
- _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay);
|
|
|
+ batadv_forw_packet_bcast_queue(bat_priv, forw_packet, jiffies + delay);
|
|
|
return NETDEV_TX_OK;
|
|
|
|
|
|
err_packet_free:
|
|
@@ -619,6 +794,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
|
|
|
struct sk_buff *skb1;
|
|
|
struct net_device *soft_iface;
|
|
|
struct batadv_priv *bat_priv;
|
|
|
+ unsigned long send_time = jiffies + msecs_to_jiffies(5);
|
|
|
bool dropped = false;
|
|
|
u8 *neigh_addr;
|
|
|
u8 *orig_neigh;
|
|
@@ -630,10 +806,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) {
|
|
|
dropped = true;
|
|
|
goto out;
|
|
@@ -714,22 +886,34 @@ 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) {
|
|
|
- _batadv_add_bcast_packet_to_list(bat_priv, forw_packet,
|
|
|
- msecs_to_jiffies(5));
|
|
|
+ batadv_forw_packet_bcast_queue(bat_priv, forw_packet,
|
|
|
+ send_time);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
out:
|
|
|
- batadv_forw_packet_free(forw_packet, dropped);
|
|
|
+ /* do we get something for free()? */
|
|
|
+ if (batadv_forw_packet_steal(forw_packet,
|
|
|
+ &bat_priv->forw_bcast_list_lock))
|
|
|
+ batadv_forw_packet_free(forw_packet, dropped);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_purge_outstanding_packets - stop/purge scheduled bcast/OGMv1 packets
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @hard_iface: the hard interface to cancel and purge bcast/ogm packets on
|
|
|
+ *
|
|
|
+ * This method cancels and purges any broadcast and OGMv1 packet on the given
|
|
|
+ * hard_iface. If hard_iface is NULL, broadcast and OGMv1 packets on all hard
|
|
|
+ * interfaces will be canceled and purged.
|
|
|
+ *
|
|
|
+ * This function might sleep.
|
|
|
+ */
|
|
|
void
|
|
|
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
|
|
|
const struct batadv_hard_iface *hard_iface)
|
|
|
{
|
|
|
- struct batadv_forw_packet *forw_packet;
|
|
|
- struct hlist_node *safe_tmp_node;
|
|
|
- bool pending;
|
|
|
+ struct hlist_head head = HLIST_HEAD_INIT;
|
|
|
|
|
|
if (hard_iface)
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
@@ -739,57 +923,18 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
|
"purge_outstanding_packets()\n");
|
|
|
|
|
|
- /* free bcast list */
|
|
|
+ /* claim bcast list for free() */
|
|
|
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
|
|
- hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
|
|
- &bat_priv->forw_bcast_list, list) {
|
|
|
- /* if purge_outstanding_packets() was called with an argument
|
|
|
- * we delete only packets belonging to the given interface
|
|
|
- */
|
|
|
- if ((hard_iface) &&
|
|
|
- (forw_packet->if_incoming != hard_iface) &&
|
|
|
- (forw_packet->if_outgoing != hard_iface))
|
|
|
- continue;
|
|
|
-
|
|
|
- spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
|
|
-
|
|
|
- /* batadv_send_outstanding_bcast_packet() will lock the list to
|
|
|
- * delete the item from the list
|
|
|
- */
|
|
|
- pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
|
|
|
- spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
|
|
-
|
|
|
- if (pending) {
|
|
|
- hlist_del(&forw_packet->list);
|
|
|
- batadv_forw_packet_free(forw_packet, true);
|
|
|
- }
|
|
|
- }
|
|
|
+ batadv_forw_packet_list_steal(&bat_priv->forw_bcast_list, &head,
|
|
|
+ hard_iface);
|
|
|
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
|
|
|
|
|
- /* free batman packet list */
|
|
|
+ /* claim batman packet list for free() */
|
|
|
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
- hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
|
|
- &bat_priv->forw_bat_list, list) {
|
|
|
- /* if purge_outstanding_packets() was called with an argument
|
|
|
- * we delete only packets belonging to the given interface
|
|
|
- */
|
|
|
- if ((hard_iface) &&
|
|
|
- (forw_packet->if_incoming != hard_iface) &&
|
|
|
- (forw_packet->if_outgoing != hard_iface))
|
|
|
- continue;
|
|
|
-
|
|
|
- spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
-
|
|
|
- /* send_outstanding_bat_packet() will lock the list to
|
|
|
- * delete the item from the list
|
|
|
- */
|
|
|
- pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
|
|
|
- spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
-
|
|
|
- if (pending) {
|
|
|
- hlist_del(&forw_packet->list);
|
|
|
- batadv_forw_packet_free(forw_packet, true);
|
|
|
- }
|
|
|
- }
|
|
|
+ batadv_forw_packet_list_steal(&bat_priv->forw_bat_list, &head,
|
|
|
+ hard_iface);
|
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
+
|
|
|
+ /* then cancel or wait for packet workers to finish and free */
|
|
|
+ batadv_forw_packet_list_free(&head);
|
|
|
}
|