Kernel lock is always taken when we do access to `pxd_pxis' lists and
`pppx_ifs' tree, so rely on it instead of netlock. The search in
`pppx_ifs' tree has no context switch. We also have no context switch
between the `pxi' free unit search and tree insertion.

Use reference counters to make `pxi' dereference safe, instead of
holding net lock. Now pppx_if_find() returns `pxi' with reference
counter bumped, and newly introduced pppx_if_rele() used for release
this `pxi'.

Also, we mark dying `pxi' by setting `pxi_ready' to null, so concurrent
thread can't receive it by pppx_if_find(). 

I also introduced pppx_if_find_locked() which returns `pxi' but doesn't
bump reference counter. pppx_if_find_locked() and pppx_if_find() both
called with kernel lock held, but I want to keep existing notation where
_locked() function returned data with non bumped counter.

The netlock is still taken when we modify `if_description' of associated
'ifnet', ant it also looks unwanted here, because `if_description' never
used in packet processing path. Unfortunately, this requires to modify
'ifnet' locking, so I want to do this with separate diff.

Index: sys/net/if_pppx.c
===================================================================
RCS file: /cvs/src/sys/net/if_pppx.c,v
retrieving revision 1.123
diff -u -p -r1.123 if_pppx.c
--- sys/net/if_pppx.c   19 Nov 2022 15:12:38 -0000      1.123
+++ sys/net/if_pppx.c   21 Nov 2022 00:29:57 -0000
@@ -57,6 +57,7 @@
 #include <sys/ioctl.h>
 #include <sys/vnode.h>
 #include <sys/selinfo.h>
+#include <sys/refcnt.h>
 
 #include <net/if.h>
 #include <net/if_types.h>
@@ -132,7 +133,7 @@ struct pppx_dev {
        /* queue of packets for userland to service - protected by splnet */
        struct mbuf_queue       pxd_svcq;
        int                     pxd_waiting;    /* [N] */
-       LIST_HEAD(,pppx_if)     pxd_pxis;       /* [N] */
+       LIST_HEAD(,pppx_if)     pxd_pxis;       /* [K] */
 };
 
 LIST_HEAD(, pppx_dev)          pppx_devs =
@@ -150,11 +151,12 @@ struct pppx_if_key {
 struct pppx_if {
        struct pppx_if_key      pxi_key;                /* [I] must be first
                                                            in the struct */
+       struct refcnt           pxi_refcnt;
 
-       RBT_ENTRY(pppx_if)      pxi_entry;              /* [N] */
-       LIST_ENTRY(pppx_if)     pxi_list;               /* [N] */
+       RBT_ENTRY(pppx_if)      pxi_entry;              /* [K] */
+       LIST_ENTRY(pppx_if)     pxi_list;               /* [K] */
 
-       int                     pxi_ready;              /* [N] */
+       int                     pxi_ready;              /* [K] */
 
        int                     pxi_unit;               /* [I] */
        struct ifnet            pxi_if;
@@ -172,7 +174,9 @@ RBT_HEAD(pppx_ifs, pppx_if) pppx_ifs = R
 RBT_PROTOTYPE(pppx_ifs, pppx_if, pxi_entry, pppx_if_cmp);
 
 int            pppx_if_next_unit(void);
-struct pppx_if *pppx_if_find(struct pppx_dev *, int, int);
+struct pppx_if *pppx_if_find_locked(struct pppx_dev *, int, int);
+static inline struct pppx_if   *pppx_if_find(struct pppx_dev *, int, int);
+static inline void              pppx_if_rele(struct pppx_if *);
 int            pppx_add_session(struct pppx_dev *,
                    struct pipex_session_req *);
 int            pppx_del_session(struct pppx_dev *,
@@ -370,11 +374,8 @@ pppxwrite(dev_t dev, struct uio *uio, in
        th = mtod(top, struct pppx_hdr *);
        m_adj(top, sizeof(struct pppx_hdr));
 
-       NET_LOCK();
-
        pxi = pppx_if_find(pxd, th->pppx_id, th->pppx_proto);
        if (pxi == NULL) {
-               NET_UNLOCK();
                m_freem(top);
                return (EINVAL);
        }
@@ -388,6 +389,8 @@ pppxwrite(dev_t dev, struct uio *uio, in
        proto = ntohl(*(uint32_t *)(th + 1));
        m_adj(top, sizeof(uint32_t));
 
+       NET_LOCK();
+
        switch (proto) {
        case AF_INET:
                ipv4_input(&pxi->pxi_if, top);
@@ -405,6 +408,8 @@ pppxwrite(dev_t dev, struct uio *uio, in
 
        NET_UNLOCK();
 
+       pppx_if_rele(pxi);
+
        return (error);
 }
 
@@ -421,17 +426,13 @@ pppxioctl(dev_t dev, u_long cmd, caddr_t
                break;
 
        case PIPEXDSESSION:
-               NET_LOCK();
                error = pppx_del_session(pxd,
                    (struct pipex_session_close_req *)addr);
-               NET_UNLOCK();
                break;
 
        case PIPEXSIFDESCR:
-               NET_LOCK();
                error = pppx_set_session_descr(pxd,
                    (struct pipex_session_descr_req *)addr);
-               NET_UNLOCK();
                break;
 
        case FIONBIO:
@@ -526,11 +527,10 @@ pppxclose(dev_t dev, int flags, int mode
 
        pxd = pppx_dev_lookup(dev);
 
-       /* XXX */
-       NET_LOCK();
-       while ((pxi = LIST_FIRST(&pxd->pxd_pxis)))
+       while ((pxi = LIST_FIRST(&pxd->pxd_pxis))) {
+               pxi->pxi_ready = 0;
                pppx_if_destroy(pxd, pxi);
-       NET_UNLOCK();
+       }
 
        LIST_REMOVE(pxd, pxd_entry);
 
@@ -566,7 +566,7 @@ pppx_if_next_unit(void)
 }
 
 struct pppx_if *
-pppx_if_find(struct pppx_dev *pxd, int session_id, int protocol)
+pppx_if_find_locked(struct pppx_dev *pxd, int session_id, int protocol)
 {
        struct pppx_if_key key;
        struct pppx_if *pxi;
@@ -582,6 +582,23 @@ pppx_if_find(struct pppx_dev *pxd, int s
        return pxi;
 }
 
+static inline struct pppx_if *
+pppx_if_find(struct pppx_dev *pxd, int session_id, int protocol)
+{
+       struct pppx_if *pxi;
+
+       if ((pxi = pppx_if_find_locked(pxd, session_id, protocol)))
+               refcnt_take(&pxi->pxi_refcnt);
+       
+       return pxi;
+}
+
+static inline void
+pppx_if_rele(struct pppx_if *pxi)
+{
+       refcnt_rele_wake(&pxi->pxi_refcnt);
+}
+
 int
 pppx_add_session(struct pppx_dev *pxd, struct pipex_session_req *req)
 {
@@ -609,7 +626,6 @@ pppx_add_session(struct pppx_dev *pxd, s
 
        pxi->pxi_session = session;
 
-       NET_LOCK();
        /* try to set the interface up */
        unit = pppx_if_next_unit();
        if (unit < 0) {
@@ -617,6 +633,7 @@ pppx_add_session(struct pppx_dev *pxd, s
                goto out;
        }
 
+       refcnt_init(&pxi->pxi_refcnt);
        pxi->pxi_unit = unit;
        pxi->pxi_key.pxik_session_id = req->pr_session_id;
        pxi->pxi_key.pxik_protocol = req->pr_protocol;
@@ -627,7 +644,6 @@ pppx_add_session(struct pppx_dev *pxd, s
                goto out;
        }
        LIST_INSERT_HEAD(&pxd->pxd_pxis, pxi, pxi_list);
-       NET_UNLOCK();
 
        snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", "pppx", unit);
        ifp->if_mtu = req->pr_peer_mru; /* XXX */
@@ -699,21 +715,18 @@ pppx_add_session(struct pppx_dev *pxd, s
 
        NET_LOCK();
        SET(ifp->if_flags, IFF_RUNNING);
-       pxi->pxi_ready = 1;
        NET_UNLOCK();
+       pxi->pxi_ready = 1;
 
        return (error);
 
 detach:
        if_detach(ifp);
 
-       NET_LOCK();
        if (RBT_REMOVE(pppx_ifs, &pppx_ifs, pxi) == NULL)
                panic("%s: inconsistent RB tree", __func__);
        LIST_REMOVE(pxi, pxi_list);
 out:
-       NET_UNLOCK();
-
        pool_put(&pppx_if_pl, pxi);
        pipex_rele_session(session);
 
@@ -725,10 +738,11 @@ pppx_del_session(struct pppx_dev *pxd, s
 {
        struct pppx_if *pxi;
 
-       pxi = pppx_if_find(pxd, req->pcr_session_id, req->pcr_protocol);
+       pxi = pppx_if_find_locked(pxd, req->pcr_session_id, req->pcr_protocol);
        if (pxi == NULL)
                return (EINVAL);
 
+       pxi->pxi_ready = 0;
        pipex_export_session_stats(pxi->pxi_session, &req->pcr_stat);
        pppx_if_destroy(pxd, pxi);
        return (0);
@@ -744,8 +758,12 @@ pppx_set_session_descr(struct pppx_dev *
        if (pxi == NULL)
                return (EINVAL);
 
+       NET_LOCK();
        (void)memset(pxi->pxi_if.if_description, 0, IFDESCRSIZE);
        strlcpy(pxi->pxi_if.if_description, req->pdr_descr, IFDESCRSIZE);
+       NET_UNLOCK();
+
+       pppx_if_rele(pxi);
 
        return (0);
 }
@@ -756,18 +774,17 @@ pppx_if_destroy(struct pppx_dev *pxd, st
        struct ifnet *ifp;
        struct pipex_session *session;
 
-       NET_ASSERT_LOCKED();
        session = pxi->pxi_session;
        ifp = &pxi->pxi_if;
-       pxi->pxi_ready = 0;
-       CLR(ifp->if_flags, IFF_RUNNING);
 
-       pipex_unlink_session(session);
+       refcnt_finalize(&pxi->pxi_refcnt, "pxifinal");
 
-       /* XXXSMP breaks atomicity */
+       NET_LOCK();
+       CLR(ifp->if_flags, IFF_RUNNING);
        NET_UNLOCK();
+
+       pipex_unlink_session(session);
        if_detach(ifp);
-       NET_LOCK();
 
        pipex_rele_session(session);
        if (RBT_REMOVE(pppx_ifs, &pppx_ifs, pxi) == NULL)

Reply via email to