[PATCH] Dynamic kernel environment support (kgetenv() and friends), ACPI quirks

Thomas E. Spanjaard tgen at netphreax.net
Fri Jan 12 11:04:46 PST 2007


Attached patch adds dynamic kernel enviroment support, adapted from 
FreeBSD; this allows us to actually be able to use ACPI quirks. As such 
I also introduce an ACPI quirk entry for the VMware ACPI-fast24 timer, 
which runs like mad.

If noone objects, I'll commit tomorrow.

Cheers,
--
        Thomas E. Spanjaard
        tgen at netphreax.net
diff -r c63dd4a6b22d sys/conf/options
--- a/sys/conf/options	Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/conf/options	Fri Jan 12 13:23:27 2007 +0000
@@ -77,6 +77,7 @@ TWA_FLASH_FIRMWARE	opt_twa.h
 #options for ACPI support
 ACPI_DEBUG		opt_acpi.h
 ACPI_NO_SEMAPHORES	opt_acpi.h
+ACPI_QUIRK_VMWARE	opt_acpi.h
 
 # Miscellaneous options.
 COMPAT_DF12	opt_compatdf12.h
diff -r c63dd4a6b22d sys/dev/acpica5/acpi.c
--- a/sys/dev/acpica5/acpi.c	Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/dev/acpica5/acpi.c	Sat Jan 20 05:52:40 2007 +0000
@@ -31,6 +31,7 @@
  */
 
 #include "opt_acpi.h"
+
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/proc.h>
@@ -81,6 +82,11 @@ struct mtx	acpi_mutex;
 struct mtx	acpi_mutex;
 #endif
 
+/*
+ * XXX TGEN We should allow for quirks to be, optionally, more specific
+ * regarding hardware they match, e.g. specifying CreatorId and
+ * CreatorRevision.
+ */
 struct acpi_quirks {
     char	*OemId;
     uint32_t	OemRevision;
@@ -94,7 +100,18 @@ static struct acpi_quirks acpi_quirks_ta
     /* Bad PCI routing table.  Used on some SuperMicro boards. */
     { "PTLTD ", 0x06040000, "pci_link" },
 #endif
-
+#ifdef ACPI_QUIRK_VMWARE
+    /*
+     * VMware's ACPI-fast24 timer runs roughly 65 times too fast, and not
+     * predictably / monotonic either. This is observed at least under SMP
+     * conditions.
+     *
+     * NOTE: this combination of OemId and OemRevision is NOT unique; it's also
+     * known and/or suspected that at least some SuperMicro boards and the
+     * Compaq Presario 1952 use this combination.
+     */
+    { "PTLTD ", 0x06040000, "timer" },
+#endif /* ACPI_QUIRK_VMWARE */
     { NULL, 0, NULL }
 };
 
@@ -652,7 +669,7 @@ acpi_quirks_set(void)
 	/* XXX No strcasecmp but this is good enough. */
 	if (*env == 'Y' || *env == 'y')
 	    goto out;
-	freeenv(env);
+	kfreeenv(env);
     }
     if ((env = kgetenv("debug.acpi.disabled")) != NULL) {
 	if (strstr("quirks", env) != NULL)
@@ -673,9 +690,7 @@ acpi_quirks_set(void)
 		if ((tmp = kmalloc(len, M_TEMP, M_NOWAIT)) == NULL)
 		    goto out;
 		ksprintf(tmp, "%s %s", env ? env : "", quirk->value);
-#ifdef notyet
-		setenv("debug.acpi.disabled", tmp);
-#endif /* notyet */
+		ksetenv("debug.acpi.disabled", tmp);
 		kfree(tmp, M_TEMP);
 		break;
 	}
@@ -683,7 +698,7 @@ acpi_quirks_set(void)
 
 out:
     if (env)
-	freeenv(env);
+	kfreeenv(env);
 }
 
 /*
diff -r c63dd4a6b22d sys/dev/acpica5/acpi_timer.c
--- a/sys/dev/acpica5/acpi_timer.c	Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/dev/acpica5/acpi_timer.c	Fri Jan 12 13:21:16 2007 +0000
@@ -27,7 +27,9 @@
  * $FreeBSD: src/sys/dev/acpica/acpi_timer.c,v 1.33 2004/05/30 20:08:23 phk Exp $
  * $DragonFly: src/sys/dev/acpica5/acpi_timer.c,v 1.11 2006/12/22 23:26:14 swildner Exp $
  */
+
 #include "opt_acpi.h"
