Now that we have a single refcounting mechanism for route entries, I'd like to use atomic operations and grab the KERNEL_LOCK only if a CPU is dropping the last reference on an entry.
Currently this only matters for MPLS. I intentionally use atomic_* ops because I'd like to see be able to see if a counter goes negative. For symmetry reasons I'm also moving the KERNEL_LOCK() inside rtalloc(). These two functions are my current targets. Comments, oks? Index: sys/net/route.c =================================================================== RCS file: /cvs/src/sys/net/route.c,v retrieving revision 1.258 diff -u -p -r1.258 route.c --- sys/net/route.c 22 Oct 2015 17:19:38 -0000 1.258 +++ sys/net/route.c 22 Oct 2015 17:21:52 -0000 @@ -215,6 +215,7 @@ rtalloc(struct sockaddr *dst, int flags, info.rti_info[RTAX_DST] = dst; s = splsoftnet(); + KERNEL_LOCK(); rt = rtable_match(tableid, dst); if (rt != NULL) { if ((rt->rt_flags & RTF_CLONING) && ISSET(flags, RT_RESOLVE)) { @@ -236,6 +237,7 @@ miss: if (ISSET(flags, RT_REPORT)) rt_missmsg(RTM_MISS, &info, 0, NULL, error, tableid); } + KERNEL_UNLOCK(); splx(s); return (rt); } @@ -337,7 +339,7 @@ rtalloc_mpath(struct sockaddr *dst, uint void rtref(struct rtentry *rt) { - rt->rt_refcnt++; + atomic_inc_int(&rt->rt_refcnt); } void @@ -348,14 +350,16 @@ rtfree(struct rtentry *rt) if (rt == NULL) return; - if (--rt->rt_refcnt <= 0) { + if (atomic_dec_int_nv(&rt->rt_refcnt) <= 0) { KASSERT(!ISSET(rt->rt_flags, RTF_UP)); KASSERT(!RT_ROOT(rt)); - rttrash--; + atomic_dec_int(&rttrash); if (rt->rt_refcnt < 0) { printf("rtfree: %p not freed (neg refs)\n", rt); return; } + + KERNEL_LOCK(); rt_timer_remove_all(rt); ifa = rt->rt_ifa; if (ifa) @@ -368,6 +372,8 @@ rtfree(struct rtentry *rt) if (rt->rt_gateway) free(rt->rt_gateway, M_RTABLE, 0); free(rt_key(rt), M_RTABLE, 0); + KERNEL_UNLOCK(); + pool_put(&rtentry_pool, rt); } } @@ -773,7 +779,7 @@ rtrequest1(int req, struct rt_addrinfo * rt->rt_flags &= ~RTF_UP; if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt); - rttrash++; + atomic_inc_int(&rttrash); if (ret_nrt != NULL) *ret_nrt = rt; Index: sys/netmpls/mpls_input.c =================================================================== RCS file: /cvs/src/sys/netmpls/mpls_input.c,v retrieving revision 1.50 diff -u -p -r1.50 mpls_input.c --- sys/netmpls/mpls_input.c 23 Sep 2015 08:49:46 -0000 1.50 +++ sys/netmpls/mpls_input.c 22 Oct 2015 17:21:52 -0000 @@ -170,9 +170,7 @@ do_v6: } } - KERNEL_LOCK(); rt = rtalloc(smplstosa(smpls), RT_REPORT|RT_RESOLVE, 0); - KERNEL_UNLOCK(); if (rt == NULL) { /* no entry for this label */ #ifdef MPLS_DEBUG @@ -290,9 +288,7 @@ do_v6: if (ifp != NULL && rt_mpls->mpls_operation != MPLS_OP_LOCAL) break; - KERNEL_LOCK(); rtfree(rt); - KERNEL_UNLOCK(); rt = NULL; } @@ -323,11 +319,7 @@ do_v6: (*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt); KERNEL_UNLOCK(); done: - if (rt) { - KERNEL_LOCK(); - rtfree(rt); - KERNEL_UNLOCK(); - } + rtfree(rt); } int @@ -394,7 +386,7 @@ mpls_do_error(struct mbuf *m, int type, struct in_ifaddr *ia; struct icmp *icp; struct ip *ip; - int nstk; + int nstk, error; for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) { if (m->m_len < sizeof(*shim) && @@ -427,9 +419,7 @@ mpls_do_error(struct mbuf *m, int type, smpls->smpls_len = sizeof(*smpls); smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; - KERNEL_LOCK(); rt = rtalloc(smplstosa(smpls), RT_REPORT|RT_RESOLVE, 0); - KERNEL_UNLOCK(); if (rt == NULL) { /* no entry for this label */ m_freem(m); @@ -442,19 +432,16 @@ mpls_do_error(struct mbuf *m, int type, * less interface we need to find some other IP to * use as source. */ - KERNEL_LOCK(); rtfree(rt); - KERNEL_UNLOCK(); m_freem(m); return (NULL); } - KERNEL_LOCK(); rtfree(rt); - if (icmp_reflect(m, NULL, ia)) { - KERNEL_UNLOCK(); - return (NULL); - } + KERNEL_LOCK(); + error = icmp_reflect(m, NULL, ia); KERNEL_UNLOCK(); + if (error) + return (NULL); ip = mtod(m, struct ip *); /* stuff to fix up which is normaly done in ip_output */