1139804Simp/*- 21541Srgrimes * Copyright (c) 1992, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * This software was developed by the Computer Systems Engineering group 61541Srgrimes * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 71541Srgrimes * contributed to Berkeley. 81541Srgrimes * 91541Srgrimes * Redistribution and use in source and binary forms, with or without 101541Srgrimes * modification, are permitted provided that the following conditions 111541Srgrimes * are met: 121541Srgrimes * 1. Redistributions of source code must retain the above copyright 131541Srgrimes * notice, this list of conditions and the following disclaimer. 141541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151541Srgrimes * notice, this list of conditions and the following disclaimer in the 161541Srgrimes * documentation and/or other materials provided with the distribution. 171541Srgrimes * 4. Neither the name of the University nor the names of its contributors 181541Srgrimes * may be used to endorse or promote products derived from this software 191541Srgrimes * without specific prior written permission. 201541Srgrimes * 211541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311541Srgrimes * SUCH DAMAGE. 321541Srgrimes * 331541Srgrimes * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 341541Srgrimes * 351541Srgrimes */ 361541Srgrimes 37116182Sobrien#include <sys/cdefs.h> 38116182Sobrien__FBSDID("$FreeBSD: stable/11/sys/kern/subr_autoconf.c 323464 2017-09-11 21:32:35Z ian $"); 39116182Sobrien 40180610Srwatson#include "opt_ddb.h" 41180610Srwatson 421541Srgrimes#include <sys/param.h> 4329680Sgibbs#include <sys/kernel.h> 44180616Srwatson#include <sys/linker.h> 45160509Sjhb#include <sys/lock.h> 46323464Sian#include <sys/malloc.h> 47160509Sjhb#include <sys/mutex.h> 4829680Sgibbs#include <sys/systm.h> 491541Srgrimes 501541Srgrimes/* 511541Srgrimes * Autoconfiguration subroutines. 521541Srgrimes */ 531541Srgrimes 541541Srgrimes/* 5529680Sgibbs * "Interrupt driven config" functions. 5629680Sgibbs */ 5760938Sjakestatic TAILQ_HEAD(, intr_config_hook) intr_config_hook_list = 5829680Sgibbs TAILQ_HEAD_INITIALIZER(intr_config_hook_list); 59211236Sgibbsstatic struct intr_config_hook *next_to_notify; 60160509Sjhbstatic struct mtx intr_config_hook_lock; 61160509SjhbMTX_SYSINIT(intr_config_hook, &intr_config_hook_lock, "intr config", MTX_DEF); 6229680Sgibbs 6329680Sgibbs/* ARGSUSED */ 64211236Sgibbsstatic void run_interrupt_driven_config_hooks(void); 65160509Sjhb 66180616Srwatson/* 67323464Sian * Private data and a shim function for implementing config_interhook_oneshot(). 68323464Sian */ 69323464Sianstruct oneshot_config_hook { 70323464Sian struct intr_config_hook 71323464Sian och_hook; /* Must be first */ 72323464Sian ich_func_t och_func; 73323464Sian void *och_arg; 74323464Sian}; 75323464Sian 76323464Sianstatic void 77323464Sianconfig_intrhook_oneshot_func(void *arg) 78323464Sian{ 79323464Sian struct oneshot_config_hook *ohook; 80323464Sian 81323464Sian ohook = arg; 82323464Sian ohook->och_func(ohook->och_arg); 83323464Sian config_intrhook_disestablish(&ohook->och_hook); 84323464Sian free(ohook, M_DEVBUF); 85323464Sian} 86323464Sian 87323464Sian/* 88180616Srwatson * If we wait too long for an interrupt-driven config hook to return, print 89180616Srwatson * a diagnostic. 90180616Srwatson */ 91180616Srwatson#define WARNING_INTERVAL_SECS 60 9229680Sgibbsstatic void 93180616Srwatsonrun_interrupt_driven_config_hooks_warning(int warned) 94180616Srwatson{ 95180616Srwatson struct intr_config_hook *hook_entry; 96180616Srwatson char namebuf[64]; 97180616Srwatson long offset; 98180616Srwatson 99180673Srwatson if (warned < 6) { 100180673Srwatson printf("run_interrupt_driven_hooks: still waiting after %d " 101180673Srwatson "seconds for", warned * WARNING_INTERVAL_SECS); 102180673Srwatson TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) { 103180673Srwatson if (linker_search_symbol_name( 104180673Srwatson (caddr_t)hook_entry->ich_func, namebuf, 105180673Srwatson sizeof(namebuf), &offset) == 0) 106180673Srwatson printf(" %s", namebuf); 107180673Srwatson else 108180673Srwatson printf(" %p", hook_entry->ich_func); 109180673Srwatson } 110180673Srwatson printf("\n"); 111180616Srwatson } 112180673Srwatson KASSERT(warned < 6, 113180673Srwatson ("run_interrupt_driven_config_hooks: waited too long")); 114180616Srwatson} 115180616Srwatson 116180616Srwatsonstatic void 117211236Sgibbsrun_interrupt_driven_config_hooks() 11829680Sgibbs{ 119211236Sgibbs static int running; 120211236Sgibbs struct intr_config_hook *hook_entry; 12129680Sgibbs 122160509Sjhb mtx_lock(&intr_config_hook_lock); 123211236Sgibbs 124211236Sgibbs /* 125211236Sgibbs * If hook processing is already active, any newly 126211236Sgibbs * registered hooks will eventually be notified. 127211236Sgibbs * Let the currently running session issue these 128211236Sgibbs * notifications. 129211236Sgibbs */ 130211236Sgibbs if (running != 0) { 131160509Sjhb mtx_unlock(&intr_config_hook_lock); 132211236Sgibbs return; 133211236Sgibbs } 134211236Sgibbs running = 1; 135211236Sgibbs 136211236Sgibbs while (next_to_notify != NULL) { 137211236Sgibbs hook_entry = next_to_notify; 138211236Sgibbs next_to_notify = TAILQ_NEXT(hook_entry, ich_links); 139211236Sgibbs mtx_unlock(&intr_config_hook_lock); 14051684Sn_hibma (*hook_entry->ich_func)(hook_entry->ich_arg); 141160509Sjhb mtx_lock(&intr_config_hook_lock); 14229680Sgibbs } 14329680Sgibbs 144211236Sgibbs running = 0; 145211236Sgibbs mtx_unlock(&intr_config_hook_lock); 146211236Sgibbs} 147211236Sgibbs 148211236Sgibbsstatic void 149211236Sgibbsboot_run_interrupt_driven_config_hooks(void *dummy) 150211236Sgibbs{ 151211236Sgibbs int warned; 152211236Sgibbs 153211236Sgibbs run_interrupt_driven_config_hooks(); 154211236Sgibbs 155211236Sgibbs /* Block boot processing until all hooks are disestablished. */ 156211236Sgibbs mtx_lock(&intr_config_hook_lock); 157180616Srwatson warned = 0; 15851684Sn_hibma while (!TAILQ_EMPTY(&intr_config_hook_list)) { 159180616Srwatson if (msleep(&intr_config_hook_list, &intr_config_hook_lock, 160217075Sjhb 0, "conifhk", WARNING_INTERVAL_SECS * hz) == 161180673Srwatson EWOULDBLOCK) { 162180616Srwatson mtx_unlock(&intr_config_hook_lock); 163180616Srwatson warned++; 164180616Srwatson run_interrupt_driven_config_hooks_warning(warned); 165180616Srwatson mtx_lock(&intr_config_hook_lock); 166180616Srwatson } 16729680Sgibbs } 168160509Sjhb mtx_unlock(&intr_config_hook_lock); 16929680Sgibbs} 170211236Sgibbs 17129680SgibbsSYSINIT(intr_config_hooks, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_FIRST, 172211236Sgibbs boot_run_interrupt_driven_config_hooks, NULL); 17329680Sgibbs 17429680Sgibbs/* 17529680Sgibbs * Register a hook that will be called after "cold" 17629680Sgibbs * autoconfiguration is complete and interrupts can 17729680Sgibbs * be used to complete initialization. 17829680Sgibbs */ 17929680Sgibbsint 180188059Simpconfig_intrhook_establish(struct intr_config_hook *hook) 18129680Sgibbs{ 18229680Sgibbs struct intr_config_hook *hook_entry; 18329680Sgibbs 184160509Sjhb mtx_lock(&intr_config_hook_lock); 185160509Sjhb TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) 18629680Sgibbs if (hook_entry == hook) 18729680Sgibbs break; 18829680Sgibbs if (hook_entry != NULL) { 189160509Sjhb mtx_unlock(&intr_config_hook_lock); 19029680Sgibbs printf("config_intrhook_establish: establishing an " 19129680Sgibbs "already established hook.\n"); 19229680Sgibbs return (1); 19329680Sgibbs } 19429680Sgibbs TAILQ_INSERT_TAIL(&intr_config_hook_list, hook, ich_links); 195211236Sgibbs if (next_to_notify == NULL) 196211236Sgibbs next_to_notify = hook; 197160509Sjhb mtx_unlock(&intr_config_hook_lock); 19829680Sgibbs if (cold == 0) 199211236Sgibbs /* 200211236Sgibbs * XXX Call from a task since not all drivers expect 201211236Sgibbs * to be re-entered at the time a hook is established. 202211236Sgibbs */ 20345739Speter /* XXX Sufficient for modules loaded after initial config??? */ 204211236Sgibbs run_interrupt_driven_config_hooks(); 20529680Sgibbs return (0); 20629680Sgibbs} 20729680Sgibbs 208323464Sian/* 209323464Sian * Register a hook function that is automatically unregistered after it runs. 210323464Sian */ 21129680Sgibbsvoid 212323464Sianconfig_intrhook_oneshot(ich_func_t func, void *arg) 213323464Sian{ 214323464Sian struct oneshot_config_hook *ohook; 215323464Sian 216323464Sian ohook = malloc(sizeof(*ohook), M_DEVBUF, M_WAITOK); 217323464Sian ohook->och_func = func; 218323464Sian ohook->och_arg = arg; 219323464Sian ohook->och_hook.ich_func = config_intrhook_oneshot_func; 220323464Sian ohook->och_hook.ich_arg = ohook; 221323464Sian config_intrhook_establish(&ohook->och_hook); 222323464Sian} 223323464Sian 224323464Sianvoid 225188059Simpconfig_intrhook_disestablish(struct intr_config_hook *hook) 22629680Sgibbs{ 22729680Sgibbs struct intr_config_hook *hook_entry; 22829680Sgibbs 229160509Sjhb mtx_lock(&intr_config_hook_lock); 230160509Sjhb TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) 23129680Sgibbs if (hook_entry == hook) 23229680Sgibbs break; 23329680Sgibbs if (hook_entry == NULL) 23429680Sgibbs panic("config_intrhook_disestablish: disestablishing an " 23529680Sgibbs "unestablished hook"); 23629680Sgibbs 237211236Sgibbs if (next_to_notify == hook) 238211236Sgibbs next_to_notify = TAILQ_NEXT(hook, ich_links); 23929680Sgibbs TAILQ_REMOVE(&intr_config_hook_list, hook, ich_links); 240160509Sjhb 24129680Sgibbs /* Wakeup anyone watching the list */ 24229680Sgibbs wakeup(&intr_config_hook_list); 243160509Sjhb mtx_unlock(&intr_config_hook_lock); 24429680Sgibbs} 245180610Srwatson 246180610Srwatson#ifdef DDB 247180610Srwatson#include <ddb/ddb.h> 248180610Srwatson 249180610SrwatsonDB_SHOW_COMMAND(conifhk, db_show_conifhk) 250180610Srwatson{ 251180610Srwatson struct intr_config_hook *hook_entry; 252180610Srwatson char namebuf[64]; 253180610Srwatson long offset; 254180610Srwatson 255180610Srwatson TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) { 256180610Srwatson if (linker_ddb_search_symbol_name( 257180610Srwatson (caddr_t)hook_entry->ich_func, namebuf, sizeof(namebuf), 258180610Srwatson &offset) == 0) { 259180610Srwatson db_printf("hook: %p at %s+%#lx arg: %p\n", 260180610Srwatson hook_entry->ich_func, namebuf, offset, 261180610Srwatson hook_entry->ich_arg); 262180610Srwatson } else { 263180610Srwatson db_printf("hook: %p at ??+?? arg %p\n", 264180610Srwatson hook_entry->ich_func, hook_entry->ich_arg); 265180610Srwatson } 266180610Srwatson } 267180610Srwatson} 268180610Srwatson#endif /* DDB */ 269