fcp.c revision 6f3a36cd4abb5c5058716ccff5c673379a4b802b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Fibre Channel SCSI ULP Mapping driver
*/
#include <sys/ndi_impldefs.h>
#include <sys/byteorder.h>
/*
* Discovery Process
* =================
*
* The discovery process is a major function of FCP. In order to help
* understand that function a flow diagram is given here. This diagram
* doesn't claim to cover all the cases and the events that can occur during
* the discovery process nor the subtleties of the code. The code paths shown
* are simplified. Its purpose is to help the reader (and potentially bug
* fixer) have an overall view of the logic of the code. For that reason the
* diagram covers the simple case of the line coming up cleanly or of a new
* port attaching to FCP the link being up. The reader must keep in mind
* that:
*
* - There are special cases where bringing devices online and offline
* is driven by Ioctl.
*
* - The behavior of the discovery process can be modified through the
* .conf file.
*
* - The line can go down and come back up at any time during the
* discovery process which explains some of the complexity of the code.
*
* ............................................................................
*
* STEP 1: The line comes up or a new Fibre Channel port attaches to FCP.
*
*
* +-------------------------+
* +-------------------------+
* | |
* | |
* | v
* | +-------------------------+
* | | fcp_handle_port_attach |
* | +-------------------------+
* | |
* | |
* +--------------------+ |
* | |
* v v
* +-------------------------+
* | fcp_statec_callback |
* +-------------------------+
* |
* |
* v
* +-------------------------+
* | fcp_handle_devices |
* +-------------------------+
* |
* |
* v
* +-------------------------+
* | fcp_handle_mapflags |
* +-------------------------+
* |
* |
* v
* +-------------------------+
* | fcp_send_els |
* | |
* | PLOGI or PRLI To all the|
* | reachable devices. |
* +-------------------------+
*
*
* ............................................................................
*
* STEP 1 are called (it is actually the same function).
*
*
* +-------------------------+
* | fcp_icmd_callback |
* | callback for PLOGI and |
* | PRLI. |
* +-------------------------+
* |
* |
* Received PLOGI Accept /-\ Received PRLI Accept
* _ _ _ _ _ _ / \_ _ _ _ _ _
* | \ / |
* | \-/ |
* | |
* v v
* +-------------------------+ +-------------------------+
* | fcp_send_els | | fcp_send_scsi |
* | | | |
* | PRLI | | REPORT_LUN |
* +-------------------------+ +-------------------------+
*
* ............................................................................
*
* STEP 3: The callback functions of the SCSI commands issued by FCP are called
* (It is actually the same function).
*
*
* +-------------------------+
* +-------------------------+
* |
* |
* |
* Receive REPORT_LUN reply /-\ Receive INQUIRY PAGE83 reply
* _ _ _ _ _ _ _ _ _ _ / \_ _ _ _ _ _ _ _ _ _ _ _
* | \ / |
* | \-/ |
* | | |
* | Receive INQUIRY reply| |
* | | |
* v v v
* +------------------------+ +----------------------+ +----------------------+
* | fcp_handle_reportlun | | fcp_handle_inquiry | | fcp_handle_page83 |
* |(Called for each Target)| | (Called for each LUN)| |(Called for each LUN) |
* +------------------------+ +----------------------+ +----------------------+
* | | |
* | | |
* | | |
* v v |
* +-----------------+ +-----------------+ |
* | fcp_send_scsi | | fcp_send_scsi | |
* | | | | |
* | INQUIRY | | INQUIRY PAGE83 | |
* | (To each LUN) | +-----------------+ |
* +-----------------+ |
* |
* v
* +------------------------+
* | fcp_call_finish_init |
* +------------------------+
* |
* v
* +-----------------------------+
* | fcp_call_finish_init_held |
* +-----------------------------+
* |
* |
* All LUNs scanned /-\
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ __ / \
* | \ /
* | \-/
* v |
* +------------------+ |
* | fcp_finish_tgt | |
* +------------------+ |
* | Target Not Offline and |
* Target Not Offline and | not marked and tgt_node_state |
* marked /-\ not FCP_TGT_NODE_ON_DEMAND |
* _ _ _ _ _ _ / \_ _ _ _ _ _ _ _ |
* | \ / | |
* | \-/ | |
* v v |
* +----------------------------+ +-------------------+ |
* | fcp_offline_target | | fcp_create_luns | |
* | | +-------------------+ |
* | A structure fcp_tgt_elem | | |
* | is created and queued in | v |
* | the FCP port list | +-------------------+ |
* | port_offline_tgts. It | | fcp_pass_to_hp | |
* | will be unqueued by the | | | |
* | watchdog timer. | | Called for each | |
* +----------------------------+ | LUN. Dispatches | |
* | | fcp_hp_task | |
* | +-------------------+ |
* | | |
* | | |
* | | |
* | +---------------->|
* | |
* +---------------------------------------------->|
* |
* |
* All the targets (devices) have been scanned /-\
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ / \
* | \ /
* | \-/
* +-------------------------------------+ |
* | fcp_finish_init | |
* | | |
* | Signal broadcasts the condition | |
* | variable port_config_cv of the FCP | |
* | port. One potential code sequence | |
* | waiting on the condition variable | |
* | the code sequence handling | |
* | BUS_CONFIG_ALL and BUS_CONFIG_DRIVER| |
* | The other is in the function | |
* | fcp_reconfig_wait which is called | |
* | in the transmit path preventing IOs | |
* | from going through till the disco- | |
* | very process is over. | |
* +-------------------------------------+ |
* | |
* | |
* +--------------------------------->|
* |
* v
* Return
*
* ............................................................................
*
* STEP 4: The hot plug task is called (for each fcp_hp_elem).
*
*
* +-------------------------+
* | fcp_hp_task |
* +-------------------------+
* |
* |
* v
* +-------------------------+
* | fcp_trigger_lun |
* +-------------------------+
* |
* |
* v
* Bring offline /-\ Bring online
* _ _ _ _ _ _ _ _ _/ \_ _ _ _ _ _ _ _ _ _
* | \ / |
* | \-/ |
* v v
* +---------------------+ +-----------------------+
* | fcp_offline_child | | fcp_get_cip |
* +---------------------+ | |
* | Creates a dev_info_t |
* | or a mdi_pathinfo_t |
* | depending on whether |
* | mpxio is on or off. |
* +-----------------------+
* |
* |
* v
* +-----------------------+
* | fcp_online_child |
* | |
* | Set device online |
* | using NDI or MDI. |
* +-----------------------+
*
* ............................................................................
*
* STEP 5: The watchdog timer expires. The watch dog timer does much more that
* what is described here. We only show the target offline path.
*
*
* +--------------------------+
* | fcp_watch |
* +--------------------------+
* |
* |
* v
* +--------------------------+
* | fcp_scan_offline_tgts |
* +--------------------------+
* |
* |
* v
* +--------------------------+
* | fcp_offline_target_now |
* +--------------------------+
* |
* |
* v
* +--------------------------+
* | fcp_offline_tgt_luns |
* +--------------------------+
* |
* |
* v
* +--------------------------+
* | fcp_offline_lun |
* +--------------------------+
* |
* |
* v
* +----------------------------------+
* | fcp_offline_lun_now |
* | |
* | A request (or two if mpxio) is |
* | sent to the hot plug task using |
* | a fcp_hp_elem structure. |
* +----------------------------------+
*/
/*
* Functions registered with DDI framework
*/
/*
* Functions registered with FC Transport framework
*/
/*
* Functions registered with SCSA framework
*/
int whom);
/*
* Internal functions
*/
int *fc_pkt_reason, int *fc_pkt_action);
int cause);
int tgt_cnt);
int internal);
int instance);
int);
int flags);
int sleep);
int tgt_cnt);
child_info_t *cip);
static void fcp_reconfigure_luns(void * tgt_handle);
static int fcp_copyin_fcp_ioctl_data(struct fcp_ioctl *, int, int *,
int *rval);
int *rval);
/*
* New functions added for mpxio support
*/
int tcount);
dev_info_t *pdip);
int what);
fc_packet_t *fpkt);
/*
* New functions added for lun masking support
*/
struct fcp_black_list_entry **pplun_blacklist);
struct fcp_black_list_entry **pplun_blacklist);
struct fcp_black_list_entry **pplun_blacklist);
extern struct mod_ops mod_driverops;
/*
* This variable is defined in modctl.c and set to '1' after the root driver
* and fs are loaded. It serves as an indication that the root filesystem can
* be used.
*/
extern int modrootloaded;
/*
* This table contains strings associated with the SCSI sense key codes. It
* is used by FCP to print a clear explanation of the code returned in the
* sense information by a device.
*/
extern char *sense_keys[];
/*
* This device is created by the SCSI pseudo nexus driver (SCSI vHCI). It is
* under this device that the paths to a physical device are created when
* MPxIO is used.
*/
extern dev_info_t *scsi_vhci_dip;
/*
* Report lun processing
*/
#define FCP_LUN_ADDRESSING 0x80
#define FCP_PD_ADDRESSING 0x00
#define FCP_VOLUME_ADDRESSING 0x40
#define MAX_INT_DMA 0x7fffffff
#define FCP_MAX_SENSE_LEN 252
#define FCP_MAX_RESPONSE_LEN 0xffffff
/*
* Property definitions
*/
#define NODE_WWN_PROP (char *)fcp_node_wwn_prop
#define PORT_WWN_PROP (char *)fcp_port_wwn_prop
#define TARGET_PROP (char *)fcp_target_prop
#define LUN_PROP (char *)fcp_lun_prop
#define SAM_LUN_PROP (char *)fcp_sam_lun_prop
#define CONF_WWN_PROP (char *)fcp_conf_wwn_prop
#define OBP_BOOT_WWN (char *)fcp_obp_boot_wwn
#define MANUAL_CFG_ONLY (char *)fcp_manual_config_only
#define INIT_PORT_PROP (char *)fcp_init_port_prop
#define TGT_PORT_PROP (char *)fcp_tgt_port_prop
#define LUN_BLACKLIST_PROP (char *)fcp_lun_blacklist_prop
/*
* Short hand macros.
*/
/*
* Driver private macros
*/
((x) >= 'a' && (x) <= 'f') ?\
((x) - 'a' + 10) : ((x) - 'A' + 10))
#define FCP_MAX(a, b) ((a) > (b) ? (a) : (b))
#define FCP_N_NDI_EVENTS \
(sizeof (fcp_ndi_event_defs) / sizeof (ndi_event_definition_t))
#define FCP_LINK_STATE_CHANGED(p, c)\
((p)->port_link_cnt != (c)->ipkt_link_cnt)
#define FCP_TGT_STATE_CHANGED(t, c)\
((t)->tgt_change_cnt != (c)->ipkt_change_cnt)
#define FCP_STATE_CHANGED(p, t, c)\
(FCP_TGT_STATE_CHANGED(t, c))
#define FCP_MUST_RETRY(fpkt)\
#define FCP_SENSE_REPORTLUN_CHANGED(es)\
#define FCP_SENSE_NO_LUN(es)\
#define FCP_VERSION "1.185"
#define FCP_NUM_ELEMENTS(array)\
/*
* Debugging, Error reporting, and tracing
*/
#define FCP_LEVEL_7 0x00040
/*
* Log contents to system messages file
*/
/*
* Log contents to trace buffer
*/
/*
* Log contents to both system messages file and trace buffer
*/
#ifdef DEBUG
#define FCP_DTRACE fc_trace_debug
#else
#define FCP_DTRACE
#endif
#define FCP_TRACE fc_trace_debug
static struct cb_ops fcp_cb_ops = {
fcp_open, /* open */
fcp_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
fcp_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* chpoll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* rev */
nodev, /* aread */
nodev /* awrite */
};
0,
nulldev, /* identify */
nulldev, /* probe */
fcp_attach, /* attach and detach are mandatory */
nodev, /* reset */
&fcp_cb_ops, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
};
char *fcp_version = FCP_NAME_VERSION;
};
static struct modlinkage modlinkage = {
&modldrv,
};
static fc_ulp_modinfo_t fcp_modinfo = {
&fcp_modinfo, /* ulp_handle */
FCTL_ULP_MODREV_4, /* ulp_rev */
FC4_SCSI_FCP, /* ulp_type */
"fcp", /* ulp_name */
FCP_STATEC_MASK, /* ulp_statec_mask */
fcp_port_attach, /* ulp_port_attach */
fcp_port_detach, /* ulp_port_detach */
fcp_port_ioctl, /* ulp_port_ioctl */
fcp_els_callback, /* ulp_els_callback */
fcp_data_callback, /* ulp_data_callback */
fcp_statec_callback /* ulp_statec_callback */
};
#ifdef DEBUG
FCP_LEVEL_2 | FCP_LEVEL_3 |\
FCP_LEVEL_4 | FCP_LEVEL_5 |\
#else
FCP_LEVEL_2 | FCP_LEVEL_3 |\
FCP_LEVEL_4 | FCP_LEVEL_5 |\
#endif
/* FCP global variables */
int fcp_bus_config_debug = 0;
static int fcp_log_size = FCP_LOG_SIZE;
static int fcp_trace = FCP_TRACE_DEFAULT;
/*
* The auto-configuration is set by default. The only way of disabling it is
* through the property MANUAL_CFG_ONLY in the fcp.conf file.
*/
static int fcp_enable_auto_configuration = 1;
static int fcp_max_bus_config_retries = 4;
static int fcp_lun_ready_retry = 300;
/*
* The value assigned to the following variable has changed several times due
* to a problem with the data underruns reporting of some firmware(s). The
* current value of 50 gives a timeout value of 25 seconds for a max number
* of 256 LUNs.
*/
static int fcp_max_target_retries = 50;
/*
* Watchdog variables
* ------------------
*
* fcp_watchdog_init
*
* Indicates if the watchdog timer is running or not. This is actually
* a counter of the number of Fibre Channel ports that attached. When
* the first port attaches the watchdog is started. When the last port
* detaches the watchdog timer is stopped.
*
* fcp_watchdog_time
*
* This is the watchdog clock counter. It is incremented by
* fcp_watchdog_time each time the watchdog timer expires.
*
* fcp_watchdog_timeout
*
* Increment value of the variable fcp_watchdog_time as well as the
* the timeout value of the watchdog timer. The unit is 1 second. It
* is strange that this is not a #define but a variable since the code
* never changes this value. The reason why it can be said that the
* unit is 1 second is because the number of ticks for the watchdog
* timer is determined like this:
*
* fcp_watchdog_tick = fcp_watchdog_timeout *
* drv_usectohz(1000000);
*
* The value 1000000 is hard coded in the code.
*
* fcp_watchdog_tick
*
* Watchdog timer value in ticks.
*/
static int fcp_watchdog_init = 0;
static int fcp_watchdog_time = 0;
static int fcp_watchdog_timeout = 1;
static int fcp_watchdog_tick;
/*
* fcp_offline_delay is a global variable to enable customisation of
* the timeout on link offlines or RSCNs. The default value is set
* to match FCP_OFFLINE_DELAY (20sec), which is 2*RA_TOV_els as
* specified in FCP4 Chapter 11 (see www.t10.org).
*
* The variable fcp_offline_delay is specified in SECONDS.
*
* If we made this a static var then the user would not be able to
* change it. This variable is set in fcp_attach().
*/
unsigned int fcp_offline_delay = FCP_OFFLINE_DELAY;
static kmutex_t fcp_global_mutex;
static kmutex_t fcp_ioctl_mutex;
static timeout_id_t fcp_watchdog_id;
const char *fcp_lun_prop = "lun";
const char *fcp_sam_lun_prop = "sam-lun";
const char *fcp_target_prop = "target";
/*
* NOTE: consumers of "node-wwn" property include stmsboot in ON
* consolidation.
*/
const char *fcp_node_wwn_prop = "node-wwn";
const char *fcp_port_wwn_prop = "port-wwn";
const char *fcp_conf_wwn_prop = "fc-port-wwn";
const char *fcp_obp_boot_wwn = "fc-boot-dev-portwwn";
const char *fcp_manual_config_only = "manual_configuration_only";
const char *fcp_init_port_prop = "initiator-port";
const char *fcp_tgt_port_prop = "target-port";
const char *fcp_lun_blacklist_prop = "pwwn-lun-blacklist";
static ddi_eventcookie_t fcp_insert_eid;
static ddi_eventcookie_t fcp_remove_eid;
static ndi_event_definition_t fcp_ndi_event_defs[] = {
};
/*
* List of valid commands for the scsi_ioctl call
*/
static uint8_t scsi_ioctl_list[] = {
};
/*
* this is used to dummy up a report lun response for cases
* where the target doesn't support it
*/
static uchar_t fcp_dummy_lun[] = {
0x00, /* MSB length (length = no of luns * 8) */
0x00,
0x00,
0x08, /* LSB length */
0x00, /* MSB reserved */
0x00,
0x00,
0x00, /* LSB reserved */
0x00, /* LUN is ZERO at the first level */
0x00,
0x00, /* second level is zero */
0x00,
0x00, /* third level is zero */
0x00,
0x00 /* fourth level is zero */
};
static uchar_t fcp_alpa_to_switch[] = {
0x00, 0x7d, 0x7c, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x77, 0x76, 0x00, 0x00, 0x75, 0x00, 0x74,
0x73, 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x70, 0x6f, 0x6e,
0x00, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x00, 0x00, 0x67,
0x66, 0x65, 0x64, 0x63, 0x62, 0x00, 0x00, 0x61, 0x60, 0x00,
0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d,
0x5c, 0x5b, 0x00, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x00,
0x00, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x00, 0x00, 0x4e,
0x4d, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b,
0x00, 0x4a, 0x49, 0x48, 0x00, 0x47, 0x46, 0x45, 0x44, 0x43,
0x42, 0x00, 0x00, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x00,
0x00, 0x3b, 0x3a, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x37,
0x36, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x31, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x2e, 0x2d, 0x2c,
0x00, 0x00, 0x00, 0x2b, 0x00, 0x2a, 0x29, 0x28, 0x00, 0x27,
0x26, 0x25, 0x24, 0x23, 0x22, 0x00, 0x00, 0x21, 0x20, 0x1f,
0x1e, 0x1d, 0x1c, 0x00, 0x00, 0x1b, 0x1a, 0x00, 0x19, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x17, 0x16, 0x15,
0x00, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x00, 0x00, 0x0e,
0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x00, 0x00, 0x08, 0x07, 0x00,
0x06, 0x00, 0x00, 0x00, 0x05, 0x04, 0x03, 0x00, 0x02, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#if !defined(lint)
#endif /* lint */
/*
* This table is used to determine whether or not it's safe to copy in
* the target node name for a lun. Since all luns behind the same target
* have the same wwnn, only tagets that do not support multiple luns are
* eligible to be enumerated under mpxio if they aren't page83 compliant.
*/
char *fcp_symmetric_disk_table[] = {
"SEAGATE ST",
"IBM DDYFT",
"SUNW SUNWGS", /* Daktari enclosure */
"SUN SENA", /* SES device */
"SUN SESS01" /* VICOM SVE box */
};
sizeof (fcp_symmetric_disk_table)/sizeof (char *);
/*
* The _init(9e) return value should be that of mod_install(9f). Under
* some circumstances, a failure may not be related mod_install(9f) and
* one would then require a return value to indicate the failure. Looking
* at mod_install(9f), it is expected to return 0 for success and non-zero
* for failure. mod_install(9f) for device drivers, further goes down the
* calling chain and ends up in ddi_installdrv(), whose return values are
* DDI_SUCCESS and DDI_FAILURE - There are also other functions in the
* calling chain of mod_install(9f) which return values like EINVAL and
* in some even return -1.
*
* To work around the vagaries of the mod_install() calling chain, return
* either 0 or ENODEV depending on the success or failure of mod_install()
*/
int
_init(void)
{
int rval;
/*
* Allocate soft state and prepare to do ddi_soft_state_zalloc()
* before registering with the transport first.
*/
sizeof (struct fcp_port), FCP_INIT_ITEMS) != 0) {
return (EINVAL);
}
return (ENODEV);
}
(void) fc_ulp_remove(&fcp_modinfo);
}
return (rval);
}
/*
* the system is done with us as a driver, so clean up
*/
int
_fini(void)
{
int rval;
/*
* don't start cleaning up until we know that the module remove
* has worked -- if this works, then we know that each instance
* has successfully been DDI_DETACHed
*/
return (rval);
}
(void) fc_ulp_remove(&fcp_modinfo);
return (rval);
}
int
{
}
/*
* attach the module
*/
static int
{
int rval = DDI_SUCCESS;
if (cmd == DDI_ATTACH) {
/* The FCP pseudo device is created here. */
0, DDI_PSEUDO, 0) == DDI_SUCCESS) {
} else {
rval = DDI_FAILURE;
}
/*
* We check the fcp_offline_delay property at this
* point. This variable is global for the driver,
* not specific to an instance.
*
* We do not recommend setting the value to less
* than 10 seconds (RA_TOV_els), or greater than
* 60 seconds.
*/
"fcp_offline_delay", FCP_OFFLINE_DELAY);
if ((fcp_offline_delay < 10) ||
(fcp_offline_delay > 60)) {
"to %d second(s). This is outside the "
"recommended range of 10..60 seconds.",
}
}
return (rval);
}
/*ARGSUSED*/
static int
{
int res = DDI_SUCCESS;
if (cmd == DDI_DETACH) {
/*
* are any, we will fail, else we will succeed (there
* should not be much to clean up)
*/
(void *) fcp_port_head);
if (fcp_port_head == NULL) {
} else {
res = DDI_FAILURE;
}
}
return (res);
}
/* ARGSUSED */
static int
{
return (EINVAL);
}
/*
* Allow only root to talk;
*/
return (EPERM);
}
return (EBUSY);
}
return (EBUSY);
}
}
return (0);
}
/* ARGSUSED */
static int
{
return (EINVAL);
}
return (ENODEV);
}
return (0);
}
/*
* fcp_ioctl
* Entry point for the FCP ioctls
*
* Input:
* See ioctl(9E)
*
* Output:
* See ioctl(9E)
*
* Returns:
* See ioctl(9E)
*
* Context:
* Kernel context.
*/
/* ARGSUSED */
static int
int *rval)
{
int ret = 0;
return (ENXIO);
}
switch (cmd) {
case FCP_TGT_INQUIRY:
case FCP_TGT_CREATE:
case FCP_TGT_DELETE:
break;
case FCP_TGT_SEND_SCSI:
break;
case FCP_STATE_COUNT:
break;
case FCP_GET_TARGET_MAPPINGS:
break;
default:
"!Invalid ioctl opcode = 0x%x", cmd);
}
return (ret);
}
/*
* fcp_setup_device_data_ioctl
* Setup handler for the "device data" style of
* ioctl for FCP. See "fcp_util.h" for data structure
* definition.
*
* Input:
* cmd = FCP ioctl command
* data = ioctl data
* mode = See ioctl(9E)
*
* Output:
* data = ioctl data
* rval = return value - see ioctl(9E)
*
* Returns:
* See ioctl(9E)
*
* Context:
* Kernel context.
*/
/* ARGSUSED */
static int
int *rval)
{
struct device_data *dev_data;
int i, error;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
/*
* Right now we can assume that the minor number matches with
* this instance of fp. If this changes we will need to
* revisit this logic.
*/
while (pptr) {
break;
else
}
return (ENXIO);
}
return (ENOMEM);
}
return (EFAULT);
}
if (cmd == FCP_TGT_INQUIRY) {
/* This ioctl is requesting INQ info of local HBA */
dev_data[0].dev_status = 0;
mode)) {
return (EFAULT);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
if (ddi_copyout((void *)&f32_ioctl,
(void *)data,
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
return (0);
}
}
return (ENXIO);
}
i++) {
continue;
} else {
continue;
}
} else {
FCP_TGT_BUSY)) {
continue;
}
} else {
}
continue;
}
switch (cmd) {
case FCP_TGT_INQUIRY:
/*
* The reason we give device type of
* lun 0 only even though in some
* cases(like maxstrat) lun 0 device
* type may be 0x3f(invalid) is that
* for bridge boxes target will appear
* as luns and the first lun could be
* a device that utility may not care
* about (like a tape device).
*/
dev_data[i].dev_status = 0;
} else {
}
break;
case FCP_TGT_CREATE:
/*
* serialize state change call backs.
* only one call back will be handled
* at a time.
*/
if (dev_data) {
sizeof (*dev_data) *
}
return (EBUSY);
}
dev_data[i].dev_status =
if (dev_data[i].dev_status != 0) {
char buf[25];
for (i = 0; i < FC_WWN_SIZE; i++) {
"%02x",
}
"!Failed to create nodes for"
" pwwn=%s; error=%x", buf,
dev_data[i].dev_status);
}
/* allow state change call backs again */
break;
case FCP_TGT_DELETE:
break;
default:
"!Invalid device data ioctl "
"opcode = 0x%x", cmd);
}
}
}
return (EFAULT);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
return (0);
}
/*
* Fetch the target mappings (path, etc.) for all LUNs
* on this port.
*/
/* ARGSUSED */
static int
{
int i, mapIndex, mappingSize;
int listlen;
char *path;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
/*
* Right now we can assume that the minor number matches with
* this instance of fp. If this changes we will need to
* revisit this logic.
*/
while (pptr) {
break;
else
}
return (ENXIO);
}
/* We use listlen to show the total buffer size */
/* Now calculate how many mapping entries will fit */
- sizeof (fc_hba_target_mappings_t);
if (listlen <= 0) {
return (ENXIO);
}
return (ENOMEM);
}
/* Now get to work */
mapIndex = 0;
/* Loop through all targets on this port */
for (i = 0; i < FCP_NUM_HASH; i++) {
/* Loop through all LUNs on this target */
continue;
}
continue;
}
mapIndex ++;
continue;
}
/*
* We had swapped lun when we stored it in
* lun_addr. We need to swap it back before
* returning it to user land
*/
sizeof (la_wwn_t));
sizeof (la_wwn_t));
/* convert ascii wwn to bytes */
"guid copy space insufficient."
"Copy Truncation - "
"available %d; need %d",
}
}
}
}
}
return (EFAULT);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
return (0);
}
/*
* fcp_setup_scsi_ioctl
* Setup handler for the "scsi passthru" style of
* ioctl for FCP. See "fcp_util.h" for data structure
* definition.
*
* Input:
* u_fscsi = ioctl data (user address space)
* mode = See ioctl(9E)
*
* Output:
* u_fscsi = ioctl data (user address space)
* rval = return value - see ioctl(9E)
*
* Returns:
* 0 = OK
* EAGAIN = See errno.h
* EBUSY = See errno.h
* EFAULT = See errno.h
* EINTR = See errno.h
* EINVAL = See errno.h
* EIO = See errno.h
* ENOMEM = See errno.h
* ENXIO = See errno.h
*
* Context:
* Kernel context.
*/
/* ARGSUSED */
static int
{
int ret = 0;
int temp_ret;
struct fcp_scsi_cmd k_fscsi;
/*
* Get fcp_scsi_cmd array element from user address space
*/
!= 0) {
return (ret);
}
/*
* Even though kmem_alloc() checks the validity of the
* buffer length, this check is needed when the
* kmem_flags set and the zero buffer length is passed.
*/
if ((k_fscsi.scsi_cdblen <= 0) ||
(k_fscsi.scsi_buflen <= 0) ||
(k_fscsi.scsi_rqlen <= 0) ||
return (EINVAL);
}
/*
* Allocate data for fcp_scsi_cmd pointer fields
*/
if (ret == 0) {
if (k_cdbbufaddr == NULL ||
k_rqbufaddr == NULL) {
}
}
/*
* Get fcp_scsi_cmd pointer fields from user
* address space
*/
if (ret == 0) {
if (ddi_copyin(u_cdbbufaddr,
mode)) {
} else if (ddi_copyin(u_bufaddr,
mode)) {
} else if (ddi_copyin(u_rqbufaddr,
mode)) {
}
}
/*
* Send scsi command (blocking)
*/
if (ret == 0) {
/*
* Prior to sending the scsi command, the
* fcp_scsi_cmd data structure must contain kernel,
* not user, addresses.
*/
/*
* After sending the scsi command, the
* fcp_scsi_cmd data structure must contain user,
* not kernel, addresses.
*/
}
/*
* Put fcp_scsi_cmd pointer fields to user address space
*/
if (ret == 0) {
if (ddi_copyout(k_cdbbufaddr,
mode)) {
} else if (ddi_copyout(k_bufaddr,
mode)) {
} else if (ddi_copyout(k_rqbufaddr,
mode)) {
}
}
/*
* Free data for fcp_scsi_cmd pointer fields
*/
if (k_cdbbufaddr != NULL) {
}
}
if (k_rqbufaddr != NULL) {
}
/*
* Put fcp_scsi_cmd array element to user address space
*/
if (temp_ret != 0) {
}
/*
* Return status
*/
return (ret);
}
/*
* fcp_copyin_scsi_cmd
* Copy in fcp_scsi_cmd data structure from user address space.
* The data may be in 32 bit or 64 bit modes.
*
* Input:
* base_addr = from address (user address space)
* mode = See ioctl(9E) and ddi_copyin(9F)
*
* Output:
* fscsi = to address (kernel address space)
*
* Returns:
* 0 = OK
* EFAULT = Error
*
* Context:
* Kernel context.
*/
static int
{
#ifdef _MULTI_DATAMODEL
struct fcp32_scsi_cmd f32scsi;
case DDI_MODEL_ILP32:
/*
* Copy data from user address space
*/
if (ddi_copyin((void *)base_addr,
&f32scsi,
sizeof (struct fcp32_scsi_cmd),
mode)) {
return (EFAULT);
}
/*
* Convert from 32 bit to 64 bit
*/
break;
case DDI_MODEL_NONE:
/*
* Copy data from user address space
*/
if (ddi_copyin((void *)base_addr,
sizeof (struct fcp_scsi_cmd),
mode)) {
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
/*
* Copy data from user address space
*/
if (ddi_copyin((void *)base_addr,
sizeof (struct fcp_scsi_cmd),
mode)) {
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
return (0);
}
/*
* fcp_copyout_scsi_cmd
* Copy out fcp_scsi_cmd data structure to user address space.
* The data may be in 32 bit or 64 bit modes.
*
* Input:
* fscsi = to address (kernel address space)
* mode = See ioctl(9E) and ddi_copyin(9F)
*
* Output:
* base_addr = from address (user address space)
*
* Returns:
* 0 = OK
* EFAULT = Error
*
* Context:
* Kernel context.
*/
static int
{
#ifdef _MULTI_DATAMODEL
struct fcp32_scsi_cmd f32scsi;
case DDI_MODEL_ILP32:
/*
* Convert from 64 bit to 32 bit
*/
/*
* Copy data to user address space
*/
if (ddi_copyout(&f32scsi,
(void *)base_addr,
sizeof (struct fcp32_scsi_cmd),
mode)) {
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
/*
* Copy data to user address space
*/
if (ddi_copyout(fscsi,
(void *)base_addr,
sizeof (struct fcp_scsi_cmd),
mode)) {
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
/*
* Copy data to user address space
*/
if (ddi_copyout(fscsi,
(void *)base_addr,
sizeof (struct fcp_scsi_cmd),
mode)) {
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
return (0);
}
/*
* fcp_send_scsi_ioctl
* Sends the SCSI command in blocking mode.
*
* Input:
* fscsi = SCSI command data structure
*
* Output:
* fscsi = SCSI command data structure
*
* Returns:
* 0 = OK
* EAGAIN = See errno.h
* EBUSY = See errno.h
* EINTR = See errno.h
* EINVAL = See errno.h
* EIO = See errno.h
* ENOMEM = See errno.h
* ENXIO = See errno.h
*
* Context:
* Kernel context.
*/
static int
{
int target_created = FALSE;
int nodma;
struct fcp_rsp_info *rsp_info;
int buf_len;
int info_len;
int sense_len;
int rsp_len;
int cmd_index;
int fc_status;
int pkt_state;
int pkt_action;
int pkt_reason;
int lcount;
int tcount;
int reconfig_status;
/*
* Check valid SCSI command
*/
for (cmd_index = 0;
ret != 0;
cmd_index++) {
/*
* First byte of CDB is the SCSI command
*/
ret = 0;
}
}
/*
* Check inputs
*/
}
/*
* Find FC port
*/
if (ret == 0) {
/*
* Acquire global mutex
*/
while (pptr) {
if (pptr->port_instance ==
break;
else
}
} else {
/*
* fc_ulp_busy_port can raise power
* so, we must not hold any mutexes involved in PM
*/
}
if (ret == 0) {
/* remember port is busy, so we will release later */
/*
* If there is a reconfiguration in progress, wait
* for it to complete.
*/
/* reacquire mutexes in order */
/*
* Will port accept DMA?
*/
? 1 : 0;
/*
* If init or offline, device not known
*
* If we are discovering (onlining), we can
* NOT obviously provide reliable data about
* devices until it is complete
*/
} else {
/*
* Find target from pwwn
*
* The wwn must be put into a local
* variable to ensure alignment.
*/
/*
* If target not found,
*/
/*
* Note: Still have global &
* port mutexes
*/
&pkt_state, &pkt_action,
&pkt_reason);
} else if (ret == 0) {
}
}
if (ret == 0) {
/*
* Acquire target
*/
/*
* If target is mark or busy,
* then target can not be used
*/
(FCP_TGT_MARK |
FCP_TGT_BUSY)) {
} else {
/*
* Mark target as busy
*/
}
/*
* Release target
*/
}
}
/*
* Release port
*/
}
/*
* Release global mutex
*/
}
if (ret == 0) {
/*
* If it's a target device, find lun from pwwn
* The wwn must be put into a local
* variable to ensure alignment.
*/
/* this is not a target */
} else if ((belun << 16) != 0) {
/*
* Since fcp only support PD and LU addressing method
* so far, the last 6 bytes of a valid LUN are expected
* to be filled with 00h.
*/
" method 0x%02x with LUN number 0x%016" PRIx64,
/*
* This is a SCSI target, but no LUN at this
* address.
*
* In the future, we may want to send this to
* the target, and let it respond
* appropriately
*/
}
}
/*
* Finished grabbing external resources
* Allocate internal packet (icmd)
*/
if (ret == 0) {
/*
* Calc rsp len assuming rsp info included
*/
sizeof (struct fcp_cmd),
lcount, /* ipkt_link_cnt */
tcount, /* ipkt_change_cnt */
0, /* cause */
FC_INVALID_RSCN_COUNT); /* invalidate the count */
} else {
/*
* Setup internal packet as sema sync
*/
}
}
if (ret == 0) {
/*
* Init fpkt pointer for use.
*/
/*
* Init fcmd pointer for use by SCSI command
*/
if (nodma) {
} else {
}
/*
* Setup internal packet(icmd)
*/
icmd->ipkt_restart = 0;
icmd->ipkt_retries = 0;
icmd->ipkt_opcode = 0;
/*
* Init the frame HEADER Pointer for use
*/
fscsi->scsi_cdblen);
if (!nodma) {
}
/*
* Send SCSI command to FC transport
*/
if (ret == 0) {
fpkt);
}
} else {
}
}
}
/*
* Wait for completion only if fc_ulp_transport was called and it
* returned a success. This is the only time callback will happen.
* Otherwise, there is no point in waiting
*/
}
/*
* Copy data to IOCTL data structures
*/
"!SCSI command to d_id=0x%x lun=0x%x"
" failed, Bad FCP response values:"
" rsvd1=%x, rsvd2=%x, sts-rsvd1=%x,"
" sts-rsvd2=%x, rsplen=%x, senselen=%x",
}
}
/*
* Calc response lengths
*/
sense_len = 0;
info_len = 0;
}
rsp_info = (struct fcp_rsp_info *)
/*
* Get SCSI status
*/
/*
* If a lun was just added or removed and the next command
* comes through this interface, we need to capture the check
* condition so we can discover the new topology.
*/
if ((FCP_SENSE_REPORTLUN_CHANGED(sense_to)) ||
(FCP_SENSE_NO_LUN(sense_to))) {
reconfig_lun = TRUE;
}
}
if (reconfig_lun == FALSE) {
}
if ((reconfig_lun == TRUE) ||
(reconfig_status == TRUE)) {
/*
* Either we've been notified the
* REPORT_LUN data has changed, or
* we've determined on our own that
* we're out of date. Kick off
* rediscovery.
*/
}
}
}
/*
* Calc residuals and buffer lengths
*/
if (ret == 0) {
fscsi->scsi_bufresid = 0;
} else {
fscsi->scsi_buflen);
}
}
}
}
fscsi->scsi_fc_rspcode = 0;
}
/*
* Copy data and request sense
*
* Data must be copied by using the FCP_CP_IN macro.
* This will ensure the proper byte order since the data
* is being copied directly from the memory mapped
* device register.
*
* The response (and request sense) will be in the
* correct byte order. No special copy is necessary.
*/
if (buf_len) {
buf_len);
}
(void *)fscsi->scsi_rqbufaddr,
}
}
/*
* Cleanup transport data structures if icmd was alloc-ed
* So, cleanup happens in the same thread that icmd was alloc-ed
*/
}
if (port_busy) {
}
/*
* Cleanup target. if a reconfig is pending, don't clear the BUSY
* flag, it'll be cleared when the reconfig is complete.
*/
/*
* If target was created,
*/
if (target_created) {
} else {
/*
* De-mark target as busy
*/
}
}
return (ret);
}
static int
{
int num_luns;
int actual_luns;
int num_masked_luns;
int lun_buflen;
struct fcp_reportlun_resp *report_lun;
fpkt->pkt_datalen);
/* get number of luns (which is supplied as LUNS * 8) */
/*
* Figure out exactly how many lun strings our response buffer
* can hold.
*/
/*
* Is our response buffer full or not? We don't want to
* potentially walk beyond the number of luns we have.
*/
if (num_luns <= lun_buflen) {
} else {
}
/* Scan each lun to see if we have masked it. */
num_masked_luns = 0;
if (fcp_lun_blacklist != NULL) {
for (i = 0; i < num_luns; i++) {
switch (lun_string[0] & 0xC0) {
case FCP_LUN_ADDRESSING:
case FCP_PD_ADDRESSING:
| lun_string[1];
}
break;
default:
break;
}
}
}
/*
* The quick and easy check. If the number of LUNs reported
* doesn't match the number we currently know about, we need
* to reconfigure.
*/
return (TRUE);
}
/*
* If the quick and easy check doesn't turn up anything, we walk
* the list of luns from the REPORT_LUN response and look for
* any luns we don't know about. If we find one, we know we need
* to reconfigure. We will skip LUNs that are masked because of the
* blacklist.
*/
for (i = 0; i < actual_luns; i++) {
lun_exists = FALSE;
switch (lun_string[0] & 0xC0) {
case FCP_LUN_ADDRESSING:
case FCP_PD_ADDRESSING:
lun_exists = TRUE;
break;
}
lun_exists = TRUE;
break;
}
}
break;
default:
break;
}
if (lun_exists == FALSE) {
break;
}
}
return (reconfig_needed);
}
/*
* This function is called by fcp_handle_page83 and uses inquiry response data
* stored in plun->lun_inq to determine whether or not a device is a member of
* the table fcp_symmetric_disk_table_size. We return 0 if it is in the table,
* otherwise 1.
*/
static int
{
char *devidptr;
int i, len;
for (i = 0; i < fcp_symmetric_disk_table_size; i++) {
return (0);
}
}
return (1);
}
/*
* This function is called by fcp_ioctl for the FCP_STATE_COUNT ioctl
* It basically returns the current count of # of state change callbacks
* i.e the value of tgt_change_cnt.
*
* INPUT:
* fcp_ioctl.fp_minor -> The minor # of the fp port
* fcp_ioctl.listlen -> 1
* fcp_ioctl.list -> Pointer to a 32 bit integer
*/
/*ARGSUSED2*/
static int
{
int ret;
&pptr)) != 0) {
return (ret);
}
return (EINVAL);
return (ENXIO);
}
/*
* FCP_STATE_INIT is set in 2 cases (not sure why it is overloaded):
* When the fcp initially attaches to the port and there are nothing
* hanging out of the port or if there was a repeat offline state change
* callback (refer fcp_statec_callback() FC_STATE_OFFLINE case).
* In the latter case, port_tmp_cnt will be non-zero and that is how we
* will differentiate the 2 cases.
*/
return (ENXIO);
}
return (EFAULT);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
return (0);
}
/*
* This function copies the fcp_ioctl structure passed in from user land
* into kernel land. Handles 32 bit applications.
*/
/*ARGSUSED*/
static int
{
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
struct fcp32_ioctl f32_ioctl;
sizeof (struct fcp32_ioctl), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
/*
* Right now we can assume that the minor number matches with
* this instance of fp. If this changes we will need to
* revisit this logic.
*/
while (t_pptr) {
break;
else
}
return (ENXIO);
return (0);
}
/*
* Function: fcp_port_create_tgt
*
* Description: As the name suggest this function creates the target context
* specified by the the WWN provided by the caller. If the
* followed by a PRLI are issued.
*
* Argument: pptr fcp port structure
* pwwn WWN of the target
* ret_val Address of the return code. It could be:
* EIO, ENOMEM or 0.
* fc_status PLOGI or PRLI status completion
* fc_pkt_state PLOGI or PRLI state completion
* fc_pkt_reason PLOGI or PRLI reason completion
* fc_pkt_action PLOGI or PRLI action completion
*
* Return Value: NULL if it failed
* Target structure address if it succeeds
*/
static struct fcp_tgt *
{
int lcount;
int error;
*ret_val = 0;
/*
* Check FC port device & get port map
*/
} else {
&devlist) != FC_SUCCESS) {
}
}
/* Set port map flags */
/* Allocate target */
if (*ret_val == 0) {
"!FC target allocation failed");
} else {
/* Setup target */
}
}
/* Release global mutex for PLOGI and PRLI */
/* Send PLOGI (If necessary) */
if (*ret_val == 0) {
}
/* Send PRLI (If necessary) */
if (*ret_val == 0) {
}
return (ptgt);
}
/*
* Function: fcp_tgt_send_plogi
*
* Description: This function sends a PLOGI to the target specified by the
* caller and waits till it completes.
*
* Argument: ptgt Target to send the plogi to.
*
* Return Value: 0
* ENOMEM
* EIO
*
* Context: User context.
*/
static int
int *fc_pkt_reason, int *fc_pkt_action)
{
struct la_els_logi logi;
int tcount;
int lcount;
ret = 0;
/* Alloc internal packet */
} else {
/*
* Setup internal packet as sema sync
*/
/*
* Setup internal packet (icmd)
*/
icmd->ipkt_restart = 0;
icmd->ipkt_retries = 0;
/*
* Setup fc_packet
*/
/*
* Setup FC frame header
*/
/*
* Setup PLOGI
*/
/*
* Send PLOGI
*/
*fc_status = login_retval =
if (*fc_status != FC_SUCCESS) {
}
}
/*
* Wait for completion
*/
}
/*
* Cleanup transport data structures if icmd was alloc-ed AND if there
* is going to be no callback (i.e if fc_ulp_login() failed).
* Otherwise, cleanup happens in callback routine.
*/
}
return (ret);
}
/*
* Function: fcp_tgt_send_prli
*
* Description: Does nothing as of today.
*
* Argument: ptgt Target to send the prli to.
*
* Return Value: 0
*/
/*ARGSUSED*/
static int
int *fc_pkt_reason, int *fc_pkt_action)
{
return (0);
}
/*
* Function: fcp_ipkt_sema_init
*
* Description: Initializes the semaphore contained in the internal packet.
*
* Argument: icmd Internal packet the semaphore of which must be
* initialized.
*
* Return Value: None
*
* Context: User context only.
*/
static void
{
/* Create semaphore for sync */
/* Setup the completion callback */
}
/*
* Function: fcp_ipkt_sema_wait
*
* Description: Wait on the semaphore embedded in the internal packet. The
* semaphore is released in the callback.
*
* Argument: icmd Internal packet to wait on for completion.
*
* Return Value: 0
* EIO
* EBUSY
* EAGAIN
*
* Context: User context only.
*
* This function does a conversion between the field pkt_state of the fc_packet
* embedded in the internal packet (icmd) and the code it returns.
*/
static int
{
int ret;
/*
* Wait on semaphore
*/
/*
* Check the status of the FC packet
*/
case FC_PKT_SUCCESS:
ret = 0;
break;
case FC_PKT_LOCAL_RJT:
switch (fpkt->pkt_reason) {
case FC_REASON_SEQ_TIMEOUT:
case FC_REASON_RX_BUF_TIMEOUT:
break;
case FC_REASON_PKT_BUSY:
break;
}
break;
case FC_PKT_TIMEOUT:
break;
case FC_PKT_LOCAL_BSY:
case FC_PKT_TRAN_BSY:
case FC_PKT_NPORT_BSY:
case FC_PKT_FABRIC_BSY:
break;
case FC_PKT_LS_RJT:
case FC_PKT_BA_RJT:
switch (fpkt->pkt_reason) {
case FC_REASON_LOGICAL_BSY:
break;
}
break;
case FC_PKT_FS_RJT:
switch (fpkt->pkt_reason) {
break;
}
break;
}
return (ret);
}
/*
* Function: fcp_ipkt_sema_callback
*
* Description: Registered as the completion callback function for the FC
* transport when the ipkt semaphore is used for sync. This will
* cleanup the used data structures, if necessary and wake up
* the user thread to complete the transaction.
*
* Argument: fpkt FC packet (points to the icmd)
*
* Return Value: None
*
* Context: User context only
*/
static void
{
/*
* Wake up user thread
*/
}
/*
* Function: fcp_ipkt_sema_cleanup
*
* Description: Called to cleanup (if necessary) the data structures used
* when ipkt sema is used for sync. This function will detect
* whether the caller is the last thread (via counter) and
* cleanup only if necessary.
*
* Argument: icmd Internal command packet
*
* Return Value: None
*
* Context: User context only
*/
static void
{
/*
* Acquire data structure
*/
/*
* Destroy semaphore
*/
/*
* Cleanup internal packet
*/
}
/*
* Function: fcp_port_attach
*
* Description: Called by the transport framework to resume, suspend or
* attach a new port.
*
* Argument: ulph Port handle
* *pinfo Port information
* cmd Command
* s_id Port ID
*
* Return Value: FC_FAILURE or FC_SUCCESS
*/
/*ARGSUSED*/
static int
{
int instance;
switch (cmd) {
case FC_CMD_ATTACH:
/*
* this port instance attaching for the first time (or after
* being detached before)
*/
instance) == DDI_SUCCESS) {
res = FC_SUCCESS;
} else {
}
break;
case FC_CMD_RESUME:
case FC_CMD_POWER_UP:
/*
* this port instance was attached and the suspended and
* will now be resumed
*/
instance) == DDI_SUCCESS) {
res = FC_SUCCESS;
}
break;
default:
/* shouldn't happen */
fcp_trace, FCP_BUF_LEVEL_2, 0,
"port_attach: unknown cmdcommand: %d", cmd);
break;
}
/* return result */
return (res);
}
/*
* detach or suspend this port instance
*
* acquires and releases the global mutex
*
* acquires and releases the mutex for this port
*
* acquires and releases the hotplug mutex for this port
*/
/*ARGSUSED*/
static int
{
int flag;
int instance;
switch (cmd) {
case FC_CMD_SUSPEND:
fcp_trace, FCP_BUF_LEVEL_8, 0,
"port suspend called for port %d", instance);
break;
case FC_CMD_POWER_DOWN:
fcp_trace, FCP_BUF_LEVEL_8, 0,
"port power down called for port %d", instance);
break;
case FC_CMD_DETACH:
fcp_trace, FCP_BUF_LEVEL_8, 0,
"port detach called for port %d", instance);
break;
default:
/* shouldn't happen */
return (FC_FAILURE);
}
FCP_BUF_LEVEL_1, 0, "fcp_port_detach returning");
}
/*
* called for ioctls on the transport's devctl interface, and the transport
* has passed it to us
*
* this will only be called for device control ioctls (i.e. hotplugging stuff)
*
* return FC_SUCCESS if we decide to claim the ioctl,
* else return FC_UNCLAIMED
*
* *rval is set iff we decide to claim the ioctl
*/
/*ARGSUSED*/
static int
{
char *ndi_nm; /* NDI name */
char *ndi_addr; /* NDI addr */
int devi_entered = 0;
fcp_trace, FCP_BUF_LEVEL_8, 0,
/* if already claimed then forget it */
if (claimed) {
/*
* for now, if this ioctl has already been claimed, then
* we just ignore it
*/
return (retval);
}
/* get our port info */
"!fcp:Invalid port handle handle in ioctl");
return (retval);
}
switch (cmd) {
case DEVCTL_BUS_GETSTATE:
case DEVCTL_BUS_QUIESCE:
case DEVCTL_BUS_UNQUIESCE:
case DEVCTL_BUS_RESET:
case DEVCTL_BUS_RESETALL:
case DEVCTL_BUS_DEV_CREATE:
return (retval);
}
break;
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_REMOVE:
case DEVCTL_DEVICE_RESET:
return (retval);
}
/* ensure we have a name and address */
fcp_trace, FCP_BUF_LEVEL_2, 0,
"ioctl: can't get name (%s) or addr (%s)",
return (retval);
}
/* get our child's DIP */
if (is_mpxio)
else
devi_entered = 1;
/* Look for virtually enumerated devices. */
goto out;
}
}
break;
default:
return (retval);
}
/* this ioctl is ours -- process it */
/* we assume it will be a success; else we'll set error value */
*rval = 0;
fcp_trace, FCP_BUF_LEVEL_8, 0,
"ioctl: claiming this one");
/* handle ioctls now */
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
}
break;
case DEVCTL_DEVICE_REMOVE:
case DEVCTL_DEVICE_OFFLINE: {
int flag = 0;
int lcount;
int tcount;
int all = 1;
}
break;
}
}
if (cmd == DEVCTL_DEVICE_REMOVE) {
}
if (is_mpxio)
else
devi_entered = 0;
if (*rval != NDI_SUCCESS) {
break;
}
all = 0;
}
}
if (all) {
/*
* The user is unconfiguring/offlining the device.
* If fabric and the auto configuration is set
* then make sure the user is the only one who
* can reconfigure the device.
*/
}
}
break;
}
case DEVCTL_DEVICE_ONLINE: {
int lcount;
int tcount;
}
break;
}
/*
* The FCP_LUN_ONLINING flag is used in fcp_scsi_start()
* to allow the device attach to occur when the device is
* FCP_LUN_OFFLINE (so we don't reject the INQUIRY command
* from the scsi_probe()).
*/
if (is_mpxio)
else
devi_entered = 0;
if (*rval != NDI_SUCCESS) {
/* Reset the FCP_LUN_ONLINING bit */
break;
}
break;
}
case DEVCTL_BUS_DEV_CREATE: {
break;
}
(void) ndi_devi_free(useless_dip);
}
break;
}
if (*rval == 0) {
if (ptgt) {
/*
* We now have a pointer to the target that
* was created. Lets point to the first LUN on
* this new target.
*/
/*
* this list (this is by design) and so we have
* to make sure we point to the first online
* LUN
*/
while (plun &&
}
}
}
/*
* Allow up to fcp_lun_ready_retry seconds to
* configure all the luns behind the target.
*
* The intent here is to allow targets with long
* reboot/reset-recovery times to become available
* while limiting the maximum wait time for an
* unresponsive target.
*/
end_time = ddi_get_lbolt() +
while (ddi_get_lbolt() < end_time) {
retval = FC_SUCCESS;
/*
* The new ndi interfaces for on-demand creation
* are inflexible, Do some more work to pass on
* a path name of some LUN (design is broken !)
*/
} else {
}
break;
}
if (!i_ddi_devi_attached(cdip)) {
} else {
/*
* This Lun is ready, lets
* check the next one.
*/
& FCP_LUN_OFFLINE)) {
}
if (!plun) {
break;
}
}
} else {
/*
* lun_cip field for a valid lun
* should never be NULL. Fail the
* command.
*/
break;
}
}
if (plun) {
} else {
char devnm[MAXNAMELEN];
int nmlen;
0) {
}
}
} else {
int i;
char buf[25];
for (i = 0; i < FC_WWN_SIZE; i++) {
}
"!Failed to create nodes for pwwn=%s; error=%x",
}
(void) ndi_devi_free(useless_dip);
break;
}
case DEVCTL_DEVICE_RESET: {
struct scsi_address ap;
}
break;
}
break;
}
/*
* There is a chance lun_tran is NULL at this point. So check
* for it. If it is NULL, it basically means that the tgt has
* been freed. So, just return a "No such device or address"
* error.
*/
break;
}
/*
* set up ap so that fcp_reset can figure out
* which target to reset
*/
}
break;
}
case DEVCTL_BUS_GETSTATE:
NDI_SUCCESS) {
}
break;
case DEVCTL_BUS_QUIESCE:
case DEVCTL_BUS_UNQUIESCE:
break;
case DEVCTL_BUS_RESET:
case DEVCTL_BUS_RESETALL:
break;
default:
break;
}
/* all done -- clean up and return */
out: if (devi_entered) {
if (is_mpxio)
else
}
}
return (retval);
}
/*ARGSUSED*/
static int
{
return (FC_UNCLAIMED);
}
return (FC_UNCLAIMED);
}
switch (r_ctl & R_CTL_ROUTING) {
case R_CTL_EXTENDED_SVC:
if (r_ctl == R_CTL_ELS_REQ) {
switch (ls_code) {
case LA_ELS_PRLI:
/*
* We really don't care if something fails.
* If the PRLI was not sent out, then the
* other end will time it out.
*/
return (FC_SUCCESS);
}
return (FC_UNCLAIMED);
/* NOTREACHED */
default:
break;
}
}
/* FALLTHROUGH */
default:
return (FC_UNCLAIMED);
}
}
/*ARGSUSED*/
static int
{
return (FC_UNCLAIMED);
}
/*
* Function: fcp_statec_callback
*
* Description: The purpose of this function is to handle a port state change.
*
* port_handle fcp_port structure
* port_state Physical state of the port
* port_top Topology
* *devlist Pointer to the first entry of a table
* containing the remote ports that can be
* reached.
* dev_cnt Number of entries pointed by devlist.
* port_sid Port ID of the local port.
*
* Return Value: None
*/
/*ARGSUSED*/
static void
{
int map_len = 0;
return; /* nothing to work with! */
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
/*
* If a thread is in detach, don't do anything.
*/
return;
}
/*
* First thing we do is set the FCP_STATE_IN_CB_DEVC flag so that if
* init_pkt is called, it knows whether or not the target's status
* (or pd) might be changing.
*/
}
/*
* the transport doesn't allocate or probe unless being
* asked to by either the applications or ULPs
*
* in cases where the port is OFFLINE at the time of port
* attach callback and the link comes ONLINE later, for
* easier automatic node creation (i.e. without you having to
* go out and run the utility to perform LOGINs) the
* following conditional is helpful
*/
if (dev_cnt) {
"!fcp%d: failed to allocate for map tags; "
" state change will not be processed",
return;
}
}
fcp_trace, FCP_BUF_LEVEL_3, 0,
port_sid);
/*
* The local port changed ID. It is the first time a port ID
* is assigned or something drastic happened. We might have
* been unplugged and replugged on another loop or fabric port
* or somebody grabbed the AL_PA we had or somebody rezoned
* the fabric we were plugged into.
*/
}
switch (FC_PORT_STATE_MASK(port_state)) {
case FC_STATE_OFFLINE:
case FC_STATE_RESET_REQUESTED:
/*
* link has gone from online to offline -- just update the
* state of this port to BUSY and MARKed to go offline
*/
fcp_trace, FCP_BUF_LEVEL_3, 0,
"link went offline");
/*
* We were offline a while ago and this one
* seems to indicate that the loop has gone
* dead forever.
*/
} else {
pptr->port_link_cnt++;
if (pptr->port_mpxio) {
}
pptr->port_state &=
pptr->port_tmp_cnt = 0;
}
break;
case FC_STATE_ONLINE:
case FC_STATE_LIP:
case FC_STATE_LIP_LBIT_SET:
/*
* link has gone from offline to online
*/
fcp_trace, FCP_BUF_LEVEL_3, 0,
"link went online");
pptr->port_link_cnt++;
while (pptr->port_ipkt_cnt) {
}
/*
* The state of the targets and luns accessible through this
* port is updated.
*/
if (!dev_cnt) {
/*
* We go directly to the online state if no remote
* ports were discovered.
*/
fcp_trace, FCP_BUF_LEVEL_3, 0,
"No remote ports discovered");
}
switch (port_top) {
case FC_TOP_FABRIC:
case FC_TOP_PUBLIC_LOOP:
case FC_TOP_PRIVATE_LOOP:
case FC_TOP_PT_PT:
}
break;
default:
/*
* We got here because we were provided with an unknown
* topology.
*/
}
"!unknown/unsupported topology (0x%x)", port_top);
break;
}
fcp_trace, FCP_BUF_LEVEL_3, 0,
"Notify ssd of the reset to reinstate the reservations");
break;
case FC_STATE_RESET:
fcp_trace, FCP_BUF_LEVEL_3, 0,
break;
case FC_STATE_DEVICE_CHANGE:
/*
* We come here when an application has requested
*/
FCP_STATE_INIT)) {
/*
* This case can happen when the FCTL is in the
* process of giving us on online and the host on
* the state changes should be serialized unless
* they are opposite (online-offline).
* The transport will give us a final state change
* so we can ignore this for the time being.
*/
break;
}
}
/*
* Extend the deadline under steady state conditions
* to provide more time for the device-change-commands
*/
if (!pptr->port_ipkt_cnt) {
}
/*
* There is another race condition here, where if we were
* in ONLINEING state and a devices in the map logs out,
* fp will give another state change as DEVICE_CHANGE
* and OLD. This will result in that target being offlined.
* The pd_handle is freed. If from the first statec callback
* panic in fc_ulp_transport with invalid pd_handle.
* The fix is to check for the link_cnt before issuing
* any command down.
*/
break;
}
/* Do nothing else */
break;
default:
"!Invalid state change=0x%x", port_state);
break;
}
if (map_tag) {
}
}
/*
* Function: fcp_handle_devices
*
* Description: This function updates the devices currently known by
* walking the list provided by the caller. The list passed
* by the caller is supposed to be the list of reachable
* devices.
*
* Argument: *pptr Fcp port structure.
* *devlist Pointer to the first entry of a table
* containing the remote ports that can be
* reached.
* dev_cnt Number of entries pointed by devlist.
* link_cnt Link state count.
* *map_tag Array of fcp_map_tag_t structures.
* cause What caused this function to be called.
*
* Return Value: None
*
* Notes: The pptr->port_mutex must be held.
*/
static void
{
int i;
int check_finish_init = 0;
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_handle_devices: called for %d dev(s)", dev_cnt);
if (dev_cnt) {
}
/*
* The following code goes through the list of remote ports that are
* accessible through this (pptr) local port (The list walked is the
* one provided by the caller which is the list of the remote ports
* currently reachable). It checks if any of them was already
* known by looking for the corresponding target structure based on
* the world wide name. If a target is part of the list it is tagged
* (ptgt->tgt_aux_state = FCP_TGT_TAGGED).
*
* Old comment
* -----------
* Before we drop port mutex; we MUST get the tags updated; This
* two step process is somewhat slow, but more reliable.
*/
/*
* get ptr to this map entry in our port's
* list (if any)
*/
if (ptgt) {
if (cause == FCP_CAUSE_LINK_CHANGE) {
}
}
}
/*
* At this point we know which devices of the new list were already
* known (The field tgt_aux_state of the target structure has been
* set to FCP_TGT_TAGGED).
*
* The following code goes through the list of targets currently known
* by the local port (the list is actually a hashing table). If a
* target is found and is not tagged, it means the target cannot
* be reached anymore through the local port (pptr). It is offlined.
* The offlining only occurs if the cause is FCP_CAUSE_LINK_CHANGE.
*/
for (i = 0; i < FCP_NUM_HASH; i++) {
(cause == FCP_CAUSE_LINK_CHANGE) &&
}
}
}
/*
* At this point, the devices that were known but cannot be reached
* anymore, have most likely been offlined.
*
* The following section of code seems to go through the list of
* remote ports that can now be reached. For every single one it
* checks if it is already known or if it is a new port.
*/
if (check_finish_init) {
ASSERT(i > 0);
check_finish_init = 0;
}
/* get a pointer to this map entry */
/*
* Check for the duplicate map entry flag. If we have marked
* this entry as a duplicate we skip it since the correct
* (perhaps even same) state change will be encountered
* later in the list.
*/
continue;
/* get ptr to this map entry in our port's list (if any) */
if (ptgt) {
/*
* This device was already known. The field
* tgt_aux_state is reset (was probably set to
* FCP_TGT_TAGGED previously in this routine).
*/
ptgt->tgt_aux_state = 0;
fcp_trace, FCP_BUF_LEVEL_3, 0,
"0x%x/0x%x/0x%x/0x%x, tgt_d_id=0x%x, "
"tgt_state=%d",
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"map_type=%x, did = %x",
}
case PORT_DEVICE_NOCHANGE:
case PORT_DEVICE_USER_CREATE:
case PORT_DEVICE_USER_LOGIN:
case PORT_DEVICE_NEW:
}
break;
case PORT_DEVICE_OLD:
/*
* to get drained
*/
while (ptgt->tgt_ipkt_cnt ||
== FC_SUCCESS) {
}
}
}
break;
case PORT_DEVICE_USER_DELETE:
case PORT_DEVICE_USER_LOGOUT:
}
}
break;
case PORT_DEVICE_CHANGED:
}
} else {
}
}
break;
default:
break;
}
}
ASSERT(i > 0);
}
}
/*
* Function: fcp_handle_mapflags
*
* Description: This function creates a target structure if the ptgt passed
* is NULL. It also kicks off the PLOGI if we are not logged
* into the target yet or the PRLI if we are logged into the
* target already. The rest of the treatment is done in the
* callbacks of the PLOGI or PRLI.
*
* Argument: *pptr FCP Port structure.
* *ptgt Target structure.
* *map_entry Array of fc_portmap_t structures.
* link_cnt Link state count.
* tgt_cnt Target state count.
* cause What caused this function to be called.
*
* Return Value: TRUE Failed
* FALSE Succeeded
*
* Notes: pptr->port_mutex must be owned.
*/
static int
{
int lcount;
int tcount;
int alloc;
int valid_ptgt_was_passed = FALSE;
/*
* This case is possible where the FCTL has come up and done discovery
* before FCP was loaded and attached. FCTL would have discovered the
* devices and later the ULP came online. In this case ULP's would get
* PORT_DEVICE_NOCHANGE but target would be NULL.
*/
/* don't already have a target */
"!FC target allocation failed");
return (ret);
}
} else {
}
/*
* Copy in the target parameters
*/
/* Copy port and node WWNs */
/*
* determine if there are any tape LUNs on this target
*/
return (ret);
}
}
}
/*
* If ptgt was NULL when this function was entered, then tgt_node_state
* was never specifically initialized but zeroed out which means
* FCP_TGT_NODE_NONE.
*/
switch (ptgt->tgt_node_state) {
case FCP_TGT_NODE_NONE:
case FCP_TGT_NODE_ON_DEMAND:
/*
* If auto configuration is set and
* the tgt_manual_config_only flag is set then
* we only want the user to be able to change
* the state through create_on_demand.
*/
} else {
}
break;
case FCP_TGT_NODE_PRESENT:
break;
}
/*
* If we are booting from a fabric device, make sure we
* mark the node state appropriately for this target to be
* enumerated
*/
sizeof (ptgt->tgt_port_wwn)) == 0) {
}
}
fcp_trace, FCP_BUF_LEVEL_3, 0,
"map_pd=%p, map_type=%x, did = %x, ulp_rscn_count=0x%x",
/*
* Reset target OFFLINE state and mark the target BUSY
*/
/*
* if we are already logged in, then we do a PRLI, else
* we do a PLOGI first (to get logged in)
*
* We will not check if we are the PLOGI initiator
*/
/*
* We've exited port_mutex before calling fcp_icmd_alloc,
* we need to make sure we reacquire it before returning.
*/
return (FALSE);
}
/* TRUE is only returned while target is intended skipped */
/* discover info about this target */
} else {
}
return (ret);
}
/*
* Function: fcp_send_els
*
* Description: Sends an ELS to the target specified by the caller. Supports
* PLOGI and PRLI.
*
* Argument: *pptr Fcp port.
* *ptgt Target to send the ELS to.
* *icmd Internal packet
* opcode ELS opcode
* lcount Link state change counter
* tcount Target state change counter
* cause What caused the call
*
* Return Value: DDI_SUCCESS
* Others
*/
static int
{
int internal = 0;
int alloc;
int cmd_len;
int resp_len;
int rval = DDI_FAILURE;
fcp_trace, FCP_BUF_LEVEL_5, 0,
if (opcode == LA_ELS_PLOGI) {
cmd_len = sizeof (la_els_logi_t);
resp_len = sizeof (la_els_logi_t);
} else {
cmd_len = sizeof (la_els_prli_t);
resp_len = sizeof (la_els_prli_t);
}
sizeof (la_els_prli_t));
return (res);
}
internal++;
}
fpkt->pkt_datalen = 0;
icmd->ipkt_retries = 0;
/* fill in fpkt info */
/* get ptr to frame hdr in fpkt */
/*
* fill in frame hdr
*/
/*
* at this point we have a filled in cmd pkt
*
* fill in the respective info, then use the transport to send
* the packet
*
* for a PLOGI call fc_ulp_login(), and
* for a PRLI call fc_ulp_issue_els()
*/
switch (opcode) {
case LA_ELS_PLOGI: {
struct la_els_logi logi;
if (rval == FC_SUCCESS) {
res = DDI_SUCCESS;
break;
}
rval, "PLOGI");
} else {
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_send_els1: state change occured"
}
break;
}
case LA_ELS_PRLI: {
struct la_els_prli prli;
/* fill in PRLI cmd ELS fields */
/* get ptr to PRLI service params */
/* fill in service params */
fprli->obsolete_1 = 0;
fprli->obsolete_2 = 0;
fprli->data_overlay_allowed = 0;
} else {
}
fprli->write_xfer_rdy_disabled = 0;
/* issue the PRLI request */
if (rval == FC_SUCCESS) {
res = DDI_SUCCESS;
break;
}
rval, "PRLI");
} else {
}
break;
}
default:
break;
}
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_send_els: returning %d", res);
if (res != DDI_SUCCESS) {
if (internal) {
}
}
return (res);
}
/*
* called internally update the state of all of the tgts and each LUN
* for this port (i.e. each target known to be attached to this port)
* if they are not already offline
*
* must be called with the port mutex owned
*
* acquires and releases the target mutexes for each target attached
* to this port
*/
void
{
int i;
for (i = 0; i < FCP_NUM_HASH; i++) {
ptgt->tgt_change_cnt++;
}
}
}
static void
{
int i;
int ndevs;
for (ndevs = 0, i = 0; i < FCP_NUM_HASH; i++) {
ndevs++;
}
}
if (ndevs == 0) {
return;
}
for (i = 0; i < FCP_NUM_HASH; i++) {
}
}
}
/*
* Function: fcp_update_tgt_state
*
* Description: This function updates the field tgt_state of a target. That
* field is a bitmap and which bit can be set or reset
* individually. The action applied to the target state is also
* applied to all the LUNs belonging to the target (provided the
* LUN is not offline). A side effect of applying the state
* modification to the target and the LUNs is the field tgt_trace
* of the target and lun_trace of the LUNs is set to zero.
*
*
* Argument: *ptgt Target structure.
* state State bits to update.
*
* Return Value: None
*
* Context: Interrupt, Kernel or User context.
* The mutex of the target (ptgt->tgt_mutex) must be owned when
* calling this function.
*/
void
{
/* The target is not offline. */
} else {
}
/* The LUN is not offline. */
} else {
}
}
}
}
}
/*
* Function: fcp_update_tgt_state
*
* Description: This function updates the field lun_state of a LUN. That
* field is a bitmap and which bit can be set or reset
* individually.
*
* Argument: *plun LUN structure.
* state State bits to update.
*
* Return Value: None
*
* Context: Interrupt, Kernel or User context.
* The mutex of the target (ptgt->tgt_mutex) must be owned when
* calling this function.
*/
void
{
}
}
/*
* Function: fcp_get_port
*
* Description: This function returns the fcp_port structure from the opaque
* handle passed by the caller. That opaque handle is the handle
* handle has been stored in the corresponding fcp_port
* structure. This function is going to walk the global list of
* fcp_port structures till one has a port_fp_handle that matches
* the handle passed by the caller. This function enters the
* mutex fcp_global_mutex while walking the global list and then
* releases it.
*
* particular port.
*
* Return Value: NULL Not found.
* Not NULL Pointer to the fcp_port structure.
*
* Context: Interrupt, Kernel or User context.
*/
static struct fcp_port *
{
break;
}
}
return (pptr);
}
static void
{
"!couldn't post response to unsolicited request: "
" state=%s reason=%s rx_id=%x ox_id=%x",
}
}
/*
* Perform general purpose preparation of a response to an unsolicited request
*/
static void
{
}
/*ARGSUSED*/
static int
{
struct la_els_prli prli;
struct la_els_prli *from;
int tcount = 0;
int lcount;
NULL) {
}
FC_INVALID_RSCN_COUNT)) == NULL) {
return (FC_FAILURE);
}
fpkt->pkt_rsplen = 0;
fpkt->pkt_datalen = 0;
/* fill in service params */
fprli->obsolete_1 = 0;
fprli->obsolete_2 = 0;
fprli->data_overlay_allowed = 0;
} else {
}
fprli->write_xfer_rdy_disabled = 0;
/* save the unsol prli payload first */
int rval;
FC_SUCCESS) {
return (FC_SUCCESS);
}
/* Let it timeout */
return (FC_FAILURE);
}
} else {
return (FC_FAILURE);
}
return (FC_SUCCESS);
}
/*
* Function: fcp_icmd_alloc
*
* Description: This function allocated a fcp_ipkt structure. The pkt_comp
* field is initialized to fcp_icmd_callback. Sometimes it is
* modified by the caller (such as fcp_send_scsi). The
* structure is also tied to the state of the line and of the
* target at a particular time. That link is established by
* setting the fields ipkt_link_cnt and ipkt_change_cnt to lcount
* and tcount which came respectively from pptr->link_cnt and
* ptgt->tgt_change_cnt.
*
* Argument: *pptr Fcp port.
* *ptgt Target (destination of the command).
* cmd_len Length of the command.
* resp_len Length of the expected response.
* data_len Length of the data.
* nodma Indicates weither the command and response.
* will be transfer through DMA or not.
* lcount Link state change counter.
* tcount Target state change counter.
* cause Reason that lead to this call.
*
* Return Value: NULL Failed.
* Not NULL Internal packet address.
*/
static struct fcp_ipkt *
{
int dma_setup = 0;
"!internal packet allocation failed");
return (NULL);
}
/*
* initialize the allocated packet
*/
/* keep track of amt of data to be sent in pkt */
/* set up pkt's ptr to the fc_packet_t struct, just after the ipkt */
/* set pkt's private ptr to point to cmd pkt */
/* set FCA private ptr to memory just beyond */
/* get ptr to fpkt substruct and fill it in */
sizeof (struct fcp_ipkt));
}
/*
* The pkt_ulp_rscn_infop (aka pkt_ulp_rsvd1) field is used to pass the
* rscn_count as fcp knows down to the transport. If a valid count was
* passed into this function, we allocate memory to actually pass down
* this info.
*
* BTW, if the kmem_zalloc fails, we won't try too hard. This will
* basically mean that fcp will not be able to help transport
* distinguish if a new RSCN has come after fcp was last informed about
* 5068068 where the device might end up going offline in case of RSCN
* storms.
*/
if (rscn_count != FC_INVALID_RSCN_COUNT) {
sizeof (fc_ulp_rscn_info_t), KM_NOSLEEP);
fcp_trace, FCP_BUF_LEVEL_6, 0,
"Failed to alloc memory to pass rscn info");
}
}
}
goto fail;
}
dma_setup++;
/*
* Must hold target mutex across setting of pkt_pd and call to
* fc_ulp_init_packet to ensure the handle to the target doesn't go
* away while we're not looking.
*/
/* ask transport to do its initialization on this pkt */
!= FC_SUCCESS) {
fcp_trace, FCP_BUF_LEVEL_6, 0,
"fc_ulp_init_packet failed");
goto fail;
}
} else {
!= FC_SUCCESS) {
fcp_trace, FCP_BUF_LEVEL_6, 0,
"fc_ulp_init_packet failed");
goto fail;
}
}
int rval;
goto fail;
}
ptgt->tgt_ipkt_cnt++;
}
pptr->port_ipkt_cnt++;
return (icmd);
fail:
sizeof (fc_ulp_rscn_info_t));
}
if (dma_setup) {
}
return (NULL);
}
/*
* Function: fcp_icmd_free
*
* Description: Frees the internal command passed by the caller.
*
* Argument: *pptr Fcp port.
* *icmd Internal packet to free.
*
* Return Value: None
*/
static void
{
/* Let the underlying layers do their cleanup. */
sizeof (fc_ulp_rscn_info_t));
}
if (ptgt) {
ptgt->tgt_ipkt_cnt--;
}
pptr->port_ipkt_cnt--;
}
/*
* Function: fcp_alloc_dma
*
* Description: Allocated the DMA resources required for the internal
* packet.
*
* Argument: *pptr FCP port.
* *icmd Internal FCP packet.
* nodma Indicates if the Cmd and Resp will be DMAed.
* flags Allocation flags (Sleep or NoSleep).
*
* Return Value: FC_SUCCESS
* FC_NOMEM
*/
static int
{
int rval;
int bound = 0;
int cmd_resp = 0;
if (nodma) {
goto fail;
}
goto fail;
}
} else {
if (rval == FC_FAILURE) {
goto fail;
}
cmd_resp++;
}
if (fpkt->pkt_datalen != 0) {
/*
* set up DMA handle and memory for the data in this packet
*/
goto fail;
}
goto fail;
}
goto fail;
}
/* bind DMA address and handle together */
goto fail;
}
bound++;
goto fail;
}
*cp = pkt_data_cookie;
cp++;
*cp = pkt_data_cookie;
}
}
return (FC_SUCCESS);
fail:
if (bound) {
}
if (fpkt->pkt_data_dma) {
}
}
if (nodma) {
}
}
} else {
if (cmd_resp) {
}
}
return (FC_NOMEM);
}
static void
{
if (fpkt->pkt_data_dma) {
}
}
if (icmd->ipkt_nodma) {
}
}
} else {
}
}
/*
* Function: fcp_lookup_target
*
* Description: Finds a target given a WWN.
*
* Argument: *pptr FCP port.
* *wwn World Wide Name of the device to look for.
*
* Return Value: NULL No target found
* Not NULL Target structure
*
* Context: Interrupt context.
* The mutex pptr->port_mutex must be owned.
*/
/* ARGSUSED */
static struct fcp_tgt *
{
int hash;
sizeof (ptgt->tgt_port_wwn)) == 0) {
break;
}
}
return (ptgt);
}
/*
* Find target structure given a port identifier
*/
static struct fcp_tgt *
{
port_id.priv_lilp_posit = 0;
&pwwn) == FC_SUCCESS) {
}
return (ptgt);
}
/*
* the packet completion callback routine for info cmd pkts
*
* this means fpkt pts to a response to either a PLOGI or a PRLI
*
* if there is an error an attempt is made to call a routine to resend
* the command that failed
*/
static void
{
struct la_els_prli *prli;
struct la_els_prli prli_s;
int free_pkt = 1;
int rval;
int lun0_newalloc;
/* get ptrs to the port and target structs for the cmd */
sizeof (prli_s));
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"ELS (%x) callback state=0x%x reason=0x%x for %x",
/*
* in a fabric environment the port device handles
* get created only after successful LOGIN into the
* transport, so the transport makes this port
* device (pd) handle available in this packet, so
* save it now
*/
}
/* which ELS cmd is this response for ?? */
switch (icmd->ipkt_opcode) {
case LA_ELS_PLOGI:
fcp_trace, FCP_BUF_LEVEL_5, 0,
"PLOGI to d_id=0x%x succeeded, wwn=%08x%08x",
/* Note that we are not allocating a new icmd */
goto fail;
}
break;
case LA_ELS_PRLI:
fcp_trace, FCP_BUF_LEVEL_5, 0,
sizeof (prli_s));
/*
* this FCP device does not support target mode
*/
goto fail;
}
&ptgt->tgt_port_wwn);
}
/* target is no longer offline */
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_icmd_callback,1: state change "
goto fail;
}
/*
* lun 0 should always respond to inquiry, so
* get the LUN struct for LUN 0
*
* Currently we deal with first level of addressing.
* If / when we start supporting 0x device types
* (DTYPE_ARRAY_CTRL, i.e. array controllers)
* this logic will need revisiting.
*/
lun0_newalloc = 0;
/*
* no LUN struct for LUN 0 yet exists,
* so create one
*/
"!Failed to allocate lun 0 for"
goto fail;
}
lun0_newalloc = 1;
}
/* fill in LUN info */
/*
* consider lun 0 as device not connected if it is
* offlined or newly allocated
*/
ptgt->tgt_report_lun_cnt = 0;
/* Retrieve the rscn count (if a valid one exists) */
rscn_count = ((fc_ulp_rscn_info_t *)
} else {
}
/* send Report Lun request to target */
sizeof (struct fcp_reportlun_resp),
"!Failed to send REPORT LUN to"
} else {
FCP_BUF_LEVEL_5, 0,
"fcp_icmd_callback,2:state change"
" occured for D_ID=0x%x",
}
goto fail;
} else {
free_pkt = 0;
}
break;
default:
"!fcp_icmd_callback Invalid opcode");
goto fail;
}
return;
}
/*
* Other PLOGI failures are not retried as the
* transport does it already
*/
if (fcp_is_retryable(icmd) &&
if (FCP_MUST_RETRY(fpkt)) {
return;
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"ELS PRLI is retried for d_id=0x%x, state=%x,"
fpkt->pkt_reason);
/*
* Retry by recalling the routine that
* originally queued this packet
*/
fpkt->pkt_timeout +=
}
fpkt);
if (rval == FC_SUCCESS) {
return;
}
if (rval == FC_STATEC_BUSY ||
rval == FC_OFFLINE) {
return;
}
"!ELS 0x%x failed to d_id=0x%x;"
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_icmd_callback,3: state change "
}
}
} else {
if (fcp_is_retryable(icmd) &&
if (FCP_MUST_RETRY(fpkt)) {
return;
}
}
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_icmd_callback,4: state change occured"
}
}
fail:
if (free_pkt) {
}
}
/*
* called internally to send an info cmd using the transport
*
* sends either an INQ or a REPORT_LUN
*
* when the packet is completed fcp_scsi_callback is called
*/
static int
{
int nodma;
fcp_trace, FCP_BUF_LEVEL_5, 0,
return (DDI_FAILURE);
}
icmd->ipkt_retries = 0;
if (nodma) {
} else {
}
/*
* Request SCSI target for expedited processing
*/
/*
* Set up for untagged queuing because we do not
* know if the fibre device supports queuing.
*/
switch (opcode) {
case SCMD_INQUIRY_PAGE83:
/*
* Prepare to get the Inquiry VPD page 83 information
*/
break;
case SCMD_INQUIRY:
break;
case SCMD_REPORT_LUN: {
d_id.priv_lilp_posit = 0;
break;
}
default:
"!fcp_send_scsi Invalid opcode");
break;
}
if (!nodma) {
}
FC_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_send_scsi,1: state change occured"
return (DDI_FAILURE);
}
}
/*
* called by fcp_scsi_callback to check to handle the case where
* REPORT_LUN returns ILLEGAL REQUEST or a UNIT ATTENTION
*/
static int
{
int rval = DDI_FAILURE;
/*
* SCSI-II Reserve Release support. Some older FC drives return
* Reservation conflict for Report Luns command.
*/
if (icmd->ipkt_nodma) {
} else {
}
return (DDI_SUCCESS);
}
sense = &sense_info;
/* no need to continue if sense length is not set */
return (rval);
}
/* casting 64-bit integer to 8-bit */
sizeof (struct scsi_extended_sense));
if (rqlen < 14) {
/* no need to continue if request length isn't long enough */
return (rval);
}
if (icmd->ipkt_nodma) {
/*
* We can safely use fcp_response_len here since the
* only path that calls fcp_check_reportlun,
* fcp_scsi_callback, has already called
* fcp_validate_fcp_response.
*/
} else {
sizeof (struct scsi_extended_sense));
}
if (!FCP_SENSE_NO_LUN(sense)) {
/* clear the flag if any */
}
if (icmd->ipkt_nodma) {
} else {
}
return (DDI_SUCCESS);
}
/*
* This is for the STK library which returns a check condition,
* to indicate device is not ready, manual assistance needed.
* This is to a report lun command when the door is open.
*/
if (icmd->ipkt_nodma) {
} else {
}
return (DDI_SUCCESS);
}
if ((FCP_SENSE_REPORTLUN_CHANGED(sense)) ||
(FCP_SENSE_NO_LUN(sense))) {
if ((FCP_SENSE_NO_LUN(sense)) &&
/*
* reconfig was triggred by ILLEGAL REQUEST but
* got ILLEGAL REQUEST again
*/
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!FCP: Unable to obtain Report Lun data"
} else {
/*
* REPORT LUN data has changed. Kick off
* rediscovery
*/
}
if (FCP_SENSE_NO_LUN(sense)) {
}
if (FCP_SENSE_REPORTLUN_CHANGED(sense)) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!FCP:Report Lun Has Changed"
} else if (FCP_SENSE_NO_LUN(sense)) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!FCP:LU Not Supported"
}
}
rval = DDI_SUCCESS;
}
fcp_trace, FCP_BUF_LEVEL_5, 0,
"D_ID=%x, sense=%x, status=%x",
return (rval);
}
/*
* Function: fcp_scsi_callback
*
* Description: This is the callback routine set by fcp_send_scsi() after
* it calls fcp_icmd_alloc(). The SCSI command completed here
* and autogenerated by FCP are: REPORT_LUN, INQUIRY and
* INQUIRY_PAGE83.
*
* Argument: *fpkt FC packet used to convey the command
*
* Return Value: None
*/
static void
{
if (icmd->ipkt_nodma) {
} else {
sizeof (struct fcp_rsp));
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"SCSI callback state=0x%x for %x, op_code=0x%x, "
"status=%x, lun num=%x",
/*
* Pre-init LUN GUID with NWWN if it is not a device that
* supports multiple luns and we know it's not page83
* compliant. Although using a NWWN is not lun unique,
* we will be fine since there is only one lun behind the taget
* in this case.
*/
if ((plun->lun_guid_size == 0) &&
(fcp_symmetric_device_probe(plun) == 0)) {
}
/*
* Some old FC tapes and FC <-> SCSI bridge devices return overrun
* when thay have more data than what is asked in CDB. An overrun
* is really when FCP_DL is smaller than the data length in CDB.
* In the case here we know that REPORT LUN command we formed within
* this binary has correct FCP_DL. So this OVERRUN is due to bad device
* behavior. In reality this is FC_SUCCESS.
*/
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
/*
* Inquiry VPD page command on A5K SES devices would
* result in data CRC errors.
*/
return;
}
}
FCP_MUST_RETRY(fpkt)) {
return;
}
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_scsi_callback,1: state change occured"
}
return;
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_scsi_callback,2: state change occured"
return;
}
if (icmd->ipkt_nodma) {
sizeof (struct fcp_rsp));
} else {
bep = &fcp_rsp_err;
}
return;
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"rsp_code=0x%x, rsp_len_set=0x%x",
return;
}
return;
}
/*
* Devices that do not support INQUIRY_PAGE83, return check condition
* with illegal request as per SCSI spec.
* Crossbridge is one such device and Daktari's SES node is another.
* We want to ideally enumerate these devices as a non-mpxio devices.
* SES nodes (Daktari only currently) are an exception to this.
*/
fcp_trace, FCP_BUF_LEVEL_3, 0,
"INQUIRY_PAGE83 for d_id %x (dtype:0x%x) failed with "
"check condition. May enumerate as non-mpxio device",
/*
* If we let Daktari's SES be enumerated as a non-mpxio
* device, there will be a discrepency in that the other
* internal FC disks will get enumerated as mpxio devices.
* Applications like luxadm expect this to be consistent.
*
* So, we put in a hack here to check if this is an SES device
* and handle it here.
*/
/*
* Since, pkt_state is actually FC_PKT_SUCCESS
* at this stage, we fake a failure here so that
* fcp_handle_page83 will create a device path using
* the WWN instead of the GUID which is not there anyway
*/
return;
}
icmd->ipkt_cause);
return;
}
int rval = DDI_FAILURE;
/*
* handle cases where report lun isn't supported
* by faking up our own REPORT_LUN response or
* UNIT ATTENTION
*/
/*
* fcp_check_reportlun might have modified the
* FCP response. Copy it in again to get an updated
* FCP response
*/
sizeof (struct fcp_rsp));
}
}
if (rval == DDI_SUCCESS) {
icmd->ipkt_cause);
} else {
}
return;
}
} else {
}
}
switch (icmd->ipkt_opcode) {
case SCMD_INQUIRY:
break;
case SCMD_REPORT_LUN:
break;
case SCMD_INQUIRY_PAGE83:
break;
default:
break;
}
}
static void
{
fcp_is_retryable(icmd)) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"Retrying %s to %x; state=%x, reason=%x",
} else {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_retry_scsi_cmd,1: state change occured"
icmd->ipkt_cause);
}
} else {
}
}
/*
* Function: fcp_handle_page83
*
* Description: Treats the response to INQUIRY_PAGE83.
*
* Argument: *fpkt FC packet used to convey the command.
* *icmd Original fcp_ipkt structure.
* ignore_page83_data
* if it's 1, that means it's a special devices's
* page83 response, it should be enumerated under mpxio
*
* Return Value: None
*/
static void
int ignore_page83_data)
{
int fail = 0;
int ret;
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_handle_page83: port=%d, tgt D_ID=0x%x, "
"dtype=0x%x, lun num=%x",
NULL, /* driver name */
NULL, /* page 80 data */
0, /* page 80 len */
dev_id_page, /* page 83 data */
SCMD_MAX_INQUIRY_PAGE83_SIZE, /* page 83 data len */
&devid);
if (ret == DDI_SUCCESS) {
if (guid) {
/*
* Check our current guid. If it's non null
* and it has changed, we need to copy it into
* lun_old_guid since we might still need it.
*/
unsigned int len;
/*
* If the guid of the LUN changes,
* reconfiguration should be triggered
* to reflect the changes.
* i.e. we should offline the LUN with
* the old guid, and online the LUN with
* the new guid.
*/
if (plun->lun_old_guid) {
}
if (plun->lun_old_guid) {
/*
* The alloc was successful then
* let's do the copy.
*/
} else {
fail = 1;
plun->lun_old_guid_size = 0;
}
}
if (!fail) {
fail = 1;
}
}
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_handle_page83: unable to create "
"GUID");
/* couldn't create good guid from devid */
fail = 1;
}
} else if (ret == DDI_NOT_WELL_FORMED) {
/* NULL filled data for page 83 */
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_handle_page83: retry GUID");
icmd->ipkt_retries = 0;
return;
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_handle_page83: bad ddi_devid_scsi_encode %x",
ret);
/*
* Since the page83 validation
* introduced late, we are being
* tolerant to the existing devices
* that already found to be working
* under mpxio, like A5200's SES device,
* its page83 response will not be standard-compliant,
* but we still want it to be enumerated under mpxio.
*/
if (fcp_symmetric_device_probe(plun) != 0) {
fail = 1;
}
}
} else {
/* bad packet state */
/*
* For some special devices (A5K SES and Daktari's SES devices),
* they should be enumerated under mpxio
* or "luxadm dis" will fail
*/
if (ignore_page83_data) {
fail = 0;
} else {
fail = 1;
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"!Devid page cmd failed. "
"fpkt_state: %x fpkt_reason: %x",
"ignore_page83: %d",
}
/*
* If lun_cip is not NULL, then we needn't update lun_mpxio to avoid
* mismatch between lun_cip and lun_mpxio.
*/
/*
* If we don't have a guid for this lun it's because we were
* unable to glean one from the page 83 response. Set the
* control flag to 0 here to make sure that we don't attempt to
* enumerate it under mpxio.
*/
} else {
}
}
}
/*
* Function: fcp_handle_inquiry
*
* Description: Called by fcp_scsi_callback to handle the response to an
* INQUIRY request.
*
* Argument: *fpkt FC packet used to convey the command.
* *icmd Original fcp_ipkt structure.
*
* Return Value: None
*/
static void
{
sizeof (struct scsi_inquiry));
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_handle_inquiry: port=%d, tgt D_ID=0x%x, lun=0x%x, "
if (pqual != 0) {
/*
* Non-zero peripheral qualifier
*/
"!Target 0x%x lun 0x%x: Nonzero peripheral qualifier: "
"Device type=0x%x Peripheral qual=0x%x\n",
fcp_trace, FCP_BUF_LEVEL_5, 0,
"!Target 0x%x lun 0x%x: Nonzero peripheral qualifier: "
"Device type=0x%x Peripheral qual=0x%x\n",
return;
}
/*
* If the device is already initialized, check the dtype
* for a change. If it has changed then update the flags
* so the create_luns will offline the old device and
* create the new device. Refer to bug: 4764752
*/
}
/*
* This code is setting/initializing the throttling in the FCA
* driver.
*/
if (!pptr->port_notify) {
FCP_SVE_THROTTLE << 8));
}
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_handle_inquiry,1:state change occured"
icmd->ipkt_cause);
return;
}
/* Retrieve the rscn count (if a valid one exists) */
rscn_count = ((fc_ulp_rscn_info_t *)
} else {
}
icmd->ipkt_cause);
}
/*
* Read Inquiry VPD Page 0x83 to uniquely
* identify this logical unit.
*/
}
/*
* Function: fcp_handle_reportlun
*
* Description: Called by fcp_scsi_callback to handle the response to a
* REPORT_LUN request.
*
* Argument: *fpkt FC packet used to convey the command.
* *icmd Original fcp_ipkt structure.
*
* Return Value: None
*/
static void
{
int i;
int nluns_claimed;
int nluns_bufmax;
int len;
struct fcp_reportlun_resp *report_lun;
if ((len < FCP_LUN_HEADER) ||
return;
}
fpkt->pkt_datalen);
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_handle_reportlun: port=%d, tgt D_ID=0x%x",
/*
* Get the number of luns (which is supplied as LUNS * 8) the
* device claims it has.
*/
/*
* Get the maximum number of luns the buffer submitted can hold.
*/
/*
* Due to limitations of certain hardware, we support only 16 bit LUNs
*/
if (nluns_claimed > FCP_MAX_LUNS_SUPPORTED) {
" 0x%x number of LUNs for target=%x", nluns_claimed,
return;
}
/*
* If there are more LUNs than we have allocated memory for,
* allocate more space and send down yet another report lun if
* the maximum number of attempts hasn't been reached.
*/
if ((nluns_claimed > nluns_bufmax) &&
fcp_trace, FCP_BUF_LEVEL_5, 0,
"!Dynamically discovered %d LUNs for D_ID=%x",
/* Retrieve the rscn count (if a valid one exists) */
rscn_count = ((fc_ulp_rscn_info_t *)
} else {
}
icmd->ipkt_cause);
}
return;
}
if (nluns_claimed > nluns_bufmax) {
fcp_trace, FCP_BUF_LEVEL_5, 0,
"Target=%x:%x:%x:%x:%x:%x:%x:%x"
" Number of LUNs lost=%x",
}
/*
* Identify missing LUNs and print warning messages
*/
int offline;
int exists = 0;
for (i = 0; i < nluns_claimed && exists == 0; i++) {
switch (lun_string[0] & 0xC0) {
case FCP_LUN_ADDRESSING:
case FCP_PD_ADDRESSING:
lun_string[1];
exists++;
break;
}
break;
default:
break;
}
}
/*
* set disappear flag when device was connected
*/
"!Lun=%x for target=%x disappeared",
} else {
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_handle_reportlun,1: state change"
icmd->ipkt_cause);
return;
}
} else if (exists) {
/*
* clear FCP_LUN_DEVICE_NOT_CONNECTED when lun 0
* actually exists in REPORT_LUN response
*/
"!Lun=%x for target=%x reappeared",
}
}
}
}
fcp_trace, FCP_BUF_LEVEL_5, 0,
"fcp_handle_reportlun: port=%d, tgt D_ID=0x%x, %d LUN(s)",
/* scan each lun */
for (i = 0; i < nluns_claimed; i++) {
fcp_trace, FCP_BUF_LEVEL_5, 0,
"handle_reportlun: d_id=%x, LUN ind=%d, LUN=%d,"
lun_string[0]);
switch (lun_string[0] & 0xC0) {
case FCP_LUN_ADDRESSING:
case FCP_PD_ADDRESSING:
/* We will skip masked LUNs because of the blacklist. */
if (fcp_lun_blacklist != NULL) {
ptgt->tgt_lun_cnt--;
break;
}
}
/* see if this LUN is already allocated */
"!Lun allocation failed"
" target=%x lun=%x",
break;
}
}
/* convert to LUN */
/* Retrieve the rscn count (if a valid one exists) */
rscn_count = ((fc_ulp_rscn_info_t *)
} else {
}
"!failed to send INQUIRY"
" target=%x lun=%x",
} else {
FCP_BUF_LEVEL_5, 0,
"fcp_handle_reportlun,2: state"
" change occured for D_ID=0x%x",
}
} else {
continue;
}
break;
case FCP_VOLUME_ADDRESSING:
/* FALLTHROUGH */
default:
"!Unsupported LUN Addressing method %x "
"in response to REPORT_LUN", lun_string[0]);
break;
}
/*
* each time through this loop we should decrement
* the tmp_cnt by one -- since we go through this loop
* one time for each LUN, the tmp_cnt should never be <=0
*/
}
if (i == 0) {
}
}
/*
* called internally to return a LUN given a target and a LUN number
*/
static struct fcp_lun *
{
return (plun);
}
}
return (NULL);
}
/*
* handle finishing one target for fcp_finish_init
*
* return true (non-zero) if we want finish_init to continue with the
* next target
*
* called with the port mutex held
*/
/*ARGSUSED*/
static int
{
int rval = 1;
fcp_trace, FCP_BUF_LEVEL_5, 0,
/*
* oh oh -- another link reset or target change
* must have occurred while we are in here
*/
return (0);
} else {
}
/*
* tgt is not offline -- is it marked (i.e. needs
* to be offlined) ??
*/
/*
* this target not offline *and*
* marked
*/
tgt_cnt, 0, 0);
} else {
/* create the LUNs */
cause);
} else {
}
}
}
return (rval);
}
/*
* this routine is called to finish port initialization
*
* Each port has a "temp" counter -- when a state change happens (e.g.
* port online), the temp count is set to the number of devices in the map.
* Then, as each device gets "discovered", the temp counter is decremented
* by one. When this count reaches zero we know that all of the devices
* in the map have been discovered (or an error has occurred), so we can
* then finish initialization -- which is done by this routine (well, this
* and fcp-finish_tgt())
*
* acquires and releases the global mutex
*
* called with the port mutex owned
*/
static void
{
#ifdef DEBUG
#endif /* DEBUG */
}
/* Wake up threads waiting on config done */
}
/*
* called from fcp_finish_init to create the LUNs for a target
*
* called with the port mutex owned
*/
static void
{
/* scan all LUNs for this target */
continue;
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_create_luns: offlining marked LUN!");
continue;
}
/*
* There are conditions in which FCP_LUN_INIT flag is cleared
* but we have a valid plun->lun_cip. To cover this case also
* CLEAR_BUSY whenever we have a valid lun_cip.
*/
0, 0))) {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_create_luns: enable lun %p failed!",
plun);
}
continue;
}
if (cause == FCP_CAUSE_USER_CREATE) {
continue;
}
fcp_trace, FCP_BUF_LEVEL_6, 0,
"create_luns: passing ONLINE elem to HP thread");
/*
* If lun has changed, prepare for offlining the old path.
* Do not offline the old path right now, since it may be
* still opened.
*/
}
/* pass an ONLINE element to the hotplug thread */
/*
* We can not synchronous attach (i.e pass
* NDI_ONLINE_ATTACH) here as we might be
* coming from an interrupt or callback
* thread.
*/
"Can not ONLINE LUN; D_ID=%x, LUN=%x\n",
}
}
}
}
/*
*/
static int
{
int rval = NDI_FAILURE;
int circ;
char *devname;
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_trigger_lun: plun=%p target=%x lun=%d cip=%p what=%x "
"flags=%x mpxio=%x\n",
/*
* lun_mpxio needs checking here because we can end up in a race
* condition where this task has been dispatched while lun_mpxio is
* set, but an earlier FCP_ONLINE task for the same LUN tried to
* enable MPXIO for the LUN, but was unable to, and hence cleared
* the flag. We rely on the serialization of the tasks here. We return
* NDI_SUCCESS so any callers continue without reporting spurious
* errors, and the still think we're an MPXIO LUN.
*/
if (online == FCP_MPXIO_PATH_CLEAR_BUSY ||
else
rval = NDI_SUCCESS;
return (rval);
}
/*
* Explicit devfs_clean() due to ndi_devi_offline() not
* executing devfs_clean() if parent lock is held.
*/
if (online == FCP_OFFLINE) {
else
/*
* dip of the old cip instead of the current lun_cip.
*/
}
if (cdip) {
if (i_ddi_devi_attached(cdip)) {
/*
* Release parent lock before calling
* devfs_clean().
*/
/*
* Return if devfs_clean() fails for
* non-MPXIO case.
* For MPXIO case, another path could be
* offlined.
*/
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_trigger_lun: devfs_clean "
"failed rval=%x dip=%p",
return (NDI_FAILURE);
}
}
}
}
return (NDI_FAILURE);
}
if (is_mpxio)
else
if (online == FCP_ONLINE) {
goto fail;
}
} else {
goto fail;
}
}
if (online == FCP_ONLINE) {
&circ);
} else {
&circ);
}
if (is_mpxio)
else
return (rval);
}
/*
* take a target offline by taking all of its LUNs offline
*/
/*ARGSUSED*/
static int
{
struct fcp_tgt_elem *elem;
ptgt->tgt_change_cnt)) {
return (0);
}
if (nowait == 0) {
}
} else {
}
return (1);
}
static void
{
}
static void
int flags)
{
}
}
}
/*
* take a LUN offline
*
* enters and leaves with the target mutex held, releasing it in the process
*
* allocates memory in non-sleep mode
*/
static void
{
struct fcp_lun_elem *elem;
if (nowait) {
return;
}
if (nowait == 0) {
}
} else {
}
}
static void
{
}
/*
* Intimate MPxIO lun busy is cleared
*/
0, 0)) {
"Can not ENABLE LUN; D_ID=%x, LUN=%x",
}
/*
* Intimate MPxIO that the lun is now marked for offline
*/
}
}
static void
int flags)
{
fcp_trace, FCP_BUF_LEVEL_4, 0,
"offline_lun: passing OFFLINE elem to HP thread");
"!offlining lun=%x (trace=%x), target=%x (trace=%x)",
"Can not OFFLINE LUN; D_ID=%x, LUN=%x\n",
}
}
}
static void
{
struct fcp_lun_elem *elem;
struct fcp_lun_elem *prev;
struct fcp_lun_elem *next;
while (elem) {
int changed = 1;
changed = 0;
}
if (!changed &&
}
if (prev) {
} else {
}
} else {
}
}
}
static void
{
struct fcp_tgt_elem *elem;
struct fcp_tgt_elem *prev;
struct fcp_tgt_elem *next;
while (elem) {
int changed = 1;
changed = 0;
}
FCP_TGT_OFFLINE)) {
}
if (prev) {
} else {
}
} else {
}
}
}
static void
{
}
if (cdip) {
(void) ndi_event_retrieve_cookie(
(void) ndi_event_run_callbacks(
}
} else {
}
}
/*
* Scan all of the command pkts for this port, moving pkts that
* match our LUN onto our own list (headed by "head")
*/
static struct fcp_pkt *
{
int cmds_found = 0;
/*
* if this pkt is for a different LUN or the
* command is sent down, skip it.
*/
continue;
}
cmds_found++;
} else {
}
if (pcmd) {
}
}
} else {
}
}
fcp_trace, FCP_BUF_LEVEL_8, 0,
"scan commands: %d cmd(s) found", cmds_found);
return (head);
}
/*
* Abort all the commands in the command queue
*/
static void
{
/* scan through the pkts and invalid them */
/*
* The lun is going to be marked offline. Indicate
* the target driver not to requeue or retry this command
* as the device is going to be offlined pretty soon.
*/
pkt->pkt_statistics = 0;
/*
* ensure we have a packet completion routine,
* then call it.
*/
}
}
/*
* the pkt_comp callback for command packets
*/
static void
{
(void *)cmd);
}
/*
* Watch thread should be freeing the packet, ignore the pkt.
*/
"!FCP: Pkt completed while aborting\n");
return;
}
#ifdef DEBUG
pptr->port_npkts--;
#endif /* DEBUG */
}
static void
{
int error = 0;
struct scsi_address save;
#ifdef DEBUG
#endif /* DEBUG */
sizeof (struct fcp_rsp));
}
if (fpkt->pkt_data_resid) {
error++;
}
}
/*
* The next two checks make sure that if there
* is no sense data or a valid response and
* the command came back with check condition,
* the command should be retried.
*/
}
}
return;
}
/*
* Update the transfer resid, if appropriate
*/
}
/*
* First see if we got a FCP protocol error.
*/
struct fcp_rsp_info *bep;
sizeof (struct fcp_rsp));
FC_SUCCESS) {
"!SCSI command to d_id=0x%x lun=0x%x"
" failed, Bad FCP response values:"
" rsvd1=%x, rsvd2=%x, sts-rsvd1=%x,"
" sts-rsvd2=%x, rsplen=%x, senselen=%x",
return;
}
sizeof (struct fcp_rsp_info));
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"FCP response error on cmd=%p"
" target=0x%x, cip=%p", cmd,
}
}
/*
* See if we got a SCSI error with sense data
*/
struct scsi_arq_status *arq;
struct scsi_extended_sense *sense_to;
sizeof (struct scsi_extended_sense));
FC_SUCCESS) {
"!SCSI command to d_id=0x%x lun=0x%x"
" failed, Bad FCP response values:"
" rsvd1=%x, rsvd2=%x, sts-rsvd1=%x,"
" sts-rsvd2=%x, rsplen=%x, senselen=%x",
return;
}
/*
* copy in sense information
*/
} else {
}
if ((FCP_SENSE_REPORTLUN_CHANGED(sense_to)) ||
(FCP_SENSE_NO_LUN(sense_to))) {
/*
* Kick off rediscovery
*/
}
if (FCP_SENSE_REPORTLUN_CHANGED(sense_to)) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!FCP: Report Lun Has Changed"
} else if (FCP_SENSE_NO_LUN(sense_to)) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!FCP: LU Not Supported"
}
}
arq->sts_rqpkt_reason = 0;
arq->sts_rqpkt_statistics = 0;
fcp_trace, FCP_BUF_LEVEL_8, 0,
"SCSI Check condition on cmd=%p target=0x%x"
" LUN=%p, cmd=%x SCSI status=%x, es key=%x"
}
} else {
/*
* Work harder to translate errors into target driver
* understandable ones. Note with despair that the target
* drivers don't decode pkt_state and pkt_reason exhaustively
* They resort to using the big hammer most often, which
* may not get fixed in the life time of this driver.
*/
pkt->pkt_statistics = 0;
case FC_PKT_TRAN_ERROR:
switch (fpkt->pkt_reason) {
case FC_REASON_OVERRUN:
break;
case FC_REASON_XCHG_BSY: {
if (ptr) {
*ptr = STATUS_BUSY;
}
break;
}
case FC_REASON_ABORTED:
break;
case FC_REASON_ABORT_FAILED:
break;
case FC_REASON_NO_SEQ_INIT:
case FC_REASON_CRC_ERROR:
break;
default:
break;
}
break;
case FC_PKT_PORT_OFFLINE: {
fcp_trace, FCP_BUF_LEVEL_8, 0,
"SCSI cmd; LOGIN REQUIRED from FCA for %x",
}
}
if (cdip) {
(void) ndi_event_retrieve_cookie(
(void) ndi_event_run_callbacks(
}
/*
* If the link goes off-line for a lip,
* this will cause a error to the ST SG
* SGEN drivers. By setting BUSY we will
* give the drivers the chance to retry
* before it blows of the job. ST will
* remember how many times it has retried.
*/
if (ptr) {
*ptr = STATUS_BUSY;
}
} else {
}
break;
}
case FC_PKT_TRAN_BSY:
/*
* Use the ssd Qfull handling here.
*/
break;
case FC_PKT_TIMEOUT:
} else {
}
break;
case FC_PKT_LOCAL_RJT:
switch (fpkt->pkt_reason) {
case FC_REASON_OFFLINE: {
}
if (cdip) {
(void) ndi_event_retrieve_cookie(
(void) ndi_event_run_callbacks(
}
break;
}
case FC_REASON_NOMEM:
case FC_REASON_QFULL: {
if (ptr) {
*ptr = STATUS_BUSY;
}
break;
}
case FC_REASON_DMA_ERROR:
break;
case FC_REASON_CRC_ERROR:
case FC_REASON_UNDERRUN: {
/*
* Work around for Bugid: 4240945.
* IB on A5k doesn't set the Underrun bit
* in the fcp status, when it is transferring
* less than requested amount of data. Work
* around the ses problem to keep luxadm
* happy till ibfirmware is fixed.
*/
sizeof (struct fcp_rsp));
}
} else {
}
break;
}
case FC_REASON_NO_CONNECTION:
case FC_REASON_UNSUPPORTED:
case FC_REASON_ILLEGAL_REQ:
case FC_REASON_BAD_SID:
case FC_REASON_DIAG_BUSY:
case FC_REASON_FCAL_OPN_FAIL:
case FC_REASON_BAD_XID:
default:
break;
}
break;
case FC_PKT_NPORT_RJT:
case FC_PKT_FABRIC_RJT:
case FC_PKT_NPORT_BSY:
case FC_PKT_FABRIC_BSY:
default:
fcp_trace, FCP_BUF_LEVEL_8, 0,
"FC Status 0x%x, reason 0x%x",
break;
}
fcp_trace, FCP_BUF_LEVEL_9, 0,
"!FC error on cmd=%p target=0x%x: pkt state=0x%x "
fpkt->pkt_reason);
}
}
static int
{
/*
* These reserved fields should ideally be zero. FCP-2 does say
* that the recipient need not check for reserved fields to be
* zero. If they are not zero, we will not make a fuss about it
* - just log it (in debug to both trace buffer and messages
* file and to trace buffer only in non-debug) and move on.
*
* Non-zero reserved fields were seen with minnows.
*
* qlc takes care of some of this but we cannot assume that all
* FCAs will do so.
*/
FCP_BUF_LEVEL_5, 0,
"Got fcp response packet with non-zero reserved fields "
"rsp->reserved_0:0x%x, rsp_reserved_1:0x%x, "
"status.reserved_0:0x%x, status.reserved_1:0x%x",
}
(FCP_MAX_RSP_IU_SIZE - sizeof (struct fcp_rsp)))) {
return (FC_FAILURE);
}
sizeof (struct fcp_rsp))) {
return (FC_FAILURE);
}
return (FC_SUCCESS);
}
/*
* This is called when there is a change the in device state. The case we're
* handling here is, if the d_id s does not match, offline this tgt and online
* a new tgt with the new d_id. called from fcp_handle_devices with
* port_mutex held.
*/
static int
{
fcp_trace, FCP_BUF_LEVEL_3, 0,
"Starting fcp_device_changed...");
/*
* The two cases where the port_device_changed is called is
* either it changes it's d_id or it's hard address.
*/
/* offline this target */
0, 1, NDI_DEVI_REMOVE);
}
"Change in target properties: Old D_ID=%x New D_ID=%x"
}
}
/*
* Function: fcp_alloc_lun
*
* Description: Creates a new lun structure and adds it to the list
* of luns of the target.
*
* Argument: ptgt Target the lun will belong to.
*
* Return Value: NULL Failed
* Not NULL Succeeded
*
* Context: Kernel context
*/
static struct fcp_lun *
{
/*
* Initialize the mutex before putting in the target list
* especially before releasing the target mutex.
*/
plun->lun_old_guid_size = 0;
}
return (plun);
}
/*
* Function: fcp_dealloc_lun
*
* Description: Frees the LUN structure passed by the caller.
*
* Argument: plun LUN structure to free.
*
* Return Value: None
*
* Context: Kernel context.
*/
static void
{
}
}
if (plun->lun_old_guid) {
}
}
/*
* Function: fcp_alloc_tgt
*
* Description: Creates a new target structure and adds it to the port
* hash list.
*
* Argument: pptr fcp port structure
* *map_entry entry describing the target to create
* link_cnt Link state change counter
*
* Return Value: NULL Failed
* Not NULL Succeeded
*
* Context: Kernel context.
*/
static struct fcp_tgt *
{
int hash;
/*
* oh oh -- another link reset
* in progress -- give up
*/
} else {
/*
* initialize the mutex before putting in the port
* wwn list, especially before releasing the port
* mutex.
*/
/* add new target entry to the port's hash list */
/* save cross-ptr */
/* initialize the target manual_config_only flag */
ptgt->tgt_manual_config_only = 0;
} else {
}
}
}
return (ptgt);
}
/*
* Function: fcp_dealloc_tgt
*
* Description: Frees the target structure passed by the caller.
*
* Argument: ptgt Target structure to free.
*
* Return Value: None
*
* Context: Kernel context.
*/
static void
{
}
/*
* Handle STATUS_QFULL and STATUS_BUSY by performing delayed retry
*
* Device discovery commands will not be retried for-ever as
* this will have repercussions on other devices that need to
* be submitted to the hotplug thread. After a quick glance
* at the SCSI-3 spec, it was found that the spec doesn't
* mandate a forever retry, rather recommends a delayed retry.
*
* Since Photon IB is single threaded, STATUS_BUSY is common
* in a 4+initiator environment. Make sure the total time
* spent on retries (including command timeout) does not
* 60 seconds
*/
static void
{
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_queue_ipkt,1:state change occured"
return;
}
/* add pkt to front of doubly-linked list */
} else {
}
}
/*
* Function: fcp_transport
*
* Description: This function submits the Fibre Channel packet to the transort
* layer by calling fc_ulp_transport(). If fc_ulp_transport()
* fails the submission, the treatment depends on the value of
* the variable internal.
*
* *fpkt Packet to submit to the transport layer.
* internal Not zero when it's an internal packet.
*
* Return Value: FC_TRAN_BUSY
* FC_STATEC_BUSY
* FC_OFFLINE
* FC_LOGINREQ
* FC_DEVICE_BUSY
* FC_SUCCESS
*/
static int
{
int rval;
if (rval == FC_SUCCESS) {
return (rval);
}
/*
* LUN isn't marked BUSY or OFFLINE, so we got here to transport
* a command, if the underlying modules see that there is a state
* change, or if a port is OFFLINE, that means, that state change
* hasn't reached FCP yet, so re-queue the command for deferred
* submission.
*/
/*
* Defer packet re-submission. Life hang is possible on
* internal commands if the port driver sends FC_STATEC_BUSY
* for ever, but that shouldn't happen in a good environment.
* Limiting re-transport for internal commands is probably a
* good idea..
* A race condition can happen when a port sees barrage of
* link transitions offline to online. If the FCTL has
* returned FC_STATEC_BUSY or FC_OFFLINE then none of the
* internal commands should be queued to do the discovery.
* The race condition is when an online comes and FCP starts
* its internal discovery and the link goes offline. It is
* possible that the statec_callback has not reached FCP
* and FCP is carrying on with its internal discovery.
* FC_STATEC_BUSY or FC_OFFLINE will be the first indication
* that the link has gone offline. At this point FCP should
* drop all the internal commands and wait for the
* statec_callback. It will be facilitated by incrementing
* port_link_cnt.
*
* For external commands, the (FC)pkt_timeout is decremented
* by the QUEUE Delay added by our driver, Care is taken to
* ensure that it doesn't become zero (zero means no timeout)
* If the time expires right inside driver queue itself,
* the watch thread will return it to the original caller
* indicating that the command has timed-out.
*/
if (internal) {
char *op;
switch (icmd->ipkt_opcode) {
case SCMD_REPORT_LUN:
op = "REPORT LUN";
break;
case SCMD_INQUIRY:
op = "INQUIRY";
break;
case SCMD_INQUIRY_PAGE83:
op = "INQUIRY-83";
break;
default:
op = "Internal SCSI COMMAND";
break;
}
rval = FC_SUCCESS;
}
} else {
fcp_trace, FCP_BUF_LEVEL_9, 0,
"fcp_transport: xport busy for pkt %p",
rval = FC_TRAN_BUSY;
} else {
rval = FC_SUCCESS;
}
}
}
return (rval);
}
/*VARARGS3*/
static void
{
char buf[256];
}
}
/*
* This function retries NS registry of FC4 type.
* It assumes that fcp_mutex is held.
* The function does nothing if topology is not fabric
* So, the topology has to be set before this function can be called
*/
static void
{
int rval;
}
return;
}
if (rval == 0) {
/* Registry successful. Reset flag */
}
}
/*
* This function registers the ULP with the switch by calling transport i/f
*/
static int
{
/*
* Prepare the Name server structure to
* register with the transport in case of
* Fabric configuration.
*/
ns_cmd.ns_resp_len = 0;
/*
* Perform the Name Server Registration for SCSI_FCP FC4 Type.
*/
"!ns_registry: failed name server registration");
return (1);
}
return (0);
}
/*
* Function: fcp_handle_port_attach
*
* Description: This function is called from fcp_port_attach() to attach a
* new port. This routine does the following:
*
* 1) Allocates an fcp_port structure and initializes it.
* 2) Tries to register the new FC-4 (FCP) capablity with the name
* server.
* through this new port. That is done by calling
* fcp_statec_callback() if the port is online.
*
* *pinfo Port information.
* s_id Port ID.
* instance Device instance number for the local port
* (returned by ddi_get_instance()).
*
* Return Value: DDI_SUCCESS
* DDI_FAILURE
*
* Context: User and Kernel context.
*/
/*ARGSUSED*/
int
{
int res = DDI_FAILURE;
int mutex_initted = FALSE;
int hba_attached = FALSE;
int soft_state_linked = FALSE;
int event_bind = FALSE;
int manual_cfg;
/*
* this port instance attaching for the first time (or after
* being detached before)
*/
instance);
return (res);
}
/* this shouldn't happen */
return (res);
}
/*
* Make a copy of ulp_port_info as fctl allocates
* a temp struct.
*/
/*
* Check for manual_configuration_only property.
* Enable manual configurtion if the property is
* set to 1, otherwise disable manual configuration.
*/
-1)) != -1) {
if (manual_cfg == 1) {
char *pathname;
"%s (%s%d) %s is enabled via %s.conf.",
}
}
sizeof (ddi_dma_cookie_t));
/*
* The two mutexes of fcp_port are initialized. The variable
* mutex_initted is incremented to remember that fact. That variable
* is checked when the routine fails and the mutexes have to be
* destroyed.
*/
/*
* The SCSI tran structure is allocate and initialized now.
*/
"!fcp%d: scsi_hba_tran_alloc failed", instance);
goto fail;
}
/* link in the transport structure then fill it in */
/*
* Allocate an ndi event handle
*/
sizeof (fcp_ndi_event_defs));
goto fail;
}
event_bind++; /* Checked in fail case */
!= DDI_SUCCESS) {
"!fcp%d: scsi_hba_attach_setup failed", instance);
goto fail;
}
hba_attached++; /* Checked in fail case */
pptr->port_mpxio = 0;
MDI_SUCCESS) {
pptr->port_mpxio++;
}
/*
* The following code is putting the new port structure in the global
* list of ports and, if it is the first port to attach, it start the
* fcp_watchdog_tick.
*
* Why put this new port in the global before we are done attaching it?
* We are actually making the structure globally known before we are
* done attaching it. The reason for that is: because of the code that
* follows. At this point the resources to handle the port are
* allocated. This function is now going to do the following:
*
* 1) It is going to try to register with the name server advertizing
* the new FCP capability of the port.
* a list of worlwide names reachable through this port and call
* itself on fcp_statec_callback(). That requires the port to
* be part of the global list.
*/
if (fcp_port_head == NULL) {
}
if (fcp_watchdog_init++ == 0) {
drv_usectohz(1000000);
}
/*
* Here an attempt is made to register with the name server, the new
* FCP capability. That is done using an RTF_ID to the name server.
* It is done synchronously. The function fcp_do_ns_registry()
* doesn't return till the name server responded.
* On failures, just ignore it for now and it will get retried during
* state change callbacks. We'll set a flag to show this failure
*/
} else {
}
/*
* Lookup for boot WWN property
*/
if (modrootloaded != 1) {
(nbytes == FC_WWN_SIZE)) {
}
if (boot_wwn) {
}
}
/*
* Handle various topologies and link states.
*/
case FC_STATE_OFFLINE:
/*
* we're attaching a port where the link is offline
*
* Wait for ONLINE, at which time a state
* change will cause a statec_callback
*
* in the mean time, do not do anything
*/
res = DDI_SUCCESS;
break;
case FC_STATE_ONLINE: {
res = DDI_SUCCESS;
break;
}
/*
* discover devices and create nodes (a private
* loop or point-to-point)
*/
/*
* At this point we are going to build a list of all the ports
* that can be reached through this local port. It looks like
* we cannot handle more than FCP_MAX_DEVICES per local port
* (128).
*/
sizeof (fc_portmap_t) * FCP_MAX_DEVICES,
KM_NOSLEEP)) == NULL) {
"!fcp%d: failed to allocate portmap",
instance);
goto fail;
}
/*
* fc_ulp_getportmap() is going to provide us with the list of
* remote ports in the buffer we just allocated. The way the
* list is going to be retrieved depends on the topology.
* However, if we are connected to a Fabric, a name server
* request may be sent to get the list of FCP capable ports.
* It should be noted that is the case the request is
* synchronous. This means we are stuck here till the name
* server replies. A lot of things can change during that time
* and including, may be, being called on
* fcp_statec_callback() for different reasons. I'm not sure
* the code can handle that.
*/
FC_SUCCESS) {
/*
* this just means the transport is
* busy perhaps building a portmap so,
* for now, succeed this port attach
* when the transport has a new map,
* it'll send us a state change then
*/
"!failed to get port map : %s", msg);
res = DDI_SUCCESS;
break; /* go return result */
}
}
/*
* We are now going to call fcp_statec_callback() ourselves.
* By issuing this call we are trying to kick off the enumera-
* tion process.
*/
/*
* let the state change callback do the SCSI device
* discovery and create the devinfos
*/
res = DDI_SUCCESS;
break;
}
default:
/* unknown port state */
"!fcp%d: invalid port state at attach=0x%x",
res = DDI_SUCCESS;
break;
}
/* free temp list if used */
}
/* note the attach time */
/* all done */
return (res);
/* a failure we have to clean up after */
fail:
if (soft_state_linked) {
/* remove this fcp_port from the linked list */
(void) fcp_soft_state_unlink(pptr);
}
/* unbind and free event set */
if (pptr->port_ndi_event_hdl) {
if (event_bind) {
}
}
if (pptr->port_ndi_event_defs) {
sizeof (fcp_ndi_event_defs));
}
/*
* Clean up mpxio stuff
*/
if (pptr->port_mpxio) {
pptr->port_mpxio--;
}
/* undo SCSI HBA setup */
if (hba_attached) {
}
}
/*
* We check soft_state_linked, because it is incremented right before
* we call increment fcp_watchdog_init. Therefore, we know if
* soft_state_linked is still FALSE, we do not want to decrement
* fcp_watchdog_init or possibly call untimeout.
*/
if (soft_state_linked) {
if (--fcp_watchdog_init == 0) {
} else {
}
} else {
}
if (mutex_initted) {
}
}
/* this makes pptr invalid */
return (DDI_FAILURE);
}
static int
{
int count = 0;
/*
* if the port is powered down or suspended, nothing else
* to do; just return.
*/
if (flag != FCP_STATE_DETACHING) {
return (FC_SUCCESS);
}
}
return (FC_FAILURE);
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_handle_port_detach: port is detaching");
/*
* after that as it was removed from the global port list.
*/
/*
* to complete.
*/
if (count++ >= FCP_ICMD_DEADLINE) {
break;
}
}
/*
* if the driver is still busy then fail to
*/
return (FC_FAILURE);
}
if (flag == FCP_STATE_DETACHING) {
}
pptr->port_link_cnt++;
/* kill watch dog timer if we're the last */
if (--fcp_watchdog_init == 0) {
} else {
}
/* clean up the port structures */
if (flag == FCP_STATE_DETACHING) {
}
return (FC_SUCCESS);
}
static void
{
/* unbind and free event set */
if (pptr->port_ndi_event_hdl) {
}
if (pptr->port_ndi_event_defs) {
sizeof (fcp_ndi_event_defs));
}
/*
* Clean up mpxio stuff
*/
if (pptr->port_mpxio) {
pptr->port_mpxio--;
}
/* clean up SCSA stuff */
}
#ifdef KSTATS_CODE
/* clean up kstats */
}
#endif
/* all done with soft state */
}
/*
* Function: fcp_kmem_cache_constructor
*
* Description: This function allocates and initializes the resources required
* to build a scsi_pkt structure the target driver. The result
* of the allocation and initialization will be cached in the
* memory cache. As DMA resources may be allocated here, that
* means DMA resources will be tied up in the cache manager.
* This is a tradeoff that has been made for performance reasons.
*
* Argument: *buf Memory to preinitialize.
* *arg FCP port structure (fcp_port).
* kmflags Value passed to kmem_cache_alloc() and
* propagated to the constructor.
*
* Return Value: 0 Allocation/Initialization was successful.
* -1 Allocation or Initialization failed.
*
*
* If the returned value is 0, the buffer is initialized like this:
*
* +================================+
* +----> | struct scsi_pkt |
* | | |
* | +--- | pkt_ha_private |
* | | | |
* | | +================================+
* | |
* | | +================================+
* | +--> | struct fcp_pkt | <---------+
* | | | |
* +----- | cmd_pkt | |
* | cmd_fp_pkt | ---+ |
* +-------->| cmd_fcp_rsp[] | | |
* | +--->| cmd_fcp_cmd[] | | |
* | | |--------------------------------| | |
* | | | struct fc_packet | <--+ |
* | | | | |
* | | | pkt_ulp_private | ----------+
* | | | pkt_fca_private | -----+
* | | | pkt_data_cookie | ---+ |
* | | | pkt_cmdlen | | |
* | |(a) | pkt_rsplen | | |
* | +----| .......... pkt_cmd ........... | ---|-|---------------+
* | (b) | pkt_cmd_cookie | ---|-|----------+ |
* +---------| .......... pkt_resp .......... | ---|-|------+ | |
* | pkt_resp_cookie | ---|-|--+ | | |
* | pkt_cmd_dma | | | | | | |
* | pkt_cmd_acc | | | | | | |
* +================================+ | | | | | |
* | dma_cookies | <--+ | | | | |
* | | | | | | |
* +================================+ | | | | |
* | fca_private | <----+ | | | |
* | | | | | |
* +================================+ | | | |
* | | | |
* | | | |
* +================================+ (d) | | | |
* | fcp_resp cookies | <-------+ | | |
* | | | | |
* +================================+ | | |
* | | |
* +================================+ (d) | | |
* | fcp_resp | <-----------+ | |
* | (DMA resources associated) | | |
* +================================+ | |
* | |
* | |
* | |
* +================================+ (c) | |
* | fcp_cmd cookies | <---------------+ |
* | | |
* +================================+ |
* |
* +================================+ (c) |
* | fcp_cmd | <--------------------+
* | (DMA resources associated) |
* +================================+
*
* (a) Only if DMA is NOT used for the FCP_CMD buffer.
* (b) Only if DMA is NOT used for the FCP_RESP buffer
* (c) Only if DMA is used for the FCP_CMD buffer.
* (d) Only if DMA is used for the FCP_RESP buffer
*/
static int
int kmflags)
{
sizeof (struct fcp_pkt));
/*
* The underlying HBA doesn't want to DMA the fcp_cmd or
* fcp_resp. The transfer of information will be done by
* bcopy.
* The naming of the flags (that is actually a value) is
* unfortunate. FC_NO_DVMA_SPACE doesn't mean "NO VIRTUAL
* DMA" but instead "NO DMA".
*/
} else {
/*
* The underlying HBA will dma the fcp_cmd buffer and fcp_resp
* buffer. A buffer is allocated for each one the ddi_dma_*
* interfaces.
*/
return (-1);
}
}
return (0);
}
/*
* Function: fcp_kmem_cache_destructor
*
* Description: Called by the destructor of the cache managed by SCSA.
* All the resources pre-allocated in fcp_pkt_constructor
* and the data also pre-initialized in fcp_pkt_constructor
* are freed and uninitialized here.
*
* Argument: *buf Memory to uninitialize.
* *arg FCP port structure (fcp_port).
*
* Return Value: None
*
* Context: kernel
*/
static void
{
/*
* If DMA was used to transfer the FCP_CMD and FCP_RESP, the
* buffer and DMA resources allocated to do so are released.
*/
}
}
/*
* Function: fcp_alloc_cmd_resp
*
* Description: This function allocated an FCP_CMD and FCP_RESP buffer that
* will be DMAed by the HBA. The buffer is allocated applying
* the DMA requirements for the HBA. The buffers allocated will
* also be bound. DMA resources are allocated in the process.
* They will be released by fcp_free_cmd_resp().
*
* Argument: *pptr FCP port.
* *fpkt fc packet for which the cmd and resp packet should be
* allocated.
* flags Allocation flags.
*
* Return Value: FC_FAILURE
* FC_SUCCESS
*
* Context: User or Kernel context only if flags == KM_SLEEP.
* Interrupt context if the KM_SLEEP is not specified.
*/
static int
{
int rval;
int cmd_len;
int resp_len;
/* Allocation of a DMA handle used in subsequent calls. */
return (FC_FAILURE);
}
/* A buffer is allocated that satisfies the DMA requirements. */
if (rval != DDI_SUCCESS) {
return (FC_FAILURE);
}
return (FC_FAILURE);
}
/* The buffer allocated is DMA bound. */
if (rval != DDI_DMA_MAPPED) {
return (FC_FAILURE);
}
if (fpkt->pkt_cmd_cookie_cnt >
return (FC_FAILURE);
}
/*
* allocated.
*/
return (FC_FAILURE);
}
/*
* here.
*/
*cp = pkt_cookie;
cp++;
&pkt_cookie);
*cp = pkt_cookie;
}
return (FC_FAILURE);
}
&fpkt->pkt_resp_acc);
if (rval != DDI_SUCCESS) {
return (FC_FAILURE);
}
return (FC_FAILURE);
}
if (rval != DDI_DMA_MAPPED) {
return (FC_FAILURE);
}
if (fpkt->pkt_resp_cookie_cnt >
return (FC_FAILURE);
}
return (FC_FAILURE);
}
*cp = pkt_cookie;
cp++;
&pkt_cookie);
*cp = pkt_cookie;
}
return (FC_SUCCESS);
}
/*
* Function: fcp_free_cmd_resp
*
* Description: This function releases the FCP_CMD and FCP_RESP buffer
* allocated by fcp_alloc_cmd_resp() and all the resources
* associated with them. That includes the DMA resources and the
* buffer allocated for the cookies of each one of them.
*
* Argument: *pptr FCP port context.
* *fpkt fc packet containing the cmd and resp packet
* to be released.
*
* Return Value: None
*
* Context: Interrupt, User and Kernel context.
*/
/* ARGSUSED */
static void
{
if (fpkt->pkt_resp_dma) {
}
if (fpkt->pkt_resp_cookie) {
}
if (fpkt->pkt_cmd_dma) {
}
if (fpkt->pkt_cmd_cookie) {
}
}
/*
* called by the transport to do our own target initialization
*
* can acquire and release the global mutex
*/
/* ARGSUSED */
static int
{
int *words;
FCP_BUF_LEVEL_8, 0,
"fcp_phys_tgt_init: called for %s (instance %d)",
/* get our port WWN property */
/* no port WWN property */
FCP_BUF_LEVEL_8, 0,
"fcp_phys_tgt_init: Returning DDI_NOT_WELL_FORMED"
" for %s (instance %d): bytes=%p nbytes=%x",
nbytes);
}
return (DDI_NOT_WELL_FORMED);
}
FCP_BUF_LEVEL_8, 0,
"fcp_phys_tgt_init: Returning DDI_FAILURE:lun"
return (DDI_NOT_WELL_FORMED);
}
if (nwords == 0) {
return (DDI_NOT_WELL_FORMED);
}
FCP_BUF_LEVEL_8, 0,
"fcp_phys_tgt_init: Returning DDI_FAILURE: No Lun"
return (DDI_FAILURE);
}
FC_WWN_SIZE) == 0);
plun->lun_tgt_count++;
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
int words;
fcp_trace, FCP_BUF_LEVEL_8, 0,
"fcp_virt_tgt_init: called for %s (instance %d) (hba_dip %p),"
fcp_trace, FCP_BUF_LEVEL_8, 0,
"fcp_virt_tgt_init: Returning DDI_NOT_WELL_FORMED"
return (DDI_NOT_WELL_FORMED);
}
/* get our port WWN property */
if (bytes) {
(void) mdi_prop_free(bytes);
}
return (DDI_NOT_WELL_FORMED);
}
words = 0;
fcp_trace, FCP_BUF_LEVEL_8, 0,
"fcp_virt_tgt_init: Returning DDI_FAILURE:lun"
(void) mdi_prop_free(bytes);
return (DDI_NOT_WELL_FORMED);
}
fcp_trace, FCP_BUF_LEVEL_8, 0,
"fcp_virt_tgt_init: Returning DDI_FAILURE: No Lun"
(void) mdi_prop_free(bytes);
(void) mdi_prop_free(&words);
return (DDI_FAILURE);
}
FC_WWN_SIZE) == 0);
(void) mdi_prop_free(bytes);
(void) mdi_prop_free(&words);
plun->lun_tgt_count++;
return (DDI_SUCCESS);
}
/*
* called by the transport to do our own target initialization
*
* can acquire and release the global mutex
*/
/* ARGSUSED */
static int
{
int rval;
/*
* Child node is getting initialized. Look at the mpxio component
* type on the child device to see if this device is mpxio managed
* or not.
*/
} else {
}
return (rval);
}
/* ARGSUSED */
static void
{
fcp_trace, FCP_BUF_LEVEL_8, 0,
"fcp_scsi_tgt_free: called for tran %s%d, dev %s%d",
return;
}
if (--plun->lun_tgt_count == 0) {
}
}
/*
* Function: fcp_scsi_start
*
* Description: This function is called by the target driver to request a
* command to be sent.
*
* Argument: *ap SCSI address of the device.
* *pkt SCSI packet containing the cmd to send.
*
* Return Value: TRAN_ACCEPT
* TRAN_BUSY
* TRAN_BADPKT
* TRAN_FATAL_ERROR
*/
static int
{
int rval;
/* ensure command isn't already issued */
fcp_trace, FCP_BUF_LEVEL_9, 0,
/*
* It is strange that we enter the fcp_port mutex and the target
* mutex to check the lun state (which has a mutex of its own).
*/
/*
* If the device is offline and is not in the process of coming
* online, fail the request.
*/
return (TRAN_FATAL_ERROR);
}
/*
* If we are suspended, kernel is trying to dump, so don't
* block, fail or defer requests - send them down right away.
* NOTE: If we are in panic (i.e. trying to dump), we can't
* assume we have been suspended. There is hardware such as
* the v880 that doesn't do PM. Thus, the check for
* ddi_in_panic.
*
* If FCP_STATE_IN_CB_DEVC is set, devices are in the process
* of changing. So, if we can queue the packet, do it. Eventually,
* either the device will have gone away or changed and we can fail
* the request, or we can proceed if the device didn't change.
*
* If the pd in the target or the packet is NULL it's probably
* because the device has gone away, we allow the request to be
* put on the internal queue here in case the device comes back within
* the offline timeout. fctl will fix up the pd's if the tgt_pd_handle
* has gone NULL, while fcp deals cases where pkt_pd is NULL. pkt_pd
* could be NULL because the device was disappearing during or since
* packet initialization.
*/
FCP_STATE_SUSPENDED)) && !ddi_in_panic()) ||
/*
* If ((LUN is busy AND
* LUN not suspended AND
* The system is not in panic state) OR
* (The port is coming up))
*
* We check to see if the any of the flags FLAG_NOINTR or
* FLAG_NOQUEUE is set. If one of them is set the value
* returned will be TRAN_BUSY. If not, the request is queued.
*/
/* see if using interrupts is allowed (so queueing'll work) */
return (TRAN_BUSY);
}
fcp_trace, FCP_BUF_LEVEL_9, 0,
"fcp_scsi_start: lun busy for pkt %p", pkt);
return (TRAN_BUSY);
}
#ifdef DEBUG
pptr->port_npkts++;
#endif /* DEBUG */
/* got queue up the pkt for later */
return (TRAN_ACCEPT);
}
/*
* Now that we released the mutexes, what was protected by them can
* change.
*/
/*
* If there is a reconfiguration in progress, wait for it to complete.
*/
/* prepare the packet */
} else {
}
/*
* if interrupts aren't allowed (e.g. at dump time) then we'll
* have to do polled I/O
*/
}
#ifdef DEBUG
pptr->port_npkts++;
#endif /* DEBUG */
if (rval == FC_SUCCESS) {
fcp_trace, FCP_BUF_LEVEL_9, 0,
return (TRAN_ACCEPT);
}
#ifdef DEBUG
pptr->port_npkts--;
#endif /* DEBUG */
/*
* For lack of clearer definitions, choose
* between TRAN_BUSY and TRAN_FATAL_ERROR.
*/
if (rval == FC_TRAN_BUSY) {
} else {
fcp_trace, FCP_BUF_LEVEL_6, 0,
"fcp_transport failed 2 for %x: %x; dip=%p",
} else {
fcp_trace, FCP_BUF_LEVEL_9, 0,
"fcp_scsi_start: FC_BUSY for pkt %p",
pkt);
} else {
rval = TRAN_ACCEPT;
}
}
}
return (rval);
}
/*
* called by the transport to abort a packet
*/
/*ARGSUSED*/
static int
{
int tgt_cnt;
if (ptgt) {
return (TRUE);
}
}
return (FALSE);
}
/*
* Perform reset
*/
int
{
int rval = 0;
rval = 1;
}
/*
* If we are in the middle of discovery, return
* SUCCESS as this target will be rediscovered
* anyway
*/
return (1);
}
rval = 1;
}
}
return (rval);
}
/*
* called by the framework to get a SCSI capability
*/
static int
{
}
/*
* called by the framework to set a SCSI capability
*/
static int
{
}
/*
* Function: fcp_pkt_setup
*
* Description: This function sets up the scsi_pkt structure passed by the
* caller. This function assumes fcp_pkt_constructor has been
* called previously for the packet passed by the caller. If
* successful this call will have the following results:
*
* - The resources needed that will be constant through out
* the whole transaction are allocated.
* - The fields that will be constant through out the whole
* transaction are initialized.
* - The scsi packet will be linked to the LUN structure
* addressed by the transaction.
*
* Argument:
* *pkt Pointer to a scsi_pkt structure.
* callback
* arg
*
* Return Value: 0 Success
* !0 Failure
*
* Context: Kernel context or interrupt context
*/
/* ARGSUSED */
static int
{
int kf;
/*
* this request is for dma allocation only
*/
/*
* First step of fcp_scsi_init_pkt: pkt allocation
* We determine if the caller is willing to wait for the
* resources.
*/
/*
* Selective zeroing of the pkt.
*/
/*
* Zero out fcp command
*/
!= FC_SUCCESS) {
return (-1);
}
/* Fill in the Fabric Channel Header */
/*
* A doubly linked list (cmd_forw, cmd_back) is built
* out of every allocated packet on a per-lun basis
*
* The packets are maintained in the list so as to satisfy
* scsi_abort() requests. At present (which is unlikely to
* change in the future) nobody performs a real scsi_abort
* in the SCSI target drivers (as they don't keep the packets
* after doing scsi_transport - so they don't know how to
* abort a packet other than sending a NULL to abort all
* outstanding packets)
*/
} else {
}
return (0);
}
/*
* Function: fcp_pkt_teardown
*
* Description: This function releases a scsi_pkt structure and all the
* resources attached to it.
*
* Argument: *pkt Pointer to a scsi_pkt structure.
*
* Return Value: None
*
* Context: User, Kernel or Interrupt context.
*/
static void
{
/*
* Remove the packet from the per-lun list
*/
} else {
}
} else {
}
}
/*
* Routine for reset notification setup, to register or cancel.
* This function is called by SCSA
*/
/*ARGSUSED*/
static int
{
}
static int
{
return (DDI_FAILURE);
}
}
static int
{
return (DDI_FAILURE);
}
}
static int
{
return (DDI_FAILURE);
}
}
/*
* called by the transport to post an event
*/
static int
{
return (DDI_FAILURE);
}
}
/*
* A target in in many cases in Fibre Channel has a one to one relation
* with a port identifier (which is also known as D_ID and also as AL_PA
* in private Loop) On Fibre Channel-to-SCSI bridge boxes a target reset
* will most likely result in resetting all LUNs (which means a reset will
* occur on all the SCSI devices connected at the other end of the bridge)
* That is the latest favorite topic for discussion, for, one can debate as
* hot as one likes and come up with arguably a best solution to one's
* satisfaction
*
* To stay on track and not digress much, here are the problems stated
* briefly:
*
* SCSA doesn't define RESET_LUN, It defines RESET_TARGET, but the
* target drivers use RESET_TARGET even if their instance is on a
* LUN. Doesn't that sound a bit broken ?
*
* FCP SCSI (the current spec) only defines RESET TARGET in the
* control fields of an FCP_CMND structure. It should have been
* fixed right there, giving flexibility to the initiators to
* minimize havoc that could be caused by resetting a target.
*/
static int
{
int rval = FC_FAILURE;
char lun_id[25];
struct fcp_rsp_info *rsp_info;
struct fcp_reset_elem *p;
int bval;
if ((p = kmem_alloc(sizeof (struct fcp_reset_elem),
KM_NOSLEEP)) == NULL) {
return (rval);
}
if (level == RESET_TARGET) {
kmem_free(p, sizeof (struct fcp_reset_elem));
return (rval);
}
} else {
kmem_free(p, sizeof (struct fcp_reset_elem));
return (rval);
}
}
kmem_free(p, sizeof (struct fcp_reset_elem));
return (rval);
}
/* fill in cmd part of packet */
if (level == RESET_TARGET) {
} else {
}
/* prepare a packet for transport */
} else {
}
/* submit the packet */
if (bval == TRAN_ACCEPT) {
int error = 3;
sizeof (struct fcp_rsp));
FC_SUCCESS) {
sizeof (struct fcp_rsp_info));
}
rval = FC_SUCCESS;
error = 0;
} else {
error = 1;
}
} else {
error = 2;
}
}
switch (error) {
case 0:
"!FCP: WWN 0x%08x%08x %s reset successfully",
break;
case 1:
"!FCP: Reset to WWN 0x%08x%08x %s failed,"
" response code=%x",
break;
case 2:
"!FCP: Reset to WWN 0x%08x%08x %s failed,"
" Bad FCP response values: rsvd1=%x,"
" rsvd2=%x, sts-rsvd1=%x, sts-rsvd2=%x,"
" rsplen=%x, senselen=%x",
break;
default:
"!FCP: Reset to WWN 0x%08x%08x %s failed",
break;
}
}
if (rval == FC_FAILURE) {
if (level == RESET_TARGET) {
} else {
}
kmem_free(p, sizeof (struct fcp_reset_elem));
return (rval);
}
if (level == RESET_TARGET) {
} else {
}
pptr->port_reset_list = p;
fcp_trace, FCP_BUF_LEVEL_3, 0,
"Notify ssd of the reset to reinstate the reservations");
return (rval);
}
/*
* called by fcp_getcap and fcp_setcap to get and set (respectively)
* SCSI capabilities
*/
/* ARGSUSED */
static int
{
int cidx;
if (cap == (char *)0) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_commoncap: invalid arg");
return (rval);
}
return (UNDEFINED);
}
/*
* Process setcap request.
*/
if (doset) {
/*
* At present, we can only set binary (0/1) values
*/
switch (cidx) {
case SCSI_CAP_ARQ:
if (val == 0) {
} else {
}
break;
case SCSI_CAP_LUN_RESET:
if (val) {
} else {
}
break;
case SCSI_CAP_SECTOR_SIZE:
break;
default:
fcp_trace, FCP_BUF_LEVEL_4, 0,
"fcp_setcap: unsupported %d", cidx);
break;
}
fcp_trace, FCP_BUF_LEVEL_5, 0,
"0x%x/0x%x/0x%x/%d",
} else {
/*
* Process getcap request.
*/
switch (cidx) {
case SCSI_CAP_DMA_MAX:
/*
* Need to make an adjustment qlc is uint_t 64
* st is int, so we will make the adjustment here
* being as nobody wants to touch this.
* It still leaves the max single block length
* of 2 gig. This should last .
*/
if (rval == -1) {
rval = MAX_INT_DMA;
}
break;
case SCSI_CAP_INITIATOR_ID:
break;
case SCSI_CAP_ARQ:
case SCSI_CAP_TAGGED_QING:
break;
case SCSI_CAP_SCSI_VERSION:
rval = 3;
break;
(ptgt->tgt_hard_addr == 0)) {
} else {
}
break;
case SCSI_CAP_LUN_RESET:
break;
default:
fcp_trace, FCP_BUF_LEVEL_4, 0,
"fcp_getcap: unsupported %d", cidx);
break;
}
fcp_trace, FCP_BUF_LEVEL_8, 0,
"0x%x/0x%x/0x%x/%d",
}
return (rval);
}
/*
* called by the transport to get the port-wwn and lun
* properties of this device, and to create a "name" based on them
*
* these properties don't exist on sun4m
*
* return 1 for success else return 0
*/
/* ARGSUSED */
static int
{
int i;
int *lun;
int numChars;
char **conf_wwn;
return (0);
}
name[0] = '\0';
return (0);
}
if (nlun == 0) {
return (0);
}
/*
* Lookup for .conf WWN property
*/
return (0);
}
ptgt->tgt_hard_addr != 0) {
} else {
}
}
/* get the our port-wwn property */
}
return (0);
}
for (i = 0; i < FC_WWN_SIZE; i++) {
}
/* Stick in the address of the form "wWWN,LUN" */
"!fcp_scsi_get_name: "
"name parameter length too small, it needs to be %d",
numChars+1);
}
return (1);
}
/*
* called by the transport to get the SCSI target id value, returning
* it in "name"
*
*
* return 1 for success else return 0
*/
/* ARGSUSED */
static int
{
int numChars;
return (0);
}
return (0);
}
"!fcp_scsi_get_bus_addr: "
"name parameter length too small, it needs to be %d",
numChars+1);
}
return (1);
}
/*
* called internally to reset the link where the specified port lives
*/
static int
{
/* disable restart of lip if we're suspended */
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_linkreset, fcp%d: link reset "
"disabled due to DDI_SUSPEND",
return (FC_FAILURE);
}
return (FC_SUCCESS);
}
/*
* If ap == NULL assume local link reset.
*/
} else {
}
}
/*
* called from fcp_port_attach() to resume a port
* acquires and releases the global mutex
* acquires and releases the port mutex
*/
/*ARGSUSED*/
static int
{
FCP_BUF_LEVEL_8, 0, "port resume: for port %d",
instance);
return (res);
}
switch (cmd) {
case FC_CMD_RESUME:
break;
case FC_CMD_POWER_UP:
/*
* If the port is DDI_SUSPENded, defer rediscovery
* until DDI_RESUME occurs
*/
return (DDI_SUCCESS);
}
}
/*
* Make a copy of ulp_port_info as fctl allocates
* a temp struct.
*/
if (fcp_watchdog_init++ == 0) {
drv_usectohz(1000000);
}
/*
* Handle various topologies and link states.
*/
case FC_STATE_OFFLINE:
/*
* Wait for ONLINE, at which time a state
* change will cause a statec_callback
*/
res = DDI_SUCCESS;
break;
case FC_STATE_ONLINE:
res = DDI_SUCCESS;
break;
}
if (!alloc_cnt) {
res = DDI_SUCCESS;
}
break;
}
} else {
(sizeof (fc_portmap_t)) * alloc_cnt,
KM_NOSLEEP)) == NULL) {
"!fcp%d: failed to allocate portmap",
instance);
break;
}
FC_SUCCESS) {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"resume failed getportmap: reason=0x%x",
res);
"!failed to get port map : %s", msg);
break;
}
}
}
/*
* do the SCSI device discovery and create
* the devinfos
*/
res = DDI_SUCCESS;
break;
default:
"!fcp%d: invalid port state at attach=0x%x",
res = DDI_SUCCESS;
break;
}
}
return (res);
}
static void
{
}
/*
* If the elements wait field is set to 1 then
* another thread is waiting for the operation to complete. Once
* it is complete, the waiting thread is signaled and the element is
* freed by the waiting thread. If the elements wait field is set to 0
* the element is freed.
*/
static void
{
} else {
}
}
/*
* This function is invoked from the taskq thread to allocate
*/
static void
fcp_hp_task(void *arg)
{
int result;
return;
}
}
static child_info_t *
int tcount)
{
/*
* Child has not been created yet. Create the child device
* based on the per-Lun flags.
*/
} else {
}
} else {
}
}
static int
{
int rval = FC_FAILURE;
int circular;
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_is_dip_present: plun->lun_cip is NULL: "
"plun: %p lun state: %x num: %d target state: %x",
return (rval);
}
while (dip) {
rval = FC_SUCCESS;
break;
}
}
return (rval);
}
static int
{
int rval = FC_FAILURE;
} else {
}
return (rval);
}
/*
* Function: fcp_create_dip
*
* Description: Creates a dev_info_t structure for the LUN specified by the
* caller.
*
* Argument: plun Lun structure
* link_cnt Link state count.
* tgt_cnt Target state change count.
*
* Return Value: NULL if it failed
* dev_info_t structure address if it succeeded
*
* Context: Kernel context
*/
static dev_info_t *
{
int failure = 0;
char **compatible = NULL;
int ncompatible;
char *scsi_binding_set;
char t_pwwn[17];
/* get the 'scsi-binding-set' property */
/* determine the node name and compatible */
if (scsi_binding_set)
#ifdef DEBUG
"device @w%02x%02x%02x%02x%02x%02x%02x%02x,%d:"
" compatible: %s",
*compatible);
#endif /* DEBUG */
failure++;
goto end_of_fcp_create_dip;
}
/*
* if the old_dip does not match the cdip, that means there is
* some property change. since we'll be using the cdip, we need
* to offline the old_dip. If the state contains FCP_LUN_CHANGED
* then the dtype for the device has been updated. Offline the
* the old device and create a new device with the new device type
* Refer to bug: 4764752
*/
#ifdef DEBUG
fcp_trace, FCP_BUF_LEVEL_2, 0,
"Old dip=%p; New dip=%p don't match", old_dip,
cdip);
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"Old dip=%p; New dip=NULL don't match", old_dip);
}
#endif
}
failure++;
goto end_of_fcp_create_dip;
}
}
/*
* Previously all the properties for the devinfo were destroyed here
* with a call to ndi_prop_remove_all(). Since this may cause loss of
* the devid property (and other properties established by the target
* driver or framework) which the code does not always recreate, this
* call was removed.
* This opens a theoretical possibility that we may return with a
* stale devid on the node if the scsi entity behind the fibre channel
* lun has changed.
*/
/* decorate the node with compatible */
failure++;
goto end_of_fcp_create_dip;
}
failure++;
goto end_of_fcp_create_dip;
}
failure++;
goto end_of_fcp_create_dip;
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_dip;
}
/*
* If there is no hard address - We might have to deal with
* that by using WWN - Having said that it is important to
* recognize this problem early so ssd can be informed of
* the right interconnect type.
*/
} else {
}
tgt_id) != DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_dip;
}
failure++;
goto end_of_fcp_create_dip;
}
sam_lun) != DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_dip;
}
(void) ndi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
}
return (cdip);
}
/*
* Function: fcp_create_pip
*
* Description: Creates a Path Id for the LUN specified by the caller.
*
* Argument: plun Lun structure
* link_cnt Link state count.
* tgt_cnt Target state count.
*
* Return Value: NULL if it failed
* mdi_pathinfo_t structure address if it succeeded
*
* Context: Kernel context
*/
static mdi_pathinfo_t *
{
int i;
char buf[MAXNAMELEN];
char uaddr[MAXNAMELEN];
int failure = 0;
char **compatible = NULL;
int ncompatible;
char *scsi_binding_set;
char t_pwwn[17];
scsi_binding_set = "vhci";
/* determine the node name and compatible */
#ifdef DEBUG
"device @w%02x%02x%02x%02x%02x%02x%02x%02x,%d:"
" compatible: %s",
*compatible);
#endif /* DEBUG */
failure++;
goto end_of_fcp_create_pip;
}
/*
* if the old_dip does not match the cdip, that means there is
* some property change. since we'll be using the cdip, we need
* to offline the old_dip. If the state contains FCP_LUN_CHANGED
* then the dtype for the device has been updated. Offline the
* the old device and create a new device with the new device type
* Refer to bug: 4764752
*/
NDI_DEVI_REMOVE, 0);
fcp_trace, FCP_BUF_LEVEL_2, 0,
"Old pip=%p; New pip=%p don't match",
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"Old pip=%p; New pip=NULL don't match",
old_pip);
}
}
/*
* Since FC_WWN_SIZE is 8 bytes and its not like the
* lun_guid_size which is dependent on the target, I don't
* believe the same trancation happens here UNLESS the standards
* change the FC_WWN_SIZE value to something larger than
* MAXNAMELEN(currently 255 bytes).
*/
for (i = 0; i < FC_WWN_SIZE; i++)
/*
* Release the locks before calling into
* mdi_pi_alloc_compatible() since this can result in a
* callback into fcp which can result in a deadlock
* (see bug # 4870272).
*
* Basically, what we are trying to avoid is the scenario where
* one thread does ndi_devi_enter() and tries to grab
* fcp_mutex and another does it the other way round.
*
* But before we do that, make sure that nobody releases the
* port in the meantime. We can do this by setting a flag.
*/
"!path alloc failed:0x%x", plun);
failure++;
goto end_of_fcp_create_pip;
}
} else {
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_pip;
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_pip;
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_pip;
}
/*
* If there is no hard address - We might have to deal with
* that by using WWN - Having said that it is important to
* recognize this problem early so ssd can be informed of
* the right interconnect type.
*/
ptgt->tgt_hard_addr != 0) {
} else {
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_pip;
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_pip;
}
!= DDI_PROP_SUCCESS) {
failure++;
goto end_of_fcp_create_pip;
}
(void) mdi_pi_free(pip, 0);
}
return (pip);
}
static dev_info_t *
{
int *words;
int circular;
continue;
}
&nbytes) != DDI_PROP_SUCCESS) {
continue;
}
}
continue;
}
continue;
}
&nbytes) != DDI_PROP_SUCCESS) {
continue;
}
}
continue;
}
continue;
}
&nwords) != DDI_PROP_SUCCESS) {
continue;
}
}
continue;
}
/*
* If there is no hard address - We might have to deal with
* that by using WWN - Having said that it is important to
* recognize this problem early so ssd can be informed of
* the right interconnect type.
*/
ptgt->tgt_hard_addr != 0) {
tgt_id =
} else {
}
continue;
}
&nwords) != DDI_PROP_SUCCESS) {
continue;
}
}
continue;
}
break;
}
}
return (cdip);
}
static int
{
char buf[MAXNAMELEN];
char uaddr[MAXNAMELEN];
int rval = FC_FAILURE;
/*
* Check if pip (and not plun->lun_cip) is NULL. plun->lun_cip can be
* non-NULL even when the LUN is not there as in the case when a LUN is
* such cases, pip will be NULL.
*
* If the device generates an RSCN, it will end up getting offlined when
* it disappeared and a new LUN will get created when it is rediscovered
* on the device. If we check for lun_cip here, the LUN will not end
* up getting onlined since this function will end up returning a
* FC_SUCCESS.
*
* The behavior is different on other devices. For instance, on a HDS,
* there was no RSCN generated by the device but the next I/O generated
* a check condition and rediscovery got triggered that way. So, in
* such cases, this path will not be exercised
*/
fcp_trace, FCP_BUF_LEVEL_4, 0,
"fcp_is_pip_present: plun->lun_cip is NULL: "
"plun: %p lun state: %x num: %d target state: %x",
return (rval);
}
if (plun->lun_old_guid) {
rval = FC_SUCCESS;
}
} else {
rval = FC_SUCCESS;
}
}
return (rval);
}
static mdi_pathinfo_t *
{
char buf[MAXNAMELEN];
char uaddr[MAXNAMELEN];
return (pip);
}
static int
{
int rval;
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_online_child: plun->lun_cip is NULL: "
"plun: %p state: %x num: %d target state: %x",
return (NDI_FAILURE);
}
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!Invoking ndi_devi_online for %s: target=%x lun=%x",
/*
* We could check for FCP_LUN_INIT here but chances
* of getting here when it's already in FCP_LUN_INIT
* is rare and a duplicate ndi_devi_online wouldn't
* hurt either (as the node would already have been
* in CF2)
*/
else
/*
* We log the message into trace buffer if the device
* is "ses" and into syslog for any other device
* type. This is to prevent the ndi_devi_online failure
*/
if (rval == NDI_SUCCESS) {
"!ndi_devi_online:"
" failed for %s: target=%x lun=%x %x",
} else {
fcp_trace, FCP_BUF_LEVEL_3, 0,
" !ndi_devi_online:"
" failed for %s: target=%x lun=%x %x",
}
} else {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"!Invoking mdi_pi_online for %s: target=%x lun=%x",
/*
* Hold path and exit phci to avoid deadlock with power
* management code during mdi_pi_online.
*/
if (rval == MDI_SUCCESS) {
/*
* Clear MPxIO path permanent disable in case
* fcp hotplug dropped the offline event.
*/
} else if (rval == MDI_NOT_SUPPORTED) {
/*
* MPxIO does not support this device yet.
* Enumerate in legacy mode.
*/
"!fcp_online_child: "
"Create devinfo failed for LU=%p", plun);
/*
* free the mdi_pathinfo node
*/
} else {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_online_child: creating devinfo "
"node 0x%p for plun 0x%p",
/*
* free the mdi_pathinfo node
*/
goto again;
}
} else {
if (cdip) {
"!fcp_online_child: mdi_pi_online:"
" failed for %s: target=%x lun=%x %x",
}
}
}
if (rval == NDI_SUCCESS) {
if (cdip) {
(void) ndi_event_retrieve_cookie(
}
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval;
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_offline_child: plun->lun_cip is NULL: "
"plun: %p lun state: %x num: %d target state: %x",
return (NDI_FAILURE);
}
if (rval != NDI_SUCCESS) {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_offline_child: ndi_devi_offline failed "
}
} else {
/*
* Exit phci to avoid deadlock with power management code
* during mdi_pi_offline
*/
if (rval == MDI_SUCCESS) {
/*
* Clear MPxIO path permanent disable as the path is
* already offlined.
*/
if (flags & NDI_DEVI_REMOVE) {
}
} else {
fcp_trace, FCP_BUF_LEVEL_3, 0,
"fcp_offline_child: mdi_pi_offline failed "
}
}
if (rval == NDI_SUCCESS) {
if (flags & NDI_DEVI_REMOVE) {
/*
* If the guid of the LUN changes, lun_cip will not
* equal to cip, and after offlining the LUN with the
* old guid, we should keep lun_cip since it's the cip
* of the LUN with the new guid.
* Otherwise remove our reference to child node.
*/
if (plun->lun_old_guid) {
plun->lun_old_guid_size = 0;
}
}
}
if (cdip) {
" target=%x lun=%x", "ndi_offline",
}
return (rval);
}
static void
{
} else {
fcp_trace, FCP_BUF_LEVEL_3, 0,
}
}
}
/*
* called when a timeout occurs
*
* can be scheduled during an attach or resume (if not already running)
*
* one timeout is set up for all ports
*
* acquires and releases the global mutex
*/
/*ARGSUSED*/
static void
{
/* increment global watchdog time */
/* scan each port in our list */
goto end_of_watchdog;
}
/*
* We check if a list of targets need to be offlined.
*/
if (pptr->port_offline_tgts) {
}
/*
* We check if a list of luns need to be offlined.
*/
if (pptr->port_offline_luns) {
}
/*
* We check if a list of targets or luns need to be reset.
*/
if (pptr->port_reset_list) {
}
/*
* This is where the pending commands (pkt) are checked for
* timeout.
*/
/*
* If a command is in this queue the bit CFLAG_IN_QUEUE
* must be set.
*/
/*
* FCP_INVALID_TIMEOUT will be set for those
* command that need to be failed. Mostly those
* cmds that could not be queued down for the
* "timeout" value. cmd->cmd_timeout is used
* to try and requeue the command regularly.
*/
/*
* This command hasn't timed out yet. Let's
* go to the next one.
*/
goto end_of_loop;
}
} else {
}
if (pcmd)
}
/*
* save the current head before dropping the
* mutex - If the head doesn't remain the
* same after re acquiring the mutex, just
* bail out and revisit on next tick.
*
* PS: The tail pointer can change as the commands
* get requeued after failure to retransport
*/
fcp_trace, FCP_BUF_LEVEL_2, 0,
"SCSI cmd 0x%x to D_ID=%x timed out",
} else {
}
/*
* Looks like linked list got changed (mostly
* happens when an an OFFLINE LUN code starts
* returning overflow queue commands in
* parallel. So bail out and revisit during
* next tick
*/
break;
}
/*
* Scan only upto the previously known tail pointer
* to avoid excessive processing - lots of new packets
* could have been added to the tail or the old ones
* re-queued.
*/
break;
}
}
if ((icmd->ipkt_restart != 0) &&
/* packet has not timed out */
continue;
}
/* time for packet re-transport */
if (pptr->port_ipkt_list) {
NULL;
}
} else {
}
}
if (fcp_is_retryable(icmd)) {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"%x to D_ID=%x Retrying..",
/*
* Update the RSCN count in the packet
* before resending.
*/
}
switch (icmd->ipkt_opcode) {
int rval;
case LA_ELS_PLOGI:
if ((rval = fc_ulp_login(
FC_SUCCESS) {
&pptr->port_mutex);
continue;
}
"PLOGI") == DDI_SUCCESS) {
&pptr->port_mutex);
continue;
}
break;
case LA_ELS_PRLI:
if ((rval = fc_ulp_issue_els(
FC_SUCCESS) {
&pptr->port_mutex);
continue;
}
"PRLI") == DDI_SUCCESS) {
&pptr->port_mutex);
continue;
}
break;
default:
if ((rval = fcp_transport(
FC_SUCCESS) {
&pptr->port_mutex);
continue;
}
"PRLI") == DDI_SUCCESS) {
&pptr->port_mutex);
continue;
}
break;
}
} else {
}
} else {
}
icmd->ipkt_cause);
}
/*
* Bail out early before getting into trouble
*/
if (save_port != fcp_port_head) {
break;
}
}
if (fcp_watchdog_init > 0) {
/* reschedule timeout to go again */
}
}
static void
{
int level;
continue;
}
if (ptgt) {
} else {
}
if (prev) {
} else {
/*
* Because we drop port mutex while doing aborts for
* packets, we can't rely on reset_list pointing to
* our head
*/
} else {
struct fcp_reset_elem *which;
}
}
}
if (level == RESET_TARGET) {
} else {
}
}
}
}
static void
{
int rval;
continue;
}
} else {
}
}
} else {
}
}
} else {
}
}
/*
* If the FCA will return all the commands in its queue then our
* work is easy, just return.
*/
return;
/*
* For RESET_LUN get hold of target pointer
*/
}
/*
* There are some severe race conditions here.
* While we are trying to abort the pkt, it might be completing
* so mark it aborted and if the abort does not succeed then
* handle it in the watch thread.
*/
int restart = 0;
continue;
}
restart = 1;
if (rval == FC_SUCCESS) {
} else {
/*
* This part is tricky. The abort
* failed and now the command could
* be completing. The cmd_state ==
* FCP_PKT_ABORTING should save
* us in fcp_cmd_callback. If we
* are already aborting ignore the
* command in fcp_cmd_callback.
* Here we leave this packet for 20
* sec to be aborted in the
* fcp_watch thread.
*/
"!Abort failed after reset %s",
msg);
cmd->cmd_timeout =
/*
* This is a hack, cmd is put in the
* overflow queue so that it can be
* timed out finally
*/
if (pptr->port_pkt_head) {
!= NULL);
= cmd;
} else {
== NULL);
= cmd;
}
}
} else {
}
}
return;
} else {
}
}
}
/*
* unlink the soft state, returning the soft state found (if any)
*
* acquires and releases the global mutex
*/
struct fcp_port *
{
/* we found a match -- remove this item */
/* we're at the head of the list */
} else {
}
break; /* success */
}
}
if (fcp_port_head == NULL) {
}
return (hptr);
}
/*
* called by fcp_scsi_hba_tgt_init to find a LUN given a
* WWN and a LUN number
*/
/* ARGSUSED */
static struct fcp_lun *
{
int hash;
sizeof (ptgt->tgt_port_wwn)) == 0) {
return (plun);
}
}
return (NULL);
}
}
return (NULL);
}
/*
* Function: fcp_prepare_pkt
*
* Description: This function prepares the SCSI cmd pkt, passed by the caller,
* for fcp_start(). It binds the data or partially maps it.
* Builds the FCP header and starts the initialization of the
* Fibre Channel header.
*
* Argument: *pptr FCP port.
* *cmd FCP packet.
* *plun LUN the command will be sent to.
*
* Context: User, Kernel and Interrupt context.
*/
static void
{
} else {
}
/* FCA needs pkt_datalen to be set */
} else {
fpkt->pkt_datalen = 0;
fcmd->fcp_data_len = 0;
}
/* set up the Tagged Queuing type */
} else {
}
} else {
}
} else {
}
}
}
/*
* Save a few kernel cycles here
*/
#ifndef __lock_lint
#endif /* __lock_lint */
}
static void
{
}
}
/*
* called to do polled I/O by fcp_start()
*
* return a transport status value, i.e. TRAN_ACCECPT for success
*/
static int
{
int rval;
#ifdef DEBUG
pptr->port_npkts++;
#endif /* DEBUG */
} else {
}
#ifdef DEBUG
pptr->port_npkts--;
#endif /* DEBUG */
switch (rval) {
case FC_SUCCESS:
rval = TRAN_ACCEPT;
} else {
}
break;
case FC_TRAN_BUSY:
break;
case FC_BADPACKET:
rval = TRAN_BADPKT;
break;
default:
break;
}
return (rval);
}
/*
* called by some of the following transport-called routines to convert
* a supplied dip ptr to a port struct ptr (i.e. to the soft state)
*/
static struct fcp_port *
{
int instance;
}
/*
* called internally to return a LUN given a dip
*/
struct fcp_lun *
{
int i;
for (i = 0; i < FCP_NUM_HASH; i++) {
return (plun); /* match found */
}
}
}
}
return (NULL); /* no LUN found */
}
/*
* pass an element to the hotplug list, kick the hotplug thread
* and wait for the element to get processed by the hotplug thread.
* on return the element is freed.
*
* return zero success and non-zero on failure
*
*
*/
static int
{
struct fcp_hp_elem *elem;
int rval;
"Can not pass_to_hp: what: %d; D_ID=%x, LUN=%x\n",
return (NDI_FAILURE);
}
}
}
return (rval);
}
/*
* pass an element to the hotplug list, and then
* kick the hotplug thread
*
* return Boolean success, i.e. non-zero if all goes well, else zero on error
*
*
* called with the target mutex owned
*
* memory acquired in NOSLEEP mode
* NOTE: if wait is set to 1 then the caller is responsible for waiting on
* for the hp daemon to process the request and is responsible for
* freeing the element
*/
static struct fcp_hp_elem *
{
struct fcp_hp_elem *elem;
/* create space for a hotplug element */
== NULL) {
"!can't allocate memory for hotplug element");
return (NULL);
}
/* fill in hotplug element */
/* schedule the hotplug task */
plun->lun_event_count++;
}
plun->lun_event_count--;
return (0);
}
return (elem);
}
static void
{
int rval;
struct scsi_address *ap;
/*
* It is possible for pkt_pd to be NULL if tgt_pd_handle was
* originally NULL, hence we try to set it to the pd pointed
* to by the SCSI device we're trying to get to.
*/
/*
* We need to notify the transport that we now have a
* reference to the remote port handle.
*/
}
/* prepare the packet */
}
cmd->cmd_fp_pkt, 0);
if (rval == FC_SUCCESS) {
return;
}
} else {
}
}
static void
{
}
/*
* Function: fcp_queue_pkt
*
* Description: This function queues the packet passed by the caller into
* the list of packets of the FCP port.
*
* Argument: *pptr FCP port.
* *cmd FCP packet to queue.
*
* Return Value: None
*
* Context: User, Kernel and Interrupt context.
*/
static void
{
/*
* zero pkt_time means hang around for ever
*/
} else {
/*
* Indicate the watch thread to fail the
* command by setting it to highest value
*/
}
}
if (pptr->port_pkt_head) {
} else {
}
}
/*
* Function: fcp_update_targets
*
* Description: This function applies the specified change of state to all
* the targets listed. The operation applied is 'set'.
*
* Argument: *pptr FCP port.
* *dev_list Array of fc_portmap_t structures.
* count Length of dev_list.
* state State bits to update.
* cause Reason for the update.
*
* Return Value: None
*
* Context: User, Kernel and Interrupt context.
* The mutex pptr->port_mutex must be held.
*/
static void
{
while (count--) {
continue;
}
ptgt->tgt_change_cnt++;
}
}
static int
{
int rval;
return (rval);
}
static int
{
int finish_init = 0;
int finish_tgt = 0;
int do_finish_init = 0;
int rval = FCP_NO_CHANGE;
if (cause == FCP_CAUSE_LINK_CHANGE ||
cause == FCP_CAUSE_LINK_DOWN) {
do_finish_init = 1;
}
FCP_BUF_LEVEL_2, 0,
"link_cnt: %d,%d; tgt_cnt: %d,%d; tmp_cnt: %d,%d;"
" cause = %d, d_id = 0x%x, tgt_done = %d",
finish_init = 1;
}
} else {
if (--ptgt->tgt_tmp_cnt <= 0) {
ptgt->tgt_tmp_cnt = 0;
finish_tgt = 1;
if (do_finish_init) {
finish_init = 1;
}
}
}
} else {
FCP_BUF_LEVEL_2, 0,
"Call Finish Init for NO target");
if (do_finish_init) {
finish_init = 1;
}
}
if (finish_tgt) {
#ifdef DEBUG
sizeof (ptgt->tgt_tmp_cnt_stack));
#endif /* DEBUG */
}
if (--pptr->port_tmp_cnt == 0) {
}
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_call_finish_init_held,1: state change occured"
}
return (rval);
}
static void
fcp_reconfigure_luns(void * tgt_handle)
{
/*
* If the timer that fires this off got canceled too late, the
* target could have been destroyed.
*/
return;
}
"!fcp%d: failed to allocate for portmap",
return;
}
dev_cnt = 1;
/*
* Clear the tgt_tid after no more references to
* the fcp_tgt
*/
}
static void
{
int i;
for (i = 0; i < FCP_NUM_HASH; i++) {
}
}
}
static void
{
/*
* Cancel any pending timeouts for this target.
*/
/*
* Set tgt_tid to NULL first to avoid a race in the callback.
* If tgt_tid is NULL, the callback will simply return.
*/
}
}
}
/*
* Function: fcp_is_retryable
*
* Description: Indicates if the internal packet is retryable.
*
* Argument: *icmd FCP internal packet.
*
* Return Value: 0 Not retryable
* 1 Retryable
*
* Context: User, Kernel and Interrupt context
*/
static int
{
return (0);
}
/*
* Function: fcp_create_on_demand
*
* Argument: *pptr FCP port.
* *pwwn Port WWN.
*
* Return Value: 0 Success
* EIO
* ENOMEM
* EBUSY
* EINVAL
*
* Context: User and Kernel context
*/
static int
{
int wait_ms;
int tcount;
int lcount;
int ret;
int error;
int ntries;
int old_manual = 0;
/* Allocates the fc_portmap_t structure. */
/*
* If FC_INVALID_RSCN_COUNT is non-zero, we will have to init as shown
* in the commented statement below:
*
* devlist->map_rscn_info.ulp_rscn_count = FC_INVALID_RSCN_COUNT;
*
* Below, the deadline for the discovery process is set.
*/
/*
* We try to find the remote port based on the WWN provided by the
*/
return (rval);
}
/*
* fc_portmap_t structure.
*/
if (ret != FC_SUCCESS) {
return (rval);
}
/*
* The map flag field is set to indicates that the creation is being
* done at the user request (Ioclt probably luxadm or cfgadm).
*/
/*
* We check to see if fcp already has a target that describes the
* device being created. If not it is created.
*/
"!FC target allocation failed");
return (ENOMEM);
}
}
ptgt->tgt_device_created = 0;
/*
* If fabric and auto config is set but the target was
* manually unconfigured then reset to the manual_config_only to
* 0 so the device will get configured.
*/
old_manual = 1;
ptgt->tgt_manual_config_only = 0;
}
}
}
FCP_BUF_LEVEL_3, 0,
"fcp_create_on_demand: mapflags ptgt=%x, "
"lcount=%x::port_link_cnt=%x, "
"tcount=%x: tgt_change_cnt=%x, rval=%x",
return (rval);
}
/*
* Due to lack of synchronization mechanisms, we perform
* periodic monitoring of our request; Because requests
* get dropped when another one supercedes (either because
* of a link change or a target change), it is difficult to
* provide a clean synchronization mechanism (such as a
* semaphore or a conditional variable) without exhaustively
* rewriting the mainline discovery code of this driver.
*/
wait_ms = 500;
FCP_BUF_LEVEL_3, 0,
"fcp_create_on_demand(1): ntries=%x, ptgt=%x, "
"lcount=%x::port_link_cnt=%x, "
"tcount=%x::tgt_change_cnt=%x, rval=%x, tgt_device_created=%x "
"tgt_tmp_cnt =%x",
ptgt->tgt_tmp_cnt);
}
} else {
rval = 0;
}
}
FCP_BUF_LEVEL_3, 0,
"fcp_create_on_demand(2): ntries=%x, ptgt=%x, "
"lcount=%x::port_link_cnt=%x, "
"tcount=%x::tgt_change_cnt=%x, rval=%x, tgt_device_created=%x "
"tgt_tmp_cnt =%x",
ptgt->tgt_tmp_cnt);
if (rval) {
}
FCP_BUF_LEVEL_3, 0,
"fcp_create_on_demand(3): ntries=%x, ptgt=%x, "
"lcount=%x::port_link_cnt=%x, "
"tcount=%x::tgt_change_cnt=%x, rval=%x, "
"tgt_device_created=%x, tgt D_ID=%x",
return (rval);
}
} else {
}
/*
* Configuring the target with no LUNs will fail. We
* should reset the node state so that it is not
* automatically configured when the LUNs are added
* to this target.
*/
if (ptgt->tgt_lun_cnt == 0) {
}
while (plun) {
if (rval != NDI_SUCCESS) {
FCP_BUF_LEVEL_3, 0,
"fcp_create_on_demand: "
"pass_to_hp_and_wait failed "
"rval=%x", rval);
} else {
}
}
}
/* if successful then set manual to 0 */
if (rval == 0) {
ptgt->tgt_manual_config_only = 0;
} else {
/* reset to 1 so the user has to do the config */
}
}
return (rval);
}
static void
{
int count;
count = 0;
while (*string) {
break;
}
}
}
static void
{
int i;
for (i = 0; i < FC_WWN_SIZE; i++) {
"%02x", wwn[i]);
}
}
static void
{
int scsi_cmd = 0;
return;
}
switch (icmd->ipkt_opcode) {
case SCMD_REPORT_LUN:
" lun=0x%%x failed");
scsi_cmd++;
break;
case SCMD_INQUIRY_PAGE83:
" lun=0x%%x failed");
scsi_cmd++;
break;
case SCMD_INQUIRY:
" lun=0x%%x failed");
scsi_cmd++;
break;
case LA_ELS_PLOGI:
break;
case LA_ELS_PRLI:
break;
}
if (icmd->ipkt_nodma) {
sizeof (struct fcp_rsp));
} else {
bep = &fcp_rsp_err;
sizeof (struct fcp_rsp));
sizeof (struct fcp_rsp_info));
}
" : Bad FCP response values rsvd1=%%x, rsvd2=%%x,"
" sts-rsvd1=%%x, sts-rsvd2=%%x, rsplen=%%x,"
" senselen=%%x. Giving up");
return;
}
}
if (icmd->ipkt_nodma) {
sense_ptr = (struct scsi_extended_sense *)
sizeof (struct fcp_rsp) +
} else {
sense_ptr = &sense_info;
sizeof (struct fcp_rsp) +
sizeof (struct scsi_extended_sense));
}
} else {
sense_key = "Undefined";
}
": sense key=%%s, ASC=%%x," " ASCQ=%%x."
" Giving up");
} else {
" : SCSI status=%%x. Giving up");
}
} else {
" Reason:%%s. Giving up");
if (scsi_cmd) {
} else {
}
}
}
static int
{
int ret = DDI_FAILURE;
char *error;
switch (rval) {
case FC_DEVICE_BUSY_NEW_RSCN:
/*
* This means that there was a new RSCN that the transport
* knows about (which the ULP *may* know about too) but the
* pkt that was sent down was related to an older RSCN. So, we
* are just going to reset the retry count and deadline and
* continue to retry. The idea is that transport is currently
* working on the new RSCN and will soon let the ULPs know
* about it and when it does the existing logic will kick in
* where it will change the tcount to indicate that something
* changed on the target. So, rediscovery will start and there
* will not be an infinite retry.
*
* For a full flow of how the RSCN info is transferred back and
* forth, see fp.c
*/
icmd->ipkt_retries = 0;
FCP_BUF_LEVEL_3, 0,
"fcp_handle_ipkt_errors: rval=%x for D_ID=%x",
/* FALLTHROUGH */
case FC_STATEC_BUSY:
case FC_DEVICE_BUSY:
case FC_PBUSY:
case FC_FBUSY:
case FC_TRAN_BUSY:
case FC_OFFLINE:
FCP_BUF_LEVEL_3, 0,
"fcp_handle_ipkt_errors: rval=%x for D_ID=%x",
fcp_is_retryable(icmd)) {
ret = DDI_SUCCESS;
}
break;
case FC_LOGINREQ:
/*
* FC_LOGINREQ used to be handled just like all the cases
* above. It has been changed to handled a PRLI that fails
* with FC_LOGINREQ different than other ipkts that fail
* with FC_LOGINREQ. If a PRLI fails with FC_LOGINREQ it is
* a simple matter to turn it into a PLOGI instead, so that's
* exactly what we do here.
*/
} else {
FCP_BUF_LEVEL_3, 0,
"fcp_handle_ipkt_errors: rval=%x for D_ID=%x",
fcp_is_retryable(icmd)) {
ret = DDI_SUCCESS;
}
}
break;
default:
"!Failed to send %s to D_ID=%x error=%s",
} else {
fcp_trace, FCP_BUF_LEVEL_2, 0,
"fcp_handle_ipkt_errors,1: state change occured"
}
break;
}
return (ret);
}
/*
* Check of outstanding commands on any LUN for this target
*/
static int
{
return (FC_SUCCESS);
}
}
}
return (FC_FAILURE);
}
static fc_portmap_t *
{
int i;
for (i = 0, *dev_cnt = 0; i < FCP_NUM_HASH; i++) {
++*dev_cnt;
}
}
}
"!fcp%d: failed to allocate for portmap for construct map",
return (devptr);
}
for (i = 0; i < FCP_NUM_HASH; i++) {
int ret;
devlist);
if (ret == FC_SUCCESS) {
devlist++;
continue;
}
devlist++;
}
}
}
return (devptr);
}
/*
* Inimate MPxIO that the lun is busy and cannot accept regular IO
*/
static void
{
int i;
for (i = 0; i < FCP_NUM_HASH; i++) {
ptgt->tgt_change_cnt, 0, 0)) {
FCP_BUF_LEVEL_2, 0,
"path_verifybusy: "
"disable lun %p failed!",
plun);
}
}
}
}
}
}
static int
{
return (NDI_FAILURE);
}
if (what == FCP_MPXIO_PATH_CLEAR_BUSY) {
/* LUN ready for IO */
} else {
/* LUN busy to accept IO */
}
return (NDI_SUCCESS);
}
/*
* Caller must free the returned string of MAXPATHLEN len
* If the device is offline (-1 instance number) NULL
* will be returned.
*/
static char *
return (NULL);
}
} else {
}
return (NULL);
}
if (ddi_get_instance(dip) < 0) {
return (NULL);
}
return (NULL);
}
/*
* In reality, the user wants a fully valid path (one they can open)
* but this string is lacking the mount point, and the minor node.
* It would be nice if we could "figure these out" somehow
* and fill them in. Otherwise, the userland code has to understand
* driver specific details of which minor node is the "best" or
* "right" one to expose. (Ex: which slice is the whole disk, or
* which tape doesn't rewind)
*/
return (path);
}
static int
{
if (reset_delay < 0)
reset_delay = 0;
if (fcp_bus_config_debug)
flag |= NDI_DEVI_DEBUG;
switch (op) {
case BUS_CONFIG_ONE:
/*
* Retry the command since we need to ensure
* the fabric devices are available for root
*/
while (retry++ < fcp_max_bus_config_retries) {
if (rval == 0)
return (rval);
}
/*
* drain taskq to make sure nodes are created and then
* try again.
*/
case BUS_CONFIG_DRIVER:
case BUS_CONFIG_ALL: {
/*
* delay till all devices report in (port_tmp_cnt == 0)
* or FCP_INIT_WAIT_TIMEOUT
*/
&pptr->port_mutex,
}
/* drain taskq to make sure nodes are created */
}
default:
return (NDI_FAILURE);
}
/*NOTREACHED*/
}
static int
{
if (fcp_bus_config_debug)
flag |= NDI_DEVI_DEBUG;
}
/*
* Routine to copy GUID into the lun structure.
* returns 0 if copy was successful and 1 if encountered a
* failure and did not copy the guid.
*/
static int
{
int retval = 0;
/* add one for the null terminator */
return (1);
}
/*
* if the plun->lun_guid already has been allocated,
* then check the size. if the size is exact, reuse
* it....if not free it an allocate the required size.
* The reallocation should NOT typically happen
* unless the GUIDs reported changes between passes.
* We free up and alloc again even if the
* size was more than required. This is due to the
* fact that the field lun_guid_size - serves
* dual role of indicating the size of the wwn
* size and ALSO the allocation size.
*/
/*
* free the allocated memory and
* initialize the field
* lun_guid_size to 0.
*/
plun->lun_guid_size = 0;
}
}
/*
* alloc only if not already done.
*/
"Unable to allocate"
"Memory for GUID!!! size %d", len);
retval = 1;
} else {
}
}
/*
* now copy the GUID
*/
}
return (retval);
}
/*
* fcp_reconfig_wait
*
* Wait for a rediscovery/reconfiguration to complete before continuing.
*/
static void
{
/*
* Quick check. If pptr->port_tmp_cnt is 0, there is no
* reconfiguration in progress.
*/
if (pptr->port_tmp_cnt == 0) {
return;
}
/*
* If we cause a reconfig by raising power, delay until all devices
* report in (port_tmp_cnt returns to 0)
*/
pptr->port_tmp_cnt) {
}
/*
* Even if fcp_tmp_count isn't 0, continue without error. The port
* we want may still be ok. If not, it will error out later
*/
}
/*
* Read masking info from fp.conf and construct the global fcp_lun_blacklist.
* We rely on the fcp_global_mutex to provide protection against changes to
* the fcp_lun_blacklist.
*
* You can describe a list of target port WWNs and LUN numbers which will
* not be configured. LUN numbers will be interpreted as decimal. White
* spaces and ',' can be used in the list of LUN numbers.
*
* To prevent LUNs 1 and 2 from being configured for target
* port 510000f010fd92a1 and target port 510000e012079df1, set:
*
* pwwn-lun-blacklist=
* "510000f010fd92a1,1,2",
* "510000e012079df1,1,2";
*/
static void
struct fcp_black_list_entry **pplun_blacklist) {
char **prop_array = NULL;
int idx = 0;
int len = 0;
return;
}
while (*curr_pwwn == ' ') {
curr_pwwn++;
}
", please check.", curr_pwwn);
continue;
}
", please check.", curr_pwwn);
continue;
}
"blacklist, please check.", curr_pwwn);
break;
}
}
continue;
}
}
}
/*
* Get the masking info about one remote target port designated by wwn.
* Lun ids could be separated by ',' or white spaces.
*/
static void
struct fcp_black_list_entry **pplun_blacklist) {
int idx = 0;
unsigned long lun_id = 0;
char lunid_buf[16];
int illegal_digit = 0;
}
idx++;
}
if (illegal_digit > 0) {
idx = 0;
illegal_digit = 0;
"the blacklist, please check digits.",
continue;
}
"the blacklist, please check the length of LUN#.",
break;
}
if (idx == 0) { /* ignore ' ' or ',' or '\0' */
offset++;
continue;
}
} else {
"the blacklist, please check %s.",
}
idx = 0;
}
}
/*
* Add one masking record
*/
static void
struct fcp_black_list_entry **pplun_blacklist) {
while (tmp_entry) {
return;
}
}
/* add to black list */
(sizeof (struct fcp_black_list_entry), KM_SLEEP);
}
/*
* Check if we should mask the specified lun of this fcp_tgt
*/
static int
struct fcp_black_list_entry *remote_port;
while (remote_port != NULL) {
remote_port->masked++;
"%02x%02x%02x%02x%02x%02x%02x%02x is masked due to black listing.\n",
}
return (TRUE);
}
}
}
return (FALSE);
}
/*
* Release all allocated resources
*/
static void
/*
* Traverse all luns
*/
while (tmp_entry) {
}
*pplun_blacklist = NULL;
}