netfilter: nfnetlink: keep going batch handling on missing modules
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>
This commit is contained in:
parent
dd302b59bd
commit
6742b9e310
|
@ -269,6 +269,12 @@ 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)
|
||||
{
|
||||
|
@ -276,13 +282,15 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
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);
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -402,7 +407,7 @@ 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,
|
||||
|
@ -410,19 +415,26 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
* 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)
|
||||
ss->commit(oskb);
|
||||
else
|
||||
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 {
|
||||
ss->abort(oskb);
|
||||
}
|
||||
|
||||
nfnl_err_deliver(&err_list, oskb);
|
||||
nfnl_unlock(subsys_id);
|
||||
|
|
Loading…
Reference in New Issue