batctl: page allocation failure

Message ID 10118093.UzH4NL7sLW@sven-edge (mailing list archive)
State RFC, archived
Headers

Commit Message

Sven Eckelmann Feb. 28, 2016, 4:23 p.m. UTC
  On Sunday 28 February 2016 00:39:08 Philipp Psurek wrote:
> Hi all,
> 
> I've got this error with
[..]
> 
> 
> ----------------------------------
[..]
> [Feb28 00:13] batctl: page allocation failure: order:4, mode:0x40d0

Does this always happen when you use `batctl o` on a system with many entries 
in the originator table? When yes, then you could try the attached (very 
rough) proof-of-concept patch. It is based on v2016.0-92-gd7c9a9e

Kind regards,
	Sven
  

Comments

Philipp Psurek Feb. 28, 2016, 5:17 p.m. UTC | #1
Hi Sven,

as already mentioned the failure does not happen any more. I'm sorry
bothering you with this unreproducible issue.

Am Sonntag, den 28.02.2016, 17:23 +0100 schrieb Sven Eckelmann:
> On Sunday 28 February 2016 00:39:08 Philipp Psurek wrote:
> > Hi all,
> > 
> > I've got this error with
> [..]
> > 
> > 
> > ----------------------------------
> [..]
> > [Feb28 00:13] batctl: page allocation failure: order:4, mode:0x40d0
> 
> Does this always happen when you use `batctl o` on a system with many 
> entries in the originator table?

I never saw this failure before. Executing 

# batctl o | wc -l
338

on an 32 MiB RAM router was never a problem.  I thought it would be the best reporting this issue.

> When yes, then you could try the attached (very rough) proof-of
> -concept patch. It is based on v2016.0-92-gd7c9a9e

You are a faster coder than me as email writer. I'll implement the
Patch in your 2nd email and report.

Best regards,
  Philipp
  

Patch

From: Sven Eckelmann <sven@narfation.org>
Date: Sun, 28 Feb 2016 17:13:12 +0100
Subject: [RFC] TEST: speedup originator output by using seq_operations
---
 net/batman-adv/bat_iv_ogm.c            | 93 ++++++++++++++--------------------
 net/batman-adv/bat_v.c                 | 91 +++++++++++++--------------------
 net/batman-adv/bridge_loop_avoidance.c |  4 +-
 net/batman-adv/debugfs.c               | 50 ++++++++++++++++--
 net/batman-adv/distributed-arp-table.c |  2 +-
 net/batman-adv/gateway_client.c        |  2 +-
 net/batman-adv/main.c                  |  5 +-
 net/batman-adv/main.h                  |  3 +-
 net/batman-adv/network-coding.c        |  2 +-
 net/batman-adv/originator.c            | 87 ++++++++++++++++++++++++++-----
 net/batman-adv/originator.h            |  7 ++-
 net/batman-adv/translation-table.c     |  4 +-
 net/batman-adv/types.h                 | 30 ++++++++++-
 13 files changed, 241 insertions(+), 139 deletions(-)

diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 2c65668..cddb3e8 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -1846,74 +1846,54 @@  batadv_iv_ogm_orig_print_neigh(struct batadv_orig_node *orig_node,
 	}
 }
 
-/**
- * batadv_iv_ogm_orig_print - print the originator table
- * @bat_priv: the bat priv with all the soft interface information
- * @seq: debugfs table seq_file struct
- * @if_outgoing: the outgoing interface for which this should be printed
- */
-static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv,
-				     struct seq_file *seq,
-				     struct batadv_hard_iface *if_outgoing)
+static void batadv_iv_ogm_oriq_seq_header(struct batadv_priv *bat_priv,
+					  struct seq_file *seq)
 {
-	struct batadv_neigh_node *neigh_node;
-	struct batadv_hashtable *hash = bat_priv->orig_hash;
-	int last_seen_msecs, last_seen_secs;
-	struct batadv_orig_node *orig_node;
-	struct batadv_neigh_ifinfo *n_ifinfo;
-	unsigned long last_seen_jiffies;
-	struct hlist_head *head;
-	int batman_count = 0;
-	u32 i;
-
 	seq_printf(seq, "  %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
 		   "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE,
 		   "Nexthop", "outgoingIF", "Potential nexthops");
+}
 
