driver_lyr.c revision 20aa1b4d581d4b759a9db7f61e4ab39b88dcb9c4
/*
* 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 (c) 2013, Joyent, Inc. All rights reserved.
*/
/*
* Layered driver support.
*/
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
#include <sys/pathname.h>
#include <sys/dditypes.h>
#include <sys/ddi_impldefs.h>
#include <sys/autoconf.h>
#include <sys/sunldi_impl.h>
#include <sys/socketvar.h>
/*
* Device contract related
*/
#include <sys/contract_impl.h>
/*
* Define macros to manipulate snode, vnode, and open device flags
*/
/*
* Define macros for accessing layered driver hash structures
*/
/*
* Define layered handle flags used in the lh_type field
*/
/*
* Define macro for devid property lookups
*/
#define DEVID_PROP_FLAGS (DDI_PROP_DONTPASS | \
/*
* Dummy string for NDI events
*/
#define NDI_EVENT_SERVICE "NDI_EVENT_SERVICE"
static void ldi_ev_lock(void);
static void ldi_ev_unlock(void);
#ifdef LDI_OBSOLETE_EVENT
#endif
/*
* globals
*/
static size_t ldi_handle_hash_count;
/*
* Use of "ldi_ev_callback_list" must be protected by ldi_ev_lock()
* and ldi_ev_unlock().
*/
static struct ldi_ev_callback_list ldi_ev_callback_list;
static uint32_t ldi_ev_id_pool = 0;
struct ldi_ev_cookie {
char *ck_evname;
};
static struct ldi_ev_cookie ldi_ev_cookies[] = {
{ LDI_EV_DEGRADE, 0, CT_DEV_EV_DEGRADED},
{ LDI_EV_DEVICE_REMOVE, 0, 0},
{ NULL} /* must terminate list */
};
void
ldi_init(void)
{
int i;
for (i = 0; i < LH_HASH_SZ; i++) {
ldi_handle_hash[i] = NULL;
}
for (i = 0; i < LI_HASH_SZ; i++) {
ldi_ident_hash[i] = NULL;
}
/*
* Initialize the LDI event subsystem
*/
sizeof (ldi_ev_callback_impl_t),
}
/*
* LDI ident manipulation functions
*/
static uint_t
{
return ((uint_t)k);
} else if (dev != DDI_DEV_T_NONE) {
} else {
return (modid);
}
}
static struct ldi_ident **
{
break;
}
return (lipp);
}
static struct ldi_ident *
{
/* get the module id */
/* allocate a new ident in case we need it */
/* search the hash for a matching ident */
/* we found an ident in the hash */
return (retlip);
}
/* initialize the new ident */
/* add it to the ident hash */
return (lip);
}
static void
{
}
static void
{
/* there are more references to this ident */
return;
}
}
/*
* LDI handle manipulation functions
*/
static uint_t
handle_hash_func(void *vp)
{
return ((uint_t)k);
}
static struct ldi_handle **
{
break;
}
return (lhpp);
}
static struct ldi_handle *
{
return (retlhp);
}
static struct ldi_handle *
{
/* allocate a new handle in case we need it */
/* search the hash for a matching handle */
/* we found a handle in the hash */
"lh=0x%p, ident=0x%p, vp=0x%p, drv=%s, minor=0x%x",
return (retlhp);
}
/* initialize the new handle */
#ifdef LDI_OBSOLETE_EVENT
#endif
/* set the device type for this handle */
} else {
}
/* get holds on other objects */
ident_hold(ident);
/* add it to the handle hash */
"lh=0x%p, ident=0x%p, vp=0x%p, drv=%s, minor=0x%x",
return (lhp);
}
static void
{
struct ldi_handle **lhpp;
"lh=0x%p, ident=0x%p, vp=0x%p, drv=%s, minor=0x%x",
/* there are more references to this handle */
return;
}
#ifdef LDI_OBSOLETE_EVENT
#endif
}
#ifdef LDI_OBSOLETE_EVENT
/*
* LDI event manipulation functions
*/
static void
{
return;
}
}
static void
{
}
static void
void *arg, void *bus_impldata)
{
"event_cookie=0x%p, ldi_eventp=0x%p", "i_ldi_callback",
}
#endif
/*
* LDI open helper functions
*/
/* get a vnode to a device by dev_t and otyp */
static int
{
/* sanity check required input parameters */
return (EINVAL);
return (ENODEV);
return (0);
}
/* get a vnode to a device by pathname */
int
{
int ret;
/* sanity check required input parameters */
return (EINVAL);
if (modrootloaded) {
/* we don't want lookupname to fail because of credentials */
/*
* all lookups should be done in the global zone. but
* lookupnameat() won't actually do this if an absolute
* path is passed in. since the ldi interfaces require an
* absolute path we pass lookupnameat() a pointer to
* the character after the leading '/' and tell it to
* start searching at the current system root directory.
*/
/* restore this threads credentials */
if (ret == 0) {
return (ENXIO);
}
}
}
int spec_type;
/*
* Root is not mounted, the minor node is not specified,
* or an OBP path has been specified.
*/
/*
* Determine if path can be pruned to produce an
* OBP or devfs path for resolve_pathname.
*/
/*
* if no minor node was specified the DEFAULT minor node
* will be returned. if there is no DEFAULT minor node
* one will be fabricated of type S_IFCHR with the minor
* number equal to the instance number.
*/
if (ret != 0)
return (ENODEV);
}
return (0);
}
static int
{
char *devidstr;
/* convert devid as a string property */
return (0);
/*
* Search for the devid. For speed and ease in locking this
* code directly uses the property implementation. See
* ddi_common_devid_to_devlist() for a comment as to why.
*/
/* check if there is a DDI_DEV_T_NONE devid property */
/* a DDI_DEV_T_NONE devid exists and matchs */
return (1);
} else {
/* a DDI_DEV_T_NONE devid exists and doesn't match */
return (0);
}
}
/* check if there is a devt specific devid property */
/* a devt specific devid exists and matchs */
return (1);
} else {
/* a devt specific devid exists and doesn't match */
return (0);
}
}
/* we didn't find any devids associated with the device */
return (0);
}
/* get a handle to a device by devid and minor name */
int
{
/* sanity check required input parameters */
return (EINVAL);
return (ENODEV);
for (i = 0; i < ndevs; i++) {
continue;
/*
* now we have to verify that the devid of the disk
* still matches what was requested.
*
* we have to do this because the devid could have
* changed between the call to ddi_lyr_devid_to_devlist()
* and e_ddi_hold_devi_by_dev(). this is because when
* ddi_lyr_devid_to_devlist() returns a list of devts
* there is no kind of hold on those devts so a device
* could have been replaced out from under us in the
* interim.
*/
break;
}
if (i == ndevs)
return (ENODEV);
return (0);
}
/* given a vnode, open a device */
static int
{
struct ldi_handle *nlhp;
int err;
/* if the vnode passed in is not a device, then bail */
return (ENXIO);
/*
* the caller may have specified a node that
* doesn't have cb_ops defined. the ldi doesn't yet
* support opening devices without a valid cb_ops.
*/
return (ENXIO);
/* open the device */
return (err);
/* possible clone open, make sure that we still have a spec node */
/*
* allocating the layered handle took a new hold on the vnode
* so we can release the hold that was returned by the clone
* open
*/
"ldi clone open", (void *)nlhp));
} else {
"ldi open", (void *)nlhp));
}
return (0);
}
/* Call a drivers prop_op(9E) interface */
static int
{
int res;
/*
* we can only be invoked after a driver has been opened and
* someone has a layered handle to it, so there had better be
* a valid ops vector.
*/
/*
* Some nexus drivers incorrectly set cb_prop_op to nodev,
* nulldev or even NULL.
*/
return (DDI_PROP_NOT_FOUND);
}
/* check if this is actually DDI_DEV_T_ANY query */
if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
return (res);
}
static void
{
}
static caddr_t
{
struct prop_driver_data *pdd;
int pdd_size;
}
/*
* i_ldi_prop_op_typed() is a wrapper for i_ldi_prop_op that is used
* by the typed ldi property lookup interfaces.
*/
static int
{
/*
* first call the drivers prop_op() interface to allow it
* it to override default property values.
*/
if (res != DDI_PROP_SUCCESS)
return (DDI_PROP_NOT_FOUND);
/* sanity check the property length */
if (prop_len == 0) {
/*
* the ddi typed interfaces don't allow a drivers to
* create properties with a length of 0. so we should
* prevent drivers from returning 0 length dynamic
* properties for typed property lookups.
*/
return (DDI_PROP_NOT_FOUND);
}
/* sanity check the property length against the element size */
return (DDI_PROP_NOT_FOUND);
/*
* got it. now allocate a prop_driver_data struct so that the
* user can free the property via ddi_prop_free().
*/
/* lookup the property again, this time get the value */
if (res != DDI_PROP_SUCCESS) {
return (DDI_PROP_NOT_FOUND);
}
/* sanity check the property length */
if (prop_len == 0) {
return (DDI_PROP_NOT_FOUND);
}
/* sanity check the property length against the element size */
return (DDI_PROP_NOT_FOUND);
}
/*
* return the prop_driver_data struct and, optionally, the length
* of the data.
*/
return (DDI_PROP_SUCCESS);
}
/*
* i_check_string looks at a string property and makes sure its
* a valid null terminated string
*/
static int
{
int i;
for (i = 0; i < prop_len; i++) {
if (str[i] == '\0')
return (0);
}
return (1);
}
/*
* i_pack_string_array takes a a string array property that is represented
* as a concatenation of strings (with the NULL character included for
* each string) and converts it into a format that can be returned by
* ldi_prop_lookup_string_array.
*/
static int
char ***str_arrayp, int *nelemp)
{
/*
* first we need to sanity check the input string array.
* in essence this can be done my making sure that the last
* character of the array passed in is null. (meaning the last
* string in the array is NULL terminated.
*/
return (1);
/* now let's count the number of strings in the array */
if (str_concat[i] == '\0')
nelem++;
/* now let's allocate memory for the new packed property */
/* let's copy the actual string data into the new property */
/* now initialize the string array pointers */
for (i = 0; i < nelem; i++) {
}
/* set the return values */
*str_arrayp = str_array;
return (0);
}
/*
* LDI Project private device usage interfaces
*/
/*
* Get a count of how many devices are currentl open by different consumers
*/
int
{
return (ldi_handle_hash_count);
}
static void
{
/* get the target devt */
/* try to get the target dip */
} else if (dev != DDI_DEV_T_NONE) {
}
/* set the target information */
}
static int
{
int ret = LDI_USAGE_CONTINUE;
/* set the target device information */
/* get the source devt */
/* try to get the source dip */
} else if (dev != DDI_DEV_T_NONE) {
}
/* set the valid source information */
/*
* if the source ident represents either:
*
* - a kernel module (and not a device or device driver)
* - a device node
*
* then we currently have all the info we need to report the
* usage information so invoke the callback function.
*/
return (ret);
}
/*
* now this is kinda gross.
*
* what we do here is attempt to associate every device instance
* of the source driver on the system with the open target driver.
* we do this because we don't know which instance of the device
* could potentially access the lower device so we assume that all
* the instances could access it.
*
* there are two ways we could have gotten here:
*
* 1) this layered ident represents one created using only a
* major number or a driver module name. this means that when
* it was created we could not associate it with a particular
* dev_t or device instance.
*
* when could this possibly happen you ask?
*
* a perfect example of this is streams persistent links.
* when a persistant streams link is formed we can't associate
* the lower device stream with any particular upper device
* stream or instance. this is because any particular upper
* device stream could be closed, then another could be
* opened with a different dev_t and device instance, and it
* would still have access to the lower linked stream.
*
* since any instance of the upper streams driver could
* potentially access the lower stream whenever it wants,
* we represent that here by associating the opened lower
* device with every existing device instance of the upper
* streams driver.
*
* 2) This case should really never happen but we'll include it
* for completeness.
*
* it's possible that we could have gotten here because we
* have a dev_t for the upper device but we couldn't find a
* dip associated with that dev_t.
*
* the only types of devices that have dev_t without an
* associated dip are unbound DLPIv2 network devices. These
* types of devices exist to be able to attach a stream to any
* instance of a hardware network device. since these types of
* devices are usually hardware devices they should never
* really have other devices open.
*/
if (dev != DDI_DEV_T_NONE)
else
/* set the source dip */
/* invoke the callback function */
}
/* if there was a target dip, release it */
return (ret);
}
/*
* ldi_usage_walker() - this walker reports LDI kernel device usage
* information via the callback() callback function. the LDI keeps track
* of what devices are being accessed in its own internal data structures.
* this function walks those data structures to determine device usage.
*/
void
{
struct ldi_handle *lhp;
int i;
int ret = LDI_USAGE_CONTINUE;
for (i = 0; i < LH_HASH_SZ; i++) {
lhp = ldi_handle_hash[i];
/* invoke the devinfo callback function */
}
if (ret != LDI_USAGE_CONTINUE)
break;
}
}
/*
* LDI Project private interfaces (streams linking interfaces)
*
* Streams supports a type of built in device layering via linking.
* Certain types of streams drivers can be streams multiplexors.
* These operations allows other streams devices to be linked under the
* multiplexor. By definition all streams multiplexors are devices
* so this linking is a type of device layering where the multiplexor
* device is layered on top of the device linked below it.
*/
/*
* ldi_mlink_lh() is invoked when streams are linked using LDI handles.
* It is not used for normal I_LINKs and I_PLINKs using file descriptors.
*
* The streams framework keeps track of links via the file_t of the lower
* stream. The LDI keeps track of devices using a vnode. In the case
* of a streams link created via an LDI handle, fnk_lh() allocates
* a file_t that the streams framework can use to track the linkage.
*/
int
{
int err;
return (EINVAL);
/*
* create a new lower vnode and a file_t that points to it,
* streams linking requires a file_t. falloc() returns with
* fpdown locked.
*/
/* try to establish the link */
if (err != 0) {
/* the link failed, free the file_t and release the vnode */
}
return (err);
}
/*
* ldi_mlink_fp() is invoked for all successful streams linkages created
* via I_LINK and I_PLINK. ldi_mlink_fp() records the linkage information
* in its internal state so that the devinfo snapshot code has some
* observability into streams device linkage information.
*/
void
{
int ret;
/* if the lower stream is not a device then return */
return;
"stp=0x%p, fpdown=0x%p", "ldi_mlink_fp",
/* check if this was a plink via a layered handle */
if (lhlink) {
/*
* increment the common snode s_count.
*
* this is done because after the link operation there
* are two ways that s_count can be decremented.
*
* when the layered handle used to create the link is
* closed, spec_close() is called and it will decrement
* s_count in the common snode. if we don't increment
* s_count here then this could cause spec_close() to
* actually close the device while it's still linked
* under a multiplexer.
*
* also, when the lower stream is unlinked, closef() is
* called for the file_t associated with this snode.
* closef() will call spec_close(), which will decrement
* s_count. if we dont't increment s_count here then this
* could cause spec_close() to actually close the device
* while there may still be valid layered handles
* pointing to it.
*/
/*
* decrement the f_count.
* this is done because the layered driver framework does
* not actually cache a copy of the file_t allocated to
* do the link. this is done here instead of in ldi_mlink_lh()
* because there is a window in ldi_mlink_lh() between where
* milnk_file() returns and we would decrement the f_count
* when the stream could be unlinked.
*/
}
/*
* NOTE: here we rely on the streams subsystem not allowing
* a stream to be multiplexed more than once. if this
* changes, we break.
*
*/
/* get a layered ident for the upper stream */
if (type == LINKNORMAL) {
/*
* if the link is not persistant then we can associate
* the upper stream with a dev_t. this is because the
* upper stream is associated with a vnode, which is
* associated with a dev_t and this binding can't change
* during the life of the stream. since the link isn't
* persistant once the stream is destroyed the link is
* destroyed. so the dev_t will be valid for the life
* of the link.
*/
} else {
/*
* if the link is persistant we can only associate the
* link with a driver (and not a dev_t.) this is
* because subsequent opens of the upper device may result
* in a different stream (and dev_t) having access to
* the lower stream.
*
* for example, if the upper stream is closed after the
* persistant link operation is compleated, a subsequent
* open of the upper device will create a new stream which
* may have a different dev_t and an unlink operation
* can be performed using this new upper stream.
*/
}
}
void
{
struct ldi_handle *lhp;
int ret;
/* if the lower stream is not a device then return */
return;
"stp=0x%p, fpdown=0x%p", "ldi_munlink_fp",
/*
* NOTE: here we rely on the streams subsystem not allowing
* a stream to be multiplexed more than once. if this
* changes, we break.
*
*/
/*
* clear the owner for this snode
* see the comment in ldi_mlink_fp() for information about how
* the ident is allocated
*/
if (type == LINKNORMAL) {
} else {
}
}
/*
* LDI Consolidation private interfaces
*/
int
{
char *name;
return (EINVAL);
return (EINVAL);
return (EINVAL);
return (0);
}
{
return (lip);
}
/*
* LDI Public interfaces
*/
int
{
char *name;
return (EINVAL);
return (EINVAL);
return (EINVAL);
"%s: li=0x%p, mod=%s, minor=0x%x, stp=0x%p",
(void *)stp));
return (0);
}
int
{
char *name;
return (EINVAL);
return (EINVAL);
"%s: li=0x%p, mod=%s, minor=0x%x",
return (0);
}
int
{
char *name;
return (EINVAL);
return (EINVAL);
"%s: li=0x%p, mod=%s, dip=0x%p",
return (0);
}
int
{
char *name;
return (EINVAL);
return (EINVAL);
"%s: li=0x%p, mod=%s",
return (0);
}
void
{
char *name;
return;
name = ident->li_modname;
"%s: li=0x%p, mod=%s",
}
/* get a handle to a device by dev_t and otyp */
int
{
int ret;
/* sanity check required input parameters */
return (EINVAL);
return (ret);
}
return (ret);
}
/* get a handle to a device by pathname */
int
{
int ret;
/* sanity check required input parameters */
return (EINVAL);
return (ret);
return (ret);
}
/* get a handle to a device by devid and minor_name */
int
{
int ret;
/* sanity check required input parameters */
return (EINVAL);
return (ret);
return (ret);
}
int
{
int err = 0;
int notify = 0;
return (EINVAL);
#ifdef LDI_OBSOLETE_EVENT
/*
* Any event handlers should have been unregistered by the
* time ldi_close() is called. If they haven't then it's a
* bug.
*
* In a debug kernel we'll panic to make the problem obvious.
*/
/*
* On a production kernel we'll "do the right thing" (unregister
* the event handlers) and then complain about having to do the
* work ourselves.
*/
err = 1;
}
if (err) {
"failed to unregister layered event handlers before "
}
#endif
/* do a layered close on the device */
/*
* Search the event callback list for callbacks with this
* handle. There are 2 cases
* 1. Called in the context of a notify. The handle consumer
* is releasing its hold on the device to allow a reconfiguration
* of the device. Simply NULL out the handle and the notify callback.
* The finalize callback is still available so that the consumer
* knows of the final disposition of the device.
* 2. Not called in the context of notify. NULL out the handle as well
* as the notify and finalize callbacks. Since the consumer has
* closed the handle, we assume it is not interested in the
* notify and finalize callbacks.
*/
ldi_ev_lock();
notify = 1;
continue;
if (!notify) {
}
}
if (notify)
/*
* Free the handle even if the device close failed. why?
*
* If the device close failed we can't really make assumptions
* about the devices state so we shouldn't allow access to the
* device via this handle any more. If the device consumer wants
* to access the device again they should open it again.
*
* in other places like spec_close() and closeandsetf().
*/
return (err);
}
int
{
int ret;
return (EINVAL);
} else {
return (ENOTSUP);
}
return (ret);
}
int
{
int ret;
return (EINVAL);
} else {
return (ENOTSUP);
}
return (ret);
}
int
{
int otyp;
int blkshift;
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* Determine device type (char or block).
* property value. Block devices may support
*/
return (DDI_FAILURE);
if (ldi_prop_exists(lh,
"Nblocks", 0);
"blksize", DEV_BSIZE);
"device-blksize", DEV_BSIZE);
/* blksize must be a power of two */
/*
* We don't support Nblocks values that don't have
* an accurate uint64_t byte count representation.
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
if (ldi_prop_exists(lh,
"nblocks", 0);
"blksize", DEV_BSIZE);
"device-blksize", DEV_BSIZE);
/* blksize must be a power of two */
/*
* We don't support nblocks values that don't have an
* accurate uint64_t byte count representation.
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
}
if (ldi_prop_exists(lh,
return (DDI_SUCCESS);
}
if (ldi_prop_exists(lh,
return (DDI_SUCCESS);
}
/* unable to determine device size */
return (DDI_FAILURE);
}
int
{
return (EINVAL);
/*
* if the data pointed to by arg is located in the kernel then
* make sure the FNATIVE flag is set.
*/
/*
* Some drivers assume that rvalp will always be non-NULL, so in
* an attempt to avoid panics if the caller passed in a NULL
* value, update rvalp to point to a temporary variable.
*/
/*
* if we get an I_PLINK from within the kernel the
* arg is a layered handle pointer instead of
* a file descriptor, so we translate this ioctl
* into a private one that can handle this.
*/
cmd = _I_PLINK_LH;
} else {
return (ENOTSUP);
}
return (ret);
}
int
{
int ret;
return (EINVAL);
} else {
return (ENOTSUP);
}
return (ret);
}
int
{
int ret;
return (DDI_PROP_INVAL_ARG);
return (DDI_PROP_INVAL_ARG);
return (DDI_PROP_INVAL_ARG);
/*
* try to find the associated dip,
* this places a hold on the driver
*/
return (DDI_PROP_NOT_FOUND);
return (ret);
}
int
{
return (EINVAL);
/* this entry point is only supported for cb devices */
return (ENOTSUP);
return (bdev_strategy(bp));
}
int
{
return (EINVAL);
/* this entry point is only supported for cb devices */
return (ENOTSUP);
}
int
{
return (EINVAL);
/* this entry point is only supported for cb devices */
return (ENOTSUP);
}
int
{
return (EINVAL);
/* this entry point is only supported for cb devices */
return (ENOTSUP);
/*
* Kaio is only supported on block devices.
*/
return (ENOTSUP);
return (ENOTSUP);
}
int
{
return (EINVAL);
/* this entry point is only supported for cb devices */
return (ENOTSUP);
/*
* Kaio is only supported on block devices.
*/
return (ENOTSUP);
return (ENOTSUP);
}
int
{
int ret;
return (EINVAL);
return (ENOTSUP);
}
/*
* If we don't have db_credp, set it. Note that we can not be called
* from interrupt context.
*/
/* Send message while honoring flow control */
return (ret);
}
int
{
return (EINVAL);
return (ENOTSUP);
/* Convert from nanoseconds to milliseconds */
return (EINVAL);
} else
timout = -1;
/* Wait for timeout millseconds for a message */
pri = 0;
return (ret);
}
int
{
return (EINVAL);
return (0);
}
int
{
return (EINVAL);
return (0);
}
int
{
int ret;
return (EINVAL);
if (ret != DDI_SUCCESS)
return (ENOTSUP);
return (0);
}
int
{
return (EINVAL);
if (ret != DDI_SUCCESS)
return (ENOTSUP);
return (0);
}
int
{
int res;
return (DDI_PROP_INVAL_ARG);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
/* if we got it then return it */
if (res == DDI_PROP_SUCCESS) {
return (res);
}
}
/* call the normal property interfaces */
return (res);
}
int
{
int res;
return (DDI_PROP_INVAL_ARG);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
int prop_len;
/* if we got it then return it */
if (res == DDI_PROP_SUCCESS) {
return (res);
}
}
/* call the normal property interfaces */
return (res);
}
int
{
int res;
return (DDI_PROP_INVAL_ARG);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
char *prop_val;
int prop_len;
/* if we got it then return it */
if (res == DDI_PROP_SUCCESS) {
char **str_array;
int nelem;
/*
* pack the returned string array into the format
* our callers expect
*/
return (res);
}
/*
* the format of the returned property must have
* been bad so throw it out
*/
}
}
/* call the normal property interfaces */
return (res);
}
int
{
int res;
return (DDI_PROP_INVAL_ARG);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
char *prop_val;
int prop_len;
/* if we got it then return it */
if (res == DDI_PROP_SUCCESS) {
/*
* sanity check the vaule returned.
*/
} else {
return (res);
}
}
}
/* call the normal property interfaces */
#ifdef DEBUG
if (res == DDI_PROP_SUCCESS) {
/*
* keep ourselves honest
* make sure the framework returns strings in the
* same format as we're demanding from drivers.
*/
struct prop_driver_data *pdd;
int pdd_prop_size;
sizeof (struct prop_driver_data);
}
#endif /* DEBUG */
return (res);
}
int
{
int res;
return (DDI_PROP_INVAL_ARG);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
int prop_len;
/* if we got it then return it */
if (res == DDI_PROP_SUCCESS) {
return (res);
}
}
/* call the normal property interfaces */
return (res);
}
int
{
int res;
return (defvalue);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
int prop_val;
int prop_len;
/*
* first call the drivers prop_op interface to allow it
* it to override default property values.
*/
prop_len = sizeof (int);
/* if we got it then return it */
if ((res == DDI_PROP_SUCCESS) &&
(prop_len == sizeof (int))) {
return (res);
}
}
/* call the normal property interfaces */
return (res);
}
{
return (defvalue);
flags |= DDI_UNBND_DLPI2;
} else if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
int prop_len;
/*
* first call the drivers prop_op interface to allow it
* it to override default property values.
*/
/* if we got it then return it */
if ((res == DDI_PROP_SUCCESS) &&
return (res);
}
}
/* call the normal property interfaces */
return (res);
}
int
{
return (0);
/* if NULL dip, prop does NOT exist */
return (0);
if (flags & LDI_DEV_T_ANY) {
flags &= ~LDI_DEV_T_ANY;
dev = DDI_DEV_T_ANY;
}
/*
* first call the drivers prop_op interface to allow it
* it to override default property values.
*/
if (res == DDI_PROP_SUCCESS) {
return (1);
}
/* call the normal property interfaces */
return (res);
}
#ifdef LDI_OBSOLETE_EVENT
int
{
int res;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
"dip=0x%p, event_cookiep=0x%p", "ldi_get_eventcookie",
return (res);
}
int
{
int res;
return (DDI_FAILURE);
return (DDI_FAILURE);
"event callback", "ldi_add_event_handler"));
return (res);
}
"ldi_eventp=0x%p, cb_id=0x%p", "ldi_add_event_handler",
return (res);
}
int
{
int res;
return (DDI_FAILURE);
!= DDI_SUCCESS) {
"event callback", "ldi_remove_event_handler"));
return (res);
}
return (res);
}
#endif
/*
* Here are some definitions of terms used in the following LDI events
* code:
*
* "LDI events" AKA "native events": These are events defined by the
* "new" LDI event framework. These events are serviced by the LDI event
* framework itself and thus are native to it.
*
* "LDI contract events": These are contract events that correspond to the
* LDI events. This mapping of LDI events to contract events is defined by
* the ldi_ev_cookies[] array above.
*
* NDI events: These are events which are serviced by the NDI event subsystem.
* LDI subsystem just provides a thin wrapper around the NDI event interfaces
* These events are therefore *not* native events.
*/
static int
ldi_native_event(const char *evname)
{
int i;
return (1);
}
return (0);
}
static uint_t
ldi_ev_sync_event(const char *evname)
{
int i;
return (ldi_ev_cookies[i].ck_sync);
}
/*
* This should never happen until non-contract based
* LDI events are introduced. If that happens, we will
* use a "special" token to indicate that there are no
* contracts corresponding to this LDI event.
*/
return (0);
}
static uint_t
ldi_contract_event(const char *evname)
{
int i;
return (ldi_ev_cookies[i].ck_ctype);
}
/*
* This should never happen until non-contract based
* LDI events are introduced. If that happens, we will
* use a "special" token to indicate that there are no
* contracts corresponding to this LDI event.
*/
return (0);
}
char *
{
int i;
if (&ldi_ev_cookies[i] == cookie_impl) {
ldi_ev_cookies[i].ck_evname));
return (ldi_ev_cookies[i].ck_evname);
}
}
/*
* Not an LDI native event. Must be NDI event service.
* Just return a generic string
*/
return (NDI_EVENT_SERVICE);
}
static int
{
int i;
if (&ldi_ev_cookies[i] == cookie_impl) {
return (1);
}
}
return (0);
}
static ldi_ev_cookie_t
ldi_get_native_cookie(const char *evname)
{
int i;
return ((ldi_ev_cookie_t)&ldi_ev_cookies[i]);
}
}
return (NULL);
}
/*
* ldi_ev_lock() needs to be recursive, since layered drivers may call
* other LDI interfaces (such as ldi_close() from within the context of
* a notify callback. Since the notify callback is called with the
* ldi_ev_lock() held and ldi_close() also grabs ldi_ev_lock, the lock needs
* to be recursive.
*/
static void
ldi_ev_lock(void)
{
} else {
while (ldi_ev_callback_list.le_busy)
}
}
static void
ldi_ev_unlock(void)
{
if (ldi_ev_callback_list.le_busy == 0) {
}
}
int
{
int res;
return (LDI_EV_FAILURE);
}
/*
* First check if it is a LDI native event
*/
if (tcookie) {
return (LDI_EV_SUCCESS);
}
/*
* Not a LDI native event. Try NDI event services
*/
"handle: %p", (void *)handlep);
return (LDI_EV_FAILURE);
}
if (res == DDI_SUCCESS) {
return (LDI_EV_SUCCESS);
} else {
return (LDI_EV_FAILURE);
}
}
/*ARGSUSED*/
static void
{
}
int
{
int ddi_event;
return (LDI_EV_FAILURE);
}
return (LDI_EV_FAILURE);
}
return (LDI_EV_FAILURE);
}
*id = 0;
"LDI handle: %p", (void *)lhp);
return (LDI_EV_FAILURE);
}
ddi_event = 0;
if (!ldi_native_cookie(cookie)) {
/*
* NDI event services only accept finalize
*/
"Only finalize"
" callback supported with this cookie",
"ldi_ev_register_callbacks",
return (LDI_EV_FAILURE);
}
i_ldi_ev_callback, (void *)lecp,
!= DDI_SUCCESS) {
"ddi_add_event_handler failed"));
return (LDI_EV_FAILURE);
}
ddi_event = 1;
"ddi_add_event_handler success"));
}
ldi_ev_lock();
/*
*/
if (!ddi_event)
else
return (LDI_EV_SUCCESS);
}
static int
{
return (0);
if (dev != DDI_DEV_T_ANY) {
return (0);
}
return (1);
}
/*
* LDI framework function to post a "notify" event to all layered drivers
* that have registered for that event
*
* Returns:
* LDI_EV_SUCCESS - registered callbacks allow event
* LDI_EV_FAILURE - registered callbacks block event
* LDI_EV_NONE - No matching LDI callbacks
*
* This function is *not* to be called by layered drivers. It is for I/O
* framework code in Solaris, such as the I/O retire code and DR code
* to call while servicing a device event such as offline or degraded.
*/
int
void *ev_data)
{
int ret;
char *lec_event;
ret = LDI_EV_NONE;
ldi_ev_lock();
/* Check if matching device */
continue;
/*
* Consumer has unregistered the handle and so
* is no longer interested in notify events.
*/
"handle, skipping"));
continue;
}
"callback. skipping"));
continue; /* not interested in notify */
}
/*
* Check if matching event
*/
continue;
}
" FAILURE"));
break;
}
/* We have a matching callback that allows the event to occur */
}
if (ret != LDI_EV_FAILURE)
goto out;
/*
* Undo notifies already sent
*/
/*
* Check if matching device
*/
continue;
"skipping"));
continue; /* not interested in finalize */
}
/*
* it is possible that in response to a notify event a
* layered driver closed its LDI handle so it is ok
* to have a NULL LDI handle for finalize. The layered
* driver is expected to maintain state in its "arg"
* parameter to keep track of the closed device.
*/
/* Check if matching event */
continue;
}
/*
* If LDI native event and LDI handle closed in context
* of notify, NULL out the finalize callback as we have
* already called the 1 finalize above allowed in this situation
*/
"ldi_invoke_notify(): NULL-ing finalize after "
"calling 1 finalize following ldi_close"));
}
}
out:
if (ret == LDI_EV_NONE) {
"LDI callbacks"));
}
return (ret);
}
/*
* Framework function to be called from a layered driver to propagate
* LDI "notify" events to exported minors.
*
* This function is a public interface exported by the LDI framework
* for use by layered drivers to propagate device events up the software
* stack.
*/
int
{
int retc;
int retl;
if (!ldi_ev_sync_event(evname)) {
"negotiatable event", evname);
return (LDI_EV_SUCCESS);
}
if (major == DDI_MAJOR_T_NONE) {
"for device %s", path);
return (LDI_EV_FAILURE);
}
/*
* Generate negotiation contract events on contracts (if any) associated
* with this minor.
*/
return (LDI_EV_FAILURE);
}
if (retl == LDI_EV_FAILURE) {
"returned FAILURE. Calling contract negend"));
return (LDI_EV_FAILURE);
}
/*
* The very fact that we are here indicates that there is a
* LDI callback (and hence a constraint) for the retire of the
* HW device. So we just return success even if there are no
* contracts or LDI callbacks against the minors layered on top
* of the HW minors
*/
return (LDI_EV_SUCCESS);
}
/*
* LDI framework function to invoke "finalize" callbacks for all layered
* drivers that have registered callbacks for that event.
*
* This function is *not* to be called by layered drivers. It is for I/O
* framework code in Solaris, such as the I/O retire code and DR code
* to call while servicing a device event such as offline or degraded.
*/
void
int ldi_result, void *ev_data)
{
char *lec_event;
int found = 0;
ldi_ev_lock();
"finalize. Skipping"));
continue; /* Not interested in finalize */
}
/*
* Check if matching device
*/
continue;
/*
* It is valid for the LDI handle to be NULL during finalize.
* The layered driver may have done an LDI close in the notify
* callback.
*/
/*
* Check if matching event
*/
"matching event {%s,%s}. Skipping",
continue;
}
found = 1;
/*
* If LDI native event and LDI handle closed in context
* of notify, NULL out the finalize callback as we have
* already called the 1 finalize above allowed in this situation
*/
"ldi_invoke_finalize(): NULLing finalize after "
"calling 1 finalize following ldi_close"));
}
}
if (found)
return;
}
/*
* Framework function to be called from a layered driver to propagate
* LDI "finalize" events to exported minors.
*
* This function is a public interface exported by the LDI framework
* for use by layered drivers to propagate device events up the software
* stack.
*/
void
{
char *evname;
if (major == DDI_MAJOR_T_NONE) {
"for device %s", path);
return;
}
}
int
{
if (id == 0) {
return (LDI_EV_FAILURE);
}
(void *)id));
ldi_ev_lock();
/*
* If there is a walk in progress, shift that walk
* along to the next element so that we can remove
* this one. This allows us to unregister an arbitrary
* number of callbacks from within a callback.
*
* See the struct definition (in sunldi_impl.h) for
* more information.
*/
}
}
(void *)id);
return (LDI_EV_SUCCESS);
}
!= DDI_SUCCESS) {
"for id (%p)", (void *)id);
ldi_ev_lock();
return (LDI_EV_FAILURE);
}
"service removal succeeded"));
} else {
"LDI native callbacks"));
}
return (LDI_EV_SUCCESS);
}