summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2014-10-17 01:51:40 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2014-10-17 01:51:40 +0000
commit408955add921fe3396cd06f800d7d8a066c9ab26 (patch)
tree91ae4f7a9012abb064041f158840bc8c3420206a
parent74fe337c10141c7b46dc14a8fc44882a404a47d6 (diff)
redo the performance throttling in the kernel.
introduce a new sysctl, hw.perfpolicy, that governs the policy. when set to anything other than manual, hw.setperf then becomes read only. phessler was heading in this direction, but this is slightly different. :)
-rw-r--r--sys/kern/init_main.c11
-rw-r--r--sys/kern/kern_sysctl.c29
-rw-r--r--sys/kern/sched_bsd.c152
-rw-r--r--sys/sys/sched.h5
-rw-r--r--sys/sys/sysctl.h6
5 files changed, 178 insertions, 25 deletions
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index e9e5827fb4b..4d5b672a619 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: init_main.c,v 1.221 2014/10/13 22:42:22 deraadt Exp $ */
+/* $OpenBSD: init_main.c,v 1.222 2014/10/17 01:51:39 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 setperf_auto(void *);
+#endif
+
int cmask = CMASK;
extern struct user *proc0paddr;
@@ -545,6 +550,10 @@ main(void *framep)
start_init_exec = 1;
wakeup((void *)&start_init_exec);
+#ifndef SMALL_KERNEL
+ timeout_set(&setperf_to, setperf_auto, NULL);
+#endif
+
/*
* proc0: nothing to do, back to sleep
*/
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 15c467c23ba..dcf6b861981 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sysctl.c,v 1.266 2014/10/11 17:12:30 deraadt Exp $ */
+/* $OpenBSD: kern_sysctl.c,v 1.267 2014/10/17 01:51:39 tedu Exp $ */
/* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */
/*-
@@ -127,8 +127,6 @@ void fill_file(struct kinfo_file *, struct file *, struct filedesc *,
void fill_kproc(struct process *, struct kinfo_proc *, struct proc *, int);
int (*cpu_cpuspeed)(int *);
-void (*cpu_setperf)(int);
-int perflevel = 100;
/*
* Lock to avoid too many processes vslocking a large amount of memory
@@ -649,11 +647,6 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
disk_count * sizeof(struct diskstats)));
case HW_DISKCOUNT:
return (sysctl_rdint(oldp, oldlenp, newp, disk_count));
-#ifndef SMALL_KERNEL
- case HW_SENSORS:
- return (sysctl_sensors(name + 1, namelen - 1, oldp, oldlenp,
- newp, newlen));
-#endif
case HW_CPUSPEED:
if (!cpu_cpuspeed)
return (EOPNOTSUPP);
@@ -661,19 +654,15 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
if (err)
return err;
return (sysctl_rdint(oldp, oldlenp, newp, cpuspeed));
+#ifndef SMALL_KERNEL
+ case HW_SENSORS:
+ return (sysctl_sensors(name + 1, namelen - 1, oldp, oldlenp,
+ newp, newlen));
case HW_SETPERF:
- if (!cpu_setperf)
- return (EOPNOTSUPP);
- err = sysctl_int(oldp, oldlenp, newp, newlen, &perflevel);
- if (err)
- return err;
- if (perflevel > 100)
- perflevel = 100;
- if (perflevel < 0)
- perflevel = 0;
- if (newp)
- cpu_setperf(perflevel);
- return (0);
+ return (sysctl_hwsetperf(oldp, oldlenp, newp, newlen));
+ case HW_PERFPOLICY:
+ return (sysctl_hwperfpolicy(oldp, oldlenp, newp, newlen));
+#endif /* !SMALL_KERNEL */
case HW_VENDOR:
if (hw_vendor)
return (sysctl_rdstring(oldp, oldlenp, newp,
diff --git a/sys/kern/sched_bsd.c b/sys/kern/sched_bsd.c
index 98bb478b8bc..67813da2131 100644
--- a/sys/kern/sched_bsd.c
+++ b/sys/kern/sched_bsd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sched_bsd.c,v 1.35 2014/07/04 05:58:31 guenther Exp $ */
+/* $OpenBSD: sched_bsd.c,v 1.36 2014/10/17 01:51:39 tedu Exp $ */
/* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */
/*-
@@ -41,6 +41,7 @@
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
@@ -563,3 +564,152 @@ schedclock(struct proc *p)
p->p_priority = p->p_usrpri;
SCHED_UNLOCK(s);
}
+
+#ifndef SMALL_KERNEL
+/*
+ * The code below handles CPU throttling.
+ */
+#include <sys/sysctl.h>
+
+#define PERFPOL_MANUAL 0
+#define PERFPOL_AUTO 1
+#define PERFPOL_HIGH 2
+int perflevel = 100;
+int perfpolicy = PERFPOL_MANUAL;
+
+void (*cpu_setperf)(int);
+
+struct timeout setperf_to;
+void setperf_auto(void *);
+
+void
+setperf_auto(void *v)
+{
+ static uint64_t *idleticks, *totalticks;
+
+ int i, j;
+ int speedup;
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *ci;
+ uint64_t idle, total, allidle, alltotal;
+
+ if (perfpolicy != PERFPOL_AUTO)
+ 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);
+}
+
+int
+sysctl_hwsetperf(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+ int err, newperf;
+
+ if (!cpu_setperf)
+ return EOPNOTSUPP;
+
+ if (perfpolicy != PERFPOL_MANUAL)
+ return sysctl_rdint(oldp, oldlenp, newp, perflevel);
+
+ newperf = perflevel;
+ err = sysctl_int(oldp, oldlenp, newp, newlen, &newperf);
+ if (err)
+ return err;
+ if (newperf > 100)
+ newperf = 100;
+ if (newperf < 0)
+ newperf = 0;
+ perflevel = newperf;
+ cpu_setperf(perflevel);
+
+ return 0;
+}
+
+int
+sysctl_hwperfpolicy(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+ char policy[32];
+ int err;
+
+ if (!cpu_setperf)
+ return EOPNOTSUPP;
+
+ switch (perfpolicy) {
+ case PERFPOL_MANUAL:
+ strlcpy(policy, "manual", sizeof(policy));
+ break;
+ case PERFPOL_AUTO:
+ strlcpy(policy, "auto", sizeof(policy));
+ break;
+ case PERFPOL_HIGH:
+ strlcpy(policy, "high", sizeof(policy));
+ break;
+ default:
+ strlcpy(policy, "unknown", sizeof(policy));
+ break;
+ }
+
+ if (newp == NULL)
+ return sysctl_rdstring(oldp, oldlenp, newp, policy);
+
+ err = sysctl_string(oldp, oldlenp, newp, newlen, policy, sizeof(policy));
+ if (err)
+ return err;
+ if (strcmp(policy, "manual") == 0)
+ perfpolicy = PERFPOL_MANUAL;
+ else if (strcmp(policy, "auto") == 0)
+ perfpolicy = PERFPOL_AUTO;
+ else if (strcmp(policy, "high") == 0)
+ perfpolicy = PERFPOL_HIGH;
+ else
+ return EINVAL;
+
+ if (perfpolicy == PERFPOL_AUTO) {
+ timeout_add_msec(&setperf_to, 200);
+ } else if (perfpolicy == PERFPOL_HIGH) {
+ perflevel = 100;
+ cpu_setperf(perflevel);
+ }
+ return 0;
+}
+#endif
+
diff --git a/sys/sys/sched.h b/sys/sys/sched.h
index 731d9dcbfd1..032580f5fe1 100644
--- a/sys/sys/sched.h
+++ b/sys/sys/sched.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sched.h,v 1.36 2014/09/09 07:07:39 blambert Exp $ */
+/* $OpenBSD: sched.h,v 1.37 2014/10/17 01:51:39 tedu Exp $ */
/* $NetBSD: sched.h,v 1.2 1999/02/28 18:14:58 ross Exp $ */
/*-
@@ -153,6 +153,9 @@ void cpu_idle_cycle(void);
void cpu_idle_leave(void);
void sched_peg_curproc(struct cpu_info *ci);
+int sysctl_hwsetperf(void *, size_t *, void *, size_t);
+int sysctl_hwperfpolicy(void *, size_t *, void *, size_t);
+
#ifdef MULTIPROCESSOR
void sched_start_secondary_cpus(void);
void sched_stop_secondary_cpus(void);
diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h
index 8bb7f090cf3..665aae654c6 100644
--- a/sys/sys/sysctl.h
+++ b/sys/sys/sysctl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sysctl.h,v 1.149 2014/08/20 06:23:03 mikeb Exp $ */
+/* $OpenBSD: sysctl.h,v 1.150 2014/10/17 01:51:39 tedu Exp $ */
/* $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $ */
/*
@@ -798,7 +798,8 @@ struct kinfo_file {
#define HW_USERMEM64 20 /* quad: non-kernel memory */
#define HW_NCPUFOUND 21 /* int: number of cpus found*/
#define HW_ALLOWPOWERDOWN 22 /* allow power button shutdown */
-#define HW_MAXID 23 /* number of valid hw ids */
+#define HW_PERFPOLICY 23 /* set performance policy */
+#define HW_MAXID 24 /* number of valid hw ids */
#define CTL_HW_NAMES { \
{ 0, 0 }, \
@@ -824,6 +825,7 @@ struct kinfo_file {
{ "usermem", CTLTYPE_QUAD }, \
{ "ncpufound", CTLTYPE_INT }, \
{ "allowpowerdown", CTLTYPE_INT }, \
+ { "perfpolicy", CTLTYPE_STRING }, \
}
/*