sunmdi.c revision 737d277a27d4872543f597e35c470e7510f61f03
/*
* 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 2006 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>
#include <sys/autoconf.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
/*
* The data should be "quiet" for this interval (in seconds) before the
* vhci cached data is flushed to the disk.
*/
static int mdi_vhcache_flush_delay = 10;
/* number of seconds the vhcache flush daemon will sleep idle before exiting */
static int mdi_vhcache_flush_daemon_idle_time = 60;
/*
* MDI falls back to discovery of all paths when a bus_config_one fails.
* The following parameters can be used to tune this operation.
*
* mdi_path_discovery_boot
* Number of times path discovery will be attempted during early boot.
* Probably there is no reason to ever set this value to greater than one.
*
* mdi_path_discovery_postboot
* Number of times path discovery will be attempted after early boot.
* Set it to a minimum of two to allow for discovery of iscsi paths which
* may happen very late during booting.
*
* mdi_path_discovery_interval
* Minimum number of seconds MDI will wait between successive discovery
* of all paths. Set it to -1 to disable discovery of all paths.
*/
static int mdi_path_discovery_boot = 1;
static int mdi_path_discovery_postboot = 2;
static int mdi_path_discovery_interval = 10;
/*
* number of seconds the asynchronous configuration thread will sleep idle
* before exiting.
*/
static int mdi_async_config_idle_time = 600;
static int mdi_bus_config_cache_hash_size = 256;
/* turns off multithreaded configuration for certain operations */
static int mdi_mtc_off = 0;
/*
*/
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 *);
static void i_mdi_log_sysevent(dev_info_t *, char *, char *);
/*
* 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 *);
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);
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 *);
static void setup_vhci_cache(mdi_vhci_t *);
static int destroy_vhci_cache(mdi_vhci_t *);
static void setup_phci_driver_list(mdi_vhci_t *);
static void free_phci_driver_list(mdi_vhci_config_t *);
static int stop_vhcache_async_threads(mdi_vhci_config_t *);
static boolean_t stop_vhcache_flush_thread(void *, int);
static void free_string_array(char **, int);
static void free_vhcache_phci(mdi_vhcache_phci_t *);
static void free_vhcache_pathinfo(mdi_vhcache_pathinfo_t *);
static void free_vhcache_client(mdi_vhcache_client_t *);
static void vhcache_pi_add(mdi_vhci_config_t *,
struct mdi_pathinfo *);
static void vhcache_pi_remove(mdi_vhci_config_t *,
struct mdi_pathinfo *);
static void free_phclient_path_list(mdi_phys_path_t *);
static void sort_vhcache_paths(mdi_vhcache_client_t *);
static int flush_vhcache(mdi_vhci_config_t *, int);
static void vhcache_dirty(mdi_vhci_config_t *);
static void free_async_client_config(mdi_async_client_config_t *);
static void single_threaded_vhconfig_enter(mdi_vhci_config_t *);
static void single_threaded_vhconfig_exit(mdi_vhci_config_t *);
static nvlist_t *read_on_disk_vhci_cache(char *);
extern int fread_nvlist(char *, nvlist_t **);
extern int fwrite_nvlist(char *, nvlist_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
*/
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 vHCI, pHCI and client count. All the pHCIs and clients
* should have been unregistered, before a vHCI can be
* unregistered.
*/
return (MDI_FAILURE);
}
/*
* Remove the vHCI from the global list
*/
if (vh == mdi_vhci_head) {
} else {
}
if (vh == mdi_vhci_tail) {
}
/* add vhci to the global list */
if (mdi_vhci_head == NULL)
mdi_vhci_head = vh;
else
mdi_vhci_tail = vh;
return (MDI_FAILURE);
}
mdi_client_table_size * sizeof (struct client_hash));
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++;
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--;
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 *
char **compatible, int ncompatible)
{
/* Verify for duplicate entry */
if (cdip) {
"i_mdi_devinfo_create: client dip %p already exists",
(void *)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.
*/
ct->ct_failover_flags = 0;
ct->ct_failover_status = 0;
ct->ct_unstable = 0;
ct->ct_path_count = 0;
/*
* Add this client component to our client hash queue
*/
return (ct);
}
/*
* 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
*/
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);
}
/*
* XXX - Is the rest of the code in this function really necessary?
* The consumers of mdi_pi_find() can search for the desired pathinfo
* node by calling mdi_pi_find(pdip, NULL, paddr). Irrespective of
* whether the search is based on the pathinfo nodes attached to
* the pHCI or the client node, the result will be the same.
*/
/*
* Find the client device corresponding to 'caddr'
*/
/*
* XXX - Passing NULL to the following function works as long as the
* the client addresses (caddr) are unique per vhci basis.
*/
/*
* 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;
int path_allocated = 0;
/* 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 matching client, create one if not found */
}
/*
* 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
*/
path_allocated = 1;
}
rv = MDI_SUCCESS;
fail:
/*
* Release the global mutex.
*/
/*
* Mark the pHCI as stable
*/
if (path_allocated)
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;
int se_flag;
int kmem_flag;
if (MDI_PHCI_IS_USER_DISABLED(ph))
if (MDI_PHCI_IS_DRV_DISABLED(ph))
/*
* Lock both dev_info nodes against changes in parallel.
*/
/* determine interrupt context */
return (pip);
}
/*
* 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.
*/
"!mdi_pi_free: "
"Timeout reached on path %p without the cond\n",
pip));
"!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);
}
}
if (rv == MDI_FAILURE)
return (rv);
}
/*
* i_mdi_pi_free():
* Free the mdi_pathinfo node
*/
static void
{
int ct_circular;
int ph_circular;
int se_flag;
int kmem_flag;
/*
* remove any per-path kstats
*/
/* determine interrupt context */
}
}
}
/*
* 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:
((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_config = 0;
ct->ct_powercnt_unconfig = 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_config) {
"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_unconfig) {
"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 configured\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_config = 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))
"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"));
} else {
int valid_path_count = 0;
"i_mdi_pm_post_unconfig i_mdi_pm_rele_client\n"));
valid_path_count ++;
}
ct->ct_powercnt_unconfig = 0;
}
}
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);
}
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;
}
}
/*
* List of vhci class names:
* A vhci class name must be in this list only if the corresponding vhci
* driver intends to use the mdi provided bus config implementation
* (i.e., mdi_vhci_bus_config()).
*/
#define N_VHCI_CLASSES (sizeof (vhci_class_list) / sizeof (char *))
/*
* Built-in list of phci drivers for every vhci class.
* All phci drivers expect iscsi have root device support.
*/
static mdi_phci_driver_info_t scsi_phci_driver_list[] = {
{ "fp", 1 },
{ "iscsi", 0 },
{ "ibsrp", 1 }
};
/*
* During boot time, the on-disk vhci cache for every vhci class is read
* in the form of an nvlist and stored here.
*/
/* nvpair names in vhci cache nvlist */
#define MDI_VHCI_CACHE_VERSION 1
#define MDI_NVPNAME_VERSION "version"
#define MDI_NVPNAME_PHCIS "phcis"
#define MDI_NVPNAME_CTADDRMAP "clientaddrmap"
/*
* Given vhci class name, return its on-disk vhci cache filename.
* Memory for the returned filename which includes the full path is allocated
* by this function.
*/
static char *
vhclass2vhcache_filename(char *vhclass)
{
char *filename;
int len;
/*
* fmt contains the on-disk vhci cache file name format;
* for scsi_vhci the filename is "/etc/devices/mdi_scsi_vhci_cache".
*/
/* the -1 below is to account for "%s" in the format string */
return (filename);
}
/*
* initialize the vhci cache related data structures and read the on-disk
* vhci cached data into memory.
*/
static void
{
int i;
/*
* Create string hash; same as mod_hash_create_strhash() except that
* we use NULL key destructor.
*/
/*
* The on-disk vhci cache is read during booting prior to the
* lights-out period by mdi_read_devices_files().
*/
for (i = 0; i < N_VHCI_CLASSES; i++) {
nvl = vhcache_nvl[i];
vhcache_nvl[i] = NULL;
break;
}
}
/*
* this is to cover the case of some one manually causing unloading
* (or detaching) and reloading (or attaching) of a vhci driver.
*/
else {
"%s: data file corrupted, will recreate\n",
}
}
CB_CL_UADMIN_PRE_VFS, "mdi_vhcache_flush");
}
/*
* free all vhci cache related resources
*/
static int
{
return (MDI_FAILURE);
if (vhc->vhc_phci_driver_list)
cphci = cphci_next) {
}
}
}
return (MDI_SUCCESS);
}
/*
* Setup the list of phci drivers associated with the specified vhci class.
* MDI uses this information to rebuild bus config cache if in case the
* cache is not available or corrupted.
*/
static void
{
char **driver_list1;
int i, j;
ndrivers = sizeof (scsi_phci_driver_list) /
sizeof (mdi_phci_driver_info_t);
ndrivers = sizeof (ib_phci_driver_list) /
sizeof (mdi_phci_driver_info_t);
} else {
driver_list = NULL;
ndrivers = 0;
}
/*
* The driver.conf file of a vhci driver can specify additional
* phci drivers using a project private "phci-drivers" property.
*/
&ndrivers1) != DDI_PROP_SUCCESS)
ndrivers1 = 0;
if (vhc->vhc_nphci_drivers == 0)
return;
for (i = 0; i < ndrivers; i++) {
}
for (j = 0; j < ndrivers1; j++, i++) {
}
if (ndrivers1)
}
/*
* Free the memory allocated for the phci driver list
*/
static void
{
int i;
return;
for (i = 0; i < vhc->vhc_nphci_drivers; i++) {
}
}
/*
* Stop all vhci cache related async threads and free their resources.
*/
static int
{
vhc->vhc_acc_thrcount != 0) {
delay(1);
}
}
vhc->vhc_acc_count = 0;
return (MDI_FAILURE);
}
} else
return (MDI_FAILURE);
return (MDI_SUCCESS);
}
/*
* Stop vhci cache flush thread
*/
/* ARGSUSED */
static boolean_t
{
delay(1);
}
} else
return (B_TRUE);
}
/*
* Enqueue the vhcache phci (cphci) at the tail of the list
*/
static void
{
else
}
/*
* Enqueue the vhcache pathinfo (cpi) at the tail of the list
*/
static void
{
else
}
/*
* Enqueue the vhcache pathinfo (cpi) at the correct location in the
* ordered list. All cpis which do not have MDI_CPI_HINT_PATH_DOES_NOT_EXIST
* flag set come at the beginning of the list. All cpis which have this
* flag set come at the end of the list.
*/
static void
{
else {
;
else
}
}
/*
* Enqueue the vhcache client (cct) at the tail of the list
*/
static void
{
else
}
static void
{
int i;
if (str) {
for (i = 0; i < nelem; i++) {
if (str[i])
}
}
}
static void
{
}
static void
{
}
static void
{
}
static char *
{
char *name_addr;
int len;
if (ret_len)
return (name_addr);
}
/*
* Copy the contents of paddrnvl to vhci cache.
* paddrnvl nvlist contains path information for a vhci client.
* See the comment in mainnvl_to_vhcache() for the format of this nvlist.
*/
static void
{
}
}
/*
* Copy the contents of caddrmapnvl to vhci cache.
* caddrmapnvl nvlist contains vhci client address to phci client address
* mappings. See the comment in mainnvl_to_vhcache() for the format of
* this nvlist.
*/
static void
{
/* the client must contain at least one path */
}
}
/*
* Copy the contents of the main nvlist to vhci cache.
*
* VHCI busconfig cached data is stored in the form of a nvlist on the disk.
* The nvlist contains the mappings between the vhci client addresses and
* their corresponding phci client addresses.
*
* The structure of the nvlist is as follows:
*
* Main nvlist:
* NAME TYPE DATA
* version int32 version number
* phcis string array array of phci paths
* clientaddrmap nvlist_t c2paddrs_nvl (see below)
*
* structure of c2paddrs_nvl:
* NAME TYPE DATA
* caddr1 nvlist_t paddrs_nvl1
* caddr2 nvlist_t paddrs_nvl2
* ...
* where caddr1, caddr2, ... are vhci client name and addresses in the
* form of "<clientname>@<clientaddress>".
* (for example: "ssd@2000002037cd9f72");
* paddrs_nvl1, paddrs_nvl2, .. are nvlists that contain path information.
*
* structure of paddrs_nvl:
* NAME TYPE DATA
* pi_addr1 uint32_array (phci-id, cpi_flags)
* pi_addr2 uint32_array (phci-id, cpi_flags)
* ...
* where pi_addr1, pi_addr2, ... are bus specific addresses of pathinfo nodes
* (so called pi_addrs, for example: "w2100002037cd9f72,0");
* phci-ids are integers that identify PHCIs to which the
* the bus specific address belongs to. These integers are used as an index
* into to the phcis string array in the main nvlist to get the PHCI path.
*/
static int
{
char **phcis, **phci_namep;
int i;
return (MDI_FAILURE);
&nphcis) != 0)
return (MDI_SUCCESS);
cphci_list[i] = cphci;
}
return (MDI_SUCCESS);
}
/*
* Build paddrnvl for the specified client using the information in the
* vhci cache and add it to the caddrmapnnvl.
* Returns 0 on success, errno on failure.
*/
static int
{
int err;
return (err);
!= 0)
goto out;
}
out:
return (err);
}
/*
* Build caddrmapnvl using the information in the vhci cache
* and add it to the mainnvl.
* Returns 0 on success, errno on failure.
*/
static int
{
int err;
return (err);
goto out;
}
out:
return (err);
}
/*
* Build nvlist using the information in the vhci cache.
* See the comment in mainnvl_to_vhcache() for the format of the nvlist.
* Returns nvl on success, NULL on failure.
*/
static nvlist_t *
{
char **phcis;
int err, i;
goto out;
}
MDI_VHCI_CACHE_VERSION)) != 0)
goto out;
return (nvl);
}
phci_count = 0;
/* build phci pathname list */
if (err == 0 &&
return (nvl);
}
out:
if (nvl)
return (NULL);
}
/*
* Lookup vhcache phci structure for the specified phci path.
*/
static mdi_vhcache_phci_t *
{
return (cphci);
}
return (NULL);
}
/*
* Lookup vhcache phci structure for the specified phci.
*/
static mdi_vhcache_phci_t *
{
return (cphci);
}
return (NULL);
}
/*
* Add the specified phci to the vhci cache if not already present.
*/
static void
{
char *pathname;
int cache_updated;
!= NULL) {
cache_updated = 0;
} else {
cache_updated = 1;
}
/*
* Since a new phci has been added, reset
* vhc_path_discovery_cutoff_time to allow for discovery of paths
* during next vhcache_discover_paths().
*/
if (cache_updated)
}
/*
* Remove the reference to the specified phci from the vhci cache.
*/
static void
{
/* do not remove the actual mdi_vhcache_phci structure */
}
}
static void
{
dst->lt_cct_lookup_time = 0;
} else {
}
}
/*
* Look up vhcache client for the specified client.
*/
static mdi_vhcache_client_t *
{
char *name_addr;
int len;
/*
* If no vhcache clean occurred since the last lookup, we can
* simply return the cct from the last lookup operation.
* It works because ccts are never freed except during the vhcache
* cleanup operation.
*/
if (token) {
}
} else {
if (token) {
token->lt_cct_lookup_time = 0;
}
}
return ((mdi_vhcache_client_t *)hv);
}
/*
* Add the specified path to the vhci cache if not already present.
* Also add the vhcache client for the client corresponding to this path
* if it doesn't already exist.
*/
static void
{
int cache_updated = 0;
/* if vhcache client for this pip doesn't already exist, add it */
cache_updated = 1;
}
cache_updated = 1;
}
break;
}
}
cache_updated = 1;
}
if (cache_updated)
}
/*
* Remove the reference to the specified path from the vhci cache.
*/
static void
{
break;
}
}
}
}
/*
* Flush the vhci cache to disk.
* Returns MDI_SUCCESS on success, MDI_FAILURE on failure.
*/
static int
{
int err;
int rv;
/*
* It is possible that the system may shutdown before
* i_ddi_io_initialized (during stmsboot for example). To allow for
* flushing the cache in this case do not check for
* i_ddi_io_initialized when force flag is set.
*/
if (force_flag == 0 && !i_ddi_io_initialized())
return (MDI_FAILURE);
} else
rv = MDI_SUCCESS;
if (err != 0) {
} else {
}
rv = MDI_FAILURE;
}
}
return (rv);
}
/*
* Call flush_vhcache() to flush the vhci cache at the scheduled time.
* Exits itself if left idle for the idle timeout period.
*/
static void
vhcache_flush_thread(void *arg)
{
/* number of seconds to sleep idle before exiting */
"mdi_vhcache_flush");
for (; ; ) {
} else {
}
}
ddi_get_lbolt() < quit_at_ticks) {
}
goto out;
}
out:
/* CALLB_CPR_EXIT releases the vhc->vhc_lock */
}
/*
* Make vhci cache dirty and schedule flushing by vhcache flush thread.
*/
static void
{
int create_thread;
/* do not flush cache until the cache is fully built */
return;
}
return;
}
create_thread = 0;
} else {
create_thread = 1;
}
if (create_thread)
}
/*
* phci bus config structure - one for for each phci bus config operation that
* we initiate on behalf of a vhci.
*/
typedef struct mdi_phci_bus_config_s {
char *phbc_phci_path;
struct mdi_phci_bus_config_s *phbc_next;
/* vhci bus config structure - one for each vhci bus config operation */
typedef struct mdi_vhci_bus_config_s {
int vhbc_thr_count;
/*
* bus config the specified phci
*/
static void
bus_config_phci(void *arg)
{
/*
* first configure all path components upto phci and then configure
* the phci children.
*/
!= NULL) {
(void) ndi_devi_config_driver(ph_dip,
} else
(void) ndi_devi_config(ph_dip,
/* release the hold that e_ddi_hold_devi_by_path() placed */
}
vhbc->vhbc_thr_count--;
if (vhbc->vhbc_thr_count == 0)
}
/*
* Bus config all phcis associated with the vhci in parallel.
* op must be BUS_CONFIG_DRIVER or BUS_CONFIG_ALL.
*/
static void
{
return;
}
KM_SLEEP);
vhbc->vhbc_thr_count++;
}
/* now create threads to initiate bus config on all phcis in parallel */
if (mdi_mtc_off)
bus_config_phci((void *)phbc);
else
}
/* wait until all threads exit */
while (vhbc->vhbc_thr_count > 0)
}
/*
* Single threaded version of bus_config_all_phcis()
*/
static void
{
}
/*
* Perform BUS_CONFIG_ONE on the specified child of the phci.
* The path includes the child component in addition to the phci path.
*/
static int
bus_config_one_phci_child(char *path)
{
char *devnm;
int rv = MDI_FAILURE;
/* extract the child component of the phci */
*devnm++ = '\0';
/*
* first configure all path components upto phci and then
* configure the phci child.
*/
NDI_SUCCESS) {
/*
* release the hold that ndi_devi_config_one() placed
*/
rv = MDI_SUCCESS;
}
/* release the hold that e_ddi_hold_devi_by_path() placed */
}
devnm--;
*devnm = '/';
return (rv);
}
/*
* Build a list of phci client paths for the specified vhci client.
* The list includes only those phci client paths which aren't configured yet.
*/
static mdi_phys_path_t *
{
int config_path, len;
/*
* include only those paths that aren't configured.
*/
config_path = 0;
config_path = 1;
else {
config_path = 1;
}
if (config_path) {
else
}
}
return (pp_head);
}
/*
* Free the memory allocated for phci client path list.
*/
static void
{
}
}
/*
* Allocated async client structure and initialize with the specified values.
*/
static mdi_async_client_config_t *
{
return (acc);
}
/*
* Free the memory allocated for the async client structure and their members.
*/
static void
{
}
/*
* Sort vhcache pathinfos (cpis) of the specified client.
* All cpis which do not have MDI_CPI_HINT_PATH_DOES_NOT_EXIST
* flag set come at the beginning of the list. All cpis which have this
* flag set come at the end of the list.
*/
static void
{
}
}
/*
* Verify whether MDI_CPI_HINT_PATH_DOES_NOT_EXIST flag setting is correct for
* every vhcache pathinfo of the specified client. If not adjust the flag
* setting appropriately.
*
* Note that MDI_CPI_HINT_PATH_DOES_NOT_EXIST flag is persisted in the
* on-disk vhci cache. So every time this flag is updated the cache must be
* flushed.
*/
static void
{
== NULL) {
return;
}
/*
* to avoid unnecessary on-disk cache updates, first check if an
* update is really needed. If no update is needed simply return.
*/
break;
}
}
return;
}
return;
}
}
else
}
}
/*
* Configure all specified paths of the client.
*/
static void
{
}
/*
* Dequeue elements from vhci async client config list and bus configure
* their corresponding phci clients.
*/
static void
config_client_paths_thread(void *arg)
{
"mdi_config_client_paths");
for (; ; ) {
ddi_get_lbolt() < quit_at_ticks) {
}
goto out;
vhc->vhc_acc_count--;
}
out:
vhc->vhc_acc_thrcount--;
/* CALLB_CPR_EXIT releases the vhc->vhc_lock */
}
/*
* Arrange for all the phci client paths (pp_head) for the specified client
* to be bus configured asynchronously by a thread.
*/
static void
{
int create_thread;
return;
if (mdi_mtc_off) {
return;
}
return;
}
}
else
vhc->vhc_acc_count++;
create_thread = 0;
} else {
vhc->vhc_acc_thrcount++;
create_thread = 1;
}
if (create_thread)
}
/*
* Return number of online paths for the specified client.
*/
static int
{
int online_count = 0;
online_count++;
}
}
return (online_count);
}
/*
* Bus configure all paths for the specified vhci client.
* If at least one path for the client is already online, the remaining paths
* will be configured asynchronously. Otherwise, it synchronously configures
* the paths until at least one path is online and then rest of the paths
* will be configured asynchronously.
*/
static void
{
== NULL ||
return;
}
/* if at least one path is online, configure the rest asynchronously */
if (nonline_paths(cct) > 0) {
return;
}
goto out;
}
if (nonline_paths(cct) > 0 &&
goto out;
}
}
}
out:
}
static void
{
}
static void
{
}
/*
* Attach the phci driver instances associated with the vhci:
* If root is mounted attach all phci driver instances.
* If root is not mounted, attach the instances of only those phci
* drivers that have the root support.
*/
static void
{
int i;
major_t m;
for (i = 0; i < vhc->vhc_nphci_drivers; i++) {
if (modrootloaded == 0 &&
continue;
m = ddi_name_to_major(
if (m != (major_t)-1) {
if (ddi_hold_installed_driver(m) != NULL)
ddi_rele_driver(m);
}
}
}
/*
* Build vhci cache:
*
* Attach phci driver instances and then drive BUS_CONFIG_ALL on
* the phci driver instances. During this process the cache gets built.
*
* Cache is built fully if the root is mounted.
* If the root is not mounted, phci drivers that do not have root support
* are not attached. As a result the cache is built partially. The entries
* in the cache reflect only those phci drivers that have root support.
*/
static int
{
return (0);
}
return (1);
}
/*
* Determine if discovery of paths is needed.
*/
static int
{
int rv = 1;
if (i_ddi_io_initialized() == 0) {
if (vhc->vhc_path_discovery_boot > 0) {
goto out;
}
} else {
if (vhc->vhc_path_discovery_postboot > 0) {
goto out;
}
}
/*
* Do full path discovery at most once per mdi_path_discovery_interval.
* This is to avoid a series of full path discoveries when opening
* stale /dev/[r]dsk links.
*/
if (mdi_path_discovery_interval != -1 &&
goto out;
rv = 0;
out:
return (rv);
}
/*
* Discover all paths:
*
* Attach phci driver instances and then drive BUS_CONFIG_ALL on all the phci
* driver instances. During this process all paths will be discovered.
*/
static int
{
int rv = 0;
if (vhcache_do_discovery(vhc)) {
rv = 1;
}
return (rv);
}
/*
* Generic vhci bus config implementation:
*
* Parameters
* vdip vhci dip
* flags bus config flags
* op bus config operation
* The remaining parameters are bus config operation specific
*
* for BUS_CONFIG_ONE
* arg pointer to name@addr
* child upon successful return from this function, *child will be
* set to the configured and held devinfo child node of vdip.
* ct_addr pointer to client address (i.e. GUID)
*
* for BUS_CONFIG_DRIVER
* arg major number of the driver
* child and ct_addr parameters are ignored
*
* for BUS_CONFIG_ALL
* arg, child, and ct_addr parameters are ignored
*
* Note that for the rest of the bus config operations, this function simply
* calls the framework provided default bus config routine.
*/
int
{
int rv = 0;
int params_valid = 0;
char *cp;
/*
* 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:
/* extract node name */
cp++;
if (*cp == '@') {
params_valid = 1;
*cp = '\0';
/* config_client_paths() releases cache_lock */
*cp = '@';
break;
}
}
break;
case BUS_CONFIG_DRIVER:
if (rv == 0)
break;
case BUS_CONFIG_ALL:
if (rv == 0)
break;
default:
break;
}
/*
* All requested child nodes are enumerated under the vhci.
* Now configure them.
*/
NDI_SUCCESS) {
return (MDI_SUCCESS);
/* discover all paths and try configuring again */
if (vhcache_discover_paths(vhc) &&
return (MDI_SUCCESS);
}
return (MDI_FAILURE);
}
/*
* Read the on-disk vhci cache into an nvlist for the specified vhci class.
*/
static nvlist_t *
{
int err;
char *filename;
return (nvl);
"%s: data file corrupted, will recreate\n", filename);
return (NULL);
}
/*
* Read on-disk vhci cache into nvlists for all vhci classes.
* Called during booting by i_ddi_read_devices_files().
*/
void
mdi_read_devices_files(void)
{
int i;
for (i = 0; i < N_VHCI_CLASSES; i++)
}
/*
* Remove all stale entries from vhci cache.
*/
static void
{
} else
}
else {
}
}
else
}
}
/*
* Remove all stale entries from vhci cache.
* Called by i_ddi_clean_devices_files() during the execution of devfsadm -C
*/
void
mdi_clean_vhcache(void)
{
mdi_vhci_t *vh;
}
}
/*
* mdi_vhci_walk_clients():
* Walker routine to traverse client dev_info nodes
* ddi_walk_devs(ddi_get_child(vdip), f, arg) returns the entire tree
* below the client, including nexus devices, which we dont want.
* So we just traverse the immediate siblings, starting from 1st client.
*/
void
int (*f)(dev_info_t *, void *), void *arg)
{
while (cdip) {
case DDI_WALK_CONTINUE:
break;
default:
return;
}
}
}
/*
* mdi_vhci_walk_phcis():
* Walker routine to traverse phci dev_info nodes
*/
void
int (*f)(dev_info_t *, void *), void *arg)
{
while (ph) {
case DDI_WALK_CONTINUE:
break;
default:
return;
}
}
}
/*
* mdi_walk_vhcis():
* Walker routine to traverse vhci dev_info nodes
*/
void
{
/*
* Scan for already registered vhci
*/
break;
} else {
}
}
}
/*
* i_mdi_log_sysevent():
* Logs events for pickup by syseventd
*/
static void
{
char *path_name;
KM_SLEEP) != DDI_SUCCESS) {
goto alloc_failed;
}
goto error;
}
goto error;
}
goto error;
}
path_name) != DDI_SUCCESS) {
goto error;
}
ph_vh_class) != DDI_SUCCESS) {
goto error;
}
return;
"!i_mdi_log_sysevent: Unable to send sysevent"));
}