-	for (i = 0; i < hash->size; i++) {
-		head = &hash->table[i];
+static void batadv_iv_ogm_orig_seq_show(struct batadv_priv *bat_priv,
+					struct seq_file *seq,
+					struct batadv_orig_node *orig_node,
+					struct batadv_hard_iface *if_outgoing)
+{
+	struct batadv_neigh_node *neigh_node;
+	int last_seen_msecs, last_seen_secs;
+	struct batadv_neigh_ifinfo *n_ifinfo;
+	unsigned long last_seen_jiffies;
 
-		rcu_read_lock();
-		hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-			neigh_node = batadv_orig_router_get(orig_node,
-							    if_outgoing);
-			if (!neigh_node)
-				continue;
+	neigh_node = batadv_orig_router_get(orig_node, if_outgoing);
+	if (!neigh_node)
+		return;
 
-			n_ifinfo = batadv_neigh_ifinfo_get(neigh_node,
-							   if_outgoing);
-			if (!n_ifinfo)
-				goto next;
+	n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
+	if (!n_ifinfo)
+		goto next;
 
-			if (n_ifinfo->bat_iv.tq_avg == 0)
-				goto next;
+	if (n_ifinfo->bat_iv.tq_avg == 0)
+		goto next;
 
-			last_seen_jiffies = jiffies - orig_node->last_seen;
-			last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
-			last_seen_secs = last_seen_msecs / 1000;
-			last_seen_msecs = last_seen_msecs % 1000;
+	last_seen_jiffies = jiffies - orig_node->last_seen;
+	last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
+	last_seen_secs = last_seen_msecs / 1000;
+	last_seen_msecs = last_seen_msecs % 1000;
 
-			seq_printf(seq, "%pM %4i.%03is   (%3i) %pM [%10s]:",
-				   orig_node->orig, last_seen_secs,
-				   last_seen_msecs, n_ifinfo->bat_iv.tq_avg,
-				   neigh_node->addr,
-				   neigh_node->if_incoming->net_dev->name);
+	seq_printf(seq, "%pM %4i.%03is   (%3i) %pM [%10s]:",
+			orig_node->orig, last_seen_secs,
+			last_seen_msecs, n_ifinfo->bat_iv.tq_avg,
+			neigh_node->addr,
+			neigh_node->if_incoming->net_dev->name);
 
-			batadv_iv_ogm_orig_print_neigh(orig_node, if_outgoing,
-						       seq);
-			seq_puts(seq, "\n");
-			batman_count++;
+	batadv_iv_ogm_orig_print_neigh(orig_node, if_outgoing,
+					seq);
+	seq_puts(seq, "\n");
 
 next:
-			batadv_neigh_node_put(neigh_node);
-			if (n_ifinfo)
-				batadv_neigh_ifinfo_put(n_ifinfo);
-		}
-		rcu_read_unlock();
-	}
-
-	if (batman_count == 0)
-		seq_puts(seq, "No batman nodes in range ...\n");
+	batadv_neigh_node_put(neigh_node);
+	if (n_ifinfo)
+		batadv_neigh_ifinfo_put(n_ifinfo);
 }
 
 /**
@@ -2062,7 +2042,8 @@  static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
 	.bat_neigh_cmp = batadv_iv_ogm_neigh_cmp,
 	.bat_neigh_is_similar_or_better = batadv_iv_ogm_neigh_is_sob,
 	.bat_neigh_print = batadv_iv_neigh_print,
-	.bat_orig_print = batadv_iv_ogm_orig_print,
+	.bat_orig_seq_header = batadv_iv_ogm_oriq_seq_header,
+	.bat_orig_seq_show = batadv_iv_ogm_orig_seq_show,
 	.bat_orig_free = batadv_iv_ogm_orig_free,
 	.bat_orig_add_if = batadv_iv_ogm_orig_add_if,
 	.bat_orig_del_if = batadv_iv_ogm_orig_del_if,
diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
index 3315b9a..6d3ff2d 100644
--- a/net/batman-adv/bat_v.c
+++ b/net/batman-adv/bat_v.c
@@ -171,72 +171,52 @@  static void batadv_v_neigh_print(struct batadv_priv *bat_priv,
 		seq_puts(seq, "No batman nodes in range ...\n");
 }
 
-/**
- * batadv_v_orig_print - print the originator table
- * @bat_priv: the bat priv with all the soft interface information
- * @seq: debugfs table seq_file struct
- * @if_outgoing: the outgoing interface for which this should be printed
- */
-static void batadv_v_orig_print(struct batadv_priv *bat_priv,
-				struct seq_file *seq,
-				struct batadv_hard_iface *if_outgoing)
+static void batadv_v_orig_seq_header(struct batadv_priv *bat_priv,
+				     struct seq_file *seq)
 {
-	struct batadv_neigh_node *neigh_node;
-	struct batadv_hashtable *hash = bat_priv->orig_hash;
-	int last_seen_msecs, last_seen_secs;
-	struct batadv_orig_node *orig_node;
-	struct batadv_neigh_ifinfo *n_ifinfo;
-	unsigned long last_seen_jiffies;
-	struct hlist_head *head;
-	int batman_count = 0;
-	u32 i;
-
 	seq_printf(seq, "  %-15s %s (%11s) %17s [%10s]: %20s ...\n",
 		   "Originator", "last-seen", "throughput", "Nexthop",
 		   "outgoingIF", "Potential nexthops");
+}
 
