Hello,

thanks for taking a look.

</snip>
> > +           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

yes. you are right. new diff addresses that with change as follows:

    @@ -1542,9 +1542,8 @@ pfr_add_tables(struct pfr_table ...)
                                    pfr_destroy_ktable(p, 0);
                                    break;
                            }
    +                       SLIST_INSERT_HEAD(&auxq, p, pfrkt_workq);
                    }
    -
    -               SLIST_INSERT_HEAD(&auxq, p, pfrkt_workq);
            }
     
            /*

</snip>

> > +                   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.

    very good point. I think this what needs to be done:

    @@ -1558,7 +1558,8 @@ pfr_add_tables(struct pfr_table *tbl, ...)
                    if (p == NULL) {
                            SLIST_REMOVE(&auxq, n, pfr_ktable, pfrkt_workq);
                            SLIST_INSERT_HEAD(&addq, n, pfrkt_workq);
    -               } else if (!(flags & PFR_FLAG_DUMMY)) {
    +               } else if (!(flags & PFR_FLAG_DUMMY) &&
    +                           !(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);


</snip>
> > +                   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.
> >                             }
> >                     }

    yes, exactly. we want to continue with outer loop if we break from inner
    one. this is what I want to do:

    @@ -1617,9 +1618,12 @@ pfr_add_tables(struct pfr_table *tbl, ...)
                                            p->pfrkt_root = r;
                                            SLIST_INSERT_HEAD(&auxq, q,
                                                pfrkt_workq);
    -                                       continue;
    +                                       break;
                                    }
                            }
    +                       if (r != SLIST_END(&addq))
    +                               continue;
    +
                            q->pfrkt_rs = pf_find_or_create_ruleset(
                                q->pfrkt_root->pfrkt_anchor);
                            /*


updated diff is below.

thanks and
regards
sashan

--------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..4dd49ea3550 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,145 @@ 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;
+                       }
+                       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) &&
+                           !(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;
+                       }
+                       p->pfrkt_rs->tables++;
+
+                       if (!p->pfrkt_anchor[0]) {
+                               q = p->pfrkt_root;
+                               p->pfrkt_root = NULL;
+                               SLIST_INSERT_HEAD(&auxq, q, pfrkt_workq);
+                               continue;
                        }
-                       SLIST_INSERT_HEAD(&addq, p, pfrkt_workq);
-                       xadd++;
-                       if (!key.pfrkt_anchor[0])
-                               goto _skip;
 
-                       /* find or create root table */
-                       bzero(key.pfrkt_anchor, sizeof(key.pfrkt_anchor));
-                       r = RB_FIND(pfr_ktablehead, &pfr_ktables, &key);
+                       /* 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);
+                                       break;
                                }
                        }
-                       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++;
+                       if (r != SLIST_END(&addq))
+                               continue;
+
+                       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 +2297,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 +2333,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)
 {

Reply via email to