forked from Minki/linux
6d5808d4ae
add_bcast_packet_to_list increases the refcount for if_incoming but the
reference count is never decreased. The reference count must be
increased for all kinds of forwarded packets which have the primary
interface stored and forw_packet_free must decrease them. Also
purge_outstanding_packets has to invoke forw_packet_free when a work
item was really cancelled.
This regression was introduced in
32ae9b221e
.
Reported-by: Antonio Quartulli <ordex@autistici.org>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
281 lines
8.5 KiB
C
281 lines
8.5 KiB
C
/*
|
|
* Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
|
|
*
|
|
* Marek Lindner, Simon Wunderlich
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA
|
|
*
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include "aggregation.h"
|
|
#include "send.h"
|
|
#include "routing.h"
|
|
#include "hard-interface.h"
|
|
|
|
/* calculate the size of the tt information for a given packet */
|
|
static int tt_len(struct batman_packet *batman_packet)
|
|
{
|
|
return batman_packet->num_tt * ETH_ALEN;
|
|
}
|
|
|
|
/* return true if new_packet can be aggregated with forw_packet */
|
|
static bool can_aggregate_with(struct batman_packet *new_batman_packet,
|
|
int packet_len,
|
|
unsigned long send_time,
|
|
bool directlink,
|
|
struct hard_iface *if_incoming,
|
|
struct forw_packet *forw_packet)
|
|
{
|
|
struct batman_packet *batman_packet =
|
|
(struct batman_packet *)forw_packet->skb->data;
|
|
int aggregated_bytes = forw_packet->packet_len + packet_len;
|
|
|
|
/**
|
|
* we can aggregate the current packet to this aggregated packet
|
|
* if:
|
|
*
|
|
* - the send time is within our MAX_AGGREGATION_MS time
|
|
* - the resulting packet wont be bigger than
|
|
* MAX_AGGREGATION_BYTES
|
|
*/
|
|
|
|
if (time_before(send_time, forw_packet->send_time) &&
|
|
time_after_eq(send_time + msecs_to_jiffies(MAX_AGGREGATION_MS),
|
|
forw_packet->send_time) &&
|
|
(aggregated_bytes <= MAX_AGGREGATION_BYTES)) {
|
|
|
|
/**
|
|
* check aggregation compatibility
|
|
* -> direct link packets are broadcasted on
|
|
* their interface only
|
|
* -> aggregate packet if the current packet is
|
|
* a "global" packet as well as the base
|
|
* packet
|
|
*/
|
|
|
|
/* packets without direct link flag and high TTL
|
|
* are flooded through the net */
|
|
if ((!directlink) &&
|
|
(!(batman_packet->flags & DIRECTLINK)) &&
|
|
(batman_packet->ttl != 1) &&
|
|
|
|
/* own packets originating non-primary
|
|
* interfaces leave only that interface */
|
|
((!forw_packet->own) ||
|
|
(forw_packet->if_incoming->if_num == 0)))
|
|
return true;
|
|
|
|
/* if the incoming packet is sent via this one
|
|
* interface only - we still can aggregate */
|
|
if ((directlink) &&
|
|
(new_batman_packet->ttl == 1) &&
|
|
(forw_packet->if_incoming == if_incoming) &&
|
|
|
|
/* packets from direct neighbors or
|
|
* own secondary interface packets
|
|
* (= secondary interface packets in general) */
|
|
(batman_packet->flags & DIRECTLINK ||
|
|
(forw_packet->own &&
|
|
forw_packet->if_incoming->if_num != 0)))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* create a new aggregated packet and add this packet to it */
|
|
static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
|
|
unsigned long send_time, bool direct_link,
|
|
struct hard_iface *if_incoming,
|
|
int own_packet)
|
|
{
|
|
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct forw_packet *forw_packet_aggr;
|
|
unsigned char *skb_buff;
|
|
|
|
if (!atomic_inc_not_zero(&if_incoming->refcount))
|
|
return;
|
|
|
|
/* own packet should always be scheduled */
|
|
if (!own_packet) {
|
|
if (!atomic_dec_not_zero(&bat_priv->batman_queue_left)) {
|
|
bat_dbg(DBG_BATMAN, bat_priv,
|
|
"batman packet queue full\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC);
|
|
if (!forw_packet_aggr) {
|
|
if (!own_packet)
|
|
atomic_inc(&bat_priv->batman_queue_left);
|
|
goto out;
|
|
}
|
|
|
|
if ((atomic_read(&bat_priv->aggregated_ogms)) &&
|
|
(packet_len < MAX_AGGREGATION_BYTES))
|
|
forw_packet_aggr->skb = dev_alloc_skb(MAX_AGGREGATION_BYTES +
|
|
sizeof(struct ethhdr));
|
|
else
|
|
forw_packet_aggr->skb = dev_alloc_skb(packet_len +
|
|
sizeof(struct ethhdr));
|
|
|
|
if (!forw_packet_aggr->skb) {
|
|
if (!own_packet)
|
|
atomic_inc(&bat_priv->batman_queue_left);
|
|
kfree(forw_packet_aggr);
|
|
goto out;
|
|
}
|
|
skb_reserve(forw_packet_aggr->skb, sizeof(struct ethhdr));
|
|
|
|
INIT_HLIST_NODE(&forw_packet_aggr->list);
|
|
|
|
skb_buff = skb_put(forw_packet_aggr->skb, packet_len);
|
|
forw_packet_aggr->packet_len = packet_len;
|
|
memcpy(skb_buff, packet_buff, packet_len);
|
|
|
|
forw_packet_aggr->own = own_packet;
|
|
forw_packet_aggr->if_incoming = if_incoming;
|
|
forw_packet_aggr->num_packets = 0;
|
|
forw_packet_aggr->direct_link_flags = 0;
|
|
forw_packet_aggr->send_time = send_time;
|
|
|
|
/* save packet direct link flag status */
|
|
if (direct_link)
|
|
forw_packet_aggr->direct_link_flags |= 1;
|
|
|
|
/* add new packet to packet list */
|
|
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
|
hlist_add_head(&forw_packet_aggr->list, &bat_priv->forw_bat_list);
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
|
/* start timer for this packet */
|
|
INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work,
|
|
send_outstanding_bat_packet);
|
|
queue_delayed_work(bat_event_workqueue,
|
|
&forw_packet_aggr->delayed_work,
|
|
send_time - jiffies);
|
|
|
|
return;
|
|
out:
|
|
hardif_free_ref(if_incoming);
|
|
}
|
|
|
|
/* aggregate a new packet into the existing aggregation */
|
|
static void aggregate(struct forw_packet *forw_packet_aggr,
|
|
unsigned char *packet_buff,
|
|
int packet_len,
|
|
bool direct_link)
|
|
{
|
|
unsigned char *skb_buff;
|
|
|
|
skb_buff = skb_put(forw_packet_aggr->skb, packet_len);
|
|
memcpy(skb_buff, packet_buff, packet_len);
|
|
forw_packet_aggr->packet_len += packet_len;
|
|
forw_packet_aggr->num_packets++;
|
|
|
|
/* save packet direct link flag status */
|
|
if (direct_link)
|
|
forw_packet_aggr->direct_link_flags |=
|
|
(1 << forw_packet_aggr->num_packets);
|
|
}
|
|
|
|
void add_bat_packet_to_list(struct bat_priv *bat_priv,
|
|
unsigned char *packet_buff, int packet_len,
|
|
struct hard_iface *if_incoming, char own_packet,
|
|
unsigned long send_time)
|
|
{
|
|
/**
|
|
* _aggr -> pointer to the packet we want to aggregate with
|
|
* _pos -> pointer to the position in the queue
|
|
*/
|
|
struct forw_packet *forw_packet_aggr = NULL, *forw_packet_pos = NULL;
|
|
struct hlist_node *tmp_node;
|
|
struct batman_packet *batman_packet =
|
|
(struct batman_packet *)packet_buff;
|
|
bool direct_link = batman_packet->flags & DIRECTLINK ? 1 : 0;
|
|
|
|
/* find position for the packet in the forward queue */
|
|
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
|
/* own packets are not to be aggregated */
|
|
if ((atomic_read(&bat_priv->aggregated_ogms)) && (!own_packet)) {
|
|
hlist_for_each_entry(forw_packet_pos, tmp_node,
|
|
&bat_priv->forw_bat_list, list) {
|
|
if (can_aggregate_with(batman_packet,
|
|
packet_len,
|
|
send_time,
|
|
direct_link,
|
|
if_incoming,
|
|
forw_packet_pos)) {
|
|
forw_packet_aggr = forw_packet_pos;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* nothing to aggregate with - either aggregation disabled or no
|
|
* suitable aggregation packet found */
|
|
if (!forw_packet_aggr) {
|
|
/* the following section can run without the lock */
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
|
/**
|
|
* if we could not aggregate this packet with one of the others
|
|
* we hold it back for a while, so that it might be aggregated
|
|
* later on
|
|
*/
|
|
if ((!own_packet) &&
|
|
(atomic_read(&bat_priv->aggregated_ogms)))
|
|
send_time += msecs_to_jiffies(MAX_AGGREGATION_MS);
|
|
|
|
new_aggregated_packet(packet_buff, packet_len,
|
|
send_time, direct_link,
|
|
if_incoming, own_packet);
|
|
} else {
|
|
aggregate(forw_packet_aggr,
|
|
packet_buff, packet_len,
|
|
direct_link);
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
}
|
|
}
|
|
|
|
/* unpack the aggregated packets and process them one by one */
|
|
void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
|
|
int packet_len, struct hard_iface *if_incoming)
|
|
{
|
|
struct batman_packet *batman_packet;
|
|
int buff_pos = 0;
|
|
unsigned char *tt_buff;
|
|
|
|
batman_packet = (struct batman_packet *)packet_buff;
|
|
|
|
do {
|
|
/* network to host order for our 32bit seqno, and the
|
|
orig_interval. */
|
|
batman_packet->seqno = ntohl(batman_packet->seqno);
|
|
|
|
tt_buff = packet_buff + buff_pos + BAT_PACKET_LEN;
|
|
receive_bat_packet(ethhdr, batman_packet,
|
|
tt_buff, tt_len(batman_packet),
|
|
if_incoming);
|
|
|
|
buff_pos += BAT_PACKET_LEN + tt_len(batman_packet);
|
|
batman_packet = (struct batman_packet *)
|
|
(packet_buff + buff_pos);
|
|
} while (aggregated_packet(buff_pos, packet_len,
|
|
batman_packet->num_tt));
|
|
}
|