1 /* $OpenBSD: mp_setperf.c,v 1.2 2007/05/01 04:18:32 gwk Exp $ */
2 /*
3 * Copyright (c) 2007 Gordon Willem Klok <gwk@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/sysctl.h>
21 #include <sys/mutex.h>
22
23 #include <machine/cpu.h>
24
25 #include <machine/intr.h>
26
27 struct mutex setperf_mp_mutex = MUTEX_INITIALIZER(IPL_HIGH);
28
29 /* underlying setperf mechanism e.g. k8_powernow_setperf() */
30 void (*ul_setperf)(int);
31
32 #define MP_SETPERF_STEADY 0 /* steady state - normal operation */
33 #define MP_SETPERF_INTRANSIT 1 /* in transition */
34 #define MP_SETPERF_PROCEED 2 /* proceed with transition */
35 #define MP_SETPERF_FINISH 3 /* return from IPI */
36
37
38 /* protected by setperf_mp_mutex */
39 volatile int mp_setperf_state = MP_SETPERF_STEADY;
40 volatile int mp_perflevel;
41
42 void mp_setperf(int);
43
44 void
45 mp_setperf(int level)
46 {
47 CPU_INFO_ITERATOR cii;
48 struct cpu_info *ci;
49 int notready, s;
50
51 if (mp_setperf_state == MP_SETPERF_STEADY) {
52 mtx_enter(&setperf_mp_mutex);
53 mp_perflevel = level;
54
55 curcpu()->ci_setperf_state = CI_SETPERF_INTRANSIT;
56 /* ask all other processors to drop what they are doing */
57 CPU_INFO_FOREACH(cii, ci) {
58 if (ci->ci_setperf_state != CI_SETPERF_INTRANSIT) {
59 ci->ci_setperf_state =
60 CI_SETPERF_SHOULDSTOP;
61 i386_send_ipi(ci, I386_IPI_SETPERF);
62 }
63 }
64
65
66 /* Loop until all processors report ready */
67 do {
68 CPU_INFO_FOREACH(cii, ci) {
69 if ((notready = (ci->ci_setperf_state
70 != CI_SETPERF_INTRANSIT)))
71 break;
72 }
73 } while (notready);
74
75 mp_setperf_state = MP_SETPERF_PROCEED; /* release the hounds */
76
77 s = splipi();
78
79 ul_setperf(mp_perflevel);
80
81 splx(s);
82
83 curcpu()->ci_setperf_state = CI_SETPERF_DONE;
84 /* Loop until all processors report done */
85 do {
86 CPU_INFO_FOREACH(cii, ci) {
87 if ((notready = (ci->ci_setperf_state
88 != CI_SETPERF_DONE)))
89 break;
90 }
91 } while (notready);
92
93 mp_setperf_state = MP_SETPERF_FINISH;
94 /* delay a little for potential straglers */
95 DELAY(2);
96 curcpu()->ci_setperf_state = CI_SETPERF_READY;
97 mp_setperf_state = MP_SETPERF_STEADY; /* restore normallity */
98 mtx_leave(&setperf_mp_mutex);
99 }
100
101 }
102
103 void
104 i386_setperf_ipi(struct cpu_info *ci)
105 {
106
107 if (ci->ci_setperf_state == CI_SETPERF_SHOULDSTOP)
108 ci->ci_setperf_state = CI_SETPERF_INTRANSIT;
109
110 while (mp_setperf_state != MP_SETPERF_PROCEED)
111 ;
112
113 ul_setperf(mp_perflevel);
114
115 ci->ci_setperf_state = CI_SETPERF_DONE;
116
117 while (mp_setperf_state != MP_SETPERF_FINISH)
118 ;
119 ci->ci_setperf_state = CI_SETPERF_READY;
120 }
121
122 void
123 mp_setperf_init()
124 {
125 CPU_INFO_ITERATOR cii;
126 struct cpu_info *ci;
127
128
129 if (!cpu_setperf)
130 return;
131 ul_setperf = cpu_setperf;
132
133 cpu_setperf = mp_setperf;
134
135 CPU_INFO_FOREACH(cii, ci) {
136 ci->ci_setperf_state = CI_SETPERF_READY;
137 }
138 mtx_init(&setperf_mp_mutex, IPL_HIGH);
139 }