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 }