seq/proc: modify seq_put_decimal_[u]ll to take a const char *, not char
[cascardo/linux.git] / net / batman-adv / translation-table.c
index 7e6df7a..7f66309 100644 (file)
 #include <linux/bitops.h>
 #include <linux/bug.h>
 #include <linux/byteorder/generic.h>
+#include <linux/cache.h>
 #include <linux/compiler.h>
 #include <linux/crc32c.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
 #include <linux/fs.h>
 #include <linux/if_ether.h>
+#include <linux/init.h>
 #include <linux/jhash.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/lockdep.h>
 #include <linux/netdevice.h>
+#include <linux/netlink.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
 #include <linux/seq_file.h>
+#include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/stddef.h>
 #include <linux/string.h>
 #include <linux/workqueue.h>
+#include <net/genetlink.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+#include <uapi/linux/batman_adv.h>
 
 #include "bridge_loop_avoidance.h"
 #include "hard-interface.h"
 #include "hash.h"
 #include "log.h"
 #include "multicast.h"
+#include "netlink.h"
 #include "originator.h"
 #include "packet.h"
 #include "soft-interface.h"
 #include "tvlv.h"
 
+static struct kmem_cache *batadv_tl_cache __read_mostly;
+static struct kmem_cache *batadv_tg_cache __read_mostly;
+static struct kmem_cache *batadv_tt_orig_cache __read_mostly;
+static struct kmem_cache *batadv_tt_change_cache __read_mostly;
+static struct kmem_cache *batadv_tt_req_cache __read_mostly;
+static struct kmem_cache *batadv_tt_roam_cache __read_mostly;
+
 /* hash class keys */
 static struct lock_class_key batadv_tt_local_hash_lock_class_key;
 static struct lock_class_key batadv_tt_global_hash_lock_class_key;
@@ -204,6 +220,20 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
        return tt_global_entry;
 }
 
