1 /*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 34 * 35 */ 36 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include "opt_ddb.h" 41 42 #include <sys/param.h> 43 #include <sys/kernel.h> 44 #include <sys/linker.h> 45 #include <sys/lock.h> 46 #include <sys/mutex.h> 47 #include <sys/systm.h> 48 49 /* 50 * Autoconfiguration subroutines. 51 */ 52 53 /* 54 * "Interrupt driven config" functions. 55 */ 56 static TAILQ_HEAD(, intr_config_hook) intr_config_hook_list = 57 TAILQ_HEAD_INITIALIZER(intr_config_hook_list); 58 static struct intr_config_hook *next_to_notify; 59 static struct mtx intr_config_hook_lock; 60 MTX_SYSINIT(intr_config_hook, &intr_config_hook_lock, "intr config", MTX_DEF); 61 62 /* ARGSUSED */ 63 static void run_interrupt_driven_config_hooks(void); 64 65 /* 66 * If we wait too long for an interrupt-driven config hook to return, print 67 * a diagnostic. 68 */ 69 #define WARNING_INTERVAL_SECS 60 70 static void 71 run_interrupt_driven_config_hooks_warning(int warned) 72 { 73 struct intr_config_hook *hook_entry; 74 char namebuf[64]; 75 long offset; 76 77 if (warned < 6) { 78 printf("run_interrupt_driven_hooks: still waiting after %d " 79 "seconds for", warned * WARNING_INTERVAL_SECS); 80 TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) { 81 if (linker_search_symbol_name( 82 (caddr_t)hook_entry->ich_func, namebuf, 83 sizeof(namebuf), &offset) == 0) 84 printf(" %s", namebuf); 85 else 86 printf(" %p", hook_entry->ich_func); 87 } 88 printf("\n"); 89 } 90 KASSERT(warned < 6, 91 ("run_interrupt_driven_config_hooks: waited too long")); 92 } 93 94 static void 95 run_interrupt_driven_config_hooks() 96 { 97 static int running; 98 struct intr_config_hook *hook_entry; 99 100 mtx_lock(&intr_config_hook_lock); 101 102 /* 103 * If hook processing is already active, any newly 104 * registered hooks will eventually be notified. 105 * Let the currently running session issue these 106 * notifications. 107 */ 108 if (running != 0) { 109 mtx_unlock(&intr_config_hook_lock); 110 return; 111 } 112 running = 1; 113 114 while (next_to_notify != NULL) { 115 hook_entry = next_to_notify; 116 next_to_notify = TAILQ_NEXT(hook_entry, ich_links); 117 mtx_unlock(&intr_config_hook_lock); 118 (*hook_entry->ich_func)(hook_entry->ich_arg); 119 mtx_lock(&intr_config_hook_lock); 120 } 121 122 running = 0; 123 mtx_unlock(&intr_config_hook_lock); 124 } 125 126 static void 127 boot_run_interrupt_driven_config_hooks(void *dummy) 128 { 129 int warned; 130 131 run_interrupt_driven_config_hooks(); 132 133 /* Block boot processing until all hooks are disestablished. */ 134 mtx_lock(&intr_config_hook_lock); 135 warned = 0; 136 while (!TAILQ_EMPTY(&intr_config_hook_list)) { 137 if (msleep(&intr_config_hook_list, &intr_config_hook_lock, 138 0, "conifhk", WARNING_INTERVAL_SECS * hz) == 139 EWOULDBLOCK) { 140 mtx_unlock(&intr_config_hook_lock); 141 warned++; 142 run_interrupt_driven_config_hooks_warning(warned); 143 mtx_lock(&intr_config_hook_lock); 144 } 145 } 146 mtx_unlock(&intr_config_hook_lock); 147 } 148 149 SYSINIT(intr_config_hooks, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_FIRST, 150 boot_run_interrupt_driven_config_hooks, NULL); 151 152 /* 153 * Register a hook that will be called after "cold" 154 * autoconfiguration is complete and interrupts can 155 * be used to complete initialization. 156 */ 157 int 158 config_intrhook_establish(struct intr_config_hook *hook) 159 { 160 struct intr_config_hook *hook_entry; 161 162 mtx_lock(&intr_config_hook_lock); 163 TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) 164 if (hook_entry == hook) 165 break; 166 if (hook_entry != NULL) { 167 mtx_unlock(&intr_config_hook_lock); 168 printf("config_intrhook_establish: establishing an " 169 "already established hook.\n"); 170 return (1); 171 } 172 TAILQ_INSERT_TAIL(&intr_config_hook_list, hook, ich_links); 173 if (next_to_notify == NULL) 174 next_to_notify = hook; 175 mtx_unlock(&intr_config_hook_lock); 176 if (cold == 0) 177 /* 178 * XXX Call from a task since not all drivers expect 179 * to be re-entered at the time a hook is established. 180 */ 181 /* XXX Sufficient for modules loaded after initial config??? */ 182 run_interrupt_driven_config_hooks(); 183 return (0); 184 } 185 186 void 187 config_intrhook_disestablish(struct intr_config_hook *hook) 188 { 189 struct intr_config_hook *hook_entry; 190 191 mtx_lock(&intr_config_hook_lock); 192 TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) 193 if (hook_entry == hook) 194 break; 195 if (hook_entry == NULL) 196 panic("config_intrhook_disestablish: disestablishing an " 197 "unestablished hook"); 198 199 if (next_to_notify == hook) 200 next_to_notify = TAILQ_NEXT(hook, ich_links); 201 TAILQ_REMOVE(&intr_config_hook_list, hook, ich_links); 202 203 /* Wakeup anyone watching the list */ 204 wakeup(&intr_config_hook_list); 205 mtx_unlock(&intr_config_hook_lock); 206 } 207 208 #ifdef DDB 209 #include <ddb/ddb.h> 210 211 DB_SHOW_COMMAND(conifhk, db_show_conifhk) 212 { 213 struct intr_config_hook *hook_entry; 214 char namebuf[64]; 215 long offset; 216 217 TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) { 218 if (linker_ddb_search_symbol_name( 219 (caddr_t)hook_entry->ich_func, namebuf, sizeof(namebuf), 220 &offset) == 0) { 221 db_printf("hook: %p at %s+%#lx arg: %p\n", 222 hook_entry->ich_func, namebuf, offset, 223 hook_entry->ich_arg); 224 } else { 225 db_printf("hook: %p at ??+?? arg %p\n", 226 hook_entry->ich_func, hook_entry->ich_arg); 227 } 228 } 229 } 230 #endif /* DDB */ 231