summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2014-10-11 16:28:07 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2014-10-11 16:28:07 +0000
commitfb6cedef0014587351eed1e2b56fe262f7be6b3e (patch)
treeea3b891c41395190573a8843a494424158f15e6c
parentf2ebb4ede8dec3bc7548b216af559167fd74f320 (diff)
resurrect a many year old diff. move CPU throttling into the kernel,
enabled by setting hw.setperf=-1. some other bugs preventing this from going in before have been fixed. my thanks to phessler for keeping the diff alive in the mean time. tested by several to not regress.
-rw-r--r--sys/kern/init_main.c11
-rw-r--r--sys/kern/kern_sysctl.c92
2 files changed, 93 insertions, 10 deletions
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index b2922581365..d00ab037998 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: init_main.c,v 1.218 2014/10/03 18:06:46 kettenis Exp $ */
+/* $OpenBSD: init_main.c,v 1.219 2014/10/11 16:28:06 tedu Exp $ */
/* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */
/*
@@ -116,6 +116,11 @@ struct sigacts sigacts0;
struct process *initprocess;
struct proc *reaperproc;
+#ifndef SMALL_KERNEL
+extern struct timeout setperf_to;
+void auto_setperf(void *);
+#endif
+
int cmask = CMASK;
extern struct user *proc0paddr;
@@ -543,6 +548,10 @@ main(void *framep)
start_init_exec = 1;
wakeup((void *)&start_init_exec);
+#ifndef SMALL_KERNEL
+ timeout_set(&setperf_to, auto_setperf, NULL);
+#endif
+
/*
* proc0: nothing to do, back to sleep
*/
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 433f4d05098..3aadefc153a 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sysctl.c,v 1.264 2014/09/15 19:08:21 miod Exp $ */
+/* $OpenBSD: kern_sysctl.c,v 1.265 2014/10/11 16:28:06 tedu Exp $ */
/* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */
/*-
@@ -129,6 +129,10 @@ void fill_kproc(struct process *, struct kinfo_proc *, struct proc *, int);
int (*cpu_cpuspeed)(int *);
void (*cpu_setperf)(int);
int perflevel = 100;
+#ifndef SMALL_KERNEL
+struct timeout setperf_to;
+void auto_setperf(void *);
+#endif
/*
* Lock to avoid too many processes vslocking a large amount of memory
@@ -254,6 +258,65 @@ int securelevel = -1;
int securelevel;
#endif
+#ifndef SMALL_KERNEL
+void
+auto_setperf(void *v)
+{
+ static uint64_t *idleticks, *totalticks;
+
+ int i, j;
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *ci;
+
+ int speedup;
+ uint64_t idle, total, allidle, alltotal;
+
+ if (!auto_perfctl)
+ return;
+ if (!idleticks)
+ if (!(idleticks = malloc(sizeof(*idleticks) * ncpusfound,
+ M_DEVBUF, M_NOWAIT | M_ZERO)))
+ return;
+ if (!totalticks)
+ if (!(totalticks = malloc(sizeof(*totalticks) * ncpusfound,
+ M_DEVBUF, M_NOWAIT | M_ZERO))) {
+ free(idleticks, M_DEVBUF, 0);
+ return;
+ }
+ alltotal = allidle = 0;
+ j = 0;
+ speedup = 0;
+ CPU_INFO_FOREACH(cii, ci) {
+ total = 0;
+ for (i = 0; i < CPUSTATES; i++) {
+ total += ci->ci_schedstate.spc_cp_time[i];
+ }
+ total -= totalticks[j];
+ idle = ci->ci_schedstate.spc_cp_time[CP_IDLE] - idleticks[j];
+ if (idle < total / 3)
+ speedup = 1;
+ alltotal += total;
+ allidle += idle;
+ idleticks[j] += idle;
+ totalticks[j] += total;
+
+ j++;
+ }
+ if (allidle < alltotal / 2)
+ speedup = 1;
+
+ if (speedup && perflevel != 100) {
+ perflevel = 100;
+ cpu_setperf(perflevel);
+ } else if (!speedup && perflevel != 0) {
+ perflevel = 0;
+ cpu_setperf(perflevel);
+ }
+
+ timeout_add_msec(&setperf_to, 100);
+}
+#endif /* !SMALL_KERNEL */
+
/*
* kernel related system variables.
*/
@@ -608,7 +671,7 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
extern char machine[], cpu_model[];
- int err, cpuspeed;
+ int err, cpuspeed, newperf;
/* all sysctl names at this level except sensors are terminal */
if (name[0] != HW_SENSORS && namelen != 1)
@@ -664,15 +727,26 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
case HW_SETPERF:
if (!cpu_setperf)
return (EOPNOTSUPP);
- err = sysctl_int(oldp, oldlenp, newp, newlen, &perflevel);
+ newperf = perflevel;
+ err = sysctl_int(oldp, oldlenp, newp, newlen, &newperf);
if (err)
return err;
- if (perflevel > 100)
- perflevel = 100;
- if (perflevel < 0)
- perflevel = 0;
- if (newp)
- cpu_setperf(perflevel);
+#ifndef SMALL_KERNEL
+ if (newp) {
+ if (newperf == -1) {
+ auto_perfctl = 1;
+ timeout_add_msec(&setperf_to, 200);
+ } else {
+ auto_perfctl = 0;
+ if (newperf > 100)
+ newperf = 100;
+ if (newperf < 0)
+ newperf = 0;
+ perflevel = newperf;
+ cpu_setperf(newperf);
+ }
+ }
+#endif /* !SMALL_KERNEL */
return (0);
case HW_VENDOR:
if (hw_vendor)