+/**
+ * batadv_tt_local_entry_free_rcu - free the tt_local_entry
+ * @rcu: rcu pointer of the tt_local_entry
+ */
+static void batadv_tt_local_entry_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_tt_local_entry *tt_local_entry;
+
+       tt_local_entry = container_of(rcu, struct batadv_tt_local_entry,
+                                     common.rcu);
+
+       kmem_cache_free(batadv_tl_cache, tt_local_entry);
+}
+
 /**
  * batadv_tt_local_entry_release - release tt_local_entry from lists and queue
  *  for free after rcu grace period
@@ -218,7 +248,7 @@ static void batadv_tt_local_entry_release(struct kref *ref)
 
        batadv_softif_vlan_put(tt_local_entry->vlan);
 
-       kfree_rcu(tt_local_entry, common.rcu);
+       call_rcu(&tt_local_entry->common.rcu, batadv_tt_local_entry_free_rcu);
 }
 
 /**
@@ -233,6 +263,20 @@ batadv_tt_local_entry_put(struct batadv_tt_local_entry *tt_local_entry)
                 batadv_tt_local_entry_release);
 }
 
+/**
+ * batadv_tt_global_entry_free_rcu - free the tt_global_entry
+ * @rcu: rcu pointer of the tt_global_entry
+ */
+static void batadv_tt_global_entry_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_tt_global_entry *tt_global_entry;
+
+       tt_global_entry = container_of(rcu, struct batadv_tt_global_entry,
+                                      common.rcu);
+
+       kmem_cache_free(batadv_tg_cache, tt_global_entry);
+}
+
 /**
  * batadv_tt_global_entry_release - release tt_global_entry from lists and queue
  *  for free after rcu grace period
@@ -246,7 +290,8 @@ static void batadv_tt_global_entry_release(struct kref *ref)
                                       common.refcount);
 
        batadv_tt_global_del_orig_list(tt_global_entry);
-       kfree_rcu(tt_global_entry, common.rcu);
+
+       call_rcu(&tt_global_entry->common.rcu, batadv_tt_global_entry_free_rcu);
 }
 
 /**
@@ -383,6 +428,19 @@ static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
        batadv_tt_global_size_mod(orig_node, vid, -1);
 }
 
+/**
+ * batadv_tt_orig_list_entry_free_rcu - free the orig_entry
+ * @rcu: rcu pointer of the orig_entry
+ */
+static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_tt_orig_list_entry *orig_entry;
+
+       orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu);
+
+       kmem_cache_free(batadv_tt_orig_cache, orig_entry);
+}
+
 /**
  * batadv_tt_orig_list_entry_release - release tt orig entry from lists and
  *  queue for free after rcu grace period
@@ -396,7 +454,7 @@ static void batadv_tt_orig_list_entry_release(struct kref *ref)
                                  refcount);
 
        batadv_orig_node_put(orig_entry->orig_node);
-       kfree_rcu(orig_entry, rcu);
+       call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu);
 }
 
 /**
@@ -426,7 +484,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
        bool event_removed = false;
        bool del_op_requested, del_op_entry;
 
-       tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC);
+       tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);
        if (!tt_change_node)
                return;
 
@@ -467,8 +525,8 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
                continue;
 del:
                list_del(&entry->list);
-               kfree(entry);
-               kfree(tt_change_node);
+               kmem_cache_free(batadv_tt_change_cache, entry);
+               kmem_cache_free(batadv_tt_change_cache, tt_change_node);
                event_removed = true;
                goto unlock;
        }
@@ -646,7 +704,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
                goto out;
        }
 
-       tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC);
+       tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
        if (!tt_local)
                goto out;
 
@@ -656,7 +714,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
                net_ratelimited_function(batadv_info, soft_iface,
                                         "adding TT local entry %pM to non-existent VLAN %d\n",
                                         addr, BATADV_PRINT_VID(vid));
-               kfree(tt_local);
+               kmem_cache_free(batadv_tl_cache, tt_local);
                tt_local = NULL;
                goto out;
        }
@@ -676,7 +734,6 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
        if (batadv_is_wifi_netdev(in_dev))
                tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
        kref_init(&tt_local->common.refcount);
-       kref_get(&tt_local->common.refcount);
        tt_local->last_seen = jiffies;
        tt_local->common.added_at = tt_local->last_seen;
        tt_local->vlan = vlan;
@@ -688,6 +745,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
            is_multicast_ether_addr(addr))
                tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
 
+       kref_get(&tt_local->common.refcount);
        hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
                                     batadv_choose_tt, &tt_local->common,
                                     &tt_local->common.hash_entry);
@@ -959,7 +1017,7 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
                        tt_diff_entries_count++;
                }
                list_del(&entry->list);
-               kfree(entry);
+               kmem_cache_free(batadv_tt_change_cache, entry);
        }
        spin_unlock_bh(&bat_priv->tt.changes_list_lock);
 
@@ -989,6 +1047,7 @@ container_register:
        kfree(tt_data);
 }
 
+#ifdef CONFIG_BATMAN_ADV_DEBUGFS
 int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
 {
        struct net_device *net_dev = (struct net_device *)seq->private;
@@ -1056,6 +1115,165 @@ out:
                batadv_hardif_put(primary_if);
        return 0;
 }
+#endif
+
+/**
+ * batadv_tt_local_dump_entry - Dump one TT local entry into a message
+ * @msg :Netlink message to dump into
+ * @portid: Port making netlink request
+ * @seq: Sequence number of netlink message
+ * @bat_priv: The bat priv with all the soft interface information
+ * @common: tt local & tt global common data
+ *
+ * Return: Error code, or 0 on success
+ */
+static int
+batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+                          struct batadv_priv *bat_priv,
+                          struct batadv_tt_common_entry *common)
+{
+       void *hdr;
+       struct batadv_softif_vlan *vlan;
+       struct batadv_tt_local_entry *local;
+       unsigned int last_seen_msecs;
+       u32 crc;
+
+       local = container_of(common, struct batadv_tt_local_entry, common);
+       last_seen_msecs = jiffies_to_msecs(jiffies - local->last_seen);
+
+       vlan = batadv_softif_vlan_get(bat_priv, common->vid);
+       if (!vlan)
+               return 0;
+
+       crc = vlan->tt.crc;
+
+       batadv_softif_vlan_put(vlan);
+
+       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
+                         NLM_F_MULTI,
+                         BATADV_CMD_GET_TRANSTABLE_LOCAL);
+       if (!hdr)
+               return -ENOBUFS;
+
+       if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
+           nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
+           nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
+           nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
+               goto nla_put_failure;
+
+       if (!(common->flags & BATADV_TT_CLIENT_NOPURGE) &&
+           nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, last_seen_msecs))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+/**
+ * batadv_tt_local_dump_bucket - Dump one TT local bucket into a message
+ * @msg: Netlink message to dump into
+ * @portid: Port making netlink request
+ * @seq: Sequence number of netlink message
+ * @bat_priv: The bat priv with all the soft interface information
+ * @head: Pointer to the list containing the local tt entries
+ * @idx_s: Number of entries to skip
+ *
+ * Return: Error code, or 0 on success
+ */
+static int
+batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+                           struct batadv_priv *bat_priv,
+                           struct hlist_head *head, int *idx_s)
+{
+       struct batadv_tt_common_entry *common;
+       int idx = 0;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(common, head, hash_entry) {
+               if (idx++ < *idx_s)
+                       continue;
+
+               if (batadv_tt_local_dump_entry(msg, portid, seq, bat_priv,
+                                              common)) {
+                       rcu_read_unlock();
+                       *idx_s = idx - 1;
+                       return -EMSGSIZE;
+               }
+       }
+       rcu_read_unlock();
+
+       *idx_s = 0;
+       return 0;
+}
+
+/**
+ * batadv_tt_local_dump - Dump TT local entries into a message
+ * @msg: Netlink message to dump into
+ * @cb: Parameters from query
+ *
+ * Return: Error code, or 0 on success
+ */
+int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(cb->skb->sk);
+       struct net_device *soft_iface;
+       struct batadv_priv *bat_priv;
+       struct batadv_hard_iface *primary_if = NULL;
+       struct batadv_hashtable *hash;
+       struct hlist_head *head;
+       int ret;
+       int ifindex;
+       int bucket = cb->args[0];
+       int idx = cb->args[1];
+       int portid = NETLINK_CB(cb->skb).portid;
+
+       ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
+       if (!ifindex)
+               return -EINVAL;
+
+       soft_iface = dev_get_by_index(net, ifindex);
+       if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       bat_priv = netdev_priv(soft_iface);
+
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       hash = bat_priv->tt.local_hash;
+
+       while (bucket < hash->size) {
+               head = &hash->table[bucket];
+
+               if (batadv_tt_local_dump_bucket(msg, portid, cb->nlh->nlmsg_seq,
+                                               bat_priv, head, &idx))
+                       break;
+
+               bucket++;
+       }
+
+       ret = msg->len;
+
+ out:
+       if (primary_if)
+               batadv_hardif_put(primary_if);
+       if (soft_iface)
+               dev_put(soft_iface);
+
+       cb->args[0] = bucket;
+       cb->args[1] = idx;
+
+       return ret;
+}
 
 static void
 batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
