Subject: Fully sysctl-aware ld.elf_so, second try
To: None <tech-userlevel@netbsd.org>
From: Quentin Garnier <cube@NetBSD.org>
List: tech-userlevel
Date: 06/30/2004 15:57:03
This is a multi-part message in MIME format.

--Multipart=_Wed__30_Jun_2004_15_57_03_+0200_ijuKTQp1FOZHi.EG
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit

I've been doing a lot of tests with the attached patch, including
several full builds of NetBSD, and the conclusion is that there is no
real speed difference between the old ld.elf_so and the patched one.

The difference between the two always measures in less than 1%, and the
general trend is that the new one wins in the situations where sysctl()
is not used, which means when /etc/ld.so.conf is empty (all archs but
i386 and sparc64), and when the loaded binary is not linked against any
of the libs listed in ld.so.conf (all non-libm binaries in i386).

When sysctl() is used, the patched one tends to be slightly slower, but
it's barely noticeable.  After 100000 execs in a row (all of them
inducing sysctl() calls), the patched might even get faster from time to
time, but in average is slower by about 0.20%.  My test system is a
i386, so this might be different on archs where making a syscall costs
more.

The resulting binary is slightly smaller (about 600 bytes according to
size(1)).

I plan to commit the change in the next few days, unless points against
it are raised.

Quentin Garnier.

--Multipart=_Wed__30_Jun_2004_15_57_03_+0200_ijuKTQp1FOZHi.EG
Content-Type: text/plain;
 name="ld.elf_so.diff"
Content-Disposition: attachment;
 filename="ld.elf_so.diff"
Content-Transfer-Encoding: 7bit

Index: load.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/load.c,v
retrieving revision 1.27
diff -u -r1.27 load.c
--- load.c	25 Nov 2003 14:36:49 -0000	1.27
+++ load.c	29 Jun 2004 07:35:01 -0000
@@ -179,6 +179,7 @@
 	bool got = false;
 	union {
 		int i;
+		u_quad_t q;
 		char s[16];
 	} val;
 
@@ -187,22 +188,24 @@
 		if (strcmp(x->name, name) != 0)
 			continue;
 
-		i = sizeof(val);
-
-		if (sysctl(x->ctl, x->ctlmax, &val, &i, NULL, 0) == -1) {
-			xwarnx(_PATH_LD_HINTS ": unknown sysctl for %s", name);
+		j = sizeof(val);
+		if ((i = _rtld_sysctl(x->ctlname, &val, &j)) == -1) {
+			xwarnx(_PATH_LD_HINTS ": invalid/unknown sysctl for %s (%d)",
+			    name, errno);
 			break;
 		}
 
-		switch (x->ctltype[x->ctlmax - 1]) {
+		switch (i) {
+		case CTLTYPE_QUAD:
+			xsnprintf(val.s, sizeof(val.s), "%" PRIu64, val.q);
+			break;
 		case CTLTYPE_INT:
 			xsnprintf(val.s, sizeof(val.s), "%d", val.i);
 			break;
 		case CTLTYPE_STRING:
 			break;
 		default:
-			xwarnx("unsupported sysctl type %d",
-			    x->ctltype[x->ctlmax - 1]);
+			xwarnx("unsupported sysctl type %d", (int)i);
 			break;
 		}
 
Index: paths.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/paths.c,v
retrieving revision 1.30
diff -u -r1.30 paths.c
--- paths.c	16 Mar 2004 05:25:12 -0000	1.30
+++ paths.c	29 Jun 2004 07:35:01 -0000
@@ -216,49 +216,6 @@
 	}
 }
 
