diff --git a/distrib/sets/lists/base/shl.mi b/distrib/sets/lists/base/shl.mi index 86e9d5c6fa25..285ca7a9fb62 100644 --- a/distrib/sets/lists/base/shl.mi +++ b/distrib/sets/lists/base/shl.mi @@ -741,6 +741,9 @@ ./usr/lib/librumpkern_nv.so base-rump-shlib rump ./usr/lib/librumpkern_nv.so.0 base-rump-shlib rump ./usr/lib/librumpkern_nv.so.0.0 base-rump-shlib rump +./usr/lib/librumpkern_simplehook_tester.so base-rump-shlib rump +./usr/lib/librumpkern_simplehook_tester.so.0 base-rump-shlib rump +./usr/lib/librumpkern_simplehook_tester.so.0.0 base-rump-shlib rump ./usr/lib/librumpkern_sljit.so base-rump-shlib rump,sljit ./usr/lib/librumpkern_sljit.so.0 base-rump-shlib rump,sljit ./usr/lib/librumpkern_sljit.so.0.0 base-rump-shlib rump,sljit diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index 4e3f2ed9a327..520000670088 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -3257,6 +3257,7 @@ ./usr/include/sys/gmon.h comp-c-include ./usr/include/sys/gpio.h comp-c-include ./usr/include/sys/hash.h comp-c-include +./usr/include/sys/hook.h comp-c-include ./usr/include/sys/idtype.h comp-c-include ./usr/include/sys/ieee754.h comp-c-include ./usr/include/sys/intr.h comp-c-include @@ -3997,6 +3998,8 @@ ./usr/lib/librumpkern_ksem_p.a comp-obsolete obsolete ./usr/lib/librumpkern_nv.a comp-c-lib rump ./usr/lib/librumpkern_nv_p.a comp-c-proflib rump,profile +./usr/lib/librumpkern_simplehook_tester.a comp-c-lib rump +./usr/lib/librumpkern_simplehook_tester_p.a comp-c-proflib rump,profile ./usr/lib/librumpkern_sljit.a comp-c-lib rump,sljit ./usr/lib/librumpkern_sljit_p.a comp-c-proflib rump,sljit,profile ./usr/lib/librumpkern_solaris.a comp-c-lib rump,zfs diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi index ce9e40061fb8..81f055ce935f 100644 --- a/distrib/sets/lists/debug/mi +++ b/distrib/sets/lists/debug/mi @@ -208,6 +208,7 @@ ./usr/lib/librumpkern_crypto_g.a comp-c-debuglib debuglib,rump ./usr/lib/librumpkern_ksem_g.a comp-obsolete obsolete,compatfile ./usr/lib/librumpkern_nv_g.a comp-c-debuglib debuglib,rump +./usr/lib/librumpkern_simplehook_tester_g.a comp-c-debuglib debuglib,rump ./usr/lib/librumpkern_sljit_g.a comp-c-debuglib debuglib,rump,sljit ./usr/lib/librumpkern_solaris_g.a comp-c-debuglib debuglib,rump,zfs ./usr/lib/librumpkern_sysproxy_g.a comp-c-debuglib debuglib,rump diff --git a/distrib/sets/lists/debug/shl.mi b/distrib/sets/lists/debug/shl.mi index 7e719d5ccfb2..62624af64c2e 100644 --- a/distrib/sets/lists/debug/shl.mi +++ b/distrib/sets/lists/debug/shl.mi @@ -261,6 +261,7 @@ ./usr/libdata/debug/usr/lib/librumpkern_crypto.so.0.0.debug comp-rump-debug debug,rump ./usr/libdata/debug/usr/lib/librumpkern_ksem.so.0.0.debug comp-obsolete obsolete,compatfile ./usr/libdata/debug/usr/lib/librumpkern_nv.so.0.0.debug comp-rump-debug debug,rump +./usr/libdata/debug/usr/lib/librumpkern_simplehook_tester.so.0.0.debug comp-rump-debug debug,rump ./usr/libdata/debug/usr/lib/librumpkern_sljit.so.0.0.debug comp-rump-debug debug,rump,sljit ./usr/libdata/debug/usr/lib/librumpkern_solaris.so.0.0.debug comp-rump-debug debug,rump,zfs ./usr/libdata/debug/usr/lib/librumpkern_sysproxy.so.0.0.debug comp-rump-debug debug,rump diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi index f85d005bd076..d06acb337652 100644 --- a/distrib/sets/lists/tests/mi +++ b/distrib/sets/lists/tests/mi @@ -2235,6 +2235,7 @@ ./usr/tests/kernel/t_pty tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_rnd tests-kernel-tests atf,rump ./usr/tests/kernel/t_sigaction tests-obsolete obsolete +./usr/tests/kernel/t_simplehook tests-kernel-tests atf,rump ./usr/tests/kernel/t_subr_prf tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_sysctl tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_sysv tests-kernel-tests compattestfile,atf diff --git a/sys/dev/pci/ixgbe/ixgbe.c b/sys/dev/pci/ixgbe/ixgbe.c index bb823f05eebb..18dbc1796331 100644 --- a/sys/dev/pci/ixgbe/ixgbe.c +++ b/sys/dev/pci/ixgbe/ixgbe.c @@ -76,7 +76,6 @@ __KERNEL_RCSID(0, "$NetBSD: ixgbe.c,v 1.284 2021/06/16 00:21:18 riastradh Exp $" #include "ixgbe.h" #include "ixgbe_phy.h" #include "ixgbe_sriov.h" -#include "vlan.h" #include #include @@ -3631,17 +3630,13 @@ ixgbe_detach(device_t dev, int flags) return (EBUSY); } -#if NVLAN > 0 - /* Make sure VLANs are not using driver */ - if (!VLAN_ATTACHED(&adapter->osdep.ec)) - ; /* nothing to do: no VLANs */ - else if ((flags & (DETACH_SHUTDOWN | DETACH_FORCE)) != 0) - vlan_ifdetach(adapter->ifp); - else { + if (VLAN_ATTACHED(&adapter->osdep.ec) && + (flags & (DETACH_SHUTDOWN | DETACH_FORCE)) == 0) { aprint_error_dev(dev, "VLANs in use, detach first\n"); return (EBUSY); } -#endif + + ether_ifdetach(adapter->ifp); adapter->osdep.detaching = true; /* @@ -3662,8 +3657,6 @@ ixgbe_detach(device_t dev, int flags) pmf_device_deregister(dev); - ether_ifdetach(adapter->ifp); - ixgbe_free_deferred_handlers(adapter); /* let hardware know driver is unloading */ diff --git a/sys/dev/pci/ixgbe/ixv.c b/sys/dev/pci/ixgbe/ixv.c index 4a6b42df1b71..9f8545d96d0f 100644 --- a/sys/dev/pci/ixgbe/ixv.c +++ b/sys/dev/pci/ixgbe/ixv.c @@ -45,7 +45,6 @@ __KERNEL_RCSID(0, "$NetBSD: ixv.c,v 1.162 2021/06/16 00:21:18 riastradh Exp $"); #endif #include "ixgbe.h" -#include "vlan.h" /************************************************************************ * Driver version @@ -600,17 +599,11 @@ ixv_detach(device_t dev, int flags) /* Stop the interface. Callouts are stopped in it. */ ixv_ifstop(adapter->ifp, 1); -#if NVLAN > 0 - /* Make sure VLANs are not using driver */ - if (!VLAN_ATTACHED(&adapter->osdep.ec)) - ; /* nothing to do: no VLANs */ - else if ((flags & (DETACH_SHUTDOWN | DETACH_FORCE)) != 0) - vlan_ifdetach(adapter->ifp); - else { + if (VLAN_ATTACHED(&adapter->osdep.ec) && + (flags & (DETACH_SHUTDOWN | DETACH_FORCE)) == 0) { aprint_error_dev(dev, "VLANs in use, detach first\n"); return EBUSY; } -#endif ether_ifdetach(adapter->ifp); callout_halt(&adapter->timer, NULL); diff --git a/sys/kern/kern_hook.c b/sys/kern/kern_hook.c index 43f3014d48ad..524c9ecf75fa 100644 --- a/sys/kern/kern_hook.c +++ b/sys/kern/kern_hook.c @@ -38,6 +38,9 @@ __KERNEL_RCSID(0, "$NetBSD: kern_hook.c,v 1.8 2019/10/16 18:29:49 christos Exp $ #include #include #include +#include +#include +#include /* * A generic linear hook. @@ -49,6 +52,23 @@ struct hook_desc { }; typedef LIST_HEAD(, hook_desc) hook_list_t; +enum hook_list_st { + HKLIST_IDLE, + HKLIST_INUSE, +}; + +struct khook_list { + hook_list_t hl_list; + kmutex_t hl_lock; + kmutex_t *hl_cvlock; + struct lwp *hl_lwp; + kcondvar_t hl_cv; + enum hook_list_st + hl_state; + khook_t *hl_active_hk; + char hl_namebuf[HOOKNAMSIZ]; +}; + int powerhook_debug = 0; static void * @@ -398,3 +418,186 @@ dopowerhooks(int why) if (powerhook_debug) printf("dopowerhooks: %s done\n", why_name); } + +/* + * A simple linear hook. + */ + +khook_list_t * +simplehook_create(int ipl, const char *wmsg) +{ + khook_list_t *l; + + l = kmem_zalloc(sizeof(*l), KM_SLEEP); + + mutex_init(&l->hl_lock, MUTEX_DEFAULT, ipl); + strlcpy(l->hl_namebuf, wmsg, sizeof(l->hl_namebuf)); + cv_init(&l->hl_cv, l->hl_namebuf); + LIST_INIT(&l->hl_list); + l->hl_state = HKLIST_IDLE; + + return l; +} + +void +simplehook_destroy(khook_list_t *l) +{ + struct hook_desc *hd; + + KASSERT(l->hl_state == HKLIST_IDLE); + + while ((hd = LIST_FIRST(&l->hl_list)) != NULL) { + LIST_REMOVE(hd, hk_list); + kmem_free(hd, sizeof(*hd)); + } + + cv_destroy(&l->hl_cv); + mutex_destroy(&l->hl_lock); + kmem_free(l, sizeof(*l)); +} + +int +simplehook_dohooks(khook_list_t *l) +{ + struct hook_desc *hd, *nexthd; + kmutex_t *cv_lock; + void (*fn)(void *); + void *arg; + + mutex_enter(&l->hl_lock); + if (l->hl_state != HKLIST_IDLE) { + mutex_exit(&l->hl_lock); + return EBUSY; + } + + /* stop removing hooks */ + l->hl_state = HKLIST_INUSE; + l->hl_lwp = curlwp; + + LIST_FOREACH(hd, &l->hl_list, hk_list) { + if (hd->hk_fn == NULL) + continue; + + fn = hd->hk_fn; + arg = hd->hk_arg; + l->hl_active_hk = hd; + l->hl_cvlock = NULL; + + mutex_exit(&l->hl_lock); + + /* do callback without l->hl_lock */ + (*fn)(arg); + + mutex_enter(&l->hl_lock); + l->hl_active_hk = NULL; + cv_lock = l->hl_cvlock; + + if (hd->hk_fn == NULL) { + if (cv_lock != NULL) { + mutex_exit(&l->hl_lock); + mutex_enter(cv_lock); + } + + cv_broadcast(&l->hl_cv); + + if (cv_lock != NULL) { + mutex_exit(cv_lock); + mutex_enter(&l->hl_lock); + } + } + } + + /* remove marked node while running hooks */ + LIST_FOREACH_SAFE(hd, &l->hl_list, hk_list, nexthd) { + if (hd->hk_fn == NULL) { + LIST_REMOVE(hd, hk_list); + kmem_free(hd, sizeof(*hd)); + } + } + + l->hl_lwp = NULL; + l->hl_state = HKLIST_IDLE; + mutex_exit(&l->hl_lock); + + return 0; +} + +khook_t * +simplehook_establish(khook_list_t *l, void (*fn)(void *), void *arg) +{ + struct hook_desc *hd; + + hd = kmem_zalloc(sizeof(*hd), KM_SLEEP); + hd->hk_fn = fn; + hd->hk_arg = arg; + + mutex_enter(&l->hl_lock); + LIST_INSERT_HEAD(&l->hl_list, hd, hk_list); + mutex_exit(&l->hl_lock); + + return hd; +} + +void +simplehook_disestablish(khook_list_t *l, khook_t *hd, kmutex_t *lock) +{ + struct hook_desc *hd0 __diagused; + kmutex_t *cv_lock; + + KASSERT(lock == NULL || mutex_owned(lock)); + mutex_enter(&l->hl_lock); + +#ifdef DIAGNOSTIC + LIST_FOREACH(hd0, &l->hl_list, hk_list) { + if (hd == hd0) + break; + } + + if (hd0 == NULL) + panic("hook_disestablish: hook %p not established", hd); +#endif + + /* The hook is not referred, remove immidiately */ + if (l->hl_state == HKLIST_IDLE) { + LIST_REMOVE(hd, hk_list); + kmem_free(hd, sizeof(*hd)); + mutex_exit(&l->hl_lock); + return; + } + + /* remove callback. hd will be removed in dohooks */ + hd->hk_fn = NULL; + hd->hk_arg = NULL; + + /* If the hook is running, wait for the completion */ + if (l->hl_active_hk == hd && + l->hl_lwp != curlwp) { + if (lock != NULL) { + cv_lock = lock; + KASSERT(l->hl_cvlock == NULL); + l->hl_cvlock = lock; + mutex_exit(&l->hl_lock); + } else { + cv_lock = &l->hl_lock; + } + + cv_wait(&l->hl_cv, cv_lock); + + if (lock == NULL) + mutex_exit(&l->hl_lock); + } else { + mutex_exit(&l->hl_lock); + } +} + +bool +simplehook_has_hooks(khook_list_t *l) +{ + bool empty; + + mutex_enter(&l->hl_lock); + empty = LIST_EMPTY(&l->hl_list); + mutex_exit(&l->hl_lock); + + return !empty; +} diff --git a/sys/net/if.c b/sys/net/if.c index dced5088f0ea..61add36d0e88 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -122,6 +122,7 @@ __KERNEL_RCSID(0, "$NetBSD: if.c,v 1.485 2021/05/17 04:07:43 yamaguchi Exp $"); #include #include #include +#include #include #include @@ -160,11 +161,6 @@ __KERNEL_RCSID(0, "$NetBSD: if.c,v 1.485 2021/05/17 04:07:43 yamaguchi Exp $"); #include #endif -#include "lagg.h" -#if NLAGG > 0 -#include -#endif - #include MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); @@ -631,8 +627,13 @@ static void if_getindex(ifnet_t *ifp) { bool hitlimit = false; + char xnamebuf[HOOKNAMSIZ]; ifp->if_index_gen = index_gen++; + snprintf(xnamebuf, sizeof(xnamebuf), + "%s-lshk", ifp->if_xname); + ifp->if_linkstate_hooks = simplehook_create(IPL_NET, + xnamebuf); ifp->if_index = if_index; if (ifindex2ifnet == NULL) { @@ -1217,8 +1218,6 @@ if_deactivate(struct ifnet *ifp) ifp->if_slowtimo = if_nullslowtimo; ifp->if_drain = if_nulldrain; - ifp->if_link_state_changed = NULL; - /* No more packets may be enqueued. */ ifp->if_snd.ifq_maxlen = 0; @@ -1552,6 +1551,8 @@ restart: ifp->if_ioctl_lock = NULL; mutex_obj_free(ifp->if_snd.ifq_lock); if_stats_fini(ifp); + KASSERT(!simplehook_has_hooks(ifp->if_linkstate_hooks)); + simplehook_destroy(ifp->if_linkstate_hooks); splx(s); @@ -2413,23 +2414,7 @@ if_link_state_change_process(struct ifnet *ifp, int link_state) /* Notify that the link state has changed. */ rt_ifmsg(ifp); -#if NCARP > 0 - if (ifp->if_carp) - carp_carpdev_state(ifp); -#endif - - if (ifp->if_link_state_changed != NULL) - ifp->if_link_state_changed(ifp, link_state); - -#if NBRIDGE > 0 - if (ifp->if_bridge != NULL) - bridge_calc_link_state(ifp->if_bridge); -#endif - -#if NLAGG > 0 - if (ifp->if_lagg != NULL) - lagg_linkstate_changed(ifp); -#endif + simplehook_dohooks(ifp->if_linkstate_hooks); DOMAIN_FOREACH(dp) { if (dp->dom_if_link_state_change != NULL) @@ -2478,6 +2463,23 @@ out: KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); } +void * +if_linkstate_change_establish(struct ifnet *ifp, void (*fn)(void *), void *arg) +{ + khook_t *hk; + + hk = simplehook_establish(ifp->if_linkstate_hooks, fn, arg); + + return (void *)hk; +} + +void +if_linkstate_change_disestablish(struct ifnet *ifp, void *vhook, kmutex_t *lock) +{ + + simplehook_disestablish(ifp->if_linkstate_hooks, vhook, lock); +} + /* * Used to mark addresses on an interface as DETATCHED or TENTATIVE * and thus start Duplicate Address Detection without changing the diff --git a/sys/net/if.h b/sys/net/if.h index 6bcc790068bc..eadb9891254b 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -85,6 +85,7 @@ #include #include #include +#include #include #include @@ -420,7 +421,6 @@ typedef struct ifnet { uint16_t if_link_queue; /* q: masked link state change queue */ /* q: is link state work scheduled? */ bool if_link_scheduled; - void (*if_link_state_changed)(struct ifnet *, int); struct pslist_entry if_pslist_entry;/* i: */ struct psref_target @@ -433,6 +433,7 @@ typedef struct ifnet { /* XXX should be protocol independent */ LIST_HEAD(, in6_multi) if_multiaddrs; /* 6: */ + khook_list_t *if_linkstate_hooks; /* :: */ #endif } ifnet_t; @@ -1244,6 +1245,11 @@ void loopinit(void); int looutput(struct ifnet *, struct mbuf *, const struct sockaddr *, const struct rtentry *); +void * if_linkstate_change_establish(struct ifnet *, + void (*)(void *), void *); +void if_linkstate_change_disestablish(struct ifnet *, + void *, kmutex_t *); + /* * These are exported because they're an easy way to tell if * an interface is going away without having to burn a flag. diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 8d639221dae3..e547915b747e 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -243,6 +243,7 @@ static int bridge_ioctl(struct ifnet *, u_long, void *); static int bridge_init(struct ifnet *); static void bridge_stop(struct ifnet *, int); static void bridge_start(struct ifnet *); +static void bridge_ifdetach(void *); static void bridge_input(struct ifnet *, struct mbuf *); static void bridge_forward(struct bridge_softc *, struct mbuf *); @@ -738,6 +739,12 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif) PSLIST_WRITER_REMOVE(bif, bif_next); BRIDGE_PSZ_PERFORM(sc); + + if_linkstate_change_disestablish(ifs, + bif->bif_linkstate_hook, BRIDGE_LOCK_OBJ(sc)); + ether_ifdetachhook_disestablish(ifs, + bif->bif_ifdetach_hook, BRIDGE_LOCK_OBJ(sc)); + BRIDGE_UNLOCK(sc); switch (ifs->if_type) { @@ -796,9 +803,10 @@ bridge_calc_csum_flags(struct bridge_softc *sc) * * Calculate the link state based on each member interface. */ -void -bridge_calc_link_state(struct bridge_softc *sc) +static void +bridge_calc_link_state(void *xsc) { + struct bridge_softc *sc = xsc; struct bridge_iflist *bif; struct ifnet *ifs; int link_state = LINK_STATE_DOWN; @@ -889,6 +897,8 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY; bif->bif_path_cost = BSTP_DEFAULT_PATH_COST; + bif->bif_linkstate_hook = if_linkstate_change_establish(ifs, + bridge_calc_link_state, sc); PSLIST_ENTRY_INIT(bif, bif_next); psref_target_init(&bif->bif_psref, bridge_psref_class); @@ -901,6 +911,9 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) BRIDGE_UNLOCK(sc); + bif->bif_ifdetach_hook = ether_ifdetachhook_establish(ifs, + bridge_ifdetach, (void *)ifs); + bridge_calc_csum_flags(sc); bridge_calc_link_state(sc); @@ -1393,17 +1406,21 @@ bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) * Detach an interface from a bridge. Called when a member * interface is detaching. */ -void -bridge_ifdetach(struct ifnet *ifp) +static void +bridge_ifdetach(void *xifs) { - struct bridge_softc *sc = ifp->if_bridge; + struct ifnet *ifs; + struct bridge_softc *sc; struct ifbreq breq; + ifs = (struct ifnet *)xifs; + sc = ifs->if_bridge; + /* ioctl_lock should prevent this from happening */ KASSERT(sc != NULL); memset(&breq, 0, sizeof(breq)); - strlcpy(breq.ifbr_ifsname, ifp->if_xname, sizeof(breq.ifbr_ifsname)); + strlcpy(breq.ifbr_ifsname, ifs->if_xname, sizeof(breq.ifbr_ifsname)); (void) bridge_ioctl_del(sc, &breq); } diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index 58a746e30d16..1410658f808b 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -269,6 +269,8 @@ struct bridge_iflist { struct ifnet *bif_ifp; /* member if */ uint32_t bif_flags; /* member if flags */ struct psref_target bif_psref; + void * *bif_linkstate_hook; + void * *bif_ifdetach_hook; }; /* @@ -333,8 +335,6 @@ struct bridge_softc { extern const uint8_t bstp_etheraddr[]; -void bridge_ifdetach(struct ifnet *); - int bridge_output(struct ifnet *, struct mbuf *, const struct sockaddr *, const struct rtentry *); @@ -346,11 +346,11 @@ void bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *, int); void bridge_calc_csum_flags(struct bridge_softc *); -void bridge_calc_link_state(struct bridge_softc *); -#define BRIDGE_LOCK(_sc) mutex_enter(&(_sc)->sc_iflist_psref.bip_lock) -#define BRIDGE_UNLOCK(_sc) mutex_exit(&(_sc)->sc_iflist_psref.bip_lock) -#define BRIDGE_LOCKED(_sc) mutex_owned(&(_sc)->sc_iflist_psref.bip_lock) +#define BRIDGE_LOCK_OBJ(_sc) (&(_sc)->sc_iflist_psref.bip_lock) +#define BRIDGE_LOCK(_sc) mutex_enter(BRIDGE_LOCK_OBJ(_sc)) +#define BRIDGE_UNLOCK(_sc) mutex_exit(BRIDGE_LOCK_OBJ(_sc)) +#define BRIDGE_LOCKED(_sc) mutex_owned(BRIDGE_LOCK_OBJ(_sc)) #define BRIDGE_PSZ_RENTER(__s) do { __s = pserialize_read_enter(); } while (0) #define BRIDGE_PSZ_REXIT(__s) do { pserialize_read_exit(__s); } while (0) diff --git a/sys/net/if_ether.h b/sys/net/if_ether.h index d194ae6fff0e..d2f4aa185350 100644 --- a/sys/net/if_ether.h +++ b/sys/net/if_ether.h @@ -198,6 +198,8 @@ struct ethercom { * being added or removed. */ ether_vlancb_t ec_vlan_cb; + /* Hooks called at the beginning of detach of this interface */ + khook_list_t *ec_ifdetach_hooks; kmutex_t *ec_lock; /* Flags used only by the kernel */ int ec_flags; @@ -385,6 +387,10 @@ void ether_ifattach(struct ifnet *, const uint8_t *); void ether_ifdetach(struct ifnet *); int ether_mediachange(struct ifnet *); void ether_mediastatus(struct ifnet *, struct ifmediareq *); +void * ether_ifdetachhook_establish(struct ifnet *, + void (*)(void *), void *arg); +void ether_ifdetachhook_disestablish(struct ifnet *, + void *, kmutex_t *); char *ether_sprintf(const uint8_t *); char *ether_snprintf(char *, size_t, const uint8_t *); diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index fd06386c8151..404e822fa34a 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -78,7 +78,6 @@ __KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.293 2021/05/17 04:07:43 yamaguchi #include "bridge.h" #include "arp.h" #include "agr.h" -#include "lagg.h" #include #include @@ -90,6 +89,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.293 2021/05/17 04:07:43 yamaguchi #include #include #include +#include #include #include @@ -997,17 +997,6 @@ ether_snprintf(char *buf, size_t len, const u_char *ap) return buf; } -static void -ether_link_state_changed(struct ifnet *ifp, int link_state) -{ -#if NVLAN > 0 - struct ethercom *ec = (void *)ifp; - - if (ec->ec_nvlans) - vlan_link_state_changed(ifp, link_state); -#endif -} - /* * Perform common duties while attaching to interface list */ @@ -1015,6 +1004,7 @@ void ether_ifattach(struct ifnet *ifp, const uint8_t *lla) { struct ethercom *ec = (struct ethercom *)ifp; + char xnamebuf[HOOKNAMSIZ]; ifp->if_type = IFT_ETHER; ifp->if_hdrlen = ETHER_HDR_LEN; @@ -1022,7 +1012,6 @@ ether_ifattach(struct ifnet *ifp, const uint8_t *lla) ifp->if_mtu = ETHERMTU; ifp->if_output = ether_output; ifp->_if_input = ether_input; - ifp->if_link_state_changed = ether_link_state_changed; if (ifp->if_baudrate == 0) ifp->if_baudrate = IF_Mbps(10); /* just a default */ @@ -1035,6 +1024,9 @@ ether_ifattach(struct ifnet *ifp, const uint8_t *lla) ec->ec_flags = 0; ifp->if_broadcastaddr = etherbroadcastaddr; bpf_attach(ifp, DLT_EN10MB, sizeof(struct ether_header)); + snprintf(xnamebuf, sizeof(xnamebuf), + "%s-ether_ifdetachhooks", ifp->if_xname); + ec->ec_ifdetach_hooks = simplehook_create(IPL_NET, xnamebuf); #ifdef MBUFTRACE mowner_init_owner(&ec->ec_tx_mowner, ifp->if_xname, "tx"); mowner_init_owner(&ec->ec_rx_mowner, ifp->if_xname, "rx"); @@ -1061,20 +1053,11 @@ ether_ifdetach(struct ifnet *ifp) ifp->if_ioctl = __FPTRCAST(int (*)(struct ifnet *, u_long, void *), enxio); -#if NBRIDGE > 0 - if (ifp->if_bridge) - bridge_ifdetach(ifp); -#endif - bpf_detach(ifp); -#if NVLAN > 0 - if (ec->ec_nvlans) - vlan_ifdetach(ifp); -#endif + simplehook_dohooks(ec->ec_ifdetach_hooks); + KASSERT(!simplehook_has_hooks(ec->ec_ifdetach_hooks)); + simplehook_destroy(ec->ec_ifdetach_hooks); -#if NLAGG > 0 - if (ifp->if_lagg) - lagg_ifdetach(ifp); -#endif + bpf_detach(ifp); ETHER_LOCK(ec); KASSERT(ec->ec_nvlans == 0); @@ -1093,6 +1076,30 @@ ether_ifdetach(struct ifnet *ifp) MOWNER_DETACH(&ec->ec_tx_mowner); } +void * +ether_ifdetachhook_establish(struct ifnet *ifp, + void (*fn)(void *), void *arg) +{ + struct ethercom *ec; + khook_t *hk; + + ec = (struct ethercom *)ifp; + hk = simplehook_establish(ec->ec_ifdetach_hooks, + fn, arg); + + return (void *)hk; +} + +void +ether_ifdetachhook_disestablish(struct ifnet *ifp, + void *vhook, kmutex_t *lock) +{ + struct ethercom *ec; + + ec = (struct ethercom *)ifp; + simplehook_disestablish(ec->ec_ifdetach_hooks, vhook, lock); +} + #if 0 /* * This is for reference. We have a table-driven version diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index 33eabe4a6f45..0efa3b042611 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -156,11 +156,14 @@ struct ifvlan { */ kmutex_t ifv_lock; /* writer lock for ifv_mib */ pserialize_t ifv_psz; + void *ifv_linkstate_hook; + void *ifv_ifdetach_hook; LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead; LIST_ENTRY(ifvlan) ifv_list; struct pslist_entry ifv_hash; int ifv_flags; + bool ifv_stopping; }; #define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ @@ -195,6 +198,8 @@ static int vlan_config(struct ifvlan *, struct ifnet *, uint16_t); static int vlan_ioctl(struct ifnet *, u_long, void *); static void vlan_start(struct ifnet *); static int vlan_transmit(struct ifnet *, struct mbuf *); +static void vlan_link_state_changed(void *); +static void vlan_ifdetach(void *); static void vlan_unconfig(struct ifnet *); static int vlan_unconfig_locked(struct ifvlan *, struct ifvlan_linkmib *); static void vlan_hash_init(void); @@ -548,10 +553,14 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag) nmib_psref = NULL; omib_cleanup = true; + ifv->ifv_ifdetach_hook = ether_ifdetachhook_establish(p, + vlan_ifdetach, ifp); /* * We inherit the parents link state. */ + ifv->ifv_linkstate_hook = if_linkstate_change_establish(p, + vlan_link_state_changed, ifv); if_link_state_change(&ifv->ifv_if, p->if_link_state); done: @@ -600,6 +609,13 @@ vlan_unconfig_locked(struct ifvlan *ifv, struct ifvlan_linkmib *nmib) KASSERT(IFNET_LOCKED(ifp)); KASSERT(mutex_owned(&ifv->ifv_lock)); + if (ifv->ifv_stopping) { + error = -1; + goto done; + } + ifv->ifv_stopping = true; + + ifv->ifv_stopping = true; ifp->if_flags &= ~(IFF_UP | IFF_RUNNING); omib = ifv->ifv_mib; @@ -683,10 +699,18 @@ vlan_unconfig_locked(struct ifvlan *ifv, struct ifvlan_linkmib *nmib) pserialize_perform(vlan_psz); mutex_exit(&ifv_hash.lock); PSLIST_ENTRY_DESTROY(ifv, ifv_hash); + if_linkstate_change_disestablish(p, + ifv->ifv_linkstate_hook, NULL); vlan_linkmib_update(ifv, nmib); + ifv->ifv_stopping = false; + /*XXX Must not disestablish ifv->ifv_ifdetach_hook with IFNET_LOCK */ + IFNET_UNLOCK(ifp); + ether_ifdetachhook_disestablish(p, ifv->ifv_ifdetach_hook, + &ifv->ifv_lock); mutex_exit(&ifv->ifv_lock); + IFNET_LOCK(ifp); nmib_psref = NULL; kmem_free(omib, sizeof(*omib)); @@ -705,6 +729,7 @@ vlan_unconfig_locked(struct ifvlan *ifv, struct ifvlan_linkmib *nmib) ifp->if_capabilities = 0; mutex_enter(&ifv->ifv_lock); done: + ifv->ifv_stopping = false; if (nmib_psref) psref_target_destroy(nmib_psref, ifvm_psref_class); @@ -830,84 +855,17 @@ vlan_linkmib_update(struct ifvlan *ifv, struct ifvlan_linkmib *nmib) * Called when a parent interface is detaching; destroy any VLAN * configuration for the parent interface. */ -void -vlan_ifdetach(struct ifnet *p) +static void +vlan_ifdetach(void *xifp) { - struct ifvlan *ifv; - struct ifvlan_linkmib *mib, **nmibs; - struct psref psref; - int error; - int bound; - int i, cnt = 0; - - bound = curlwp_bind(); - - mutex_enter(&ifv_list.lock); - LIST_FOREACH(ifv, &ifv_list.list, ifv_list) { - mib = vlan_getref_linkmib(ifv, &psref); - if (mib == NULL) - continue; - - if (mib->ifvm_p == p) - cnt++; - - vlan_putref_linkmib(mib, &psref); - } - mutex_exit(&ifv_list.lock); - - if (cnt == 0) { - curlwp_bindx(bound); - return; - } - - /* - * The value of "cnt" does not increase while ifv_list.lock - * and ifv->ifv_lock are released here, because the parent - * interface is detaching. - */ - nmibs = kmem_alloc(sizeof(*nmibs) * cnt, KM_SLEEP); - for (i = 0; i < cnt; i++) { - nmibs[i] = kmem_alloc(sizeof(*nmibs[i]), KM_SLEEP); - } - - mutex_enter(&ifv_list.lock); - - i = 0; - LIST_FOREACH(ifv, &ifv_list.list, ifv_list) { - struct ifnet *ifp = &ifv->ifv_if; - - /* IFNET_LOCK must be held before ifv_lock. */ - IFNET_LOCK(ifp); - mutex_enter(&ifv->ifv_lock); - - /* XXX ifv_mib = NULL? */ - if (ifv->ifv_mib->ifvm_p == p) { - KASSERTMSG(i < cnt, - "no memory for unconfig, parent=%s", p->if_xname); - error = vlan_unconfig_locked(ifv, nmibs[i]); - if (!error) { - nmibs[i] = NULL; - i++; - } - - } - - mutex_exit(&ifv->ifv_lock); - IFNET_UNLOCK(ifp); - } - - mutex_exit(&ifv_list.lock); - - curlwp_bindx(bound); - - for (i = 0; i < cnt; i++) { - if (nmibs[i]) - kmem_free(nmibs[i], sizeof(*nmibs[i])); - } + struct ifnet *ifp; - kmem_free(nmibs, sizeof(*nmibs) * cnt); + ifp = (struct ifnet *)xifp; - return; + /* IFNET_LOCK must be held before ifv_lock. */ + IFNET_LOCK(ifp); + vlan_unconfig(ifp); + IFNET_UNLOCK(ifp); } static int @@ -1663,30 +1621,28 @@ out: /* * If the parent link state changed, the vlan link state should change also. */ -void -vlan_link_state_changed(struct ifnet *p, int link_state) +static void +vlan_link_state_changed(void *xifv) { - struct ifvlan *ifv; + struct ifvlan *ifv = xifv; + struct ifnet *ifp, *p; struct ifvlan_linkmib *mib; struct psref psref; - struct ifnet *ifp; - - mutex_enter(&ifv_list.lock); - LIST_FOREACH(ifv, &ifv_list.list, ifv_list) { - mib = vlan_getref_linkmib(ifv, &psref); - if (mib == NULL) - continue; - - if (mib->ifvm_p == p) { - ifp = &mib->ifvm_ifvlan->ifv_if; - if_link_state_change(ifp, link_state); - } + mib = vlan_getref_linkmib(ifv, &psref); + if (mib == NULL) + return; + if (mib->ifvm_p == NULL) { vlan_putref_linkmib(mib, &psref); + return; } - mutex_exit(&ifv_list.lock); + ifp = &ifv->ifv_if; + p = mib->ifvm_p; + if_link_state_change(ifp, p->if_link_state); + + vlan_putref_linkmib(mib, &psref); } /* diff --git a/sys/net/if_vlanvar.h b/sys/net/if_vlanvar.h index f7e611eda35a..4f9b6c0796ad 100644 --- a/sys/net/if_vlanvar.h +++ b/sys/net/if_vlanvar.h @@ -82,8 +82,6 @@ struct vlanreq { #ifdef _KERNEL void vlan_input(struct ifnet *, struct mbuf *); -void vlan_ifdetach(struct ifnet *); -void vlan_link_state_changed(struct ifnet *, int); /* * Locking notes: diff --git a/sys/net/lagg/if_lagg.c b/sys/net/lagg/if_lagg.c index 2e3073e96f34..62a09db1ea0f 100644 --- a/sys/net/lagg/if_lagg.c +++ b/sys/net/lagg/if_lagg.c @@ -147,6 +147,8 @@ static void lagg_start(struct ifnet *); static int lagg_media_change(struct ifnet *); static void lagg_media_status(struct ifnet *, struct ifmediareq *); static int lagg_vlan_cb(struct ethercom *, uint16_t, bool); +static void lagg_linkstate_changed(void *); +static void lagg_ifdetach(void *); static struct lagg_softc * lagg_softc_alloc(enum lagg_iftypes); static void lagg_softc_free(struct lagg_softc *); @@ -187,6 +189,8 @@ static void lagg_port_syncmulti(struct lagg_softc *, struct lagg_port *); static void lagg_port_purgemulti(struct lagg_softc *, struct lagg_port *); static void lagg_port_syncvlan(struct lagg_softc *, struct lagg_port *); static void lagg_port_purgevlan(struct lagg_softc *, struct lagg_port *); +static void lagg_lladdr_update_work(struct lagg_work *, void *); +static void lagg_lladdr_update_ports(struct lagg_softc *); static struct if_clone lagg_cloner = IF_CLONE_INITIALIZER("lagg", lagg_clone_create, lagg_clone_destroy); @@ -246,6 +250,17 @@ lagg_evcnt_attach(struct lagg_softc *sc, sc->sc_evgroup, name); } +static inline bool +lagg_lladdr_is_default(struct lagg_softc *sc) +{ + + if (memcmp(sc->sc_lladdr, sc->sc_lladdr_default, + sizeof(sc->sc_lladdr)) == 0) + return true; + + return false; +} + static inline void lagg_in6_ifattach(struct ifnet *ifp) { @@ -344,6 +359,7 @@ lagg_clone_create(struct if_clone *ifc, int unit) { struct lagg_softc *sc; struct ifnet *ifp; + char xnamebuf[MAXCOMLEN]; int error; sc = lagg_softc_alloc(lagg_iftype); @@ -371,6 +387,17 @@ lagg_clone_create(struct if_clone *ifc, int unit) ifp->if_start = lagg_start; IFQ_SET_READY(&ifq->if_send); + lagg_work_set(&sc->sc_wk_lladdr, + lagg_lladdr_update_work, sc); + snprintf(xnamebuf, sizeof(xnamebuf), + "%s_wq", ifp->if_xname); + sc->sc_wq = lagg_workq_create(xnamebuf, + PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE); + if (sc->sc_wq == NULL) { + error = ENOMEM; + goto destroy_lock; + } + error = lagg_setup_sysctls(sc); if (error != 0) goto destroy_psz; @@ -385,7 +412,10 @@ lagg_clone_create(struct if_clone *ifc, int unit) switch (lagg_iftype) { case LAGG_IF_TYPE_ETHERNET: - cprng_fast(sc->sc_lladdr, sizeof(sc->sc_lladdr)); + cprng_fast(sc->sc_lladdr_default, + sizeof(sc->sc_lladdr_default)); + memcpy(sc->sc_lladdr, sc->sc_lladdr_default, + sizeof(sc->sc_lladdr)); ether_set_vlan_cb((struct ethercom *)ifp, lagg_vlan_cb); ether_ifattach(ifp, sc->sc_lladdr); break; @@ -406,6 +436,7 @@ lagg_clone_create(struct if_clone *ifc, int unit) destroy_psz: pserialize_destroy(sc->sc_psz); +destroy_lock: mutex_destroy(&sc->sc_lock); lagg_softc_free(sc); @@ -423,6 +454,8 @@ lagg_clone_destroy(struct ifnet *ifp) lagg_delport_all(sc); IFNET_UNLOCK(ifp); + lagg_workq_wait(sc->sc_wq, &sc->sc_wk_lladdr); + switch (ifp->if_type) { case IFT_ETHER: ether_ifdetach(ifp); @@ -437,6 +470,7 @@ lagg_clone_destroy(struct ifnet *ifp) lagg_teardown_sysctls(sc); pserialize_destroy(sc->sc_psz); + lagg_workq_destroy(sc->sc_wq); mutex_destroy(&sc->sc_lock); lagg_softc_free(sc); @@ -1594,65 +1628,133 @@ lagg_pr_detach(struct lagg_softc *sc) } static void -lagg_lladdr_set(struct lagg_softc *sc, struct lagg_port *lp, u_long if_type) +lagg_lladdr_set(struct lagg_softc *sc, struct lagg_port *lp) { - struct ifnet *ifp, *ifp_port; - const uint8_t *lladdr; + struct ifnet *ifp_port; + bool do_setsadl; - KASSERT(IFNET_LOCKED(&sc->sc_if)); KASSERT(LAGG_LOCKED(sc)); - ifp = &sc->sc_if; + switch (lp->lp_iftype) { + case IFT_ETHER: + break; + default: + return; + } + ifp_port = lp->lp_ifp; + do_setsadl = false; - if (ifp->if_type != IFT_ETHER) + IFNET_LOCK(ifp_port); + memcpy(lp->lp_lladdr, CLLADDR(ifp_port->if_sadl), ETHER_ADDR_LEN); + if (ifp_port->if_type != lp->lp_iftype) + do_setsadl = true; + IFNET_UNLOCK(ifp_port); + + if (lagg_lladdr_is_default(sc)) { + memcpy(sc->sc_lladdr, lp->lp_lladdr, ETHER_ADDR_LEN); + lagg_workq_add(sc->sc_wq, &sc->sc_wk_lladdr); + } else { + do_setsadl = true; + } + + if (!do_setsadl) return; + IFNET_LOCK(ifp_port); + if_set_sadl(ifp_port, sc->sc_lladdr, ETHER_ADDR_LEN, false); + IFNET_UNLOCK(ifp_port); +} + +static void +lagg_lladdr_unset(struct lagg_softc *sc, struct lagg_port *lp, u_int if_type) +{ + struct ifnet *ifp_port; + struct lagg_port *lp0; + bool do_setsadl; + uint8_t *lladdr; + + KASSERT(LAGG_LOCKED(sc)); + switch (lp->lp_iftype) { case IFT_ETHER: - memcpy(lp->lp_lladdr, CLLADDR(ifp_port->if_sadl), - ETHER_ADDR_LEN); - - if (SIMPLEQ_EMPTY(&sc->sc_ports)) { - if_set_sadl(ifp, CLLADDR(ifp_port->if_sadl), - ETHER_ADDR_LEN, 0); - LAGG_UNLOCK(sc); - /* apply new IPv6LLA */ - lagg_in6_ifdetach(&sc->sc_if); - lagg_in6_ifattach(&sc->sc_if); - LAGG_LOCK(sc); - } + break; + default: + return; + } - lladdr = CLLADDR(ifp->if_sadl); + do_setsadl = true; - if (lp->lp_iftype != if_type || - memcmp(lp->lp_lladdr, lladdr, ETHER_ADDR_LEN) != 0) { - IFNET_LOCK(ifp_port); - if_set_sadl(ifp_port, lladdr, ETHER_ADDR_LEN, false); - IFNET_UNLOCK(ifp_port); + if (memcmp(lp->lp_lladdr, sc->sc_lladdr, + ETHER_ADDR_LEN) == 0) { + if (lp->lp_iftype == if_type) + do_setsadl = false; + + lp0 = SIMPLEQ_FIRST(&sc->sc_ports); + if (lp0 == NULL) { + lladdr = sc->sc_lladdr_default; + } else { + lladdr = lp0->lp_lladdr; } - break; + + memcpy(sc->sc_lladdr, lladdr, ETHER_ADDR_LEN); + lagg_workq_add(sc->sc_wq, &sc->sc_wk_lladdr); + lagg_lladdr_update_ports(sc); } + + if (!do_setsadl) + return; + + ifp_port = lp->lp_ifp; + IFNET_LOCK(ifp_port); + if_set_sadl(ifp_port, lp->lp_lladdr, ETHER_ADDR_LEN, false); + IFNET_UNLOCK(ifp_port); } static void -lagg_lladdr_update(struct lagg_softc *sc) +lagg_lladdr_update_work(struct lagg_work *wk, void *xsc) +{ + struct lagg_softc *sc; + struct ifnet *ifp; + + sc = (struct lagg_softc *)xsc; + ifp = &sc->sc_if; + + IFNET_LOCK(ifp); + LAGG_LOCK(sc); + + if (ifp->if_type != IFT_ETHER) + goto out; + + if (memcmp(sc->sc_lladdr, CLLADDR(ifp->if_sadl), + ETHER_ADDR_LEN) == 0) { + goto out; + } + + if_set_sadl(ifp, sc->sc_lladdr, sizeof(sc->sc_lladdr), false); + + /* Generate new IPv6 link-local address */ + lagg_in6_ifdetach(ifp); + lagg_in6_ifattach(ifp); +out: + LAGG_UNLOCK(sc); + IFNET_UNLOCK(ifp); +} + +static void +lagg_lladdr_update_ports(struct lagg_softc *sc) { struct lagg_port *lp; struct ifnet *ifp_port; - const uint8_t *lladdr; bool stopped; int error; KASSERT(LAGG_LOCKED(sc)); - KASSERT(sc->sc_if.if_type == IFT_ETHER); - - lladdr = CLLADDR(sc->sc_if.if_sadl); LAGG_PORTS_FOREACH(sc, lp) { ifp_port = lp->lp_ifp; - if (memcmp(lladdr, CLLADDR(ifp_port->if_sadl), + if (memcmp(sc->sc_lladdr, CLLADDR(ifp_port->if_sadl), ETHER_ADDR_LEN) == 0) { continue; } @@ -1665,7 +1767,8 @@ lagg_lladdr_update(struct lagg_softc *sc) stopped = false; } - if_set_sadl(ifp_port, lladdr, ETHER_ADDR_LEN, false); + if_set_sadl(ifp_port, sc->sc_lladdr, + ETHER_ADDR_LEN, false); if (stopped) { error = ifp_port->if_init(ifp_port); @@ -1681,54 +1784,6 @@ lagg_lladdr_update(struct lagg_softc *sc) } } -static void -lagg_lladdr_unset(struct lagg_softc *sc, struct lagg_port *lp, u_int if_type) -{ - struct ifnet *ifp, *ifp_port; - struct lagg_port *lp0; - - KASSERT(IFNET_LOCKED(&sc->sc_if)); - KASSERT(LAGG_LOCKED(sc)); - - ifp = &sc->sc_if; - ifp_port = lp->lp_ifp; - - if (ifp->if_type != IFT_ETHER) - return; - - switch (lp->lp_iftype) { - case IFT_ETHER: - if (memcmp(lp->lp_lladdr, CLLADDR(ifp->if_sadl), - ETHER_ADDR_LEN) == 0) { - lp0 = SIMPLEQ_FIRST(&sc->sc_ports); - if (lp0 == NULL) { - if_set_sadl(ifp, sc->sc_lladdr, - ETHER_ADDR_LEN, 0); - } else { - if_set_sadl(ifp, lp0->lp_lladdr, - ETHER_ADDR_LEN, 0); - } - - LAGG_UNLOCK(sc); - lagg_in6_ifdetach(ifp); - lagg_in6_ifattach(ifp); - LAGG_LOCK(sc); - - lagg_lladdr_update(sc); - } - - if (lp->lp_iftype != if_type || - memcmp(lp->lp_lladdr, CLLADDR(ifp_port->if_sadl), - ETHER_ADDR_LEN) != 0) { - IFNET_LOCK(ifp_port); - if_set_sadl(ifp_port, lp->lp_lladdr, - ETHER_ADDR_LEN, false); - IFNET_UNLOCK(ifp_port); - } - break; - } -} - static int lagg_ether_addmulti(struct lagg_softc *sc, struct ifreq *ifr) { @@ -1954,6 +2009,14 @@ lagg_addport_locked(struct lagg_softc *sc, struct lagg_port *lp) } } + lp->lp_softc = sc; + lp->lp_prio = LAGG_PORT_PRIO; + lp->lp_linkstate_hook = if_linkstate_change_establish(ifp_port, + lagg_linkstate_changed, ifp_port); + lp->lp_ifdetach_hook = ether_ifdetachhook_establish(ifp_port, + lagg_ifdetach, ifp_port); + + /* save and change items of ifp_port */ IFNET_LOCK(ifp_port); if (ISSET(ifp_port->if_flags, IFF_RUNNING)) { ifp_port->if_stop(ifp_port, 0); @@ -1961,24 +2024,21 @@ lagg_addport_locked(struct lagg_softc *sc, struct lagg_port *lp) } /* to delete ipv6 link local address */ lagg_in6_ifdetach(ifp_port); - IFNET_UNLOCK(ifp_port); - lp->lp_softc = sc; + lp->lp_mtu = ifp_port->if_mtu; lp->lp_iftype = ifp_port->if_type; lp->lp_ioctl = ifp_port->if_ioctl; lp->lp_output = ifp_port->if_output; lp->lp_ifcapenable = ifp_port->if_capenable; - lp->lp_mtu = mtu_port; - lp->lp_prio = LAGG_PORT_PRIO; - psref_target_init(&lp->lp_psref, lagg_port_psref_class); - IFNET_LOCK(ifp_port); ifp_port->if_type = if_type; ifp_port->if_ioctl = lagg_port_ioctl; ifp_port->if_output = lagg_port_output; IFNET_UNLOCK(ifp_port); - lagg_lladdr_set(sc, lp, if_type); + lagg_lladdr_set(sc, lp); + + psref_target_init(&lp->lp_psref, lagg_port_psref_class); error = lagg_proto_allocport(sc, lp); if (error != 0) @@ -2011,6 +2071,10 @@ remove_port: restore_lladdr: lagg_lladdr_unset(sc, lp, if_type); + ether_ifdetachhook_disestablish(ifp_port, + lp->lp_ifdetach_hook, &sc->sc_lock); + if_linkstate_change_disestablish(ifp_port, + lp->lp_linkstate_hook, NULL); psref_target_destroy(&lp->lp_psref, lagg_port_psref_class); IFNET_LOCK(ifp_port); @@ -2046,15 +2110,19 @@ lagg_addport(struct lagg_softc *sc, struct ifnet *ifp_port) } static void -lagg_delport_locked(struct lagg_softc *sc, struct lagg_port *lp) +lagg_delport_locked(struct lagg_softc *sc, struct lagg_port *lp, + bool is_ifdetach) { struct ifnet *ifp_port; struct ifreq ifr; u_long if_type; - bool stopped, detaching; + bool stopped; KASSERT(LAGG_LOCKED(sc)); + if (lp == NULL) + return; + ifp_port = lp->lp_ifp; stopped = false; @@ -2063,6 +2131,11 @@ lagg_delport_locked(struct lagg_softc *sc, struct lagg_port *lp) atomic_store_release(&ifp_port->if_lagg, NULL); pserialize_perform(sc->sc_psz); + if_linkstate_change_disestablish(ifp_port, + lp->lp_linkstate_hook, NULL); + ether_ifdetachhook_disestablish(ifp_port, + lp->lp_ifdetach_hook, &sc->sc_lock); + lagg_proto_stopport(sc, lp); psref_target_destroy(&lp->lp_psref, lagg_port_psref_class); @@ -2085,7 +2158,6 @@ lagg_delport_locked(struct lagg_softc *sc, struct lagg_port *lp) lagg_port_purgemulti(sc, lp); lagg_port_purgevlan(sc, lp); - detaching = lp->lp_detaching; IFNET_LOCK(ifp_port); if_type = ifp_port->if_type; ifp_port->if_type = lp->lp_iftype; @@ -2103,7 +2175,7 @@ lagg_delport_locked(struct lagg_softc *sc, struct lagg_port *lp) ifp_port->if_init(ifp_port); } - if (!detaching) { + if (!is_ifdetach) { IFNET_LOCK(ifp_port); lagg_in6_ifattach(ifp_port); IFNET_UNLOCK(ifp_port); @@ -2117,7 +2189,7 @@ lagg_delport_all(struct lagg_softc *sc) LAGG_LOCK(sc); LAGG_PORTS_FOREACH_SAFE(sc, lp, lp0) { - lagg_delport_locked(sc, lp); + lagg_delport_locked(sc, lp, false); } LAGG_UNLOCK(sc); } @@ -2134,7 +2206,7 @@ lagg_delport(struct lagg_softc *sc, struct ifnet *ifp_port) return ENOENT; LAGG_LOCK(sc); - lagg_delport_locked(sc, lp); + lagg_delport_locked(sc, lp, false); LAGG_UNLOCK(sc); return 0; @@ -2302,14 +2374,15 @@ lagg_port_output(struct ifnet *ifp, struct mbuf *m, return error; } -void -lagg_ifdetach(struct ifnet *ifp_port) +static void +lagg_ifdetach(void *xifp) { + struct ifnet *ifp_port; struct lagg_port *lp; struct lagg_softc *sc; - struct psref psref; int s; + ifp_port = (struct ifnet *)xifp; IFNET_ASSERT_UNLOCKED(ifp_port); s = pserialize_read_enter(); @@ -2318,28 +2391,28 @@ lagg_ifdetach(struct ifnet *ifp_port) pserialize_read_exit(s); return; } - lagg_port_getref(lp, &psref); - pserialize_read_exit(s); sc = lp->lp_softc; - if (sc != NULL) { - IFNET_LOCK(&sc->sc_if); - LAGG_LOCK(sc); - lagg_port_putref(lp, &psref); - } else { - lagg_port_putref(lp, &psref); + if (sc == NULL) { + pserialize_read_exit(s); return; } + pserialize_read_exit(s); - lp->lp_detaching = true; - lagg_delport_locked(sc, lp); + LAGG_LOCK(sc); + /* + * reload if_lagg because it may write + * after pserialize_read_exit. + */ + lp = ifp_port->if_lagg; + lagg_delport_locked(sc, lp, true); LAGG_UNLOCK(sc); - IFNET_UNLOCK(&sc->sc_if); } void -lagg_linkstate_changed(struct ifnet *ifp) +lagg_linkstate_changed(void *xifp) { + struct ifnet *ifp = xifp; struct lagg_port *lp; struct psref psref; int s, bound; diff --git a/sys/net/lagg/if_laggproto.h b/sys/net/lagg/if_laggproto.h index 5b940a28f621..24b7f15f5d6c 100644 --- a/sys/net/lagg/if_laggproto.h +++ b/sys/net/lagg/if_laggproto.h @@ -68,6 +68,8 @@ struct lagg_port { struct lagg_softc *lp_softc; /* parent lagg */ void *lp_proto_ctx; bool lp_detaching; + void *lp_linkstate_hook; + void *lp_ifdetach_hook; uint32_t lp_prio; /* port priority */ uint32_t lp_flags; /* port flags */ @@ -142,6 +144,7 @@ struct lagg_softc { struct ifmedia sc_media; u_char sc_iftype; uint8_t sc_lladdr[ETHER_ADDR_LEN]; + uint8_t sc_lladdr_default[ETHER_ADDR_LEN]; LIST_HEAD(, lagg_mc_entry) sc_mclist; TAILQ_HEAD(, lagg_vlantag) @@ -153,6 +156,8 @@ struct lagg_softc { size_t sc_nports; char sc_evgroup[16]; struct evcnt sc_novar; + struct workqueue *sc_wq; + struct lagg_work sc_wk_lladdr; struct sysctllog *sc_sysctllog; const struct sysctlnode *sc_sysctlnode; @@ -195,7 +200,7 @@ struct lagg_softc { #define LAGG_LOCK(_sc) mutex_enter(&(_sc)->sc_lock) #define LAGG_UNLOCK(_sc) mutex_exit(&(_sc)->sc_lock) #define LAGG_LOCKED(_sc) mutex_owned(&(_sc)->sc_lock) -#define LAGG_CLLADDR(_sc) CLLADDR((_sc)->sc_if.if_sadl) +#define LAGG_CLLADDR(_sc) ((const void *)(_sc)->sc_lladdr) #define LAGG_PORTS_FOREACH(_sc, _lp) \ SIMPLEQ_FOREACH((_lp), &(_sc)->sc_ports, lp_entry) diff --git a/sys/net/lagg/if_laggvar.h b/sys/net/lagg/if_laggvar.h index 6c37530abb0a..8515fe6f70d5 100644 --- a/sys/net/lagg/if_laggvar.h +++ b/sys/net/lagg/if_laggvar.h @@ -29,9 +29,6 @@ #ifndef _NET_LAGG_IF_LAGGVAR_H_ #define _NET_LAGG_IF_LAGGVAR_H_ -void lagg_ifdetach(struct ifnet *); -void lagg_linkstate_changed(struct ifnet *); - extern struct mbuf * (*lagg_input_ethernet_p)(struct ifnet *, struct mbuf *); diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index b2d92871774d..0f6c9d4a0151 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -113,6 +113,7 @@ struct carp_softc { struct ethercom sc_ac; #define sc_if sc_ac.ec_if #define sc_carpdev sc_ac.ec_if.if_carpdev + void *sc_linkstate_hook; int ah_cookie; int lh_cookie; struct ip_moptions sc_imo; @@ -907,6 +908,7 @@ carp_clone_destroy(struct ifnet *ifp) static void carpdetach(struct carp_softc *sc) { + struct ifnet *ifp; struct carp_if *cif; int s; @@ -929,13 +931,16 @@ carpdetach(struct carp_softc *sc) KERNEL_LOCK(1, NULL); s = splnet(); - if (sc->sc_carpdev != NULL) { - /* XXX linkstatehook removal */ - cif = (struct carp_if *)sc->sc_carpdev->if_carp; + ifp = sc->sc_carpdev; + if (ifp != NULL) { + if_linkstate_change_disestablish(ifp, + sc->sc_linkstate_hook, NULL); + + cif = (struct carp_if *)ifp->if_carp; TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); if (!--cif->vhif_nvrs) { - ifpromisc(sc->sc_carpdev, 0); - sc->sc_carpdev->if_carp = NULL; + ifpromisc(ifp, 0); + ifp->if_carp = NULL; free(cif, M_IFADDR); } } @@ -1708,9 +1713,10 @@ carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp) if (sc->sc_naddrs || sc->sc_naddrs6) sc->sc_if.if_flags |= IFF_UP; carp_set_enaddr(sc); + sc->sc_linkstate_hook = if_linkstate_change_establish(ifp, + carp_carpdev_state, (void *)ifp); KERNEL_LOCK(1, NULL); s = splnet(); - /* XXX linkstatehooks establish */ carp_carpdev_state(ifp); splx(s); KERNEL_UNLOCK_ONE(NULL); diff --git a/sys/rump/kern/Makefile.rumpkerncomp b/sys/rump/kern/Makefile.rumpkerncomp index 315f099f2e9d..e705971b8467 100644 --- a/sys/rump/kern/Makefile.rumpkerncomp +++ b/sys/rump/kern/Makefile.rumpkerncomp @@ -3,7 +3,7 @@ .include -RUMPKERNCOMPS= crypto nv sysproxy tty z +RUMPKERNCOMPS= crypto nv sysproxy tty z simplehook_tester RUMPSYSEMUS= sys_cygwin sys_linux sys_sunos .if make(rumpdescribe) diff --git a/sys/rump/kern/lib/libsimplehook_tester/Makefile b/sys/rump/kern/lib/libsimplehook_tester/Makefile new file mode 100644 index 000000000000..4082cef48830 --- /dev/null +++ b/sys/rump/kern/lib/libsimplehook_tester/Makefile @@ -0,0 +1,13 @@ + + +LIB= rumpkern_simplehook_tester +COMMENT=Tester for simplehook + +SRCS= simplehook_tester.c + +.ifdef RUMP_DEBUG +CPPFLAGS.simplehook_tester.c+= -DSIMPLEHOOK_TESTER_DEBUG +.endif + +.include +.include diff --git a/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c b/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c new file mode 100644 index 000000000000..f2f88fc0f14b --- /dev/null +++ b/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c @@ -0,0 +1,725 @@ + +/* $NetBSD: $ */ +/* + * Copyright (c) 2021 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: $"); + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef SIMPLEHOOK_TESTER_DEBUG +#define HK_DPRINTF(a) printf a +#else +#define HK_DPRINTF(a) __nothing +#endif + +MODULE(MODULE_CLASS_MISC, simplehook_tester, NULL); +extern int simplehook_tester_init(void); +struct tester_context; + +struct tester_hook { + struct tester_context *th_ctx; + khook_t *th_hook; + size_t th_idx; + int th_count; + bool th_stopping; + bool th_stopped; + bool th_disestablish; +}; + +static struct tester_context { + kmutex_t ctx_mutex; + kcondvar_t ctx_cv; + struct sysctllog *ctx_sysctllog; + struct workqueue *ctx_wq; + struct work ctx_wk; + bool ctx_wk_enqueued; + bool ctx_wk_waiting; + + khook_list_t *ctx_hooks; + struct tester_hook ctx_hook[2]; + + khook_t *ctx_nbhook; +} tester_ctx; + +static int +simplehook_tester_created(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + int error, val; + size_t i; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_hooks != NULL ? 1 : 0; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (val != 0 && val != 1) + return EINVAL; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + if (val == 1) { + if (ctx->ctx_hooks != NULL) { + error = EEXIST; + } else { + HK_DPRINTF(("[%s, %d]: create hook list\n", + __func__, __LINE__)); + ctx->ctx_hooks = simplehook_create(IPL_NONE, + "tester hooks"); + KASSERT(ctx->ctx_hooks != NULL); + } + } else { + if (ctx->ctx_hooks == NULL) { + error = ENXIO; + } else if (ctx->ctx_wk_waiting) { + error = EBUSY; + } else { + ctx->ctx_wk_waiting = true; + mutex_exit(&ctx->ctx_mutex); + + workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk); + + mutex_enter(&ctx->ctx_mutex); + ctx->ctx_wk_waiting = false; + + HK_DPRINTF(("[%s, %d]: destroy hook list\n", + __func__, __LINE__)); + simplehook_destroy(ctx->ctx_hooks); + ctx->ctx_hooks = NULL; + ctx->ctx_nbhook = NULL; + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + ctx->ctx_hook[i].th_hook = NULL; + } + } + } + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static void +simplehook_tester_work(struct work *wk, void *xctx) +{ + struct tester_context *ctx; + + ctx = xctx; + + mutex_enter(&ctx->ctx_mutex); + ctx->ctx_wk_enqueued = false; + mutex_exit(&ctx->ctx_mutex); + + simplehook_dohooks(ctx->ctx_hooks); +} + +static int +simplehook_tester_dohooks(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_wk_enqueued ? 1 : 0; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (val != 0 && val != 1) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + if (val == 1) { + if (ctx->ctx_wk_enqueued) { + error = EEXIST; + } else if (ctx->ctx_wk_waiting) { + error = EBUSY; + } else if (ctx->ctx_hooks == NULL) { + error = ENXIO; + } else { + HK_DPRINTF(("[%s, %d]: dohook\n", __func__, __LINE__)); + ctx->ctx_wk_enqueued = true; + workqueue_enqueue(ctx->ctx_wq, + &ctx->ctx_wk, NULL); + } + } else { + if (ctx->ctx_wk_waiting) { + error = EBUSY; + } else { + ctx->ctx_wk_waiting = true; + mutex_exit(&ctx->ctx_mutex); + + workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk); + + mutex_enter(&ctx->ctx_mutex); + ctx->ctx_wk_waiting = false; + } + } + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static void +simplehook_tester_hook(void *xth) +{ + struct tester_context *ctx; + struct tester_hook *th; + + th = xth; + ctx = th->th_ctx; + mutex_enter(&ctx->ctx_mutex); + + HK_DPRINTF(("[%s, %d]: hook%zu called\n", + __func__, __LINE__, th->th_idx)); + + th->th_stopped = false; + + while (th->th_stopping) { + HK_DPRINTF(("[%s, %d]: hook%zu stopping\n", + __func__, __LINE__, th->th_idx)); + th->th_stopped = true; + cv_wait(&ctx->ctx_cv, &ctx->ctx_mutex); + } + + if (th->th_stopped) { + HK_DPRINTF(("[%s, %d]: hook%zu restart\n", + __func__, __LINE__, th->th_idx)); + th->th_stopped = false; + } + + th->th_count++; + + if (th->th_disestablish && th->th_hook != NULL) { + HK_DPRINTF(("[%s, %d]: disestablish runing hook%zu\n", + __func__, __LINE__, th->th_idx)); + simplehook_disestablish(ctx->ctx_hooks, + th->th_hook, &ctx->ctx_mutex); + th->th_hook = NULL; + } + + HK_DPRINTF(("[%s, %d]: hook%zu exit\n", + __func__, __LINE__, th->th_idx)); + + mutex_exit(&ctx->ctx_mutex); +} + +static void +simplehook_tester_hook_nb(void *xctx) +{ + + HK_DPRINTF(("[%s, %d]: non-block hook called\n", + __func__, __LINE__)); + + HK_DPRINTF(("[%s, %d]: sleep 1 sec\n", + __func__, __LINE__)); + kpause("smplhk_nb", true, 1 * hz, NULL); + + HK_DPRINTF(("[%s, %d]: non-block hook exit\n", + __func__, __LINE__)); +} + +static int +simplehook_tester_established(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int val, error; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_hook == NULL ? 0 : 1; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + + if (val == 1) { + if (th->th_hook != NULL) { + error = EEXIST; + } else { + th->th_hook = simplehook_establish(ctx->ctx_hooks, + simplehook_tester_hook, th); + KASSERT(th->th_hook != NULL); + HK_DPRINTF(("[%s, %d]: established hook%zu (%p)\n", + __func__, __LINE__, th->th_idx, th->th_hook)); + } + } else { + if (th->th_hook == NULL) { + error = ENXIO; + } else { + bool stopped = false; + if (th->th_stopping) { + HK_DPRINTF(("[%s, %d]: stopping = false\n", + __func__, __LINE__)); + th->th_stopping = false; + cv_broadcast(&ctx->ctx_cv); + stopped = true; + } + HK_DPRINTF(("[%s, %d]: disestablish hook%zu (%p)\n", + __func__, __LINE__, th->th_idx, th->th_hook)); + simplehook_disestablish(ctx->ctx_hooks, + th->th_hook, &ctx->ctx_mutex); + th->th_hook = NULL; + if (stopped) { + HK_DPRINTF(("[%s, %d]: disestablished hook%zu\n", + __func__, __LINE__, th->th_idx)); + } + } + } + + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static int +simplehook_tester_established_nb(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + int val, error; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_nbhook == NULL ? 0 : 1; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + + if (val == 1) { + if (ctx->ctx_nbhook != NULL) { + error = EEXIST; + } else { + ctx->ctx_nbhook = simplehook_establish(ctx->ctx_hooks, + simplehook_tester_hook_nb, ctx); + KASSERT(ctx->ctx_nbhook != NULL); + HK_DPRINTF(("[%s, %d]: established nbhook (%p)\n", + __func__, __LINE__, ctx->ctx_nbhook)); + } + } else { + if (ctx->ctx_nbhook == NULL) { + error = ENXIO; + } else { + HK_DPRINTF(("[%s, %d]: disestablish nbhook (%p)\n", + __func__, __LINE__, ctx->ctx_nbhook)); + simplehook_disestablish(ctx->ctx_hooks, + ctx->ctx_nbhook, NULL); + ctx->ctx_nbhook = NULL; + HK_DPRINTF(("[%s, %d]: disestablished\n", + __func__, __LINE__)); + } + } + + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static int +simplehook_tester_stopping(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int error; + bool val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_stopping; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + if (val == true && !th->th_stopping) { + th->th_stopping = true; + } else if (val == false && th->th_stopping) { + th->th_stopping = false; + cv_broadcast(&ctx->ctx_cv); + } + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static int +simplehook_tester_stopped(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + bool val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_stopped; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + return sysctl_lookup(SYSCTLFN_CALL(&node)); +} + +static int +simplehook_tester_disestablish(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int error; + bool val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_disestablish; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (val != 0 && val != 1) + return EINVAL; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + if (val == true && !th->th_disestablish) { + th->th_disestablish = true; + } else if (val == false && th->th_disestablish) { + th->th_disestablish = false; + } + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +simplehook_tester_count(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int error, val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_count; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + mutex_enter(&ctx->ctx_mutex); + th->th_count = val; + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +simplehook_tester_create_sysctl(struct tester_context *ctx) +{ + struct sysctllog **log; + const struct sysctlnode *rnode, *cnode; + void *ptr; + char buf[32]; + int error; + size_t i; + + log = &ctx->ctx_sysctllog; + ptr = (void *)ctx; + + error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, + CTLTYPE_NODE, "simplehook_tester", + SYSCTL_DESCR("simplehook testing interface"), + NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "hook_list", + SYSCTL_DESCR("hook list"), NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "created", SYSCTL_DESCR("create and destroy hook list"), + simplehook_tester_created, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "dohooks", SYSCTL_DESCR("do hooks"), + simplehook_tester_dohooks, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "nbhook", + SYSCTL_DESCR("non-blocking hook"), NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "established", + SYSCTL_DESCR("establish and disestablish hook"), + simplehook_tester_established_nb, + 0, ptr, 0, CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + snprintf(buf, sizeof(buf), "hook%zu", i); + ptr = (void *)&ctx->ctx_hook[i]; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, buf, + SYSCTL_DESCR("hook information"), NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "established", + SYSCTL_DESCR("establish and disestablish hook"), + simplehook_tester_established, + 0, ptr, 0, CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_BOOL, "stopping", + SYSCTL_DESCR("stopping at beginning of the hook"), + simplehook_tester_stopping, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READONLY, + CTLTYPE_BOOL, "stopped", + SYSCTL_DESCR("the hook is stopped"), + simplehook_tester_stopped, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_BOOL, + "disestablish_in_hook", + SYSCTL_DESCR("disestablish this hook in it"), + simplehook_tester_disestablish, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "count", + SYSCTL_DESCR("the number of calls of the hook"), + simplehook_tester_count, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + } + + HK_DPRINTF(("[%s, %d]: created sysctls\n", __func__, __LINE__)); + return 0; + +bad: + sysctl_teardown(log); + return error; +} + +static void +simplehook_tester_init_ctx(struct tester_context *ctx) +{ + size_t i; + + memset(ctx, 0, sizeof(*ctx)); + mutex_init(&ctx->ctx_mutex, MUTEX_DEFAULT, IPL_NONE); + workqueue_create(&ctx->ctx_wq, "shook_tester_wq", + simplehook_tester_work, ctx, PRI_NONE, IPL_NONE, WQ_MPSAFE); + cv_init(&ctx->ctx_cv, "simplehook_tester_cv"); + + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + ctx->ctx_hook[i].th_ctx = ctx; + ctx->ctx_hook[i].th_idx = i; + } +} + + +int +simplehook_tester_init(void) +{ + int error; + + simplehook_tester_init_ctx(&tester_ctx); + error = simplehook_tester_create_sysctl(&tester_ctx); + + return error; +} + +static int +simplehook_tester_fini(void) +{ + struct tester_context *ctx; + struct tester_hook *th; + khook_list_t *hooks; + size_t i; + + ctx = &tester_ctx; + + sysctl_teardown(&ctx->ctx_sysctllog); + + mutex_enter(&ctx->ctx_mutex); + + hooks = ctx->ctx_hooks; + ctx->ctx_hooks = NULL; + ctx->ctx_wk_waiting = true; + + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + th = &ctx->ctx_hook[i]; + th->th_stopping = false; + } + cv_broadcast(&ctx->ctx_cv); + + mutex_exit(&ctx->ctx_mutex); + + workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk); + + workqueue_destroy(ctx->ctx_wq); + if (hooks != NULL) + simplehook_destroy(hooks); + cv_destroy(&ctx->ctx_cv); + mutex_destroy(&ctx->ctx_mutex); + + return 0; +} + +static int +simplehook_tester_modcmd(modcmd_t cmd, void *arg __unused) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = simplehook_tester_init(); + break; + + case MODULE_CMD_FINI: + error = simplehook_tester_fini(); + break; + + case MODULE_CMD_STAT: + default: + error = ENOTTY; + } + + return error; +} diff --git a/sys/rump/librump/rumpnet/net_stub.c b/sys/rump/librump/rumpnet/net_stub.c index 4b7df65e9f97..846a91d8fcd7 100644 --- a/sys/rump/librump/rumpnet/net_stub.c +++ b/sys/rump/librump/rumpnet/net_stub.c @@ -59,10 +59,8 @@ rumpnet_stub(void) */ /* bridge */ -__weak_alias(bridge_ifdetach,rumpnet_stub); __weak_alias(bridge_output,rumpnet_stub); __weak_alias(bridge_calc_csum_flags,rumpnet_stub); -__weak_alias(bridge_calc_link_state,rumpnet_stub); /* agr */ __weak_alias(agr_input,rumpnet_stub); @@ -75,8 +73,6 @@ __weak_alias(pppoedisc_input,rumpnet_stub); /* vlan */ __weak_alias(vlan_input,rumpnet_stub); -__weak_alias(vlan_ifdetach,rumpnet_stub); -__weak_alias(vlan_link_state_changed,rumpnet_stub); /* ipsec */ /* FIXME: should modularize netipsec and reduce reverse symbol references */ @@ -111,7 +107,6 @@ __weak_alias(key_sp_unref,rumpnet_stub); /* lagg */ __weak_alias(lagg_ifdetach,rumpnet_stub); __weak_alias(lagg_input_ethernet,rumpnet_stub); -__weak_alias(lagg_linkstate_changed,rumpnet_stub); struct ifnet_head ifnet_list; struct pslist_head ifnet_pslist; diff --git a/sys/sys/Makefile b/sys/sys/Makefile index 291962f69361..1f2571c2a228 100644 --- a/sys/sys/Makefile +++ b/sys/sys/Makefile @@ -23,7 +23,7 @@ INCS= acct.h acl.h agpio.h aio.h ansi.h aout_mids.h ataio.h atomic.h \ fault.h \ fcntl.h fd_set.h fdio.h featuretest.h file.h filedesc.h filio.h \ flashio.h float_ieee754.h fstypes.h futex.h gcq.h gmon.h gpio.h hash.h \ - idtype.h ieee754.h intr.h intrio.h inttypes.h ioccom.h ioctl.h \ + hook.h idtype.h ieee754.h intr.h intrio.h inttypes.h ioccom.h ioctl.h \ ioctl_compat.h iostat.h ipc.h ipmi.h \ joystick.h \ kcore.h kcov.h kcpuset.h kgdb.h kmem.h ksem.h ksyms.h ktrace.h \ diff --git a/sys/sys/hook.h b/sys/sys/hook.h new file mode 100644 index 000000000000..f19ab9d8f8f5 --- /dev/null +++ b/sys/sys/hook.h @@ -0,0 +1,52 @@ +/* $NetBSD: $ */ + +/* + * Copyright (c) 2021 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_HOOK_H_ +#define _SYS_HOOK_H_ + +#include + +#define HOOKNAMSIZ 128 + +struct khook_list; +struct hook_desc; + +typedef struct khook_list khook_list_t; +typedef struct hook_desc khook_t; + +khook_list_t *simplehook_create(int, const char *); +void simplehook_destroy(khook_list_t *); +int simplehook_dohooks(khook_list_t *); + +khook_t *simplehook_establish(khook_list_t *, + void (*)(void *), void *); +void simplehook_disestablish(khook_list_t *, + khook_t *, kmutex_t *); +bool simplehook_has_hooks(khook_list_t *); + +#endif /* !_SYS_HOOK_H_ */ diff --git a/tests/kernel/Makefile b/tests/kernel/Makefile index e98d30beddef..2fe7eac18eb4 100644 --- a/tests/kernel/Makefile +++ b/tests/kernel/Makefile @@ -32,6 +32,7 @@ TESTS_SH+= t_origin TESTS_SH+= t_procpath TESTS_SH+= t_fexecve TESTS_SH+= t_fpufork +TESTS_SH+= t_simplehook BINDIR= ${TESTSDIR} PROGS= h_fexecve diff --git a/tests/kernel/t_simplehook.sh b/tests/kernel/t_simplehook.sh new file mode 100644 index 000000000000..d313c8668175 --- /dev/null +++ b/tests/kernel/t_simplehook.sh @@ -0,0 +1,294 @@ +# $NetBSD: $ +# +# Copyright (c) 2021 Internet Initiative Japan Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +DEBUG=${DEBUG:-false} +SOCK=unix://commsock +HIJACKING="env LD_PRELOAD=/usr/lib/librumphijack.so \ + RUMPHIJACK=path=/rump,socket=all:nolocal,sysctl=yes" + +atf_sysctl_rd() +{ + local sysctl_key=$1 + local expected=$2 + + atf_check -s exit:0 -o match:"$expected" \ + rump.sysctl -n $sysctl_key +} + +atf_sysctl_wr() +{ + local sysctl_key=$1 + local value=$2 + + atf_check -s exit:0 -o ignore rump.sysctl -w $sysctl_key=$value +} + +atf_sysctl_wait() +{ + local sysctl_key=$1 + local expected=$2 + local n=10 + local i + local v + + for i in $(seq $n); do + v=$(rump.sysctl -n $sysctl_key) + if [ x"$v" = x"$expected" ]; then + return + fi + sleep 0.5 + done + + atf_fail "Couldn't get the value for $n seconds." +} + +atf_test_case simplehook_basic cleanup +simplehook_basic_head() +{ + + atf_set "descr" "tests for basically functions of simplehook" + atf_set "require.progs" "rump_server" +} + + +simplehook_basic_body() +{ + local key_hklist="kern.simplehook_tester.hook_list" + local key_hk0="kern.simplehook_tester.hook0" + local key_hk1="kern.simplehook_tester.hook1" + + rump_server -lrumpkern_simplehook_tester $SOCK + + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester + + # create and destroy + atf_sysctl_rd ${key_hklist}.created '0' + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hklist}.created '0' + $DEBUG && rump.sysctl -e kern.simplehook_tester + + # establish one hook + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hklist}.dohooks '0' + atf_sysctl_rd ${key_hk0}.count '1' + atf_sysctl_wr ${key_hk0}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' + + # establish two hooks + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk1}.established '1' + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hk1}.count '0' + + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hklist}.dohooks '0' + atf_sysctl_rd ${key_hk0}.count '1' + atf_sysctl_rd ${key_hk1}.count '1' + + atf_sysctl_wr ${key_hk0}.established '0' + atf_sysctl_wr ${key_hk1}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' +} + +simplehook_basic_cleanup() +{ + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + $DEBUG && $HIJACKING dmesg + rump.halt +} + +atf_test_case simplehook_disestablish cleanup +simplehook_disestablish_head() +{ + + atf_set "descr" "tests for disestablish of simplehook" + atf_set "require.progs" "rump_server" +} + +simplehook_disestablish_body() +{ + local key_hklist="kern.simplehook_tester.hook_list" + local key_hk0="kern.simplehook_tester.hook0" + local key_hk1="kern.simplehook_tester.hook1" + + rump_server -lrumpkern_simplehook_tester $SOCK + + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester + + # + # disestablish on the running hook + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk0}.disestablish_in_hook '1' + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + # already disestablished + atf_sysctl_rd ${key_hk0}.established '0' + atf_sysctl_wr ${key_hk0}.disestablish_in_hook '0' + + atf_sysctl_wr ${key_hklist}.created '0' + + # + # disestablish called hook while doing other hook + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk1}.established '1' + + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hk1}.count '0' + atf_sysctl_wr ${key_hk0}.stopping '1' + + # calls hook1 -> hook0 + atf_sysctl_wr ${key_hklist}.dohooks '1' + + # stop in hook0 + atf_sysctl_wait ${key_hk0}.stopped '1' + + atf_sysctl_rd ${key_hk1}.count '1' + atf_sysctl_wr ${key_hk1}.established '0' + + # wakeup hook0 + atf_sysctl_wr ${key_hk0}.stopping '0' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + atf_sysctl_wr ${key_hk0}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' + + # + # disestablish a hook in running hook list + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk1}.established '1' + + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hk1}.count '0' + atf_sysctl_wr ${key_hk1}.stopping '1' + + # calls hook1 -> hook0 + atf_sysctl_wr ${key_hklist}.dohooks '1' + + # stop in hook1 + atf_sysctl_wait ${key_hk1}.stopped '1' + + atf_sysctl_wr ${key_hk0}.established '0' + + # wakeup hook1 + atf_sysctl_wr ${key_hk1}.stopping '0' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + # hook0 is not called + atf_sysctl_rd ${key_hk0}.count '0' + + atf_sysctl_wr ${key_hk1}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' + + # + # disestablish the running hook + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk0}.stopping '1' + + atf_sysctl_wr ${key_hklist}.dohooks '1' + + atf_sysctl_wait ${key_hk0}.stopped '1' + atf_sysctl_wr ${key_hk0}.established '0' + + atf_sysctl_wr ${key_hklist}.dohooks '0' + atf_sysctl_wr ${key_hklist}.created '0' +} + +simplehook_disestablish_cleanup() +{ + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + $DEBUG && $HIJACKING dmesg + rump.halt +} + +atf_test_case simplehook_nolock cleanup +simplehook_nolock_head() +{ + + atf_set "descr" "tests for hook that does not use lock in it" + atf_set "require.progs" "rump_server" +} + +simplehook_nolock_body() +{ + local key_hklist="kern.simplehook_tester.hook_list" + local key_hk="kern.simplehook_tester.nbhook" + + rump_server -lrumpkern_simplehook_tester $SOCK + + export RUMP_SERVER=$SOCK + $DEBUG && rump.sysctl -e kern.simplehook_tester + atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester + + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk}.established '1' + + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hk}.established '0' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + atf_sysctl_wr ${key_hklist}.created '0' +} + +simplehook_nolock_cleanup() +{ + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + $DEBUG && $HIJACKING dmesg + rump.halt +} + +atf_init_test_cases() +{ + + atf_add_test_case simplehook_basic + atf_add_test_case simplehook_disestablish + atf_add_test_case simplehook_nolock +}