1/* $NetBSD: callb.c,v 1.1 2009/03/26 22:11:45 ad Exp $ */ 2 3/* 4 * CDDL HEADER START 5 * 6 * The contents of this file are subject to the terms of the 7 * Common Development and Distribution License, Version 1.0 only 8 * (the "License"). You may not use this file except in compliance 9 * with the License. 10 * 11 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 12 * or http://www.opensolaris.org/os/licensing. 13 * See the License for the specific language governing permissions 14 * and limitations under the License. 15 * 16 * When distributing Covered Code, include this CDDL HEADER in each 17 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 18 * If applicable, add the following below this CDDL HEADER, with the 19 * fields enclosed by brackets "[]" replaced with your own identifying 20 * information: Portions Copyright [yyyy] [name of copyright owner] 21 * 22 * CDDL HEADER END 23 */ 24/* 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29#pragma ident "%Z%%M% %I% %E% SMI" 30 31#include <sys/param.h> 32#include <sys/types.h> 33#include <sys/time.h> 34#include <sys/sysmacros.h> 35#include <sys/systm.h> 36#include <sys/proc.h> 37#include <sys/mutex.h> 38#include <sys/condvar.h> 39#include <sys/callb.h> 40#include <sys/kmem.h> 41#include <sys/cmn_err.h> 42#include <sys/debug.h> 43#include <sys/kobj.h> 44#include <sys/systm.h> /* for delay() */ 45#include <sys/taskq.h> /* For TASKQ_NAMELEN */ 46#include <sys/kernel.h> 47 48#define CB_MAXNAME TASKQ_NAMELEN 49 50/* 51 * The callb mechanism provides generic event scheduling/echoing. 52 * A callb function is registered and called on behalf of the event. 53 */ 54typedef struct callb { 55 struct callb *c_next; /* next in class or on freelist */ 56 kthread_id_t c_thread; /* ptr to caller's thread struct */ 57 char c_flag; /* info about the callb state */ 58 uchar_t c_class; /* this callb's class */ 59 kcondvar_t c_done_cv; /* signal callb completion */ 60 boolean_t (*c_func)(); /* cb function: returns true if ok */ 61 void *c_arg; /* arg to c_func */ 62 char c_name[CB_MAXNAME+1]; /* debug:max func name length */ 63} callb_t; 64 65/* 66 * callb c_flag bitmap definitions 67 */ 68#define CALLB_FREE 0x0 69#define CALLB_TAKEN 0x1 70#define CALLB_EXECUTING 0x2 71 72/* 73 * Basic structure for a callb table. 74 * All callbs are organized into different class groups described 75 * by ct_class array. 76 * The callbs within a class are single-linked and normally run by a 77 * serial execution. 78 */ 79typedef struct callb_table { 80 kmutex_t ct_lock; /* protect all callb states */ 81 callb_t *ct_freelist; /* free callb structures */ 82 int ct_busy; /* != 0 prevents additions */ 83 kcondvar_t ct_busy_cv; /* to wait for not busy */ 84 int ct_ncallb; /* num of callbs allocated */ 85 callb_t *ct_first_cb[NCBCLASS]; /* ptr to 1st callb in a class */ 86} callb_table_t; 87 88int callb_timeout_sec = CPR_KTHREAD_TIMEOUT_SEC; 89 90static callb_id_t callb_add_common(boolean_t (*)(void *, int), 91 void *, int, char *, kthread_id_t); 92 93static callb_table_t callb_table; /* system level callback table */ 94static callb_table_t *ct = &callb_table; 95static kmutex_t callb_safe_mutex; 96callb_cpr_t callb_cprinfo_safe = { 97 &callb_safe_mutex, CALLB_CPR_ALWAYS_SAFE, 0, 0, 0 }; 98 99/* 100 * Init all callb tables in the system. 101 */ 102void 103callb_init(void *dummy __unused) 104{ 105 callb_table.ct_busy = 0; /* mark table open for additions */ 106 mutex_init(&callb_safe_mutex, NULL, MUTEX_DEFAULT, NULL); 107 mutex_init(&callb_table.ct_lock, NULL, MUTEX_DEFAULT, NULL); 108} 109 110void 111callb_fini(void *dummy __unused) 112{ 113 callb_t *cp; 114 115 mutex_enter(&ct->ct_lock); 116 while ((cp = ct->ct_freelist) != NULL) { 117 ct->ct_freelist = cp->c_next; 118 ct->ct_ncallb--; 119 kmem_free(cp, sizeof (callb_t)); 120 } 121 ASSERT(ct->ct_ncallb == 0); 122 mutex_exit(&ct->ct_lock); 123 mutex_destroy(&callb_safe_mutex); 124 mutex_destroy(&callb_table.ct_lock); 125} 126 127/* 128 * callout_add() is called to register func() be called later. 129 */ 130static callb_id_t 131callb_add_common(boolean_t (*func)(void *arg, int code), 132 void *arg, int class, char *name, kthread_id_t t) 133{ 134 callb_t *cp; 135 136 ASSERT(class < NCBCLASS); 137 138 mutex_enter(&ct->ct_lock); 139 while (ct->ct_busy) 140 cv_wait(&ct->ct_busy_cv, &ct->ct_lock); 141 if ((cp = ct->ct_freelist) == NULL) { 142 ct->ct_ncallb++; 143 cp = (callb_t *)kmem_zalloc(sizeof (callb_t), KM_SLEEP); 144 } 145 ct->ct_freelist = cp->c_next; 146 cp->c_thread = t; 147 cp->c_func = func; 148 cp->c_arg = arg; 149 cp->c_class = (uchar_t)class; 150 cp->c_flag |= CALLB_TAKEN; 151#ifdef DEBUG 152 if (strlen(name) > CB_MAXNAME) 153 cmn_err(CE_WARN, "callb_add: name of callback function '%s' " 154 "too long -- truncated to %d chars", 155 name, CB_MAXNAME); 156#endif 157 (void) strncpy(cp->c_name, name, CB_MAXNAME); 158 cp->c_name[CB_MAXNAME] = '\0'; 159 160 /* 161 * Insert the new callb at the head of its class list. 162 */ 163 cp->c_next = ct->ct_first_cb[class]; 164 ct->ct_first_cb[class] = cp; 165 166 mutex_exit(&ct->ct_lock); 167 return ((callb_id_t)cp); 168} 169 170/* 171 * The default function to add an entry to the callback table. Since 172 * it uses curthread as the thread identifier to store in the table, 173 * it should be used for the normal case of a thread which is calling 174 * to add ITSELF to the table. 175 */ 176callb_id_t 177callb_add(boolean_t (*func)(void *arg, int code), 178 void *arg, int class, char *name) 179{ 180 return (callb_add_common(func, arg, class, name, curthread)); 181} 182 183/* 184 * A special version of callb_add() above for use by threads which 185 * might be adding an entry to the table on behalf of some other 186 * thread (for example, one which is constructed but not yet running). 187 * In this version the thread id is an argument. 188 */ 189callb_id_t 190callb_add_thread(boolean_t (*func)(void *arg, int code), 191 void *arg, int class, char *name, kthread_id_t t) 192{ 193 return (callb_add_common(func, arg, class, name, t)); 194} 195 196/* 197 * callout_delete() is called to remove an entry identified by id 198 * that was originally placed there by a call to callout_add(). 199 * return -1 if fail to delete a callb entry otherwise return 0. 200 */ 201int 202callb_delete(callb_id_t id) 203{ 204 callb_t **pp; 205 callb_t *me = (callb_t *)id; 206 207 mutex_enter(&ct->ct_lock); 208 209 for (;;) { 210 pp = &ct->ct_first_cb[me->c_class]; 211 while (*pp != NULL && *pp != me) 212 pp = &(*pp)->c_next; 213 214#ifdef DEBUG 215 if (*pp != me) { 216 cmn_err(CE_WARN, "callb delete bogus entry 0x%p", 217 (void *)me); 218 mutex_exit(&ct->ct_lock); 219 return (-1); 220 } 221#endif /* DEBUG */ 222 223 /* 224 * It is not allowed to delete a callb in the middle of 225 * executing otherwise, the callb_execute() will be confused. 226 */ 227 if (!(me->c_flag & CALLB_EXECUTING)) 228 break; 229 230 cv_wait(&me->c_done_cv, &ct->ct_lock); 231 } 232 /* relink the class list */ 233 *pp = me->c_next; 234 235 /* clean up myself and return the free callb to the head of freelist */ 236 me->c_flag = CALLB_FREE; 237 me->c_next = ct->ct_freelist; 238 ct->ct_freelist = me; 239 240 mutex_exit(&ct->ct_lock); 241 return (0); 242} 243 244/* 245 * class: indicates to execute all callbs in the same class; 246 * code: optional argument for the callb functions. 247 * return: = 0: success 248 * != 0: ptr to string supplied when callback was registered 249 */ 250void * 251callb_execute_class(int class, int code) 252{ 253 callb_t *cp; 254 void *ret = NULL; 255 256 ASSERT(class < NCBCLASS); 257 258 mutex_enter(&ct->ct_lock); 259 260 for (cp = ct->ct_first_cb[class]; 261 cp != NULL && ret == 0; cp = cp->c_next) { 262 while (cp->c_flag & CALLB_EXECUTING) 263 cv_wait(&cp->c_done_cv, &ct->ct_lock); 264 /* 265 * cont if the callb is deleted while we're sleeping 266 */ 267 if (cp->c_flag == CALLB_FREE) 268 continue; 269 cp->c_flag |= CALLB_EXECUTING; 270 271#ifdef CALLB_DEBUG 272 printf("callb_execute: name=%s func=%p arg=%p\n", 273 cp->c_name, (void *)cp->c_func, (void *)cp->c_arg); 274#endif /* CALLB_DEBUG */ 275 276 mutex_exit(&ct->ct_lock); 277 /* If callback function fails, pass back client's name */ 278 if (!(*cp->c_func)(cp->c_arg, code)) 279 ret = cp->c_name; 280 mutex_enter(&ct->ct_lock); 281 282 cp->c_flag &= ~CALLB_EXECUTING; 283 cv_broadcast(&cp->c_done_cv); 284 } 285 mutex_exit(&ct->ct_lock); 286 return (ret); 287} 288 289/* 290 * callers make sure no recursive entries to this func. 291 * dp->cc_lockp is registered by callb_add to protect callb_cpr_t structure. 292 * 293 * When calling to stop a kernel thread (code == CB_CODE_CPR_CHKPT) we 294 * use a cv_timedwait() in case the kernel thread is blocked. 295 * 296 * Note that this is a generic callback handler for daemon CPR and 297 * should NOT be changed to accommodate any specific requirement in a daemon. 298 * Individual daemons that require changes to the handler shall write 299 * callback routines in their own daemon modules. 300 */ 301boolean_t 302callb_generic_cpr(void *arg, int code) 303{ 304 callb_cpr_t *cp = (callb_cpr_t *)arg; 305 clock_t ret = 0; /* assume success */ 306 307 mutex_enter(cp->cc_lockp); 308 309 switch (code) { 310 case CB_CODE_CPR_CHKPT: 311 cp->cc_events |= CALLB_CPR_START; 312 while (!(cp->cc_events & CALLB_CPR_SAFE)) 313 /* cv_timedwait() returns -1 if it times out. */ 314 if ((ret = cv_timedwait(&cp->cc_callb_cv, 315 cp->cc_lockp, 316 callb_timeout_sec * hz)) == -1) 317 break; 318 break; 319 320 case CB_CODE_CPR_RESUME: 321 cp->cc_events &= ~CALLB_CPR_START; 322 cv_signal(&cp->cc_stop_cv); 323 break; 324 } 325 mutex_exit(cp->cc_lockp); 326 return (ret != -1); 327} 328 329/* 330 * The generic callback function associated with kernel threads which 331 * are always considered safe. 332 */ 333/* ARGSUSED */ 334boolean_t 335callb_generic_cpr_safe(void *arg, int code) 336{ 337 return (B_TRUE); 338} 339/* 340 * Prevent additions to callback table. 341 */ 342void 343callb_lock_table(void) 344{ 345 mutex_enter(&ct->ct_lock); 346 ASSERT(ct->ct_busy == 0); 347 ct->ct_busy = 1; 348 mutex_exit(&ct->ct_lock); 349} 350 351/* 352 * Allow additions to callback table. 353 */ 354void 355callb_unlock_table(void) 356{ 357 mutex_enter(&ct->ct_lock); 358 ASSERT(ct->ct_busy != 0); 359 ct->ct_busy = 0; 360 cv_broadcast(&ct->ct_busy_cv); 361 mutex_exit(&ct->ct_lock); 362} 363