diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -624,6 +625,8 @@ struct pf_keth_rules rules; uint32_t ticket; int open; + struct vnet *vnet; + struct epoch_context epoch_ctx; }; union pf_krule_ptr { diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -3712,18 +3712,18 @@ struct ether_header *e; struct pf_keth_rule *r, *rm; struct pf_mtag *mtag; + struct pf_keth_settings *settings; uint8_t action; - PF_RULES_RLOCK_TRACKER; + NET_EPOCH_ASSERT(); MPASS(kif->pfik_ifp->if_vnet == curvnet); NET_EPOCH_ASSERT(); e = mtod(m, struct ether_header *); - PF_RULES_RLOCK(); - - r = TAILQ_FIRST(&V_pf_keth->rules); + settings = ck_pr_load_ptr(&V_pf_keth); + r = TAILQ_FIRST(&settings->rules); rm = NULL; while (r != NULL) { @@ -3753,26 +3753,21 @@ r = rm; /* Default to pass. */ - if (r == NULL) { - PF_RULES_RUNLOCK(); + if (r == NULL) return (PF_PASS); - } /* Execute action. */ counter_u64_add(r->packets[dir == PF_OUT], 1); counter_u64_add(r->bytes[dir == PF_OUT], m_length(m, NULL)); /* Shortcut. Don't tag if we're just going to drop anyway. */ - if (r->action == PF_DROP) { - PF_RULES_RUNLOCK(); + if (r->action == PF_DROP) return (PF_DROP); - } if (r->tag > 0) { mtag = pf_get_mtag(m); if (mtag == NULL) { counter_u64_add(V_pf_status.counters[PFRES_MEMORY], 1); - PF_RULES_RUNLOCK(); return (PF_DROP); } mtag->tag = r->tag; @@ -3782,7 +3777,6 @@ mtag = pf_get_mtag(m); if (mtag == NULL) { counter_u64_add(V_pf_status.counters[PFRES_MEMORY], 1); - PF_RULES_RUNLOCK(); return (PF_DROP); } mtag->qid = r->qid; @@ -3790,8 +3784,6 @@ action = r->action; - PF_RULES_RUNLOCK(); - return (action); } diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -107,6 +107,7 @@ static int pfioctl(struct cdev *, u_long, caddr_t, int, struct thread *); static int pf_begin_eth(uint32_t *); +static void pf_rollback_eth_cb(struct epoch_context *); static int pf_rollback_eth(uint32_t); static int pf_commit_eth(uint32_t); static void pf_free_eth_rule(struct pf_keth_rule *); @@ -701,6 +702,12 @@ PF_RULES_WASSERT(); + if (V_pf_keth_inactive->open) { + /* We may be waiting for NET_EPOCH_CALL(pf_rollback_eth_cb) to + * finish. */ + return (EBUSY); + } + /* Purge old inactive rules. */ TAILQ_FOREACH_SAFE(rule, &V_pf_keth_inactive->rules, entries, tmp) { TAILQ_REMOVE(&V_pf_keth_inactive->rules, rule, entries); @@ -713,6 +720,24 @@ return (0); } +static void +pf_rollback_eth_cb(struct epoch_context *ctx) +{ + struct pf_keth_settings *settings; + + settings = __containerof(ctx, struct pf_keth_settings, epoch_ctx); + + CURVNET_SET(settings->vnet); + + MPASS(settings == V_pf_keth_inactive); + + PF_RULES_WLOCK(); + pf_rollback_eth(V_pf_keth_inactive->ticket); + PF_RULES_WUNLOCK(); + + CURVNET_RESTORE(); +} + static int pf_rollback_eth(uint32_t ticket) { @@ -780,15 +805,20 @@ ticket != V_pf_keth_inactive->ticket) return (EBUSY); + PF_RULES_WASSERT(); + pf_eth_calc_skip_steps(&V_pf_keth_inactive->rules); settings = V_pf_keth; - V_pf_keth = V_pf_keth_inactive; + ck_pr_store_ptr(&V_pf_keth, V_pf_keth_inactive); V_pf_keth_inactive = settings; V_pf_keth_inactive->ticket = V_pf_keth->ticket; - /* Clean up inactive rules. */ - return (pf_rollback_eth(ticket)); + /* Clean up inactive rules (i.e. previously active rules), only when + * we're sure they're no longer used. */ + NET_EPOCH_CALL(pf_rollback_eth_cb, &V_pf_keth_inactive->epoch_ctx); + + return (0); } #ifdef ALTQ diff --git a/sys/netpfil/pf/pf_ruleset.c b/sys/netpfil/pf/pf_ruleset.c --- a/sys/netpfil/pf/pf_ruleset.c +++ b/sys/netpfil/pf/pf_ruleset.c @@ -154,6 +154,7 @@ TAILQ_INIT(&settings->rules); settings->ticket = 0; settings->open = 0; + settings->vnet = curvnet; } struct pf_kruleset *