@@ -18,15 +18,430 @@
#include "aggregation.h"
#include "main.h"
+#include <linux/atomic.h>
+#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
#include <linux/skbuff.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/workqueue.h>
+#include "send.h"
#include "tvlv.h"
/**
+ * batadv_aggr_hardif_start - schedule aggregation packet transmission
+ * @hard_iface: the hard interface to schedule the transmission on
+ *
+ * Schedules an aggregation packet transmission for the next aggregation
+ * interval, plus/minus some jitter.
+ */
+void batadv_aggr_hardif_start(struct batadv_hard_iface *hard_iface)
+{
+ unsigned int msecs = BATADV_MAX_AGGREGATION_MS * 1000;
+
+ /* msecs * [0.9, 1.1] */
+ msecs += prandom_u32() % (msecs / 5) - (msecs / 10);
+ queue_delayed_work(batadv_event_workqueue, &hard_iface->aggr.work,
+ msecs_to_jiffies(msecs / 1000));
+}
+
+/**
+ * batadv_aggr_hardif_start_urgent - schedule urgent aggregate transmission
+ * @hard_iface: the hard interface to schedule the transmission on
+ *
+ * Schedules an urgent aggregation packet transmission, plus/minus some jitter.
+ * That is at some time between an interval 10 to 20 times faster than the
+ * regular aggregation interval.
+ */
+static void
+batadv_aggr_hardif_start_urgent(struct batadv_hard_iface *hard_iface)
+{
+ unsigned int msecs = BATADV_MAX_AGGREGATION_MS * 1000;
+
+ /* msecs * [0.05, 0.1] */
+ msecs = (msecs / 20) + prandom_u32() % (msecs / 20);
+ queue_delayed_work(batadv_event_workqueue, &hard_iface->aggr.work,
+ msecs_to_jiffies(msecs / 1000));
+}
+
+/**
+ * batadv_aggr_skb_queue_free - free all elements in an skb queue
+ * @head: the skb queue to empty
+ *
+ * Empties an skb queue and frees all the skbs it contained.
+ */
+static void batadv_aggr_skb_queue_free(struct sk_buff_head *head)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(head)))
+ kfree_skb(skb);
+}
+
+/**
+ * batadv_aggr_hardif_stop - shutdown an aggregation routine
+ * @hard_iface: the interface to stop aggregation on
+ *
+ * Stops an aggregation timer and destroys the packets queued
+ * on the given interface.
+ */
+void batadv_aggr_hardif_stop(struct batadv_hard_iface *hard_iface)
+{
+ cancel_delayed_work_sync(&hard_iface->aggr.work);
+
+ spin_lock_bh(&hard_iface->aggr.aggr_list_lock);
+ batadv_aggr_skb_queue_free(&hard_iface->aggr.aggr_list);
+ spin_unlock_bh(&hard_iface->aggr.aggr_list_lock);
+}
+
+/**
+ * batadv_aggr_chunk_reserve - reserve space in an aggregation packet
+ * @hard_iface: the interface to reserve on
+ * @skb: the to be aggregated packet to reserve for
+ * @size: size of the aggregation packet
+ *
+ * Tries to reserve space in the aggregation packet for the given skb.
+ * If reservation was successful, then the size of the to be allocated
+ * aggregation packet is increased accordingly.
+ *
+ * Return: True if there was enough space in the aggregation packet left,
+ * false otherwise.
+ */
+static bool batadv_aggr_chunk_reserve(struct batadv_hard_iface *hard_iface,
+ struct sk_buff *skb,
+ int *size)
+{
+ unsigned int len = skb->len + sizeof(struct batadv_tvlv_hdr);
+
+ len -= sizeof(((struct batadv_aggr_packet *)0)->packet_type);
+ len -= sizeof(((struct batadv_aggr_packet *)0)->version);
+
+ if (*size + len > hard_iface->net_dev->mtu)
+ return false;
+
+ *size += len;
+ return true;
+}
+
+/**
+ * batadv_aggr_get_chunk - gets a chunk of packets from the aggregation queue
+ * @hard_iface: the interface to get to be aggregated packets from
+ * @head: queue to stage a chunk of to be aggregated+transmitted packets on
+ * @size: size of the aggregation packet
+ *
+ * Tries to grab as many packets from the aggregation queue as fit into a
+ * single aggregation packet.
+ *
+ * Return: True if there are no packets in the aggregation queue
+ * of the provided interface left afterwards, false otherwise.
+ */
+static bool batadv_aggr_get_chunk(struct batadv_hard_iface *hard_iface,
+ struct sk_buff_head *head,
+ int *size)
+{
+ struct sk_buff *skb, *skb_tmp;
+ bool emptied = true;
+
+ *size = sizeof(struct batadv_aggr_packet);
+
+ if (skb_queue_empty(&hard_iface->aggr.aggr_list))
+ return emptied;
+
+ spin_lock_bh(&hard_iface->aggr.aggr_list_lock);
+ skb_queue_walk_safe(&hard_iface->aggr.aggr_list, skb, skb_tmp) {
+ if (!batadv_aggr_chunk_reserve(hard_iface, skb, size)) {
+ emptied = false;
+ break;
+ }
+
+ skb_unlink(skb, &hard_iface->aggr.aggr_list);
+ skb_queue_tail(head, skb);
+ }
+ spin_unlock_bh(&hard_iface->aggr.aggr_list_lock);
+
+ return emptied;
+}
+
+/**
+ * batadv_aggr_alloc_skb - allocate an aggregation packet
+ * @size: size of the to be allocated packet (excluding ethernet header)
+ *
+ * Allocates a broadcast aggregation packet.
+ *
+ * Return: An aggregation packet on success, NULL otherwise.
+ */
+static struct sk_buff *batadv_aggr_alloc_skb(int size)
+{
+ struct batadv_aggr_packet *aggr_packet;
+ struct sk_buff *skb;
+ unsigned char *skb_buff;
+ unsigned int offset;
+
+ skb = dev_alloc_skb(size + ETH_HLEN + NET_IP_ALIGN);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+ skb_reset_network_header(skb);
+
+ skb_buff = skb_put(skb, sizeof(*aggr_packet));
+ aggr_packet = (struct batadv_aggr_packet *)skb_buff;
+ aggr_packet->packet_type = BATADV_BCAST_AGGR;
+ aggr_packet->version = BATADV_COMPAT_VERSION;
+
+ offset = skb_network_offset(skb) + sizeof(*aggr_packet);
+ skb_set_transport_header(skb, offset);
+
+ return skb;
+}
+
+/**
+ * batadv_aggr_get_pkttypes - get the packet type of a batman packet
+ * @skb: the packet to get the type from
+ *
+ * Return: The packet type of the provided batman packet.
+ */
+static u8 batadv_aggr_get_pkttype(struct sk_buff *skb)
+{
+ struct batadv_aggr_packet *packet;
+
+ packet = (struct batadv_aggr_packet *)skb_network_header(skb);
+
+ return packet->packet_type;
+}
+
+/**
+ * batadv_aggr_put_tvlvhdr - append a tvlv header to an skb
+ * @skb: the aggregation packet to append the tvlv header to
+ * @type: the packet (= tvlv) type to set in the tvlv header
+ * @len: the size of the to be added tvlv data
+ *
+ * Appends a tvlv header to the given aggregation packet and sets its type and
+ * length as provided.
+ */
+static void batadv_aggr_put_tvlvhdr(struct sk_buff *skb, u8 type,
+ unsigned int len)
+{
+ struct batadv_tvlv_hdr *tvlv_hdr;
+
+ tvlv_hdr = (struct batadv_tvlv_hdr *)skb_put(skb, sizeof(*tvlv_hdr));
+
+ tvlv_hdr->type = type;
+ tvlv_hdr->version = 1;
+ tvlv_hdr->len = htons(len);
+}
+
+/**
+ * batadv_aggr_queue_is_full - check for slots left in aggregation queue
+ * @hard_iface: the interface to check
+ *
+ * Return: True if if the queue is full, false otherwise.
+ */
+static inline bool
+batadv_aggr_queue_is_full(struct batadv_hard_iface *hard_iface)
+{
+ struct sk_buff_head *head = &hard_iface->aggr.aggr_list;
+
+ return skb_queue_len(head) >= BATADV_AGGR_QUEUE_LEN;
+}
+
+/**
+ * batadv_aggr_squash_chunk - squash packets into an aggregate
+ * @head: a list of to be squashed packets
+ * @size: the size of the to be created aggregation packet
+ * (excluding the ethernet header)
+ *
+ * Allocates an aggregation packet and squashes the provided list of broadcast
+ * packets into it. The provided list of packets is freed/consumed.
+ *
+ * Return: An aggregation packet ready for transmission on success, NULL
+ * otherwise.
+ */
+static struct sk_buff *
+batadv_aggr_squash_chunk(struct sk_buff_head *head,
+ int size)
+{
+ struct sk_buff *skb, *skb_tmp, *skb_aggr;
+ struct batadv_aggr_packet *aggr_packet;
+ unsigned int len, offset, tvlv_len = 0;
+ unsigned char *to;
+ u8 type;
+
+ if (skb_queue_empty(head))
+ return NULL;
+
+ skb_aggr = batadv_aggr_alloc_skb(size);
+ if (!skb_aggr) {
+ batadv_aggr_skb_queue_free(head);
+ return NULL;
+ }
+
+ aggr_packet = (struct batadv_aggr_packet *)skb_network_header(skb_aggr);
+
+ skb_queue_walk_safe(head, skb, skb_tmp) {
+ offset = skb_network_offset(skb);
+ offset += sizeof(aggr_packet->packet_type);
+ offset += sizeof(aggr_packet->version);
+ len = skb->len - offset;
+ type = batadv_aggr_get_pkttype(skb);
+
+ batadv_aggr_put_tvlvhdr(skb_aggr, type, len);
+ to = skb_put(skb_aggr, len);
+ skb_copy_bits(skb, offset, to, len);
+ skb_unlink(skb, head);
+ consume_skb(skb);
+
+ tvlv_len += len + sizeof(struct batadv_tvlv_hdr);
+ }
+
+ aggr_packet->tvlv_len = htons(tvlv_len);
+
+ return skb_aggr;
+}
+
+/**
+ * __batadv_aggr_send_chunk - send a prepared aggregation packet
+ * @hard_iface: the interface to transmit on
+ * @skb: the prepared aggregation packet to send
+ */
+static void __batadv_aggr_send_chunk(struct batadv_hard_iface *hard_iface,
+ struct sk_buff *skb)
+{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+
+ batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_TX);
+ batadv_add_counter(bat_priv, BATADV_CNT_AGGR_TX_BYTES,
+ skb->len + ETH_HLEN);
+
+ /* ToDo: Track transmission failures? */
+ batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr);
+}
+
+/**
+ * batadv_aggr_send_chunk - prepare and transmit an aggregation packet
+ * @hard_iface: the interface to transmit on
+ *
+ * Fetches as many packets from the aggregation queue of the provided interface
+ * as fit into a single aggregation packet. Then aggregates them into such an
+ * aggregation packet and transmits the final aggregate.
+ *
+ * Return: True if there are no packets in the aggregation queue
+ * of the provided interface left afterwards, false otherwise.
+ */
+static bool batadv_aggr_send_chunk(struct batadv_hard_iface *hard_iface)
+{
+ struct sk_buff_head head;
+ struct sk_buff *skb;
+ int size = 0;
+ bool emptied;
+
+ skb_queue_head_init(&head);
+ emptied = batadv_aggr_get_chunk(hard_iface, &head, &size);
+
+ skb = batadv_aggr_squash_chunk(&head, size);
+ if (!skb)
+ goto out;
+
+ __batadv_aggr_send_chunk(hard_iface, skb);
+
+out:
+ return emptied;
+}
+
+/**
+ * batadv_aggr_work - periodic aggregation worker
+ * @work: work queue item
+ *
+ * Prepares and sends out an aggregation packet. In the end rearms the timer
+ * either to the next aggregation interval, or if there were still packets left
+ * in the aggregation queue, sets it to an earlier time.
+ */
+static void batadv_aggr_work(struct work_struct *work)
+{
+ struct batadv_hard_iface_aggr *aggr;
+ struct batadv_hard_iface *hard_iface;
+ struct batadv_priv *bat_priv;
+ bool emptied;
+
+ aggr = container_of(work, struct batadv_hard_iface_aggr, work.work);
+ hard_iface = container_of(aggr, struct batadv_hard_iface, aggr);
+ bat_priv = netdev_priv(hard_iface->soft_iface);
+
+ emptied = batadv_aggr_send_chunk(hard_iface);
+ if (emptied) {
+ batadv_aggr_hardif_start(hard_iface);
+ } else {
+ batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_URGENT);
+ batadv_aggr_hardif_start_urgent(hard_iface);
+ }
+}
+
+/**
+ * batadv_aggr_queue - queue a broadcast packet for aggregation
+ * @skb: the packet to queue
+ * @hard_iface: the interface to queue on
+ *
+ * Return: NET_XMIT_SUCCESS if the skb was queued, NET_XMIT_DROP otherwise.
+ * The former consumes the skb.
+ */
+int batadv_aggr_queue(struct sk_buff *skb, struct batadv_hard_iface *hard_iface)
+{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+
+ if (!atomic_read(&bat_priv->aggregation))
+ return NET_XMIT_DROP;
+
+ if (atomic_read(&bat_priv->aggr_num_disabled))
+ return NET_XMIT_DROP;
+
+ if (batadv_aggr_queue_is_full(hard_iface)) {
+ batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_QUEUE_FULL);
+ return NET_XMIT_DROP;
+ }
+
+ spin_lock_bh(&hard_iface->aggr.aggr_list_lock);
+ skb_queue_tail(&hard_iface->aggr.aggr_list, skb);
+ spin_unlock_bh(&hard_iface->aggr.aggr_list_lock);
+
+ batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX);
+ batadv_add_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX_BYTES,
+ skb->len + ETH_HLEN);
+
+ return NET_XMIT_SUCCESS;
+}
+
+/**
+ * batadv_aggr_purge_orig - reset originator aggregation state modifications
+ * @orig: the originator which is going to get purged
+ */
+void batadv_aggr_purge_orig(struct batadv_orig_node *orig)
+{
+ struct batadv_priv *bat_priv = orig->bat_priv;
+
+ if (!test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities) &&
+ test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capa_initialized))
+ atomic_dec(&bat_priv->aggr_num_disabled);
+}
+
+/**
+ * batadv_aggr_hardif_init - initialize an interface for aggregation
+ * @hard_iface: the interface to initialize
+ */
+void batadv_aggr_hardif_init(struct batadv_hard_iface *hard_iface)
+{
+ INIT_DELAYED_WORK(&hard_iface->aggr.work, batadv_aggr_work);
+ skb_queue_head_init(&hard_iface->aggr.aggr_list);
+ spin_lock_init(&hard_iface->aggr.aggr_list_lock);
+}
+
+/**
* batadv_aggr_add_counter_rx - update aggregation rx statistics
* @bat_priv: the bat priv with all the soft interface information
* @skb: the skb to count
@@ -150,6 +565,55 @@ static int batadv_aggr_tvlv_handler(struct batadv_priv *bat_priv,
}
/**
+ * batadv_aggr_ogm_handler - process incoming aggregation tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tvlv_value: tvlv buffer containing an aggregated broadcast packet
+ * @tvlv_value_len: tvlv buffer length
+ * @ctx: handler specific context information
+ * (here: recv_if, h_source and packet type of aggregate)
+ *
+ * Parses an aggregation tvlv attached to an originator message and updates
+ * aggregation capabilities accordingly.
+ *
+ * Return: Always NET_RX_SUCCESS.
+ */
+static int batadv_aggr_ogm_handler(struct batadv_priv *bat_priv,
+ void *tvlv_value, u16 tvlv_value_len,
+ void *ctx)
+{
+ struct batadv_orig_node *orig = ctx;
+ bool orig_aggr_enabled = !!tvlv_value;
+ bool orig_initialized;
+
+ orig_initialized = test_bit(BATADV_ORIG_CAPA_HAS_AGGR,
+ &orig->capa_initialized);
+
+ /* If aggregation support is turned on decrease the disabled aggregation
+ * node counter only if we had increased it for this node before. If
+ * this is a completely new orig_node no need to decrease the counter.
+ */
+ if (orig_aggr_enabled &&
+ !test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities)) {
+ if (orig_initialized)
+ atomic_dec(&bat_priv->aggr_num_disabled);
+ set_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities);
+ /* If aggregation support is being switched off or if this is an initial
+ * OGM without aggregation support then increase the disabled
+ * aggregation node counter.
+ */
+ } else if (!orig_aggr_enabled &&
+ (test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities) ||
+ !orig_initialized)) {
+ atomic_inc(&bat_priv->aggr_num_disabled);
+ clear_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities);
+ }
+
+ set_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capa_initialized);
+
+ return NET_RX_SUCCESS;
+}
+
+/**
* batadv_aggr_mesh_init - initialise the generic aggregation engine
* @bat_priv: the bat priv with all the soft interface information
*
@@ -160,6 +624,13 @@ int batadv_aggr_mesh_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register2(bat_priv, batadv_aggr_tvlv_handler,
BATADV_BCAST_AGGR, BATADV_TVLV_ANY, 1,
BATADV_TVLV_HANDLER_MORECTX);
+ batadv_tvlv_handler_register2(bat_priv, batadv_aggr_ogm_handler,
+ BATADV_IV_OGM, BATADV_TVLV_AGGR, 1,
+ BATADV_TVLV_HANDLER_CIFNOTFND);
+ batadv_tvlv_handler_register2(bat_priv, batadv_aggr_ogm_handler,
+ BATADV_OGM2, BATADV_TVLV_AGGR, 1,
+ BATADV_TVLV_HANDLER_CIFNOTFND);
+
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_AGGR, 1, NULL, 0);
return 0;
@@ -172,6 +643,10 @@ int batadv_aggr_mesh_init(struct batadv_priv *bat_priv)
void batadv_aggr_mesh_free(struct batadv_priv *bat_priv)
{
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_AGGR, 1);
+ batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, BATADV_TVLV_AGGR,
+ 1);
+ batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM,
+ BATADV_TVLV_AGGR, 1);
batadv_tvlv_handler_unregister2(bat_priv, BATADV_BCAST_AGGR,
BATADV_TVLV_ANY, 1);
}
@@ -27,6 +27,14 @@ struct sk_buff;
void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv,
struct sk_buff *skb);
+void batadv_aggr_hardif_start(struct batadv_hard_iface *hard_iface);
+void batadv_aggr_hardif_stop(struct batadv_hard_iface *hard_iface);
+
+int batadv_aggr_queue(struct sk_buff *skb,
+ struct batadv_hard_iface *hard_iface);
+
+void batadv_aggr_purge_orig(struct batadv_orig_node *orig);
+void batadv_aggr_hardif_init(struct batadv_hard_iface *hard_iface);
int batadv_aggr_mesh_init(struct batadv_priv *bat_priv);
void batadv_aggr_mesh_free(struct batadv_priv *bat_priv);
@@ -37,6 +45,29 @@ static inline void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv,
{
}
+static inline void
+batadv_aggr_hardif_start(struct batadv_hard_iface *hard_iface)
+{
+}
+
+static inline void batadv_aggr_hardif_stop(struct batadv_hard_iface *hard_iface)
+{
+}
+
+static inline int batadv_aggr_queue(struct sk_buff *skb,
+ struct batadv_hard_iface *hard_iface)
+{
+ return NET_XMIT_DROP;
+}
+
+static inline void batadv_aggr_purge_orig(struct batadv_orig_node *orig)
+{
+}
+
+static inline void batadv_aggr_hardif_init(struct batadv_hard_iface *hard_iface)
+{
+}
+
static inline int batadv_aggr_mesh_init(struct batadv_priv *bat_priv)
{
return 0;
@@ -684,7 +684,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
unsigned int skb_size;
atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left;
- if (atomic_read(&bat_priv->aggregated_ogms) &&
+ if (atomic_read(&bat_priv->aggregation) &&
packet_len < BATADV_MAX_AGGREGATION_BYTES)
skb_size = BATADV_MAX_AGGREGATION_BYTES;
else
@@ -777,7 +777,7 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
/* 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) {
+ if (atomic_read(&bat_priv->aggregation) && !own_packet) {
hlist_for_each_entry(forw_packet_pos,
&bat_priv->forw_bat_list, list) {
if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet,
@@ -803,7 +803,7 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
* we hold it back for a while, so that it might be aggregated
* later on
*/
- if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+ if (!own_packet && atomic_read(&bat_priv->aggregation))
send_time += max_aggregation_jiffies;
batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
@@ -38,6 +38,7 @@
#include <net/net_namespace.h>
#include <net/rtnetlink.h>
+#include "aggregation.h"
#include "bat_v.h"
#include "bridge_loop_avoidance.h"
#include "debugfs.h"
@@ -746,6 +747,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (ret < 0)
goto err_upper;
+ batadv_aggr_hardif_start(hard_iface);
+
hard_iface->if_num = bat_priv->num_ifaces;
bat_priv->num_ifaces++;
hard_iface->if_status = BATADV_IF_INACTIVE;
@@ -832,6 +835,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
batadv_hardif_put(new_if);
}
+ batadv_aggr_hardif_stop(hard_iface);
bat_priv->algo_ops->iface.disable(hard_iface);
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
@@ -901,6 +905,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
batadv_v_hardif_init(hard_iface);
+ batadv_aggr_hardif_init(hard_iface);
batadv_check_known_mac_addr(hard_iface->net_dev);
kref_get(&hard_iface->refcount);
@@ -349,6 +349,16 @@ int batadv_max_header_len(void)
header_len = max_t(int, header_len,
sizeof(struct batadv_coded_packet));
#endif
+#ifdef CONFIG_BATMAN_ADV_AGGR
+ header_len = max_t(int, header_len,
+ sizeof(struct batadv_aggr_packet) +
+ sizeof(struct batadv_tvlv_hdr) +
+ sizeof(struct batadv_bcast_packet) -
+ sizeof(((struct batadv_bcast_packet *)0)
+ ->packet_type) -
+ sizeof(((struct batadv_bcast_packet *)0)
+ ->version));
+#endif
return header_len + ETH_HLEN;
}
@@ -148,6 +148,7 @@ enum batadv_mesh_state {
#define BATADV_BCAST_QUEUE_LEN 256
#define BATADV_BATMAN_QUEUE_LEN 256
+#define BATADV_AGGR_QUEUE_LEN 512
enum batadv_uev_action {
BATADV_UEV_ADD = 0,
@@ -38,6 +38,7 @@
#include <net/sock.h>
#include <uapi/linux/batman_adv.h>
+#include "aggregation.h"
#include "bat_algo.h"
#include "distributed-arp-table.h"
#include "fragmentation.h"
@@ -857,6 +858,7 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
batadv_mcast_purge_orig(orig_node);
batadv_frag_purge_orig(orig_node, NULL);
+ batadv_aggr_purge_orig(orig_node);
if (orig_node->bat_priv->algo_ops->orig.free)
orig_node->bat_priv->algo_ops->orig.free(orig_node);
@@ -40,6 +40,7 @@
#include <linux/stddef.h>
#include <linux/workqueue.h>
+#include "aggregation.h"
#include "distributed-arp-table.h"
#include "fragmentation.h"
#include "gateway_client.h"
@@ -126,6 +127,14 @@ int batadv_send_skb_packet(struct sk_buff *skb,
int batadv_send_broadcast_skb(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
+ int ret;
+
+ skb_reset_network_header(skb);
+
+ ret = batadv_aggr_queue(skb, hard_iface);
+ if (ret == NET_XMIT_SUCCESS)
+ return ret;
+
return batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr);
}
@@ -779,7 +779,10 @@ static int batadv_softif_init_late(struct net_device *dev)
if (!bat_priv->bat_counters)
return -ENOMEM;
- atomic_set(&bat_priv->aggregated_ogms, 1);
+ atomic_set(&bat_priv->aggregation, 1);
+#ifdef CONFIG_BATMAN_ADV_AGGR
+ atomic_set(&bat_priv->aggr_num_disabled, 0);
+#endif
atomic_set(&bat_priv->bonding, 0);
#ifdef CONFIG_BATMAN_ADV_BLA
atomic_set(&bat_priv->bridge_loop_avoidance, 1);
@@ -962,10 +965,16 @@ static const struct {
{ "frag_fwd" },
{ "frag_fwd_bytes" },
#ifdef CONFIG_BATMAN_ADV_AGGR
+ { "aggr_tx" },
+ { "aggr_tx_bytes" },
{ "aggr_rx" },
{ "aggr_rx_bytes" },
+ { "aggr_parts_tx" },
+ { "aggr_parts_tx_bytes" },
{ "aggr_parts_rx" },
{ "aggr_parts_rx_bytes" },
+ { "aggr_queue_full" },
+ { "aggr_urgent" },
#endif
{ "tt_request_tx" },
{ "tt_request_rx" },
@@ -665,7 +665,9 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
return count;
}
-BATADV_ATTR_SIF_BOOL(aggregated_ogms, 0644, NULL);
+BATADV_ATTR_SIF_BOOL(aggregation, 0644, NULL);
+static BATADV_ATTR(aggregated_ogms, 0644, batadv_show_aggregation,
+ batadv_store_aggregation);
BATADV_ATTR_SIF_BOOL(bonding, 0644, NULL);
#ifdef CONFIG_BATMAN_ADV_BLA
BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, 0644, batadv_bla_status_update);
@@ -698,6 +700,7 @@ static BATADV_ATTR(isolation_mark, 0644, batadv_show_isolation_mark,
static struct batadv_attribute *batadv_mesh_attrs[] = {
&batadv_attr_aggregated_ogms,
+ &batadv_attr_aggregation,
&batadv_attr_bonding,
#ifdef CONFIG_BATMAN_ADV_BLA
&batadv_attr_bridge_loop_avoidance,
@@ -133,6 +133,20 @@ enum batadv_hard_iface_wifi_flags {
BATADV_HARDIF_WIFI_CFG80211_INDIRECT = BIT(3),
};
+#ifdef CONFIG_BATMAN_ADV_AGGR
+/**
+ * struct batadv_hard_iface_aggr - per hard-interface aggregation data
+ * @aggr_list: list for to be aggregated broadcast packets
+ * @aggr_list_lock: lock protecting aggr_list
+ * @work: work item for periodic aggregation packet transmissions
+ */
+struct batadv_hard_iface_aggr {
+ struct sk_buff_head aggr_list;
+ spinlock_t aggr_list_lock; /* protects aggr_list */
+ struct delayed_work work;
+};
+#endif
+
/**
* struct batadv_hard_iface - network device known to batman-adv
* @list: list node for batadv_hardif_list
@@ -149,6 +163,7 @@ enum batadv_hard_iface_wifi_flags {
* @rcu: struct used for freeing in an RCU-safe manner
* @bat_iv: per hard-interface B.A.T.M.A.N. IV data
* @bat_v: per hard-interface B.A.T.M.A.N. V data
+ * @aggr: per hard-interface aggregation data
* @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
* @neigh_list: list of unique single hop neighbors via this interface
* @neigh_list_lock: lock protecting neigh_list
@@ -169,6 +184,9 @@ struct batadv_hard_iface {
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
struct batadv_hard_iface_bat_v bat_v;
#endif
+#ifdef CONFIG_BATMAN_ADV_AGGR
+ struct batadv_hard_iface_aggr aggr;
+#endif
struct dentry *debug_dir;
struct hlist_head neigh_list;
/* neigh_list_lock protects: neigh_list */
@@ -376,12 +394,14 @@ struct batadv_orig_node {
* @BATADV_ORIG_CAPA_HAS_TT: orig node has tt capability
* @BATADV_ORIG_CAPA_HAS_MCAST: orig node has some multicast capability
* (= orig node announces a tvlv of type BATADV_TVLV_MCAST)
+ * @BATADV_ORIG_CAPA_HAS_AGGR: orig node has broadcast aggregation capability
*/
enum batadv_orig_capabilities {
BATADV_ORIG_CAPA_HAS_DAT,
BATADV_ORIG_CAPA_HAS_NC,
BATADV_ORIG_CAPA_HAS_TT,
BATADV_ORIG_CAPA_HAS_MCAST,
+ BATADV_ORIG_CAPA_HAS_AGGR,
};
/**
@@ -556,10 +576,17 @@ struct batadv_bcast_duplist_entry {
* @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter
* @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter
* @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter
+ * @BATADV_CNT_AGGR_TX: transmitted aggregation traffic packet count
+ * @BATADV_CNT_AGGR_TX_BYTES: transmitted aggregation traffic bytes counter
* @BATADV_CNT_AGGR_RX: received aggregation traffic packet count
* @BATADV_CNT_AGGR_RX_BYTES: received aggregation traffic bytes counter
+ * @BATADV_CNT_AGGR_PARTS_TX: transmitted aggregated traffic packet counter
+ * @BATADV_CNT_AGGR_PARTS_TX_BYTES: transmitted aggregated bytes counter
* @BATADV_CNT_AGGR_PARTS_RX: received aggregated traffic packet counter
* @BATADV_CNT_AGGR_PARTS_RX_BYTES: received aggregated bytes counter
+ * @BATADV_CNT_AGGR_QUEUE_FULL: counter for packets dismissed for aggregation
+ * due to a full aggregation queue
+ * @BATADV_CNT_AGGR_URGENT: counter for early aggregation packet transmissions
* @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter
* @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter
* @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter
@@ -604,10 +631,16 @@ enum batadv_counters {
BATADV_CNT_FRAG_FWD,
BATADV_CNT_FRAG_FWD_BYTES,
#ifdef CONFIG_BATMAN_ADV_AGGR
+ BATADV_CNT_AGGR_TX,
+ BATADV_CNT_AGGR_TX_BYTES,
BATADV_CNT_AGGR_RX,
BATADV_CNT_AGGR_RX_BYTES,
+ BATADV_CNT_AGGR_PARTS_TX,
+ BATADV_CNT_AGGR_PARTS_TX_BYTES,
BATADV_CNT_AGGR_PARTS_RX,
BATADV_CNT_AGGR_PARTS_RX_BYTES,
+ BATADV_CNT_AGGR_QUEUE_FULL,
+ BATADV_CNT_AGGR_URGENT,
#endif
BATADV_CNT_TT_REQUEST_TX,
BATADV_CNT_TT_REQUEST_RX,
@@ -1011,7 +1044,9 @@ struct batadv_priv_bat_v {
* @mesh_state: current status of the mesh (inactive/active/deactivating)
* @soft_iface: net device which holds this struct as private data
* @bat_counters: mesh internal traffic statistic counters (see batadv_counters)
- * @aggregated_ogms: bool indicating whether OGM aggregation is enabled
+ * @aggregation: bool indicating whether aggregation is enabled
+ * @aggr_num_disabled: number of nodes that have no broadcast aggregation
+ * capability
* @bonding: bool indicating whether traffic bonding is enabled
* @fragmentation: bool indicating whether traffic fragmentation is enabled
* @packet_size_max: max packet size that can be transmitted via
@@ -1065,7 +1100,10 @@ struct batadv_priv {
atomic_t mesh_state;
struct net_device *soft_iface;
u64 __percpu *bat_counters; /* Per cpu counters */
- atomic_t aggregated_ogms;
+ atomic_t aggregation;
+#ifdef CONFIG_BATMAN_ADV_AGGR
+ atomic_t aggr_num_disabled;
+#endif
atomic_t bonding;
atomic_t fragmentation;
atomic_t packet_size_max;