From: Seokmann Ju Date: Thu, 5 Jul 2007 20:16:51 +0000 (-0700) Subject: [SCSI] qla2xxx: add support for NPIV X-Git-Tag: v2.6.23-rc1~1053^2~16 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2c3dfe3f6ad8daff5acdb01713e4f2b116e78136;p=linux-2.6 [SCSI] qla2xxx: add support for NPIV Following patch adds support for NPIV (N-Port ID Virtualization) to the qla2xxx. - supported within switched-fabric topologies only. - supports up to 63 virtual ports on each physical port. Signed-off-by: Seokmann Ju Signed-off-by: James Bottomley --- diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile index 411663af7b..71ddb5db49 100644 --- a/drivers/scsi/qla2xxx/Makefile +++ b/drivers/scsi/qla2xxx/Makefile @@ -1,4 +1,4 @@ qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ - qla_dbg.o qla_sup.o qla_attr.o + qla_dbg.o qla_sup.o qla_attr.o qla_mid.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 8081b637d9..b79c4dfc2a 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -6,8 +6,11 @@ */ #include "qla_def.h" +#include #include +int qla24xx_vport_disable(struct fc_vport *, bool); + /* SYSFS attributes --------------------------------------------------------- */ static ssize_t @@ -959,6 +962,122 @@ qla2x00_get_host_port_state(struct Scsi_Host *shost) fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; } +static int +qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) +{ + int ret = 0; + scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata; + scsi_qla_host_t *vha; + + ret = qla24xx_vport_create_req_sanity_check(fc_vport); + if (ret) { + DEBUG15(printk("qla24xx_vport_create_req_sanity_check failed, " + "status %x\n", ret)); + return (ret); + } + + vha = qla24xx_create_vhost(fc_vport); + if (vha == NULL) { + DEBUG15(printk ("qla24xx_create_vhost failed, vha = %p\n", + vha)); + return FC_VPORT_FAILED; + } + if (disable) { + atomic_set(&vha->vp_state, VP_OFFLINE); + fc_vport_set_state(fc_vport, FC_VPORT_DISABLED); + } else + atomic_set(&vha->vp_state, VP_FAILED); + + /* ready to create vport */ + qla_printk(KERN_INFO, vha, "VP entry id %d assigned.\n", vha->vp_idx); + + /* initialized vport states */ + atomic_set(&vha->loop_state, LOOP_DOWN); + vha->vp_err_state= VP_ERR_PORTDWN; + vha->vp_prev_err_state= VP_ERR_UNKWN; + /* Check if physical ha port is Up */ + if (atomic_read(&ha->loop_state) == LOOP_DOWN || + atomic_read(&ha->loop_state) == LOOP_DEAD) { + /* Don't retry or attempt login of this virtual port */ + DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n", + vha->host_no)); + atomic_set(&vha->loop_state, LOOP_DEAD); + if (!disable) + fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); + } + + if (scsi_add_host(vha->host, &fc_vport->dev)) { + DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n", + vha->host_no, vha->vp_idx)); + goto vport_create_failed_2; + } + + /* initialize attributes */ + fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name); + fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name); + fc_host_supported_classes(vha->host) = + fc_host_supported_classes(ha->host); + fc_host_supported_speeds(vha->host) = + fc_host_supported_speeds(ha->host); + + qla24xx_vport_disable(fc_vport, disable); + + return 0; +vport_create_failed_2: + qla24xx_disable_vp(vha); + qla24xx_deallocate_vp_id(vha); + kfree(vha->port_name); + kfree(vha->node_name); + scsi_host_put(vha->host); + return FC_VPORT_FAILED; +} + +int +qla24xx_vport_delete(struct fc_vport *fc_vport) +{ + scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata; + scsi_qla_host_t *vha = fc_vport->dd_data; + + qla24xx_disable_vp(vha); + qla24xx_deallocate_vp_id(vha); + + down(&ha->vport_sem); + ha->cur_vport_count--; + clear_bit(vha->vp_idx, (unsigned long *)ha->vp_idx_map); + up(&ha->vport_sem); + + kfree(vha->node_name); + kfree(vha->port_name); + + if (vha->timer_active) { + qla2x00_vp_stop_timer(vha); + DEBUG15(printk ("scsi(%ld): timer for the vport[%d] = %p " + "has stopped\n", + vha->host_no, vha->vp_idx, vha)); + } + + fc_remove_host(vha->host); + + scsi_remove_host(vha->host); + + scsi_host_put(vha->host); + + return 0; +} + +int +qla24xx_vport_disable(struct fc_vport *fc_vport, bool disable) +{ + scsi_qla_host_t *vha = fc_vport->dd_data; + + if (disable) + qla24xx_disable_vp(vha); + else + qla24xx_enable_vp(vha); + + return 0; +} + struct fc_function_template qla2xxx_transport_functions = { .show_host_node_name = 1, @@ -996,6 +1115,49 @@ struct fc_function_template qla2xxx_transport_functions = { .issue_fc_host_lip = qla2x00_issue_lip, .get_fc_host_stats = qla2x00_get_fc_host_stats, + + .vport_create = qla24xx_vport_create, + .vport_disable = qla24xx_vport_disable, + .vport_delete = qla24xx_vport_delete, +}; + +struct fc_function_template qla2xxx_transport_vport_functions = { + + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + + .get_host_port_id = qla2x00_get_host_port_id, + .show_host_port_id = 1, + .get_host_speed = qla2x00_get_host_speed, + .show_host_speed = 1, + .get_host_port_type = qla2x00_get_host_port_type, + .show_host_port_type = 1, + .get_host_symbolic_name = qla2x00_get_host_symbolic_name, + .show_host_symbolic_name = 1, + .set_host_system_hostname = qla2x00_set_host_system_hostname, + .show_host_system_hostname = 1, + .get_host_fabric_name = qla2x00_get_host_fabric_name, + .show_host_fabric_name = 1, + .get_host_port_state = qla2x00_get_host_port_state, + .show_host_port_state = 1, + + .dd_fcrport_size = sizeof(struct fc_port *), + .show_rport_supported_classes = 1, + + .get_starget_node_name = qla2x00_get_starget_node_name, + .show_starget_node_name = 1, + .get_starget_port_name = qla2x00_get_starget_port_name, + .show_starget_port_name = 1, + .get_starget_port_id = qla2x00_get_starget_port_id, + .show_starget_port_id = 1, + + .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo, + .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo, + .show_rport_dev_loss_tmo = 1, + + .issue_fc_host_lip = qla2x00_issue_lip, + .get_fc_host_stats = qla2x00_get_fc_host_stats, }; void @@ -1004,4 +1166,6 @@ qla2x00_init_host_attr(scsi_qla_host_t *ha) fc_host_node_name(ha->host) = wwn_to_u64(ha->node_name); fc_host_port_name(ha->host) = wwn_to_u64(ha->port_name); fc_host_supported_classes(ha->host) = FC_COS_CLASS3; + fc_host_max_npiv_vports(ha->host) = MAX_NUM_VPORT_FABRIC; + fc_host_npiv_vports_inuse(ha->host) = ha->cur_vport_count; } diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index 5b12278968..49dffeb785 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -21,6 +21,7 @@ /* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */ /* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */ /* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */ +/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */ /* * Local Macro Definitions. */ @@ -30,7 +31,8 @@ defined(QL_DEBUG_LEVEL_7) || defined(QL_DEBUG_LEVEL_8) || \ defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_10) || \ defined(QL_DEBUG_LEVEL_11) || defined(QL_DEBUG_LEVEL_12) || \ - defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14) + defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14) || \ + defined(QL_DEBUG_LEVEL_15) #define QL_DEBUG_ROUTINES #endif @@ -125,6 +127,12 @@ #define DEBUG14(x) do {} while (0) #endif +#if defined(QL_DEBUG_LEVEL_15) +#define DEBUG15(x) do {x;} while (0) +#else +#define DEBUG15(x) do {} while (0) +#endif + /* * Firmware Dump structure definition */ diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index e8948b679f..a1ca590ba4 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -1551,6 +1551,9 @@ typedef struct fc_port { unsigned long last_queue_full; unsigned long last_ramp_up; + + struct list_head vp_fcport; + uint16_t vp_idx; } fc_port_t; /* @@ -1999,6 +2002,36 @@ struct gid_list_info { }; #define GID_LIST_SIZE (sizeof(struct gid_list_info) * MAX_FIBRE_DEVICES) +/* NPIV */ +typedef struct vport_info { + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; + int vp_id; + uint16_t loop_id; + unsigned long host_no; + uint8_t port_id[3]; + int loop_state; +} vport_info_t; + +typedef struct vport_params { + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; + uint32_t options; +#define VP_OPTS_RETRY_ENABLE BIT_0 +#define VP_OPTS_VP_DISABLE BIT_1 +} vport_params_t; + +/* NPIV - return codes of VP create and modify */ +#define VP_RET_CODE_OK 0 +#define VP_RET_CODE_FATAL 1 +#define VP_RET_CODE_WRONG_ID 2 +#define VP_RET_CODE_WWPN 3 +#define VP_RET_CODE_RESOURCES 4 +#define VP_RET_CODE_NO_MEM 5 +#define VP_RET_CODE_NOT_FOUND 6 + +#define to_qla_parent(x) (((x)->parent) ? (x)->parent : (x)) + /* * ISP operations */ @@ -2073,6 +2106,16 @@ struct qla_msix_entry { uint16_t msix_entry; }; +#define WATCH_INTERVAL 1 /* number of seconds */ + +/* NPIV */ +#define MAX_MULTI_ID_LOOP 126 +#define MAX_MULTI_ID_FABRIC 64 +#define MAX_NUM_VPORT_LOOP (MAX_MULTI_ID_LOOP - 1) +#define MAX_NUM_VPORT_FABRIC (MAX_MULTI_ID_FABRIC - 1) +#define MAX_NUM_VHBA_LOOP (MAX_MULTI_ID_LOOP - 1) +#define MAX_NUM_VHBA_FABRIC (MAX_MULTI_ID_FABRIC - 1) + /* * Linux Host Adapter structure */ @@ -2108,6 +2151,8 @@ typedef struct scsi_qla_host { uint32_t msix_enabled :1; uint32_t disable_serdes :1; uint32_t gpsc_supported :1; + uint32_t vsan_enabled :1; + uint32_t npiv_supported :1; } flags; atomic_t loop_state; @@ -2147,6 +2192,7 @@ typedef struct scsi_qla_host { #define BEACON_BLINK_NEEDED 25 #define REGISTER_FDMI_NEEDED 26 #define FCPORT_UPDATE_NEEDED 27 +#define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */ uint32_t device_flags; #define DFLG_LOCAL_DEVICES BIT_0 @@ -2237,6 +2283,11 @@ typedef struct scsi_qla_host { /* ISP configuration data. */ uint16_t loop_id; /* Host adapter loop id */ + uint16_t switch_cap; +#define FLOGI_SEQ_DEL BIT_8 +#define FLOGI_MID_SUPPORT BIT_10 +#define FLOGI_VSAN_SUPPORT BIT_12 +#define FLOGI_SP_SUPPORT BIT_13 uint16_t fb_rev; port_id_t d_id; /* Host adapter port id */ @@ -2344,6 +2395,7 @@ typedef struct scsi_qla_host { #define MBX_UPDATE_FLASH_ACTIVE 3 struct semaphore mbx_cmd_sem; /* Serialialize mbx access */ + struct semaphore vport_sem; /* Virtual port synchronization */ struct semaphore mbx_intr_sem; /* Used for completion notification */ uint32_t mbx_flags; @@ -2428,6 +2480,37 @@ typedef struct scsi_qla_host { struct fc_host_statistics fc_host_stat; struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES]; + + struct list_head vp_list; /* list of VP */ + struct fc_vport *fc_vport; /* holds fc_vport * for each vport */ + uint8_t vp_idx_map[16]; + uint16_t num_vhosts; /* number of vports created */ + uint16_t num_vsans; /* number of vsan created */ + uint16_t vp_idx; /* vport ID */ + + struct scsi_qla_host *parent; /* holds pport */ + unsigned long vp_flags; + struct list_head vp_fcports; /* list of fcports */ +#define VP_IDX_ACQUIRED 0 /* bit no 0 */ +#define VP_CREATE_NEEDED 1 +#define VP_BIND_NEEDED 2 +#define VP_DELETE_NEEDED 3 +#define VP_SCR_NEEDED 4 /* State Change Request registration */ + atomic_t vp_state; +#define VP_OFFLINE 0 +#define VP_ACTIVE 1 +#define VP_FAILED 2 +// #define VP_DISABLE 3 + uint16_t vp_err_state; + uint16_t vp_prev_err_state; +#define VP_ERR_UNKWN 0 +#define VP_ERR_PORTDWN 1 +#define VP_ERR_FAB_UNSUPPORTED 2 +#define VP_ERR_FAB_NORESOURCES 3 +#define VP_ERR_FAB_LOGOUT 4 +#define VP_ERR_ADAP_NORESOURCES 5 + int max_npiv_vports; /* 63 or 125 per topoloty */ + int cur_vport_count; } scsi_qla_host_t; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index a0a722cf42..63a11fef5d 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -69,6 +69,16 @@ struct port_database_24xx { uint8_t reserved_3[24]; }; +struct vp_database_24xx { + uint16_t vp_status; + uint8_t options; + uint8_t id; + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; + uint16_t port_id_low; + uint16_t port_id_high; +}; + struct nvram_24xx { /* NVRAM header. */ uint8_t id[4]; @@ -962,6 +972,25 @@ struct mid_db_24xx { struct mid_db_entry_24xx entries[MAX_MID_VPS]; }; + /* + * Virtual Fabric ID type definition. + */ +typedef struct vf_id { + uint16_t id : 12; + uint16_t priority : 4; +} vf_id_t; + +/* + * Virtual Fabric HopCt type definition. + */ +typedef struct vf_hopct { + uint16_t reserved : 8; + uint16_t hopct : 8; +} vf_hopct_t; + +/* + * Virtual Port Control IOCB + */ #define VP_CTRL_IOCB_TYPE 0x30 /* Vitual Port Control entry. */ struct vp_ctrl_entry_24xx { uint8_t entry_type; /* Entry type. */ @@ -974,6 +1003,7 @@ struct vp_ctrl_entry_24xx { uint16_t vp_idx_failed; uint16_t comp_status; /* Completion status. */ +#define CS_VCE_IOCB_ERROR 0x01 /* Error processing IOCB */ #define CS_VCE_ACQ_ID_ERROR 0x02 /* Error while acquireing ID. */ #define CS_VCE_BUSY 0x05 /* Firmware not ready to accept cmd. */ @@ -982,24 +1012,34 @@ struct vp_ctrl_entry_24xx { #define VCE_COMMAND_DISABLE_VPS 0x08 /* Disable VPs. */ #define VCE_COMMAND_DISABLE_VPS_REINIT 0x09 /* Disable VPs and reinit link. */ #define VCE_COMMAND_DISABLE_VPS_LOGO 0x0a /* Disable VPs and LOGO ports. */ +#define VCE_COMMAND_DISABLE_VPS_LOGO_ALL 0x0b /* Disable VPs and LOGO ports. */ uint16_t vp_count; uint8_t vp_idx_map[16]; - - uint8_t reserved_4[32]; + uint16_t flags; + struct vf_id id; + uint16_t reserved_4; + struct vf_hopct hopct; + uint8_t reserved_5[8]; }; +/* + * Modify Virtual Port Configuration IOCB + */ #define VP_CONFIG_IOCB_TYPE 0x31 /* Vitual Port Config entry. */ struct vp_config_entry_24xx { uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ + uint8_t handle_count; uint8_t entry_status; /* Entry Status. */ uint32_t handle; /* System handle. */ - uint16_t reserved_1; + uint16_t flags; +#define CS_VF_BIND_VPORTS_TO_VF BIT_0 +#define CS_VF_SET_QOS_OF_VPORTS BIT_1 +#define CS_VF_SET_HOPS_OF_VPORTS BIT_2 uint16_t comp_status; /* Completion status. */ #define CS_VCT_STS_ERROR 0x01 /* Specified VPs were not disabled. */ @@ -1009,27 +1049,29 @@ struct vp_config_entry_24xx { #define CS_VCT_BUSY 0x05 /* Firmware not ready to accept cmd. */ uint8_t command; -#define VCT_COMMAND_MOD_VPS 0x00 /* Enable VPs. */ -#define VCT_COMMAND_MOD_ENABLE_VPS 0x08 /* Disable VPs. */ +#define VCT_COMMAND_MOD_VPS 0x00 /* Modify VP configurations. */ +#define VCT_COMMAND_MOD_ENABLE_VPS 0x01 /* Modify configuration & enable VPs. */ uint8_t vp_count; - uint8_t vp_idx1; - uint8_t vp_idx2; + uint8_t vp_index1; + uint8_t vp_index2; uint8_t options_idx1; uint8_t hard_address_idx1; - uint16_t reserved_2; + uint16_t reserved_vp1; uint8_t port_name_idx1[WWN_SIZE]; uint8_t node_name_idx1[WWN_SIZE]; uint8_t options_idx2; uint8_t hard_address_idx2; - uint16_t reserved_3; + uint16_t reserved_vp2; uint8_t port_name_idx2[WWN_SIZE]; uint8_t node_name_idx2[WWN_SIZE]; - - uint8_t reserved_4[8]; + struct vf_id id; + uint16_t reserved_4; + struct vf_hopct hopct; + uint8_t reserved_5; }; #define VP_RPT_ID_IOCB_TYPE 0x32 /* Report ID Acquisition entry. */ @@ -1054,5 +1096,30 @@ struct vp_rpt_id_entry_24xx { uint8_t reserved_4[32]; }; +#define VF_EVFP_IOCB_TYPE 0x26 /* Exchange Virtual Fabric Parameters entry. */ +struct vf_evfp_entry_24xx { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + uint16_t comp_status; /* Completion status. */ + uint16_t timeout; /* timeout */ + uint16_t adim_tagging_mode; + + uint16_t vfport_id; + uint32_t exch_addr; + + uint16_t nport_handle; /* N_PORT handle. */ + uint16_t control_flags; + uint32_t io_parameter_0; + uint32_t io_parameter_1; + uint32_t tx_address[2]; /* Data segment 0 address. */ + uint32_t tx_len; /* Data segment 0 length. */ + uint32_t rx_address[2]; /* Data segment 1 address. */ + uint32_t rx_len; /* Data segment 1 length. */ +}; + /* END MID Support ***********************************************************/ #endif diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 74544ae4b0..b44eff2803 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -62,6 +62,38 @@ extern int ql2xfdmienable; extern int ql2xallocfwdump; extern int ql2xextended_error_logging; extern int ql2xqfullrampup; +extern int num_hosts; + +/* + * Global Functions in qla_mid.c source file. + */ +extern struct scsi_host_template qla2x00_driver_template; +extern struct scsi_host_template qla24xx_driver_template; +extern struct scsi_transport_template *qla2xxx_transport_vport_template; +extern uint8_t qla2x00_mem_alloc(scsi_qla_host_t *); +extern void qla2x00_timer(scsi_qla_host_t *); +extern void qla2x00_start_timer(scsi_qla_host_t *, void *, unsigned long); +extern void qla2x00_stop_timer(scsi_qla_host_t *); +extern uint32_t qla24xx_allocate_vp_id(scsi_qla_host_t *); +extern void qla24xx_deallocate_vp_id(scsi_qla_host_t *); +extern int qla24xx_disable_vp (scsi_qla_host_t *); +extern int qla24xx_enable_vp (scsi_qla_host_t *); +extern void qla2x00_mem_free(scsi_qla_host_t *); +extern int qla24xx_control_vp(scsi_qla_host_t *, int ); +extern int qla24xx_modify_vp_config(scsi_qla_host_t *); +extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t); +extern void qla2x00_vp_stop_timer(scsi_qla_host_t *); +extern int qla24xx_configure_vhba (scsi_qla_host_t *); +extern int qla24xx_get_vp_entry(scsi_qla_host_t *, uint16_t, int); +extern int qla24xx_get_vp_database(scsi_qla_host_t *, uint16_t); +extern int qla2x00_do_dpc_vp(scsi_qla_host_t *); +extern void qla24xx_report_id_acquisition(scsi_qla_host_t *, + struct vp_rpt_id_entry_24xx *); +extern scsi_qla_host_t * qla24xx_find_vhost_by_name(scsi_qla_host_t *, + uint8_t *); +extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *); +extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *); +extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *); extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *); @@ -77,6 +109,10 @@ extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *); extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *); extern void qla2xxx_wake_dpc(scsi_qla_host_t *); +extern void qla2x00_alert_all_vps(scsi_qla_host_t *, uint16_t *); +extern void qla2x00_async_event(scsi_qla_host_t *, uint16_t *); +extern void qla2x00_vp_abort_isp(scsi_qla_host_t *); +extern int qla24xx_vport_delete(struct fc_vport *); /* * Global Function Prototypes in qla_iocb.c source file. @@ -128,7 +164,7 @@ qla2x00_abort_target(fc_port_t *); extern int qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *, - uint8_t *, uint16_t *); + uint8_t *, uint16_t *, uint16_t *); extern int qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *); @@ -303,6 +339,7 @@ struct class_device_attribute; extern struct class_device_attribute *qla2x00_host_attrs[]; struct fc_function_template; extern struct fc_function_template qla2xxx_transport_functions; +extern struct fc_function_template qla2xxx_transport_vport_functions; extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *); extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *); extern void qla2x00_init_host_attr(scsi_qla_host_t *); diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index ec5b2dd90d..a086b3f0df 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -88,6 +88,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = ha->vp_idx; return (ct_pkt); } @@ -1186,6 +1187,7 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = ha->vp_idx; return ct_pkt; } @@ -1746,6 +1748,7 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size, ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = ha->vp_idx; return ct_pkt; } diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 2a45aec4ff..bd95f7dc5c 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -899,6 +899,10 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) &ha->fw_subminor_version, &ha->fw_attributes, &ha->fw_memory_size); qla2x00_resize_request_q(ha); + ha->flags.npiv_supported = 0; + if (IS_QLA24XX(ha) && + (ha->fw_attributes & BIT_2)) + ha->flags.npiv_supported = 1; if (ql2xallocfwdump) qla2x00_alloc_fw_dump(ha); @@ -1101,6 +1105,8 @@ qla2x00_init_rings(scsi_qla_host_t *ha) int rval; unsigned long flags = 0; int cnt; + struct mid_init_cb_24xx *mid_init_cb = + (struct mid_init_cb_24xx *) ha->init_cb; spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1132,6 +1138,10 @@ qla2x00_init_rings(scsi_qla_host_t *ha) ha->isp_ops.update_fw_options(ha); DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no)); + + mid_init_cb->count = MAX_NUM_VPORT_FABRIC; + ha->max_npiv_vports = MAX_NUM_VPORT_FABRIC; + rval = qla2x00_init_firmware(ha, ha->init_cb_size); if (rval) { DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n", @@ -1263,6 +1273,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) int rval; uint16_t loop_id; uint16_t topo; + uint16_t sw_cap; uint8_t al_pa; uint8_t area; uint8_t domain; @@ -1270,7 +1281,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) /* Get host addresses. */ rval = qla2x00_get_adapter_id(ha, - &loop_id, &al_pa, &area, &domain, &topo); + &loop_id, &al_pa, &area, &domain, &topo, &sw_cap); if (rval != QLA_SUCCESS) { if (LOOP_TRANSITION(ha) || atomic_read(&ha->loop_down_timer) || (rval == QLA_COMMAND_ERROR && loop_id == 0x7)) { @@ -1295,6 +1306,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) /* initialize */ ha->min_external_loopid = SNS_FIRST_LOOP_ID; ha->operating_mode = LOOP; + ha->switch_cap = 0; switch (topo) { case 0: @@ -1307,6 +1319,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) case 1: DEBUG3(printk("scsi(%ld): HBA in FL topology.\n", ha->host_no)); + ha->switch_cap = sw_cap; ha->current_topology = ISP_CFG_FL; strcpy(connect_type, "(FL_Port)"); break; @@ -1322,6 +1335,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) case 3: DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n", ha->host_no)); + ha->switch_cap = sw_cap; ha->operating_mode = P2P; ha->current_topology = ISP_CFG_F; strcpy(connect_type, "(F_Port)"); @@ -1743,7 +1757,6 @@ qla2x00_rport_del(void *data) spin_unlock_irqrestore(&fcport->rport_lock, flags); if (rport) fc_remote_port_delete(rport); - } /** @@ -1765,6 +1778,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) /* Setup fcport template structure. */ memset(fcport, 0, sizeof (fc_port_t)); fcport->ha = ha; + fcport->vp_idx = ha->vp_idx; fcport->port_type = FCT_UNKNOWN; fcport->loop_id = FC_NO_LOOP_ID; atomic_set(&fcport->state, FCS_UNCONFIGURED); @@ -1911,6 +1925,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) char *id_iter; uint16_t loop_id; uint8_t domain, area, al_pa; + scsi_qla_host_t *pha = to_qla_parent(ha); found_devs = 0; new_fcport = NULL; @@ -1942,7 +1957,10 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) /* * Mark local devices that were present with FCF_DEVICE_LOST for now. */ - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { + if (fcport->vp_idx != ha->vp_idx) + continue; + if (atomic_read(&fcport->state) == FCS_ONLINE && fcport->port_type != FCT_BROADCAST && (fcport->flags & FCF_FABRIC_DEVICE) == 0) { @@ -1988,6 +2006,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) new_fcport->d_id.b.area = area; new_fcport->d_id.b.al_pa = al_pa; new_fcport->loop_id = loop_id; + new_fcport->vp_idx = ha->vp_idx; rval2 = qla2x00_get_port_database(ha, new_fcport, 0); if (rval2 != QLA_SUCCESS) { DEBUG2(printk("scsi(%ld): Failed to retrieve fcport " @@ -2003,7 +2022,10 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) /* Check for matching device in port list. */ found = 0; fcport = NULL; - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { + if (fcport->vp_idx != ha->vp_idx) + continue; + if (memcmp(new_fcport->port_name, fcport->port_name, WWN_SIZE)) continue; @@ -2023,7 +2045,13 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) if (!found) { /* New device, add to fcports list. */ new_fcport->flags &= ~FCF_PERSISTENT_BOUND; - list_add_tail(&new_fcport->list, &ha->fcports); + if (ha->parent) { + new_fcport->ha = ha; + new_fcport->vp_idx = ha->vp_idx; + list_add_tail(&new_fcport->vp_fcport, + &ha->vp_fcports); + } + list_add_tail(&new_fcport->list, &pha->fcports); /* Allocate a new replacement fcport. */ fcport = new_fcport; @@ -2199,11 +2227,13 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) void qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) { + scsi_qla_host_t *pha = to_qla_parent(ha); + fcport->ha = ha; fcport->login_retry = 0; - fcport->port_login_retry_count = ha->port_down_retry_count * + fcport->port_login_retry_count = pha->port_down_retry_count * PORT_RETRY_TIME; - atomic_set(&fcport->port_down_timer, ha->port_down_retry_count * + atomic_set(&fcport->port_down_timer, pha->port_down_retry_count * PORT_RETRY_TIME); fcport->flags &= ~FCF_LOGIN_NEEDED; @@ -2234,6 +2264,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) uint16_t mb[MAILBOX_REGISTER_COUNT]; uint16_t loop_id; LIST_HEAD(new_fcports); + scsi_qla_host_t *pha = to_qla_parent(ha); /* If FL port exists, then SNS is present */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) @@ -2307,7 +2338,10 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) * Logout all previous fabric devices marked lost, except * tape devices. */ - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { + if (fcport->vp_idx !=ha->vp_idx) + continue; + if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) break; @@ -2332,13 +2366,16 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) } /* Starting free loop ID. */ - next_loopid = ha->min_external_loopid; + next_loopid = pha->min_external_loopid; /* * Scan through our port list and login entries that need to be * logged in. */ - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { + if (fcport->vp_idx != ha->vp_idx) + continue; + if (atomic_read(&ha->loop_down_timer) || test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) break; @@ -2380,11 +2417,18 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) break; } - /* Remove device from the new list and add it to DB */ - list_move_tail(&fcport->list, &ha->fcports); - /* Login and update database */ qla2x00_fabric_dev_login(ha, fcport, &next_loopid); + + if (ha->parent) { + fcport->ha = ha; + fcport->vp_idx = ha->vp_idx; + list_add_tail(&fcport->vp_fcport, + &ha->vp_fcports); + list_move_tail(&fcport->list, + &ha->parent->fcports); + } else + list_move_tail(&fcport->list, &ha->fcports); } } while (0); @@ -2428,6 +2472,11 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) int swl_idx; int first_dev, last_dev; port_id_t wrap, nxt_d_id; + int vp_index; + int empty_vp_index; + int found_vp; + scsi_qla_host_t *vha; + scsi_qla_host_t *pha = to_qla_parent(ha); rval = QLA_SUCCESS; @@ -2461,13 +2510,13 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) return (QLA_MEMORY_ALLOC_FAILED); } new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); - + new_fcport->vp_idx = ha->vp_idx; /* Set start port ID scan at adapter ID. */ first_dev = 1; last_dev = 0; /* Starting free loop ID. */ - loop_id = ha->min_external_loopid; + loop_id = pha->min_external_loopid; for (; loop_id <= ha->last_loop_id; loop_id++) { if (qla2x00_is_reserved_id(ha, loop_id)) continue; @@ -2521,10 +2570,42 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) break; } - /* Bypass if host adapter. */ - if (new_fcport->d_id.b24 == ha->d_id.b24) + /* Bypass if same physical adapter. */ + if (new_fcport->d_id.b24 == pha->d_id.b24) continue; + /* Bypass virtual ports of the same host. */ + if (pha->num_vhosts) { + vp_index = find_next_bit( + (unsigned long *)pha->vp_idx_map, + MAX_MULTI_ID_FABRIC + 1, 1); + + for (;vp_index <= MAX_MULTI_ID_FABRIC; + vp_index = find_next_bit( + (unsigned long *)pha->vp_idx_map, + MAX_MULTI_ID_FABRIC + 1, vp_index + 1)) { + empty_vp_index = 1; + found_vp = 0; + list_for_each_entry(vha, &pha->vp_list, + vp_list) { + if (vp_index == vha->vp_idx) { + empty_vp_index = 0; + found_vp = 1; + break; + } + } + + if (empty_vp_index) + continue; + + if (found_vp && + new_fcport->d_id.b24 == vha->d_id.b24) + break; + } + if (vp_index <= MAX_MULTI_ID_FABRIC) + continue; + } + /* Bypass if same domain and area of adapter. */ if (((new_fcport->d_id.b24 & 0xffff00) == (ha->d_id.b24 & 0xffff00)) && ha->current_topology == @@ -2537,7 +2618,9 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) /* Locate matching device in database. */ found = 0; - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { + if (new_fcport->vp_idx != fcport->vp_idx) + continue; if (memcmp(new_fcport->port_name, fcport->port_name, WWN_SIZE)) continue; @@ -2605,6 +2688,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) } new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); new_fcport->d_id.b24 = nxt_d_id.b24; + new_fcport->vp_idx = ha->vp_idx; } kfree(swl); @@ -2637,6 +2721,7 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev) int found; fc_port_t *fcport; uint16_t first_loop_id; + scsi_qla_host_t *pha = to_qla_parent(ha); rval = QLA_SUCCESS; @@ -2663,7 +2748,7 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev) /* Check for loop ID being already in use. */ found = 0; fcport = NULL; - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { if (fcport->loop_id == dev->loop_id && fcport != dev) { /* ID possibly in use */ found++; @@ -2710,6 +2795,7 @@ qla2x00_device_resync(scsi_qla_host_t *ha) uint8_t rscn_out_iter; uint8_t format; port_id_t d_id; + scsi_qla_host_t *pha = to_qla_parent(ha); rval = QLA_RSCNS_HANDLED; @@ -2776,7 +2862,10 @@ qla2x00_device_resync(scsi_qla_host_t *ha) rval = QLA_SUCCESS; - list_for_each_entry(fcport, &ha->fcports, list) { + list_for_each_entry(fcport, &pha->fcports, list) { + if (fcport->vp_idx != ha->vp_idx) + continue; + if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || (fcport->d_id.b24 & mask) != d_id.b24 || fcport->port_type == FCT_BROADCAST) @@ -3940,3 +4029,40 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) ret = qla2x00_stop_firmware(ha); } } + +int +qla24xx_configure_vhba(scsi_qla_host_t *ha) +{ + int rval = QLA_SUCCESS; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + + if (!ha->parent) + return -EINVAL; + + rval = qla2x00_fw_ready(ha); + if (rval == QLA_SUCCESS) { + clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); + qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); + } + + ha->flags.management_server_logged_in = 0; + + /* Login to SNS first */ + qla24xx_login_fabric(ha, NPH_SNS, 0xff, 0xff, 0xfc, + mb, BIT_1); + if (mb[0] != MBS_COMMAND_COMPLETE) { + DEBUG15(qla_printk(KERN_INFO, ha, + "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x " + "mb[2]=%x mb[6]=%x mb[7]=%x\n", NPH_SNS, + mb[0], mb[1], mb[2], mb[6], mb[7])); + return (QLA_FUNCTION_FAILED); + } + + atomic_set(&ha->loop_down_timer, 0); + atomic_set(&ha->loop_state, LOOP_UP); + set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); + rval = qla2x00_loop_resync(ha); + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index c517a1478e..c71863ff54 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -179,7 +179,6 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address; /* Load data segments */ - scsi_for_each_sg(cmd, sg, tot_dsds, i) { cont_entry_t *cont_pkt; @@ -316,9 +315,14 @@ qla2x00_start_scsi(srb_t *sp) goto queuing_error; /* Map the sg table so we have an accurate count of sg entries needed */ - nseg = scsi_dma_map(cmd); - if (nseg < 0) - goto queuing_error; + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + } else + nseg = 0; + tot_dsds = nseg; /* Calculate the number of request entries needed. */ @@ -414,9 +418,10 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, { mrk_entry_t *mrk; struct mrk_entry_24xx *mrk24; + scsi_qla_host_t *pha = to_qla_parent(ha); mrk24 = NULL; - mrk = (mrk_entry_t *)qla2x00_req_pkt(ha); + mrk = (mrk_entry_t *)qla2x00_req_pkt(pha); if (mrk == NULL) { DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n", __func__, ha->host_no)); @@ -433,6 +438,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, mrk24->lun[1] = LSB(lun); mrk24->lun[2] = MSB(lun); host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun)); + mrk24->vp_index = ha->vp_idx; } else { SET_TARGET_ID(ha, mrk->target, loop_id); mrk->lun = cpu_to_le16(lun); @@ -440,7 +446,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, } wmb(); - qla2x00_isp_cmd(ha); + qla2x00_isp_cmd(pha); return (QLA_SUCCESS); } @@ -712,9 +718,14 @@ qla24xx_start_scsi(srb_t *sp) goto queuing_error; /* Map the sg table so we have an accurate count of sg entries needed */ - nseg = scsi_dma_map(cmd); - if (nseg < 0) + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) goto queuing_error; + } else + nseg = 0; + tot_dsds = nseg; req_cnt = qla24xx_calc_iocbs(tot_dsds); @@ -750,6 +761,7 @@ qla24xx_start_scsi(srb_t *sp) cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vp_idx; int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 6ce532cdc4..0ba4c8d378 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -9,7 +9,6 @@ #include static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t); -static void qla2x00_async_event(scsi_qla_host_t *, uint16_t *); static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t); static void qla2x00_status_entry(scsi_qla_host_t *, void *); static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *); @@ -244,7 +243,7 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0) * @ha: SCSI driver HA context * @mb: Mailbox registers (0 - 3) */ -static void +void qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) { #define LS_UNKNOWN 2 @@ -386,6 +385,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_mark_all_devices_lost(ha, 1); } + if (ha->parent) { + atomic_set(&ha->vp_state, VP_FAILED); + fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + } + set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); ha->flags.management_server_logged_in = 0; @@ -422,6 +426,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_mark_all_devices_lost(ha, 1); } + if (ha->parent) { + atomic_set(&ha->vp_state, VP_FAILED); + fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + } + ha->flags.management_server_logged_in = 0; ha->link_data_rate = PORT_SPEED_UNKNOWN; if (ql2xfdmienable) @@ -440,6 +449,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_mark_all_devices_lost(ha, 1); } + if (ha->parent) { + atomic_set(&ha->vp_state, VP_FAILED); + fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + } + set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); ha->operating_mode = LOOP; @@ -465,6 +479,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_mark_all_devices_lost(ha, 1); } + if (ha->parent) { + atomic_set(&ha->vp_state, VP_FAILED); + fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + } + if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) { set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); } @@ -491,6 +510,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_mark_all_devices_lost(ha, 1); } + if (ha->parent) { + atomic_set(&ha->vp_state, VP_FAILED); + fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED); + } + set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); break; @@ -530,6 +554,10 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) break; case MBA_RSCN_UPDATE: /* State Change Registration */ + /* Check if the Vport has issued a SCR */ + if (ha->parent && test_bit(VP_SCR_NEEDED, &ha->vp_flags)) + break; + DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n", ha->host_no)); DEBUG(printk(KERN_INFO @@ -589,6 +617,9 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->host_no, mb[1], mb[2])); break; } + + if (!ha->parent && ha->num_vhosts) + qla2x00_alert_all_vps(ha, mb); } static void @@ -1393,6 +1424,10 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) case MS_IOCB_TYPE: qla24xx_ms_entry(ha, (struct ct_entry_24xx *)pkt); break; + case VP_RPT_ID_IOCB_TYPE: + qla24xx_report_id_acquisition(ha, + (struct vp_rpt_id_entry_24xx *)pkt); + break; default: /* Type Not Supported. */ DEBUG4(printk(KERN_WARNING diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 71e32a2485..2cd0cff259 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -42,25 +42,29 @@ qla2x00_mbx_sem_timeout(unsigned long data) * Kernel context. */ static int -qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) +qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) { int rval; unsigned long flags = 0; - device_reg_t __iomem *reg = ha->iobase; + device_reg_t __iomem *reg; struct timer_list tmp_intr_timer; uint8_t abort_active; - uint8_t io_lock_on = ha->flags.init_done; + uint8_t io_lock_on; uint16_t command; uint16_t *iptr; uint16_t __iomem *optr; uint32_t cnt; uint32_t mboxes; unsigned long wait_time; + scsi_qla_host_t *ha = to_qla_parent(pvha); + + reg = ha->iobase; + io_lock_on = ha->flags.init_done; rval = QLA_SUCCESS; abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + DEBUG11(printk("%s(%ld): entered.\n", __func__, pvha->host_no)); /* * Wait for active mailbox commands to finish by waiting at most tov @@ -889,7 +893,7 @@ qla2x00_abort_target(fc_port_t *fcport) */ int qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, - uint8_t *area, uint8_t *domain, uint16_t *top) + uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap) { int rval; mbx_cmd_t mc; @@ -899,8 +903,9 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, ha->host_no)); mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID; + mcp->mb[9] = ha->vp_idx; mcp->out_mb = MBX_0; - mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = 30; mcp->flags = 0; rval = qla2x00_mailbox_command(ha, mcp); @@ -913,6 +918,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, *area = MSB(mcp->mb[2]); *domain = LSB(mcp->mb[3]); *top = mcp->mb[6]; + *sw_cap = mcp->mb[7]; if (rval != QLA_SUCCESS) { /*EMPTY*/ @@ -1009,7 +1015,11 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", ha->host_no)); - mcp->mb[0] = MBC_INITIALIZE_FIRMWARE; + if (ha->flags.npiv_supported) + mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE; + else + mcp->mb[0] = MBC_INITIALIZE_FIRMWARE; + mcp->mb[2] = MSW(ha->init_cb_dma); mcp->mb[3] = LSW(ha->init_cb_dma); mcp->mb[4] = 0; @@ -1081,7 +1091,8 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) mcp->mb[3] = LSW(pd_dma); mcp->mb[6] = MSW(MSD(pd_dma)); mcp->mb[7] = LSW(MSD(pd_dma)); - mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->mb[9] = ha->vp_idx; + mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_0; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { mcp->mb[1] = fcport->loop_id; @@ -1259,7 +1270,8 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name, ha->host_no)); mcp->mb[0] = MBC_GET_PORT_NAME; - mcp->out_mb = MBX_1|MBX_0; + mcp->mb[9] = ha->vp_idx; + mcp->out_mb = MBX_9|MBX_1|MBX_0; if (HAS_EXTENDED_IDS(ha)) { mcp->mb[1] = loop_id; mcp->mb[10] = opt; @@ -1447,6 +1459,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; + lg->vp_index = cpu_to_le16(ha->vp_idx); rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB " @@ -1701,6 +1714,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; + lg->vp_index = cpu_to_le16(ha->vp_idx); rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB " @@ -1863,7 +1877,8 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma, mcp->mb[6] = MSW(MSD(id_list_dma)); mcp->mb[7] = LSW(MSD(id_list_dma)); mcp->mb[8] = 0; - mcp->out_mb |= MBX_8|MBX_7|MBX_6|MBX_3|MBX_2; + mcp->mb[9] = ha->vp_idx; + mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2; } else { mcp->mb[1] = MSW(id_list_dma); mcp->mb[2] = LSW(id_list_dma); @@ -2212,6 +2227,7 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp) abt->port_id[0] = fcport->d_id.b.al_pa; abt->port_id[1] = fcport->d_id.b.area; abt->port_id[2] = fcport->d_id.b.domain; + abt->vp_index = fcport->vp_idx; rval = qla2x00_issue_iocb(ha, abt, abt_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n", @@ -2249,7 +2265,7 @@ qla24xx_abort_target(fc_port_t *fcport) int rval; struct tsk_mgmt_cmd *tsk; dma_addr_t tsk_dma; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *pha; if (fcport == NULL) return 0; @@ -2257,7 +2273,8 @@ qla24xx_abort_target(fc_port_t *fcport) DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no)); ha = fcport->ha; - tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma); + pha = to_qla_parent(ha); + tsk = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &tsk_dma); if (tsk == NULL) { DEBUG2_3(printk("%s(%ld): failed to allocate Task Management " "IOCB.\n", __func__, ha->host_no)); @@ -2273,6 +2290,8 @@ qla24xx_abort_target(fc_port_t *fcport) tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa; tsk->p.tsk.port_id[1] = fcport->d_id.b.area; tsk->p.tsk.port_id[2] = fcport->d_id.b.domain; + tsk->p.tsk.vp_index = fcport->vp_idx; + rval = qla2x00_issue_iocb(ha, tsk, tsk_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue Target Reset IOCB " @@ -2303,7 +2322,7 @@ qla24xx_abort_target(fc_port_t *fcport) } atarget_done: - dma_pool_free(ha->s_dma_pool, tsk, tsk_dma); + dma_pool_free(pha->s_dma_pool, tsk, tsk_dma); return rval; } @@ -2610,3 +2629,354 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, return rval; } + +/* + * qla24xx_get_vp_database + * Get the VP's database for all configured ports. + * + * Input: + * ha = adapter block pointer. + * size = size of initialization control block. + * + * Returns: + * qla2x00 local function return status code. + * + * Context: + * Kernel context. + */ +int +qla24xx_get_vp_database(scsi_qla_host_t *ha, uint16_t size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("scsi(%ld):%s - entered.\n", + ha->host_no, __func__)); + + mcp->mb[0] = MBC_MID_GET_VP_DATABASE; + mcp->mb[2] = MSW(ha->init_cb_dma); + mcp->mb[3] = LSW(ha->init_cb_dma); + mcp->mb[4] = 0; + mcp->mb[5] = 0; + mcp->mb[6] = MSW(MSD(ha->init_cb_dma)); + mcp->mb[7] = LSW(MSD(ha->init_cb_dma)); + mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->buf_size = size; + mcp->flags = MBX_DMA_OUT; + mcp->tov = MBX_TOV_SECONDS; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval != QLA_SUCCESS) { + /*EMPTY*/ + DEBUG2_3_11(printk("%s(%ld): failed=%x " + "mb0=%x.\n", + __func__, ha->host_no, rval, mcp->mb[0])); + } else { + /*EMPTY*/ + DEBUG11(printk("%s(%ld): done.\n", + __func__, ha->host_no)); + } + + return rval; +} + +int +qla24xx_get_vp_entry(scsi_qla_host_t *ha, uint16_t size, int vp_id) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_MID_GET_VP_ENTRY; + mcp->mb[2] = MSW(ha->init_cb_dma); + mcp->mb[3] = LSW(ha->init_cb_dma); + mcp->mb[4] = 0; + mcp->mb[5] = 0; + mcp->mb[6] = MSW(MSD(ha->init_cb_dma)); + mcp->mb[7] = LSW(MSD(ha->init_cb_dma)); + mcp->mb[9] = vp_id; + mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->in_mb = MBX_0; + mcp->buf_size = size; + mcp->flags = MBX_DMA_OUT; + mcp->tov = 30; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval != QLA_SUCCESS) { + /*EMPTY*/ + DEBUG2_3_11(printk("qla24xx_get_vp_entry(%ld): failed=%x " + "mb0=%x.\n", + ha->host_no, rval, mcp->mb[0])); + } else { + /*EMPTY*/ + DEBUG11(printk("qla24xx_get_vp_entry(%ld): done.\n", + ha->host_no)); + } + + return rval; +} + +void +qla24xx_report_id_acquisition(scsi_qla_host_t *ha, + struct vp_rpt_id_entry_24xx *rptid_entry) +{ + uint8_t vp_idx; + scsi_qla_host_t *vha; + + if (rptid_entry->entry_status != 0) + return; + if (rptid_entry->entry_status != __constant_cpu_to_le16(CS_COMPLETE)) + return; + + if (rptid_entry->format == 0) { + DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d," + " number of VPs acquired %d\n", __func__, ha->host_no, + MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count))); + DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__, + rptid_entry->port_id[2], rptid_entry->port_id[1], + rptid_entry->port_id[0])); + } else if (rptid_entry->format == 1) { + vp_idx = LSB(rptid_entry->vp_idx); + DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled " + "- status %d - " + "with port id %02x%02x%02x\n",__func__,ha->host_no, + vp_idx, MSB(rptid_entry->vp_idx), + rptid_entry->port_id[2], rptid_entry->port_id[1], + rptid_entry->port_id[0])); + if (vp_idx == 0) + return; + + if (MSB(rptid_entry->vp_idx) == 1) + return; + + list_for_each_entry(vha, &ha->vp_list, vp_list) + if (vp_idx == vha->vp_idx) + break; + + if (!vha) + return; + + vha->d_id.b.domain = rptid_entry->port_id[2]; + vha->d_id.b.area = rptid_entry->port_id[1]; + vha->d_id.b.al_pa = rptid_entry->port_id[0]; + + /* + * Cannot configure here as we are still sitting on the + * response queue. Handle it in dpc context. + */ + set_bit(VP_IDX_ACQUIRED, &vha->vp_flags); + set_bit(VP_DPC_NEEDED, &ha->dpc_flags); + + wake_up_process(ha->dpc_thread); + } +} + +/* + * qla24xx_modify_vp_config + * Change VP configuration for vha + * + * Input: + * vha = adapter block pointer. + * + * Returns: + * qla2xxx local function return status code. + * + * Context: + * Kernel context. + */ +int +qla24xx_modify_vp_config(scsi_qla_host_t *vha) +{ + int rval; + struct vp_config_entry_24xx *vpmod; + dma_addr_t vpmod_dma; + scsi_qla_host_t *pha; + + /* This can be called by the parent */ + pha = to_qla_parent(vha); + + vpmod = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &vpmod_dma); + if (!vpmod) { + DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP " + "IOCB.\n", __func__, pha->host_no)); + return QLA_MEMORY_ALLOC_FAILED; + } + + memset(vpmod, 0, sizeof(struct vp_config_entry_24xx)); + vpmod->entry_type = VP_CONFIG_IOCB_TYPE; + vpmod->entry_count = 1; + vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS; + vpmod->vp_count = 1; + vpmod->vp_index1 = vha->vp_idx; + vpmod->options_idx1 = BIT_3|BIT_4|BIT_5; + memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE); + memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE); + vpmod->entry_count = 1; + + rval = qla2x00_issue_iocb(pha, vpmod, vpmod_dma, 0); + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB" + "(%x).\n", __func__, pha->host_no, rval)); + } else if (vpmod->comp_status != 0) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- error status (%x).\n", __func__, pha->host_no, + vpmod->comp_status)); + rval = QLA_FUNCTION_FAILED; + } else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- completion status (%x).\n", __func__, pha->host_no, + le16_to_cpu(vpmod->comp_status))); + rval = QLA_FUNCTION_FAILED; + } else { + /* EMPTY */ + DEBUG11(printk("%s(%ld): done.\n", __func__, pha->host_no)); + fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING); + } + dma_pool_free(pha->s_dma_pool, vpmod, vpmod_dma); + + return rval; +} + +/* + * qla24xx_control_vp + * Enable a virtual port for given host + * + * Input: + * ha = adapter block pointer. + * vhba = virtual adapter (unused) + * index = index number for enabled VP + * + * Returns: + * qla2xxx local function return status code. + * + * Context: + * Kernel context. + */ +int +qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) +{ + int rval; + int map, pos; + struct vp_ctrl_entry_24xx *vce; + dma_addr_t vce_dma; + scsi_qla_host_t *ha = vha->parent; + int vp_index = vha->vp_idx; + + DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__, + ha->host_no, vp_index)); + + if (vp_index == 0 || vp_index >= MAX_MULTI_ID_LOOP) + return QLA_PARAMETER_ERROR; + + vce = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vce_dma); + if (!vce) { + DEBUG2_3(printk("%s(%ld): " + "failed to allocate VP Control IOCB.\n", __func__, + ha->host_no)); + return QLA_MEMORY_ALLOC_FAILED; + } + memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx)); + + vce->entry_type = VP_CTRL_IOCB_TYPE; + vce->entry_count = 1; + vce->command = cpu_to_le16(cmd); + vce->vp_count = __constant_cpu_to_le16(1); + + /* index map in firmware starts with 1; decrement index + * this is ok as we never use index 0 + */ + map = (vp_index - 1) / 8; + pos = (vp_index - 1) & 7; + down(&ha->vport_sem); + vce->vp_idx_map[map] |= 1 << pos; + up(&ha->vport_sem); + + rval = qla2x00_issue_iocb(ha, vce, vce_dma, 0); + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB" + "(%x).\n", __func__, ha->host_no, rval)); + printk("%s(%ld): failed to issue VP control IOCB" + "(%x).\n", __func__, ha->host_no, rval); + } else if (vce->entry_status != 0) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- error status (%x).\n", __func__, ha->host_no, + vce->entry_status)); + printk("%s(%ld): failed to complete IOCB " + "-- error status (%x).\n", __func__, ha->host_no, + vce->entry_status); + rval = QLA_FUNCTION_FAILED; + } else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- completion status (%x).\n", __func__, ha->host_no, + le16_to_cpu(vce->comp_status))); + printk("%s(%ld): failed to complete IOCB " + "-- completion status (%x).\n", __func__, ha->host_no, + le16_to_cpu(vce->comp_status)); + rval = QLA_FUNCTION_FAILED; + } else { + DEBUG2(printk("%s(%ld): done.\n", __func__, ha->host_no)); + } + + dma_pool_free(ha->s_dma_pool, vce, vce_dma); + + return rval; +} + +/* + * qla2x00_send_change_request + * Receive or disable RSCN request from fabric controller + * + * Input: + * ha = adapter block pointer + * format = registration format: + * 0 - Reserved + * 1 - Fabric detected registration + * 2 - N_port detected registration + * 3 - Full registration + * FF - clear registration + * vp_idx = Virtual port index + * + * Returns: + * qla2x00 local function return status code. + * + * Context: + * Kernel Context + */ + +int +qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format, + uint16_t vp_idx) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + /* + * This command is implicitly executed by firmware during login for the + * physical hosts + */ + if (vp_idx == 0) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_SEND_CHANGE_REQUEST; + mcp->mb[1] = format; + mcp->mb[9] = vp_idx; + mcp->out_mb = MBX_9|MBX_1|MBX_0; + mcp->in_mb = MBX_0|MBX_1; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval == QLA_SUCCESS) { + if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { + rval = BIT_1; + } + } else + rval = BIT_1; + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c new file mode 100644 index 0000000000..54dc415d8b --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -0,0 +1,497 @@ +/* + * QLOGIC LINUX SOFTWARE + * + * QLogic ISP2x00 device driver for Linux 2.6.x + * Copyright (C) 2003-2005 QLogic Corporation + * (www.qlogic.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include "qla_def.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +void qla2x00_vp_stop_timer(scsi_qla_host_t *); + +void +qla2x00_vp_stop_timer(scsi_qla_host_t *vha) +{ + if (vha->parent && vha->timer_active) { + del_timer_sync(&vha->timer); + vha->timer_active = 0; + } +} + +uint32_t +qla24xx_allocate_vp_id(scsi_qla_host_t *vha) +{ + uint32_t vp_id; + scsi_qla_host_t *ha = vha->parent; + + /* Find an empty slot and assign an vp_id */ + down(&ha->vport_sem); + vp_id = find_first_zero_bit((unsigned long *)ha->vp_idx_map, + MAX_MULTI_ID_FABRIC); + if (vp_id > MAX_MULTI_ID_FABRIC) { + DEBUG15(printk ("vp_id %d is bigger than MAX_MULTI_ID_FABRID\n", + vp_id)); + up(&ha->vport_sem); + return vp_id; + } + + set_bit(vp_id, (unsigned long *)ha->vp_idx_map); + ha->num_vhosts++; + vha->vp_idx = vp_id; + list_add_tail(&vha->vp_list, &ha->vp_list); + up(&ha->vport_sem); + return vp_id; +} + +void +qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) +{ + uint16_t vp_id; + scsi_qla_host_t *ha = vha->parent; + + down(&ha->vport_sem); + vp_id = vha->vp_idx; + ha->num_vhosts--; + clear_bit(vp_id, (unsigned long *)ha->vp_idx_map); + list_del(&vha->vp_list); + up(&ha->vport_sem); +} + +scsi_qla_host_t * +qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name) +{ + scsi_qla_host_t *vha; + + /* Locate matching device in database. */ + list_for_each_entry(vha, &ha->vp_list, vp_list) { + if (!memcmp(port_name, vha->port_name, WWN_SIZE)) + return vha; + } + return NULL; +} + +/* + * qla2x00_mark_vp_devices_dead + * Updates fcport state when device goes offline. + * + * Input: + * ha = adapter block pointer. + * fcport = port structure pointer. + * + * Return: + * None. + * + * Context: + */ +void +qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) +{ + fc_port_t *fcport; + scsi_qla_host_t *pha = to_qla_parent(vha); + + list_for_each_entry(fcport, &pha->fcports, list) { + if (fcport->vp_idx != vha->vp_idx) + continue; + + DEBUG15(printk("scsi(%ld): Marking port dead, " + "loop_id=0x%04x :%x\n", + vha->host_no, fcport->loop_id, fcport->vp_idx)); + + atomic_set(&fcport->state, FCS_DEVICE_DEAD); + qla2x00_mark_device_lost(vha, fcport, 0, 0); + } +} + +int +qla24xx_disable_vp(scsi_qla_host_t *vha) +{ + int ret; + + ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + + /* Delete all vp's fcports from parent's list */ + qla2x00_mark_vp_devices_dead(vha); + atomic_set(&vha->vp_state, VP_FAILED); + vha->flags.management_server_logged_in = 0; + if (ret == QLA_SUCCESS) { + fc_vport_set_state(vha->fc_vport, FC_VPORT_DISABLED); + } else { + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); + return -1; + } + return 0; +} + +int +qla24xx_enable_vp(scsi_qla_host_t *vha) +{ + int ret; + scsi_qla_host_t *ha = vha->parent; + + /* Check if physical ha port is Up */ + if (atomic_read(&ha->loop_state) == LOOP_DOWN || + atomic_read(&ha->loop_state) == LOOP_DEAD ) { + vha->vp_err_state = VP_ERR_PORTDWN; + fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN); + goto enable_failed; + } + + /* Initialize the new vport unless it is a persistent port */ + down(&ha->vport_sem); + ret = qla24xx_modify_vp_config(vha); + up(&ha->vport_sem); + + if (ret != QLA_SUCCESS) { + fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); + goto enable_failed; + } + + DEBUG15(qla_printk(KERN_INFO, ha, + "Virtual port with id: %d - Enabled\n", vha->vp_idx)); + return 0; + +enable_failed: + DEBUG15(qla_printk(KERN_INFO, ha, + "Virtual port with id: %d - Disabled\n", vha->vp_idx)); + return 1; +} + +/** + * qla24xx_modify_vport() - Modifies the virtual fabric port's configuration + * @ha: HA context + * @vp: pointer to buffer of virtual port parameters. + * @ret_code: return error code: + * + * Returns the virtual port id, or MAX_VSAN_ID, if couldn't create. + */ +uint32_t +qla24xx_modify_vhba(scsi_qla_host_t *ha, vport_params_t *vp, uint32_t *vp_id) +{ + scsi_qla_host_t *vha; + + vha = qla24xx_find_vhost_by_name(ha, vp->port_name); + if (!vha) { + *vp_id = MAX_NUM_VPORT_LOOP; + return VP_RET_CODE_WWPN; + } + + if (qla24xx_enable_vp(vha)) { + scsi_host_put(vha->host); + qla2x00_mem_free(vha); + *vp_id = MAX_NUM_VPORT_LOOP; + return VP_RET_CODE_RESOURCES; + } + + *vp_id = vha->vp_idx; + return VP_RET_CODE_OK; +} + +void +qla24xx_configure_vp(scsi_qla_host_t *vha) +{ + struct fc_vport *fc_vport; + int ret; + + fc_vport = vha->fc_vport; + + DEBUG15(printk("scsi(%ld): %s: change request #3 for this host.\n", + vha->host_no, __func__)); + ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx); + if (ret != QLA_SUCCESS) { + DEBUG15(qla_printk(KERN_ERR, vha, "Failed to enable receiving" + " of RSCN requests: 0x%x\n", ret)); + return; + } else { + /* Corresponds to SCR enabled */ + clear_bit(VP_SCR_NEEDED, &vha->vp_flags); + } + + vha->flags.online = 1; + if (qla24xx_configure_vhba(vha)) + return; + + atomic_set(&vha->vp_state, VP_ACTIVE); + fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE); +} + +void +qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb) +{ + int i, vp_idx_matched; + scsi_qla_host_t *vha; + + if (ha->parent) + return; + + i = find_next_bit((unsigned long *)ha->vp_idx_map, + MAX_MULTI_ID_FABRIC + 1, 1); + for (;i <= MAX_MULTI_ID_FABRIC; + i = find_next_bit((unsigned long *)ha->vp_idx_map, + MAX_MULTI_ID_FABRIC + 1, i + 1)) { + vp_idx_matched = 0; + + list_for_each_entry(vha, &ha->vp_list, vp_list) { + if (i == vha->vp_idx) { + vp_idx_matched = 1; + break; + } + } + + if (vp_idx_matched) { + switch (mb[0]) { + case MBA_LIP_OCCURRED: + case MBA_LOOP_UP: + case MBA_LOOP_DOWN: + case MBA_LIP_RESET: + case MBA_POINT_TO_POINT: + case MBA_CHG_IN_CONNECTION: + case MBA_PORT_UPDATE: + case MBA_RSCN_UPDATE: + DEBUG15(printk("scsi(%ld)%s: Async_event for" + " VP[%d], mb = 0x%x, vha=%p\n", + vha->host_no, __func__,i, *mb, vha)); + qla2x00_async_event(vha, mb); + break; + } + } + } +} + +void +qla2x00_vp_abort_isp(scsi_qla_host_t *vha) +{ + /* + * Physical port will do most of the abort and recovery work. We can + * just treat it as a loop down + */ + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + qla2x00_mark_all_devices_lost(vha, 0); + } else { + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + } + + DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n", + vha->host_no, vha->vp_idx)); + qla24xx_enable_vp(vha); +} + +int +qla2x00_do_dpc_vp(scsi_qla_host_t *vha) +{ + if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) { + /* VP acquired. complete port configuration */ + qla24xx_configure_vp(vha); + return 0; + } + + if (test_and_clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) + qla2x00_vp_abort_isp(vha); + + if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) && + (!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) { + clear_bit(RESET_ACTIVE, &vha->dpc_flags); + } + + if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { + if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) { + qla2x00_loop_resync(vha); + clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags); + } + } + + return 0; +} + +void +qla2x00_do_dpc_all_vps(scsi_qla_host_t *ha) +{ + int ret; + int i, vp_idx_matched; + scsi_qla_host_t *vha; + + if (ha->parent) + return; + if (list_empty(&ha->vp_list)) + return; + + clear_bit(VP_DPC_NEEDED, &ha->dpc_flags); + + i = find_next_bit((unsigned long *)ha->vp_idx_map, + MAX_MULTI_ID_FABRIC + 1, 1); + for (;i <= MAX_MULTI_ID_FABRIC; + i = find_next_bit((unsigned long *)ha->vp_idx_map, + MAX_MULTI_ID_FABRIC + 1, i + 1)) { + vp_idx_matched = 0; + + list_for_each_entry(vha, &ha->vp_list, vp_list) { + if (i == vha->vp_idx) { + vp_idx_matched = 1; + break; + } + } + + if (vp_idx_matched) + ret = qla2x00_do_dpc_vp(vha); + } +} + +int +qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) +{ + scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata; + scsi_qla_host_t *vha; + uint8_t port_name[WWN_SIZE]; + + if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR) + return VPCERR_UNSUPPORTED; + + /* Check up the F/W and H/W support NPIV */ + if (!ha->flags.npiv_supported) + return VPCERR_UNSUPPORTED; + + /* Check up whether npiv supported switch presented */ + if (!(ha->switch_cap & FLOGI_MID_SUPPORT)) + return VPCERR_NO_FABRIC_SUPP; + + /* Check up unique WWPN */ + u64_to_wwn(fc_vport->port_name, port_name); + vha = qla24xx_find_vhost_by_name(ha, port_name); + if (vha) + return VPCERR_BAD_WWN; + + /* Check up max-npiv-supports */ + if (ha->num_vhosts > ha->max_npiv_vports) { + DEBUG15(printk("scsi(%ld): num_vhosts %d is bigger than " + "max_npv_vports %d.\n", ha->host_no, + (uint16_t) ha->num_vhosts, (int) ha->max_npiv_vports)); + return VPCERR_UNSUPPORTED; + } + return 0; +} + +scsi_qla_host_t * +qla24xx_create_vhost(struct fc_vport *fc_vport) +{ + scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata; + scsi_qla_host_t *vha; + struct Scsi_Host *host; + + host = scsi_host_alloc(&qla24xx_driver_template, + sizeof(scsi_qla_host_t)); + if (!host) { + printk(KERN_WARNING + "qla2xxx: scsi_host_alloc() failed for vport\n"); + return(NULL); + } + + vha = (scsi_qla_host_t *)host->hostdata; + + /* clone the parent hba */ + memcpy(vha, ha, sizeof (scsi_qla_host_t)); + + fc_vport->dd_data = vha; + + vha->node_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL); + if (!vha->node_name) + goto create_vhost_failed_1; + + vha->port_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL); + if (!vha->port_name) + goto create_vhost_failed_2; + + /* New host info */ + u64_to_wwn(fc_vport->node_name, vha->node_name); + u64_to_wwn(fc_vport->port_name, vha->port_name); + + vha->host = host; + vha->host_no = host->host_no; + vha->parent = ha; + vha->fc_vport = fc_vport; + vha->device_flags = 0; + vha->instance = num_hosts; + vha->vp_idx = qla24xx_allocate_vp_id(vha); + if (vha->vp_idx > ha->max_npiv_vports) { + DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", + vha->host_no)); + goto create_vhost_failed_3; + } + vha->mgmt_svr_loop_id = 10 + vha->vp_idx; + + init_MUTEX(&vha->mbx_cmd_sem); + init_MUTEX_LOCKED(&vha->mbx_intr_sem); + + INIT_LIST_HEAD(&vha->list); + INIT_LIST_HEAD(&vha->fcports); + INIT_LIST_HEAD(&vha->vp_fcports); + + vha->dpc_flags = 0L; + set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); + set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); + + /* + * To fix the issue of processing a parent's RSCN for the vport before + * its SCR is complete. + */ + set_bit(VP_SCR_NEEDED, &vha->vp_flags); + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + + qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL); + + host->can_queue = vha->request_q_length + 128; + host->this_id = 255; + host->cmd_per_lun = 3; + host->max_cmd_len = MAX_CMDSZ; + host->max_channel = MAX_BUSES - 1; + host->max_lun = MAX_LUNS; + host->unique_id = vha->instance; + host->max_id = MAX_TARGETS_2200; + host->transportt = qla2xxx_transport_vport_template; + + DEBUG15(printk("DEBUG: detect vport hba %ld at address = %p\n", + vha->host_no, vha)); + + vha->flags.init_done = 1; + num_hosts++; + + down(&ha->vport_sem); + set_bit(vha->vp_idx, (unsigned long *)ha->vp_idx_map); + ha->cur_vport_count++; + up(&ha->vport_sem); + + return vha; + +create_vhost_failed_3: + kfree(vha->port_name); + +create_vhost_failed_2: + kfree(vha->node_name); + +create_vhost_failed_1: + return NULL; +} diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 18baa5bf69..a8658b1d28 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -29,8 +29,7 @@ static struct kmem_cache *srb_cachep; /* * Ioctl related information. */ -static int num_hosts; - +int num_hosts; int ql2xlogintimeout = 20; module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xlogintimeout, @@ -112,7 +111,7 @@ static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *); static int qla2x00_change_queue_depth(struct scsi_device *, int); static int qla2x00_change_queue_type(struct scsi_device *, int); -static struct scsi_host_template qla2x00_driver_template = { +struct scsi_host_template qla2x00_driver_template = { .module = THIS_MODULE, .name = QLA2XXX_DRIVER_NAME, .queuecommand = qla2x00_queuecommand, @@ -143,7 +142,7 @@ static struct scsi_host_template qla2x00_driver_template = { .shost_attrs = qla2x00_host_attrs, }; -static struct scsi_host_template qla24xx_driver_template = { +struct scsi_host_template qla24xx_driver_template = { .module = THIS_MODULE, .name = QLA2XXX_DRIVER_NAME, .queuecommand = qla24xx_queuecommand, @@ -171,21 +170,21 @@ static struct scsi_host_template qla24xx_driver_template = { }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; +struct scsi_transport_template *qla2xxx_transport_vport_template = NULL; /* TODO Convert to inlines * * Timer routines */ -#define WATCH_INTERVAL 1 /* number of seconds */ -static void qla2x00_timer(scsi_qla_host_t *); +void qla2x00_timer(scsi_qla_host_t *); -static __inline__ void qla2x00_start_timer(scsi_qla_host_t *, +__inline__ void qla2x00_start_timer(scsi_qla_host_t *, void *, unsigned long); static __inline__ void qla2x00_restart_timer(scsi_qla_host_t *, unsigned long); -static __inline__ void qla2x00_stop_timer(scsi_qla_host_t *); +__inline__ void qla2x00_stop_timer(scsi_qla_host_t *); -static inline void +__inline__ void qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval) { init_timer(&ha->timer); @@ -202,7 +201,7 @@ qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval) mod_timer(&ha->timer, jiffies + interval * HZ); } -static __inline__ void +__inline__ void qla2x00_stop_timer(scsi_qla_host_t *ha) { del_timer_sync(&ha->timer); @@ -213,8 +212,8 @@ static int qla2x00_do_dpc(void *data); static void qla2x00_rst_aen(scsi_qla_host_t *); -static uint8_t qla2x00_mem_alloc(scsi_qla_host_t *); -static void qla2x00_mem_free(scsi_qla_host_t *ha); +uint8_t qla2x00_mem_alloc(scsi_qla_host_t *); +void qla2x00_mem_free(scsi_qla_host_t *ha); static int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha); static void qla2x00_free_sp_pool(scsi_qla_host_t *ha); static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *); @@ -438,6 +437,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device)); srb_t *sp; int rval; + scsi_qla_host_t *pha = to_qla_parent(ha); rval = fc_remote_port_chkready(rport); if (rval) { @@ -453,7 +453,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) if (atomic_read(&fcport->state) != FCS_ONLINE) { if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD || - atomic_read(&ha->loop_state) == LOOP_DEAD) { + atomic_read(&pha->loop_state) == LOOP_DEAD) { cmd->result = DID_NO_CONNECT << 16; goto qc24_fail_command; } @@ -462,7 +462,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) spin_unlock_irq(ha->host->host_lock); - sp = qla2x00_get_new_sp(ha, fcport, cmd, done); + sp = qla2x00_get_new_sp(pha, fcport, cmd, done); if (!sp) goto qc24_host_busy_lock; @@ -475,8 +475,8 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) return 0; qc24_host_busy_free_sp: - qla2x00_sp_free_dma(ha, sp); - mempool_free(sp, ha->srb_mempool); + qla2x00_sp_free_dma(pha, sp); + mempool_free(sp, pha->srb_mempool); qc24_host_busy_lock: spin_lock_irq(ha->host->host_lock); @@ -548,16 +548,17 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha) { int return_status; unsigned long wait_online; + scsi_qla_host_t *pha = to_qla_parent(ha); wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ); - while (((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) || - test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || - test_bit(ISP_ABORT_RETRY, &ha->dpc_flags) || - ha->dpc_active) && time_before(jiffies, wait_online)) { + while (((test_bit(ISP_ABORT_NEEDED, &pha->dpc_flags)) || + test_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &pha->dpc_flags) || + pha->dpc_active) && time_before(jiffies, wait_online)) { msleep(1000); } - if (ha->flags.online) + if (pha->flags.online) return_status = QLA_SUCCESS; else return_status = QLA_FUNCTION_FAILED; @@ -588,14 +589,15 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) { int return_status = QLA_SUCCESS; unsigned long loop_timeout ; + scsi_qla_host_t *pha = to_qla_parent(ha); /* wait for 5 min at the max for loop to be ready */ loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ); - while ((!atomic_read(&ha->loop_down_timer) && - atomic_read(&ha->loop_state) == LOOP_DOWN) || - atomic_read(&ha->loop_state) != LOOP_READY) { - if (atomic_read(&ha->loop_state) == LOOP_DEAD) { + while ((!atomic_read(&pha->loop_down_timer) && + atomic_read(&pha->loop_state) == LOOP_DOWN) || + atomic_read(&pha->loop_state) != LOOP_READY) { + if (atomic_read(&pha->loop_state) == LOOP_DEAD) { return_status = QLA_FUNCTION_FAILED; break; } @@ -650,6 +652,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) unsigned long serial; unsigned long flags; int wait = 0; + scsi_qla_host_t *pha = to_qla_parent(ha); qla2x00_block_error_handler(cmd); @@ -663,9 +666,9 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) serial = cmd->serial_number; /* Check active list for command command. */ - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&pha->hardware_lock, flags); for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) { - sp = ha->outstanding_cmds[i]; + sp = pha->outstanding_cmds[i]; if (sp == NULL) continue; @@ -677,7 +680,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) __func__, ha->host_no, sp, serial)); DEBUG3(qla2x00_print_scsi_cmd(cmd)); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); if (ha->isp_ops.abort_command(ha, sp)) { DEBUG2(printk("%s(%ld): abort_command " "mbx failed.\n", __func__, ha->host_no)); @@ -686,11 +689,11 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) "mbx success.\n", __func__, ha->host_no)); wait = 1; } - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&pha->hardware_lock, flags); break; } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); /* Wait for the command to be returned. */ if (wait) { @@ -731,6 +734,7 @@ qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t) srb_t *sp; struct scsi_cmnd *cmd; unsigned long flags; + scsi_qla_host_t *pha = to_qla_parent(ha); status = 0; @@ -739,19 +743,20 @@ qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t) * array */ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { - spin_lock_irqsave(&ha->hardware_lock, flags); - sp = ha->outstanding_cmds[cnt]; + spin_lock_irqsave(&pha->hardware_lock, flags); + sp = pha->outstanding_cmds[cnt]; if (sp) { cmd = sp->cmd; - spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (cmd->device->id == t) { + spin_unlock_irqrestore(&pha->hardware_lock, flags); + if (cmd->device->id == t && + ha->vp_idx == sp->ha->vp_idx) { if (!qla2x00_eh_wait_on_command(ha, cmd)) { status = 1; break; } } } else { - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); } } return (status); @@ -782,14 +787,12 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; - int ret; + int ret = FAILED; unsigned int id, lun; unsigned long serial; qla2x00_block_error_handler(cmd); - ret = FAILED; - id = cmd->device->id; lun = cmd->device->lun; serial = cmd->serial_number; @@ -912,15 +915,14 @@ static int qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); + scsi_qla_host_t *pha = to_qla_parent(ha); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; - int ret; + int ret = FAILED; unsigned int id, lun; unsigned long serial; qla2x00_block_error_handler(cmd); - ret = FAILED; - id = cmd->device->id; lun = cmd->device->lun; serial = cmd->serial_number; @@ -944,7 +946,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) goto eh_bus_reset_done; /* Flush outstanding commands. */ - if (!qla2x00_eh_wait_for_pending_commands(ha)) + if (!qla2x00_eh_wait_for_pending_commands(pha)) ret = FAILED; eh_bus_reset_done: @@ -974,14 +976,13 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; - int ret; + int ret = FAILED; unsigned int id, lun; unsigned long serial; + scsi_qla_host_t *pha = to_qla_parent(ha); qla2x00_block_error_handler(cmd); - ret = FAILED; - id = cmd->device->id; lun = cmd->device->lun; serial = cmd->serial_number; @@ -1004,21 +1005,24 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) * while dpc is stuck for the mailbox to complete. */ qla2x00_wait_for_loop_ready(ha); - set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); - if (qla2x00_abort_isp(ha)) { - clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); + set_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); + if (qla2x00_abort_isp(pha)) { + clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); /* failed. schedule dpc to try */ - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + set_bit(ISP_ABORT_NEEDED, &pha->dpc_flags); if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) goto eh_host_reset_lock; } - clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); + clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); /* Waiting for our command in done_queue to be returned to OS.*/ - if (qla2x00_eh_wait_for_pending_commands(ha)) + if (qla2x00_eh_wait_for_pending_commands(pha)) ret = SUCCESS; + if (ha->parent) + qla2x00_vp_abort_isp(ha); + eh_host_reset_lock: qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__, (ret == FAILED) ? "failed" : "succeded"); @@ -1435,6 +1439,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->host = host; ha->host_no = host->host_no; sprintf(ha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, ha->host_no); + ha->parent = NULL; /* Set ISP-type information. */ qla2x00_set_isp_flags(ha); @@ -1452,7 +1457,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->prev_topology = 0; ha->init_cb_size = sizeof(init_cb_t); - ha->mgmt_svr_loop_id = MANAGEMENT_SERVER; + ha->mgmt_svr_loop_id = MANAGEMENT_SERVER + ha->vp_idx; ha->link_data_rate = PORT_SPEED_UNKNOWN; ha->optrom_size = OPTROM_SIZE_2300; @@ -1524,8 +1529,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->request_q_length = REQUEST_ENTRY_CNT_24XX; ha->response_q_length = RESPONSE_ENTRY_CNT_2300; ha->last_loop_id = SNS_LAST_LOOP_ID_2300; - ha->init_cb_size = sizeof(struct init_cb_24xx); - ha->mgmt_svr_loop_id = 10; + ha->init_cb_size = sizeof(struct mid_init_cb_24xx); + ha->mgmt_svr_loop_id = 10 + ha->vp_idx; ha->isp_ops.pci_config = qla24xx_pci_config; ha->isp_ops.reset_chip = qla24xx_reset_chip; ha->isp_ops.chip_diag = qla24xx_chip_diag; @@ -1563,10 +1568,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->instance = num_hosts; init_MUTEX(&ha->mbx_cmd_sem); + init_MUTEX(&ha->vport_sem); init_MUTEX_LOCKED(&ha->mbx_intr_sem); INIT_LIST_HEAD(&ha->list); INIT_LIST_HEAD(&ha->fcports); + INIT_LIST_HEAD(&ha->vp_list); + + set_bit(0, (unsigned long *) ha->vp_idx_map); qla2x00_config_dma_addressing(ha); if (qla2x00_mem_alloc(ha)) { @@ -1789,7 +1798,8 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport, int do_login, int defer) { - if (atomic_read(&fcport->state) == FCS_ONLINE) + if (atomic_read(&fcport->state) == FCS_ONLINE && + ha->vp_idx == fcport->vp_idx) qla2x00_schedule_rport_del(ha, fcport, defer); /* @@ -1840,19 +1850,23 @@ void qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) { fc_port_t *fcport; + scsi_qla_host_t *pha = to_qla_parent(ha); - list_for_each_entry(fcport, &ha->fcports, list) { - if (fcport->port_type != FCT_TARGET) + list_for_each_entry(fcport, &pha->fcports, list) { + if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx) continue; - /* * No point in marking the device as lost, if the device is * already DEAD. */ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD) continue; - if (atomic_read(&fcport->state) == FCS_ONLINE) - qla2x00_schedule_rport_del(ha, fcport, defer); + if (atomic_read(&fcport->state) == FCS_ONLINE) { + if (defer) + qla2x00_schedule_rport_del(ha, fcport, defer); + else if (ha->vp_idx == fcport->vp_idx) + qla2x00_schedule_rport_del(ha, fcport, defer); + } atomic_set(&fcport->state, FCS_DEVICE_LOST); } @@ -1868,7 +1882,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) * 0 = success. * 1 = failure. */ -static uint8_t +uint8_t qla2x00_mem_alloc(scsi_qla_host_t *ha) { char name[16]; @@ -1920,33 +1934,33 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) continue; } - snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME, - ha->host_no); - ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev, - DMA_POOL_SIZE, 8, 0); - if (ha->s_dma_pool == NULL) { + /* get consistent memory allocated for init control block */ + ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, + ha->init_cb_size, &ha->init_cb_dma, GFP_KERNEL); + if (ha->init_cb == NULL) { qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - s_dma_pool\n"); + "Memory Allocation failed - init_cb\n"); qla2x00_mem_free(ha); msleep(100); continue; } + memset(ha->init_cb, 0, ha->init_cb_size); - /* get consistent memory allocated for init control block */ - ha->init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, - &ha->init_cb_dma); - if (ha->init_cb == NULL) { + snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME, + ha->host_no); + ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev, + DMA_POOL_SIZE, 8, 0); + if (ha->s_dma_pool == NULL) { qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - init_cb\n"); + "Memory Allocation failed - s_dma_pool\n"); qla2x00_mem_free(ha); msleep(100); continue; } - memset(ha->init_cb, 0, ha->init_cb_size); if (qla2x00_allocate_sp_pool(ha)) { qla_printk(KERN_WARNING, ha, @@ -2052,7 +2066,7 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) * Input: * ha = adapter block pointer. */ -static void +void qla2x00_mem_free(scsi_qla_host_t *ha) { struct list_head *fcpl, *fcptemp; @@ -2088,12 +2102,13 @@ qla2x00_mem_free(scsi_qla_host_t *ha) if (ha->ms_iocb) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); - if (ha->init_cb) - dma_pool_free(ha->s_dma_pool, ha->init_cb, ha->init_cb_dma); - if (ha->s_dma_pool) dma_pool_destroy(ha->s_dma_pool); + if (ha->init_cb) + dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, + ha->init_cb, ha->init_cb_dma); + if (ha->gid_list) dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, ha->gid_list_dma); @@ -2199,6 +2214,7 @@ qla2x00_free_sp_pool( scsi_qla_host_t *ha) static int qla2x00_do_dpc(void *data) { + int rval; scsi_qla_host_t *ha; fc_port_t *fcport; uint8_t status; @@ -2347,7 +2363,7 @@ qla2x00_do_dpc(void *data) if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags))) { - qla2x00_loop_resync(ha); + rval = qla2x00_loop_resync(ha); clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags); } @@ -2374,6 +2390,8 @@ qla2x00_do_dpc(void *data) if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags)) ha->isp_ops.beacon_blink(ha); + qla2x00_do_dpc_all_vps(ha); + ha->dpc_active = 0; } /* End of while(1) */ @@ -2452,7 +2470,7 @@ qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp) * * Context: Interrupt ***************************************************************************/ -static void +void qla2x00_timer(scsi_qla_host_t *ha) { unsigned long cpu_flags = 0; @@ -2461,6 +2479,7 @@ qla2x00_timer(scsi_qla_host_t *ha) int index; srb_t *sp; int t; + scsi_qla_host_t *pha = to_qla_parent(ha); /* * Ports - Port down timer. @@ -2506,23 +2525,29 @@ qla2x00_timer(scsi_qla_host_t *ha) atomic_set(&ha->loop_state, LOOP_DEAD); /* Schedule an ISP abort to return any tape commands. */ - spin_lock_irqsave(&ha->hardware_lock, cpu_flags); - for (index = 1; index < MAX_OUTSTANDING_COMMANDS; - index++) { - fc_port_t *sfcp; + /* NPIV - scan physical port only */ + if (!ha->parent) { + spin_lock_irqsave(&ha->hardware_lock, + cpu_flags); + for (index = 1; + index < MAX_OUTSTANDING_COMMANDS; + index++) { + fc_port_t *sfcp; + + sp = ha->outstanding_cmds[index]; + if (!sp) + continue; + sfcp = sp->fcport; + if (!(sfcp->flags & FCF_TAPE_PRESENT)) + continue; - sp = ha->outstanding_cmds[index]; - if (!sp) - continue; - sfcp = sp->fcport; - if (!(sfcp->flags & FCF_TAPE_PRESENT)) - continue; - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - break; + set_bit(ISP_ABORT_NEEDED, + &ha->dpc_flags); + break; + } + spin_unlock_irqrestore(&ha->hardware_lock, + cpu_flags); } - spin_unlock_irqrestore(&ha->hardware_lock, cpu_flags); - set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags); start_dpc++; } @@ -2566,8 +2591,9 @@ qla2x00_timer(scsi_qla_host_t *ha) test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) || test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) || test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) || + test_bit(VP_DPC_NEEDED, &ha->dpc_flags) || test_bit(RELOGIN_NEEDED, &ha->dpc_flags))) - qla2xxx_wake_dpc(ha); + qla2xxx_wake_dpc(pha); qla2x00_restart_timer(ha, WATCH_INTERVAL); } @@ -2711,14 +2737,24 @@ qla2x00_module_init(void) qla2xxx_transport_template = fc_attach_transport(&qla2xxx_transport_functions); - if (!qla2xxx_transport_template) + if (!qla2xxx_transport_template) { + kmem_cache_destroy(srb_cachep); + return -ENODEV; + } + qla2xxx_transport_vport_template = + fc_attach_transport(&qla2xxx_transport_vport_functions); + if (!qla2xxx_transport_vport_template) { + kmem_cache_destroy(srb_cachep); + fc_release_transport(qla2xxx_transport_template); return -ENODEV; + } printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n"); ret = pci_register_driver(&qla2xxx_pci_driver); if (ret) { kmem_cache_destroy(srb_cachep); fc_release_transport(qla2xxx_transport_template); + fc_release_transport(qla2xxx_transport_vport_template); } return ret; } @@ -2733,6 +2769,7 @@ qla2x00_module_exit(void) qla2x00_release_firmware(); kmem_cache_destroy(srb_cachep); fc_release_transport(qla2xxx_transport_template); + fc_release_transport(qla2xxx_transport_vport_template); } module_init(qla2x00_module_init);