diff mbox series

[net-next,v2,3/3] netfilter: nf_flow_table: count pending offload workqueue tasks

Message ID 20220516191032.340243-4-vladbu@nvidia.com
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series Conntrack offload debuggability improvements | expand

Commit Message

Vlad Buslov May 16, 2022, 7:10 p.m. UTC
To improve hardware offload debuggability count pending 'add', 'del' and
'stats' flow_table offload workqueue tasks. Counters are incremented before
scheduling new task and decremented when workqueue handler finishes
executing. These counters allow user to diagnose congestion on hardware
offload workqueues that can happen when either CPU is starved and workqueue
jobs are executed at lower rate than new ones are added or when
hardware/driver can't keep up with the rate.

Implement the described counters as percpu counters inside new struct
netns_ft which is stored inside struct net. Expose them via new procfs file
'/proc/net/stats/nf_flowtable' that is similar to existing 'nf_conntrack'
file.

Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
---

Notes:
    Changes V1 -> V2:
    
    - Combine patches that expose add, del, stats tasks.
    
    - Use percpu stats to count pending workqueue tasks instead of atomics.
    
    - Expose the stats via /proc/net/stats/nf_flowtable file instead of
    sysctls.

 include/net/net_namespace.h           |  6 ++
 include/net/netfilter/nf_flow_table.h | 10 +++
 include/net/netns/flow_table.h        | 14 +++++
 net/netfilter/Kconfig                 |  8 +++
 net/netfilter/nf_flow_table_core.c    | 38 +++++++++++-
 net/netfilter/nf_flow_table_offload.c | 17 +++++-
 net/netfilter/nf_flow_table_sysctl.c  | 88 +++++++++++++++++++++++++++
 7 files changed, 176 insertions(+), 5 deletions(-)
 create mode 100644 include/net/netns/flow_table.h

Comments

Vlad Buslov May 17, 2022, 11:16 a.m. UTC | #1
On Tue 17 May 2022 at 13:20, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> On Mon, May 16, 2022 at 10:10:32PM +0300, Vlad Buslov wrote:
> [...]
>> diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
>> index ddc54b6d18ee..c8fc5c7ef04a 100644
>> --- a/net/netfilter/Kconfig
>> +++ b/net/netfilter/Kconfig
>> @@ -734,6 +734,14 @@ config NF_FLOW_TABLE
>>  
>>  	  To compile it as a module, choose M here.
>>  
>> +config NF_FLOW_TABLE_PROCFS
>> +	bool "Supply flow table statistics in procfs"
>> +	default y
>> +	depends on PROC_FS
>> +	help
>> +	  This option enables for the flow table offload statistics
>> +	  to be shown in procfs under net/netfilter/nf_flowtable.
>
> This belongs to patch 2/3.
>
> Then, use NF_FLOW_TABLE_PROCFS to conditionally add it to
> nf_flow_table if this is enabled in .config? To honor this new Kconfig
> toggle.
>
> I mean instead of:
>
> obj-$(CONFIG_NF_FLOW_TABLE)    += nf_flow_table.o
>  nf_flow_table-objs             := nf_flow_table_core.o nf_flow_table_ip.o \
> -                                  nf_flow_table_offload.o
> +                                  nf_flow_table_offload.o \
> +                                  nf_flow_table_sysctl.o
>
> this?
>
> nf_flow_table-$(CONFIG_NF_FLOW_TABLE_SYSCTL)    += nf_flow_table_sysctl.o

In V2 I have both sysctl and procfs implementations in single file.
As I replied for previous patch in series: Should I split those in two
separate files (nf_flow_table_sysctl.c and nf_flow_table_procfs.c) that
both could be conditionally compiled depending on their respective
configs?

>
>>  config NETFILTER_XTABLES
>>  	tristate "Netfilter Xtables support (required for ip_tables)"
>>  	default m if NETFILTER_ADVANCED=n
>> diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
>> index e2598f98017c..c86dd627ef42 100644
>> --- a/net/netfilter/nf_flow_table_core.c
>> +++ b/net/netfilter/nf_flow_table_core.c
>> @@ -662,17 +662,51 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
>>  }
>>  EXPORT_SYMBOL_GPL(nf_flow_table_free);
>>  
>> +static int nf_flow_table_init_net(struct net *net)
>> +{
>> +	net->ft.stat = alloc_percpu(struct nf_flow_table_stat);
>
> Missing check for NULL in case alloc_percpu() fails?
>

I might be missing something, but why isn't NULL check in following line
sufficient?

>> +	return net->ft.stat ? 0 : -ENOMEM;
>> +}
Pablo Neira Ayuso May 17, 2022, 11:20 a.m. UTC | #2
On Mon, May 16, 2022 at 10:10:32PM +0300, Vlad Buslov wrote:
[...]
> diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
> index ddc54b6d18ee..c8fc5c7ef04a 100644
> --- a/net/netfilter/Kconfig
> +++ b/net/netfilter/Kconfig
> @@ -734,6 +734,14 @@ config NF_FLOW_TABLE
>  
>  	  To compile it as a module, choose M here.
>  
> +config NF_FLOW_TABLE_PROCFS
> +	bool "Supply flow table statistics in procfs"
> +	default y
> +	depends on PROC_FS
> +	help
> +	  This option enables for the flow table offload statistics
> +	  to be shown in procfs under net/netfilter/nf_flowtable.