@@ -1259,7 +1477,7 @@ static void batadv_tt_changes_list_free(struct batadv_priv *bat_priv)
        list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
                                 list) {
                list_del(&entry->list);
-               kfree(entry);
+               kmem_cache_free(batadv_tt_change_cache, entry);
        }
 
        atomic_set(&bat_priv->tt.local_changes, 0);
@@ -1341,7 +1559,7 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
                goto out;
        }
 
-       orig_entry = kzalloc(sizeof(*orig_entry), GFP_ATOMIC);
+       orig_entry = kmem_cache_zalloc(batadv_tt_orig_cache, GFP_ATOMIC);
        if (!orig_entry)
                goto out;
 
@@ -1351,9 +1569,9 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
        orig_entry->orig_node = orig_node;
        orig_entry->ttvn = ttvn;
        kref_init(&orig_entry->refcount);
-       kref_get(&orig_entry->refcount);
 
        spin_lock_bh(&tt_global->list_lock);
+       kref_get(&orig_entry->refcount);
        hlist_add_head_rcu(&orig_entry->list,
                           &tt_global->orig_list);
        spin_unlock_bh(&tt_global->list_lock);
@@ -1411,7 +1629,8 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
                goto out;
 
        if (!tt_global_entry) {
-               tt_global_entry = kzalloc(sizeof(*tt_global_entry), GFP_ATOMIC);
+               tt_global_entry = kmem_cache_zalloc(batadv_tg_cache,
+                                                   GFP_ATOMIC);
                if (!tt_global_entry)
                        goto out;
 
@@ -1428,13 +1647,13 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
                if (flags & BATADV_TT_CLIENT_ROAM)
                        tt_global_entry->roam_at = jiffies;
                kref_init(&common->refcount);
-               kref_get(&common->refcount);
                common->added_at = jiffies;
 
                INIT_HLIST_HEAD(&tt_global_entry->orig_list);
                atomic_set(&tt_global_entry->orig_list_count, 0);
                spin_lock_init(&tt_global_entry->list_lock);
 
+               kref_get(&common->refcount);
                hash_added = batadv_hash_add(bat_priv->tt.global_hash,
                                             batadv_compare_tt,
                                             batadv_choose_tt, common,
@@ -1579,6 +1798,7 @@ batadv_transtable_best_orig(struct batadv_priv *bat_priv,
        return best_entry;
 }
 
+#ifdef CONFIG_BATMAN_ADV_DEBUGFS
 /**
  * batadv_tt_global_print_entry - print all orig nodes who announce the address
  *  for this global entry
@@ -1702,6 +1922,219 @@ out:
                batadv_hardif_put(primary_if);
        return 0;
 }
+#endif
+
+/**
+ * batadv_tt_global_dump_subentry - Dump all TT local entries into a message
+ * @msg: Netlink message to dump into
+ * @portid: Port making netlink request
+ * @seq: Sequence number of netlink message
+ * @common: tt local & tt global common data
+ * @orig: Originator node announcing a non-mesh client
+ * @best: Is the best originator for the TT entry
+ *
+ * Return: Error code, or 0 on success
+ */
+static int
+batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
+                              struct batadv_tt_common_entry *common,
+                              struct batadv_tt_orig_list_entry *orig,
+                              bool best)
+{
+       void *hdr;
+       struct batadv_orig_node_vlan *vlan;
+       u8 last_ttvn;
+       u32 crc;
+
+       vlan = batadv_orig_node_vlan_get(orig->orig_node,
+                                        common->vid);
+       if (!vlan)
+               return 0;
+
+       crc = vlan->tt.crc;
+
+       batadv_orig_node_vlan_put(vlan);
+
+       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
+                         NLM_F_MULTI,
+                         BATADV_CMD_GET_TRANSTABLE_GLOBAL);
+       if (!hdr)
+               return -ENOBUFS;
+
+       last_ttvn = atomic_read(&orig->orig_node->last_ttvn);
+
+       if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
+           nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
+                   orig->orig_node->orig) ||
+           nla_put_u8(msg, BATADV_ATTR_TT_TTVN, orig->ttvn) ||
+           nla_put_u8(msg, BATADV_ATTR_TT_LAST_TTVN, last_ttvn) ||
+           nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
+           nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
+           nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
+               goto nla_put_failure;
+
+       if (best && nla_put_flag(msg, BATADV_ATTR_FLAG_BEST))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+/**
+ * batadv_tt_global_dump_entry - Dump one TT global entry into a message
+ * @msg: Netlink message to dump into
+ * @portid: Port making netlink request
+ * @seq: Sequence number of netlink message
+ * @bat_priv: The bat priv with all the soft interface information
+ * @common: tt local & tt global common data
+ * @sub_s: Number of entries to skip
+ *
+ * This function assumes the caller holds rcu_read_lock().
+ *
+ * Return: Error code, or 0 on success
+ */
+static int
+batadv_tt_global_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+                           struct batadv_priv *bat_priv,
+                           struct batadv_tt_common_entry *common, int *sub_s)
+{
+       struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
+       struct batadv_tt_global_entry *global;
+       struct hlist_head *head;
+       int sub = 0;
+       bool best;
+
+       global = container_of(common, struct batadv_tt_global_entry, common);
+       best_entry = batadv_transtable_best_orig(bat_priv, global);
+       head = &global->orig_list;
+
+       hlist_for_each_entry_rcu(orig_entry, head, list) {
+               if (sub++ < *sub_s)
+                       continue;
+
+               best = (orig_entry == best_entry);
+
+               if (batadv_tt_global_dump_subentry(msg, portid, seq, common,
+                                                  orig_entry, best)) {
+                       *sub_s = sub - 1;
+                       return -EMSGSIZE;
+               }
+       }
+
+       *sub_s = 0;
+       return 0;
+}
+
+/**
+ * batadv_tt_global_dump_bucket - Dump one TT local bucket into a message
+ * @msg: Netlink message to dump into
+ * @portid: Port making netlink request
+ * @seq: Sequence number of netlink message
+ * @bat_priv: The bat priv with all the soft interface information
+ * @head: Pointer to the list containing the global tt entries
+ * @idx_s: Number of entries to skip
+ * @sub: Number of entries to skip
+ *
+ * Return: Error code, or 0 on success
+ */
+static int
+batadv_tt_global_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+                            struct batadv_priv *bat_priv,
+                            struct hlist_head *head, int *idx_s, int *sub)
+{
+       struct batadv_tt_common_entry *common;
+       int idx = 0;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(common, head, hash_entry) {
+               if (idx++ < *idx_s)
+                       continue;
+
+               if (batadv_tt_global_dump_entry(msg, portid, seq, bat_priv,
+                                               common, sub)) {
+                       rcu_read_unlock();
+                       *idx_s = idx - 1;
+                       return -EMSGSIZE;
+               }
+       }
+       rcu_read_unlock();
+
+       *idx_s = 0;
+       *sub = 0;
+       return 0;
+}
+
+/**
+ * batadv_tt_global_dump -  Dump TT global entries into a message
+ * @msg: Netlink message to dump into
+ * @cb: Parameters from query
+ *
+ * Return: Error code, or length of message on success
+ */
+int batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(cb->skb->sk);
+       struct net_device *soft_iface;
+       struct batadv_priv *bat_priv;
+       struct batadv_hard_iface *primary_if = NULL;
+       struct batadv_hashtable *hash;
+       struct hlist_head *head;
+       int ret;
+       int ifindex;
+       int bucket = cb->args[0];
+       int idx = cb->args[1];
+       int sub = cb->args[2];
+       int portid = NETLINK_CB(cb->skb).portid;
+
+       ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
+       if (!ifindex)
+               return -EINVAL;
+
+       soft_iface = dev_get_by_index(net, ifindex);
+       if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       bat_priv = netdev_priv(soft_iface);
+
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       hash = bat_priv->tt.global_hash;
+
+       while (bucket < hash->size) {
+               head = &hash->table[bucket];
+
+               if (batadv_tt_global_dump_bucket(msg, portid,
+                                                cb->nlh->nlmsg_seq, bat_priv,
+                                                head, &idx, &sub))
+                       break;
+
+               bucket++;
+       }
+
+       ret = msg->len;
+
+ out:
+       if (primary_if)
+               batadv_hardif_put(primary_if);
+       if (soft_iface)
+               dev_put(soft_iface);
+
+       cb->args[0] = bucket;
+       cb->args[1] = idx;
+       cb->args[2] = sub;
+
+       return ret;
+}
 
 /**
  * _batadv_tt_global_del_orig_entry - remove and free an orig_entry
@@ -2280,7 +2713,7 @@ static void batadv_tt_req_node_release(struct kref *ref)
 
        tt_req_node = container_of(ref, struct batadv_tt_req_node, refcount);
 
-       kfree(tt_req_node);
+       kmem_cache_free(batadv_tt_req_cache, tt_req_node);
 }
 
 /**
@@ -2367,7 +2800,7 @@ batadv_tt_req_node_new(struct batadv_priv *bat_priv,
                        goto unlock;
        }
 
-       tt_req_node = kmalloc(sizeof(*tt_req_node), GFP_ATOMIC);
+       tt_req_node = kmem_cache_alloc(batadv_tt_req_cache, GFP_ATOMIC);
        if (!tt_req_node)
                goto unlock;
 
@@ -3104,7 +3537,7 @@ static void batadv_tt_roam_list_free(struct batadv_priv *bat_priv)
 
        list_for_each_entry_safe(node, safe, &bat_priv->tt.roam_list, list) {
                list_del(&node->list);
-               kfree(node);
+               kmem_cache_free(batadv_tt_roam_cache, node);
        }
 
        spin_unlock_bh(&bat_priv->tt.roam_list_lock);
@@ -3121,7 +3554,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
                        continue;
 
                list_del(&node->list);
-               kfree(node);
+               kmem_cache_free(batadv_tt_roam_cache, node);
        }
        spin_unlock_bh(&bat_priv->tt.roam_list_lock);
 }
@@ -3162,7 +3595,8 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
        }
 
        if (!ret) {
-               tt_roam_node = kmalloc(sizeof(*tt_roam_node), GFP_ATOMIC);
+               tt_roam_node = kmem_cache_alloc(batadv_tt_roam_cache,
+                                               GFP_ATOMIC);
                if (!tt_roam_node)
                        goto unlock;
 
@@ -3865,3 +4299,85 @@ bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
 
        return ret;
 }
+
+/**
+ * batadv_tt_cache_init - Initialize tt memory object cache
+ *
+ * Return: 0 on success or negative error number in case of failure.
+ */
+int __init batadv_tt_cache_init(void)
+{
+       size_t tl_size = sizeof(struct batadv_tt_local_entry);
+       size_t tg_size = sizeof(struct batadv_tt_global_entry);
+       size_t tt_orig_size = sizeof(struct batadv_tt_orig_list_entry);
+       size_t tt_change_size = sizeof(struct batadv_tt_change_node);
+       size_t tt_req_size = sizeof(struct batadv_tt_req_node);
+       size_t tt_roam_size = sizeof(struct batadv_tt_roam_node);
+
+       batadv_tl_cache = kmem_cache_create("batadv_tl_cache", tl_size, 0,
+                                           SLAB_HWCACHE_ALIGN, NULL);
+       if (!batadv_tl_cache)
+               return -ENOMEM;
+
+       batadv_tg_cache = kmem_cache_create("batadv_tg_cache", tg_size, 0,
+                                           SLAB_HWCACHE_ALIGN, NULL);
+       if (!batadv_tg_cache)
+               goto err_tt_tl_destroy;
+
+       batadv_tt_orig_cache = kmem_cache_create("batadv_tt_orig_cache",
+                                                tt_orig_size, 0,
+                                                SLAB_HWCACHE_ALIGN, NULL);
+       if (!batadv_tt_orig_cache)
+               goto err_tt_tg_destroy;
+
+       batadv_tt_change_cache = kmem_cache_create("batadv_tt_change_cache",
+                                                  tt_change_size, 0,
+                                                  SLAB_HWCACHE_ALIGN, NULL);
+       if (!batadv_tt_change_cache)
+               goto err_tt_orig_destroy;
+
+       batadv_tt_req_cache = kmem_cache_create("batadv_tt_req_cache",
+                                               tt_req_size, 0,
+                                               SLAB_HWCACHE_ALIGN, NULL);
+       if (!batadv_tt_req_cache)
+               goto err_tt_change_destroy;
+
+       batadv_tt_roam_cache = kmem_cache_create("batadv_tt_roam_cache",
+                                                tt_roam_size, 0,
+                                                SLAB_HWCACHE_ALIGN, NULL);
+       if (!batadv_tt_roam_cache)
+               goto err_tt_req_destroy;
+
+       return 0;
+
+err_tt_req_destroy:
+       kmem_cache_destroy(batadv_tt_req_cache);
+       batadv_tt_req_cache = NULL;
+err_tt_change_destroy:
+       kmem_cache_destroy(batadv_tt_change_cache);
+       batadv_tt_change_cache = NULL;
+err_tt_orig_destroy:
+       kmem_cache_destroy(batadv_tt_orig_cache);
+       batadv_tt_orig_cache = NULL;
+err_tt_tg_destroy:
+       kmem_cache_destroy(batadv_tg_cache);
+       batadv_tg_cache = NULL;
+err_tt_tl_destroy:
+       kmem_cache_destroy(batadv_tl_cache);
+       batadv_tl_cache = NULL;
+
+       return -ENOMEM;
+}
+
+/**
+ * batadv_tt_cache_destroy - Destroy tt memory object cache
+ */
+void batadv_tt_cache_destroy(void)
+{
+       kmem_cache_destroy(batadv_tl_cache);
+       kmem_cache_destroy(batadv_tg_cache);
+       kmem_cache_destroy(batadv_tt_orig_cache);
+       kmem_cache_destroy(batadv_tt_change_cache);
+       kmem_cache_destroy(batadv_tt_req_cache);
+       kmem_cache_destroy(batadv_tt_roam_cache);
+}