Skip to content

Commit

Permalink
netfilter: nfnetlink: keep going batch handling on missing modules
Browse files Browse the repository at this point in the history
After a fresh boot with no modules in place at all and a large rulesets, the
existing nfnetlink_rcv_batch() funcion can take long time to commit the ruleset
due to the many abort path. This is specifically a problem for the existing
client of this code, ie. nf_tables, since it results in several
synchronize_rcu() call in a row.

This patch changes the policy to keep full batch processing on missing modules
errors so we abort only once.

Reported-by: Eric Leblond <eric@regit.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Pablo Neira Ayuso committed Jul 2, 2015
1 parent dd302b5 commit 6742b9e
Showing 1 changed file with 25 additions and 13 deletions.
38 changes: 25 additions & 13 deletions net/netfilter/nfnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,20 +269,28 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
}
}

enum {
NFNL_BATCH_FAILURE = (1 << 0),
NFNL_BATCH_DONE = (1 << 1),
NFNL_BATCH_REPLAY = (1 << 2),
};

static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
u_int16_t subsys_id)
{
struct sk_buff *oskb = skb;
struct net *net = sock_net(skb->sk);
const struct nfnetlink_subsystem *ss;
const struct nfnl_callback *nc;
bool success = true, done = false;
static LIST_HEAD(err_list);
u32 status;
int err;

if (subsys_id >= NFNL_SUBSYS_COUNT)
return netlink_ack(skb, nlh, -EINVAL);
replay:
status = 0;

skb = netlink_skb_clone(oskb, GFP_KERNEL);
if (!skb)
return netlink_ack(oskb, nlh, -ENOMEM);
Expand Down Expand Up @@ -336,10 +344,10 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
if (type == NFNL_MSG_BATCH_BEGIN) {
/* Malformed: Batch begin twice */
nfnl_err_reset(&err_list);
success = false;
status |= NFNL_BATCH_FAILURE;
goto done;
} else if (type == NFNL_MSG_BATCH_END) {
done = true;
status |= NFNL_BATCH_DONE;
goto done;
} else if (type < NLMSG_MIN_TYPE) {
err = -EINVAL;
Expand Down Expand Up @@ -382,11 +390,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
* original skb.
*/
if (err == -EAGAIN) {
nfnl_err_reset(&err_list);
ss->abort(oskb);
nfnl_unlock(subsys_id);
kfree_skb(skb);
goto replay;
status |= NFNL_BATCH_REPLAY;
goto next;
}
}
ack:
Expand All @@ -402,27 +407,34 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
*/
nfnl_err_reset(&err_list);
netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM);
success = false;
status |= NFNL_BATCH_FAILURE;
goto done;
}
/* We don't stop processing the batch on errors, thus,
* userspace gets all the errors that the batch
* triggers.
*/
if (err)
success = false;
status |= NFNL_BATCH_FAILURE;
}

next:
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
skb_pull(skb, msglen);
}
done:
if (success && done)
if (status & NFNL_BATCH_REPLAY) {
ss->abort(oskb);
nfnl_err_reset(&err_list);
nfnl_unlock(subsys_id);
kfree_skb(skb);
goto replay;
} else if (status == NFNL_BATCH_DONE) {
ss->commit(oskb);
else
} else {
ss->abort(oskb);
}

nfnl_err_deliver(&err_list, oskb);
nfnl_unlock(subsys_id);
Expand Down

0 comments on commit 6742b9e

Please sign in to comment.