This belongs to patch 2/3.

Then, use NF_FLOW_TABLE_PROCFS to conditionally add it to
nf_flow_table if this is enabled in .config? To honor this new Kconfig
toggle.

I mean instead of:

obj-$(CONFIG_NF_FLOW_TABLE)    += nf_flow_table.o
 nf_flow_table-objs             := nf_flow_table_core.o nf_flow_table_ip.o \
-                                  nf_flow_table_offload.o
+                                  nf_flow_table_offload.o \
+                                  nf_flow_table_sysctl.o

this?

nf_flow_table-$(CONFIG_NF_FLOW_TABLE_SYSCTL)    += nf_flow_table_sysctl.o

>  config NETFILTER_XTABLES
>  	tristate "Netfilter Xtables support (required for ip_tables)"
>  	default m if NETFILTER_ADVANCED=n
> diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
> index e2598f98017c..c86dd627ef42 100644
> --- a/net/netfilter/nf_flow_table_core.c
> +++ b/net/netfilter/nf_flow_table_core.c
> @@ -662,17 +662,51 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
>  }
>  EXPORT_SYMBOL_GPL(nf_flow_table_free);
>  
> +static int nf_flow_table_init_net(struct net *net)
> +{
> +	net->ft.stat = alloc_percpu(struct nf_flow_table_stat);

Missing check for NULL in case alloc_percpu() fails?

> +	return net->ft.stat ? 0 : -ENOMEM;
> +}
Pablo Neira Ayuso May 17, 2022, 12:26 p.m. UTC | #3
On Tue, May 17, 2022 at 02:16:04PM +0300, Vlad Buslov wrote:
> 
> On Tue 17 May 2022 at 13:20, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > On Mon, May 16, 2022 at 10:10:32PM +0300, Vlad Buslov wrote:
> > [...]
> >> diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
> >> index ddc54b6d18ee..c8fc5c7ef04a 100644
> >> --- a/net/netfilter/Kconfig
> >> +++ b/net/netfilter/Kconfig
> >> @@ -734,6 +734,14 @@ config NF_FLOW_TABLE
> >>  
> >>  	  To compile it as a module, choose M here.
> >>  
> >> +config NF_FLOW_TABLE_PROCFS
> >> +	bool "Supply flow table statistics in procfs"
> >> +	default y
> >> +	depends on PROC_FS
> >> +	help
> >> +	  This option enables for the flow table offload statistics
> >> +	  to be shown in procfs under net/netfilter/nf_flowtable.
> >
> > This belongs to patch 2/3.
> >
> > Then, use NF_FLOW_TABLE_PROCFS to conditionally add it to
> > nf_flow_table if this is enabled in .config? To honor this new Kconfig
> > toggle.
> >
> > I mean instead of:
> >
> > obj-$(CONFIG_NF_FLOW_TABLE)    += nf_flow_table.o
> >  nf_flow_table-objs             := nf_flow_table_core.o nf_flow_table_ip.o \
> > -                                  nf_flow_table_offload.o
> > +                                  nf_flow_table_offload.o \
> > +                                  nf_flow_table_sysctl.o
> >
> > this?
> >
> > nf_flow_table-$(CONFIG_NF_FLOW_TABLE_SYSCTL)    += nf_flow_table_sysctl.o
> 
> In V2 I have both sysctl and procfs implementations in single file.
> As I replied for previous patch in series: Should I split those in two
> separate files (nf_flow_table_sysctl.c and nf_flow_table_procfs.c) that
> both could be conditionally compiled depending on their respective
> configs?

Same file is fine.

Probably instead ?

nf_flow_table-$(CONFIG_SYSCTL)    += nf_flow_table_sysctl.o

so the #ifdef CONFIG_SYSCTL in nf_flow_table_sysctl.c can go away.