+
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/kernel.h>
diff -r c63dd4a6b22d sys/kern/kern_environment.c
--- a/sys/kern/kern_environment.c	Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/kern/kern_environment.c	Mon Jan 22 08:03:13 2007 +0000
@@ -29,23 +29,91 @@
 
 /*
  * The unified bootloader passes us a pointer to a preserved copy of
- * bootstrap/kernel environment variables.
+ * bootstrap/kernel environment variables. We convert them to a dynamic array
+ * of strings later when the VM subsystem is up.
  * We make these available using sysctl for both in-kernel and
- * out-of-kernel consumers.
+ * out-of-kernel consumers, as well as the k{get,set,unset,test,free}env()
+ * functions for in-kernel consumers.
  *
  * Note that the current sysctl infrastructure doesn't allow 
  * dynamic insertion or traversal through handled spaces.  Grr.
+ *
+ * Note also that the dynamic kernel environment space is not yet exported via
+ * sysctl. It should be handled somewhat like kinfo.
  */
 
 #include <sys/param.h>
 #include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/malloc.h>
+#include <sys/spinlock.h>
+#include <sys/spinlock2.h>
 #include <sys/systm.h>
 #include <sys/sysctl.h>
-#include <sys/libkern.h>
-
-char	*kern_envp;
-
-static char	*kernenv_next(char *cp);
+
+/* exported variables */
+char		*kern_envp;		/* sys/systm.h */
+
+/* local variables */
+char		**kenv_dynp;
+int		kenv_isdynamic;
+struct spinlock	kenv_dynlock;
+
+/* constants */
+MALLOC_DEFINE(M_KENV, "kenv", "kernel environment dynamic storage");
+#define	KENV_DYNMAXNUM	512
+#define	KENV_MAXNAMELEN	128
+#define KENV_MAXVALLEN	128
+
+/* local prototypes */
+static char	*kenv_getstring_dynamic(const char *, int *);
+static char	*kenv_getstring_static(const char*);
+static char	*kernenv_next(char *);
+
+/*
+ * Look up a string in the dynamic environment array.
+ *
+ * Must be called with kenv_dynlock held.
+ */
+static char *
+kenv_getstring_dynamic(const char *name, int *idx)
+{
+    char *cp;
+    int len, i;
+
+    len = strlen(name);
+    /* note: kunsetenv() never leaves gaps in the array */
+    for (cp = kenv_dynp[0], i = 0; cp != NULL; cp = kenv_dynp[i++]) {
+        if ((strncmp(cp, name, len) == 0) && (cp[len] == '=')) {
+            if (idx != NULL)
+                *idx = i;
+            return(cp + len + 1);
+        }
+    }
+    return(NULL);
+}
+
+/*
+ * Look up a string in the static environment array.
+ */
+static char *
+kenv_getstring_static(const char *name)
+{
+    char *cp, *ep;
+    int len;
+
+    for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
+        for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
+            ;
+        if (*ep != '=')
+            continue;
+        len = ep - cp;
+        ep++;
+        if (!strncmp(name, cp, len) && name[len] == 0)
+            return(ep);
+    }
+    return(NULL);
+}
 
 /*
  * Look up an environment variable by name.
@@ -53,20 +121,132 @@ char *
 char *
 kgetenv(const char *name)
 {
-    char	*cp, *ep;
-    int		len;
-    
-    for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
-	for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
-	    ;
-	if (*ep != '=')
-	    continue;
-	len = ep - cp;
-	ep++;
-	if (!strncmp(name, cp, len) && name[len] == 0)
-	    return(ep);
-    }
-    return(NULL);
+    char buf[KENV_MAXNAMELEN + 1 + KENV_MAXVALLEN + 1];
+    char *ret, *cp;
+    int len;
+
+    if (kenv_isdynamic) {
+        spin_lock_wr(&kenv_dynlock);
+        cp = kenv_getstring_dynamic(name, NULL);
+        if (cp != NULL) {
+            strcpy(buf, cp);
+            spin_unlock_wr(&kenv_dynlock);
+            len = strlen(buf) + 1;
+            ret = kmalloc(len, M_KENV, M_WAITOK);
+            strcpy(ret, buf);
+        } else {
+            spin_unlock_wr(&kenv_dynlock);
+            ret = NULL;
+        }
+    } else
+        ret = kenv_getstring_static(name);
+    return(ret);
+}
+
+/*
+ * Set an environment variable by name.
+ */
+int
+ksetenv(const char *name, const char *value)
+{
+    char *buf, *cp, *oldenv;
+    int namelen, vallen, i;
+
+    if (kenv_isdynamic) {
+        namelen = strlen(name) + 1;
+	vallen = strlen(value) + 1;
+	if ((namelen > KENV_MAXNAMELEN) || (vallen > KENV_MAXVALLEN))
+            return(-1);
+	buf = kmalloc(namelen + vallen, M_KENV, M_WAITOK);
+	ksprintf(buf, "%s=%s", name, value);
+
+	spin_lock_wr(&kenv_dynlock);
+	cp = kenv_getstring_dynamic(name, &i);
+	if (cp != NULL) {
+            /* replace existing environment variable */
+            oldenv = kenv_dynp[i];
+	    kenv_dynp[i] = buf;
+	    spin_unlock_wr(&kenv_dynlock);
+	    kfree(oldenv, M_KENV);
+	} else {
+            /* new environment variable: find first free slot */
+            for (i = 0; (cp = kenv_dynp[i]) != NULL; i++)
+                ;
+
+	    /* bounds checking */
+	    if (i < 0 || i >= (KENV_DYNMAXNUM - 1)) {
+                kfree(buf, M_KENV);
+		spin_unlock_wr(&kenv_dynlock);
+		return(-1);
+	    }
+
+	    kenv_dynp[i] = buf;
+	    kenv_dynp[i + 1] = NULL;
+	    spin_unlock_wr(&kenv_dynlock);
+	}
+	return(0);
+    } else {
+        kprintf("WARNING: ksetenv: dynamic array not created yet\n");
+	return(-1);
+    }
+}
+
+/*
+ * Unset an environment variable by name.
+ */
+int
+kunsetenv(const char *name)
+{
+    char *cp, *oldenv;
+    int i, j;
+
+    if (kenv_isdynamic) {
+        spin_lock_wr(&kenv_dynlock);
+	cp = kenv_getstring_dynamic(name, &i);
+	if (cp != NULL) {
+            oldenv = kenv_dynp[i];
+	    for (j = i + 1; kenv_dynp[j] != NULL; j++)
+                kenv_dynp[i++] = kenv_dynp[j];
+	    kenv_dynp[i] = NULL;
+	    spin_unlock_wr(&kenv_dynlock);
+	    kfree(oldenv, M_KENV);
+	    return(0);
+	}
+	spin_unlock_wr(&kenv_dynlock);
+	return(-1);
+    } else {
+        kprintf("WARNING: kunsetenv: dynamic array not created yet\n");
+	return(-1);
+    }
+}
+
+/*
+ * Free an environment variable that has been copied for a consumer.
+ */
+void
+kfreeenv(char *env)
+{
+    if (kenv_isdynamic)
+        kfree(env, M_KENV);
+}
+
+/*
+ * Test if an environment variable is defined.
+ */
+int
+ktestenv(const char *name)
+{
+    char *cp;
+
+    if (kenv_isdynamic) {
+        spin_lock_wr(&kenv_dynlock);
+	cp = kenv_getstring_dynamic(name, NULL);
+	spin_unlock_wr(&kenv_dynlock);
+    } else
+        cp = kenv_getstring_static(name);
+    if (cp != NULL)
+        return(1);
+    return(0);
 }
 
 /*
@@ -81,6 +261,7 @@ kgetenv_string(const char *name, char *d
     if (tmp != NULL) {
 	strncpy(data, tmp, size);
 	data[size - 1] = 0;
+	kfreeenv(tmp);
 	return (1);
     } else
 	return (0);
@@ -116,15 +297,21 @@ kgetenv_quad(const char *name, quad_t *d
 	return(0);
     
     iv = strtoq(value, &vtp, 0);
-    if ((vtp == value) || (*vtp != '\0'))
+    if ((vtp == value) || (*vtp != '\0')) {
+       kfreeenv(value);
 	return(0);
+    }
     
     *data = iv;
+    kfreeenv(value);
     return(1);
 }
 
+/*
+ * Boottime kernel environment sysctl handler.
+ */
 static int
