Home | History | Annotate | Download | only in os
      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	"@(#)dacf_clnt.c	1.2	05/06/08 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