you would need to move:

        unsigned int nf_ft_hw_max __read_mostly;

to nf_flow_table_offload.c

Make sense?

> >>  config NETFILTER_XTABLES
> >>  	tristate "Netfilter Xtables support (required for ip_tables)"
> >>  	default m if NETFILTER_ADVANCED=n
> >> diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
> >> index e2598f98017c..c86dd627ef42 100644
> >> --- a/net/netfilter/nf_flow_table_core.c
> >> +++ b/net/netfilter/nf_flow_table_core.c
> >> @@ -662,17 +662,51 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
> >>  }
> >>  EXPORT_SYMBOL_GPL(nf_flow_table_free);
> >>  
> >> +static int nf_flow_table_init_net(struct net *net)
> >> +{
> >> +	net->ft.stat = alloc_percpu(struct nf_flow_table_stat);
> >
> > Missing check for NULL in case alloc_percpu() fails?
> >
> 
> I might be missing something, but why isn't NULL check in following line
> sufficient?

I overlook the return is check for NULL, sorry for the noise.

> >> +	return net->ft.stat ? 0 : -ENOMEM;
> >> +}
>
Vlad Buslov May 17, 2022, 3:18 p.m. UTC | #4
On Tue 17 May 2022 at 14:26, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> On Tue, May 17, 2022 at 02:16:04PM +0300, Vlad Buslov wrote:
>> 
>> On Tue 17 May 2022 at 13:20, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
>> > On Mon, May 16, 2022 at 10:10:32PM +0300, Vlad Buslov wrote:
>> > [...]
>> >> diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
>> >> index ddc54b6d18ee..c8fc5c7ef04a 100644
>> >> --- a/net/netfilter/Kconfig
>> >> +++ b/net/netfilter/Kconfig
>> >> @@ -734,6 +734,14 @@ config NF_FLOW_TABLE
>> >>  
>> >>  	  To compile it as a module, choose M here.
>> >>  
>> >> +config NF_FLOW_TABLE_PROCFS
>> >> +	bool "Supply flow table statistics in procfs"
>> >> +	default y
>> >> +	depends on PROC_FS
>> >> +	help
>> >> +	  This option enables for the flow table offload statistics
>> >> +	  to be shown in procfs under net/netfilter/nf_flowtable.
>> >
>> > This belongs to patch 2/3.
>> >
>> > Then, use NF_FLOW_TABLE_PROCFS to conditionally add it to
>> > nf_flow_table if this is enabled in .config? To honor this new Kconfig
>> > toggle.
>> >
>> > I mean instead of:
>> >
>> > obj-$(CONFIG_NF_FLOW_TABLE)    += nf_flow_table.o
>> >  nf_flow_table-objs             := nf_flow_table_core.o nf_flow_table_ip.o \
>> > -                                  nf_flow_table_offload.o
>> > +                                  nf_flow_table_offload.o \
>> > +                                  nf_flow_table_sysctl.o
>> >
>> > this?
>> >
>> > nf_flow_table-$(CONFIG_NF_FLOW_TABLE_SYSCTL)    += nf_flow_table_sysctl.o
>> 
>> In V2 I have both sysctl and procfs implementations in single file.
>> As I replied for previous patch in series: Should I split those in two
>> separate files (nf_flow_table_sysctl.c and nf_flow_table_procfs.c) that
>> both could be conditionally compiled depending on their respective
>> configs?
>
> Same file is fine.
>
> Probably instead ?
>
> nf_flow_table-$(CONFIG_SYSCTL)    += nf_flow_table_sysctl.o
>
> so the #ifdef CONFIG_SYSCTL in nf_flow_table_sysctl.c can go away.
>
> you would need to move:
>
>         unsigned int nf_ft_hw_max __read_mostly;
>
> to nf_flow_table_offload.c
>
> Make sense?

Yep. Will send the V3 soon.

Thanks,
Vlad


[...]
diff mbox series

Patch

diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index c4f5601f6e32..bf770c13e19b 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -26,6 +26,9 @@ 
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 #include <net/netns/conntrack.h>
 #endif
+#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
+#include <net/netns/flow_table.h>
+#endif
 #include <net/netns/nftables.h>
 #include <net/netns/xfrm.h>
 #include <net/netns/mpls.h>
