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 }