Index: sys/netinet/netdump/netdump_client.c =================================================================== --- sys/netinet/netdump/netdump_client.c +++ sys/netinet/netdump/netdump_client.c @@ -108,6 +108,7 @@ static int netdump_modevent(module_t mod, int type, void *priv); static void netdump_network_poll(void); static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m); +static void netdump_reinit_internal(struct ifnet *ifp); static int netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen); static int netdump_send_arp(in_addr_t dst); @@ -131,8 +132,14 @@ #define nd_gateway nd_conf.kda_gateway.in4 /* General dynamic settings. */ +static struct sx nd_conf_lk; +SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock"); +#define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk) +#define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk) +#define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED) static struct ether_addr nd_gw_mac; static struct ifnet *nd_ifp; +static eventhandler_tag nd_detach_cookie; static uint16_t nd_server_port = NETDUMP_PORT; FEATURE(netdump, "Netdump client support"); @@ -1065,52 +1072,86 @@ static struct cdev *netdump_cdev; +static void +netdump_ifdetach(void *arg __unused, struct ifnet *ifp) +{ + struct diocskerneldump_arg kda; + + NETDUMP_WLOCK(); + if (ifp == nd_ifp) { + bzero(&kda, sizeof(kda)); + kda.kda_index = KDA_REMOVE_DEV; + (void)dumper_remove(nd_conf.kda_iface, &kda); + + netdump_mbuf_drain(); + nd_enabled = 0; + + nd_ifp = NULL; + if_rele(ifp); + } + NETDUMP_WUNLOCK(); +} + static int netdump_configure(struct diocskerneldump_arg *conf, struct thread *td) { - struct epoch_tracker et; struct ifnet *ifp; + NETDUMP_ASSERT_WLOCKED(); + CURVNET_SET(TD_TO_VNET(td)); if (!IS_DEFAULT_VNET(curvnet)) { CURVNET_RESTORE(); return (EINVAL); } - NET_EPOCH_ENTER(et); - CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - if (strcmp(ifp->if_xname, conf->kda_iface) == 0) - break; - } - /* XXX ref */ - NET_EPOCH_EXIT(et); + ifp = ifunit_ref(conf->kda_iface); CURVNET_RESTORE(); if (ifp == NULL) return (ENOENT); - if ((if_getflags(ifp) & IFF_UP) == 0) + if ((if_getflags(ifp) & IFF_UP) == 0) { + if_rele(ifp); return (ENXIO); - if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER) + } + if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER) { + if_rele(ifp); return (ENODEV); + } + if (nd_ifp != NULL) + if_rele(nd_ifp); nd_ifp = ifp; - netdump_reinit(ifp); + + netdump_reinit_internal(ifp); memcpy(&nd_conf, conf, sizeof(nd_conf)); nd_enabled = 1; + return (0); } /* * Reinitialize the mbuf pool used by drivers while dumping. This is called - * from the generic ioctl handler for SIOCSIFMTU after the driver has - * reconfigured itself. + * from the generic ioctl handler for SIOCSIFMTU after any NIC driver has + * reconfigured itself. (I.e., it may not be a configured netdump interface.) */ void netdump_reinit(struct ifnet *ifp) +{ + NETDUMP_WLOCK(); + if (ifp != nd_ifp) { + NETDUMP_WUNLOCK(); + return; + } + netdump_reinit_internal(ifp); + NETDUMP_WUNLOCK(); +} + +static void +netdump_reinit_internal(struct ifnet *ifp) { int clsize, nmbuf, ncl, nrxr; - if (ifp != nd_ifp) - return; + NETDUMP_ASSERT_WLOCKED(); ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize); KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr)); @@ -1156,6 +1197,8 @@ conf = NULL; error = 0; + NETDUMP_WLOCK(); + switch (cmd) { #ifdef COMPAT_FREEBSD11 case DIOCSKERNELDUMP_FREEBSD11: @@ -1342,6 +1385,7 @@ explicit_bzero(&kda_copy, sizeof(kda_copy)); if (conf != NULL) explicit_bzero(conf, sizeof(*conf)); + NETDUMP_WUNLOCK(); return (error); } @@ -1373,6 +1417,9 @@ if (error != 0) return (error); + nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, + netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY); + if ((arg = kern_getenv("net.dump.iface")) != NULL) { strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface)); freeenv(arg); @@ -1392,10 +1439,13 @@ conf.kda_af = AF_INET; /* Ignore errors; we print a message to the console. */ + NETDUMP_WLOCK(); (void)netdump_configure(&conf, curthread); + NETDUMP_WUNLOCK(); } break; case MOD_UNLOAD: + NETDUMP_WLOCK(); if (nd_enabled) { struct diocskerneldump_arg kda; @@ -1408,7 +1458,10 @@ netdump_mbuf_drain(); nd_enabled = 0; } + NETDUMP_WUNLOCK(); destroy_dev(netdump_cdev); + EVENTHANDLER_DEREGISTER(ifnet_departure_event, + nd_detach_cookie); break; default: error = EOPNOTSUPP;