Skip to content

Instantly share code, notes, and snippets.

@tuxillo
Created June 11, 2014 23:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuxillo/982cecc71af297dddde8 to your computer and use it in GitHub Desktop.
Save tuxillo/982cecc71af297dddde8 to your computer and use it in GitHub Desktop.
diff --git a/usr.sbin/powerd/Makefile b/usr.sbin/powerd/Makefile
index 2890ca0..6df7072 100644
--- a/usr.sbin/powerd/Makefile
+++ b/usr.sbin/powerd/Makefile
@@ -3,6 +3,7 @@
PROG= powerd
MAN= powerd.8
+SRCS= acpi.c powerd.c powerd.h
LDADD+= -lutil
DPADD+= ${LIBUTIL}
diff --git a/usr.sbin/powerd/acpi.c b/usr.sbin/powerd/acpi.c
new file mode 100644
index 0000000..c9dff9f
--- /dev/null
+++ b/usr.sbin/powerd/acpi.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * by Antonio Huete <tuxillo@quantumachine.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+
+#include "powerd.h"
+
+int
+powerd_acpi_probe(void)
+{
+ char buf[64];
+ int dom;
+
+ for (dom = 0; dom < MAXDOM; dom++) {
+ snprintf(buf, sizeof(buf),
+ "hw.acpi.cpu.px_dom%d.available", dom);
+ if (sysctlbyname(buf, NULL, NULL, NULL, 0) != -1)
+ return 1; /* Found */
+ }
+ return 0; /* Not found */
+}
+
+void
+powerd_acpi_init(void)
+{
+ struct powerd_cpu_domain *cdp;
+ char members[1024];
+ size_t msize;
+ size_t buflen;
+ char buf[128];
+ char *sysid;
+ char *ptr;
+ char *str;
+ int dom;
+ int n;
+ int v;
+
+ SLIST_INIT(&psdevs[ACPIDEV].doms);
+
+ /*
+ * Iterate MAXDOM domains using ACPI P-State sysctls and create a
+ * new structure for every domain found. All domains found will be
+ * on a SLIST for convenience.
+ * Please note that this assumes P-States do not change after they
+ * have been discovered and set to the sysctl.
+ */
+ for (dom = 0; dom < MAXDOM; dom++) {
+ snprintf(buf, sizeof(buf),
+ "hw.acpi.cpu.px_dom%d.available", dom);
+ if (sysctlbyname(buf, NULL, NULL, NULL, 0) != -1) {
+ cdp = malloc(sizeof(*cdp));
+ if (cdp == NULL)
+ err(1, "Out of memory");
+
+ bzero(cdp, sizeof(*cdp));
+ msize = sizeof(members);
+ snprintf(buf, sizeof(buf),
+ "hw.acpi.cpu.px_dom%d.members", dom);
+ if (sysctlbyname(buf, members, &msize, NULL, 0) == 0) {
+ members[msize] = 0;
+ for (str = strtok(members, " "); str;
+ str = strtok(NULL, " ")) {
+ n = -1;
+ sscanf(str, "cpu%d", &n);
+ if (n >= 0)
+ cdp->ncpus++;
+ }
+ }
+
+ asprintf(&sysid, "hw.acpi.cpu.px_dom%d.available", dom);
+ buflen = sizeof(buf) - 1;
+ v = sysctlbyname(sysid, buf, &buflen, NULL, 0);
+ free(sysid);
+ if (v < 0)
+ err(1, "sysctl failed");
+ buf[buflen] = 0; /* NULL termination */
+
+ ptr = buf;
+ while (ptr && (v = strtol(ptr, &ptr, 10)) > 0)
+ psdevs[ACPIDEV].ncpus++;
+
+ SLIST_INSERT_HEAD(&psdevs[ACPIDEV].doms, cdp, entry);
+ }
+ }
+}
+
+int
+powerd_acpi_setfreq(int dom, void *freq)
+{
+ char *buf;
+ size_t buflen;
+ int error;
+ int v;
+
+ error = 0;
+ v = *(int *)(freq);
+
+ buflen = sizeof(v);
+ asprintf(&buf, "hw.acpi.cpu.px_dom%d.select", dom);
+ if ((error = sysctlbyname(buf, NULL, NULL, &v, buflen)) < 0)
+ syslog(LOG_WARN, "dom%d: failed setting state to %d", dom, v);
+
+ return error;
+}
diff --git a/usr.sbin/powerd/clockmod.c b/usr.sbin/powerd/clockmod.c
new file mode 100644
index 0000000..1e9faff
--- /dev/null
+++ b/usr.sbin/powerd/clockmod.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * by Antonio Huete <tuxillo@quantumachine.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+const char *clockmod_fmt[3] = {
+ "machdep.clockmod_dom%d.available",
+ "machdep.clockmod_dom%d.members",
+ "machdep.clockmod_dom%d.select"
+};
+
+int
+powerd_clockmod_probe(void)
+{
+ char buf[64];
+ int dom;
+
+ for (dom = 0; dom < MAXDOM; dom++) {
+ snprintf(buf, sizeof(buf),
+ "machdep.clockmod_dom%d.available", dom);
+ if (sysctlbyname(buf, NULL, NULL, NULL, 0) != -1)
+ return 1; /* Found */
+ }
+ return 0; /* Not found */
+}
+
+void
+powerd_clockmod_init(void)
+{
+}
+
+int
+powerd_clockmod_getcpudom(int dom)
+{
+ return 0;
+}
+
+int
+powerd_clockmod_setfreq(int dom, int freq)
+{
+ return 0;
+}
diff --git a/usr.sbin/powerd/powerd.c b/usr.sbin/powerd/powerd.c
index 0a48c3f..a96b089 100644
--- a/usr.sbin/powerd/powerd.c
+++ b/usr.sbin/powerd/powerd.c
@@ -34,24 +34,12 @@
/*
* The powerd daemon monitors the cpu load and adjusts cpu frequencies
- * via hw.acpi.cpu.px_dom*.
+ * via appropriate sysctls.
*/
+#include "powerd.h"
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/kinfo.h>
-#include <sys/file.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <syslog.h>
-
-static void usage(void);
-static double getcputime(void);
-static void acpi_setcpufreq(int nstate);
-static void setupdominfo(void);
-
+int StateDev = 0;
+int DelaySecs = 60;
int DebugOpt;
int CpuLimit; /* # of cpus at max frequency */
int DomLimit; /* # of domains at max frequency */
@@ -115,10 +103,10 @@ main(int ac, char **av)
}
/*
- * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel
+ * Wait P-State sysctl to be created by kernel.
*
- * Since hw.acpi.cpu.px_dom* creation is queued into ACPI
- * taskqueue and ACPI taskqueue is shared across various
+ * For example, since hw.acpi.cpu.px_dom* creation is queued
+ * into ACPI taskqueue and ACPI taskqueue is shared across various
* ACPI modules, any delay in other modules may cause
* hw.acpi.cpu.px_dom* to be created at quite a later time
* (e.g. cmbat module's task could take quite a lot of time).
@@ -131,14 +119,11 @@ main(int ac, char **av)
getcputime();
savg = 0.0;
- setupdominfo();
- if (DomBeg >= DomEnd) {
- sleep(1);
+ if (setupdominfo() == -1) {
+ sleep(DelaySecs);
continue;
}
- DomLimit = DomEnd;
- CpuLimit = NCpus;
break;
}
@@ -154,15 +139,16 @@ main(int ac, char **av)
savg = (savg * 7.0 + qavg) / 8.0;
nstate = savg / Trigger;
- if (nstate > NCpus)
- nstate = NCpus;
+ if (nstate > psdevs[StateDev].ncpus)
+ nstate = psdevs[StateDev].ncpus;
if (DebugOpt) {
printf("\rqavg=%5.2f savg=%5.2f %2d/%2d ncpus=%d\r",
qavg, savg, CpuLimit, DomLimit, nstate);
fflush(stdout);
}
+
if (nstate != CpuLimit)
- acpi_setcpufreq(nstate);
+ setcpufreq(nstate);
sleep(1);
}
}
@@ -179,45 +165,35 @@ setupdominfo(void)
char members[1024];
char *str;
size_t msize;
+ int domfound = 0;
+ static tries = 0;
int i;
int n;
- for (i = 0; i < 256; ++i) {
- snprintf(buf, sizeof(buf),
- "hw.acpi.cpu.px_dom%d.available", i);
- if (sysctlbyname(buf, NULL, NULL, NULL, 0) >= 0)
- break;
- }
- DomBeg = i;
-
- for (i = 255; i >= DomBeg; --i) {
- snprintf(buf, sizeof(buf),
- "hw.acpi.cpu.px_dom%d.available", i);
- if (sysctlbyname(buf, NULL, NULL, NULL, 0) >= 0) {
- ++i;
- break;
- }
- }
- DomEnd = i;
-
- for (i = DomBeg; i < DomEnd; ++i) {
- snprintf(buf, sizeof(buf),
- "hw.acpi.cpu.px_dom%d.members", i);
- msize = sizeof(members);
- if (sysctlbyname(buf, members, &msize, NULL, 0) == 0) {
- members[msize] = 0;
- for (str = strtok(members, " "); str;
- str = strtok(NULL, " ")) {
- n = -1;
- sscanf(str, "cpu%d", &n);
- if (n >= 0) {
- ++NCpus;
- ++CpuCount[i];
- CpuToDom[n]= i;
- }
- }
+ /* Find out which method to use */
+ if (psdevs[ACPIDEV].probe()) {
+ if (DebugOpt)
+ syslog(LOG_INFO, "Using ACPI P-State sysctl.");
+ StateDev = ACPIDEV;
+ } else if (pstate_dev_avail[CLKMDEV].probe()) {
+ if (DebugOpt)
+ syslog(LOG_INFO, "Using clockmod(4) sysctl.");
+ StateDev = CLKMDEV;
+ } else {
+ if (++tries > 120 && DebugOpt) {
+ tries = 0;
+ syslog(LOG_WARNING, "Can't detect CPU P-State handlers.");
}
+ StateDev = INVLDEV;
+ return;
}
+
+ /*
+ * Initialise the method we found.
+ * XXX Use priorities please
+ */
+ PSDEV_INIT();
+
}
/*
@@ -253,6 +229,31 @@ getcputime(void)
return((double)delta / 1000000.0);
}
+static
+void
+freq_hilo(char *buf, int *hi, int *lo)
+{
+ char *tmp;
+ int v;
+
+ if (buf == NULL)
+ return;
+
+ tmp = strdup(buf);
+ if (active_fmt == clockmod_fmt) {
+ /* Remove % sign for strtol() */
+ while ((tmp = strchr(tmp, '%')) != NULL)
+ *(tmp++) = ' ';
+ }
+
+ while (tmp && (v = strtol(tmp, &tmp, 10)) > 0) {
+ if (*lo == 0 || *lo > v)
+ *lo = v;
+ if (*hi == 0 || *hi < v)
+ *hi = v;
+ }
+}
+
/*
* nstate is the requested number of cpus that we wish to run at full
* frequency. We calculate how many domains we have to adjust to reach
@@ -262,7 +263,7 @@ getcputime(void)
*/
static
void
-acpi_setcpufreq(int nstate)
+setcpufreq(int nstate)
{
int ncpus = 0;
int increasing = (nstate > CpuLimit);
@@ -274,7 +275,6 @@ acpi_setcpufreq(int nstate)
int desired;
int v;
char *sysid;
- char *ptr;
char buf[256];
size_t buflen;
cpumask_t global_cpumask;
@@ -320,7 +320,7 @@ acpi_setcpufreq(int nstate)
/*
* Retrieve availability list
*/
- asprintf(&sysid, "hw.acpi.cpu.px_dom%d.available", dom);
+ asprintf(&sysid, active_fmt[0], dom);
buflen = sizeof(buf) - 1;
v = sysctlbyname(sysid, buf, &buflen, NULL, 0);
free(sysid);
@@ -331,21 +331,15 @@ acpi_setcpufreq(int nstate)
/*
* Parse out the highest and lowest cpu frequencies
*/
- ptr = buf;
highest = lowest = 0;
- while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
- if (lowest == 0 || lowest > v)
- lowest = v;
- if (highest == 0 || highest < v)
- highest = v;
- }
+ freq_hilo(buf, &highest, &lowest);
/*
* Calculate the desired cpu frequency, test, and set.
*/
desired = increasing ? highest : lowest;
- asprintf(&sysid, "hw.acpi.cpu.px_dom%d.select", dom);
+ asprintf(&sysid, active_fmt[2], dom);
buflen = sizeof(v);
v = 0;
sysctlbyname(sysid, &v, &buflen, NULL, 0);
diff --git a/usr.sbin/powerd/powerd.h b/usr.sbin/powerd/powerd.h
new file mode 100644
index 0000000..b6cdf52
--- /dev/null
+++ b/usr.sbin/powerd/powerd.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The powerd daemon monitors the cpu load and adjusts cpu frequencies
+ * via appropriate sysctls.
+ */
+#ifndef _POWERD_H_
+#define _POWERD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+/*
+ * As of now there are at least two methods of detecting/changing
+ * CPU P-States.
+ * One is via ACPI's sysctls hw.acpi.cpu.px_dom* but there is also
+ * clockmod(4) which provides different sysctls that need to be taken
+ * in account. Although it's unlikely devices diverge from ACPI in the
+ * future, below data structures will provide an easy way of plugging-in
+ * new ways of identifying, detecting and changing CPU P-States.
+ *
+ * Every P-State "method" will have to provide ways of:
+ * - Probing: Determine whether the method is available.
+ * - Initialising: Set up the data structure itself.
+ * - Giving information: I.e. domain index of a CPU
+ * - Changing frequencies.
+ *
+ */
+#define MAXDOM 256
+#define NFREQS 32
+#define NDEVS 2
+#define CPUPDOM 16
+
+enum {
+ ACPIDEV = 0,
+ CLKMDEV = 1,
+ INVLDEV = 99
+};
+
+struct powerd_cpu_domain {
+ SLIST_ENTRY(powerd_cpu_domain) entry;
+ uint16_t idx;
+ uint16_t nfreqs;
+ uint16_t domfreqs[NFREQS];
+};
+
+struct powerd_pstate_dev {
+ SLIST_HEAD(dom_head, powerd_cpu_domain) doms;
+ uint8_t prio;
+ uint16_t ndomains;
+ uint16_t ncpus;
+
+ int (*probe)(void);
+ void (*init)(void);
+ int (*getcpudom)(int);
+ int (*setfreq)(int, void *);
+};
+
+#define PSDEV_INIT() (psdevs[StateDev].init())
+#define PSDEV_SETFREQ(dom,freq) (psdevs[StateDev].setfreq(dom, freq))
+
+/* ACPI prototypes */
+int powerd_acpi_probe(void);
+void powerd_acpi_init(void);
+int powerd_acpi_getcpudom(int);
+int powerd_acpi_setfreq(int, void *);
+
+/* clockmod prototypes */
+int powerd_clockmod_probe(void);
+void powerd_clockmod_init(void);
+int powerd_clockmod_getcpudom(int);
+int powerd_clockmod_setfreq(int, void *);
+
+
+struct powerd_pstate_dev psdevs[NDEVS] = {
+ /* ACPI-sysctl */
+ {
+ SLIST_HEAD_INITIALIZER(doms),
+ 0, /* preferred method */
+ 0,
+ powerd_acpi_probe,
+ powerd_acpi_init,
+ powerd_acpi_getcpudom,
+ powerd_acpi_setfreq
+ },
+ /* clockmod-sysctl */
+ {
+ SLIST_HEAD_INITIALIZER(doms),
+ 0, /* preferred method */
+ 1,
+ powerd_clockmod_probe,
+ powerd_clockmod_init,
+ powerd_clockmod_getcpudom,
+ powerd_clockmod_setfreq
+ }
+};
+
+#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment