svn commit: r347473 - head/sys/netinet/netdump

Conrad Meyer cem at FreeBSD.org
Fri May 10 23:13:00 UTC 2019


Author: cem
Date: Fri May 10 23:12:59 2019
New Revision: 347473
URL: https://svnweb.freebsd.org/changeset/base/347473

Log:
  netdump: Ref the interface we're attached to
  
  Serialize netdump configuration / deconfiguration, and discard our
  configuration when the affiliated interface goes away by monitoring
  ifnet_departure_event.
  
  Reviewed by:	markj, with input from vangyzen@ (earlier version)
  Sponsored by:	Dell EMC Isilon
  Differential Revision:	https://reviews.freebsd.org/D20206

Modified:
  head/sys/netinet/netdump/netdump_client.c

Modified: head/sys/netinet/netdump/netdump_client.c
==============================================================================
--- head/sys/netinet/netdump/netdump_client.c	Fri May 10 23:12:37 2019	(r347472)
+++ head/sys/netinet/netdump/netdump_client.c	Fri May 10 23:12:59 2019	(r347473)
@@ -93,6 +93,8 @@ static int	 netdump_configure(struct diocskerneldump_a
 		    struct thread *);
 static int	 netdump_dumper(void *priv __unused, void *virtual,
 		    vm_offset_t physical __unused, off_t offset, size_t length);
+static bool	 netdump_enabled(void);
+static int	 netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS);
 static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp,
 		    struct ether_addr dst, u_short etype);
 static void	 netdump_handle_arp(struct mbuf **mb);
@@ -102,11 +104,13 @@ static int	 netdump_ioctl(struct cdev *dev __unused, u
 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);
 static int	 netdump_start(struct dumperinfo *di);
 static int	 netdump_udp_output(struct mbuf *m);
+static void	 netdump_unconfigure(void);
 
 /* Must be at least as big as the chunks dumpsys() gives us. */
 static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
@@ -131,8 +135,17 @@ static struct {
 #define	nd_gateway	nd_conf.ndc_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_RLOCK()			sx_slock(&nd_conf_lk)
+#define NETDUMP_RUNLOCK()		sx_sunlock(&nd_conf_lk)
+#define NETDUMP_ASSERT_WLOCKED()	sx_assert(&nd_conf_lk, SA_XLOCKED)
+#define NETDUMP_ASSERT_LOCKED()		sx_assert(&nd_conf_lk, SA_LOCKED)
 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");
@@ -144,10 +157,8 @@ static int nd_debug;
 SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
     &nd_debug, 0,
     "Debug message verbosity");
-static int nd_enabled;
-SYSCTL_INT(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD,
-    &nd_enabled, 0,
-    "netdump configuration status");
+SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT,
+    &nd_ifp, 0, netdump_enabled_sysctl, "I", "netdump configuration status");
 static char nd_path[MAXPATHLEN];
 SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW,
     nd_path, sizeof(nd_path),
@@ -165,6 +176,29 @@ SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLA
     &nd_arp_retries, 0,
     "Number of ARP attempts before giving up");
 
+static bool
+netdump_enabled(void)
+{
+
+	NETDUMP_ASSERT_LOCKED();
+	return (nd_ifp != NULL);
+}
+
+static int
+netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	int en, error;
+
+	NETDUMP_RLOCK();
+	en = netdump_enabled();
+	NETDUMP_RUNLOCK();
+
+	error = SYSCTL_OUT(req, &en, sizeof(en));
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	return (EPERM);
+}
+
 /*
  * Checks for netdump support on a network interface
  *
@@ -248,7 +282,7 @@ netdump_udp_output(struct mbuf *m)
 	struct udpiphdr *ui;
 	struct ip *ip;
 
-	MPASS(nd_ifp != NULL);
+	MPASS(netdump_enabled());
 
 	M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
 	if (m == NULL) {
@@ -306,7 +340,7 @@ netdump_send_arp(in_addr_t dst)
 	struct arphdr *ah;
 	int pktlen;
 
-	MPASS(nd_ifp != NULL);
+	MPASS(netdump_enabled());
 
 	/* Fill-up a broadcast address. */
 	memset(&bcast, 0xFF, ETHER_ADDR_LEN);