-struct list {
-	const struct ctlname *ctl;
-	int numentries;
-};
-
-#ifdef CTL_MACHDEP_NAMES
-static const struct ctlname ctl_machdep[] = CTL_MACHDEP_NAMES;
-#endif
-static const struct ctlname ctl_toplvl[] = CTL_NAMES;
-
-const struct list toplevel[] = {
-	{ 0, 0 },
-	{ ctl_toplvl, CTL_MAXID },
-	{ 0, -1 },
-};
-
-const struct list secondlevel[] = {
-	{ 0, 0 },			/* CTL_UNSPEC */
-	{ 0, KERN_MAXID },		/* CTL_KERN */
-	{ 0, VM_MAXID },		/* CTL_VM */
-	{ 0, VFS_MAXID },		/* CTL_VFS */
-	{ 0, NET_MAXID },		/* CTL_NET */
-	{ 0, CTL_DEBUG_MAXID },		/* CTL_DEBUG */
-	{ 0, HW_MAXID },		/* CTL_HW */
-#ifdef CTL_MACHDEP_NAMES
-	{ ctl_machdep, CPU_MAXID },	/* CTL_MACHDEP */
-#else
-	{ 0, 0 },			/* CTL_MACHDEP */
-#endif
-	{ 0, USER_MAXID },		/* CTL_USER_NAMES */
-	{ 0, DDBCTL_MAXID },		/* CTL_DDB_NAMES */
-	{ 0, 2 },			/* dummy name */
-	{ 0, -1 },
-};
-
-const struct list *lists[] = {
-	toplevel,
-	secondlevel,
-	0
-};
-
-#define CTL_MACHDEP_SIZE (sizeof(ctl_machdep) / sizeof(ctl_machdep[0]))
-
 /*
  * Process library mappings of the form:
  *	<library_name>	<machdep_variable> <value,...:library_name,...> ... 
@@ -268,7 +225,7 @@
 {
 	Library_Xform *hwptr = NULL;
 	const char *ptr, *key, *ekey, *lib, *elib, *l;
-	int i, j, k;
+	int i, j;
 	
 	dbg((" processing mapping \"%.*s\"", (int)(ep - bp), bp));
 
@@ -290,37 +247,7 @@
 
 	dbg((" sysctl \"%.*s\"", (int)(bp - ptr), ptr));
 
-	for (i = 0; (l = getstr(&ptr, bp, ".")) != NULL; i++, ptr++) {
-
-		if (lists[i] == NULL || i >= RTLD_MAX_CTL) {
-			xwarnx("sysctl nesting too deep");
-			goto cleanup;
-		}
-
-		for (j = 1; lists[i][j].numentries != -1; j++) {
-
-			if (lists[i][j].ctl == NULL)
-				continue;
-
-			for (k = 1; k < lists[i][j].numentries; k++)
-				if (matchstr(lists[i][j].ctl[k].ctl_name, l,
-				    ptr))
-					break;
-
-			if (lists[i][j].numentries == -1) {
-				xwarnx("unknown sysctl variable name `%.*s'",
-				    (int)(ptr - l), l);
-				goto cleanup;
-			}
-
-			hwptr->ctl[hwptr->ctlmax] = k;
-			hwptr->ctltype[hwptr->ctlmax++] =
-			    lists[i][j].ctl[k].ctl_type;
-		}
-	}
-
-	for (i = 0; i < hwptr->ctlmax; i++)
-		dbg((" sysctl %d, %d", hwptr->ctl[i], hwptr->ctltype[i]));
+	hwptr->ctlname = exstrdup(ptr, bp);
 
 	for (i = 0; bp++, (ptr = getword(&bp, ep, WS)) != NULL;) {
 		dbg((" ptr = %.*s", (int)(bp - ptr), ptr));
@@ -456,3 +383,69 @@
 
 	(void)munmap(buf, sz);
 }
+
+/* Basic name -> sysctl MIB translation */
+int
+_rtld_sysctl(const char *name, void *oldp, size_t *oldlen)
+{
+	const char *node, *ep;
+	struct sysctlnode query, *result, *newresult;
+	int mib[CTL_MAXNAME], i, r;
+	size_t res_size, n;
+	u_int miblen = 0;
+
+	/* Start with 16 entries, will grow it up as needed. */
+	res_size = 16 * sizeof(struct sysctlnode);
+	result = (struct sysctlnode *)malloc(res_size);
+	if (result == NULL)
+		return (-1);
+
+	ep = name + strlen(name);
+	do {
+		while (*name == '/' || *name == '.')
+			name++;
+		if (name >= ep)
+			break;
+
+		mib[miblen] = CTL_QUERY;
+		memset(&query, 0, sizeof(query));
+		query.sysctl_flags = SYSCTL_VERSION;
+
+		if (sysctl(mib, miblen+1, result, &res_size, &query,
+		    sizeof(query)) == -1) {
+			if (errno != ENOMEM)
+				goto bad;
+			/* Grow up result */
+			newresult = (struct sysctlnode *)realloc(result, res_size);
+			if (newresult == NULL)
+				goto bad;
+			result = newresult;
+			if (sysctl(mib, miblen+1, result, &res_size, &query,
+			    sizeof(query)) == -1)
+				goto bad;
+		}
+		n = res_size / sizeof(struct sysctlnode);
+
+		node = getstr(&name, ep, "./");
+
+		for (i = 0; i < n; i++)
+			if (matchstr(result[i].sysctl_name, node, name)) {
+				mib[miblen] = result[i].sysctl_num;
+				miblen++;
+				break;
+			}
+	} while (name < ep && miblen <= CTL_MAXNAME);
+
+	if (name < ep)
+		goto bad;
+	r = SYSCTL_TYPE(result[i].sysctl_flags);
+
+	free(result);
+	if (sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1)
+		return (-1);
+	return r;
+
+bad:
+	free(result);
+	return (-1);
+}
Index: rtld.h
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/rtld.h,v
retrieving revision 1.70
diff -u -r1.70 rtld.h
--- rtld.h	12 Aug 2003 09:18:49 -0000	1.70
+++ rtld.h	29 Jun 2004 07:35:01 -0000
@@ -97,9 +97,7 @@
 typedef struct _rtld_library_xform_t {
 	struct _rtld_library_xform_t *next;
 	char *name;
-	int ctl[RTLD_MAX_CTL];
-	int ctltype[RTLD_MAX_CTL];
-	int ctlmax;
+	const char *ctlname;
 	struct {
 		char *value;
 		char *library[RTLD_MAX_LIBRARY];
@@ -243,6 +241,7 @@
 /* path.c */
 void _rtld_add_paths(Search_Path **, const char *);
 void _rtld_process_hints(Search_Path **, Library_Xform **, const char *);
+int _rtld_sysctl(const char *, void *, size_t *);
 
 /* reloc.c */
 int _rtld_do_copy_relocations(const Obj_Entry *);

--Multipart=_Wed__30_Jun_2004_15_57_03_+0200_ijuKTQp1FOZHi.EG--