Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / net / batman-adv / send.c
index 6191159..8d4e1f5 100644 (file)
@@ -315,8 +315,7 @@ out:
  *
  * Wrap the given skb into a batman-adv unicast or unicast-4addr header
  * depending on whether BATADV_UNICAST or BATADV_UNICAST_4ADDR was supplied
- * as packet_type. Then send this frame to the given orig_node and release a
- * reference to this orig_node.
+ * as packet_type. Then send this frame to the given orig_node.
  *
  * Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
  */
@@ -370,8 +369,6 @@ int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
                ret = NET_XMIT_SUCCESS;
 
 out:
-       if (orig_node)
-               batadv_orig_node_put(orig_node);
        if (ret == NET_XMIT_DROP)
                kfree_skb(skb);
        return ret;
@@ -403,6 +400,7 @@ int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
        struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
        struct batadv_orig_node *orig_node;
        u8 *src, *dst;
+       int ret;
 
        src = ethhdr->h_source;
        dst = ethhdr->h_dest;
@@ -414,8 +412,13 @@ int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
        }
        orig_node = batadv_transtable_search(bat_priv, src, dst, vid);
 
-       return batadv_send_skb_unicast(bat_priv, skb, packet_type,
-                                      packet_subtype, orig_node, vid);
+       ret = batadv_send_skb_unicast(bat_priv, skb, packet_type,
+                                     packet_subtype, orig_node, vid);
+
+       if (orig_node)
+               batadv_orig_node_put(orig_node);
+
+       return ret;
 }
 
 /**
@@ -433,12 +436,25 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
                           unsigned short vid)
 {
        struct batadv_orig_node *orig_node;
+       int ret;
 
        orig_node = batadv_gw_get_selected_orig(bat_priv);
-       return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST_4ADDR,
-                                      BATADV_P_DATA, orig_node, vid);
+       ret = batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST_4ADDR,
+                                     BATADV_P_DATA, orig_node, vid);
+
+       if (orig_node)
+               batadv_orig_node_put(orig_node);
+
+       return ret;
 }
 
+/**
+ * batadv_forw_packet_free - free a forwarding packet
+ * @forw_packet: The packet to free
+ *
+ * This frees a forwarding packet and releases any resources it might
+ * have claimed.
+ */
 void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
 {
        kfree_skb(forw_packet->skb);
@@ -446,9 +462,73 @@ void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
                batadv_hardif_put(forw_packet->if_incoming);
        if (forw_packet->if_outgoing)
                batadv_hardif_put(forw_packet->if_outgoing);
+       if (forw_packet->queue_left)
+               atomic_inc(forw_packet->queue_left);
        kfree(forw_packet);
 }
 
+/**
+ * batadv_forw_packet_alloc - allocate a forwarding packet
+ * @if_incoming: The (optional) if_incoming to be grabbed
+ * @if_outgoing: The (optional) if_outgoing to be grabbed
+ * @queue_left: The (optional) queue counter to decrease
+ * @bat_priv: The bat_priv for the mesh of this forw_packet
+ *
+ * Allocates a forwarding packet and tries to get a reference to the
+ * (optional) if_incoming, if_outgoing and queue_left. If queue_left
+ * is NULL then bat_priv is optional, too.
+ *
+ * Return: An allocated forwarding packet on success, NULL otherwise.
+ */
+struct batadv_forw_packet *
+batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming,
+                        struct batadv_hard_iface *if_outgoing,
+                        atomic_t *queue_left,
+                        struct batadv_priv *bat_priv)
+{
+       struct batadv_forw_packet *forw_packet;
+       const char *qname;
+
+       if (queue_left && !batadv_atomic_dec_not_zero(queue_left)) {
+               qname = "unknown";
+
+               if (queue_left == &bat_priv->bcast_queue_left)
+                       qname = "bcast";
+
+               if (queue_left == &bat_priv->batman_queue_left)
+                       qname = "batman";
+
+               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                          "%s queue is full\n", qname);
+
+               return NULL;
+       }
+
+       forw_packet = kmalloc(sizeof(*forw_packet), GFP_ATOMIC);
+       if (!forw_packet)
+               goto err;
+
+       if (if_incoming)
+               kref_get(&if_incoming->refcount);
+
+       if (if_outgoing)
+               kref_get(&if_outgoing->refcount);
+
+       forw_packet->skb = NULL;
+       forw_packet->queue_left = queue_left;
+       forw_packet->if_incoming = if_incoming;
+       forw_packet->if_outgoing = if_outgoing;
+       forw_packet->num_packets = 0;
+
+       return forw_packet;
+
+err:
+       if (queue_left)
+               atomic_inc(queue_left);
+
+       return NULL;
+}
+
 static void
 _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
                                 struct batadv_forw_packet *forw_packet,
@@ -487,24 +567,20 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
        struct batadv_bcast_packet *bcast_packet;
        struct sk_buff *newskb;
 
-       if (!batadv_atomic_dec_not_zero(&bat_priv->bcast_queue_left)) {
-               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "bcast packet queue full\n");
-               goto out;
-       }
-
        primary_if = batadv_primary_if_get_selected(bat_priv);
        if (!primary_if)
-               goto out_and_inc;
-
-       forw_packet = kmalloc(sizeof(*forw_packet), GFP_ATOMIC);
+               goto err;
 
+       forw_packet = batadv_forw_packet_alloc(primary_if, NULL,
+                                              &bat_priv->bcast_queue_left,
+                                              bat_priv);
+       batadv_hardif_put(primary_if);
        if (!forw_packet)
-               goto out_and_inc;
+               goto err;
 
        newskb = skb_copy(skb, GFP_ATOMIC);
        if (!newskb)
-               goto packet_free;
+               goto err_packet_free;
 
        /* as we have a copy now, it is safe to decrease the TTL */
        bcast_packet = (struct batadv_bcast_packet *)newskb->data;
@@ -513,11 +589,6 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
        skb_reset_mac_header(newskb);
 
        forw_packet->skb = newskb;
-       forw_packet->if_incoming = primary_if;
-       forw_packet->if_outgoing = NULL;
-
-       /* how often did we send the bcast packet ? */
-       forw_packet->num_packets = 0;
 
        INIT_DELAYED_WORK(&forw_packet->delayed_work,
                          batadv_send_outstanding_bcast_packet);
@@ -525,13 +596,9 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
        _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay);
        return NETDEV_TX_OK;
 
-packet_free:
-       kfree(forw_packet);
-out_and_inc:
-       atomic_inc(&bat_priv->bcast_queue_left);
-out:
-       if (primary_if)
-               batadv_hardif_put(primary_if);
+err_packet_free:
+       batadv_forw_packet_free(forw_packet);
+err:
        return NETDEV_TX_BUSY;
 }
 
@@ -592,7 +659,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
 
 out:
        batadv_forw_packet_free(forw_packet);
-       atomic_inc(&bat_priv->bcast_queue_left);
 }
 
 void
@@ -633,9 +699,6 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
 
                if (pending) {
                        hlist_del(&forw_packet->list);
-                       if (!forw_packet->own)
-                               atomic_inc(&bat_priv->bcast_queue_left);
-
                        batadv_forw_packet_free(forw_packet);
                }
        }
@@ -663,9 +726,6 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
 
                if (pending) {
                        hlist_del(&forw_packet->list);
-                       if (!forw_packet->own)
-                               atomic_inc(&bat_priv->batman_queue_left);
-
                        batadv_forw_packet_free(forw_packet);
                }
        }