-	for (i = 0; i < hash->size; i++) {
-		head = &hash->table[i];
+static void batadv_v_orig_seq_show(struct batadv_priv *bat_priv,
+				   struct seq_file *seq,
+				   struct batadv_orig_node *orig_node,
+				   struct batadv_hard_iface *if_outgoing)
+{
+	struct batadv_neigh_node *neigh_node;
+	struct batadv_neigh_ifinfo *n_ifinfo;
+	int last_seen_msecs, last_seen_secs;
+	unsigned long last_seen_jiffies;
 
-		rcu_read_lock();
-		hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-			neigh_node = batadv_orig_router_get(orig_node,
-							    if_outgoing);
-			if (!neigh_node)
-				continue;
+	neigh_node = batadv_orig_router_get(orig_node, if_outgoing);
+	if (!neigh_node)
+		return;
 
-			n_ifinfo = batadv_neigh_ifinfo_get(neigh_node,
-							   if_outgoing);
-			if (!n_ifinfo)
-				goto next;
+	n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
+	if (!n_ifinfo)
+		goto next;
 
-			last_seen_jiffies = jiffies - orig_node->last_seen;
-			last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
-			last_seen_secs = last_seen_msecs / 1000;
-			last_seen_msecs = last_seen_msecs % 1000;
+	last_seen_jiffies = jiffies - orig_node->last_seen;
+	last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
+	last_seen_secs = last_seen_msecs / 1000;
+	last_seen_msecs = last_seen_msecs % 1000;
 
-			seq_printf(seq, "%pM %4i.%03is (%9u.%1u) %pM [%10s]:",
-				   orig_node->orig, last_seen_secs,
-				   last_seen_msecs,
-				   n_ifinfo->bat_v.throughput / 10,
-				   n_ifinfo->bat_v.throughput % 10,
-				   neigh_node->addr,
-				   neigh_node->if_incoming->net_dev->name);
+	seq_printf(seq, "%pM %4i.%03is (%9u.%1u) %pM [%10s]:",
+			orig_node->orig, last_seen_secs,
+			last_seen_msecs,
+			n_ifinfo->bat_v.throughput / 10,
+			n_ifinfo->bat_v.throughput % 10,
+			neigh_node->addr,
+			neigh_node->if_incoming->net_dev->name);
 
-			batadv_v_orig_print_neigh(orig_node, if_outgoing, seq);
-			seq_puts(seq, "\n");
-			batman_count++;
+	batadv_v_orig_print_neigh(orig_node, if_outgoing, seq);
+	seq_puts(seq, "\n");
 
 next:
-			batadv_neigh_node_put(neigh_node);
-			if (n_ifinfo)
-				batadv_neigh_ifinfo_put(n_ifinfo);
-		}
-		rcu_read_unlock();
-	}
-
-	if (batman_count == 0)
-		seq_puts(seq, "No batman nodes in range ...\n");
+	batadv_neigh_node_put(neigh_node);
+	if (n_ifinfo)
+		batadv_neigh_ifinfo_put(n_ifinfo);
 }
 
 static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1,
