subr_autoconf.c revision 211236
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: head/sys/kern/subr_autoconf.c 211236 2010-08-12 19:50:40Z gibbs $"); 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 */ 56static TAILQ_HEAD(, intr_config_hook) intr_config_hook_list = 57 TAILQ_HEAD_INITIALIZER(intr_config_hook_list); 58static struct intr_config_hook *next_to_notify; 59static struct mtx intr_config_hook_lock; 60MTX_SYSINIT(intr_config_hook, &intr_config_hook_lock, "intr config", MTX_DEF); 61 62/* ARGSUSED */ 63static 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 70static void 71run_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 94static void 95run_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 126static void 127boot_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 PCONFIG, "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 149SYSINIT(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 */ 157int 158config_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 186void 187config_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 211DB_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