On Sun, May 08, 2022 at 06:15:49PM +0200, Alexandr Nedvedicky wrote: > OK ?
3 comments inline > --------8<---------------8<---------------8<------------------8<-------- > diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c > index 8315b115474..1f036e1368f 100644 > --- a/sys/net/pf_ioctl.c > +++ b/sys/net/pf_ioctl.c > @@ -2217,12 +2217,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int > flags, struct proc *p) > error = ENODEV; > goto fail; > } > - NET_LOCK(); > - PF_LOCK(); > error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size, > &io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL); > - PF_UNLOCK(); > - NET_UNLOCK(); > break; > } > > diff --git a/sys/net/pf_table.c b/sys/net/pf_table.c > index fb23bcabe04..79fd3e0447b 100644 > --- a/sys/net/pf_table.c > +++ b/sys/net/pf_table.c > @@ -189,6 +189,7 @@ void pfr_clstats_ktable(struct > pfr_ktable *, time_t, int); > struct pfr_ktable *pfr_create_ktable(struct pfr_table *, time_t, int, > int); > void pfr_destroy_ktables(struct pfr_ktableworkq *, int); > +void pfr_destroy_ktables_aux(struct pfr_ktableworkq *); > void pfr_destroy_ktable(struct pfr_ktable *, int); > int pfr_ktable_compare(struct pfr_ktable *, > struct pfr_ktable *); > @@ -1493,14 +1494,16 @@ pfr_clr_tables(struct pfr_table *filter, int *ndel, > int flags) > int > pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) > { > - struct pfr_ktableworkq addq, changeq; > - struct pfr_ktable *p, *q, *r, key; > + struct pfr_ktableworkq addq, changeq, auxq; > + struct pfr_ktable *p, *q, *r, *n, *w, key; > int i, rv, xadd = 0; > time_t tzero = gettime(); > > ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); > SLIST_INIT(&addq); > SLIST_INIT(&changeq); > + SLIST_INIT(&auxq); > + /* pre-allocate all memory outside of locks */ > for (i = 0; i < size; i++) { > YIELD(flags & PFR_FLAG_USERIOCTL); > if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags)) > @@ -1509,65 +1512,142 @@ pfr_add_tables(struct pfr_table *tbl, int size, int > *nadd, int flags) > flags & PFR_FLAG_USERIOCTL)) > senderr(EINVAL); > key.pfrkt_flags |= PFR_TFLAG_ACTIVE; > - p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); > + p = pfr_create_ktable(&key.pfrkt_t, tzero, 0, > + !(flags & PFR_FLAG_USERIOCTL)); > + if (p == NULL) > + senderr(ENOMEM); > + > + /* > + * Note: we also pre-allocate a root table here. We keep it > + * at ->pfrkt_root, which we must not forget about. > + */ > + key.pfrkt_flags = 0; > + memset(key.pfrkt_anchor, 0, sizeof(key.pfrkt_anchor)); > + p->pfrkt_root = pfr_create_ktable(&key.pfrkt_t, 0, 0, > + !(flags & PFR_FLAG_USERIOCTL)); > + if (p->pfrkt_root == NULL) { > + pfr_destroy_ktable(p, 0); > + senderr(ENOMEM); > + } > + > + SLIST_FOREACH(q, &auxq, pfrkt_workq) { > + if (!pfr_ktable_compare(p, q)) { > + /* > + * We need no lock here, because `p` is empty, > + * there are no rules or shadow tables > + * attached. > + */ > + pfr_destroy_ktable(p->pfrkt_root, 0); > + p->pfrkt_root = NULL; > + pfr_destroy_ktable(p, 0); > + break; This break... > + } > + } ... end here > + > ... and then we insert a destroyed p > + SLIST_INSERT_HEAD(&auxq, p, pfrkt_workq); > + } > + > + /* > + * auxq contains freshly allocated tables with no dups. > + * also note there are no rulesets attached, because > + * the attach operation requires PF_LOCK(). > + */ > + NET_LOCK(); > + PF_LOCK(); > + SLIST_FOREACH_SAFE(n, &auxq, pfrkt_workq, w) { > + p = RB_FIND(pfr_ktablehead, &pfr_ktables, n); > if (p == NULL) { > - p = pfr_create_ktable(&key.pfrkt_t, tzero, 1, > - !(flags & PFR_FLAG_USERIOCTL)); > - if (p == NULL) > - senderr(ENOMEM); > - SLIST_FOREACH(q, &addq, pfrkt_workq) { > - if (!pfr_ktable_compare(p, q)) { > - pfr_destroy_ktable(p, 0); > - goto _skip; > - } > + SLIST_REMOVE(&auxq, n, pfr_ktable, pfrkt_workq); > + SLIST_INSERT_HEAD(&addq, n, pfrkt_workq); > + } else if (!(flags & PFR_FLAG_DUMMY)) { I compared the old and new code to see if it is equivalent. Before the condtion looked like this. } else if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { > + p->pfrkt_nflags = (p->pfrkt_flags & > + ~PFR_TFLAG_USRMASK) | key.pfrkt_flags; > + SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq); > + } > + xadd++; > + } > + > + if (!(flags & PFR_FLAG_DUMMY)) { > + /* > + * addq contains tables we have to insert and attach rules to > + * them > + * > + * changeq contains tables we need to update > + * > + * auxq contains pre-allocated tables, we won't use and we must > + * free them > + */ > + SLIST_FOREACH_SAFE(p, &addq, pfrkt_workq, w) { > + p->pfrkt_rs = pf_find_or_create_ruleset( > + p->pfrkt_anchor); > + if (p->pfrkt_rs == NULL) { > + xadd--; > + SLIST_REMOVE(&addq, p, pfr_ktable, pfrkt_workq); > + SLIST_INSERT_HEAD(&auxq, p, pfrkt_workq); > + continue; > } > - SLIST_INSERT_HEAD(&addq, p, pfrkt_workq); > - xadd++; > - if (!key.pfrkt_anchor[0]) > - goto _skip; > + p->pfrkt_rs->tables++; > > - /* find or create root table */ > - bzero(key.pfrkt_anchor, sizeof(key.pfrkt_anchor)); > - r = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); > + if (!p->pfrkt_anchor[0]) { > + q = p->pfrkt_root; > + p->pfrkt_root = NULL; > + SLIST_INSERT_HEAD(&auxq, q, pfrkt_workq); > + continue; > + } > + > + /* use pre-allocated root table as a key */ > + q = p->pfrkt_root; > + p->pfrkt_root = NULL; > + r = RB_FIND(pfr_ktablehead, &pfr_ktables, q); > if (r != NULL) { > p->pfrkt_root = r; > - goto _skip; > + SLIST_INSERT_HEAD(&auxq, q, pfrkt_workq); > + continue; > } > - SLIST_FOREACH(q, &addq, pfrkt_workq) { > - if (!pfr_ktable_compare(&key, q)) { > - p->pfrkt_root = q; > - goto _skip; > + /* > + * there is a chance we could create root table in > + * earlier iteration. such table may exist in addq only > + * then. > + */ > + SLIST_FOREACH(r, &addq, pfrkt_workq) { > + if (!pfr_ktable_compare(r, q)) { > + /* > + * `r` is our root table we've found > + * earlier, `q` can get dropped. > + */ > + p->pfrkt_root = r; > + SLIST_INSERT_HEAD(&auxq, q, > + pfrkt_workq); > + continue; This continue goes to the r list, but I think you want to continue p list. > } > } > - key.pfrkt_flags = 0; > - r = pfr_create_ktable(&key.pfrkt_t, 0, 1, > - !(flags & PFR_FLAG_USERIOCTL)); > - if (r == NULL) > - senderr(ENOMEM); > - SLIST_INSERT_HEAD(&addq, r, pfrkt_workq); > - p->pfrkt_root = r; > - } else if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { > - SLIST_FOREACH(q, &changeq, pfrkt_workq) > - if (!pfr_ktable_compare(&key, q)) > - goto _skip; > - p->pfrkt_nflags = (p->pfrkt_flags & > - ~PFR_TFLAG_USRMASK) | key.pfrkt_flags; > - SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq); > - xadd++; > + q->pfrkt_rs = pf_find_or_create_ruleset( > + q->pfrkt_root->pfrkt_anchor); > + /* > + * root tables are attached to main ruleset, > + * because ->pfrkt_anchor[0] == '\0' > + */ > + KASSERT(q->pfrkt_rs == &pf_main_ruleset); > + q->pfrkt_rs->tables++; > + p->pfrkt_root = q; > + SLIST_INSERT_HEAD(&addq, q, pfrkt_workq); > } > -_skip: > - ; > - } > - if (!(flags & PFR_FLAG_DUMMY)) { > + > pfr_insert_ktables(&addq); > pfr_setflags_ktables(&changeq); > - } else > - pfr_destroy_ktables(&addq, 0); > + } > + PF_UNLOCK(); > + NET_UNLOCK(); > + > + pfr_destroy_ktables_aux(&auxq); > + if (flags & PFR_FLAG_DUMMY) > + pfr_destroy_ktables_aux(&addq); > + > if (nadd != NULL) > *nadd = xadd; > return (0); > _bad: > - pfr_destroy_ktables(&addq, 0); > + pfr_destroy_ktables_aux(&auxq); > return (rv); > } > > @@ -2214,6 +2294,7 @@ pfr_create_ktable(struct pfr_table *tbl, time_t tzero, > int attachruleset, > kt->pfrkt_t = *tbl; > > if (attachruleset) { > + PF_ASSERT_LOCKED(); > rs = pf_find_or_create_ruleset(tbl->pfrt_anchor); > if (!rs) { > pfr_destroy_ktable(kt, 0); > @@ -2249,6 +2330,31 @@ pfr_destroy_ktables(struct pfr_ktableworkq *workq, int > flushaddr) > } > } > > +void > +pfr_destroy_ktables_aux(struct pfr_ktableworkq *auxq) > +{ > + struct pfr_ktable *p; > + > + while ((p = SLIST_FIRST(auxq)) != NULL) { > + SLIST_REMOVE_HEAD(auxq, pfrkt_workq); > + /* > + * There must be no extra data (rules, shadow tables, ...) > + * attached, because auxq holds just empty memory to be > + * initialized. Therefore we can also be called with no lock. > + */ > + if (p->pfrkt_root != NULL) { > + KASSERT(p->pfrkt_root->pfrkt_rs == NULL); > + KASSERT(p->pfrkt_root->pfrkt_shadow == NULL); > + KASSERT(p->pfrkt_root->pfrkt_root == NULL); > + pfr_destroy_ktable(p->pfrkt_root, 0); > + p->pfrkt_root = NULL; > + } > + KASSERT(p->pfrkt_rs == NULL); > + KASSERT(p->pfrkt_shadow == NULL); > + pfr_destroy_ktable(p, 0); > + } > +} > + > void > pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) > {