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 }