summaryrefslogtreecommitdiff
path: root/drivers/staging/batman-adv/aggregation.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/batman-adv/aggregation.c')
-rw-r--r--drivers/staging/batman-adv/aggregation.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/drivers/staging/batman-adv/aggregation.c b/drivers/staging/batman-adv/aggregation.c
new file mode 100644
index 000000000000..9c6e681f6fb6
--- /dev/null
+++ b/drivers/staging/batman-adv/aggregation.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007-2009 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"
+
+/* calculate the size of the hna information for a given packet */
+static int hna_len(struct batman_packet *batman_packet)
+{
+ return batman_packet->num_hna * 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 batman_if *if_incoming,
+ struct forw_packet *forw_packet)
+{
+ struct batman_packet *batman_packet =
+ (struct batman_packet *)forw_packet->packet_buff;
+ 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) &&
+ (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))
+ 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 batman_if *if_incoming,
+ int own_packet)
+{
+ struct forw_packet *forw_packet_aggr;
+
+ forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC);
+ if (!forw_packet_aggr)
+ return;
+
+ forw_packet_aggr->packet_buff = kmalloc(MAX_AGGREGATION_BYTES,
+ GFP_ATOMIC);
+ if (!forw_packet_aggr->packet_buff) {
+ kfree(forw_packet_aggr);
+ return;
+ }
+
+ INIT_HLIST_NODE(&forw_packet_aggr->list);
+
+ forw_packet_aggr->packet_len = packet_len;
+ memcpy(forw_packet_aggr->packet_buff,
+ packet_buff,
+ forw_packet_aggr->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(&forw_bat_list_lock);
+ hlist_add_head(&forw_packet_aggr->list, &forw_bat_list);
+ spin_unlock(&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);
+}
+
+/* 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)
+{
+ memcpy((forw_packet_aggr->packet_buff + forw_packet_aggr->packet_len),
+ 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(unsigned char *packet_buff, int packet_len,
+ struct batman_if *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(&forw_bat_list_lock);
+ /* own packets are not to be aggregated */
+ if ((atomic_read(&aggregation_enabled)) && (!own_packet)) {
+ hlist_for_each_entry(forw_packet_pos, tmp_node, &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 == NULL) {
+ /* the following section can run without the lock */
+ spin_unlock(&forw_bat_list_lock);
+ 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(&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 batman_if *if_incoming)
+{
+ struct batman_packet *batman_packet;
+ int buff_pos = 0;
+ unsigned char *hna_buff;
+
+ batman_packet = (struct batman_packet *)packet_buff;
+
+ while (aggregated_packet(buff_pos, packet_len,
+ batman_packet->num_hna)) {
+
+ /* network to host order for our 16bit seqno, and the
+ orig_interval. */
+ batman_packet->seqno = ntohs(batman_packet->seqno);
+
+ hna_buff = packet_buff + buff_pos + BAT_PACKET_LEN;
+ receive_bat_packet(ethhdr, batman_packet,
+ hna_buff, hna_len(batman_packet),
+ if_incoming);
+
+ buff_pos += BAT_PACKET_LEN + hna_len(batman_packet);
+ batman_packet = (struct batman_packet *)
+ (packet_buff + buff_pos);
+ }
+}