-sysctl_kernenv(SYSCTL_HANDLER_ARGS)
+sysctl_kenv_boot(SYSCTL_HANDLER_ARGS)
 {
     int		*name = (int *)arg1;
     u_int	namelen = arg2;
@@ -153,8 +340,46 @@ sysctl_kernenv(SYSCTL_HANDLER_ARGS)
     error = SYSCTL_OUT(req, cp, strlen(cp) + 1);
     return (error);
 }
-
-SYSCTL_NODE(_kern, OID_AUTO, environment, CTLFLAG_RD, sysctl_kernenv, "kernel environment space");
+SYSCTL_NODE(_kern, OID_AUTO, environment, CTLFLAG_RD, sysctl_kenv_boot,
+	    "kernel boot environment space");
+
+#ifdef notyet
+/*
+ * Runtime kernel environment sysctl handler.
+ */
+static int
+sysctl_kenv(SYSCTL_HANDLER_ARGS)
+{
+    int		*name = (int *)arg1;
+    u_int	namelen = arg2;
+    char	*bufp;
+    ptr_t	buf;
+    int		total, i, len;
+
+    if (kenv_isdynamic) {
+        spin_lock_wr(&kenv_dynlock);
+        if (oidp->oid_number == KERN_ENV_ALL) {
+            total = 0;
+            for (i = 0; kenv_dynp[i] != NULL; i++) {
+                len = strlen(kenv_dynp[i]) + 1;
+		total += len;
+	    }
+	    bufp = kmalloc(total, M_KENV, M_WAITOK);
+	    buf = bufp
+	    for (i = 0; kenv_dynp[i] != NULL; i++) {
+                len = strlen(kenv_dynp[i]) + 1;
+		strcpy(buf, kenv_dynp[i]);
+		buf += len;
+	    }
+	    error = SYSCTL_OUT(req, bufp, total);
+	    return(error);
+	}
+    }
+    return(-1);
+}
+SYSCTL_NODE(_kern, OID_AUTO, env, CTLFLAG_RD, sysctl_kenv,
+	    "kernel runtime environment space");
+#endif /* notyet */
 
 /*
  * Find the next entry after the one which (cp) falls within, return a
@@ -197,3 +422,54 @@ tunable_str_init(void *data)
 	TUNABLE_STR_FETCH(d->path, d->var, d->size);
 }
 
+/*
+ * Create the dynamic kenv stuff, and copy in the static kenv as passed by the
+ * bootloader.
+ */
+static void
+kenv_init(void *dummy)
+{
+    char *cp;
+    int len, i;
+
+    kenv_dynp = kmalloc(KENV_DYNMAXNUM * sizeof(char *), M_KENV,
+                        M_WAITOK | M_ZERO);
+
+    /* copy the static kenv to our dynamic kenv */
+    i = 0;
+    for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
+        len = strlen(cp) + 1;
+        if (i < (KENV_DYNMAXNUM - 1)) {
+            kenv_dynp[i] = kmalloc(len, M_KENV, M_WAITOK);
+            strcpy(kenv_dynp[i++], cp);
+        } else
+            kprintf("WARNING: kenv: exhausted dynamic storage, "
+                    "ignoring string %s\n", cp);
+    }
+    kenv_dynp[i] = NULL;
+
+    spin_init(&kenv_dynlock);
+    kenv_isdynamic = 1;
+}
+SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, kenv_init, NULL);
+
+/*
+ * Destroy the dynamic kenv stuff.
+ */
+static void
+kenv_uninit(void *dummy)
+{
+    int i;
+
+    if (kenv_isdynamic) {
+        spin_lock_wr(&kenv_dynlock);
+        /* destroy our environment variables */
+        for (i = 0; kenv_dynp[i] != NULL; i++)
+            kfree(kenv_dynp[i], M_KENV);
+	kenv_isdynamic = 0;
+	kfree(kenv_dynp, M_KENV);
+	spin_unlock_wr(&kenv_dynlock);
+	spin_uninit(&kenv_dynlock);
+    }
+}
+SYSUNINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, kenv_uninit, NULL);
diff -r c63dd4a6b22d sys/sys/systm.h
--- a/sys/sys/systm.h	Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/sys/systm.h	Sat Jan 20 04:23:52 2007 +0000
@@ -223,9 +223,19 @@ void	cons_lock(void);
 void	cons_lock(void); 
 void	cons_unlock(void);
 
+/*
+ * Kernel environment support functions and sundry.
+ *
+ * XXX TGEN Fix all calls to freeenv() and testenv() to their k- counterparts,
+ * so that these compat defines can go the way of the bit bucket.
+ */
 char	*kgetenv (const char *name);
-#define	testenv	kgetenv
-#define	freeenv(p)
+int	ksetenv (const char *name, const char *value);
+int	kunsetenv (const char *name);
+void	kfreeenv(char *env);
+#define	freeenv kfreeenv
+int	ktestenv(const char *name);
+#define	testenv	ktestenv
 int	kgetenv_int (const char *name, int *data);
 int	kgetenv_string (const char *name, char *data, int size);
 int	kgetenv_quad (const char *name, quad_t *data);
Attachment:
signature.asc
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pgp00006.pgp
Type: application/octet-stream
Size: 186 bytes
Desc: "Description: OpenPGP digital signature"
URL: <http://lists.dragonflybsd.org/pipermail/kernel/attachments/20070112/e6ae7a2b/attachment-0015.obj>


More information about the Kernel mailing list