@@ -281,7 +261,8 @@  static struct batadv_algo_ops batadv_batman_v __read_mostly = {
 	.bat_hardif_neigh_init = batadv_v_hardif_neigh_init,
 	.bat_ogm_emit = batadv_v_ogm_emit,
 	.bat_ogm_schedule = batadv_v_ogm_schedule,
-	.bat_orig_print = batadv_v_orig_print,
+	.bat_orig_seq_header = batadv_v_orig_seq_header,
+	.bat_orig_seq_show = batadv_v_orig_seq_show,
 	.bat_neigh_cmp = batadv_v_neigh_cmp,
 	.bat_neigh_is_similar_or_better = batadv_v_neigh_is_sob,
 	.bat_neigh_print = batadv_v_neigh_print,
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 642167f..c00b9b8 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -1807,7 +1807,7 @@  int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
 	bool is_own;
 	u8 *primary_addr;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
@@ -1865,7 +1865,7 @@  int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
 	bool is_own;
 	u8 *primary_addr;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index 3dc5208..ddaae44 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -269,11 +269,43 @@  static int neighbors_open(struct inode *inode, struct file *file)
 	return single_open(file, batadv_hardif_neigh_seq_print_text, net_dev);
 }
 
-static int batadv_originators_open(struct inode *inode, struct file *file)
+static const struct seq_operations batadv_orig_seq_ops = {
+	.start  = batadv_orig_seq_start,
+	.next   = batadv_orig_seq_next,
+	.stop   = batadv_orig_seq_stop,
+	.show   = batadv_orig_seq_show,
+};
+
+/**
+ * batadv_seq_open - TODO
+ * @inode: TODO
+ * @f: TODO
+ * @ops: TODO
+ * @size: TODO
+ *
+ * Return: TODO
+ */
+static int batadv_seq_open(struct inode *inode, struct file *f,
+			   const struct seq_operations *ops, int size)
 {
+	struct batadv_seq_private *p;
 	struct net_device *net_dev = (struct net_device *)inode->i_private;
 
-	return single_open(file, batadv_orig_seq_print_text, net_dev);
+	BUG_ON(size < sizeof(*p));
+
+	p = __seq_open_private(f, ops, size);
+	if (p == NULL)
+		return -ENOMEM;
+
+	p->net_dev = net_dev;
+
+	return 0;
+}
+
+static int batadv_originators_open(struct inode *inode, struct file *file)
+{
+	return batadv_seq_open(inode, file, &batadv_orig_seq_ops,
+			       sizeof(struct batadv_seq_hash_iter));
 }
 
 /**
@@ -375,6 +407,18 @@  struct batadv_debuginfo batadv_debuginfo_##_name = {	\
 		}					\
 }
 
+#define BATADV_DEBUGINFO_SEQ(_name, _mode, _open)	\
+struct batadv_debuginfo batadv_debuginfo_##_name = {	\
+	.attr = { .name = __stringify(_name),		\
+		  .mode = _mode, },			\
+	.fops = { .owner = THIS_MODULE,			\
+		  .open = _open,			\
+		  .read	= seq_read,			\
+		  .llseek = seq_lseek,			\
+		  .release = seq_release_private,	\
+		}					\
+}
+
 /* the following attributes are general and therefore they will be directly
  * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs
  */
@@ -387,7 +431,7 @@  static struct batadv_debuginfo *batadv_general_debuginfos[] = {
 
 /* The following attributes are per soft interface */
 static BATADV_DEBUGINFO(neighbors, S_IRUGO, neighbors_open);
-static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open);
+static BATADV_DEBUGINFO_SEQ(originators, S_IRUGO, batadv_originators_open);
 static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open);
 static BATADV_DEBUGINFO(transtable_global, S_IRUGO,
 			batadv_transtable_global_open);
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index 919a8d2..dc553e5 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -809,7 +809,7 @@  int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
 	int last_seen_msecs, last_seen_secs, last_seen_mins;
 	u32 i;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index c59aff5..1c2360c 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -637,7 +637,7 @@  int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
 	struct batadv_gw_node *gw_node;
 	int gw_count = 0;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index d64ddb9..74c81eb 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -269,13 +269,14 @@  bool batadv_is_my_mac(struct batadv_priv *bat_priv, const u8 *addr)
  * batadv_seq_print_text_primary_if_get - called from debugfs table printing
  *  function that requires the primary interface
  * @seq: debugfs table seq_file struct
+ * @net_dev: batman-adv net_device
  *
  * Return: primary interface if found or NULL otherwise.
  */
 struct batadv_hard_iface *