@@ -140,6 +143,9 @@  struct net {
 #if defined(CONFIG_NF_TABLES) || defined(CONFIG_NF_TABLES_MODULE)
 	struct netns_nftables	nft;
 #endif
+#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
+	struct netns_ft ft;
+#endif
 #endif
 #ifdef CONFIG_WEXT_CORE
 	struct sk_buff_head	wext_nlevents;
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index e09c29fa034e..94606c04b6fe 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -360,4 +360,14 @@  static inline struct nf_ft_net *nf_ft_pernet_get(struct nf_flowtable *flow_table
 int nf_flow_table_init_sysctl(struct net *net);
 void nf_flow_table_fini_sysctl(struct net *net);
 
+#define NF_FLOW_TABLE_STAT_INC(net, count) __this_cpu_inc((net)->ft.stat->count)
+#define NF_FLOW_TABLE_STAT_DEC(net, count) __this_cpu_dec((net)->ft.stat->count)
+#define NF_FLOW_TABLE_STAT_INC_ATOMIC(net, count)	\
+	this_cpu_inc((net)->ft.stat->count)
+#define NF_FLOW_TABLE_STAT_DEC_ATOMIC(net, count)	\
+	this_cpu_dec((net)->ft.stat->count)
+
+int nf_flow_table_init_proc(struct net *net);
+void nf_flow_table_fini_proc(struct net *net);
+
 #endif /* _NF_FLOW_TABLE_H */
diff --git a/include/net/netns/flow_table.h b/include/net/netns/flow_table.h
new file mode 100644
index 000000000000..1c5fc657e267
--- /dev/null
+++ b/include/net/netns/flow_table.h
@@ -0,0 +1,14 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __NETNS_FLOW_TABLE_H
+#define __NETNS_FLOW_TABLE_H
+
+struct nf_flow_table_stat {
+	unsigned int count_wq_add;
+	unsigned int count_wq_del;
+	unsigned int count_wq_stats;
+};
+
+struct netns_ft {
+	struct nf_flow_table_stat __percpu *stat;
+};
+#endif
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index ddc54b6d18ee..c8fc5c7ef04a 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -734,6 +734,14 @@  config NF_FLOW_TABLE
 
 	  To compile it as a module, choose M here.
 
+config NF_FLOW_TABLE_PROCFS
+	bool "Supply flow table statistics in procfs"
+	default y
+	depends on PROC_FS
+	help
+	  This option enables for the flow table offload statistics
+	  to be shown in procfs under net/netfilter/nf_flowtable.
+
 config NETFILTER_XTABLES
 	tristate "Netfilter Xtables support (required for ip_tables)"
 	default m if NETFILTER_ADVANCED=n
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index e2598f98017c..c86dd627ef42 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -662,17 +662,51 @@  void nf_flow_table_free(struct nf_flowtable *flow_table)
 }
 EXPORT_SYMBOL_GPL(nf_flow_table_free);
 
+static int nf_flow_table_init_net(struct net *net)
+{
+	net->ft.stat = alloc_percpu(struct nf_flow_table_stat);
+	return net->ft.stat ? 0 : -ENOMEM;
+}
+
+static void nf_flow_table_fini_net(struct net *net)
+{
+	free_percpu(net->ft.stat);
+}
+
 static int nf_flow_table_pernet_init(struct net *net)
 {
-	return nf_flow_table_init_sysctl(net);
+	int ret;
+
+	ret = nf_flow_table_init_net(net);
+	if (ret < 0)
+		return ret;
+
+	ret = nf_flow_table_init_sysctl(net);
+	if (ret < 0)
+		goto out_sysctl;
+
+	ret = nf_flow_table_init_proc(net);
+	if (ret < 0)
+		goto out_proc;
+
+	return 0;
+
+out_proc:
+	nf_flow_table_fini_sysctl(net);
+out_sysctl:
+	nf_flow_table_fini_net(net);
+	return ret;
 }
 
 static void nf_flow_table_pernet_exit(struct list_head *net_exit_list)
 {
 	struct net *net;
 
-	list_for_each_entry(net, net_exit_list, exit_list)
+	list_for_each_entry(net, net_exit_list, exit_list) {
+		nf_flow_table_fini_proc(net);
 		nf_flow_table_fini_sysctl(net);
+		nf_flow_table_fini_net(net);
+	}
 }
 
 unsigned int nf_ft_net_id __read_mostly;
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index a6e763901eb9..381bd598a16f 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -993,17 +993,22 @@  static void flow_offload_work_stats(struct flow_offload_work *offload)
 static void flow_offload_work_handler(struct work_struct *work)
 {
 	struct flow_offload_work *offload;
+	struct net *net;
 
 	offload = container_of(work, struct flow_offload_work, work);
+	net = read_pnet(&offload->flowtable->net);
 	switch (offload->cmd) {
 		case FLOW_CLS_REPLACE:
 			flow_offload_work_add(offload);
+			NF_FLOW_TABLE_STAT_DEC_ATOMIC(net, count_wq_add);
 			break;
 		case FLOW_CLS_DESTROY:
 			flow_offload_work_del(offload);
+			NF_FLOW_TABLE_STAT_DEC_ATOMIC(net, count_wq_del);
 			break;
 		case FLOW_CLS_STATS:
 			flow_offload_work_stats(offload);
+			NF_FLOW_TABLE_STAT_DEC_ATOMIC(net, count_wq_stats);
 			break;
 		default:
 			WARN_ON_ONCE(1);
@@ -1015,12 +1020,18 @@  static void flow_offload_work_handler(struct work_struct *work)
 
 static void flow_offload_queue_work(struct flow_offload_work *offload)
 {
-	if (offload->cmd == FLOW_CLS_REPLACE)
+	struct net *net = read_pnet(&offload->flowtable->net);
+
+	if (offload->cmd == FLOW_CLS_REPLACE) {
+		NF_FLOW_TABLE_STAT_INC(net, count_wq_add);
 		queue_work(nf_flow_offload_add_wq, &offload->work);
-	else if (offload->cmd == FLOW_CLS_DESTROY)
+	} else if (offload->cmd == FLOW_CLS_DESTROY) {
+		NF_FLOW_TABLE_STAT_INC(net, count_wq_del);
 		queue_work(nf_flow_offload_del_wq, &offload->work);
-	else
+	} else {
+		NF_FLOW_TABLE_STAT_INC(net, count_wq_stats);
 		queue_work(nf_flow_offload_stats_wq, &offload->work);
+	}
 }
 
 static struct flow_offload_work *
diff --git a/net/netfilter/nf_flow_table_sysctl.c b/net/netfilter/nf_flow_table_sysctl.c
index ce8c0a2c4bdb..2aefd7542527 100644
--- a/net/netfilter/nf_flow_table_sysctl.c
+++ b/net/netfilter/nf_flow_table_sysctl.c
@@ -1,7 +1,95 @@ 
 // SPDX-License-Identifier: GPL-2.0-only
 #include <linux/kernel.h>
+#include <linux/proc_fs.h>
 #include <net/netfilter/nf_flow_table.h>
 
+#ifdef CONFIG_NF_FLOW_TABLE_PROCFS
+static void *nf_flow_table_cpu_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct net *net = seq_file_net(seq);
+	int cpu;
+
+	if (*pos == 0)
+		return SEQ_START_TOKEN;
+
+	for (cpu = *pos - 1; cpu < nr_cpu_ids; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu + 1;
+		return per_cpu_ptr(net->ft.stat, cpu);
+	}
+
+	return NULL;
+}
+
+static void *nf_flow_table_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct net *net = seq_file_net(seq);
+	int cpu;
+
+	for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu + 1;
+		return per_cpu_ptr(net->ft.stat, cpu);
+	}
+	(*pos)++;
+	return NULL;
+}
+
+static void nf_flow_table_cpu_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int nf_flow_table_cpu_seq_show(struct seq_file *seq, void *v)
+{
+	const struct nf_flow_table_stat *st = v;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_puts(seq, "wq_add   wq_del   wq_stats\n");
+		return 0;
+	}
+
+	seq_printf(seq, "%8d %8d %8d\n",
+		   st->count_wq_add,
+		   st->count_wq_del,
+		   st->count_wq_stats
+		);
+	return 0;
+}
+
+static const struct seq_operations nf_flow_table_cpu_seq_ops = {
+	.start	= nf_flow_table_cpu_seq_start,
+	.next	= nf_flow_table_cpu_seq_next,
+	.stop	= nf_flow_table_cpu_seq_stop,
+	.show	= nf_flow_table_cpu_seq_show,
+};
+
+int nf_flow_table_init_proc(struct net *net)
+{
+	struct proc_dir_entry *pde;
+
+	pde = proc_create_net("nf_flowtable", 0444, net->proc_net_stat,
+			      &nf_flow_table_cpu_seq_ops,
+			      sizeof(struct seq_net_private));
+	return pde ? 0 : -ENOMEM;
+}
+
+void nf_flow_table_fini_proc(struct net *net)
+{
+	remove_proc_entry("nf_flowtable", net->proc_net_stat);
+}
+#else
+int nf_flow_table_init_proc(struct net *net)
+{
+	return 0;
+}
+
+void nf_flow_table_fini_proc(struct net *net)
+{
+}
+#endif /* CONFIG_NF_FLOW_TABLE_PROCFS */
+
 unsigned int nf_ft_hw_max __read_mostly;
 
 #ifdef CONFIG_SYSCTL