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;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to