@@ -409,7 +443,7 @@ netdump_send(uint32_t type, off_t offset, unsigned cha
 	rcvd_acks = 0;
 	retries = 0;
 
-	MPASS(nd_ifp != NULL);
+	MPASS(netdump_enabled());
 
 retransmit:
 	/* Chunks can be too big to fit in packets. */
@@ -875,7 +909,7 @@ static void
 netdump_network_poll(void)
 {
 
-	MPASS(nd_ifp != NULL);
+	MPASS(netdump_enabled());
 
 	nd_ifp->if_netdump_methods->nd_poll(nd_ifp, 1000);
 }
@@ -945,7 +979,7 @@ netdump_start(struct dumperinfo *di)
 	error = 0;
 
 	/* Check if the dumping is allowed to continue. */
-	if (nd_enabled == 0)
+	if (!netdump_enabled())
 		return (EINVAL);
 
 	if (panicstr == NULL) {
@@ -954,8 +988,6 @@ netdump_start(struct dumperinfo *di)
 		return (EINVAL);
 	}
 
-	MPASS(nd_ifp != NULL);
-
 	if (nd_server.s_addr == INADDR_ANY) {
 		printf("netdump_start: can't netdump; no server IP given\n");
 		return (EINVAL);
@@ -1065,36 +1097,68 @@ static struct cdevsw netdump_cdevsw = {
 
 static struct cdev *netdump_cdev;
 
+static void
+netdump_unconfigure(void)
+{
+	struct diocskerneldump_arg kda;
+
+	NETDUMP_ASSERT_WLOCKED();
+	KASSERT(netdump_enabled(), ("%s: nd_ifp NULL", __func__));
+
+	bzero(&kda, sizeof(kda));
+	kda.kda_index = KDA_REMOVE_DEV;
+	(void)dumper_remove(nd_conf.ndc_iface, &kda);
+
+	netdump_mbuf_drain();
+
+	if_rele(nd_ifp);
+	nd_ifp = NULL;
+
+	bzero(&nd_conf, sizeof(nd_conf));
+}
+
+static void
+netdump_ifdetach(void *arg __unused, struct ifnet *ifp)
+{
+
+	NETDUMP_WLOCK();
+	if (ifp == nd_ifp)
+		netdump_unconfigure();
+	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 (netdump_enabled())
+		if_rele(nd_ifp);
 	nd_ifp = ifp;
 
-	netdump_reinit(ifp);
+	netdump_reinit_internal(ifp);
+
 #define COPY_SIZED(elm) do {	\
 	_Static_assert(sizeof(nd_conf.ndc_ ## elm) ==			\
 	    sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
@@ -1107,23 +1171,35 @@ netdump_configure(struct diocskerneldump_arg *conf, st
 	COPY_SIZED(gateway);
 	COPY_SIZED(af);
 #undef COPY_SIZED
-	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)
 {
-	int clsize, nmbuf, ncl, nrxr;
 
-	if (ifp != nd_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;
+
+	NETDUMP_ASSERT_WLOCKED();
+
 	ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize);
 	KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr));
 
@@ -1168,6 +1244,8 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 
 	conf = NULL;
 	error = 0;
+	NETDUMP_WLOCK();
+
 	switch (cmd) {
 #ifdef COMPAT_FREEBSD11
 	case DIOCSKERNELDUMP_FREEBSD11:
@@ -1177,10 +1255,8 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 			error = ENXIO;
 			break;
 		}
-		if (nd_enabled) {
-			nd_enabled = 0;
-			netdump_mbuf_drain();
-		}
+		if (netdump_enabled())
+			netdump_unconfigure();
 		break;
 #endif
 #ifdef COMPAT_FREEBSD12
@@ -1197,17 +1273,15 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 			error = ENXIO;
 			break;
 		}
-		if (nd_enabled) {
-			nd_enabled = 0;
-			netdump_mbuf_drain();
-		}
+		if (netdump_enabled())
+			netdump_unconfigure();
 		break;
 
 	case NETDUMPGCONF_FREEBSD12:
 		gone_in(14, "FreeBSD 12.x ABI compat");
 		conf12 = (void *)addr;
 
-		if (!nd_enabled) {
+		if (!netdump_enabled()) {
 			error = ENXIO;
 			break;
 		}
@@ -1232,7 +1306,7 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 		 * For now, index is ignored; netdump doesn't support multiple
 		 * configurations (yet).
 		 */
-		if (!nd_enabled) {
+		if (!netdump_enabled()) {
 			error = ENXIO;
 			conf = NULL;
 			break;
@@ -1293,13 +1367,10 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 		if (conf->kda_index == KDA_REMOVE ||
 		    conf->kda_index == KDA_REMOVE_DEV ||
 		    conf->kda_index == KDA_REMOVE_ALL) {
-			if (nd_enabled || conf->kda_index == KDA_REMOVE_ALL) {
-				error = dumper_remove(conf->kda_iface, conf);
-				if (error == 0) {
-					nd_enabled = 0;
-					netdump_mbuf_drain();
-				}
-			}
+			if (netdump_enabled())
+				netdump_unconfigure();
+			if (conf->kda_index == KDA_REMOVE_ALL)
+				error = dumper_remove(NULL, conf);
 			break;
 		}
 
@@ -1342,10 +1413,8 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 			    conf->kda_encryptedkeysize);
 			free(encryptedkey, M_TEMP);
 		}
-		if (error != 0) {
-			nd_enabled = 0;
-			netdump_mbuf_drain();
-		}
+		if (error != 0)
+			netdump_unconfigure();
 		break;
 	default:
 		error = ENOTTY;
@@ -1354,6 +1423,7 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, c
 	explicit_bzero(&kda_copy, sizeof(kda_copy));
 	if (conf != NULL)
 		explicit_bzero(conf, sizeof(*conf));
+	NETDUMP_WUNLOCK();
 	return (error);
 }
 
@@ -1385,6 +1455,9 @@ netdump_modevent(module_t mod __unused, int what, void
 		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);
@@ -1404,23 +1477,21 @@ netdump_modevent(module_t mod __unused, int what, void
 			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:
-		if (nd_enabled) {
-			struct diocskerneldump_arg kda;
-
+		NETDUMP_WLOCK();
+		if (netdump_enabled()) {
 			printf("netdump: disabling dump device for unload\n");
-
-			bzero(&kda, sizeof(kda));
-			kda.kda_index = KDA_REMOVE_DEV;
-			(void)dumper_remove(nd_conf.ndc_iface, &kda);
-
-			netdump_mbuf_drain();
-			nd_enabled = 0;
+			netdump_unconfigure();
 		}
+		NETDUMP_WUNLOCK();
 		destroy_dev(netdump_cdev);
+		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
+		    nd_detach_cookie);
 		break;
 	default:
 		error = EOPNOTSUPP;


More information about the svn-src-head mailing list