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)