1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * DACF (Device Autoconfiguration Framework) client code. 31 * 32 * DACF has two clients. the first is dacf modules which implement 33 * configuration operations; the second is the set of hooks in the kernel 34 * which do rule matching and invoke configuration operations. 35 * 36 * This file implements the second part, the kernel hooks. 37 * 38 * Currently implemented are post-attach and pre-detach handlers, and the hook 39 * for ddi_create_minor_common() which sets up post-attach and pre-detach 40 * reservations. 41 * 42 * This code depends on the core dacf code (in dacf.c) but the converse should 43 * never be true. 44 * 45 * This file also implements '__kernel', the kernel-supplied dacf module. 46 * For now, this is pretty much empty, except under DEBUG, in which case it 47 * contains some debugging code. 48 */ 49 50 #include <sys/param.h> 51 #include <sys/modctl.h> 52 #include <sys/sysmacros.h> 53 #include <sys/kmem.h> 54 #include <sys/cmn_err.h> 55 #include <sys/pathname.h> 56 #include <sys/ddi_impldefs.h> 57 #include <sys/sunddi.h> 58 #include <sys/autoconf.h> 59 #include <sys/modhash.h> 60 #include <sys/dacf_impl.h> 61 #include <sys/systm.h> 62 #include <sys/debug.h> 63 64 /* 65 * dacfc_match_create_minor() 66 * Check to see if this minor node creation sequence matches a dacf 67 * (device autoconfiguration framework) rule. If so make a reservation 68 * for the operation to be invoked at post-attach and/or pre-detach time. 69 */ 70 void 71 dacfc_match_create_minor(char *name, char *node_type, dev_info_t *dip, 72 struct ddi_minor_data *dmdp, int flag) 73 { 74 dacf_rule_t *r; 75 char *dev_path, *dev_pathp, *drv_mname = NULL; 76 dacf_rsrvlist_t *pa_rsrv, *pd_rsrv; 77 78 if (flag & CLONE_DEV) { 79 return; 80 } 81 82 /* 83 * Because dacf currently only implements post-attach and pre-detach 84 * processing, we only care about minor nodes created during attach. 85 * However, there is no restriction on drivers about when to create 86 * minor nodes. 87 */ 88 if (!DEVI_IS_ATTACHING(dmdp->dip)) { 89 return; 90 } 91 92 dev_path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 93 dev_pathp = ddi_pathname(dip, dev_path); 94 pa_rsrv = kmem_alloc(sizeof (dacf_rsrvlist_t), KM_SLEEP); 95 pd_rsrv = kmem_alloc(sizeof (dacf_rsrvlist_t), KM_SLEEP); 96 97 if (name) { 98 const char *drv_name = ddi_driver_name(dip); 99 if (drv_name == NULL) 100 drv_name = "???"; 101 drv_mname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 102 (void) snprintf(drv_mname, MAXPATHLEN, "%s:%s", drv_name, name); 103 } 104 105 mutex_enter(&dacf_lock); 106 107 /* 108 * Ensure that we don't wind up in a 'matching loop' against a devinfo 109 * node, which could cause deadlock. This could happen as follows: 110 * 111 * We match (just below) 112 * We invoke a task (later, at the end of devi_attach) 113 * this means we have taken the per-devinfo lock 114 * The task invoke winds up causing the same driver (that has 115 * just finished attaching) to create another minor node. 116 * We try to re-acquire the per-devinfo list lock again in the 117 * process of making another reservation 118 */ 119 mutex_enter(&(DEVI(dip)->devi_lock)); 120 if (DEVI_IS_INVOKING_DACF(dip)) { 121 mutex_exit(&(DEVI(dip)->devi_lock)); 122 cmn_err(CE_WARN, 123 "!dacf detected deadlock, aborting matching procedure\n"); 124 mutex_exit(&dacf_lock); 125 kmem_free(pa_rsrv, sizeof (dacf_rsrvlist_t)); 126 kmem_free(pd_rsrv, sizeof (dacf_rsrvlist_t)); 127 kmem_free(dev_path, MAXPATHLEN); 128 if (drv_mname) { 129 kmem_free(drv_mname, MAXPATHLEN); 130 } 131 return; 132 } 133 mutex_exit(&(DEVI(dip)->devi_lock)); 134 135 /* 136 * Do rule matching. It's possible to construct two rules that would 137 * match against the same minor node, so we match from most to least 138 * specific: 139 * device path 140 * minor node name (concatenation of drv_name:name 141 * node type 142 * 143 * Future additions to the set of device-specifiers should be 144 * sensitive to this ordering. 145 */ 146 147 /* 148 * post-attach matching 149 */ 150 r = NULL; 151 if (dev_pathp) { 152 r = dacf_match(DACF_OPID_POSTATTACH, DACF_DS_DEV_PATH, 153 dev_pathp); 154 } 155 if (!r && drv_mname) { 156 r = dacf_match(DACF_OPID_POSTATTACH, DACF_DS_DRV_MNAME, 157 drv_mname); 158 } 159 if (!r && node_type) { 160 r = dacf_match(DACF_OPID_POSTATTACH, DACF_DS_MIN_NT, node_type); 161 } 162 if (r) { 163 dacf_rsrv_make(pa_rsrv, r, dmdp, &(DEVI(dip)->devi_dacf_tasks)); 164 165 if (dacfdebug & DACF_DBG_MSGS) 166 printf("dacf: made 'post-attach' reservation for " 167 "%s, %s, %s\n", name, node_type, dev_pathp); 168 } else { 169 kmem_free(pa_rsrv, sizeof (dacf_rsrvlist_t)); 170 } 171 172 /* 173 * pre-detach matching 174 */ 175 r = NULL; 176 if (dev_pathp) { 177 r = dacf_match(DACF_OPID_PREDETACH, DACF_DS_DEV_PATH, 178 dev_pathp); 179 } 180 if (!r && drv_mname) { 181 r = dacf_match(DACF_OPID_PREDETACH, DACF_DS_DRV_MNAME, 182 drv_mname); 183 } 184 if (!r && node_type) { 185 r = dacf_match(DACF_OPID_PREDETACH, DACF_DS_MIN_NT, node_type); 186 } 187 if (r) { 188 dacf_rsrv_make(pd_rsrv, r, dmdp, &(DEVI(dip)->devi_dacf_tasks)); 189 190 if (dacfdebug & DACF_DBG_MSGS) { 191 printf("dacf: made 'pre-detach' reservation for " 192 "%s, %s, %s\n", name, node_type, dev_pathp); 193 } 194 } else { 195 kmem_free(pd_rsrv, sizeof (dacf_rsrvlist_t)); 196 } 197 198 mutex_exit(&dacf_lock); 199 kmem_free(dev_path, MAXPATHLEN); 200 if (drv_mname) { 201 kmem_free(drv_mname, MAXPATHLEN); 202 } 203 } 204 205 /* 206 * dacfc_postattach() 207 * autoconfiguration for post-attach events. 208 * 209 * strategy: try to configure. If some of the configuration operations 210 * fail, emit a warning. 211 */ 212 int 213 dacfc_postattach(dev_info_t *devi) 214 { 215 int err = DACF_SUCCESS; 216 char *path, *pathp; 217 dacf_rsrvlist_t **opsp, *op; 218 ASSERT(MUTEX_HELD(&dacf_lock)); 219 220 /* 221 * Instruct dacf_process_rsrvs() to invoke each POSTATTACH op. 222 */ 223 opsp = &DEVI(devi)->devi_dacf_tasks; 224 dacf_process_rsrvs(opsp, DACF_OPID_POSTATTACH, DACF_PROC_INVOKE); 225 226 /* 227 * Check to see that all POSTATTACH's succeeded. 228 */ 229 for (op = *opsp; op != NULL; op = op->rsrv_next) { 230 if (op->rsrv_rule->r_opid != DACF_OPID_POSTATTACH) 231 continue; 232 if (op->rsrv_result == DACF_SUCCESS) 233 continue; 234 if (dacfdebug & DACF_DBG_DEVI) { 235 cmn_err(CE_WARN, "op failed, err = %d\n", 236 op->rsrv_result); 237 } 238 err = DACF_FAILURE; 239 break; 240 } 241 242 /* 243 * If one or more postattach's failed, give up. 244 */ 245 if ((err == DACF_FAILURE) && (dacfdebug & DACF_DBG_DEVI)) { 246 path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 247 if ((pathp = ddi_pathname(devi, path)) == NULL) 248 pathp = "<unknown>"; 249 cmn_err(CE_WARN, "%s attached, but failed to auto-configure", 250 pathp); 251 kmem_free(path, MAXPATHLEN); 252 } 253 254 return (err); 255 } 256 257 /* 258 * dacfc_predetach() 259 * auto-unconfiguration for pre-detach events. 260 * 261 * strategy: call the pre-detach operation for all matching reservations. 262 * If any of these fail, make (one) attempt to reconfigure things back 263 * into a sane state. if that fails, our state is uncertain. 264 */ 265 int 266 dacfc_predetach(dev_info_t *devi) 267 { 268 int err = DDI_SUCCESS; 269 char *path, *pathp; 270 dacf_rsrvlist_t **opsp, *op; 271 ASSERT(MUTEX_HELD(&dacf_lock)); 272 273 /* 274 * Instruct dacf_process_rsrvs() to invoke each PREDETACH op. 275 */ 276 opsp = &DEVI(devi)->devi_dacf_tasks; 277 dacf_process_rsrvs(opsp, DACF_OPID_PREDETACH, DACF_PROC_INVOKE); 278 279 /* 280 * Check to see that all PREDETACH's succeeded. 281 */ 282 for (op = *opsp; op != NULL; op = op->rsrv_next) { 283 if (op->rsrv_rule->r_opid != DACF_OPID_PREDETACH) 284 continue; 285 if (op->rsrv_result == 0) 286 continue; 287 err = DDI_FAILURE; 288 break; 289 } 290 291 /* 292 * If one or more predetach's failed, make one attempt to fix things 293 * by re-running all of the POST-ATTACH operations. If any of those 294 * fail, give up. 295 */ 296 if (err == DDI_FAILURE) { 297 int pa_err; 298 299 if (dacfdebug & DACF_DBG_DEVI) { 300 path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 301 if ((pathp = ddi_pathname(devi, path)) == NULL) 302 pathp = "<unknown>"; 303 cmn_err(CE_WARN, "%s failed to auto-unconfigure, " 304 "attempting to reconfigure...", pathp); 305 kmem_free(path, MAXPATHLEN); 306 } 307 308 pa_err = dacfc_postattach(devi); 309 310 if (dacfdebug & DACF_DBG_DEVI) { 311 path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 312 if ((pathp = ddi_pathname(devi, path)) == NULL) 313 pathp = "<unknown>"; 314 315 if (pa_err == DDI_FAILURE) { 316 cmn_err(CE_WARN, "%s failed to " 317 "auto-unconfigure, and could not be " 318 "re-autoconfigured.", pathp); 319 } else { 320 cmn_err(CE_WARN, "%s failed to " 321 "auto-unconfigure, but was successfully " 322 "re-autoconfigured.", pathp); 323 } 324 kmem_free(path, MAXPATHLEN); 325 } 326 } 327 328 return (err); 329 } 330 331 /* 332 * kmod_dacfsw: 333 * This is the declaration for the kernel-supplied '__kernel' dacf module. 334 * DACF supplies a framework based around loadable modules. However, it 335 * may be convenient (in the future) to have a module provided by the 336 * kernel. This is useful in cases when a module can't be loaded (early in 337 * boot), or for code that would never get unloaded anyway. 338 */ 339 #ifdef DEBUG 340 /*ARGSUSED*/ 341 static int 342 kmod_test_postattach(dacf_infohdl_t info_hdl, dacf_arghdl_t arg_hdl, int flags) 343 { 344 const char *verbose = dacf_get_arg(arg_hdl, "verbose"); 345 if (verbose && (strcmp(verbose, "true") == 0)) { 346 cmn_err(CE_WARN, "got kmod_test_postattach\n"); 347 } 348 return (0); 349 } 350 #endif 351 352 static dacf_op_t kmod_op_test[] = { 353 #ifdef DEBUG 354 { DACF_OPID_POSTATTACH, kmod_test_postattach }, 355 #endif 356 { DACF_OPID_END, NULL }, 357 }; 358 359 static dacf_opset_t kmod_opsets[] = { 360 #ifdef DEBUG 361 { "kmod_test", kmod_op_test }, 362 #endif 363 { NULL, NULL }, 364 }; 365 366 struct dacfsw kmod_dacfsw = { 367 DACF_MODREV_1, kmod_opsets 368 }; 369