-batadv_seq_print_text_primary_if_get(struct seq_file *seq)
+batadv_seq_print_text_primary_if_get(struct seq_file *seq,
+				     struct net_device *net_dev)
 {
-	struct net_device *net_dev = (struct net_device *)seq->private;
 	struct batadv_priv *bat_priv = netdev_priv(net_dev);
 	struct batadv_hard_iface *primary_if;
 
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index e602408..94e9f57 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -203,7 +203,8 @@  int batadv_mesh_init(struct net_device *soft_iface);
 void batadv_mesh_free(struct net_device *soft_iface);
 bool batadv_is_my_mac(struct batadv_priv *bat_priv, const u8 *addr);
 struct batadv_hard_iface *
-batadv_seq_print_text_primary_if_get(struct seq_file *seq);
+batadv_seq_print_text_primary_if_get(struct seq_file *seq,
+				     struct net_device *net_dev);
 int batadv_max_header_len(void);
 void batadv_skb_set_priority(struct sk_buff *skb, int offset);
 int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index 32f9fa1..38fbecc 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -1905,7 +1905,7 @@  int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset)
 	struct batadv_nc_node *nc_node;
 	int i;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index e63d6a5..1b9faef 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -698,7 +698,7 @@  int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
 	struct batadv_priv *bat_priv = netdev_priv(net_dev);
 	struct batadv_hard_iface *primary_if;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		return 0;
 
@@ -1235,35 +1235,95 @@  void batadv_purge_orig_ref(struct batadv_priv *bat_priv)
 	_batadv_purge_orig(bat_priv);
 }
 
-int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
+static void batadv_orig_seq_show_header(struct seq_file *seq)
 {
-	struct net_device *net_dev = (struct net_device *)seq->private;
-	struct batadv_priv *bat_priv = netdev_priv(net_dev);
+	struct batadv_seq_hash_iter *iter = seq->private;
+	struct batadv_priv *bat_priv = netdev_priv(iter->p.net_dev);
 	struct batadv_hard_iface *primary_if;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, iter->p.net_dev);
 	if (!primary_if)
-		return 0;
+		return;
 
 	seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
 		   BATADV_SOURCE_VERSION, primary_if->net_dev->name,
-		   primary_if->net_dev->dev_addr, net_dev->name,
+		   primary_if->net_dev->dev_addr, iter->p.net_dev->name,
 		   bat_priv->bat_algo_ops->name);
 
 	batadv_hardif_put(primary_if);
 
-	if (!bat_priv->bat_algo_ops->bat_orig_print) {
-		seq_puts(seq,
-			 "No printing function for this routing protocol\n");
+	if (bat_priv->bat_algo_ops->bat_orig_seq_header)
+		bat_priv->bat_algo_ops->bat_orig_seq_header(bat_priv, seq);
+}
+
+void *batadv_orig_seq_start(struct seq_file *seq, loff_t *pos)
+	__acquires(RCU)
+{
+	rcu_read_lock();
+
+	if (*pos != 0)
+		return 0; // TODO some kind of index, hashbucket?
+	return SEQ_START_TOKEN;
+}
+
+void *batadv_orig_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct batadv_seq_hash_iter *iter = seq->private;
+	struct batadv_priv *bat_priv = netdev_priv(iter->p.net_dev);
+	struct batadv_hashtable *hash = bat_priv->orig_hash;
+	struct hlist_head *head;
+
+	if (v == SEQ_START_TOKEN) {
+		iter->bucket = 0;
+		iter->entry = NULL;
+		/* TODO seq_puts(seq, "No batman nodes in range ...\n"); */
+	} else {
+		iter->entry = rcu_dereference_raw(hlist_next_rcu(iter->entry));
+		if (!iter->entry)
+			iter->bucket++;
+	}
+
+	if (iter->entry)
+		return iter->entry;
+
+	for (; iter->bucket < hash->size; iter->bucket++) {
+		head = &hash->table[iter->bucket];
+		iter->entry = rcu_dereference_raw(hlist_first_rcu(head));
+
+		if (iter->entry)
+			return iter->entry;
+	}
+
+	return NULL;
+}
+
+int batadv_orig_seq_show(struct seq_file *seq, void *v)
+{
+	struct batadv_seq_hash_iter *iter = seq->private;
+	struct batadv_priv *bat_priv = netdev_priv(iter->p.net_dev);
+	struct batadv_orig_node *orig_node;
+
+	if (v == SEQ_START_TOKEN) {
+		batadv_orig_seq_show_header(seq);
 		return 0;
 	}
 
-	bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq,
-					       BATADV_IF_DEFAULT);
+	orig_node = hlist_entry(iter->entry, struct batadv_orig_node, hash_entry);
+
+	if (bat_priv->bat_algo_ops->bat_orig_seq_show)
+		bat_priv->bat_algo_ops->bat_orig_seq_show(bat_priv, seq,
+							  orig_node,
+							  BATADV_IF_DEFAULT);
 
 	return 0;
 }
 
