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
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/systm.h>
33 #include <sys/apic.h>
34 #include <sys/callb.h>
35 #include <sys/cpuvar.h>
36 #include <sys/proc.h>
37 #include <sys/processor.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 #define IS_CPU(cpu_id) (cpu[cpu_id] != NULL)
45
46 #define INTRD_NORMAL_SLEEPTIME 10
47 #define INTRD_IDLE_SLEEPTIME 45
48 #define INTRD_ONECPU_SLEEPTIME (60 * 15)
49
50 #define INTRD_NUMBER_SAMPLES 10
51
52
53 static kmutex_t intrd_lock;
54 static kcondvar_t intrd_cv;
55
56 /*
57 * Interrupt CPU instance.
58 */
59 typedef struct _intrd_cpu {
60 list_node_t ic_next;
61 boolean_t ic_offline;
62 hrtime_t ic_tot[INTRD_NUMBER_SAMPLES];
63 list_t ic_ivec_list;
64 cpu_t *ic_cpu;
65 } intrd_cpu_t;
66
67 /*
68 * Interrupt vector instance.
69 */
70 typedef struct _intrd_ivec {
71 list_node_t ii_next;
72 } intrd_ivec_t;
73
74 static list_t intrd_cpu_list; /* List of all CPU's */
75
76 static uint8_t intrd_cs = 0; /* Index of current sample */
77 static long intrd_sleeptime = INTRD_NORMAL_SLEEPTIME;
78
79 /*
80 * Function prototypes.
81 */
82 static void intrd_update(void *);
83 static void intrd_cpu_register(processorid_t);
84 static int intrd_cpu_setup(cpu_setup_t, int, void *);
85 static void intrd_stat_all(void);
86
87 void
88 intrd(void)
89 {
90 processorid_t cpu_id;
91 callb_cpr_t cpr;
92 user_t *u = PTOU(curproc);
93
94 proc_intrd = ttoproc(curthread);
95 proc_intrd->p_cstime = proc_intrd->p_stime = 0;
96 proc_intrd->p_cutime = proc_intrd->p_utime = 0;
97
98 (void) strncpy(u->u_psargs, INTRD_NAME, sizeof(u->u_psargs));
99 (void) strncpy(u->u_comm, INTRD_NAME, sizeof(u->u_comm));
100
101 /* Global mutex lock */
102 mutex_init(&intrd_lock, NULL, MUTEX_DEFAULT, NULL);
103
104 /* Initialize list of CPUs */
105 list_create(&intrd_cpu_list, sizeof (intrd_cpu_t),
106 offsetof(intrd_cpu_t, ic_next));
107
108 /*
109 * Build a list of all CPUs able for interrupt handling.
110 */
111 mutex_enter(&cpu_lock);
112 for (cpu_id = 0; cpu_id <= max_cpu_seqid_ever; cpu_id++) {
113 if (IS_CPU(cpu_id))
114 intrd_cpu_register(cpu_id);
115 }
116
117 /*
118 * Register a callback if a CPU goes offline or comes online.
119 */
120 register_cpu_setup_func(intrd_cpu_setup, NULL);
121 mutex_exit(&cpu_lock);
122
123 // ASSERT(!list_is_empty(&intrd_cpu_list));
124 // ASSERT(intrd_number_cpus >= 1);
125
126 CALLB_CPR_INIT(&cpr, &intrd_lock, callb_generic_cpr, INTRD_NAME);
127
128 mutex_enter(&intrd_lock);
129 for (;;) {
130
131 intrd_stat_all();
132
133 CALLB_CPR_SAFE_BEGIN(&cpr);
134 cv_timedwait(&intrd_cv, &intrd_lock, ddi_get_lbolt() +
135 SEC_TO_TICK(intrd_sleeptime));
136 CALLB_CPR_SAFE_END(&cpr, &intrd_lock);
137 }
138
139 CALLB_CPR_EXIT(&cpr);
140
141 /*
142 * Unregister CPU callback.
143 */
144 mutex_enter(&cpu_lock);
145 unregister_cpu_setup_func(intrd_cpu_setup, NULL);
146 mutex_exit(&cpu_lock);
147 }
148
149 /*
150 * Allocate a new intrd CPU structure and add CPU
151 * to the global list of CPUs.
152 */
153 static void
154 intrd_cpu_register(processorid_t cpu_id)
155 {
156 cpu_t *cp = cpu[cpu_id];
157 intrd_cpu_t *new_cpu;
158
159 new_cpu = kmem_alloc(sizeof (intrd_cpu_t), KM_SLEEP);
160 new_cpu->ic_cpu = cp;
161
162 list_create(&new_cpu->ic_ivec_list, sizeof (intrd_ivec_t),
163 offsetof(intrd_ivec_t, ii_next));
164
165 list_link_init(&new_cpu->ic_next);
166
167 /* Check if this CPU can handle interrupts */
168 mutex_enter(&cpu_lock);
169 if (cpu_is_nointr(cp))
170 new_cpu->ic_offline = B_TRUE;
171 mutex_exit(&cpu_lock);
172
173 /* Add new CPU to the list of all CPU's */
174 list_insert_tail(&intrd_cpu_list, new_cpu);
175 }
176
177 /*
178 * Remove a CPU from the global list of CPUs.
179 */
180 static void
181 intrd_cpu_unregister(processorid_t cpu_id)
182 {
183 intrd_cpu_t *icpu;
184
185 mutex_enter(&intrd_lock);
186 for (icpu = list_head(&intrd_cpu_list); icpu != NULL;
187 icpu = list_next(&intrd_cpu_list, icpu)) {
188 if (icpu->ic_cpu == cpu[cpu_id]) {
189 list_remove(&intrd_cpu_list, icpu);
190 break;
191 }
192 }
193 mutex_exit(&intrd_lock);
194 }
195
196 /*
197 * Hook for CPU changes.
198 */
199 static int
200 intrd_cpu_setup(cpu_setup_t what, int cpu_id, void *arg)
201 {
202
203 switch (what) {
204 /* XXX */
205 case CPU_OFF:
206 intrd_cpu_unregister(cpu_id);
207 cv_signal(&intrd_cv);
208 break;
209
210 case CPU_INTR_ON:
211 intrd_cpu_register(cpu_id);
212 cv_signal(&intrd_cv);
213 break;
214
215 default:
216 break;
217 }
218
219 return (0);
220 }
221
222 static void
223 intrd_cpu_stat(intrd_cpu_t *icpu)
224 {
225 hrtime_t msnsecs[NCMSTATES];
226
227 get_cpu_mstate(icpu->ic_cpu, msnsecs);
228
229 icpu->ic_tot[intrd_cs] = msnsecs[CMS_IDLE] + msnsecs[CMS_USER] +
230 msnsecs[CMS_SYSTEM];
231 }
232
233 static void
234 intrd_irq_stat(intrd_cpu_t *icpu)
235 {
236 apic_irq_t *irq;
237 int irq_id;
238
239 /* XXX: Do better then APIC_MAX_VECTOR */
240 for (irq_id = 0; irq_id < APIC_MAX_VECTOR; irq_id++) {
241 irq = apic_irq_table[irq_id];
242 }
243 }
244
245 static void
246 intrd_stat_all(void)
247 {
248 intrd_cpu_t *icpu;
249
250 for (icpu = list_head(&intrd_cpu_list); icpu != NULL;
251 icpu = list_next(&intrd_cpu_list, icpu)) {
252
253 intrd_cpu_stat(icpu);
254 intrd_irq_stat(icpu);
255 }
256 }
257
258 static void
259 foreach_cpu(void (*intrd_cpu_cb_func)(intrd_cpu_t *))
260 {
261 intrd_cpu_t *cpu;
262
263 for (cpu = list_head(&intrd_cpu_list); cpu != NULL;
264 cpu = list_next(&intrd_cpu_list, cpu)) {
265 intrd_cpu_cb_func(cpu);
266 }
267 }
268
269 static void
270 intrd_interrupt_move(void)
271 {
272 /* XXX: pcitool_set_intr(dip, arg, mode); */
273 }