1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2013 David Hoeppner. All rights reserved.
14 */
15
16 /*
17 * Interrupt Load Balancer.
18 *
19 *
20 */
21
22 /* XXX
23 *
24 * intrd_cpu_list::walk list |::print intrd_cpu_t
25 *
26 *
27 *
28 */
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 #include <sys/apic.h>
33 #include <sys/callb.h>
34 #include <sys/cpuvar.h>
35 #include <sys/proc.h>
36 #include <sys/processor.h>
37 #include <sys/sdt.h>
38 #include <sys/sysmacros.h>
39 #include <sys/time.h>
40
41 extern proc_t *proc_intrd;
42
43 #define INTRD_NAME "intrd"
44
45 #define IS_CPU(cpu_id) (cpu[cpu_id] != NULL)
46
47 #define INTRD_NORMAL_SLEEPTIME 10
48 #define INTRD_IDLE_SLEEPTIME 45
49 #define INTRD_ONECPU_SLEEPTIME (60 * 15)
50
51 #define INTRD_NUMBER_SAMPLES 10
52
53
54 static kmutex_t intrd_lock;
55 static kcondvar_t intrd_cv;
56
57 /*
58 * Interrupt CPU instance.
59 */
60 typedef struct _intrd_cpu {
61 list_node_t ic_next;
62 boolean_t ic_offline;
63 hrtime_t ic_tot[INTRD_NUMBER_SAMPLES];
64 list_t ic_ivec_list;
65 processorid_t ic_cpu_id; /* XXX duplicate */
66 } intrd_cpu_t;
67
68 /*
69 * Interrupt vector instance.
70 */
71 typedef struct _intrd_ivec {
72 list_node_t ii_next;
73 } intrd_ivec_t;
74
75 static list_t intrd_cpu_list; /* List of all CPU's */
76
77 static uint8_t intrd_cs = 0; /* Index of current sample */
78 static long intrd_sleeptime = INTRD_NORMAL_SLEEPTIME;
79
80 /*
81 * Function prototypes.
82 */
83 static void intrd_update(void *);
84 static void intrd_cpu_register(processorid_t);
85 static int intrd_cpu_setup(cpu_setup_t, int, void *);
86 static void intrd_stat_all(void);
87
88 /*
89 * Helper macros.
90 */
91 #define FOREACH_INTRD_CPU(icpu, icpu_list) \
92 for (icpu = list_head(&icpu_list); icpu != NULL; \
93 icpu = list_next(&icpu_list, icpu))
94
95 #define DTRACE_INTRD(name) \
96 DTRACE_PROBE(__intrd_##name)
97
98 #define DEBUG 1
99 #ifdef DEBUG
100 #define INTRD_IMPLDBG(args) cmn_err args
101 #else
102 #define INTRD_IMPLDBG(args)
103 #endif
104
105 void
106 intrd(void)
107 {
108 processorid_t cpu_id;
109 callb_cpr_t cpr;
110 user_t *u = PTOU(curproc);
111
112 proc_intrd = ttoproc(curthread);
113 proc_intrd->p_cstime = proc_intrd->p_stime = 0;
114 proc_intrd->p_cutime = proc_intrd->p_utime = 0;
115
116 (void) strncpy(u->u_psargs, INTRD_NAME, sizeof(u->u_psargs));
117 (void) strncpy(u->u_comm, INTRD_NAME, sizeof(u->u_comm));
118
119 /* Global mutex lock */
120 mutex_init(&intrd_lock, NULL, MUTEX_DEFAULT, NULL);
121
122 /* Initialize list for CPUs */
123 list_create(&intrd_cpu_list, sizeof (intrd_cpu_t),
124 offsetof(intrd_cpu_t, ic_next));
125
126 /*
127 * Build a list of all CPUs able for interrupt handling.
128 */
129 mutex_enter(&cpu_lock);
130 for (cpu_id = 0; cpu_id <= max_cpu_seqid_ever; cpu_id++) {
131 if (IS_CPU(cpu_id))
132 intrd_cpu_register(cpu_id);
133 }
134
135 /*
136 * Register a callback if a CPU goes offline or comes online.
137 */
138 register_cpu_setup_func(intrd_cpu_setup, NULL);
139 mutex_exit(&cpu_lock);
140
141 /* We should have at least one CPU */
142 // ASSERT(!list_is_empty(&intrd_cpu_list));
143 // ASSERT(intrd_number_cpus >= 1);
144
145 CALLB_CPR_INIT(&cpr, &intrd_lock, callb_generic_cpr, INTRD_NAME);
146
147 mutex_enter(&intrd_lock);
148 for (;;) {
149
150 DTRACE_INTRD(stat_all);
151 intrd_stat_all();
152
153 CALLB_CPR_SAFE_BEGIN(&cpr);
154 cv_timedwait(&intrd_cv, &intrd_lock, ddi_get_lbolt() +
155 SEC_TO_TICK(intrd_sleeptime));
156 CALLB_CPR_SAFE_END(&cpr, &intrd_lock);
157 }
158
159 CALLB_CPR_EXIT(&cpr);
160
161 /*
162 * Unregister CPU callback.
163 */
164 mutex_enter(&cpu_lock);
165 unregister_cpu_setup_func(intrd_cpu_setup, NULL);
166 mutex_exit(&cpu_lock);
167 }
168
169 /*
170 * Allocate a new intrd CPU structure and add CPU
171 * to the global list of CPUs.
172 */
173 static void
174 intrd_cpu_register(processorid_t cpu_id)
175 {
176 cpu_t *cp = cpu[cpu_id];
177 intrd_cpu_t *new_cpu;
178
179 new_cpu = kmem_alloc(sizeof (intrd_cpu_t), KM_SLEEP);
180 new_cpu->ic_cpu_id = cpu_id;
181
182 /* Initialize list for interrupt vectors */
183 list_create(&new_cpu->ic_ivec_list, sizeof (intrd_ivec_t),
184 offsetof(intrd_ivec_t, ii_next));
185
186 list_link_init(&new_cpu->ic_next);
187
188 /* Check if this CPU can handle interrupts */
189 mutex_enter(&cpu_lock);
190 if (cpu_is_nointr(cp))
191 new_cpu->ic_offline = B_TRUE;
192 mutex_exit(&cpu_lock);
193
194 /* Add new CPU to the list of all CPU's */
195 list_insert_tail(&intrd_cpu_list, new_cpu);
196
197 INTRD_IMPLDBG((CE_CONT, "intrd_cpu_register: cpu=0x%x", cpu_id));
198 }
199
200 /*
201 * Remove a CPU from the global list of CPUs.
202 */
203 static void
204 intrd_cpu_unregister(processorid_t cpu_id)
205 {
206 intrd_cpu_t *icpu;
207
208 mutex_enter(&intrd_lock);
209 FOREACH_INTRD_CPU(icpu, intrd_cpu_list) {
210 if (icpu->ic_cpu_id == cpu_id) {
211 list_remove(&intrd_cpu_list, icpu);
212 /* XXX or just offline CPU; statistics? */
213 break;
214 }
215 }
216 mutex_exit(&intrd_lock);
217
218 INTRD_IMPLDBG((CE_CONT, "intrd_cpu_unregister: cpu=0x%x",
219 cpu_id));
220 }
221
222 /*
223 * Hook for CPU changes.
224 */
225 static int
226 intrd_cpu_setup(cpu_setup_t what, int cpu_id, void *arg)
227 {
228
229 switch (what) {
230 /* XXX */
231 case CPU_OFF:
232 intrd_cpu_unregister(cpu_id);
233 cv_signal(&intrd_cv);
234 break;
235
236 case CPU_INTR_ON:
237 intrd_cpu_register(cpu_id);
238 cv_signal(&intrd_cv);
239 break;
240
241 default:
242 break;
243 }
244
245 return (0);
246 }
247
248 static void
249 intrd_cpu_stat(intrd_cpu_t *icpu)
250 {
251 cpu_t *cp;
252 hrtime_t msnsecs[NCMSTATES];
253
254 cp = cpu[icpu->ic_cpu_id];
255 get_cpu_mstate(cp, msnsecs);
256
257 icpu->ic_tot[intrd_cs] = msnsecs[CMS_IDLE] + msnsecs[CMS_USER] +
258 msnsecs[CMS_SYSTEM];
259 }
260
261 static void
262 intrd_irq_stat(intrd_cpu_t *icpu)
263 {
264 apic_irq_t *irqp;
265 int irq_id;
266
267 /* XXX: Do better then APIC_MAX_VECTOR */
268 for (irq_id = APIC_FIRST_FREE_IRQ; irq_id < APIC_MAX_VECTOR;
269 irq_id++) {
270 /*
271 irqp = apic_irq_table[irq_id];
272 if (icpu->ic_cpu_id == (processorid_t)irqp->airq_cpu) {
273 INTRD_IMPLDBG((CE_CONT, "intrd_irq_stat: irq:0x%x",
274 irqp->airq_cpu));
275 }
276 */
277 }
278 }
279
280 static void
281 intrd_stat_all(void)
282 {
283 intrd_cpu_t *icpu;
284
285 FOREACH_INTRD_CPU(icpu, intrd_cpu_list) {
286 intrd_cpu_stat(icpu);
287 intrd_irq_stat(icpu);
288 }
289 }
290
291 static void
292 intrd_interrupt_move(void)
293 {
294 /* XXX: pcitool_set_intr(dip, arg, mode); */
295 }