Date: Sun, 24 Apr 2016 16:41:54 +0000 (UTC) From: "Bjoern A. Zeeb" <bz@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r298548 - in projects/vnet/sys: net netinet netinet6 Message-ID: <201604241641.u3OGfsG5080361@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: bz Date: Sun Apr 24 16:41:54 2016 New Revision: 298548 URL: https://svnweb.freebsd.org/changeset/base/298548 Log: Virtualise the netisr registration in order to do a per-vnet de-registration to prevent further packets for a specific protocol (IP, ARP, IPv6) to come up from ether_demux(). We currently have not better "plug-and-play" hook in place at that level but should think about that in some distant future (e.g., to one day be able to load ip, or ipv6). Note: this commit will be reverted soon. It turns out that while the idea is good, and basically works, it can possibly lead to deadlocks. Sponsored by: The FreeBSD Foundation Modified: projects/vnet/sys/net/if_epair.c projects/vnet/sys/net/if_ethersubr.c projects/vnet/sys/net/netisr.c projects/vnet/sys/net/rtsock.c projects/vnet/sys/netinet/if_ether.c projects/vnet/sys/netinet/igmp.c projects/vnet/sys/netinet/ip_input.c projects/vnet/sys/netinet6/ip6_input.c Modified: projects/vnet/sys/net/if_epair.c ============================================================================== --- projects/vnet/sys/net/if_epair.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/net/if_epair.c Sun Apr 24 16:41:54 2016 (r298548) @@ -959,6 +959,7 @@ vnet_epair_init(const void *unused __unu V_epair_cloner = if_clone_advanced(epairname, 0, epair_clone_match, epair_clone_create, epair_clone_destroy); + netisr_register(&epair_nh); } VNET_SYSINIT(vnet_epair_init, SI_SUB_PSEUDO, SI_ORDER_ANY, vnet_epair_init, NULL); @@ -967,6 +968,7 @@ static void vnet_epair_uninit(const void *unused __unused) { + netisr_unregister(&epair_nh); if_clone_detach(V_epair_cloner); } VNET_SYSUNINIT(vnet_epair_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, @@ -984,12 +986,10 @@ epair_modevent(module_t mod, int type, v epair_nh.nh_qlimit = 42 * ifqmaxlen; /* 42 shall be the number. */ if (TUNABLE_INT_FETCH("net.link.epair.netisr_maxqlen", &qlimit)) epair_nh.nh_qlimit = qlimit; - netisr_register(&epair_nh); if (bootverbose) printf("%s initialized.\n", epairname); break; case MOD_UNLOAD: - netisr_unregister(&epair_nh); epair_dpcpu_detach(); if (bootverbose) printf("%s unloaded.\n", epairname); @@ -1006,5 +1006,5 @@ static moduledata_t epair_mod = { 0 }; -DECLARE_MODULE(if_epair, epair_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); +DECLARE_MODULE(if_epair, epair_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE); MODULE_VERSION(if_epair, 1); Modified: projects/vnet/sys/net/if_ethersubr.c ============================================================================== --- projects/vnet/sys/net/if_ethersubr.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/net/if_ethersubr.c Sun Apr 24 16:41:54 2016 (r298548) @@ -654,14 +654,6 @@ static struct netisr_handler ether_nh = }; static void -ether_init(__unused void *arg) -{ - - netisr_register(ðer_nh); -} -SYSINIT(ether, SI_SUB_INIT_IF, SI_ORDER_ANY, ether_init, NULL); - -static void vnet_ether_init(__unused void *arg) { int i; @@ -672,12 +664,13 @@ vnet_ether_init(__unused void *arg) if ((i = pfil_head_register(&V_link_pfil_hook)) != 0) printf("%s: WARNING: unable to register pfil link hook, " "error %d\n", __func__, i); + netisr_register(ðer_nh); } VNET_SYSINIT(vnet_ether_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_ether_init, NULL); static void -vnet_ether_destroy(__unused void *arg) +vnet_ether_pfil_destroy(__unused void *arg) { int i; @@ -685,10 +678,17 @@ vnet_ether_destroy(__unused void *arg) printf("%s: WARNING: unable to unregister pfil link hook, " "error %d\n", __func__, i); } -VNET_SYSUNINIT(vnet_ether_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY, - vnet_ether_destroy, NULL); +VNET_SYSUNINIT(vnet_ether_pfil_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY, + vnet_ether_pfil_destroy, NULL); +static void +vnet_ether_destroy(__unused void *arg) +{ + netisr_unregister(ðer_nh); +} +VNET_SYSUNINIT(vnet_ether_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, + vnet_ether_destroy, NULL); static void ether_input(struct ifnet *ifp, struct mbuf *m) @@ -710,7 +710,9 @@ ether_input(struct ifnet *ifp, struct mb * so assert it is correct here. */ KASSERT(m->m_pkthdr.rcvif == ifp, ("%s: ifnet mismatch", __func__)); + CURVNET_SET_QUIET(ifp->if_vnet); netisr_dispatch(NETISR_ETHER, m); + CURVNET_RESTORE(); m = mn; } } Modified: projects/vnet/sys/net/netisr.c ============================================================================== --- projects/vnet/sys/net/netisr.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/net/netisr.c Sun Apr 24 16:41:54 2016 (r298548) @@ -208,7 +208,8 @@ SYSCTL_UINT(_net_isr, OID_AUTO, maxprot, * The netisr_proto array describes all registered protocols, indexed by * protocol number. See netisr_internal.h for more details. */ -static struct netisr_proto netisr_proto[NETISR_MAXPROT]; +static VNET_DEFINE(struct netisr_proto, netisr_proto[NETISR_MAXPROT]); +#define V_netisr_proto VNET(netisr_proto) /* * Per-CPU workstream data. See netisr_internal.h for more details. @@ -396,31 +397,31 @@ netisr_register(const struct netisr_hand * Test that no existing registration exists for this protocol. */ NETISR_WLOCK(); - KASSERT(netisr_proto[proto].np_name == NULL, + KASSERT(V_netisr_proto[proto].np_name == NULL, ("%s(%u, %s): name present", __func__, proto, name)); - KASSERT(netisr_proto[proto].np_handler == NULL, + KASSERT(V_netisr_proto[proto].np_handler == NULL, ("%s(%u, %s): handler present", __func__, proto, name)); - netisr_proto[proto].np_name = name; - netisr_proto[proto].np_handler = nhp->nh_handler; - netisr_proto[proto].np_m2flow = nhp->nh_m2flow; - netisr_proto[proto].np_m2cpuid = nhp->nh_m2cpuid; - netisr_proto[proto].np_drainedcpu = nhp->nh_drainedcpu; + V_netisr_proto[proto].np_name = name; + V_netisr_proto[proto].np_handler = nhp->nh_handler; + V_netisr_proto[proto].np_m2flow = nhp->nh_m2flow; + V_netisr_proto[proto].np_m2cpuid = nhp->nh_m2cpuid; + V_netisr_proto[proto].np_drainedcpu = nhp->nh_drainedcpu; if (nhp->nh_qlimit == 0) - netisr_proto[proto].np_qlimit = netisr_defaultqlimit; + V_netisr_proto[proto].np_qlimit = netisr_defaultqlimit; else if (nhp->nh_qlimit > netisr_maxqlimit) { printf("%s: %s requested queue limit %u capped to " "net.isr.maxqlimit %u\n", __func__, name, nhp->nh_qlimit, netisr_maxqlimit); - netisr_proto[proto].np_qlimit = netisr_maxqlimit; + V_netisr_proto[proto].np_qlimit = netisr_maxqlimit; } else - netisr_proto[proto].np_qlimit = nhp->nh_qlimit; - netisr_proto[proto].np_policy = nhp->nh_policy; - netisr_proto[proto].np_dispatch = nhp->nh_dispatch; + V_netisr_proto[proto].np_qlimit = nhp->nh_qlimit; + V_netisr_proto[proto].np_policy = nhp->nh_policy; + V_netisr_proto[proto].np_dispatch = nhp->nh_dispatch; CPU_FOREACH(i) { npwp = &(DPCPU_ID_PTR(i, nws))->nws_work[proto]; bzero(npwp, sizeof(*npwp)); - npwp->nw_qlimit = netisr_proto[proto].np_qlimit; + npwp->nw_qlimit = V_netisr_proto[proto].np_qlimit; } NETISR_WUNLOCK(); } @@ -445,7 +446,7 @@ netisr_clearqdrops(const struct netisr_h ("%s(%u): protocol too big for %s", __func__, proto, name)); NETISR_WLOCK(); - KASSERT(netisr_proto[proto].np_handler != NULL, + KASSERT(V_netisr_proto[proto].np_handler != NULL, ("%s(%u): protocol not registered for %s", __func__, proto, name)); @@ -478,7 +479,7 @@ netisr_getqdrops(const struct netisr_han ("%s(%u): protocol too big for %s", __func__, proto, name)); NETISR_RLOCK(&tracker); - KASSERT(netisr_proto[proto].np_handler != NULL, + KASSERT(V_netisr_proto[proto].np_handler != NULL, ("%s(%u): protocol not registered for %s", __func__, proto, name)); @@ -509,10 +510,10 @@ netisr_getqlimit(const struct netisr_han ("%s(%u): protocol too big for %s", __func__, proto, name)); NETISR_RLOCK(&tracker); - KASSERT(netisr_proto[proto].np_handler != NULL, + KASSERT(V_netisr_proto[proto].np_handler != NULL, ("%s(%u): protocol not registered for %s", __func__, proto, name)); - *qlimitp = netisr_proto[proto].np_qlimit; + *qlimitp = V_netisr_proto[proto].np_qlimit; NETISR_RUNLOCK(&tracker); } @@ -541,11 +542,11 @@ netisr_setqlimit(const struct netisr_han ("%s(%u): protocol too big for %s", __func__, proto, name)); NETISR_WLOCK(); - KASSERT(netisr_proto[proto].np_handler != NULL, + KASSERT(V_netisr_proto[proto].np_handler != NULL, ("%s(%u): protocol not registered for %s", __func__, proto, name)); - netisr_proto[proto].np_qlimit = qlimit; + V_netisr_proto[proto].np_qlimit = qlimit; CPU_FOREACH(i) { npwp = &(DPCPU_ID_PTR(i, nws))->nws_work[proto]; npwp->nw_qlimit = qlimit; @@ -600,16 +601,16 @@ netisr_unregister(const struct netisr_ha ("%s(%u): protocol too big for %s", __func__, proto, name)); NETISR_WLOCK(); - KASSERT(netisr_proto[proto].np_handler != NULL, + KASSERT(V_netisr_proto[proto].np_handler != NULL, ("%s(%u): protocol not registered for %s", __func__, proto, name)); - netisr_proto[proto].np_name = NULL; - netisr_proto[proto].np_handler = NULL; - netisr_proto[proto].np_m2flow = NULL; - netisr_proto[proto].np_m2cpuid = NULL; - netisr_proto[proto].np_qlimit = 0; - netisr_proto[proto].np_policy = 0; + V_netisr_proto[proto].np_name = NULL; + V_netisr_proto[proto].np_handler = NULL; + V_netisr_proto[proto].np_m2flow = NULL; + V_netisr_proto[proto].np_m2cpuid = NULL; + V_netisr_proto[proto].np_qlimit = 0; + V_netisr_proto[proto].np_policy = 0; CPU_FOREACH(i) { npwp = &(DPCPU_ID_PTR(i, nws))->nws_work[proto]; netisr_drain_proto(npwp); @@ -763,13 +764,16 @@ netisr_process_workstream_proto(struct n VNET_ASSERT(m->m_pkthdr.rcvif != NULL, ("%s:%d rcvif == NULL: m=%p", __func__, __LINE__, m)); CURVNET_SET(m->m_pkthdr.rcvif->if_vnet); - netisr_proto[proto].np_handler(m); + V_netisr_proto[proto].np_handler(m); CURVNET_RESTORE(); } KASSERT(local_npw.nw_len == 0, ("%s(%u): len %u", __func__, proto, local_npw.nw_len)); - if (netisr_proto[proto].np_drainedcpu) - netisr_proto[proto].np_drainedcpu(nwsp->nws_cpu); + /* We can just use the one from the default VNET. */ + CURVNET_SET_QUIET(vnet0); + if (V_netisr_proto[proto].np_drainedcpu) + V_netisr_proto[proto].np_drainedcpu(nwsp->nws_cpu); + CURVNET_RESTORE(); NWS_LOCK(nwsp); npwp->nw_handled += handled; return (handled); @@ -905,10 +909,12 @@ netisr_queue_src(u_int proto, uintptr_t #ifdef NETISR_LOCKING NETISR_RLOCK(&tracker); #endif - KASSERT(netisr_proto[proto].np_handler != NULL, - ("%s: invalid proto %u", __func__, proto)); + if (V_netisr_proto[proto].np_handler == NULL) { + m_freem(m); + return (ENOPROTOOPT); + } - m = netisr_select_cpuid(&netisr_proto[proto], NETISR_DISPATCH_DEFERRED, + m = netisr_select_cpuid(&V_netisr_proto[proto], NETISR_DISPATCH_DEFERRED, source, m, &cpuid); if (m != NULL) { KASSERT(!CPU_ABSENT(cpuid), ("%s: CPU %u absent", __func__, @@ -950,9 +956,11 @@ netisr_dispatch_src(u_int proto, uintptr #ifdef NETISR_LOCKING NETISR_RLOCK(&tracker); #endif - npp = &netisr_proto[proto]; - KASSERT(npp->np_handler != NULL, ("%s: invalid proto %u", __func__, - proto)); + npp = &V_netisr_proto[proto]; + if (npp->np_handler == NULL) { + m_freem(m); + return (ENOPROTOOPT); + } dispatch_policy = netisr_get_dispatch(npp); if (dispatch_policy == NETISR_DISPATCH_DEFERRED) @@ -970,7 +978,7 @@ netisr_dispatch_src(u_int proto, uintptr npwp = &nwsp->nws_work[proto]; npwp->nw_dispatched++; npwp->nw_handled++; - netisr_proto[proto].np_handler(m); + V_netisr_proto[proto].np_handler(m); error = 0; goto out_unlock; } @@ -984,7 +992,7 @@ netisr_dispatch_src(u_int proto, uintptr * already running. */ sched_pin(); - m = netisr_select_cpuid(&netisr_proto[proto], NETISR_DISPATCH_HYBRID, + m = netisr_select_cpuid(&V_netisr_proto[proto], NETISR_DISPATCH_HYBRID, source, m, &cpuid); if (m == NULL) { error = ENOBUFS; @@ -1021,7 +1029,7 @@ netisr_dispatch_src(u_int proto, uintptr */ nwsp->nws_flags |= NWS_DISPATCHING; NWS_UNLOCK(nwsp); - netisr_proto[proto].np_handler(m); + V_netisr_proto[proto].np_handler(m); NWS_LOCK(nwsp); nwsp->nws_flags &= ~NWS_DISPATCHING; npwp->nw_handled++; @@ -1194,7 +1202,7 @@ sysctl_netisr_proto(SYSCTL_HANDLER_ARGS) counter = 0; NETISR_RLOCK(&tracker); for (proto = 0; proto < NETISR_MAXPROT; proto++) { - npp = &netisr_proto[proto]; + npp = &V_netisr_proto[proto]; if (npp->np_name == NULL) continue; snpp = &snp_array[counter]; @@ -1303,7 +1311,7 @@ sysctl_netisr_work(SYSCTL_HANDLER_ARGS) continue; NWS_LOCK(nwsp); for (proto = 0; proto < NETISR_MAXPROT; proto++) { - npp = &netisr_proto[proto]; + npp = &V_netisr_proto[proto]; if (npp->np_name == NULL) continue; nwp = &nwsp->nws_work[proto]; @@ -1352,7 +1360,7 @@ DB_SHOW_COMMAND(netisr, db_show_netisr) continue; first = 1; for (proto = 0; proto < NETISR_MAXPROT; proto++) { - if (netisr_proto[proto].np_handler == NULL) + if (V_netisr_proto[proto].np_handler == NULL) continue; nwp = &nwsp->nws_work[proto]; if (first) { @@ -1362,7 +1370,7 @@ DB_SHOW_COMMAND(netisr, db_show_netisr) db_printf("%3s ", ""); db_printf( "%6s %5d %5d %5d %8ju %8ju %8ju %8ju\n", - netisr_proto[proto].np_name, nwp->nw_len, + V_netisr_proto[proto].np_name, nwp->nw_len, nwp->nw_watermark, nwp->nw_qlimit, nwp->nw_dispatched, nwp->nw_hybrid_dispatched, nwp->nw_qdrops, nwp->nw_queued); Modified: projects/vnet/sys/net/rtsock.c ============================================================================== --- projects/vnet/sys/net/rtsock.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/net/rtsock.c Sun Apr 24 16:41:54 2016 (r298548) @@ -197,10 +197,27 @@ rts_init(void) if (TUNABLE_INT_FETCH("net.route.netisr_maxqlen", &tmp)) rtsock_nh.nh_qlimit = tmp; - netisr_register(&rtsock_nh); } SYSINIT(rtsock, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, rts_init, 0); +static void +vnet_rts_init(void) +{ + + netisr_register(&rtsock_nh); +} +VNET_SYSINIT(vnet_rtsock, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, + vnet_rts_init, 0); + +static void +vnet_rts_uninit(void) +{ + + netisr_unregister(&rtsock_nh); +} +VNET_SYSUNINIT(vnet_rts_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, + vnet_rts_uninit, 0); + static int raw_input_rts_cb(struct mbuf *m, struct sockproto *proto, struct sockaddr *src, struct rawcb *rp) Modified: projects/vnet/sys/netinet/if_ether.c ============================================================================== --- projects/vnet/sys/netinet/if_ether.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/netinet/if_ether.c Sun Apr 24 16:41:54 2016 (r298548) @@ -143,7 +143,6 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUT } while (0) -static void arp_init(void); static void arpintr(struct mbuf *); static void arptimer(void *); #ifdef INET @@ -1327,7 +1326,7 @@ arp_iflladdr(void *arg __unused, struct } static void -arp_init(void) +vnet_arp_init(void) { netisr_register(&arp_nh); @@ -1335,4 +1334,20 @@ arp_init(void) iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, arp_iflladdr, NULL, EVENTHANDLER_PRI_ANY); } -SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_SECOND, arp_init, 0); +VNET_SYSINIT(vnet_arp_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_SECOND, + vnet_arp_init, 0); + +#ifdef VIMAGE +/* + * We have to unregister ARP along with IP otherwise we risk doing INADDR_HASH + * lookups after destroying the hash. Ideally this would go on SI_ORDER_3.5. + */ +static void +vnet_arp_destroy(__unused void *arg) +{ + + netisr_unregister(&arp_nh); +} +VNET_SYSUNINIT(vnet_arp_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, + vnet_arp_destroy, NULL); +#endif Modified: projects/vnet/sys/netinet/igmp.c ============================================================================== --- projects/vnet/sys/netinet/igmp.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/netinet/igmp.c Sun Apr 24 16:41:54 2016 (r298548) @@ -3592,6 +3592,15 @@ igmp_rec_type_to_str(const int type) } #endif +static void +vnet_igmp_init(const void *unused __unused) +{ + + netisr_register(&igmp_nh); +} +VNET_SYSINIT(vnet_igmp_init, SI_SUB_PROTO_MC, SI_ORDER_ANY, + vnet_igmp_init, NULL); + #ifdef VIMAGE static void vnet_igmp_uninit(const void *unused __unused) @@ -3599,6 +3608,8 @@ vnet_igmp_uninit(const void *unused __un /* This can happen when we shutdown the entire network stack. */ CTR1(KTR_IGMPV3, "%s: tearing down", __func__); + + netisr_unregister(&igmp_nh); } VNET_SYSUNINIT(vnet_igmp_uninit, SI_SUB_PROTO_MC, SI_ORDER_ANY, vnet_igmp_uninit, NULL); @@ -3644,11 +3655,9 @@ igmp_modevent(module_t mod, int type, vo CTR1(KTR_IGMPV3, "%s: initializing", __func__); IGMP_LOCK_INIT(); m_raopt = igmp_ra_alloc(); - netisr_register(&igmp_nh); break; case MOD_UNLOAD: CTR1(KTR_IGMPV3, "%s: tearing down", __func__); - netisr_unregister(&igmp_nh); m_free(m_raopt); m_raopt = NULL; IGMP_LOCK_DESTROY(); @@ -3664,4 +3673,4 @@ static moduledata_t igmp_mod = { igmp_modevent, 0 }; -DECLARE_MODULE(igmp, igmp_mod, SI_SUB_PROTO_MC, SI_ORDER_ANY); +DECLARE_MODULE(igmp, igmp_mod, SI_SUB_PROTO_MC, SI_ORDER_MIDDLE); Modified: projects/vnet/sys/netinet/ip_input.c ============================================================================== --- projects/vnet/sys/netinet/ip_input.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/netinet/ip_input.c Sun Apr 24 16:41:54 2016 (r298548) @@ -332,7 +332,7 @@ ip_init(void) /* Skip initialization of globals for non-default instances. */ if (!IS_DEFAULT_VNET(curvnet)) - return; + goto out; pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); if (pr == NULL) @@ -354,6 +354,7 @@ ip_init(void) ip_protox[pr->pr_protocol] = pr - inetsw; } +out: netisr_register(&ip_nh); #ifdef RSS netisr_register(&ip_direct_nh); @@ -367,6 +368,11 @@ ip_destroy(void *unused __unused) struct ifnet *ifp; int error; +#ifdef RSS + netisr_unregister(&ip_direct_nh); +#endif + netisr_unregister(&ip_nh); + if ((error = pfil_head_unregister(&V_inet_pfil_hook)) != 0) printf("%s: WARNING: unable to unregister pfil hook, " "error %d\n", __func__, error); Modified: projects/vnet/sys/netinet6/ip6_input.c ============================================================================== --- projects/vnet/sys/netinet6/ip6_input.c Sun Apr 24 16:36:33 2016 (r298547) +++ projects/vnet/sys/netinet6/ip6_input.c Sun Apr 24 16:41:54 2016 (r298548) @@ -219,7 +219,7 @@ ip6_init(void) /* Skip global initialization stuff for non-default instances. */ if (!IS_DEFAULT_VNET(curvnet)) - return; + goto out; pr = pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); if (pr == NULL) @@ -241,6 +241,7 @@ ip6_init(void) ip6_protox[pr->pr_protocol] = pr - inet6sw; } +out: netisr_register(&ip6_nh); #ifdef RSS netisr_register(&ip6_direct_nh); @@ -313,6 +314,11 @@ ip6_destroy(void *unused __unused) struct ifnet *ifp; int error; +#ifdef RSS + netisr_unregister(&ip6_direct_nh); +#endif + netisr_unregister(&ip6_nh); + if ((error = pfil_head_unregister(&V_inet6_pfil_hook)) != 0) printf("%s: WARNING: unable to unregister pfil hook, " "error %d\n", __func__, error);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201604241641.u3OGfsG5080361>