/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "cfga_fp.h"
/* define */
LUN_SIZE + \
/* Some forward declarations */
uchar_t, char **);
static fpcfga_ret_t lun_unconf(char *, int, char *, char *, char **);
static void copy_pwwn_data_to_str(char *, const uchar_t *);
char *, int, int *, int *, char **, cfga_flags_t);
int, int *, int *, char **, cfga_flags_t);
int *, int *);
char **, HBA_HANDLE, int, HBA_PORTATTRIBUTES);
/*
* This function initiates the creation of the new device node for a given
* port WWN.
* So, apidt->dyncomp CANNOT be NULL
*/
static fpcfga_ret_t
{
return (FPCFGA_LIB_ERR);
}
return (FPCFGA_LIB_ERR);
}
PORT_WWN_PROP, 0);
return (FPCFGA_LIB_ERR);
}
apidt->xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
/* Let driver handle creation of the new path */
if (dev_dtype == DTYPE_UNKNOWN) {
/*
* Unknown DTYPES are devices such as another system's
* FC HBA port. We have tried to configure it but
* have failed. Since devices with no device type
* or an unknown dtype cannot be configured, we will
* return an appropriate error message.
*/
} else {
}
return (FPCFGA_LIB_ERR);
}
return (FPCFGA_OK);
}
/*
* Online, in RCM, all the LUNs for a particular device.
* Caller can specify the # of luns in the lunlist that have to be onlined
* by passing a count that is not -ve.
*
* INPUT :
* apidt - this is expected to have the list of luns for the device and so
* is assumed to be filled in prior to this call
* count - # of LUNs in the list that have to be onlined.
* errstring - If non-NULL, it will hold any error messages
*
* RETURNS :
* 0 on success
* non-zero otherwise
*/
static fpcfga_ret_t
{
int i = 0, ret = 0;
/* This check may be redundant, but safer this way */
/* User has requested not to notify RCM framework */
return (FPCFGA_OK);
}
break;
FPCFGA_OK) {
ret++;
}
}
if (ret > 0)
return (retval);
}
/*
* Online in RCM for devices which only have paths
*/
void
char **errstring)
{
return;
}
continue;
}
}
}
/*
* Offline, in RCM, all the LUNs for a particular device.
* This function should not be called for the MPXIO case.
*
* INPUT :
* apidt - this is expected to have the list of luns for the device and so
* is assumed to be filled in prior to this call
* errstring - If non-NULL, it will hold any error messages
*
* RETURNS :
* FPCFGA_OK on success
* error code otherwise
*/
static fpcfga_ret_t
{
int count = 0;
/* User has requested not to notify RCM framework */
return (FPCFGA_OK);
}
continue;
}
if (((ret == 0) &&
((ret != 0) &&
DI_DEVICE_OFFLINE))) {
/* Offline the device through RCM */
flags) != 0) {
/*
* Bring everything back online in
* rcm and return
*/
return (FPCFGA_LIB_ERR);
}
count++;
}
} else {
/* Offline the device through RCM */
flags) != 0) {
/*
* Bring everything back online in
* rcm and return
*/
NULL);
return (FPCFGA_LIB_ERR);
}
count++;
}
}
return (FPCFGA_OK);
}
/*
* Remove, in RCM, all the LUNs for a particular device.
* This function should not be called for the MPXIO case.
*
* INPUT :
* apidt - this is expected to have the list of luns for the device and so
* is assumed to be filled in prior to this call
* errstring - If non-NULL, it will hold any error messages
*
* RETURNS :
* FPCFGA_OK on success
* error code otherwise
*/
static fpcfga_ret_t
{
int count = 0;
/* User has requested not to notify RCM framework */
return (FPCFGA_OK);
}
continue;
if (((ret == 0) &&
((ret != 0) &&
DI_DEVICE_OFFLINE))) {
/* remove the device through RCM */
flags) != 0) {
/*
* Bring everything back online in
* rcm and return
*/
return (FPCFGA_LIB_ERR);
}
count++;
}
} else {
/* remove the device through RCM */
flags) != 0) {
/*
* Bring everything back online in rcm and
* return
*/
NULL);
return (FPCFGA_LIB_ERR);
}
count++;
}
}
return (FPCFGA_OK);
}
static fpcfga_ret_t
char **errstring)
{
return (FPCFGA_OK);
/*
* We have an MPXIO managed device here.
* So, we have to concoct a path for the device.
*
* xport_phys looks like :
*/
*ptr = '\0';
}
/*
* Get pointer to driver name from VHCI path
* So, if lunlistp->path is
* we need a pointer to the last '/'
*
* Assumption:
* With MPXIO there will be only one entry per lun
* So, there will only be one entry in the linked list
* apidt->lunlist
*/
/* This shouldn't happen, but anyways ... */
return (FPCFGA_LIB_ERR);
}
/*
* Make pathname to look something like :
*/
/*
* apidt_create() will make sure that lunlist->path
* has a "@<something>" at the end even if the driver
* state is "detached"
*/
/* This shouldn't happen, but anyways ... */
pathname, 0);
return (FPCFGA_LIB_ERR);
}
*ptr = '\0';
/* Now, concoct the path */
} else {
/*
* non-MPXIO path, use the path that is passed in
*/
}
return (FPCFGA_LIB_ERR);
}
if (devctl_device_remove(hdl) != 0) {
return (FPCFGA_LIB_ERR);
}
return (FPCFGA_OK);
}
static fpcfga_ret_t
{
lun_cnt++;
/*
* Unconfigure each LUN.
* Note that for MPXIO devices, lunlistp->path will be a
* vHCI path
*/
strlen(SCSI_VHCI_ROOT)) == 0) {
if (lunlistp->node_state ==
return (ret);
}
}
} else {
return (ret);
}
}
}
} else {
/*
* Unconfigure each LUN.
* Note that for MPXIO devices, lunlistp->path will be a
* vHCI path
*/
return (ret);
}
}
}
/*
* when all luns are unconfigured
* indicate to remove repository entry.
*/
if (lun_cnt == unusable_lun_cnt) {
}
}
return (ret);
}
/*
* Check if the given physical path (the xport_phys) is part of the
* pHCI list and if the RCM should be done for a particular pHCI.
* Skip non-MPxIO dev node if any.
*/
static fpcfga_ret_t
{
/* a safety check */
return (FPCFGA_LIB_ERR);
}
strlen(SCSI_VHCI_ROOT)) != 0) {
continue;
}
num_active_paths = 0; /* # of paths in ONLINE/STANDBY */
xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
*pathname_ptr = '\0';
}
if (root == DI_NODE_NIL) {
return (FPCFGA_LIB_ERR);
}
DI_NODE_NIL) {
return (FPCFGA_LIB_ERR);
}
found = 0;
} else {
found = 1;
break;
}
}
}
if (found == 0) {
xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
/* found vhci_path we are looking for */
found = 0;
path != DI_PATH_NIL;
xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
/*
* Check if the phci path has the same
* xport addr and the target addr with current lun
*/
strlen(pathname_ptr)) == 0) &&
/* SUCCESS Found xport_phys */
found = 1;
} else if ((di_path_state(path) ==
} else {
/*
* state now, so should do a RCM online after
* the unconfiguration of current path.
*/
}
}
if (found == 1) {
if (num_active_paths != 0) {
/*
* so no need to do the RCM
*/
}
if (non_operational_path_count == 0) {
}
} else {
/*
* Fail all operations here
*/
xport_phys, 0);
return (FPCFGA_APID_NOEXIST);
}
}
/* Mark duplicated paths for same vhci in the list */
strlen(SCSI_VHCI_ROOT)) != 0) {
continue;
}
/*
* don't do RCM for dup
*/
}
}
}
return (FPCFGA_OK);
}
/*
* apidt->dyncomp has to be non-NULL by the time this routine is called
*/
{
int no_config_attempt = 0;
/*
* No dynamic component specified. Just return success.
* Should not see this case. Just a safety check.
*/
return (FPCFGA_OK);
}
/* Now construct the string we are going to put in the repository */
return (FPCFGA_LIB_ERR);
}
/* If force update of repository is sought, do it first */
if (optflag & FLAG_FORCE_UPDATE_REP) {
/* Ignore any failure in rep update */
(void) update_fabric_wwn_list(
((state_change_cmd == CFGA_CMD_CONFIGURE) ?
}
/*
* Checking the sense key data as well as the additional
* sense key. The SES Node is not required to repond
* to Report LUN. In the case of Minnow, the SES node
* returns with KEY_ILLEGAL_REQUEST and the additional
* sense key of 0x20. In this case we will blindly
* send the SCSI Inquiry call to lun 0
*
* if we get any other error we will set the inq_type
* appropriately
*/
lun = 0;
} else {
if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
} else {
/*
* Failed to get the LUN data for the device
* If we find that there is a lunlist for this
* device it could mean that there are dangling
* devinfo nodes. So, we will go ahead and try
* to unconfigure them.
*/
(state_change_cmd == CFGA_CMD_CONFIGURE)) {
if (status ==
return (FPCFGA_APID_NOEXIST);
} else {
return (FPCFGA_LIB_ERR);
}
} else {
/* unconfig with lunlist not empty */
}
}
}
}
for (i = 0; i < num_luns; i++) {
/*
* issue the inquiry to the first valid lun found
* in the lun_string
*/
/*
* if Inquiry is returned correctly, check the
* peripheral qualifier for the lun. if it is non-zero
* then try the SCSI Inquiry on the next lun
*/
if (status == HBA_STATUS_OK) {
if (peri_qual == DPQ_POSSIBLE) {
break;
}
}
}
/*
* If there are no luns on this target, we will attempt to send
* the SCSI Inquiry to lun 0
*/
if (num_luns == 0) {
lun = 0;
}
if (status != HBA_STATUS_OK) {
if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
} else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
return (FPCFGA_APID_NOEXIST);
} else {
/*
* Failed to get the inq_dtype of device
* If we find that there is a lunlist for this
* device it could mean that there dangling
* devinfo nodes. So, we will go ahead and try
* to unconfigure them. We'll just set the
* inq_dtype to some invalid value (0xFF)
*/
(state_change_cmd == CFGA_CMD_CONFIGURE)) {
return (FPCFGA_LIB_ERR);
} else {
/* unconfig with lunlist not empty */
}
}
}
switch (state_change_cmd) {
case CFGA_CMD_CONFIGURE:
return (FPCFGA_OK);
}
((flags & CFGA_FLAG_FORCE) == 0)) {
/*
* We assume all DTYPE_UNKNOWNs are HBAs and we wont
* waste time trying to config them. If they are not
* HBAs, then there is something wrong since they should
* have had a valid dtype.
*
* However, if the force flag is set (cfgadm -f), we
* go ahead and try to configure.
*
* In this path, however, the force flag is not set.
*/
return (FPCFGA_OK);
}
errno = 0;
/*
* We'll issue the devctl_bus_dev_create() call even if the
* path exists in the devinfo tree. This is to take care of
* the situation where the device may be in a state other
* than the online and attached state.
*/
/*
* Could not configure device. To provide a more
* meaningful error message, first see if the supplied port
* WWN is there on the fabric. Otherwise print the error
* message using the information received from the driver
*/
if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
return (FPCFGA_APID_NOEXIST);
} else {
return (FPCFGA_LIB_ERR);
}
}
}
/*
* There may be multiple LUNs associated with the
* WWN we created nodes for. So, we'll call
* apidt_create() again and let it build a list of
* all the LUNs for this WWN using the devinfo tree.
* We will then online all those devices in RCM
*/
return (FPCFGA_LIB_ERR);
}
return (ret);
}
return (ret);
}
}
return (FPCFGA_OK);
case CFGA_CMD_UNCONFIGURE:
return (FPCFGA_OPNOTSUPP);
}
/*
* But first, remove entry from the repository if it is
* there ... provided the force update flag is not set
* (in which case the update is already done) or if
* the no-update flag is not set.
*/
if ((optflag &
(FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) {
update_str, errstring)) {
return
}
}
if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
return (FPCFGA_APID_NOEXIST);
}
return (FPCFGA_OK);
}
/*
* If there are multiple paths to the mpxio
* device, we will not check in RCM ONLY when there
*/
FPCFGA_OK) {
return (FPCFGA_XPORT_NOT_IN_PHCI_LIST);
}
/*
* dev_rcm_offline() updates errstring
*/
FPCFGA_OK) {
return (ret);
}
FPCFGA_OK) {
/* when inq failed don't attempt to reconfigure */
if (!no_config_attempt) {
}
return (ret);
}
FPCFGA_OK) {
return (ret);
}
/*
* If we offlined a lun in RCM when there are multiple paths but
* in RCM now. This is a try best, will not fail for it.
*/
/* Update the repository if we havent already done it */
if ((optflag &
(FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) {
if (((optflag & FLAG_REMOVE_UNUSABLE_FCP_DEV) !=
(((optflag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
(unconf_flag == ALL_APID_LUNS_UNUSABLE))) {
update_str, errstring)) {
return (FPCFGA_UNCONF_OK_UPD_REP_FAILED);
}
}
}
return (FPCFGA_OK);
default:
return (FPCFGA_OPNOTSUPP);
}
}
/*
* This function copies a port_wwn got by reading the property on a device
* node (from_ptr in the function below) on to an array (to_ptr) so that it is
* readable.
*
* Caller responsible to allocate enough memory in "to_ptr"
*/
static void
{
return;
}
static fpcfga_ret_t
char *dyncomp, int unusable_flag,
{
while (pnode != DI_PATH_NIL) {
(*num_devs)++;
xport_phys, 0);
(*failure_count)++;
continue;
}
node_path, 0);
(*failure_count)++;
continue;
}
DI_NODE_NIL) {
(*failure_count)++;
continue;
}
(*failure_count)++;
continue;
}
*ptr = '\0';
}
(*failure_count)++;
continue;
}
/*
* Try to offline in RCM first and if that is successful,
* unconfigure the LUN. If offlining in RCM fails, then
* update the failure_count which gets passed back to caller
*
* Here we got to check if unusable_flag is set or not.
* If set, then unconfigure only those luns which are in
* node_state DI_PATH_STATE_OFFLINE. If not set, unconfigure
* all luns.
*/
if ((unusable_flag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
flags) != 0) {
(*failure_count)++;
pnode);
continue;
!= FPCFGA_OK) {
(void) fp_rcm_online(pathname,
(*failure_count)++;
pnode);
continue;
flags) != 0) {
/*
* Bring everything back online
* in rcm and continue
*/
(void) fp_rcm_online(pathname,
(*failure_count)++;
pnode);
continue;
}
} else {
continue;
}
} else {
(*failure_count)++;
continue;
(*failure_count)++;
continue;
flags) != 0) {
/*
* Bring everything back online
* in rcm and continue
*/
(*failure_count)++;
continue;
}
}
/* Update the repository only on a successful unconfigure */
(*failure_count)++;
continue;
}
/* Init the string to be removed from repository */
errstring)) {
(*failure_count)++;
/* Cleanup and continue from here just for clarity */
continue;
}
}
return (FPCFGA_OK);
}
static fpcfga_ret_t
{
while (dnode != DI_NODE_NIL) {
(*num_devs)++;
/* Get the physical path for this node */
/*
* We don't try to offline in RCM here because we
* don't know the path to offline. Just continue to
* the next node.
*/
(*failure_count)++;
continue;
}
/* Now get the LUN # of this device thru the property */
/* Next get the port WWN of the device */
/* A failure in any of the above is not good */
/*
* We don't try to offline in RCM here because we
* don't know the path to offline. Just continue to
* the next node.
*/
ERRARG_DI_GET_PROP, node_path, 0);
(*failure_count)++;
continue;
}
/* Prepend the "/devices" prefix to the path and copy it */
/*
* If the driver is detached, some part of the path
* may be missing and so we'll manually construct it
*/
}
/*
* Try to offline in RCM first and if that is successful,
* unconfigure the LUN. If offlining in RCM fails, then
* update the failure count
*
* Here we got to check if unusable_flag is set or not.
* If set, then unconfigure only those luns which are in
* node_state DI_DEVICE_OFFLINE or DI_DEVICE_DOWN.
* If not set, unconfigure all luns.
*/
if ((unusable_flag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
flags) != 0) {
(*failure_count)++;
continue;
!= FPCFGA_OK) {
(void) fp_rcm_online(pathname,
(*failure_count)++;
continue;
flags) != 0) {
/*
* Bring everything back online
* in rcm and continue
*/
(void) fp_rcm_online(pathname,
(*failure_count)++;
continue;
}
} else {
continue;
}
} else {
(*failure_count)++;
continue;
(*failure_count)++;
continue;
flags) != 0) {
/*
* Bring everything back online
* in rcm and continue
*/
(*failure_count)++;
continue;
}
}
/* Update the repository only on a successful unconfigure */
(*failure_count)++;
continue;
}
/* Init the string to be removed from repository */
errstring)) {
(*failure_count)++;
continue;
}
}
return (FPCFGA_OK);
}
/*
* INPUT:
* apidt - Pointer to apid_t structure with data filled in
* flags - Flags for special handling
*
* OUTPUT:
* errstring - Applicable only on a failure from plugin
* num_devs - Incremented per lun
* failure_count - Incremented on any failed operation on lun
*
* RETURNS:
* non-FPCFGA_OK on any validation check error. If this value is returned, no
* devices were handled. Consequently num_devs and failure_count
* will not be incremented.
* FPCFGA_OK This return value doesn't mean that all devices were successfully
* unconfigured, you have to check failure_count.
*/
static fpcfga_ret_t
int *num_devs, int *failure_count)
{
/*
* apidt->xport_phys is something like :
* Make sure we copy both the devinfo and pathinfo nodes
*/
/* Now get rid of the ':' at the end */
*ptr = '\0';
return (FPCFGA_INVALID_PATH);
}
DI_NODE_NIL) {
apidt->xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
apidt->xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
/*
* Search all the fp nodes to see if any match the one we are trying
* to unconfigure
*/
/* Skip the "/devices" prefix */
while (fp_node != DI_NODE_NIL) {
/* Found the fp node. 'pathname' has the full path */
break;
}
}
if (fp_node == DI_NODE_NIL) {
apidt->xport_phys, 0);
return (FPCFGA_LIB_ERR);
}
/* No devinfo or pathinfo nodes. Great ! Just return success */
return (FPCFGA_OK);
}
/* First unconfigure any non-MPXIO nodes */
/*
* Now we will traverse any path info nodes that are there
*
* Only MPXIO devices have pathinfo nodes
*/
/*
* We don't want to check the return value of unconf_non_vhci_nodes()
* and unconf_vhci_nodes(). But instead, we are interested only in
* consistently incrementing num_devs and failure_count so that we can
* compare them.
*/
return (FPCFGA_OK);
}
/*
* This function handles configuring/unconfiguring all the devices w.r.t
* the FCA port specified by apidt.
*
* In the unconfigure case, it first unconfigures all the devices that are
* seen through the given port at that moment and then unconfigures all the
* devices that still (somehow) have devinfo nodes on the system for that FCA
* port.
*
* INPUT:
* cmd - CFGA_CMD_CONFIGURE or CFGA_CMD_UNCONFIGURE
* apidt - Pointer to apid_t structure with data filled in
* flags - Flags for special handling
*
* OUTPUT:
* errstring - Applicable only on a failure from plugin
*
* RETURNS:
* FPCFGA_OK on success
* non-FPCFGA_OK otherwise
*/
static fpcfga_ret_t
{
char *my_apid;
int discIndex;
return (FPCFGA_LIB_ERR);
}
discIndex++) {
discIndex, &discPortAttrs)) {
/* Move on to the next target */
continue;
}
/*
* Construct a fake apid string similar to the one the
* plugin gets from the framework and have apidt_create()
* fill in the apid_t structure.
*/
continue;
}
}
}
/*
* We have now handled all the devices that are currently visible
* through the given FCA port. But, it is possible that there are
* some devinfo nodes hanging around. For the unconfigure operation,
* this has to be looked into too.
*/
if (cmd == CFGA_CMD_UNCONFIGURE) {
/* dev_cs_failed will be updated to indicate any failures */
&num_devs, &dev_cs_failed);
}
if (dev_cs_failed == 0)
return (FPCFGA_OK);
/*
* For the discovered ports, num_devs is counted on target
* basis, but for invisible targets, num_devs is counted on
* lun basis.
*
* But if dev_cs_failed and num_devs are incremented
* consistently, comparation of these two counters is still
* meaningful.
*/
if (dev_cs_failed == num_devs) {
/* Failed on all devices seen through this FCA port */
((cmd == CFGA_CMD_CONFIGURE) ?
ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
return (FPCFGA_LIB_ERR);
} else {
/* Failed only on some of the devices */
return (FPCFGA_LIB_ERR);
}
} else {
if (dev_cs_failed == num_devs) {
/* Failed on all devices seen through this FCA port */
((cmd == CFGA_CMD_CONFIGURE) ?
ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
return (FPCFGA_LIB_ERR);
} else {
/* Failed only on some of the devices */
return (FPCFGA_LIB_ERR);
}
}
/*
* Should never get here
*/
}
{
int portIndex;
return (ret);
}
/*
*/
switch (state_change_cmd) {
case CFGA_CMD_CONFIGURE:
return (FPCFGA_OK);
}
break;
case CFGA_CMD_UNCONFIGURE:
return (FPCFGA_OPNOTSUPP);
}
break;
default:
return (FPCFGA_LIB_ERR);
}
return (ret);
}