/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* DACF: device autoconfiguration support
*
* DACF provides a fast, lightweight policy engine for the I/O subsystem.
* This policy engine provides a mechanism for auto-configuring and
* auto-unconfiguring devices.
*
* After a device is attach(9E)ed, additional configuration may be needed in
* order to make the device available for use by the system. For example,
* STREAMS modules may need to be pushed atop the driver in order to create
* a STREAMS stack. If the device is to be removed from the system, these
* configuration operations need to be undone, and the device prepared for
* detach(9E).
*
* It is desirable to move the implementation of such policies outside of the
* kernel proper, since such operations are typically infrequent. To this end,
* DACF manages kernel modules in (module_path)/dacf directories. These adhere
* operations. The kernel loads these modules when the operations they
* implement are needed, and can unload them at any time thereafter.
* Implementing configuration operations in external modules can also increase
* code reuse.
*
* DACF provides a policy database which associates
*
* (device descr., kernel action) --> (configuration operation, parameters)
*
* - Device description is matching rule, for example:
* minor-nodetype="ddi_keyboard"
* - Kernel action is a reference to a dacf kernel hook.
* currently supported are "post-attach" and "pre-detach"
* - Configuration action is a reference to a module and a set of operations
* within the module, for example: consconfig:kbd_config
* - Parameters is a list of name="value" parameters to be passed to the
* configuration operation when invoked.
*
*
* DACF kernel hooks are comprised of a call into the rule-matching engine,
* using parameters from the hook in order find a matching rule. If one is
* found, the framework can invoke the configuration operation immediately, or
* defer doing so until later, by putting the rule on a 'reservation list.'
*/
#include <sys/sysmacros.h>
#include <sys/pathname.h>
#include <sys/ddi_impldefs.h>
#include <sys/autoconf.h>
#include <sys/dacf_impl.h>
/*
* Enumeration of the ops exported by the dacf framework.
*
* To add a new op to the framework, add it to this list, update dacf.h,
* (don't miss DACF_NUM_OPIDS) and modify dacf_rule_matrix.
*
*/
typedef struct dacf_opmap {
const char *name;
} dacf_opmap_t;
{ "post-attach", DACF_OPID_POSTATTACH },
{ "pre-detach", DACF_OPID_PREDETACH },
{ NULL, 0 },
};
/*
* Enumeration of the options exported by the dacf framework (currently none).
*
* To add a new option, add it to this array.
*/
typedef struct dacf_opt {
const char *optname;
} dacf_opt_t;
#ifdef DEBUG
{ "testopt", 1 },
{ "testopt2", 2 },
#endif
{ NULL, 0 },
};
/*
* Enumeration of the device specifiers exported by the dacf framework.
*
* To add a new devspec to the framework, add it to this list, update dacf.h,
* (don't miss DACF_NUM_DEVSPECS), modify dacf_rule_matrix, and modify
* dacf_match().
*/
typedef struct dacf_ds {
const char *name;
} dacf_ds_t;
{ "minor-nodetype", DACF_DS_MIN_NT },
{ "driver-minorname", DACF_DS_DRV_MNAME },
{ "device-path", DACF_DS_DEV_PATH },
};
/*
* This is the lookup table for the hash tables that dacf manages. Given an
* op id and devspec type, one can obtain the hash for that type of data.
*/
};
int dacfdebug = 0;
uint_t, dacf_arg_t *);
static void dacf_rule_val_dtor(mod_hash_val_t);
/*PRINTFLIKE1*/
static void
{
if (dacfdebug & DACF_DBG_MSGS) {
/*
* sprintf up the string that is 'dacf debug: <the message>'
*/
}
}
/*
* dacf_init()
* initialize the dacf framework by creating the various hash tables.
*/
void
{
int i, j;
dprintf("dacf_init: creating hashmatrix\n");
#ifdef DEBUG
/*
* Sanity check that DACF_NUM_DEVSPECS and the devspecs are in sync
*/
continue;
ASSERT(i == DACF_NUM_DEVSPECS);
/*
* Sanity check that DACF_NUM_OPIDS and the dacf_ops are in sync
*/
continue;
ASSERT(i == DACF_NUM_OPIDS);
#endif
for (i = 0; i < DACF_NUM_OPIDS; i++) {
for (j = 0; j < DACF_NUM_DEVSPECS; j++) {
if (dacf_rule_matrix[i][j] == NULL) {
continue;
}
/*
* Set up a hash table with no key destructor. The
* keys are carried in the rule_t, so the val_dtor
* will take care of the key as well.
*/
"dacf hashmatrix [%d][%d]", i, j);
*(dacf_rule_matrix[i][j]) = mod_hash_create_extended(
hbuf, /* hash name */
DACF_RULE_HASHSIZE, /* # hash elems */
mod_hash_null_keydtor, /* key dtor */
dacf_rule_val_dtor, /* value dtor */
mod_hash_strkey_cmp, /* key comparator */
KM_SLEEP);
}
}
dprintf("dacf_init: creating module_hash\n");
/*
* dacf_module_hash stores the currently registered dacf modules
* by name.
*/
dprintf("dacf_init: creating info_hash\n");
/*
* dacf_info_hash stores pointers to data that modules can associate
* on a per minornode basis. The type of data stored is opaque to the
* framework-- thus there is no destructor supplied.
*/
sizeof (struct ddi_minor_data));
/*
* Register the '__kernel' module.
*
* These are operations that are provided by the kernel, not by a
* module. We just feed the framework a dacfsw structure; it will get
* marked as 'loaded' by dacf_module_register(), and will always be
* available.
*/
(void) read_dacf_binding_file(NULL);
dprintf("dacf_init: dacf is ready\n");
}
/*
* dacf_clear_rules()
* clear the dacf rule database. This is typically done in advance of
* rereading the dacf binding file.
*/
void
{
int i, j;
for (i = 0; i < DACF_NUM_OPIDS; i++) {
for (j = 0; j < DACF_NUM_DEVSPECS; j++) {
if ((dacf_rule_matrix[i][j] != NULL) &&
(*(dacf_rule_matrix[i][j]) != NULL)) {
mod_hash_clear(*(dacf_rule_matrix[i][j]));
}
}
}
}
/*
* dacf_rule_insert()
* Create an entry in the dacf rule database.
* If 'module' is null, the kernel is the 'module'. (see dacf_rule_ctor()).
*/
int
{
dprintf("dacf_rule_insert called: %s=\"%s\", %s:%s, %s\n",
/*
* Fetch the hash table associated with this op-name and devspec-type.
* Some ops may not support all devspec-types, since they may be
* meaningless, so hash may be null.
*/
return (-1);
}
/*
* Allocate a rule and fill it in, take a hold on it.
*/
op_args);
(mod_hash_val_t)rule) != 0) {
/*
* We failed, so release hold. This will cause the rule and
* associated data to get nuked.
*/
return (-1);
}
return (0);
}
/*
* dacf_rule_ctor()
* Allocate and fill out entries in a dacf_rule_t.
*/
static dacf_rule_t *
{
dacf_arg_t *p;
/*
* Fill in the entries
*/
/*
* If module is 'null' we set it to __kernel, meaning that this op
* is implemented by the kernel.
*/
}
p = op_args;
while (p != NULL) {
/*
* dacf_arg_insert() should always succeed, since we're copying
* another (already duplicate-free) list.
*/
p = p->arg_next;
}
return (rule);
}
/*
* dacf_rule_val_dtor()
* This is the destructor for dacf_rule_t's in the rule database. It
* simply does a dacf_rule_rele() on the rule. This function will take
* care of destroying the rule if its ref count has dropped to 0.
*/
static void
{
}
/*
* dacf_rule_destroy()
* destroy a dacf_rule_t
*/
void
{
/*
* Free arguments.
*/
/*
* Module may be null for a kernel-managed op-set
*/
}
/*
* dacf_rule_hold()
* dacf rules are ref-counted. This function increases the reference
* count on an rule.
*/
void
{
}
/*
* dacf_rule_rele()
* drop the ref count on an rule, and destroy the rule if its
* ref count drops to 0.
*/
void
{
}
}
/*
* dacf_rsrv_make()
* add an rule to a reservation list to be processed later.
*/
void
{
/*
* Bump the ref count on rule, so it won't get freed as long as it's on
* this reservation list.
*/
dprintf("dacf: reservation made\n");
}
/*
* dacf_clr_rsrvs()
* clear reservation list of operations of type 'op'
*/
void
{
}
/*
* dacf_process_rsrvs()
* iterate across a locked reservation list, processing each element
* which matches 'op' according to 'flags'.
*
* if DACF_PROC_INVOKE is specified, the elements that match 'op'
* will have their operations invoked. The return value from that
* operation is placed in the rsrv_result field of the dacf_rsrvlist_t
*/
void
{
return;
/*
* Walk the list, finding rules whose opid's match op, and performing
* the work described by 'flags'.
*/
p = p->rsrv_next;
continue;
}
if (flags & DACF_PROC_INVOKE) {
p->rsrv_ihdl, 0);
}
if (flags & DACF_PROC_RELE) {
dp = p;
p = p->rsrv_next;
} else {
p = p->rsrv_next;
}
}
}
/*
* dacf_get_op_hash()
* Given an op name, (i.e. "post-attach" or "pre-detach") and a
* devspec-type, return the hash that represents that op indexed
* by that devspec.
*/
static mod_hash_t *
{
/*
* dacf_rule_matrix is an array of pointers to pointers to hashes.
*/
return (NULL);
}
}
/*
* dacf_arg_insert()
* Create and insert an entry in an argument list.
* Returns -1 if the argument name is a duplicate of another already
* present in the hash.
*/
int
{
/*
* Don't allow duplicates.
*/
return (-1);
}
}
return (0);
}
/*
* dacf_arglist_delete()
* free all the elements of a list of dacf_arg_t's.
*/
void
{
}
}
/*
* dacf_match()
* Match a device-spec to a rule.
*/
{
(mod_hash_val_t *)&rule) == 0) {
return (rule);
}
return (NULL); /* Not Found */
}
/*
* dacf_module_register()
* register a module with the framework. Use when a module gets loaded,
* or for the kernel to register a "virtual" module (i.e. a "module"
* which the kernel provides). Makes a copy of the interface description
* provided by the module.
*/
int
{
char *str;
return (EINVAL);
}
"version %d interface, not registered\n", mod_name,
return (EINVAL);
}
/*
* count how many opsets are provided.
*/
;
/*
* Temporary: It's ok for the kernel dacf_sw to have no opsets, since
* we don't have any opsets to export yet (in NON-DEBUG).
*/
"not registered.\n", mod_name);
return (EINVAL);
}
/*
* Look to see if the module has been previously registered with the
* framework. If so, we can fail with EBUSY.
*/
(mod_hash_val_t)&module) == 0) {
/*
* See if it is loaded currently
*/
"already registered.", mod_name);
return (EBUSY);
}
} else {
/*
* This is the first time we've ever seen the module; stick
* it into the module hash. If that fails, we've had a
* race between two threads, both trying to insert the same
* new module. It's safe to stick the module into the
* hash only partly filled in, since dm_lock protects the
* structure, and we've got that write-locked.
*/
(mod_hash_val_t)module) != 0) {
"already registered.", mod_name);
return (EBUSY);
}
}
/*
* In either case (first time we've seen it or not), the module is
* not loaded, and we hold it write-locked.
*/
/*
* Alloc array of opsets for this module. Add one for the final
* NULL entry
*/
for (i = 0; i < nelems; i++) {
}
if (dacfdebug & DACF_DBG_MSGS) {
for (i = 0; i < nelems; i++) {
}
}
return (0);
}
/*
* dacf_module_unregister()
* remove a module from the framework, and free framework-allocated
* resources.
*/
int
{
/*
* Can't unregister __kernel, since there is no real way to get it
* back-- Once it gets marked with dm_loaded == 0, the kernel will
* try to modload() if it is ever needed, which will fail utterly,
* and send op_invoke into a loop in it's modload logic
*
* If this is behavior is ever needed in the future, we can just
* add a flag indicating that this module is really a fake.
*/
/*
* If NOAUL_DACF is set, or we try to get a write-lock on dm_lock and
* that fails, return EBUSY, and fail to unregister.
*/
(mod_hash_val_t)&module) == 0) {
if ((moddebug & MODDEBUG_NOAUL_DACF) ||
return (EBUSY);
}
} else {
return (EINVAL);
}
return (0);
}
/*
* dacf_destroy_opsets()
* given a module, destroy all of it's associated op-sets.
*/
static void
{
dacf_opset_t *p;
int i;
p = &(array[i]);
/*
* count nelems in opset_ops
*/
break;
}
}
/*
* Free the array of op ptrs.
*/
}
/*
* i has counted how big array is; +1 to account for the last element.
*/
}
/*
* dacf_opset_copy()
* makes a copy of a dacf_opset_t.
*/
static void
{
dprintf("dacf_opset_copy: called\n");
dprintf("dacf_opset_copy: counting ops\n");
break;
}
}
KM_SLEEP);
dprintf("dacf_opset_copy: copying ops\n");
for (i = 0; i < nelems; i++) {
}
dprintf("dacf_opset_copy: done copying ops\n");
}
/*
* dacf_op_invoke()
* Invoke a op in a opset in a module given the rule to invoke.
*
* If the return value of dacf_op_invoke is 0, then rval contains the
* return value of the _op_ being invoked. Otherwise, dacf_op_invoke's
* return value indicates why the op invocation failed.
*/
int
{
/*
* Take laps, trying to load the dacf module. For the case of kernel-
* provided operations, __kernel will be found in the hash table, and
* no modload will be needed.
*/
for (;;) {
(mod_hash_val_t *)&module) == 0) {
/*
* Found the module, and it is loaded.
*/
break;
}
}
/*
* If we're here, either: 1) it's not in the hash, or 2) it is,
* but dm_loaded is 0, meaning the module needs to be loaded.
*/
dprintf("dacf_op_invoke: calling modload\n");
return (DACF_ERR_MOD_NOTFOUND);
}
}
/*
* Loop through the opsets exported by this module, and find the one
* we care about.
*/
break;
}
}
return (DACF_ERR_OPSET_NOTFOUND);
}
/*
* Call the appropriate routine in the target by looping across the
* ops until we find the one whose id matches opid.
*/
break;
}
}
return (DACF_ERR_OP_NOTFOUND);
}
dprintf("dacf_op_invoke: found op, invoking...\n");
/*
* Drop dacf_lock here, so that op_func's that cause drivers to
* get loaded don't wedge the system when they try to acquire dacf_lock
* to do matching.
*
* Mark that an invoke is happening to prevent recursive invokes
*/
/*
* Completed the invocation against module, so let go of it.
*/
/*
* Drop our r-lock on the module, now that we no longer need the module
* to stay loaded.
*/
if (rval == DACF_SUCCESS) {
return (DACF_SUCCESS);
} else {
return (DACF_ERR_OP_FAILED);
}
}
/*
* dacf_get_devspec()
* given a devspec-type as a string, return a corresponding dacf_devspec_t
*/
{
dacf_ds_t *p = &dacf_devspecs[0];
return (p->id);
}
p++;
}
return (DACF_DS_ERROR);
}
/*
* dacf_devspec_to_str()
* given a dacf_devspec_t, return a pointer to the human readable string
* representation of that device specifier.
*/
const char *
{
dacf_ds_t *p = &dacf_devspecs[0];
return (p->name);
}
p++;
}
return (NULL);
}
/*
* dacf_get_op()
* given a op name, returns the corresponding dacf_opid_t.
*/
{
dacf_opmap_t *p = &dacf_ops[0];
return (p->id);
}
p++;
}
return (DACF_OPID_ERROR);
}
/*
* dacf_opid_to_str()
* given a dacf_opid_t, return the human-readable op-name.
*/
const char *
{
dacf_opmap_t *p = &dacf_ops[0];
return (p->name);
}
p++;
}
return (NULL);
}
/*
* dacf_getopt()
* given an option specified as a string, add it to the bit-field of
* options given. Returns -1 if the option is unrecognized.
*/
int
{
dacf_opt_t *p = &dacf_options[0];
/*
* Look through the list for the option given
*/
return (0);
}
p++;
}
return (-1);
}
/*
* This family of functions forms the dacf interface which is exported to
* presented above this point.
*
* Note: These routines use a dacf_infohdl_t to struct ddi_minor_data * and
* assume that the resulting pointer is not to an alias node. That is true
* because dacf_op_invoke guarantees it by first resolving the alias.
*/
/*
* dacf_minor_name()
* given a dacf_infohdl_t, obtain the minor name of the device instance
* being configured.
*/
const char *
{
}
/*
* dacf_minor_number()
* given a dacf_infohdl_t, obtain the device minor number of the instance
* being configured.
*/
{
}
/*
* dacf_get_dev()
* given a dacf_infohdl_t, obtain the dev_t of the instance being
* configured.
*/
{
}
/*
* dacf_driver_name()
* given a dacf_infohdl_t, obtain the device driver name of the device
* instance being configured.
*/
const char *
{
}
/*
* dacf_devinfo_node()
* given a dacf_infohdl_t, obtain the dev_info_t of the device instance
* being configured.
*/
{
}
/*
* dacf_get_arg()
* given the dacf_arghdl_t passed to a op and the name of an argument,
* return the value of that argument.
*
* returns NULL if the argument is not found.
*/
const char *
{
}
}
return (NULL);
}
/*
* dacf_store_info()
* associate instance-specific data with a device instance. Future
* configuration ops invoked for this instance can retrieve this data using
* dacf_retrieve_info() below. Modules are responsible for cleaning up
* this data as appropriate, and should store NULL as the value of 'data'
* when the data is no longer valid.
*/
void
{
/*
* If the client is 'storing NULL' we can represent that by blowing
* the info entry out of the hash.
*/
} else {
/*
* mod_hash_replace can only fail on out of memory, but we sleep
* for memory in this hash, so it is safe to ignore the retval.
*/
}
}
/*
* dacf_retrieve_info()
* retrieve instance-specific data associated with a device instance.
*/
void *
{
void *data;
(mod_hash_val_t *)&data) != 0) {
return (NULL);
}
return (data);
}
/*
* dacf_makevp()
* make a vnode for the specified dacf_infohdl_t.
*/
struct vnode *
{
return (vp);
}