sunmdi.c revision 72a500659c3f44bbdccd0f1edf8e290ab0f1a9b6
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Multipath driver interface (MDI) implementation; see mdi_impl.h for a more
* detailed discussion of the overall mpxio architecture.
*
* Default locking order:
*
* _NOTE(LOCK_ORDER(mdi_mutex, mdi_phci::ph_mutex))
* _NOTE(LOCK_ORDER(mdi_mutex, mdi_client::ct_mutex))
* _NOTE(LOCK_ORDER(mdi_phci::ph_mutex mdi_pathinfo::pi_mutex))
* _NOTE(LOCK_ORDER(mdi_phci::ph_mutex mdi_client::ct_mutex))
* _NOTE(LOCK_ORDER(mdi_client::ct_mutex mdi_pathinfo::pi_mutex))
*/
#include <sys/bootconf.h>
#include <sys/ddipropdefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/mdi_impldefs.h>
#ifdef DEBUG
int mdi_debug = 1;
#else /* !DEBUG */
#endif /* DEBUG */
extern pri_t minclsyspri;
extern int modrootloaded;
/*
* Global mutex:
* Protects vHCI list and structure members, pHCI and Client lists.
*/
/*
* Registered vHCI class driver lists
*/
int mdi_vhci_count;
/*
* Client Hash Table size
*/
static int mdi_client_table_size = CLIENT_HASH_TABLE_SIZE;
/*
* taskq interface definitions
*/
#define MDI_TASKQ_N_THREADS 8
#define MDI_TASKQ_PRI minclsyspri
static int mdi_max_bus_config_threads = 100;
/*
* To reduce unnecessary BUS_CONFIG_ALLs, do not BUS_CONFIG_ALL phcis in the
* context of a BUS_CONFIG_ONE if a BUS_CONFIG_ALL has already been performed
* in the last mdi_bus_config_timeout seconds.
*/
/*
*/
const char *mdi_component_prop = "mpxio-component";
const char *mdi_component_prop_vhci = "vhci";
const char *mdi_component_prop_phci = "phci";
const char *mdi_component_prop_client = "client";
/*
* MDI client global unique identifier property name
*/
const char *mdi_client_guid_prop = "client-guid";
/*
*/
const char *mdi_load_balance = "load-balance";
const char *mdi_load_balance_none = "none";
const char *mdi_load_balance_rr = "round-robin";
const char *mdi_load_balance_lba = "logical-block";
/*
* Obsolete vHCI class definition; to be removed after Leadville update
*/
const char *mdi_vhci_class_scsi = MDI_HCI_CLASS_SCSI;
static char vhci_greeting[] =
"\tThere already exists one vHCI driver for class %s\n"
"\tOnly one vHCI driver for each class is allowed\n";
/*
* Static function prototypes
*/
static void i_mdi_phci_post_detach(dev_info_t *,
ddi_detach_cmd_t, int);
static int i_mdi_client_pre_detach(dev_info_t *,
static void i_mdi_client_post_detach(dev_info_t *,
ddi_detach_cmd_t, int);
static void i_mdi_pm_hold_pip(mdi_pathinfo_t *);
static void i_mdi_pm_rele_pip(mdi_pathinfo_t *);
static void i_mdi_pm_hold_client(mdi_client_t *, int);
static void i_mdi_pm_rele_client(mdi_client_t *, int);
static void i_mdi_pm_reset_client(mdi_client_t *);
static void i_mdi_pm_hold_all_phci(mdi_client_t *);
static int i_mdi_power_all_phci(mdi_client_t *);
/*
* Internal mdi_pathinfo node functions
*/
static int i_mdi_pi_kstat_create(mdi_pathinfo_t *);
static void i_mdi_pi_kstat_destroy(mdi_pathinfo_t *);
static mdi_vhci_t *i_mdi_vhci_class2vhci(char *);
static void i_mdi_phci_get_client_lock(mdi_phci_t *,
mdi_client_t *);
static void i_mdi_phci_unlock(mdi_phci_t *);
mdi_client_t *, int);
mdi_client_t *);
static void i_mdi_client_remove_path(mdi_client_t *,
mdi_pathinfo_t *);
static int i_mdi_pi_state_change(mdi_pathinfo_t *,
mdi_pathinfo_state_t, int);
static int i_mdi_pi_offline(mdi_pathinfo_t *, int);
char **, int, int);
static void i_mdi_client_update_state(mdi_client_t *);
static int i_mdi_client_compute_state(mdi_client_t *,
mdi_phci_t *);
static void i_mdi_client_unlock(mdi_client_t *);
int);
/*
* Failover related function prototypes
*/
static int i_mdi_failover(void *);
/*
* misc internal functions
*/
static int i_mdi_get_hash_key(char *);
static int i_map_nvlist_error_to_mdi(int);
static void i_mdi_report_path_state(mdi_client_t *,
mdi_pathinfo_t *);
/* called once when first vhci registers with mdi */
static void
{
static int initialized = 0;
if (initialized)
return;
initialized = 1;
/*
* Create our taskq resources
*/
}
/*
* mdi_get_component_type():
* Return mpxio component type
* Return Values:
* MDI_COMPONENT_NONE
* MDI_COMPONENT_VHCI
* MDI_COMPONENT_PHCI
* MDI_COMPONENT_CLIENT
* XXX This doesn't work under multi-level MPxIO and should be
* removed when clients migrate mdi_is_*() interfaces.
*/
int
{
}
/*
* mdi_vhci_register():
* Register a vHCI module with the mpxio framework
* mdi_vhci_register() is called by vHCI drivers to register the
* 'class_driver' vHCI driver and its MDI entrypoints with the
* mpxio framework. The vHCI driver must call this interface as
* part of its attach(9e) handler.
* Competing threads may try to attach mdi_vhci_register() as
* the vHCI drivers are loaded and attached as a result of pHCI
* driver instance registration (mdi_phci_register()) with the
* framework.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
/*ARGSUSED*/
int
int flags)
{
i_mdi_init();
/*
* Scan for already registered vhci
*/
/*
* vHCI has already been created. Check for valid
* vHCI ops registration. We only support one vHCI
* module per class
*/
return (MDI_FAILURE);
}
break;
}
}
/*
* if not yet created, create the vHCI component
*/
char *load_balance;
/*
* Allocate and initialize the mdi extensions
*/
KM_SLEEP);
== 0) {
}
}
/*
* Store the vHCI ops vectors
*/
/*
* other members of vh_bus_config are initialized by
* the above kmem_zalloc of the vhci structure.
*/
if (mdi_vhci_head == NULL) {
mdi_vhci_head = vh;
}
if (mdi_vhci_tail) {
}
mdi_vhci_tail = vh;
}
/*
* Claim the devfs node as a vhci component
*/
/*
* Initialize our back reference from dev_info node
*/
return (MDI_SUCCESS);
}
/*
* mdi_vhci_unregister():
* Unregister a vHCI module from mpxio framework
* mdi_vhci_unregister() is called from the detach(9E) entrypoint
* of a vhci to unregister it from the framework.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
/*ARGSUSED*/
int
{
/*
* Check for invalid VHCI
*/
return (MDI_FAILURE);
/*
* Scan the list of registered vHCIs for a match
*/
break;
}
return (MDI_FAILURE);
}
/*
* Check the pHCI and client count. All the pHCIs and clients
* should have been unregistered, before a vHCI can be
* unregistered.
*/
"!mdi_vhci_unregister: pHCI in registered state.\n"));
return (MDI_FAILURE);
}
/*
* Remove the vHCI from the global list
*/
if (vh == mdi_vhci_head) {
} else {
}
if (vh == mdi_vhci_tail) {
}
mdi_client_table_size * sizeof (struct client_hash));
/*
* there must be no more tasks on the bus config taskq as the vhci
* driver can not be detached while bus config is in progress.
*/
}
return (MDI_SUCCESS);
}
/*
* i_mdi_vhci_class2vhci():
* Look for a matching vHCI module given a vHCI class name
* Return Values:
* Handle to a vHCI component
* NULL
*/
static mdi_vhci_t *
i_mdi_vhci_class2vhci(char *class)
{
break;
}
}
return (vh);
}
/*
* i_devi_get_vhci():
* Utility function to get the handle to a vHCI component
* Return Values:
* Handle to a vHCI component
* NULL
*/
{
}
return (vh);
}
/*
* mdi_phci_register():
* Register a pHCI module with mpxio framework
* mdi_phci_register() is called by pHCI drivers to register with
* the mpxio framework and a specific 'class_driver' vHCI. The
* pHCI driver must call this interface as part of its attach(9e)
* handler.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
/*ARGSUSED*/
int
{
mdi_phci_t *ph;
mdi_vhci_t *vh;
char *data;
char *pathname;
/*
* Check for mpxio-disable property. Enable mpxio if the property is
* missing or not set to "yes".
* If the property is set to "yes" then emit a brief message.
*/
&data) == DDI_SUCCESS)) {
"?%s (%s%d) multipath capabilities "
ddi_driver_name(pdip)));
return (MDI_FAILURE);
}
}
/*
* Search for a matching vHCI
*/
return (MDI_FAILURE);
}
ph->ph_unstable = 0;
ph->ph_vprivate = 0;
}
if (vh->vh_phci_tail) {
}
vh->vh_phci_count++;
/* to force discovery of all phci children during busconfig */
return (MDI_SUCCESS);
}
/*
* mdi_phci_unregister():
* Unregister a pHCI module from mpxio framework
* mdi_phci_unregister() is called by the pHCI drivers from their
* detach(9E) handler to unregister their instances from the
* framework.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
/*ARGSUSED*/
int
{
mdi_vhci_t *vh;
mdi_phci_t *ph;
"!pHCI unregister: Not a valid pHCI"));
return (MDI_FAILURE);
}
"!pHCI unregister: Not a valid vHCI"));
return (MDI_FAILURE);
}
while (tmp) {
break;
}
}
} else {
}
}
vh->vh_phci_count--;
/*
* If no busconfig is in progress, release the phci busconfig resources.
* We only need vh->vh_phci_count of busconfig resources.
*/
int count;
while (count--) {
}
}
return (MDI_SUCCESS);
}
/*
* i_devi_get_phci():
* Utility function to return the phci extensions.
*/
static mdi_phci_t *
{
}
return (ph);
}
/*
* mdi_phci_path2devinfo():
* Utility function to search for a valid phci device given
* the devfs pathname.
*/
{
char *temp_pathname;
mdi_vhci_t *vh;
mdi_phci_t *ph;
/*
* Invalid vHCI component, return failure
*/
return (NULL);
}
*temp_pathname = '\0';
break;
}
}
}
return (pdip);
}
/*
* mdi_phci_get_path_count():
* get number of path information nodes associated with a given
* pHCI device.
*/
int
{
mdi_phci_t *ph;
int count = 0;
}
return (count);
}
/*
* i_mdi_phci_lock():
* Lock a pHCI device
* Return Values:
* None
* Note:
* The default locking order is:
* _NOTE(LOCK_ORDER(mdi_phci::ph_mutex mdi_pathinfo::pi_mutex))
* But there are number of situations where locks need to be
* grabbed in reverse order. This routine implements try and lock
* mechanism depending on the requested parameter option.
*/
static void
{
if (pip) {
/* Reverse locking is requested. */
while (MDI_PHCI_TRYLOCK(ph) == 0) {
/*
* tryenter failed. Try to grab again
* after a small delay
*/
delay(1);
}
} else {
}
}
/*
* i_mdi_phci_get_client_lock():
* Lock a pHCI device
* Return Values:
* None
* Note:
* The default locking order is:
* _NOTE(LOCK_ORDER(mdi_phci::ph_mutex mdi_client::ct_mutex))
* But there are number of situations where locks need to be
* grabbed in reverse order. This routine implements try and lock
* mechanism depending on the requested parameter option.
*/
static void
{
if (ct) {
/* Reverse locking is requested. */
while (MDI_PHCI_TRYLOCK(ph) == 0) {
/*
* tryenter failed. Try to grab again
* after a small delay
*/
delay(1);
}
} else {
}
}
/*
* i_mdi_phci_unlock():
* Unlock the pHCI component
*/
static void
{
}
/*
* i_mdi_devinfo_create():
* create client device's devinfo node
* Return Values:
* dev_info
* NULL
* Notes:
*/
static dev_info_t *
{
/* Verify for duplicate entry */
if (cdip) {
"i_mdi_devinfo_create: client dip %p already exists",
(void *)cdip);
}
DEVI_SID_NODEID, &cdip);
} else {
DEVI_SID_NODEID, &cdip);
}
goto fail;
/*
* Create component type and Global unique identifier
* properties
*/
goto fail;
}
/* Decorate the node with compatible property */
if (compatible &&
goto fail;
}
return (cdip);
fail:
if (cdip) {
(void) ndi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
}
return (NULL);
}
/*
* i_mdi_devinfo_find():
* Find a matching devinfo node for given client node name
* and its guid.
* Return Values:
* Handle to a dev_info node or NULL
*/
static dev_info_t *
{
char *data;
int circular;
continue;
}
&data) != DDI_PROP_SUCCESS) {
continue;
}
continue;
}
break;
}
return (cdip);
}
/*
* i_mdi_devinfo_remove():
* Remove a client device node
*/
static int
{
int rv = MDI_SUCCESS;
if (rv != NDI_SUCCESS) {
" failed. cdip = %p\n", cdip));
}
/*
* Convert to MDI error code
*/
switch (rv) {
case NDI_SUCCESS:
rv = MDI_SUCCESS;
break;
case NDI_BUSY:
break;
default:
rv = MDI_FAILURE;
break;
}
}
return (rv);
}
/*
* i_devi_get_client()
* Utility function to get mpxio component extensions
*/
static mdi_client_t *
{
if (MDI_CLIENT(cdip)) {
}
return (ct);
}
/*
* i_mdi_is_child_present():
* Search for the presence of client device dev_info node
*/
static int
{
int rv = MDI_FAILURE;
int circular;
while (dip) {
rv = MDI_SUCCESS;
break;
}
}
return (rv);
}
/*
* i_mdi_client_lock():
* Grab client component lock
* Return Values:
* None
* Note:
* The default locking order is:
* _NOTE(LOCK_ORDER(mdi_client::ct_mutex mdi_pathinfo::pi_mutex))
* But there are number of situations where locks need to be
* grabbed in reverse order. This routine implements try and lock
* mechanism depending on the requested parameter option.
*/
static void
{
if (pip) {
/*
* Reverse locking is requested.
*/
while (MDI_CLIENT_TRYLOCK(ct) == 0) {
/*
* tryenter failed. Try to grab again
* after a small delay
*/
delay(1);
}
} else {
}
}
/*
* i_mdi_client_unlock():
* Unlock a client component
*/
static void
{
}
/*
* i_mdi_client_alloc():
* Allocate and initialize a client structure. Caller should
* hold the global mdi_mutex.
* Return Values:
* Handle to a client component
*/
/*ARGSUSED*/
static mdi_client_t *
{
/*
* Allocate and initialize a component structure.
*/
goto fail;
goto fail;
goto fail;
ct->ct_failover_flags = 0;
ct->ct_failover_status = 0;
ct->ct_unstable = 0;
goto fail;
ct->ct_path_count = 0;
/*
* Add this client component to our client hash queue
*/
return (ct);
fail:
if (guid)
if (drvname)
if (lb_args)
return (NULL);
}
/*
* i_mdi_client_enlist_table():
* Attach the client device to the client hash table. Caller
* should hold the mdi_mutex
*/
static void
{
int index;
struct client_hash *head;
head->ct_hash_count++;
vh->vh_client_count++;
}
/*
* i_mdi_client_delist_table():
* Attach the client device to the client hash table.
* Caller should hold the mdi_mutex
*/
static void
{
int index;
char *guid;
struct client_hash *head;
break;
}
}
if (next) {
head->ct_hash_count--;
} else {
}
vh->vh_client_count--;
}
}
/*
* i_mdi_client_free():
* Free a client component
*/
static int
{
int rv = MDI_SUCCESS;
/*
* Clear out back ref. to dev_info_t node
*/
/*
* Remove this client from our hash queue
*/
/*
* Uninitialize and free the component
*/
}
return (rv);
}
/*
* i_mdi_client_find():
* Find the client structure corresponding to a given guid
* Caller should hold the mdi_mutex
*/
static mdi_client_t *
{
int index;
struct client_hash *head;
break;
}
}
return (ct);
}
/*
* i_mdi_client_update_state():
* Compute and update client device state
* Notes:
* A client device can be in any of three possible states:
*
* MDI_CLIENT_STATE_OPTIMAL - Client in optimal state with more
* MDI_CLIENT_STATE_DEGRADED - Client device in degraded state with
* no alternate paths available as standby. A failure on the online
* would result in loss of access to device data.
* MDI_CLIENT_STATE_FAILED - Client device in failed state with
* no paths available to access the device.
*/
static void
{
int state;
}
/*
* i_mdi_client_compute_state():
* Compute client device state
*
* mdi_phci_t * Pointer to pHCI structure which should
* while computing the new value. Used by
* i_mdi_phci_offline() to find the new
* client state after DR of a pHCI.
*/
static int
{
int state;
int online_count = 0;
int standby_count = 0;
continue;
}
online_count++;
}
if (online_count == 0) {
if (standby_count == 0) {
" ct = %p\n", ct));
} else if (standby_count == 1) {
} else {
}
} else if (online_count == 1) {
if (standby_count == 0) {
} else {
}
} else {
}
return (state);
}
/*
* i_mdi_client2devinfo():
* Utility function
*/
{
}
/*
* mdi_client_path2_devinfo():
* Given the parent devinfo and child devfs pathname, search for
* a valid devfs node handle.
*/
{
char *temp_pathname;
int circular;
/*
* Allocate temp buffer
*/
/*
* Lock parent against changes
*/
*temp_pathname = '\0';
break;
}
}
/*
* Release devinfo lock
*/
/*
* Free the temp buffer
*/
return (cdip);
}
/*
* mdi_client_get_path_count():
* Utility function to get number of path information nodes
* associated with a given client device.
*/
int
{
int count = 0;
}
return (count);
}
/*
* i_mdi_get_hash_key():
* Create a hash using strings as keys
*
*/
static int
i_mdi_get_hash_key(char *str)
{
char *p;
for (p = str; *p != '\0'; p++) {
g = *p;
hash += g;
}
}
/*
* mdi_get_lb_policy():
* Get current load balancing policy for a given client device
*/
{
}
return (lb);
}
/*
* mdi_set_lb_region_size():
* Set current region size for the load-balance
*/
int
{
int rv = MDI_FAILURE;
rv = MDI_SUCCESS;
}
return (rv);
}
/*
* mdi_Set_lb_policy():
* Set current load balancing policy for a given client device
*/
int
{
int rv = MDI_FAILURE;
rv = MDI_SUCCESS;
}
return (rv);
}
/*
* mdi_failover():
* failover function called by the vHCI drivers to initiate
* a failover operation. This is typically due to non-availability
* of online paths to route I/O requests. Failover can be
* triggered through user application also.
*
* The vHCI driver calls mdi_failover() to initiate a failover
* operation. mdi_failover() calls back into the vHCI driver's
* vo_failover() entry point to perform the actual failover
* operation. The reason for requiring the vHCI driver to
* initiate failover by calling mdi_failover(), instead of directly
* executing vo_failover() itself, is to ensure that the mdi
* framework can keep track of the client state properly.
* Additionally, mdi_failover() provides as a convenience the
* option of performing the failover operation synchronously or
* asynchronously
*
* Upon successful completion of the failover operation, the
* paths that were previously ONLINE will be in the STANDBY state,
* and the newly activated paths will be in the ONLINE state.
*
* The flags modifier determines whether the activation is done
* synchronously: MDI_FAILOVER_SYNC
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
* MDI_BUSY
*/
/*ARGSUSED*/
int
{
int rv;
/* cdip is not a valid client device. Nothing more to do. */
return (MDI_FAILURE);
}
/* A path to the client is being freed */
return (MDI_BUSY);
}
if (MDI_CLIENT_IS_FAILED(ct)) {
/*
* Client is in failed state. Nothing more to do.
*/
return (MDI_FAILURE);
}
if (MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) {
/*
* Failover is already in progress; return BUSY
*/
return (MDI_BUSY);
}
/*
* Make sure that mdi_pathinfo node state changes are processed.
* We do not allow failovers to progress while client path state
* changes are in progress
*/
if (ct->ct_unstable) {
if (flags == MDI_FAILOVER_ASYNC) {
return (MDI_BUSY);
} else {
while (ct->ct_unstable)
}
}
/*
* Client device is in stable state. Before proceeding, perform sanity
* checks again.
*/
/*
* Client is in failed state. Nothing more to do.
*/
return (MDI_FAILURE);
}
/*
* Set the client state as failover in progress.
*/
if (flags == MDI_FAILOVER_ASYNC) {
/*
* Submit the initiate failover request via CPR safe
* taskq threads.
*/
return (MDI_ACCEPT);
} else {
/*
* Synchronous failover mode. Typically invoked from the user
* land.
*/
}
return (rv);
}
/*
* i_mdi_failover():
* internal failover function. Invokes vHCI drivers failover
* callback function and process the failover status
* Return Values:
* None
*
* Note: A client device in failover state can not be detached or freed.
*/
static int
i_mdi_failover(void *arg)
{
int rv = MDI_SUCCESS;
/*
* Call vHCI drivers callback routine
*/
}
/*
* Save the failover return status
*/
/*
* As a result of failover, client status would have been changed.
* Update the client state and wake up anyone waiting on this client
* device.
*/
return (rv);
}
/*
* Load balancing is logical block.
* IOs within the range described by region_size
* would go on the same path. This would improve the
* performance by cache-hit on some of the RAID devices.
* Search only for online paths(At some point we
* may want to balance across target ports).
* If no paths are found then default to round-robin.
*/
static int
{
int path_index = -1;
int online_path_count = 0;
int online_nonpref_path_count = 0;
while (pip) {
}
next = (mdi_pathinfo_t *)
}
if (online_path_count > 0) {
preferred = 1;
} else if (online_nonpref_path_count > 0) {
preferred = 0;
} else {
path_cnt = 0;
}
if (path_cnt) {
if (path_index == 0 &&
return (MDI_SUCCESS);
}
path_index --;
next = (mdi_pathinfo_t *)
}
"!lba %p, no pip !!\n",
} else {
"!lba %p, no pip for path_index, "
"pip %p\n", pip));
}
}
return (MDI_FAILURE);
}
/*
* mdi_select_path():
* select a path to access a client device.
*
* mdi_select_path() function is called by the vHCI drivers to
* select a path to route the I/O request to. The caller passes
* the block I/O data transfer structure ("buf") as one of the
* parameters. The mpxio framework uses the buf structure
* contents to maintain per path statistics (total I/O size /
* count pending). If more than one online paths are available to
* select, the framework automatically selects a suitable path
* for routing I/O request. If a failover operation is active for
* this client device the call shall be failed with MDI_BUSY error
* code.
*
* By default this function returns a suitable path in online
* state based on the current load balancing policy. Currently
* we support LOAD_BALANCE_NONE (Previously selected online path
* will continue to be used till the path is usable) and
* LOAD_BALANCE_RR (Online paths will be selected in a round
* robin fashion), LOAD_BALANCE_LB(Online paths will be selected
* based on the logical block). The load balancing
* through vHCI drivers configuration file (driver.conf).
*
* vHCI drivers may override this default behavior by specifying
* appropriate flags. If start_pip is specified (non NULL) is
* used as start point to walk and find the next appropriate path.
* The following values are currently defined:
* MDI_SELECT_STANDBY_PATH (to select an STANDBY path).
*
* The non-standard behavior is used by the scsi_vhci driver,
* attach of client devices (to avoid an unnecessary failover
* when the STANDBY path comes up first), during failover
* (to activate a STANDBY path as ONLINE).
*
* The selected path in returned in a held state (ref_cnt).
* Caller should release the hold by calling mdi_rele_path().
*
* Return Values:
* MDI_SUCCESS - Completed successfully
* MDI_BUSY - Client device is busy failing over
* MDI_NOPATH - Client device is online, but no valid path are
* available to access this client device
* MDI_FAILURE - Invalid client device or state
* MDI_DEVI_ONLINING
* - Client device (struct dev_info state) is in
* onlining state.
*/
/*ARGSUSED*/
int
{
int retry = 0;
if (flags != 0) {
/*
* disable default behavior
*/
sb = 0;
}
/* mdi extensions are NULL, Nothing more to do */
return (MDI_FAILURE);
}
if (sb) {
if (MDI_CLIENT_IS_FAILED(ct)) {
/*
* Client is not ready to accept any I/O requests.
* Fail this request.
*/
"client state offline ct = %p\n", ct));
return (MDI_FAILURE);
}
if (MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) {
/*
* Check for Failover is in progress. If so tell the
* caller that this device is busy.
*/
"client failover in progress ct = %p\n", ct));
return (MDI_BUSY);
}
/*
* Check to see whether the client device is attached.
* If not so, let the vHCI driver manually select a path
*/
if ((MDI_CLIENT_IS_DETACHED(ct)) ||
return (MDI_DEVI_ONLINING);
}
}
/*
* Cache in the client list head. If head of the list is NULL
* return MDI_NOPATH
*/
return (MDI_NOPATH);
}
/*
* for non default behavior, bypass current
* load balancing policy and always use LOAD_BALANCE_RR
* except that the start point will be adjusted based
* on the provided start_pip
*/
switch (lbp) {
case LOAD_BALANCE_NONE:
/*
* Load balancing is None or Alternate path mode
* Start looking for a online mdi_pathinfo node starting from
* last known selected path
*/
preferred = 1;
}
do {
/*
* No need to explicitly check if the path is disabled.
* Since we are checking for state == ONLINE and the
*/
/*
* Return the path in hold state. Caller should
* release the lock by calling mdi_rele_path()
*/
return (MDI_SUCCESS);
}
/*
* Path is busy.
*/
if (MDI_PI_IS_DRV_DISABLE_TRANSIENT(pip) ||
retry = 1;
/*
* Keep looking for a next available online path
*/
}
preferred = 0;
cont = 0;
}
} while (cont);
break;
case LOAD_BALANCE_LBA:
/*
* Make sure we are looking
* for an online path. Otherwise, if it is for a STANDBY
* path request, it will go through and fetch an ONLINE
* path which is not desirable.
*/
== MDI_SUCCESS) {
return (MDI_SUCCESS);
}
}
/* FALLTHROUGH */
case LOAD_BALANCE_RR:
/*
* Load balancing is Round Robin. Start looking for a online
* mdi_pathinfo node starting from last known selected path
* as the start point. If override flags are specified,
* process accordingly.
* If the search is already in effect(start_pip not null),
* then lets just use the same path preference to continue the
* traversal.
*/
} else {
preferred = 1;
}
} else {
if (!sb) {
if (preferred == 0) {
/*
* Looks like we have completed
* the traversal as preferred
* value is 0. Time to bail out.
*/
return (MDI_NOPATH);
} else {
/*
* Looks like we reached the
* end of the list. Lets enable
* traversal of non preferred
* paths.
*/
preferred = 0;
}
}
}
}
do {
if (sb) {
preferred) ? 1 : 0);
} else {
if (flags == MDI_SELECT_ONLINE_PATH) {
preferred) ? 1 : 0);
} else if (flags == MDI_SELECT_STANDBY_PATH) {
preferred) ? 1 : 0);
} else if (flags == (MDI_SELECT_ONLINE_PATH |
preferred) ? 1 : 0);
} else {
cond = 0;
}
}
/*
* No need to explicitly check if the path is disabled.
* Since we are checking for state == ONLINE and the
*/
if (cond) {
/*
* Return the path in hold state. Caller should
* release the lock by calling mdi_rele_path()
*/
if (sb)
return (MDI_SUCCESS);
}
/*
* Path is busy.
*/
if (MDI_PI_IS_DRV_DISABLE_TRANSIENT(pip) ||
retry = 1;
/*
* Keep looking for a next available online path
*/
if (!sb) {
if (preferred == 1) {
/*
* Looks like we reached the
* end of the list. Lets enable
* traversal of non preferred
* paths.
*/
preferred = 0;
} else {
/*
* We have done both the passes
* Preferred as well as for
* Non-preferred. Bail out now.
*/
cont = 0;
}
} else {
/*
* Standard behavior case.
*/
}
}
if (cont == 0) {
break;
}
if (!sb) {
/*
* We need to handle the selection of
* non-preferred path in the following
* case:
*
* +------+ +------+ +------+ +-----+
* | A : 1| - | B : 1| - | C : 0| - |NULL |
* +------+ +------+ +------+ +-----+
*
* If we start the search with B, we need to
* skip beyond B to pick C which is non -
* preferred in the second pass. The following
* test, if true, will allow us to skip over
* the 'start'(B in the example) to select
* other non preferred elements.
*/
!= preferred)) {
/*
* try again after going past the start
* pip
*/
goto do_again;
}
} else {
/*
* Standard behavior case
*/
/* look for nonpreferred paths */
preferred = 0;
/*
* Exit condition
*/
cont = 0;
}
}
} while (cont);
break;
}
if (retry == 1) {
return (MDI_BUSY);
} else {
return (MDI_NOPATH);
}
}
/*
* For a client, return the next available path to any phci
*
* Note:
* Caller should hold the branch's devinfo node to get a consistent
* snap shot of the mdi_pathinfo nodes.
*
* Please note that even the list is stable the mdi_pathinfo
* node state and properties are volatile. The caller should lock
* and unlock the nodes by calling mdi_pi_lock() and
* mdi_pi_unlock() functions to get a stable properties.
*
* If there is a need to use the nodes beyond the hold of the
* devinfo node period (For ex. I/O), then mdi_pathinfo node
* need to be held against unexpected removal by calling
* mdi_hold_path() and should be released by calling
* mdi_rele_path() on completion.
*/
{
if (!MDI_CLIENT(ct_dip))
return (NULL);
/*
* Walk through client link
*/
}
/*
* For a phci, return the next available path to any client
* Note: ditto mdi_get_next_phci_path()
*/
{
mdi_phci_t *ph;
return (NULL);
/*
* Walk through pHCI link
*/
}
/*
* mdi_get_nextpath():
* mdi_pathinfo node walker function. Get the next node from the
* client or pHCI device list.
*
* XXX This is wrapper function for compatibility purposes only.
*
* It doesn't work under Multi-level MPxIO, where a dip
* is both client and phci (which link should next_path follow?).
* Once Leadville is modified to call mdi_get_next_phci/client_path,
* this interface should be removed.
*/
void
{
if (MDI_CLIENT(dip)) {
} else {
}
}
/*
* mdi_hold_path():
* Hold the mdi_pathinfo node against unwanted unexpected free.
* Return Values:
* None
*/
void
{
if (pip) {
}
}
/*
* mdi_rele_path():
* Release the mdi_pathinfo node which was selected
* through mdi_select_path() mechanism or manually held by
* calling mdi_hold_path().
* Return Values:
* None
*/
void
{
if (pip) {
}
}
}
/*
* mdi_pi_lock():
* Lock the mdi_pathinfo node.
* Note:
* The caller should release the lock by calling mdi_pi_unlock()
*/
void
{
if (pip) {
}
}
/*
* mdi_pi_unlock():
* Unlock the mdi_pathinfo node.
* Note:
* The mdi_pathinfo node should have been locked with mdi_pi_lock()
*/
void
{
if (pip) {
}
}
/*
* mdi_pi_find():
* Search the list of mdi_pathinfo nodes attached to the
* Returns a pointer to the mdi_pathinfo node if a matching node is
* found.
* Return Values:
* mdi_pathinfo node handle
* NULL
* Notes:
* Caller need not hold any locks to call this function.
*/
{
mdi_phci_t *ph;
mdi_vhci_t *vh;
return (NULL);
}
/*
* Invalid pHCI device, Nothing more to do.
*/
"!mdi_pi_find: invalid phci"));
return (NULL);
}
/*
* Invalid vHCI device, Nothing more to do.
*/
"!mdi_pi_find: invalid phci"));
return (NULL);
}
/*
* Look for client device identified by caddr (guid)
*/
/*
* Find a mdi_pathinfo node under pHCI list for a matching
* unit address.
*/
break;
}
}
return (pip);
}
/*
* Find the client device corresponding to 'caddr'
*/
/*
* Client not found, Obviously mdi_pathinfo node has not been
* created yet.
*/
return (pip);
}
/*
* Hold the client lock and look for a mdi_pathinfo node with matching
* pHCI and paddr
*/
/*
* Release the global mutex as it is no more needed. Note: We always
* respect the locking order while acquiring.
*/
/*
* Compare the unit address
*/
break;
}
}
return (pip);
}
/*
* mdi_pi_alloc():
* Allocate and initialize a new instance of a mdi_pathinfo node.
* The mdi_pathinfo node returned by this function identifies a
* unique device path is capable of having properties attached
* and passed to mdi_pi_online() to fully attach and online the
* path and client device node.
* The mdi_pathinfo node returned by this function must be
* destroyed using mdi_pi_free() if the path is no longer
* operational or if the caller fails to attach a client device
* node when calling mdi_pi_online(). The framework will not free
* the resources allocated.
* This function can be called from both interrupt and kernel
* contexts. DDI_NOSLEEP flag should be used while calling
* from interrupt contexts.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
* MDI_NOMEM
*/
/*ARGSUSED*/
int
{
mdi_vhci_t *vh;
mdi_phci_t *ph;
/* Nothing more to do */
return (MDI_FAILURE);
}
/* Invalid pHCI device, return failure */
"!mdi_pi_alloc: invalid pHCI=%p", pdip));
return (MDI_FAILURE);
}
/* Invalid vHCI device, return failure */
"!mdi_pi_alloc: invalid pHCI=%p", pdip));
return (MDI_FAILURE);
}
if (MDI_PHCI_IS_READY(ph) == 0) {
/*
* Do not allow new node creation when pHCI is in
*/
"mdi_pi_alloc: pHCI=%p is not ready", ph));
return (MDI_BUSY);
}
/*
* Look for a client device with matching guid identified by caddr,
* If not found create one
*/
goto fail;
}
/*
* Allocate a devinfo node
*/
goto fail;
}
}
/*
* Compare the unit address
*/
break;
}
}
/*
* This is a new path for this client device. Allocate and
* initialize a new pathinfo node
*/
goto fail;
}
}
rv = MDI_SUCCESS;
fail:
/*
* Release the global mutex.
*/
/*
* Mark the pHCI as stable
*/
return (rv);
}
/*ARGSUSED*/
int
{
}
/*
* i_mdi_pi_alloc():
* Allocate a mdi_pathinfo node and add to the pHCI path list
* Return Values:
* mdi_pathinfo
*/
/*ARGSUSED*/
static mdi_pathinfo_t *
{
int ct_circular;
int ph_circular;
goto fail;
if (MDI_PHCI_IS_USER_DISABLED(ph))
if (MDI_PHCI_IS_DRV_DISABLED(ph))
pi_addr =
goto fail;
goto fail;
/*
* Lock both dev_info nodes against changes in parallel.
*/
return (pip);
fail:
if (pi_prop)
(void) nvlist_free(pi_prop);
if (pi_addr)
return (NULL);
}
/*
* i_mdi_phci_add_path():
* Add a mdi_pathinfo node to pHCI list.
* Notes:
* Caller should per-pHCI mutex
*/
static void
{
} else {
}
ph->ph_path_count++;
}
/*
* i_mdi_client_add_path():
* Add mdi_pathinfo node to client list
*/
static void
{
} else {
}
ct->ct_path_count++;
}
/*
* mdi_pi_free():
* Free the mdi_pathinfo node and also client device node if this
* is the last path to the device
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
* MDI_BUSY
*/
/*ARGSUSED*/
int
{
int rv = MDI_SUCCESS;
mdi_vhci_t *vh;
mdi_phci_t *ph;
int (*f)();
int client_held = 0;
/*
* Invalid pHCI device, return failure
*/
"!mdi_pi_free: invalid pHCI"));
return (MDI_FAILURE);
}
/* Invalid pHCI device, return failure */
"!mdi_pi_free: invalid vHCI"));
return (MDI_FAILURE);
}
/*
* Invalid Client device, return failure
*/
"!mdi_pi_free: invalid client"));
return (MDI_FAILURE);
}
/*
* Check to see for busy condition. A mdi_pathinfo can only be freed
* if the node state is either offline or init and the reference count
* is zero.
*/
MDI_PI_IS_INITING(pip))) {
/*
* Node is busy
*/
"!mdi_pi_free: pathinfo node is busy pip=%p", pip));
return (MDI_BUSY);
}
/*
*/
"%d cmds still pending on path: %p\n",
/*
* The timeout time reached without ref_cnt being zero
* being signaled.
*/
"!i_mdi_pi_free: "
"Timeout reached on path %p without the cond\n",
pip));
"!i_mdi_pi_free: "
"%d cmds still pending on path: %p\n",
return (MDI_BUSY);
}
}
client_held = 1;
}
/* Prevent further failovers till mdi_mutex is held */
/*
* Wait till failover is complete before removing this node.
*/
while (MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct))
if (!MDI_PI_IS_INITING(pip)) {
if (f != NULL) {
}
}
/*
* If vo_pi_uninit() completed successfully.
*/
if (rv == MDI_SUCCESS) {
if (client_held) {
"i_mdi_pm_rele_client\n"));
}
if (ct->ct_path_count == 0) {
/*
* Client lost its last path.
* Clean up the client device
*/
return (rv);
}
}
return (rv);
}
/*
* i_mdi_pi_free():
* Free the mdi_pathinfo node
*/
static void
{
int ct_circular;
int ph_circular;
/*
* remove any per-path kstats
*/
}
}
}
/*
* i_mdi_phci_remove_path():
* Remove a mdi_pathinfo node from pHCI list.
* Notes:
* Caller should hold per-pHCI mutex
*/
static void
{
break;
}
}
if (path) {
ph->ph_path_count--;
if (prev) {
} else {
ph->ph_path_head =
}
}
}
/*
* Clear the pHCI link
*/
}
/*
* i_mdi_client_remove_path():
* Remove a mdi_pathinfo node from client path list.
*/
static void
{
break;
}
}
if (path) {
ct->ct_path_count--;
if (prev) {
} else {
ct->ct_path_head =
}
}
}
}
}
/*
* i_mdi_pi_state_change():
* online a mdi_pathinfo node
*
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
/*ARGSUSED*/
static int
{
int rv = MDI_SUCCESS;
mdi_vhci_t *vh;
mdi_phci_t *ph;
int (*f)();
/*
* Invalid pHCI device, fail the request
*/
"!mdi_pi_state_change: invalid phci"));
return (MDI_FAILURE);
}
/*
* Invalid vHCI device, fail the request
*/
"!mdi_pi_state_change: invalid vhci"));
return (MDI_FAILURE);
}
/*
* Invalid client device, fail the request
*/
"!mdi_pi_state_change: invalid client"));
return (MDI_FAILURE);
}
/*
* If this path has not been initialized yet, Callback vHCI driver's
* pathinfo node initialize entry point
*/
if (MDI_PI_IS_INITING(pip)) {
if (f != NULL) {
if (rv != MDI_SUCCESS) {
"!vo_pi_init: failed vHCI=0x%p, pip=0x%p",
return (MDI_FAILURE);
}
}
}
/*
* states
*/
if (MDI_PHCI_IS_READY(ph) == 0) {
"!mdi_pi_state_change: pHCI not ready, pHCI=%p", ph));
return (MDI_BUSY);
}
/*
* Check if mdi_pathinfo state is in transient state.
* If yes, offlining is in progress and wait till transient state is
* cleared.
*/
if (MDI_PI_IS_TRANSIENT(pip)) {
while (MDI_PI_IS_TRANSIENT(pip)) {
}
}
/*
* Grab the client lock in reverse order sequence and release the
* mdi_pathinfo mutex.
*/
/*
* Wait till failover state is cleared
*/
while (MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct))
/*
* Mark the mdi_pathinfo node state as transient
*/
switch (state) {
break;
break;
case MDI_PATHINFO_STATE_FAULT:
/*
* Mark the pathinfo state as FAULTED
*/
break;
/*
* ndi_devi_offline() cannot hold pip or ct locks.
*/
/*
* Do not offline if path will become last path and path
* is busy for user initiated events.
*/
if ((flag & NDI_DEVI_REMOVE) &&
if (rv != NDI_SUCCESS) {
/*
* Convert to MDI error code
*/
switch (rv) {
case NDI_BUSY:
break;
default:
rv = MDI_FAILURE;
break;
}
goto state_change_exit;
} else {
}
}
/*
* Mark the mdi_pathinfo node state as transient
*/
break;
}
if (f != NULL) {
if (rv == MDI_NOT_SUPPORTED) {
}
if (rv != MDI_SUCCESS) {
"!vo_pi_state_change: failed rv = %x", rv));
}
}
if (MDI_PI_IS_TRANSIENT(pip)) {
if (rv == MDI_SUCCESS) {
} else {
}
}
/*
* Wake anyone waiting for this mdi_pathinfo node
*/
/*
* Mark the client device as stable
*/
if (rv == MDI_SUCCESS) {
if (ct->ct_unstable == 0) {
/*
* Onlining the mdi_pathinfo node will impact the
* client state Update the client and dev_info node
* state accordingly
*/
rv = NDI_SUCCESS;
switch (MDI_CLIENT_STATE(ct)) {
case MDI_CLIENT_STATE_OPTIMAL:
if (cdip &&
((state == MDI_PATHINFO_STATE_ONLINE) ||
(state == MDI_PATHINFO_STATE_STANDBY))) {
/*
* Must do ndi_devi_online() through
* hotplug thread for deferred
* attach mechanism to work
*/
if ((rv != NDI_SUCCESS) &&
(MDI_CLIENT_STATE(ct) ==
/*
* ndi_devi_online failed.
* Reset client flags to
* offline.
*/
"!ndi_devi_online: failed "
" Error: %x", rv));
}
if (rv != NDI_SUCCESS) {
/* Reset the path state */
}
}
break;
case MDI_CLIENT_STATE_FAILED:
/*
* This is the last path case for
* non-user initiated events.
*/
if (((flag & NDI_DEVI_REMOVE) == 0) &&
DS_INITIALIZED)) {
if (rv != NDI_SUCCESS) {
/*
* ndi_devi_offline failed.
* Reset client flags to
* online as the path could not
* be offlined.
*/
"!ndi_devi_offline: failed "
" Error: %x", rv));
}
}
break;
}
/*
* Convert to MDI error code
*/
switch (rv) {
case NDI_SUCCESS:
rv = MDI_SUCCESS;
break;
case NDI_BUSY:
break;
default:
rv = MDI_FAILURE;
break;
}
}
}
/*
* Mark the pHCI as stable again.
*/
return (rv);
}
/*
* mdi_pi_online():
* Place the path_info node in the online state. The path is
* now available to be selected by mdi_select_path() for
* transporting I/O requests to client devices.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
int
{
int client_held = 0;
int rv;
if (rv != MDI_SUCCESS)
return (rv);
"i_mdi_pm_hold_pip\n"));
client_held = 1;
}
if (client_held) {
if (ct->ct_power_cnt == 0) {
}
"i_mdi_pm_hold_client\n"));
}
/*
* Create the per-path (pathinfo) IO and error kstats which
* are reported via iostat(1m).
*
* Defer creating the per-path kstats if device is not yet
* attached; the names of the kstats are constructed in part
* using the devices instance number which is assigned during
* process of attaching the client device.
*
* The framework post_attach handler, mdi_post_attach(), is
* is responsible for initializing the client's pathinfo list
* once successfully attached.
*/
return (rv);
return (rv);
}
/*
* mdi_pi_standby():
* Place the mdi_pathinfo node in standby state
*
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
int
{
}
/*
* mdi_pi_fault():
* Place the mdi_pathinfo node in fault'ed state
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
int
{
}
/*
* mdi_pi_offline():
* Offline a mdi_pathinfo node.
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
int
{
int ret, client_held = 0;
if (ret == MDI_SUCCESS) {
client_held = 1;
}
if (client_held) {
"mdi_pi_offline i_mdi_pm_rele_client\n"));
}
}
return (ret);
}
/*
* i_mdi_pi_offline():
* Offline a mdi_pathinfo node and call the vHCI driver's callback
*/
static int
{
int (*f)();
int rv;
/*
*/
"%d cmds still pending on path: %p\n",
/*
* The timeout time reached without ref_cnt being zero
* being signaled.
*/
"Timeout reached on path %p without the cond\n",
pip));
"%d cmds still pending on path: %p\n",
}
}
/*
* Notify vHCI that has registered this event
*/
if (f != NULL) {
flags)) != MDI_SUCCESS) {
}
}
/*
* Set the mdi_pathinfo node state and clear the transient condition
*/
if (rv == MDI_SUCCESS) {
if (ct->ct_unstable == 0) {
/*
* Onlining the mdi_pathinfo node will impact the
* client state Update the client and dev_info node
* state accordingly
*/
rv = NDI_SUCCESS;
if (cdip &&
(i_ddi_node_state(cdip) >=
DS_INITIALIZED)) {
if (rv != NDI_SUCCESS) {
/*
* ndi_devi_offline failed.
* Reset client flags to
* online.
*/
"!ndi_devi_offline: failed "
" Error: %x", rv));
}
}
}
/*
* Convert to MDI error code
*/
switch (rv) {
case NDI_SUCCESS:
rv = MDI_SUCCESS;
break;
case NDI_BUSY:
break;
default:
rv = MDI_FAILURE;
break;
}
}
}
/*
* Change in the mdi_pathinfo node state will impact the client state
*/
return (rv);
}
/*
* mdi_pi_get_addr():
* Get the unit address associated with a mdi_pathinfo node
*
* Return Values:
* char *
*/
char *
{
return (NULL);
}
/*
* mdi_pi_get_client():
* Get the client devinfo associated with a mdi_pathinfo node
*
* Return Values:
* Handle to client device dev_info node
*/
{
if (pip) {
}
return (dip);
}
/*
* mdi_pi_get_phci():
* Get the pHCI devinfo associated with the mdi_pathinfo node
* Return Values:
* Handle to dev_info node
*/
{
if (pip) {
}
return (dip);
}
/*
* mdi_pi_get_client_private():
* Get the client private information associated with the
* mdi_pathinfo node
*/
void *
{
if (pip) {
}
return (cprivate);
}
/*
* mdi_pi_set_client_private():
* Set the client private information in the mdi_pathinfo node
*/
void
{
if (pip) {
}
}
/*
* mdi_pi_get_phci_private():
* Get the pHCI private information associated with the
* mdi_pathinfo node
*/
{
if (pip) {
}
return (pprivate);
}
/*
* mdi_pi_set_phci_private():
* Set the pHCI private information in the mdi_pathinfo node
*/
void
{
if (pip) {
}
}
/*
* mdi_pi_get_state():
* Get the mdi_pathinfo node state. Transient states are internal
* and not provided to the users
*/
{
if (pip) {
if (MDI_PI_IS_TRANSIENT(pip)) {
/*
* mdi_pathinfo is in state transition. Return the
* last good state.
*/
} else {
}
}
return (state);
}
/*
* Note that the following function needs to be the new interface for
* mdi_pi_get_state when mpxio gets integrated to ON.
*/
int
{
if (pip) {
if (MDI_PI_IS_TRANSIENT(pip)) {
/*
* mdi_pathinfo is in state transition. Return the
* last good state.
*/
} else {
}
}
return (MDI_SUCCESS);
}
/*
* mdi_pi_get_preferred:
* Get the preferred path flag
*/
int
{
if (pip) {
}
return (0);
}
/*
* mdi_pi_set_preferred:
* Set the preferred path flag
*/
void
{
if (pip) {
}
}
/*
* mdi_pi_set_state():
* Set the mdi_pathinfo node state
*/
void
{
if (pip) {
}
}
/*
* Property functions:
*/
int
{
int rv;
switch (val) {
case 0:
break;
case EINVAL:
case ENOTSUP:
break;
case ENOMEM:
break;
default:
break;
}
return (rv);
}
/*
* mdi_pi_get_next_prop():
* Property walk function. The caller should hold mdi_pi_lock()
* and release by calling mdi_pi_unlock() at the end of walk to
* get a consistent value.
*/
nvpair_t *
{
return (NULL);
}
}
/*
* mdi_prop_remove():
* Remove the named property from the named list.
*/
int
{
return (DDI_PROP_NOT_FOUND);
}
return (DDI_PROP_NOT_FOUND);
}
if (name) {
} else {
char nvp_name[MAXNAMELEN];
while (nvp) {
nvpair_name(nvp));
nvp_name);
}
}
return (DDI_PROP_SUCCESS);
}
/*
* mdi_prop_size():
* Get buffer size needed to pack the property data.
* Caller should hold the mdi_pathinfo_t lock to get a consistent
* buffer size.
*/
int
{
int rv;
*buflenp = 0;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_pack():
* pack the property list. The caller should hold the
* mdi_pathinfo_t node to get a consistent data
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_byte():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_byte_array():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_int():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_int64():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_int_array():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_string():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_update_string_array():
*/
int
{
int rv;
return (DDI_PROP_INVAL_ARG);
}
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_byte():
* Look for byte property identified by name. The data returned
* is the actual property and valid as long as mdi_pathinfo_t node
* is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_byte_array():
* Look for byte array property identified by name. The data
* returned is the actual property and valid as long as
* mdi_pathinfo_t node is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_int():
* Look for int property identified by name. The data returned
* is the actual property and valid as long as mdi_pathinfo_t
* node is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_int64():
* Look for int64 property identified by name. The data returned
* is the actual property and valid as long as mdi_pathinfo_t node
* is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_int_array():
* Look for int array property identified by name. The data
* returned is the actual property and valid as long as
* mdi_pathinfo_t node is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_string():
* Look for string property identified by name. The data
* returned is the actual property and valid as long as
* mdi_pathinfo_t node is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_lookup_string_array():
* Look for string array property identified by name. The data
* returned is the actual property and valid as long as
* mdi_pathinfo_t node is alive.
*/
int
{
int rv;
return (DDI_PROP_NOT_FOUND);
}
return (i_map_nvlist_error_to_mdi(rv));
}
/*
* mdi_prop_free():
* Symmetrical function to ddi_prop_free(). nvlist_lookup_xx()
* functions return the pointer to actual property data and not a
* copy of it. So the data returned is valid as long as
* mdi_pathinfo_t node is valid.
*/
/*ARGSUSED*/
int
mdi_prop_free(void *data)
{
return (DDI_PROP_SUCCESS);
}
/*ARGSUSED*/
static void
{
char *ct_status;
char *status;
char lb_buf[64];
(MDI_CLIENT_IS_REPORT_DEV_NEEDED(ct) == 0)) {
return;
}
ct_status = "optimal";
ct_status = "degraded";
ct_status = "failed";
} else {
ct_status = "unknown";
}
if (MDI_PI_IS_OFFLINE(pip)) {
status = "offline";
} else if (MDI_PI_IS_ONLINE(pip)) {
status = "online";
} else if (MDI_PI_IS_STANDBY(pip)) {
status = "standby";
} else if (MDI_PI_IS_FAULT(pip)) {
status = "faulted";
} else {
status = "unknown";
}
"%s, region-size: %d", mdi_load_balance_lba,
"%s", mdi_load_balance_none);
} else {
}
if (dip) {
"path %s (%s%d) to target address: %s is %s"
" Load balancing: %s\n",
}
}
#ifdef DEBUG
/*
* i_mdi_log():
* Utility function for error message management
*
*/
/*VARARGS3*/
static void
{
char buf[MAXNAMELEN];
char name[MAXNAMELEN];
int log_only = 0;
int boot_only = 0;
int console_only = 0;
if (dip) {
} else {
}
} else {
name[0] = '\0';
}
switch (buf[0]) {
case '!':
log_only = 1;
break;
case '?':
boot_only = 1;
break;
case '^':
console_only = 1;
break;
}
switch (level) {
case CE_NOTE:
/* FALLTHROUGH */
case CE_CONT:
case CE_WARN:
case CE_PANIC:
if (boot_only) {
} else if (console_only) {
} else if (log_only) {
} else {
}
break;
default:
break;
}
}
#endif /* DEBUG */
void
{
/*
* Client online notification. Mark client state as online
* restore our binding with dev_info node
*/
/* catch for any memory leaks */
if (ct->ct_power_cnt == 0)
(void) i_mdi_power_all_phci(ct);
"i_mdi_pm_hold_client\n"));
}
void
{
mdi_phci_t *ph;
/* pHCI online notification. Mark state accordingly */
}
/*
* mdi_devi_online():
* device online.
* Return Values:
* NDI_SUCCESS
* MDI_FAILURE
*/
/*ARGSUSED*/
int
{
}
if (MDI_CLIENT(dip)) {
}
return (NDI_SUCCESS);
}
/*
* mdi_devi_offline():
* offline.
*
* Return Values:
* NDI_SUCCESS
* NDI_FAILURE
*/
/*ARGSUSED*/
int
{
int rv = NDI_SUCCESS;
if (MDI_CLIENT(dip)) {
if (rv != NDI_SUCCESS)
return (rv);
}
/* set client back online */
}
}
return (rv);
}
/*ARGSUSED*/
static int
{
int rv = NDI_SUCCESS;
mdi_phci_t *ph;
/*
* pHCI component offline notification
* Make sure that this pHCI instance is free to be offlined.
* If it is OK to proceed, Offline and remove all the child
* mdi_pathinfo nodes. This process automatically offlines
* corresponding client devices, for which this pHCI provides
* critical services.
*/
dip));
return (rv);
}
if (MDI_PHCI_IS_OFFLINE(ph)) {
return (NDI_SUCCESS);
}
/*
* Check to see if the pHCI can be offlined
*/
if (ph->ph_unstable) {
"!One or more target devices are in transient "
"state. This device can not be removed at "
"this moment. Please try again later."));
return (NDI_BUSY);
}
/*
* The mdi_pathinfo state is OK. Check the client state.
* If failover in progress fail the pHCI from offlining
*/
if ((MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) ||
(ct->ct_unstable)) {
/*
* Failover is in progress, Fail the DR
*/
"!pHCI device (%s%d) is Busy. %s",
"This device can not be removed at "
"this moment. Please try again later."));
return (NDI_BUSY);
}
/*
* Check to see of we are removing the last path of this
* client device...
*/
/*
* ndi_devi_offline() failed.
* This pHCI provides the critical path
* to one or more client devices.
* Return busy.
*/
"!pHCI device (%s%d) is Busy. %s",
"This device can not be removed at "
"this moment. Please try again later."));
failed_pip = pip;
break;
} else {
}
} else {
}
}
if (failed_pip) {
while (pip != failed_pip) {
switch (MDI_CLIENT_STATE(ct)) {
case MDI_CLIENT_STATE_OPTIMAL:
if (cdip) {
(void) ndi_devi_online(cdip, 0);
continue;
}
break;
case MDI_CLIENT_STATE_FAILED:
if (cdip) {
(void) ndi_devi_offline(cdip, 0);
continue;
}
break;
}
}
return (NDI_BUSY);
}
/*
* Mark the pHCI as offline
*/
/*
* Mark the child mdi_pathinfo nodes as transient
*/
}
/*
* Give a chance for any pending commands to execute
*/
delay(1);
if (!MDI_PI_IS_OFFLINE(pip)) {
"!pHCI device (%s%d) is Busy. %s",
"This device can not be removed at "
"this moment. Please try again later."));
return (NDI_BUSY);
}
}
return (rv);
}
/*ARGSUSED*/
static int
{
int rv = NDI_SUCCESS;
/*
* Client component to go offline. Make sure that we are
* not in failing over state and update client state
* accordingly
*/
dip));
if (ct->ct_unstable) {
/*
* One or more paths are in transient state,
* Dont allow offline of a client device
*/
"!One or more paths to this device is "
"in transient state. This device can not "
"be removed at this moment. "
"Please try again later."));
return (NDI_BUSY);
}
if (MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) {
/*
* Failover is in progress, Dont allow DR of
* a client device
*/
"!Client device (%s%d) is Busy. %s",
"This device can not be removed at "
"this moment. Please try again later."));
return (NDI_BUSY);
}
/*
* Unbind our relationship with the dev_info node
*/
if (flags & NDI_DEVI_REMOVE) {
}
}
return (rv);
}
/*
* mdi_pre_attach():
* Pre attach() notification handler
*/
/*ARGSUSED*/
int
{
/* don't support old DDI_PM_RESUME */
(cmd == DDI_PM_RESUME))
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* mdi_post_attach():
* Post attach() notification handler
*/
/*ARGSUSED*/
void
{
mdi_phci_t *ph;
switch (cmd) {
case DDI_ATTACH:
"!pHCI post_attach: called %p\n", ph));
if (error == DDI_SUCCESS) {
} else {
"!pHCI post_attach: failed error=%d\n",
error));
}
break;
case DDI_RESUME:
"!pHCI post_resume: called %p\n", ph));
if (error == DDI_SUCCESS) {
} else {
"!pHCI post_resume: failed error=%d\n",
error));
}
break;
}
}
if (MDI_CLIENT(dip)) {
switch (cmd) {
case DDI_ATTACH:
"!Client post_attach: called %p\n", ct));
if (error != DDI_SUCCESS) {
"!Client post_attach: failed error=%d\n",
error));
"mdi_post_attach i_mdi_pm_reset_client\n"));
break;
}
/*
* Client device has successfully attached.
* Create kstats for any pathinfo structures
* initially associated with this client.
*/
pip = (mdi_pathinfo_t *)
(void) i_mdi_pi_kstat_create(pip);
}
break;
case DDI_RESUME:
"!Client post_attach: called %p\n", ct));
if (error == DDI_SUCCESS) {
} else {
"!Client post_resume: failed error=%d\n",
error));
}
break;
}
}
}
/*
* mdi_pre_detach():
* Pre detach notification handler
*/
/*ARGSUSED*/
int
{
int rv = DDI_SUCCESS;
if (MDI_CLIENT(dip)) {
}
}
return (rv);
}
/*ARGSUSED*/
static int
{
int rv = DDI_SUCCESS;
mdi_phci_t *ph;
return (rv);
}
switch (cmd) {
case DDI_DETACH:
"!pHCI pre_detach: called %p\n", ph));
if (!MDI_PHCI_IS_OFFLINE(ph)) {
/*
* mdi_pathinfo nodes are still attached to
* this pHCI. Fail the detach for this pHCI.
*/
"!pHCI pre_detach: "
"mdi_pathinfo nodes are still attached "
"%p\n", ph));
rv = DDI_FAILURE;
break;
}
break;
case DDI_SUSPEND:
/*
* pHCI is getting suspended. Since mpxio client
* devices may not be suspended at this point, to avoid
* a potential stack overflow, it is important to suspend
* client devices before pHCI can be suspended.
*/
"!pHCI pre_suspend: called %p\n", ph));
/*
* Suspend all the client devices accessible through this pHCI
*/
next =
if ((MDI_CLIENT_IS_DETACHED(ct) == 0) &&
MDI_CLIENT_IS_SUSPENDED(ct) == 0) {
DDI_SUCCESS) {
/*
* Suspend of one of the client
* device has failed.
*/
"!Suspend of device (%s%d) failed.",
ddi_get_instance(cdip)));
failed_pip = pip;
break;
}
} else {
}
}
if (rv == DDI_SUCCESS) {
/*
* Suspend of client devices is complete. Proceed
* with pHCI suspend.
*/
} else {
/*
* Revert back all the suspended client device states
* to converse.
*/
while (pip != failed_pip) {
next =
if (MDI_CLIENT_IS_SUSPENDED(ct)) {
} else {
}
}
}
break;
default:
rv = DDI_FAILURE;
break;
}
return (rv);
}
/*ARGSUSED*/
static int
{
int rv = DDI_SUCCESS;
return (rv);
}
switch (cmd) {
case DDI_DETACH:
"!Client pre_detach: called %p\n", ct));
break;
case DDI_SUSPEND:
"!Client pre_suspend: called %p\n", ct));
break;
default:
rv = DDI_FAILURE;
break;
}
return (rv);
}
/*
* mdi_post_detach():
* Post detach notification handler
*/
/*ARGSUSED*/
void
{
/*
* too
*/
if (MDI_CLIENT(dip))
}
/*ARGSUSED*/
static void
{
mdi_phci_t *ph;
/*
* too
*/
return;
}
/*
* Detach of pHCI failed. Restore back converse
* state
*/
switch (cmd) {
case DDI_DETACH:
"!pHCI post_detach: called %p\n", ph));
if (error != DDI_SUCCESS)
break;
case DDI_SUSPEND:
"!pHCI post_suspend: called %p\n", ph));
if (error != DDI_SUCCESS)
break;
}
}
/*ARGSUSED*/
static void
{
return;
}
/*
* Detach of Client failed. Restore back converse
* state
*/
switch (cmd) {
case DDI_DETACH:
"!Client post_detach: called %p\n", ct));
"i_mdi_pm_rele_client\n"));
} else {
"i_mdi_pm_reset_client\n"));
}
if (error != DDI_SUCCESS)
break;
case DDI_SUSPEND:
"!Client post_suspend: called %p\n", ct));
if (error != DDI_SUCCESS)
break;
}
}
/*
* create and install per-path (client - pHCI) statistics
* I/O stats supported: nread, nwritten, reads, and writes
* Error stats - hard errors, soft errors, & transport errors
*/
static int
{
char ksname[KSTAT_STRLEN];
const char *err_postfix = ",err";
struct mdi_pi_kstats *mdi_statp;
return (MDI_SUCCESS);
continue;
/*
* We have found a different path with same parent
* kstats for a given client-pHCI are common
*/
return (MDI_SUCCESS);
}
}
/*
* stats are named as follows: TGTx.HBAy, e.g. "ssd0.fp0"
* clamp length of name against max length of error kstat name
*/
return (MDI_FAILURE);
}
return (MDI_FAILURE);
}
sizeof (struct pi_errs) / sizeof (kstat_named_t), 0);
return (MDI_FAILURE);
}
return (MDI_SUCCESS);
}
/*
* destroy per-path properties
*/
static void
{
struct mdi_pi_kstats *mdi_statp;
return;
/*
* the kstat may be shared between multiple pathinfo nodes
* decrement this pathinfo's usage, removing the kstats
* themselves when the last pathinfo reference is removed.
*/
if (--mdi_statp->pi_kstat_ref != 0)
return;
}
/*
* update I/O paths KSTATS
*/
void
{
/*
* I/O can be driven across a path prior to having path
* statistics available, i.e. probe(9e).
*/
} else {
}
}
}
/*
* disable the path to a particular pHCI (pHCI specified in the phci_path
* argument) for a particular client (specified in the client_path argument).
* Disabling a path means that MPxIO will not select the disabled path for
* routing any new I/O requests.
*/
int
{
}
/*
* Enable the path to a particular pHCI (pHCI specified in the phci_path
* argument) for a particular client (specified in the client_path argument).
* Enabling a path means that MPxIO may select the enabled path for routing
* future I/O requests, subject to other path state constraints.
*/
int
{
}
/*
*/
int
{
mdi_phci_t *ph;
int found_it;
int (*f)() = NULL;
int rv;
int sync_flag = 0;
" failed. ph = NULL operation = %d\n", op));
return (MDI_FAILURE);
}
" Invalid operation = %d\n", op));
return (MDI_FAILURE);
}
/*
*/
"Operation %d for the phci\n", op));
switch (flags) {
case USER_DISABLE:
if (op == MDI_DISABLE_OP)
else
break;
case DRIVER_DISABLE:
if (op == MDI_DISABLE_OP)
else
break;
case DRIVER_DISABLE_TRANSIENT:
if (op == MDI_DISABLE_OP)
else
break;
default:
"!i_mdi_pi_enable_disable:"
" Invalid flag argument= %d\n", flags));
}
/*
* path info's to each client.
*/
/*
* Do a callback into the mdi consumer to let it
*/
if (f != NULL) {
if (rv != MDI_SUCCESS) {
"!vo_pi_state_change: failed rv = %x", rv));
}
}
next =
switch (flags) {
case USER_DISABLE:
if (op == MDI_DISABLE_OP)
else
break;
case DRIVER_DISABLE:
if (op == MDI_DISABLE_OP)
else
break;
case DRIVER_DISABLE_TRANSIENT:
else
break;
}
/*
* Do a callback into the mdi consumer to let it
*/
if (f != NULL) {
if (rv != MDI_SUCCESS) {
"!vo_pi_state_change: failed rv = %x", rv));
}
}
}
} else {
/*
* Disable a specific client.
*/
"!i_mdi_pi_enable_disable:"
" failed. ct = NULL operation = %d\n", op));
return (MDI_FAILURE);
}
found_it = 0;
found_it = 1;
break;
}
}
if (found_it == 0) {
"!i_mdi_pi_enable_disable:"
" failed. Could not find corresponding pip\n"));
return (MDI_FAILURE);
}
/*
* Do a callback into the mdi consumer to let it
*/
if (f != NULL) {
if (rv != MDI_SUCCESS) {
"!vo_pi_state_change: failed rv = %x", rv));
}
}
switch (flags) {
case USER_DISABLE:
if (op == MDI_DISABLE_OP)
else
break;
case DRIVER_DISABLE:
if (op == MDI_DISABLE_OP)
else
break;
case DRIVER_DISABLE_TRANSIENT:
else
break;
}
/*
* Do a callback into the mdi consumer to let it
*/
if (f != NULL) {
if (rv != MDI_SUCCESS) {
"!vo_pi_state_change: failed rv = %x", rv));
}
}
}
return (MDI_SUCCESS);
}
/*ARGSUSED3*/
int
{
char *paddr;
return (MDI_FAILURE);
return (MDI_FAILURE);
paddr++; /* skip '@' */
if (flags & NDI_DEVI_DEBUG) {
}
}
return (MDI_FAILURE);
return (MDI_FAILURE);
/* TODO: holding should happen inside search functions */
return (MDI_SUCCESS);
}
/*
* Ensure phci powered up
*/
static void
{
return;
}
return;
}
}
/*
* Allow phci powered down
*/
static void
{
return;
}
}
static void
{
}
static void
{
}
}
static void
{
}
if (ct->ct_power_cnt == 0) {
return;
}
}
static void
{
ct->ct_power_cnt = 0;
ct->ct_powercnt_held = 0;
}
static void
{
}
}
static int
{
int ret;
/* bring all components of phci to full power */
if (ret == DDI_FAILURE) {
"pm_powerup FAILED for %s%d\n",
return (MDI_FAILURE);
}
return (MDI_SUCCESS);
}
static int
{
int succeeded = 0;
succeeded = 1;
}
}
/*
* mdi_bus_power():
* 1. Place the phci(s) into powered up state so that
* client can do power management
* 2. Ensure phci powered up as client power managing
* Return Values:
* MDI_SUCCESS
* MDI_FAILURE
*/
int
{
int ret = MDI_SUCCESS;
/*
* BUS_POWER_NOINVOL not supported
*/
if (op == BUS_POWER_NOINVOL)
return (MDI_FAILURE);
/*
* ignore other OPs.
* return quickly to save cou cycles on the ct processing
*/
switch (op) {
break;
case BUS_POWER_HAS_CHANGED:
break;
default:
}
return (MDI_FAILURE);
/*
* wait till the mdi_pathinfo node state change are processed
*/
switch (op) {
"BUS_POWER_PRE_NOTIFICATION:"
"%s@%s, olevel=%d, nlevel=%d, comp=%d\n",
/* serialize power level change per client */
while (MDI_CLIENT_IS_POWER_TRANSITION(ct))
if (ct->ct_power_cnt == 0) {
}
/*
* if new_level > 0:
* - hold phci(s)
* - power up phci(s) if not already
* ignore power down
*/
if (bpc->bpc_nlevel > 0) {
"mdi_bus_power i_mdi_pm_hold_client\n"));
}
}
break;
"BUS_POWER_POST_NOTIFICATION:"
"%s@%s, olevel=%d, nlevel=%d, comp=%d result=%d\n",
*(int *)result));
if (*(int *)result == DDI_SUCCESS) {
if (bpc->bpc_nlevel > 0) {
} else {
}
}
/* release the hold we did in pre-notification */
"mdi_bus_power i_mdi_pm_rele_client\n"));
}
/* another thread might started attaching */
"mdi_bus_power i_mdi_pm_rele_client\n"));
/* detaching has been taken care in pm_post_unconfig */
"mdi_bus_power i_mdi_pm_reset_client\n"));
}
}
break;
/* need to do more */
case BUS_POWER_HAS_CHANGED:
"BUS_POWER_HAS_CHANGED:"
"%s@%s, olevel=%d, nlevel=%d, comp=%d\n",
if (bphc->bphc_nlevel > 0 &&
if (ct->ct_power_cnt == 0) {
}
"mdi_bus_power i_mdi_pm_hold_client\n"));
}
"mdi_bus_power i_mdi_pm_rele_client\n"));
}
break;
}
return (ret);
}
static int
{
int ret = MDI_SUCCESS;
return (MDI_FAILURE);
while (MDI_CLIENT_IS_POWER_TRANSITION(ct))
if (!MDI_CLIENT_IS_FAILED(ct)) {
"i_mdi_pm_pre_config_one already configured\n"));
return (MDI_SUCCESS);
}
if (ct->ct_powercnt_held) {
"i_mdi_pm_pre_config_one ALREADY held\n"));
return (MDI_SUCCESS);
}
if (ct->ct_power_cnt == 0) {
}
"i_mdi_pm_pre_config_one i_mdi_pm_hold_client\n"));
ct->ct_powercnt_reset = 0;
return (ret);
}
static int
{
int ret = MDI_SUCCESS;
int circ;
/* ndi_devi_config_one */
if (child) {
return (i_mdi_pm_pre_config_one(child));
}
/* devi_config_common */
while (cdip) {
if (ret != MDI_SUCCESS)
break;
}
return (ret);
}
static int
{
int ret = MDI_SUCCESS;
return (MDI_FAILURE);
while (MDI_CLIENT_IS_POWER_TRANSITION(ct))
"i_mdi_pm_pre_unconfig node detached already\n"));
return (MDI_SUCCESS);
}
if (MDI_CLIENT_IS_POWERED_DOWN(ct) &&
(flags & NDI_AUTODETACH)) {
"i_mdi_pm_pre_unconfig auto-modunload\n"));
return (MDI_FAILURE);
}
if (ct->ct_powercnt_held) {
"i_mdi_pm_pre_unconfig ct_powercnt_held\n"));
*held = 1;
return (MDI_SUCCESS);
}
if (ct->ct_power_cnt == 0) {
}
"i_mdi_pm_pre_unconfig i_mdi_pm_hold_client\n"));
ct->ct_powercnt_reset = 0;
if (ret == MDI_SUCCESS)
*held = 1;
return (ret);
}
static int
int flags)
{
int ret = MDI_SUCCESS;
int circ;
*held = 0;
/* ndi_devi_unconfig_one */
if (child) {
}
/* devi_unconfig_common */
while (cdip) {
}
if (*held)
ret = MDI_SUCCESS;
return (ret);
}
static void
{
return;
while (MDI_CLIENT_IS_POWER_TRANSITION(ct))
"i_mdi_pm_post_config_one NOT held\n"));
return;
}
/* client has not been updated */
if (MDI_CLIENT_IS_FAILED(ct)) {
"i_mdi_pm_post_config_one NOT configured\n"));
return;
}
/* another thread might have powered it down or detached it */
if ((MDI_CLIENT_IS_POWERED_DOWN(ct) &&
"i_mdi_pm_post_config i_mdi_pm_reset_client\n"));
} else {
int valid_path_count = 0;
"i_mdi_pm_post_config i_mdi_pm_rele_client\n"));
valid_path_count ++;
}
}
ct->ct_powercnt_held = 0;
}
static void
{
int circ;
/* ndi_devi_config_one */
if (child) {
return;
}
/* devi_config_common */
while (cdip) {
}
}
static void
{
return;
while (MDI_CLIENT_IS_POWER_TRANSITION(ct))
if (!ct->ct_powercnt_held) {
"i_mdi_pm_post_unconfig NOT held\n"));
return;
}
/* failure detaching or another thread just attached it */
if ((MDI_CLIENT_IS_POWERED_DOWN(ct) &&
"i_mdi_pm_post_unconfig i_mdi_pm_reset_client\n"));
}
"i_mdi_pm_post_unconfig not changed\n"));
}
static void
{
int circ;
if (!held) {
"i_mdi_pm_post_unconfig held = %d\n", held));
return;
}
if (child) {
return;
}
while (cdip) {
}
}
int
{
/*
* Handling ndi_devi_config_one and ndi_devi_unconfig_one.
* Power up pHCI for the named client device.
* Note: Before the client is enumerated under vhci by phci,
* client_dip can be NULL. Then proceed to power up all the
* pHCIs.
*/
}
switch (op) {
case MDI_PM_PRE_CONFIG:
break;
case MDI_PM_PRE_UNCONFIG:
flags);
break;
case MDI_PM_POST_CONFIG:
break;
case MDI_PM_POST_UNCONFIG:
break;
case MDI_PM_HOLD_POWER:
case MDI_PM_RELE_POWER:
if (op == MDI_PM_HOLD_POWER) {
if (ct->ct_power_cnt == 0) {
(void) i_mdi_power_all_phci(ct);
"mdi_power i_mdi_pm_hold_client\n"));
}
} else {
"mdi_power i_mdi_pm_rele_client\n"));
} else {
"mdi_power i_mdi_pm_reset_client\n"));
}
}
break;
default:
break;
}
return (ret);
}
int
{
return (MDI_FAILURE);
if (mdi_class) {
}
return (MDI_SUCCESS);
}
int
{
return (MDI_FAILURE);
if (mdi_class) {
}
return (MDI_SUCCESS);
}
int
{
if (!MDI_CLIENT(dip))
return (MDI_FAILURE);
if (mdi_class) {
}
return (MDI_SUCCESS);
}
/*
* XXX This list should include all phci drivers needed during boot time
* though it currently contains "fp" only.
* Hopefully, the mechanism provided here will be replaced with a better
* mechanism by vhci driven enumeration project.
*/
static char *phci_driver_list[] = { "fp" };
#define N_PHCI_DRIVERS (sizeof (phci_driver_list) / sizeof (char *))
static void
{
int i;
major_t m;
for (i = 0; i < N_PHCI_DRIVERS; i++) {
m = ddi_name_to_major(phci_driver_list[i]);
if (m != (major_t)-1) {
if (ddi_hold_installed_driver(m) != NULL)
ddi_rele_driver(m);
}
}
}
/* bus config the specified phci */
static void
i_mdi_phci_bus_config(void *arg)
{
int rv;
/*
* Must have already held the phci parent in
* i_mdi_bus_config_all_phcis().
* First configure the phci itself.
*/
/* release the hold that i_mdi_bus_config_all_phcis() placed */
if (rv == NDI_SUCCESS) {
/* now bus config the phci */
} else
/* release the hold that ndi_devi_config_one() placed */
}
}
/*
* Bus config all registered phcis associated with the vhci in parallel.
* This process guarantees that the child nodes are enumerated under the vhci,
* but not necessarily attached.
* op must be BUS_CONFIG_DRIVER or BUS_CONFIG_ALL.
*/
static int
{
mdi_vhci_t *vh;
mdi_phci_t *ph;
int phci_count, rv;
static int first_time = 1;
"!MDI: %s on all phcis: major = %d, flags = 0x%x, optimize = %d\n",
/*
* Reduce unnecessary BUS_CONFIG_ALLs when opening stale
* /dev/[r]dsk links.
*/
return (MDI_SUCCESS);
}
/*
* To initiate bus configs on all phcis in parallel, create a taskq
* with multiple threads. Since creation of a taskq is a heavy weight
* operation, taskq is created once per vhci and destroyed only when
* vhci unregisters with mdi.
*
* If multiple bus config requests arrive at a time, bus configs on
* phcis are initiated on behalf of one of the requests. Other requests
* wait until the bus configs on phcis is done.
*
* When a BUS_CONFIG_ALL on phcis completes, the following is done
* to avoid more of unnecessary bus configs.
*
* o all BUS_CONFIG_ALL requests currently waiting with optimize
* flag set are returned, i.e., no new BUS_CONFIG_ALL is initiated
* on phcis on behalf of these requests.
*
* o all BUS_CONFIG_ALL or BUS_CONFIG_DRIVER requests currently
* waiting but have arrived prior to initiating BUS_CONFIG_ALL on
* phcis are also returned.
*
* In other cases a new BUS_CONFIG_ALL or BUS_CONFIG_DRIVER is
* initiated on phcis on behalf of a new request.
*/
/* check if a bus config on phcis is in progress */
/* wait until the current bus configs on phcis are done */
if (current_op == BUS_CONFIG_ALL &&
req_time < start_time)) {
return (MDI_SUCCESS);
}
}
/*
* At this point we are single threaded until vh_bus_config.start_time
* is reset to 0 at the end of this function.
*/
first_time = 0;
}
if (vh->vh_phci_count == 0) {
rv = MDI_SUCCESS;
goto out1;
}
/*
* Create a taskq to initiate bus configs in parallel on phcis.
* Taskq allocation can be done in mdi_vhci_register() routine
* instead of here. For most systems, doing it here on demand saves
* resources as this code path is never called most of the times.
*/
/*
* it is ok even if vh->vh_phci_count changes after we release
* the mdi_mutex as phci_count is used just as an
* advisory number to taskq_create.
*/
/*
* As we are single threaded, it is ok to access the
* vh_bus_config.taskq member of vh outside of mdi_mutex
*/
"mdi_bus_config_taskq", mdi_max_bus_config_threads,
rv = MDI_FAILURE;
goto out;
}
}
/* allocate at least vh->vh_phci_count phci bus config structures */
int count;
while (count--) {
/*
* there is no need to hold a lock here as we
* are single threaded and no one else manipulates
* the list while we are here.
*/
}
/*
* as new phcis could register with mdi after we dropped
* the mdi_mutex, we need to recheck the vh->vh_phci_count.
* Hence the while loop.
*/
}
/* build a phci config handle to be passed to a taskq thread */
/*
* We need to hold the phci dip before bus configuring the phci.
* But placing a hold on the phci dip is not safe here due to
* the race with phci detach. To get around this race,
* we place a hold on the phci dip's parent and note down
* the phci's name@addr. Later, in i_mdi_phci_bus_config(),
* we'll first configure the phci itself before bus
* configuring the phci.
*/
}
"!MDI: initiating %s on all phcis, major = %d, flags = 0x%x\n",
/*
* again, no need to hold a lock here as we are single threaded and
* no one else manipulates the list while we are here.
*/
}
/* wait until all phci bus configs are done */
rv = MDI_SUCCESS;
out:
out1:
}
return (rv);
}
/*
* A simple bus config implementation for vhcis with the assumption that all
* phcis are always registered with MDI.
*
* BUS_CONFIG_ALL
*
* Do BUS_CONFIG_ALL on all phcis associated with the vhci.
*
* BUS_CONFIG_DRIVER
*
* Do BUS_CONFIG_DRIVER on all phcis associated with the vhci.
*
* BUS_CONFIG_ONE
*
* If the requested child has already been enumerated under the vhci
* configure the child and return. Otherwise do BUS_CONFIG_ALL on all
* phcis associated with the vhci.
*/
int
{
int rv = MDI_SUCCESS;
/*
* While bus configuring phcis, the phci driver interactions with MDI
* cause child nodes to be enumerated under the vhci node for which
* they need to ndi_devi_enter the vhci node.
*
* Unfortunately, to avoid the deadlock, we ourself can not wait for
* for the bus config operations on phcis to finish while holding the
* ndi_devi_enter lock. To avoid this deadlock, skip bus configs on
* phcis and call the default framework provided bus config function
* if we are called with ndi_devi_enter lock held.
*/
if (DEVI_BUSY_OWNED(vdip)) {
"!MDI: vhci bus config: vhci dip is busy owned\n"));
goto default_bus_config;
}
switch (op) {
case BUS_CONFIG_ONE:
/*
* First try to directly configure the requested child.
* This will work only if the requested child has already
* been enumerated under vhci, which is usually the most common
* case.
*/
NDI_SUCCESS) {
return (MDI_SUCCESS);
}
"will do BUS_CONFIG_ALL on all phcis\n", (char *)arg));
/* now do BUS_CONFIG_ALL on all phcis */
break;
case BUS_CONFIG_DRIVER:
break;
case BUS_CONFIG_ALL:
break;
default:
break;
}
/*
* i_mdi_bus_config_all_phcis() guarantees that child nodes are
* enumerated under the vhci, but not necessarily attached.
* Now configure the appropriate child nodes.
*/
if (rv == MDI_SUCCESS &&
NDI_SUCCESS) {
return (MDI_SUCCESS);
}
return (MDI_FAILURE);
}
void *
{
return (ct->ct_vprivate);
}
return (NULL);
}
void
{
}
}
/*
* mdi_pi_get_vhci_private():
* Get the vhci private information associated with the
* mdi_pathinfo node
*/
void *
{
if (pip) {
}
return (vprivate);
}
/*
* mdi_pi_set_vhci_private():
* Set the vhci private information in the mdi_pathinfo node
*/
void
{
if (pip) {
}
}
/*
* mdi_phci_get_vhci_private():
* Get the vhci private information associated with the
* mdi_phci node
*/
void *
{
mdi_phci_t *ph;
return (ph->ph_vprivate);
}
return (NULL);
}
/*
* mdi_phci_set_vhci_private():
* Set the vhci private information in the mdi_phci node
*/
void
{
mdi_phci_t *ph;
}
}