Commit 2ed37183 authored by Oz Shlomo's avatar Oz Shlomo Committed by Pablo Neira Ayuso
Browse files

netfilter: flowtable: separate replace, destroy and stats to different workqueues



Currently the flow table offload replace, destroy and stats work items are
executed on a single workqueue. As such, DESTROY and STATS commands may
be backloged after a burst of REPLACE work items. This scenario can bloat
up memory and may cause active connections to age.

Instatiate add, del and stats workqueues to avoid backlogs of non-dependent
actions. Provide sysfs control over the workqueue attributes, allowing
userspace applications to control the workqueue cpumask.

Signed-off-by: default avatarOz Shlomo <ozsh@nvidia.com>
Reviewed-by: default avatarPaul Blakey <paulb@nvidia.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent ebfbc46b
Loading
Loading
Loading
Loading
+36 −8
Original line number Diff line number Diff line
@@ -13,7 +13,9 @@
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_tuple.h>

static struct workqueue_struct *nf_flow_offload_wq;
static struct workqueue_struct *nf_flow_offload_add_wq;
static struct workqueue_struct *nf_flow_offload_del_wq;
static struct workqueue_struct *nf_flow_offload_stats_wq;

struct flow_offload_work {
	struct list_head	list;
@@ -826,7 +828,12 @@ static void flow_offload_work_handler(struct work_struct *work)

static void flow_offload_queue_work(struct flow_offload_work *offload)
{
	queue_work(nf_flow_offload_wq, &offload->work);
	if (offload->cmd == FLOW_CLS_REPLACE)
		queue_work(nf_flow_offload_add_wq, &offload->work);
	else if (offload->cmd == FLOW_CLS_DESTROY)
		queue_work(nf_flow_offload_del_wq, &offload->work);
	else
		queue_work(nf_flow_offload_stats_wq, &offload->work);
}

static struct flow_offload_work *
@@ -898,8 +905,11 @@ void nf_flow_offload_stats(struct nf_flowtable *flowtable,

void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
{
	if (nf_flowtable_hw_offload(flowtable))
		flush_workqueue(nf_flow_offload_wq);
	if (nf_flowtable_hw_offload(flowtable)) {
		flush_workqueue(nf_flow_offload_add_wq);
		flush_workqueue(nf_flow_offload_del_wq);
		flush_workqueue(nf_flow_offload_stats_wq);
	}
}

static int nf_flow_table_block_setup(struct nf_flowtable *flowtable,
@@ -1011,15 +1021,33 @@ EXPORT_SYMBOL_GPL(nf_flow_table_offload_setup);

int nf_flow_table_offload_init(void)
{
	nf_flow_offload_wq  = alloc_workqueue("nf_flow_table_offload",
					      WQ_UNBOUND, 0);
	if (!nf_flow_offload_wq)
	nf_flow_offload_add_wq  = alloc_workqueue("nf_ft_offload_add",
						  WQ_UNBOUND | WQ_SYSFS, 0);
	if (!nf_flow_offload_add_wq)
		return -ENOMEM;

	nf_flow_offload_del_wq  = alloc_workqueue("nf_ft_offload_del",
						  WQ_UNBOUND | WQ_SYSFS, 0);
	if (!nf_flow_offload_del_wq)
		goto err_del_wq;

	nf_flow_offload_stats_wq  = alloc_workqueue("nf_ft_offload_stats",
						    WQ_UNBOUND | WQ_SYSFS, 0);
	if (!nf_flow_offload_stats_wq)
		goto err_stats_wq;

	return 0;

err_stats_wq:
	destroy_workqueue(nf_flow_offload_del_wq);
err_del_wq:
	destroy_workqueue(nf_flow_offload_add_wq);
	return -ENOMEM;
}

void nf_flow_table_offload_exit(void)
{
	destroy_workqueue(nf_flow_offload_wq);
	destroy_workqueue(nf_flow_offload_add_wq);
	destroy_workqueue(nf_flow_offload_del_wq);
	destroy_workqueue(nf_flow_offload_stats_wq);
}