+void batadv_orig_seq_stop(struct seq_file *seq, void *v)
+	__releases(RCU)
+{
+	rcu_read_unlock();
+}
+
 /**
  * batadv_orig_hardif_seq_print_text - writes originator infos for a specific
  *  outgoing interface
@@ -1285,6 +1345,8 @@  int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset)
 		goto out;
 	}
 
+	/*
+	TODO
 	bat_priv = netdev_priv(hard_iface->soft_iface);
 	if (!bat_priv->bat_algo_ops->bat_orig_print) {
 		seq_puts(seq,
@@ -1303,6 +1365,7 @@  int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset)
 		   hard_iface->soft_iface->name, bat_priv->bat_algo_ops->name);
 
 	bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq, hard_iface);
+	*/
 
 out:
 	if (hard_iface)
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index 64a8951..ba5a915 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -29,6 +29,7 @@ 
 #include <linux/stddef.h>
 #include <linux/types.h>
 
+#include "hard-interface.h"
 #include "hash.h"
 
 struct seq_file;
@@ -71,7 +72,11 @@  batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
 		       struct batadv_hard_iface *if_outgoing);
 void batadv_orig_ifinfo_put(struct batadv_orig_ifinfo *orig_ifinfo);
 
-int batadv_orig_seq_print_text(struct seq_file *seq, void *offset);
+void *batadv_orig_seq_start(struct seq_file *seq, loff_t *pos);
+void *batadv_orig_seq_next(struct seq_file *seq, void *v, loff_t *pos);
+int batadv_orig_seq_show(struct seq_file *seq, void *v);
+void batadv_orig_seq_stop(struct seq_file *seq, void *v);
+
 int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset);
 int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
 			    int max_if_num);
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 2ed55f4..eab36a9 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -1001,7 +1001,7 @@  int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
 	bool no_purge;
 	u16 np_flag = BATADV_TT_CLIENT_NOPURGE;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
@@ -1699,7 +1699,7 @@  int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
 	struct hlist_head *head;
 	u32 i;
 
-	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	primary_if = batadv_seq_print_text_primary_if_get(seq, net_dev);
 	if (!primary_if)
 		goto out;
 
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 9abfb3e..764332e 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1297,8 +1297,12 @@  struct batadv_algo_ops {
 	void (*bat_neigh_print)(struct batadv_priv *priv, struct seq_file *seq);
 	void (*bat_neigh_free)(struct batadv_neigh_node *neigh);
 	/* orig_node handling API */
-	void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq,
-			       struct batadv_hard_iface *hard_iface);
+	void (*bat_orig_seq_header)(struct batadv_priv *priv,
+				    struct seq_file *seq);
+	void (*bat_orig_seq_show)(struct batadv_priv *priv,
+				  struct seq_file *seq,
+				  struct batadv_orig_node *orig_node,
+				  struct batadv_hard_iface *if_outgoing);
 	void (*bat_orig_free)(struct batadv_orig_node *orig_node);
 	int (*bat_orig_add_if)(struct batadv_orig_node *orig_node,
 			       int max_if_num);
@@ -1403,4 +1407,26 @@  enum batadv_tvlv_handler_flags {
 	BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
 };
 
+/**
+ * struct batadv_seq_private - private data of seq_open
+ * @net_dev: network device the seq private data belongs to
+ */
+struct batadv_seq_private {
+	struct net_device *net_dev;
+};
+
+/**
+ * struct batadv_seq_hash_iter - private data of seq_open for hash iterators
+ * @p: standard batadv_seq_private data
+ * @header_shown: whether header was already shown
+ * @bucket: current hash bucket
+ * @entry: current hash entry
+ */
+struct batadv_seq_hash_iter {
+	struct batadv_seq_private p;
+	int header_shown;
+	u32 bucket;
+	struct hlist_node *entry;
+};
+
 #endif /* _NET_BATMAN_ADV_TYPES_H_ */