summaryrefslogtreecommitdiffstats
path: root/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
authorwollman <wollman@FreeBSD.org>1998-05-15 20:11:40 +0000
committerwollman <wollman@FreeBSD.org>1998-05-15 20:11:40 +0000
commitbbc4497adab2d7702eab9a609897b2e5f672289e (patch)
tree10594c024ae545493609ccaa7de3d90faf27616a /sys/kern/uipc_usrreq.c
parentbe2e5ffcfc349cb4483b536bd57048eca466e0c9 (diff)
downloadFreeBSD-src-bbc4497adab2d7702eab9a609897b2e5f672289e.zip
FreeBSD-src-bbc4497adab2d7702eab9a609897b2e5f672289e.tar.gz
Convert socket structures to be type-stable and add a version number.
Define a parameter which indicates the maximum number of sockets in a system, and use this to size the zone allocators used for sockets and for certain PCBs. Convert PF_LOCAL PCB structures to be type-stable and add a version number. Define an external format for infomation about socket structures and use it in several places. Define a mechanism to get all PF_LOCAL and PF_INET PCB lists through sysctl(3) without blocking network interrupts for an unreasonable length of time. This probably still has some bugs and/or race conditions, but it seems to work well enough on my machines. It is now possible for `netstat' to get almost all of its information via the sysctl(3) interface rather than reading kmem (changes to follow).
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r--sys/kern/uipc_usrreq.c161
1 files changed, 138 insertions, 23 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index b225c7c..3921513 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* From: @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94
- * $Id: uipc_usrreq.c,v 1.33 1998/04/17 22:36:50 des Exp $
+ * $Id: uipc_usrreq.c,v 1.34 1998/05/07 04:58:21 msmith Exp $
*/
#include <sys/param.h>
@@ -42,6 +42,7 @@
#include <sys/malloc.h> /* XXX must be before <sys/file.h> */
#include <sys/file.h>
#include <sys/filedesc.h>
+#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/namei.h>
#include <sys/proc.h>
@@ -51,8 +52,17 @@
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/un.h>
+#include <sys/unpcb.h>
#include <sys/vnode.h>
+#include <vm/vm_zone.h>
+
+struct vm_zone *unp_zone;
+static unp_gen_t unp_gencnt;
+static u_int unp_count;
+
+static struct unp_head unp_shead, unp_dhead;
+
/*
* Unix communications domain.
*
@@ -60,6 +70,7 @@
* SEQPACKET, RDM
* rethink name space problems
* need a proper out-of-band
+ * lock pushdown
*/
static struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL };
static ino_t unp_ino; /* prototype for fake inode numbers */
@@ -468,12 +479,17 @@ unp_attach(so)
if (error)
return (error);
}
- MALLOC(unp, struct unpcb *, sizeof *unp, M_PCB, M_NOWAIT);
+ unp = zalloc(unp_zone);
if (unp == NULL)
return (ENOBUFS);
bzero(unp, sizeof *unp);
- so->so_pcb = (caddr_t)unp;
+ unp->unp_gencnt = ++unp_gencnt;
+ unp_count++;
+ LIST_INIT(&unp->unp_refs);
unp->unp_socket = so;
+ LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead
+ : &unp_shead, unp, unp_link);
+ so->so_pcb = (caddr_t)unp;
return (0);
}
@@ -481,6 +497,9 @@ static void
unp_detach(unp)
register struct unpcb *unp;
{
+ LIST_REMOVE(unp, unp_link);
+ unp->unp_gencnt = ++unp_gencnt;
+ --unp_count;
if (unp->unp_vnode) {
unp->unp_vnode->v_socket = 0;
vrele(unp->unp_vnode);
@@ -488,8 +507,8 @@ unp_detach(unp)
}
if (unp->unp_conn)
unp_disconnect(unp);
- while (unp->unp_refs)
- unp_drop(unp->unp_refs, ECONNRESET);
+ while (unp->unp_refs.lh_first)
+ unp_drop(unp->unp_refs.lh_first, ECONNRESET);
soisdisconnected(unp->unp_socket);
unp->unp_socket->so_pcb = 0;
if (unp_rights) {
@@ -505,7 +524,7 @@ unp_detach(unp)
}
if (unp->unp_addr)
FREE(unp->unp_addr, M_SONAME);
- FREE(unp, M_PCB);
+ zfree(unp_zone, unp);
}
static int
@@ -637,8 +656,7 @@ unp_connect2(so, so2)
switch (so->so_type) {
case SOCK_DGRAM:
- unp->unp_nextref = unp2->unp_refs;
- unp2->unp_refs = unp;
+ LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink);
soisconnected(so);
break;
@@ -666,20 +684,7 @@ unp_disconnect(unp)
switch (unp->unp_socket->so_type) {
case SOCK_DGRAM:
- if (unp2->unp_refs == unp)
- unp2->unp_refs = unp->unp_nextref;
- else {
- unp2 = unp2->unp_refs;
- for (;;) {
- if (unp2 == 0)
- panic("unp_disconnect");
- if (unp2->unp_nextref == unp)
- break;
- unp2 = unp2->unp_nextref;
- }
- unp2->unp_nextref = unp->unp_nextref;
- }
- unp->unp_nextref = 0;
+ LIST_REMOVE(unp, unp_reflink);
unp->unp_socket->so_state &= ~SS_ISCONNECTED;
break;
@@ -701,6 +706,103 @@ unp_abort(unp)
}
#endif
+static int
+unp_pcblist SYSCTL_HANDLER_ARGS
+{
+ int error, i, n, s;
+ struct unpcb *unp, **unp_list;
+ unp_gen_t gencnt;
+ struct xunpgen xug;
+ struct unp_head *head;
+
+ head = ((long)arg1 == SOCK_DGRAM ? &unp_dhead : &unp_shead);
+
+ /*
+ * The process of preparing the PCB list is too time-consuming and
+ * resource-intensive to repeat twice on every request.
+ */
+ if (req->oldptr == 0) {
+ n = unp_count;
+ req->oldidx = 2 * (sizeof xug)
+ + (n + n/8) * sizeof(struct xunpcb);
+ return 0;
+ }
+
+ if (req->newptr != 0)
+ return EPERM;
+
+ /*
+ * OK, now we're committed to doing something.
+ */
+ gencnt = unp_gencnt;
+ n = unp_count;
+
+ xug.xug_len = sizeof xug;
+ xug.xug_count = n;
+ xug.xug_gen = gencnt;
+ xug.xug_sogen = so_gencnt;
+ error = SYSCTL_OUT(req, &xug, sizeof xug);
+ if (error)
+ return error;
+
+ unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK);
+ if (unp_list == 0)
+ return ENOMEM;
+
+ for (unp = head->lh_first, i = 0; unp && i < n;
+ unp = unp->unp_link.le_next) {
+ if (unp->unp_gencnt <= gencnt)
+ unp_list[i++] = unp;
+ }
+ n = i; /* in case we lost some during malloc */
+
+ error = 0;
+ for (i = 0; i < n; i++) {
+ unp = unp_list[i];
+ if (unp->unp_gencnt <= gencnt) {
+ struct xunpcb xu;
+ xu.xu_len = sizeof xu;
+ xu.xu_unpp = unp;
+ /*
+ * XXX - need more locking here to protect against
+ * connect/disconnect races for SMP.
+ */
+ if (unp->unp_addr)
+ bcopy(unp->unp_addr, &xu.xu_addr,
+ unp->unp_addr->sun_len);
+ if (unp->unp_conn && unp->unp_conn->unp_addr)
+ bcopy(unp->unp_conn->unp_addr,
+ &xu.xu_caddr,
+ unp->unp_conn->unp_addr->sun_len);
+ bcopy(unp, &xu.xu_unp, sizeof *unp);
+ sotoxsocket(unp->unp_socket, &xu.xu_socket);
+ error = SYSCTL_OUT(req, &xu, sizeof xu);
+ }
+ }
+ if (!error) {
+ /*
+ * Give the user an updated idea of our state.
+ * If the generation differs from what we told
+ * her before, she knows that something happened
+ * while we were processing this request, and it
+ * might be necessary to retry.
+ */
+ xug.xug_gen = unp_gencnt;
+ xug.xug_sogen = so_gencnt;
+ xug.xug_count = unp_count;
+ error = SYSCTL_OUT(req, &xug, sizeof xug);
+ }
+ free(unp_list, M_TEMP);
+ return error;
+}
+
+SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD,
+ (caddr_t)(long)SOCK_DGRAM, 0, unp_pcblist, "S,xunpcb",
+ "List of active local datagram sockets");
+SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist, CTLFLAG_RD,
+ (caddr_t)(long)SOCK_STREAM, 0, unp_pcblist, "S,xunpcb",
+ "List of active local stream sockets");
+
static void
unp_shutdown(unp)
struct unpcb *unp;
@@ -722,10 +824,13 @@ unp_drop(unp, errno)
so->so_error = errno;
unp_disconnect(unp);
if (so->so_head) {
+ LIST_REMOVE(unp, unp_link);
+ unp->unp_gencnt = ++unp_gencnt;
+ unp_count--;
so->so_pcb = (caddr_t) 0;
if (unp->unp_addr)
FREE(unp->unp_addr, M_SONAME);
- FREE(unp, M_PCB);
+ zfree(unp_zone, unp);
sofree(so);
}
}
@@ -779,6 +884,16 @@ unp_externalize(rights)
return (0);
}
+void
+unp_init(void)
+{
+ unp_zone = zinit("unpcb", sizeof(struct unpcb), nmbclusters, 0, 0);
+ if (unp_zone == 0)
+ panic("unp_init");
+ LIST_INIT(&unp_dhead);
+ LIST_INIT(&unp_shead);
+}
+
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
OpenPOWER on IntegriCloud