inpcb: Save UDP inpcb into temporary memory during in_pcblist
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 21 Jan 2011 05:30:19 +0000 (13:30 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 31 Jan 2011 01:46:08 +0000 (09:46 +0800)
The temorary memory is used later to do the SYSCTL_OUT without
the udbinfo serializer being held.  Mainly to avoid deadlock
triggered by holding serializer and copyout.

Reminded-by: dillon@
sys/netinet/in_pcb.c
sys/netinet/in_pcb.h
sys/netinet/udp_usrreq.c

index a92c486..e82671b 100644 (file)
@@ -1421,12 +1421,15 @@ in_pcblist_global(SYSCTL_HANDLER_ARGS)
 }
 
 int
-in_pcblist_global_nomarker(SYSCTL_HANDLER_ARGS)
+in_pcblist_global_nomarker(SYSCTL_HANDLER_ARGS, struct xinpcb **xi0, int *nxi0)
 {
        struct inpcbinfo *pcbinfo = arg1;
        struct inpcb *inp;
-       struct xinpcb xi;
-       int error;
+       struct xinpcb *xi;
+       int nxi;
+
+       *nxi0 = 0;
+       *xi0 = NULL;
 
        /*
         * The process of preparing the PCB list is too time-consuming and
@@ -1442,17 +1445,35 @@ in_pcblist_global_nomarker(SYSCTL_HANDLER_ARGS)
        if (req->newptr != NULL)
                return EPERM;
 
-       error = 0;
+       if (pcbinfo->ipi_count == 0)
+               return 0;
+
+       nxi = 0;
+       xi = kmalloc(pcbinfo->ipi_count * sizeof(*xi), M_TEMP,
+                    M_WAITOK | M_ZERO | M_NULLOK);
+       if (xi == NULL)
+               return ENOMEM;
+
        LIST_FOREACH(inp, &pcbinfo->pcblisthead, inp_list) {
+               struct xinpcb *xi_ptr = &xi[nxi];
+
                if (prison_xinpcb(req->td, inp))
                        continue;
-               bzero(&xi, sizeof xi);
-               xi.xi_len = sizeof xi;
-               bcopy(inp, &xi.xi_inp, sizeof *inp);
+
+               xi_ptr->xi_len = sizeof(*xi_ptr);
+               bcopy(inp, &xi_ptr->xi_inp, sizeof(*inp));
                if (inp->inp_socket)
-                       sotoxsocket(inp->inp_socket, &xi.xi_socket);
-               if ((error = SYSCTL_OUT(req, &xi, sizeof xi)) != 0)
-                       break;
+                       sotoxsocket(inp->inp_socket, &xi_ptr->xi_socket);
+               ++nxi;
        }
-       return error;
+
+       if (nxi == 0) {
+               kfree(xi, M_TEMP);
+               return 0;
+       }
+
+       *nxi0 = nxi;
+       *xi0 = xi;
+
+       return 0;
 }
index 8286e39..43e2bf2 100644 (file)
@@ -402,6 +402,7 @@ extern int  ipport_hifirstauto;
 extern int     ipport_hilastauto;
 
 union netmsg;
+struct xinpcb;
 
 void   in_pcbpurgeif0 (struct inpcb *, struct ifnet *);
 void   in_losing (struct inpcb *);
@@ -441,7 +442,8 @@ void        in_pcbremlists (struct inpcb *inp);
 int    prison_xinpcb (struct thread *p, struct inpcb *inp);
 
 int    in_pcblist_global(SYSCTL_HANDLER_ARGS);
-int    in_pcblist_global_nomarker(SYSCTL_HANDLER_ARGS);
+int    in_pcblist_global_nomarker(SYSCTL_HANDLER_ARGS,
+           struct xinpcb **, int *);
 #endif /* _KERNEL */
 
 #endif /* !_NETINET_IN_PCB_H_ */
index 6fe30e6..3ba2b28 100644 (file)
@@ -719,12 +719,29 @@ done:
 static int
 udp_pcblist(SYSCTL_HANDLER_ARGS)
 {
-       int error;
+       struct xinpcb *xi;
+       int error, nxi, i;
 
        udbinfo_lock();
-       error = in_pcblist_global_nomarker(oidp, arg1, arg2, req);
+       error = in_pcblist_global_nomarker(oidp, arg1, arg2, req, &xi, &nxi);
        udbinfo_unlock();
 
+       if (error) {
+               KKASSERT(xi == NULL);
+               return error;
+       }
+       if (nxi == 0) {
+               KKASSERT(xi == NULL);
+               return 0;
+       }
+
+       for (i = 0; i < nxi; ++i) {
+               error = SYSCTL_OUT(req, &xi[i], sizeof(xi[i]));
+               if (error)
+                       break;
+       }
+       kfree(xi, M_TEMP);
+
        return error;
 }
 SYSCTL_PROC(_net_inet_udp, UDPCTL_PCBLIST, pcblist, CTLFLAG_RD, &udbinfo, 0,