diff options
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_os.c')
| -rw-r--r-- | drivers/scsi/qla4xxx/ql4_os.c | 1084 | 
1 files changed, 1030 insertions, 54 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 30f31b127f3..4169c8baa11 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -8,6 +8,7 @@  #include <linux/slab.h>  #include <linux/blkdev.h>  #include <linux/iscsi_boot_sysfs.h> +#include <linux/inet.h>  #include <scsi/scsi_tcq.h>  #include <scsi/scsicam.h> @@ -31,6 +32,13 @@ static struct kmem_cache *srb_cachep;  /*   * Module parameter information and variables   */ +int ql4xdisablesysfsboot = 1; +module_param(ql4xdisablesysfsboot, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ql4xdisablesysfsboot, +		"Set to disable exporting boot targets to sysfs\n" +		" 0 - Export boot targets\n" +		" 1 - Do not export boot targets (Default)"); +  int ql4xdontresethba = 0;  module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR);  MODULE_PARM_DESC(ql4xdontresethba, @@ -63,7 +71,7 @@ static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO;  module_param(ql4xsess_recovery_tmo, int, S_IRUGO);  MODULE_PARM_DESC(ql4xsess_recovery_tmo,  		"Target Session Recovery Timeout.\n" -		" Default: 30 sec."); +		" Default: 120 sec.");  static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);  /* @@ -415,7 +423,7 @@ static int qla4xxx_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)  	qla_ep = ep->dd_data;  	ha = to_qla_host(qla_ep->host); -	if (adapter_up(ha)) +	if (adapter_up(ha) && !test_bit(AF_BUILD_DDB_LIST, &ha->flags))  		ret = 1;  	return ret; @@ -975,6 +983,150 @@ static int qla4xxx_conn_get_param(struct iscsi_cls_conn *cls_conn,  } +int qla4xxx_get_ddb_index(struct scsi_qla_host *ha, uint16_t *ddb_index) +{ +	uint32_t mbx_sts = 0; +	uint16_t tmp_ddb_index; +	int ret; + +get_ddb_index: +	tmp_ddb_index = find_first_zero_bit(ha->ddb_idx_map, MAX_DDB_ENTRIES); + +	if (tmp_ddb_index >= MAX_DDB_ENTRIES) { +		DEBUG2(ql4_printk(KERN_INFO, ha, +				  "Free DDB index not available\n")); +		ret = QLA_ERROR; +		goto exit_get_ddb_index; +	} + +	if (test_and_set_bit(tmp_ddb_index, ha->ddb_idx_map)) +		goto get_ddb_index; + +	DEBUG2(ql4_printk(KERN_INFO, ha, +			  "Found a free DDB index at %d\n", tmp_ddb_index)); +	ret = qla4xxx_req_ddb_entry(ha, tmp_ddb_index, &mbx_sts); +	if (ret == QLA_ERROR) { +		if (mbx_sts == MBOX_STS_COMMAND_ERROR) { +			ql4_printk(KERN_INFO, ha, +				   "DDB index = %d not available trying next\n", +				   tmp_ddb_index); +			goto get_ddb_index; +		} +		DEBUG2(ql4_printk(KERN_INFO, ha, +				  "Free FW DDB not available\n")); +	} + +	*ddb_index = tmp_ddb_index; + +exit_get_ddb_index: +	return ret; +} + +static int qla4xxx_match_ipaddress(struct scsi_qla_host *ha, +				   struct ddb_entry *ddb_entry, +				   char *existing_ipaddr, +				   char *user_ipaddr) +{ +	uint8_t dst_ipaddr[IPv6_ADDR_LEN]; +	char formatted_ipaddr[DDB_IPADDR_LEN]; +	int status = QLA_SUCCESS, ret = 0; + +	if (ddb_entry->fw_ddb_entry.options & DDB_OPT_IPV6_DEVICE) { +		ret = in6_pton(user_ipaddr, strlen(user_ipaddr), dst_ipaddr, +			       '\0', NULL); +		if (ret == 0) { +			status = QLA_ERROR; +			goto out_match; +		} +		ret = sprintf(formatted_ipaddr, "%pI6", dst_ipaddr); +	} else { +		ret = in4_pton(user_ipaddr, strlen(user_ipaddr), dst_ipaddr, +			       '\0', NULL); +		if (ret == 0) { +			status = QLA_ERROR; +			goto out_match; +		} +		ret = sprintf(formatted_ipaddr, "%pI4", dst_ipaddr); +	} + +	if (strcmp(existing_ipaddr, formatted_ipaddr)) +		status = QLA_ERROR; + +out_match: +	return status; +} + +static int qla4xxx_match_fwdb_session(struct scsi_qla_host *ha, +				      struct iscsi_cls_conn *cls_conn) +{ +	int idx = 0, max_ddbs, rval; +	struct iscsi_cls_session *cls_sess = iscsi_conn_to_session(cls_conn); +	struct iscsi_session *sess, *existing_sess; +	struct iscsi_conn *conn, *existing_conn; +	struct ddb_entry *ddb_entry; + +	sess = cls_sess->dd_data; +	conn = cls_conn->dd_data; + +	if (sess->targetname == NULL || +	    conn->persistent_address == NULL || +	    conn->persistent_port == 0) +		return QLA_ERROR; + +	max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : +				     MAX_DEV_DB_ENTRIES; + +	for (idx = 0; idx < max_ddbs; idx++) { +		ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, idx); +		if (ddb_entry == NULL) +			continue; + +		if (ddb_entry->ddb_type != FLASH_DDB) +			continue; + +		existing_sess = ddb_entry->sess->dd_data; +		existing_conn = ddb_entry->conn->dd_data; + +		if (existing_sess->targetname == NULL || +		    existing_conn->persistent_address == NULL || +		    existing_conn->persistent_port == 0) +			continue; + +		DEBUG2(ql4_printk(KERN_INFO, ha, +				  "IQN = %s User IQN = %s\n", +				  existing_sess->targetname, +				  sess->targetname)); + +		DEBUG2(ql4_printk(KERN_INFO, ha, +				  "IP = %s User IP = %s\n", +				  existing_conn->persistent_address, +				  conn->persistent_address)); + +		DEBUG2(ql4_printk(KERN_INFO, ha, +				  "Port = %d User Port = %d\n", +				  existing_conn->persistent_port, +				  conn->persistent_port)); + +		if (strcmp(existing_sess->targetname, sess->targetname)) +			continue; +		rval = qla4xxx_match_ipaddress(ha, ddb_entry, +					existing_conn->persistent_address, +					conn->persistent_address); +		if (rval == QLA_ERROR) +			continue; +		if (existing_conn->persistent_port != conn->persistent_port) +			continue; +		break; +	} + +	if (idx == max_ddbs) +		return QLA_ERROR; + +	DEBUG2(ql4_printk(KERN_INFO, ha, +			  "Match found in fwdb sessions\n")); +	return QLA_SUCCESS; +} +  static struct iscsi_cls_session *  qla4xxx_session_create(struct iscsi_endpoint *ep,  			uint16_t cmds_max, uint16_t qdepth, @@ -984,8 +1136,7 @@ qla4xxx_session_create(struct iscsi_endpoint *ep,  	struct scsi_qla_host *ha;  	struct qla_endpoint *qla_ep;  	struct ddb_entry *ddb_entry; -	uint32_t ddb_index; -	uint32_t mbx_sts = 0; +	uint16_t ddb_index;  	struct iscsi_session *sess;  	struct sockaddr *dst_addr;  	int ret; @@ -1000,32 +1151,9 @@ qla4xxx_session_create(struct iscsi_endpoint *ep,  	dst_addr = (struct sockaddr *)&qla_ep->dst_addr;  	ha = to_qla_host(qla_ep->host); -get_ddb_index: -	ddb_index = find_first_zero_bit(ha->ddb_idx_map, MAX_DDB_ENTRIES); - -	if (ddb_index >= MAX_DDB_ENTRIES) { -		DEBUG2(ql4_printk(KERN_INFO, ha, -				  "Free DDB index not available\n")); +	ret = qla4xxx_get_ddb_index(ha, &ddb_index); +	if (ret == QLA_ERROR)  		return NULL; -	} - -	if (test_and_set_bit(ddb_index, ha->ddb_idx_map)) -		goto get_ddb_index; - -	DEBUG2(ql4_printk(KERN_INFO, ha, -			  "Found a free DDB index at %d\n", ddb_index)); -	ret = qla4xxx_req_ddb_entry(ha, ddb_index, &mbx_sts); -	if (ret == QLA_ERROR) { -		if (mbx_sts == MBOX_STS_COMMAND_ERROR) { -			ql4_printk(KERN_INFO, ha, -				   "DDB index = %d not available trying next\n", -				   ddb_index); -			goto get_ddb_index; -		} -		DEBUG2(ql4_printk(KERN_INFO, ha, -				  "Free FW DDB not available\n")); -		return NULL; -	}  	cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, qla_ep->host,  				       cmds_max, sizeof(struct ddb_entry), @@ -1040,6 +1168,8 @@ get_ddb_index:  	ddb_entry->fw_ddb_device_state = DDB_DS_NO_CONNECTION_ACTIVE;  	ddb_entry->ha = ha;  	ddb_entry->sess = cls_sess; +	ddb_entry->unblock_sess = qla4xxx_unblock_ddb; +	ddb_entry->ddb_change = qla4xxx_ddb_change;  	cls_sess->recovery_tmo = ql4xsess_recovery_tmo;  	ha->fw_ddb_index_map[ddb_entry->fw_ddb_index] = ddb_entry;  	ha->tot_ddbs++; @@ -1077,6 +1207,9 @@ qla4xxx_conn_create(struct iscsi_cls_session *cls_sess, uint32_t conn_idx)  	DEBUG2(printk(KERN_INFO "Func: %s\n", __func__));  	cls_conn = iscsi_conn_setup(cls_sess, sizeof(struct qla_conn),  				    conn_idx); +	if (!cls_conn) +		return NULL; +  	sess = cls_sess->dd_data;  	ddb_entry = sess->dd_data;  	ddb_entry->conn = cls_conn; @@ -1109,7 +1242,7 @@ static int qla4xxx_conn_start(struct iscsi_cls_conn *cls_conn)  	struct iscsi_session *sess;  	struct ddb_entry *ddb_entry;  	struct scsi_qla_host *ha; -	struct dev_db_entry *fw_ddb_entry; +	struct dev_db_entry *fw_ddb_entry = NULL;  	dma_addr_t fw_ddb_entry_dma;  	uint32_t mbx_sts = 0;  	int ret = 0; @@ -1120,12 +1253,25 @@ static int qla4xxx_conn_start(struct iscsi_cls_conn *cls_conn)  	ddb_entry = sess->dd_data;  	ha = ddb_entry->ha; +	/* Check if we have  matching FW DDB, if yes then do not +	 * login to this target. This could cause target to logout previous +	 * connection +	 */ +	ret = qla4xxx_match_fwdb_session(ha, cls_conn); +	if (ret == QLA_SUCCESS) { +		ql4_printk(KERN_INFO, ha, +			   "Session already exist in FW.\n"); +		ret = -EEXIST; +		goto exit_conn_start; +	} +  	fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),  					  &fw_ddb_entry_dma, GFP_KERNEL);  	if (!fw_ddb_entry) {  		ql4_printk(KERN_ERR, ha,  			   "%s: Unable to allocate dma buffer\n", __func__); -		return -ENOMEM; +		ret = -ENOMEM; +		goto exit_conn_start;  	}  	ret = qla4xxx_set_param_ddbentry(ha, ddb_entry, cls_conn, &mbx_sts); @@ -1138,9 +1284,7 @@ static int qla4xxx_conn_start(struct iscsi_cls_conn *cls_conn)  		if (mbx_sts)  			if (ddb_entry->fw_ddb_device_state ==  						DDB_DS_SESSION_ACTIVE) { -				iscsi_conn_start(ddb_entry->conn); -				iscsi_conn_login_event(ddb_entry->conn, -						ISCSI_CONN_STATE_LOGGED_IN); +				ddb_entry->unblock_sess(ddb_entry->sess);  				goto exit_set_param;  			} @@ -1167,8 +1311,9 @@ exit_set_param:  	ret = 0;  exit_conn_start: -	dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), -			  fw_ddb_entry, fw_ddb_entry_dma); +	if (fw_ddb_entry) +		dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), +				  fw_ddb_entry, fw_ddb_entry_dma);  	return ret;  } @@ -1344,6 +1489,101 @@ static int qla4xxx_task_xmit(struct iscsi_task *task)  	return -ENOSYS;  } +static void qla4xxx_copy_fwddb_param(struct scsi_qla_host *ha, +				     struct dev_db_entry *fw_ddb_entry, +				     struct iscsi_cls_session *cls_sess, +				     struct iscsi_cls_conn *cls_conn) +{ +	int buflen = 0; +	struct iscsi_session *sess; +	struct iscsi_conn *conn; +	char ip_addr[DDB_IPADDR_LEN]; +	uint16_t options = 0; + +	sess = cls_sess->dd_data; +	conn = cls_conn->dd_data; + +	conn->max_recv_dlength = BYTE_UNITS * +			  le16_to_cpu(fw_ddb_entry->iscsi_max_rcv_data_seg_len); + +	conn->max_xmit_dlength = BYTE_UNITS * +			  le16_to_cpu(fw_ddb_entry->iscsi_max_snd_data_seg_len); + +	sess->initial_r2t_en = +			    (BIT_10 & le16_to_cpu(fw_ddb_entry->iscsi_options)); + +	sess->max_r2t = le16_to_cpu(fw_ddb_entry->iscsi_max_outsnd_r2t); + +	sess->imm_data_en = (BIT_11 & le16_to_cpu(fw_ddb_entry->iscsi_options)); + +	sess->first_burst = BYTE_UNITS * +			       le16_to_cpu(fw_ddb_entry->iscsi_first_burst_len); + +	sess->max_burst = BYTE_UNITS * +				 le16_to_cpu(fw_ddb_entry->iscsi_max_burst_len); + +	sess->time2wait = le16_to_cpu(fw_ddb_entry->iscsi_def_time2wait); + +	sess->time2retain = le16_to_cpu(fw_ddb_entry->iscsi_def_time2retain); + +	conn->persistent_port = le16_to_cpu(fw_ddb_entry->port); + +	sess->tpgt = le32_to_cpu(fw_ddb_entry->tgt_portal_grp); + +	options = le16_to_cpu(fw_ddb_entry->options); +	if (options & DDB_OPT_IPV6_DEVICE) +		sprintf(ip_addr, "%pI6", fw_ddb_entry->ip_addr); +	else +		sprintf(ip_addr, "%pI4", fw_ddb_entry->ip_addr); + +	iscsi_set_param(cls_conn, ISCSI_PARAM_TARGET_NAME, +			(char *)fw_ddb_entry->iscsi_name, buflen); +	iscsi_set_param(cls_conn, ISCSI_PARAM_INITIATOR_NAME, +			(char *)ha->name_string, buflen); +	iscsi_set_param(cls_conn, ISCSI_PARAM_PERSISTENT_ADDRESS, +			(char *)ip_addr, buflen); +} + +void qla4xxx_update_session_conn_fwddb_param(struct scsi_qla_host *ha, +					     struct ddb_entry *ddb_entry) +{ +	struct iscsi_cls_session *cls_sess; +	struct iscsi_cls_conn *cls_conn; +	uint32_t ddb_state; +	dma_addr_t fw_ddb_entry_dma; +	struct dev_db_entry *fw_ddb_entry; + +	fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), +					  &fw_ddb_entry_dma, GFP_KERNEL); +	if (!fw_ddb_entry) { +		ql4_printk(KERN_ERR, ha, +			   "%s: Unable to allocate dma buffer\n", __func__); +		goto exit_session_conn_fwddb_param; +	} + +	if (qla4xxx_get_fwddb_entry(ha, ddb_entry->fw_ddb_index, fw_ddb_entry, +				    fw_ddb_entry_dma, NULL, NULL, &ddb_state, +				    NULL, NULL, NULL) == QLA_ERROR) { +		DEBUG2(ql4_printk(KERN_ERR, ha, "scsi%ld: %s: failed " +				  "get_ddb_entry for fw_ddb_index %d\n", +				  ha->host_no, __func__, +				  ddb_entry->fw_ddb_index)); +		goto exit_session_conn_fwddb_param; +	} + +	cls_sess = ddb_entry->sess; + +	cls_conn = ddb_entry->conn; + +	/* Update params */ +	qla4xxx_copy_fwddb_param(ha, fw_ddb_entry, cls_sess, cls_conn); + +exit_session_conn_fwddb_param: +	if (fw_ddb_entry) +		dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), +				  fw_ddb_entry, fw_ddb_entry_dma); +} +  void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,  				       struct ddb_entry *ddb_entry)  { @@ -1360,7 +1600,7 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,  	if (!fw_ddb_entry) {  		ql4_printk(KERN_ERR, ha,  			   "%s: Unable to allocate dma buffer\n", __func__); -		return; +		goto exit_session_conn_param;  	}  	if (qla4xxx_get_fwddb_entry(ha, ddb_entry->fw_ddb_index, fw_ddb_entry, @@ -1370,7 +1610,7 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,  				  "get_ddb_entry for fw_ddb_index %d\n",  				  ha->host_no, __func__,  				  ddb_entry->fw_ddb_index)); -		return; +		goto exit_session_conn_param;  	}  	cls_sess = ddb_entry->sess; @@ -1379,6 +1619,12 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,  	cls_conn = ddb_entry->conn;  	conn = cls_conn->dd_data; +	/* Update timers after login */ +	ddb_entry->default_relogin_timeout = +				le16_to_cpu(fw_ddb_entry->def_timeout); +	ddb_entry->default_time2wait = +				le16_to_cpu(fw_ddb_entry->iscsi_def_time2wait); +  	/* Update params */  	conn->max_recv_dlength = BYTE_UNITS *  			  le16_to_cpu(fw_ddb_entry->iscsi_max_rcv_data_seg_len); @@ -1407,6 +1653,11 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,  	memcpy(sess->initiatorname, ha->name_string,  	       min(sizeof(ha->name_string), sizeof(sess->initiatorname))); + +exit_session_conn_param: +	if (fw_ddb_entry) +		dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), +				  fw_ddb_entry, fw_ddb_entry_dma);  }  /* @@ -1607,6 +1858,9 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)  		vfree(ha->chap_list);  	ha->chap_list = NULL; +	if (ha->fw_ddb_dma_pool) +		dma_pool_destroy(ha->fw_ddb_dma_pool); +  	/* release io space registers  */  	if (is_qla8022(ha)) {  		if (ha->nx_pcibase) @@ -1689,6 +1943,16 @@ static int qla4xxx_mem_alloc(struct scsi_qla_host *ha)  		goto mem_alloc_error_exit;  	} +	ha->fw_ddb_dma_pool = dma_pool_create("ql4_fw_ddb", &ha->pdev->dev, +					      DDB_DMA_BLOCK_SIZE, 8, 0); + +	if (ha->fw_ddb_dma_pool == NULL) { +		ql4_printk(KERN_WARNING, ha, +			   "%s: fw_ddb_dma_pool allocation failed..\n", +			   __func__); +		goto mem_alloc_error_exit; +	} +  	return QLA_SUCCESS;  mem_alloc_error_exit: @@ -1800,6 +2064,60 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha)  	}  } +void qla4xxx_check_relogin_flash_ddb(struct iscsi_cls_session *cls_sess) +{ +	struct iscsi_session *sess; +	struct ddb_entry *ddb_entry; +	struct scsi_qla_host *ha; + +	sess = cls_sess->dd_data; +	ddb_entry = sess->dd_data; +	ha = ddb_entry->ha; + +	if (!(ddb_entry->ddb_type == FLASH_DDB)) +		return; + +	if (adapter_up(ha) && !test_bit(DF_RELOGIN, &ddb_entry->flags) && +	    !iscsi_is_session_online(cls_sess)) { +		if (atomic_read(&ddb_entry->retry_relogin_timer) != +		    INVALID_ENTRY) { +			if (atomic_read(&ddb_entry->retry_relogin_timer) == +					0) { +				atomic_set(&ddb_entry->retry_relogin_timer, +					   INVALID_ENTRY); +				set_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags); +				set_bit(DF_RELOGIN, &ddb_entry->flags); +				DEBUG2(ql4_printk(KERN_INFO, ha, +				       "%s: index [%d] login device\n", +					__func__, ddb_entry->fw_ddb_index)); +			} else +				atomic_dec(&ddb_entry->retry_relogin_timer); +		} +	} + +	/* Wait for relogin to timeout */ +	if (atomic_read(&ddb_entry->relogin_timer) && +	    (atomic_dec_and_test(&ddb_entry->relogin_timer) != 0)) { +		/* +		 * If the relogin times out and the device is +		 * still NOT ONLINE then try and relogin again. +		 */ +		if (!iscsi_is_session_online(cls_sess)) { +			/* Reset retry relogin timer */ +			atomic_inc(&ddb_entry->relogin_retry_count); +			DEBUG2(ql4_printk(KERN_INFO, ha, +				"%s: index[%d] relogin timed out-retrying" +				" relogin (%d), retry (%d)\n", __func__, +				ddb_entry->fw_ddb_index, +				atomic_read(&ddb_entry->relogin_retry_count), +				ddb_entry->default_time2wait + 4)); +			set_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags); +			atomic_set(&ddb_entry->retry_relogin_timer, +				   ddb_entry->default_time2wait + 4); +		} +	} +} +  /**   * qla4xxx_timer - checks every second for work to do.   * @ha: Pointer to host adapter structure. @@ -1809,6 +2127,8 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)  	int start_dpc = 0;  	uint16_t w; +	iscsi_host_for_each_session(ha->host, qla4xxx_check_relogin_flash_ddb); +  	/* If we are in the middle of AER/EEH processing  	 * skip any processing and reschedule the timer  	 */ @@ -2078,7 +2398,12 @@ static void qla4xxx_fail_session(struct iscsi_cls_session *cls_session)  	sess = cls_session->dd_data;  	ddb_entry = sess->dd_data;  	ddb_entry->fw_ddb_device_state = DDB_DS_SESSION_FAILED; -	iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); + +	if (ddb_entry->ddb_type == FLASH_DDB) +		iscsi_block_session(ddb_entry->sess); +	else +		iscsi_session_failure(cls_session->dd_data, +				      ISCSI_ERR_CONN_FAILED);  }  /** @@ -2163,7 +2488,7 @@ recover_ha_init_adapter:  		/* NOTE: AF_ONLINE flag set upon successful completion of  		 *       qla4xxx_initialize_adapter */ -		status = qla4xxx_initialize_adapter(ha); +		status = qla4xxx_initialize_adapter(ha, RESET_ADAPTER);  	}  	/* Retry failed adapter initialization, if necessary @@ -2245,17 +2570,108 @@ static void qla4xxx_relogin_devices(struct iscsi_cls_session *cls_session)  			iscsi_unblock_session(ddb_entry->sess);  		} else {  			/* Trigger relogin */ -			iscsi_session_failure(cls_session->dd_data, -					      ISCSI_ERR_CONN_FAILED); +			if (ddb_entry->ddb_type == FLASH_DDB) { +				if (!test_bit(DF_RELOGIN, &ddb_entry->flags)) +					qla4xxx_arm_relogin_timer(ddb_entry); +			} else +				iscsi_session_failure(cls_session->dd_data, +						      ISCSI_ERR_CONN_FAILED);  		}  	}  } +int qla4xxx_unblock_flash_ddb(struct iscsi_cls_session *cls_session) +{ +	struct iscsi_session *sess; +	struct ddb_entry *ddb_entry; +	struct scsi_qla_host *ha; + +	sess = cls_session->dd_data; +	ddb_entry = sess->dd_data; +	ha = ddb_entry->ha; +	ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ddb[%d]" +		   " unblock session\n", ha->host_no, __func__, +		   ddb_entry->fw_ddb_index); + +	iscsi_unblock_session(ddb_entry->sess); + +	/* Start scan target */ +	if (test_bit(AF_ONLINE, &ha->flags)) { +		ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ddb[%d]" +			   " start scan\n", ha->host_no, __func__, +			   ddb_entry->fw_ddb_index); +		scsi_queue_work(ha->host, &ddb_entry->sess->scan_work); +	} +	return QLA_SUCCESS; +} + +int qla4xxx_unblock_ddb(struct iscsi_cls_session *cls_session) +{ +	struct iscsi_session *sess; +	struct ddb_entry *ddb_entry; +	struct scsi_qla_host *ha; + +	sess = cls_session->dd_data; +	ddb_entry = sess->dd_data; +	ha = ddb_entry->ha; +	ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ddb[%d]" +		   " unblock user space session\n", ha->host_no, __func__, +		   ddb_entry->fw_ddb_index); +	iscsi_conn_start(ddb_entry->conn); +	iscsi_conn_login_event(ddb_entry->conn, +			       ISCSI_CONN_STATE_LOGGED_IN); + +	return QLA_SUCCESS; +} +  static void qla4xxx_relogin_all_devices(struct scsi_qla_host *ha)  {  	iscsi_host_for_each_session(ha->host, qla4xxx_relogin_devices);  } +static void qla4xxx_relogin_flash_ddb(struct iscsi_cls_session *cls_sess) +{ +	uint16_t relogin_timer; +	struct iscsi_session *sess; +	struct ddb_entry *ddb_entry; +	struct scsi_qla_host *ha; + +	sess = cls_sess->dd_data; +	ddb_entry = sess->dd_data; +	ha = ddb_entry->ha; + +	relogin_timer = max(ddb_entry->default_relogin_timeout, +			    (uint16_t)RELOGIN_TOV); +	atomic_set(&ddb_entry->relogin_timer, relogin_timer); + +	DEBUG2(ql4_printk(KERN_INFO, ha, +			  "scsi%ld: Relogin index [%d]. TOV=%d\n", ha->host_no, +			  ddb_entry->fw_ddb_index, relogin_timer)); + +	qla4xxx_login_flash_ddb(cls_sess); +} + +static void qla4xxx_dpc_relogin(struct iscsi_cls_session *cls_sess) +{ +	struct iscsi_session *sess; +	struct ddb_entry *ddb_entry; +	struct scsi_qla_host *ha; + +	sess = cls_sess->dd_data; +	ddb_entry = sess->dd_data; +	ha = ddb_entry->ha; + +	if (!(ddb_entry->ddb_type == FLASH_DDB)) +		return; + +	if (test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags) && +	    !iscsi_is_session_online(cls_sess)) { +		DEBUG2(ql4_printk(KERN_INFO, ha, +				  "relogin issued\n")); +		qla4xxx_relogin_flash_ddb(cls_sess); +	} +} +  void qla4xxx_wake_dpc(struct scsi_qla_host *ha)  {  	if (ha->dpc_thread) @@ -2356,6 +2772,12 @@ dpc_post_reset_ha:  	if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags))  		qla4xxx_get_dhcp_ip_address(ha); +	/* ---- relogin device? --- */ +	if (adapter_up(ha) && +	    test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) { +		iscsi_host_for_each_session(ha->host, qla4xxx_dpc_relogin); +	} +  	/* ---- link change? --- */  	if (test_and_clear_bit(DPC_LINK_CHANGED, &ha->dpc_flags)) {  		if (!test_bit(AF_LINK_UP, &ha->flags)) { @@ -2368,8 +2790,12 @@ dpc_post_reset_ha:  			 * fatal error recovery.  Therefore, the driver must  			 * manually relogin to devices when recovering from  			 * connection failures, logouts, expired KATO, etc. */ - -			qla4xxx_relogin_all_devices(ha); +			if (test_and_clear_bit(AF_BUILD_DDB_LIST, &ha->flags)) { +				qla4xxx_build_ddb_list(ha, ha->is_reset); +				iscsi_host_for_each_session(ha->host, +						qla4xxx_login_flash_ddb); +			} else +				qla4xxx_relogin_all_devices(ha);  		}  	}  } @@ -2867,6 +3293,9 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])  			  " target ID %d\n", __func__, ddb_index[0],  			  ddb_index[1])); +	ha->pri_ddb_idx = ddb_index[0]; +	ha->sec_ddb_idx = ddb_index[1]; +  exit_boot_info_free:  	dma_free_coherent(&ha->pdev->dev, size, buf, buf_dma);  exit_boot_info: @@ -3034,6 +3463,9 @@ static int qla4xxx_get_boot_info(struct scsi_qla_host *ha)  		return ret;  	} +	if (ql4xdisablesysfsboot) +		return QLA_SUCCESS; +  	if (ddb_index[0] == 0xffff)  		goto sec_target; @@ -3066,7 +3498,15 @@ static int qla4xxx_setup_boot_info(struct scsi_qla_host *ha)  	struct iscsi_boot_kobj *boot_kobj;  	if (qla4xxx_get_boot_info(ha) != QLA_SUCCESS) -		return 0; +		return QLA_ERROR; + +	if (ql4xdisablesysfsboot) { +		ql4_printk(KERN_INFO, ha, +			   "%s: syfsboot disabled - driver will trigger login" +			   "and publish session for discovery .\n", __func__); +		return QLA_SUCCESS; +	} +  	ha->boot_kset = iscsi_boot_create_host_kset(ha->host->host_no);  	if (!ha->boot_kset) @@ -3108,7 +3548,7 @@ static int qla4xxx_setup_boot_info(struct scsi_qla_host *ha)  	if (!boot_kobj)  		goto put_host; -	return 0; +	return QLA_SUCCESS;  put_host:  	scsi_host_put(ha->host); @@ -3174,9 +3614,507 @@ static void qla4xxx_create_chap_list(struct scsi_qla_host *ha)  exit_chap_list:  	dma_free_coherent(&ha->pdev->dev, chap_size,  			chap_flash_data, chap_dma); -	return;  } +static void qla4xxx_get_param_ddb(struct ddb_entry *ddb_entry, +				  struct ql4_tuple_ddb *tddb) +{ +	struct scsi_qla_host *ha; +	struct iscsi_cls_session *cls_sess; +	struct iscsi_cls_conn *cls_conn; +	struct iscsi_session *sess; +	struct iscsi_conn *conn; + +	DEBUG2(printk(KERN_INFO "Func: %s\n", __func__)); +	ha = ddb_entry->ha; +	cls_sess = ddb_entry->sess; +	sess = cls_sess->dd_data; +	cls_conn = ddb_entry->conn; +	conn = cls_conn->dd_data; + +	tddb->tpgt = sess->tpgt; +	tddb->port = conn->persistent_port; +	strncpy(tddb->iscsi_name, sess->targetname, ISCSI_NAME_SIZE); +	strncpy(tddb->ip_addr, conn->persistent_address, DDB_IPADDR_LEN); +} + +static void qla4xxx_convert_param_ddb(struct dev_db_entry *fw_ddb_entry, +				      struct ql4_tuple_ddb *tddb) +{ +	uint16_t options = 0; + +	tddb->tpgt = le32_to_cpu(fw_ddb_entry->tgt_portal_grp); +	memcpy(&tddb->iscsi_name[0], &fw_ddb_entry->iscsi_name[0], +	       min(sizeof(tddb->iscsi_name), sizeof(fw_ddb_entry->iscsi_name))); + +	options = le16_to_cpu(fw_ddb_entry->options); +	if (options & DDB_OPT_IPV6_DEVICE) +		sprintf(tddb->ip_addr, "%pI6", fw_ddb_entry->ip_addr); +	else +		sprintf(tddb->ip_addr, "%pI4", fw_ddb_entry->ip_addr); + +	tddb->port = le16_to_cpu(fw_ddb_entry->port); +} + +static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha, +				     struct ql4_tuple_ddb *old_tddb, +				     struct ql4_tuple_ddb *new_tddb) +{ +	if (strcmp(old_tddb->iscsi_name, new_tddb->iscsi_name)) +		return QLA_ERROR; + +	if (strcmp(old_tddb->ip_addr, new_tddb->ip_addr)) +		return QLA_ERROR; + +	if (old_tddb->port != new_tddb->port) +		return QLA_ERROR; + +	DEBUG2(ql4_printk(KERN_INFO, ha, +			  "Match Found, fw[%d,%d,%s,%s], [%d,%d,%s,%s]", +			  old_tddb->port, old_tddb->tpgt, old_tddb->ip_addr, +			  old_tddb->iscsi_name, new_tddb->port, new_tddb->tpgt, +			  new_tddb->ip_addr, new_tddb->iscsi_name)); + +	return QLA_SUCCESS; +} + +static int qla4xxx_is_session_exists(struct scsi_qla_host *ha, +				     struct dev_db_entry *fw_ddb_entry) +{ +	struct ddb_entry *ddb_entry; +	struct ql4_tuple_ddb *fw_tddb = NULL; +	struct ql4_tuple_ddb *tmp_tddb = NULL; +	int idx; +	int ret = QLA_ERROR; + +	fw_tddb = vzalloc(sizeof(*fw_tddb)); +	if (!fw_tddb) { +		DEBUG2(ql4_printk(KERN_WARNING, ha, +				  "Memory Allocation failed.\n")); +		ret = QLA_SUCCESS; +		goto exit_check; +	} + +	tmp_tddb = vzalloc(sizeof(*tmp_tddb)); +	if (!tmp_tddb) { +		DEBUG2(ql4_printk(KERN_WARNING, ha, +				  "Memory Allocation failed.\n")); +		ret = QLA_SUCCESS; +		goto exit_check; +	} + +	qla4xxx_convert_param_ddb(fw_ddb_entry, fw_tddb); + +	for (idx = 0; idx < MAX_DDB_ENTRIES; idx++) { +		ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, idx); +		if (ddb_entry == NULL) +			continue; + +		qla4xxx_get_param_ddb(ddb_entry, tmp_tddb); +		if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb)) { +			ret = QLA_SUCCESS; /* found */ +			goto exit_check; +		} +	} + +exit_check: +	if (fw_tddb) +		vfree(fw_tddb); +	if (tmp_tddb) +		vfree(tmp_tddb); +	return ret; +} + +static int qla4xxx_is_flash_ddb_exists(struct scsi_qla_host *ha, +				       struct list_head *list_nt, +				       struct dev_db_entry *fw_ddb_entry) +{ +	struct qla_ddb_index  *nt_ddb_idx, *nt_ddb_idx_tmp; +	struct ql4_tuple_ddb *fw_tddb = NULL; +	struct ql4_tuple_ddb *tmp_tddb = NULL; +	int ret = QLA_ERROR; + +	fw_tddb = vzalloc(sizeof(*fw_tddb)); +	if (!fw_tddb) { +		DEBUG2(ql4_printk(KERN_WARNING, ha, +				  "Memory Allocation failed.\n")); +		ret = QLA_SUCCESS; +		goto exit_check; +	} + +	tmp_tddb = vzalloc(sizeof(*tmp_tddb)); +	if (!tmp_tddb) { +		DEBUG2(ql4_printk(KERN_WARNING, ha, +				  "Memory Allocation failed.\n")); +		ret = QLA_SUCCESS; +		goto exit_check; +	} + +	qla4xxx_convert_param_ddb(fw_ddb_entry, fw_tddb); + +	list_for_each_entry_safe(nt_ddb_idx, nt_ddb_idx_tmp, list_nt, list) { +		qla4xxx_convert_param_ddb(&nt_ddb_idx->fw_ddb, tmp_tddb); +		if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb)) { +			ret = QLA_SUCCESS; /* found */ +			goto exit_check; +		} +	} + +exit_check: +	if (fw_tddb) +		vfree(fw_tddb); +	if (tmp_tddb) +		vfree(tmp_tddb); +	return ret; +} + +static void qla4xxx_free_nt_list(struct list_head *list_nt) +{ +	struct qla_ddb_index  *nt_ddb_idx, *nt_ddb_idx_tmp; + +	/* Free up the normaltargets list */ +	list_for_each_entry_safe(nt_ddb_idx, nt_ddb_idx_tmp, list_nt, list) { +		list_del_init(&nt_ddb_idx->list); +		vfree(nt_ddb_idx); +	} + +} + +static struct iscsi_endpoint *qla4xxx_get_ep_fwdb(struct scsi_qla_host *ha, +					struct dev_db_entry *fw_ddb_entry) +{ +	struct iscsi_endpoint *ep; +	struct sockaddr_in *addr; +	struct sockaddr_in6 *addr6; +	struct sockaddr *dst_addr; +	char *ip; + +	/* TODO: need to destroy on unload iscsi_endpoint*/ +	dst_addr = vmalloc(sizeof(*dst_addr)); +	if (!dst_addr) +		return NULL; + +	if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) { +		dst_addr->sa_family = AF_INET6; +		addr6 = (struct sockaddr_in6 *)dst_addr; +		ip = (char *)&addr6->sin6_addr; +		memcpy(ip, fw_ddb_entry->ip_addr, IPv6_ADDR_LEN); +		addr6->sin6_port = htons(le16_to_cpu(fw_ddb_entry->port)); + +	} else { +		dst_addr->sa_family = AF_INET; +		addr = (struct sockaddr_in *)dst_addr; +		ip = (char *)&addr->sin_addr; +		memcpy(ip, fw_ddb_entry->ip_addr, IP_ADDR_LEN); +		addr->sin_port = htons(le16_to_cpu(fw_ddb_entry->port)); +	} + +	ep = qla4xxx_ep_connect(ha->host, dst_addr, 0); +	vfree(dst_addr); +	return ep; +} + +static int qla4xxx_verify_boot_idx(struct scsi_qla_host *ha, uint16_t idx) +{ +	if (ql4xdisablesysfsboot) +		return QLA_SUCCESS; +	if (idx == ha->pri_ddb_idx || idx == ha->sec_ddb_idx) +		return QLA_ERROR; +	return QLA_SUCCESS; +} + +static void qla4xxx_setup_flash_ddb_entry(struct scsi_qla_host *ha, +					  struct ddb_entry *ddb_entry) +{ +	ddb_entry->ddb_type = FLASH_DDB; +	ddb_entry->fw_ddb_index = INVALID_ENTRY; +	ddb_entry->fw_ddb_device_state = DDB_DS_NO_CONNECTION_ACTIVE; +	ddb_entry->ha = ha; +	ddb_entry->unblock_sess = qla4xxx_unblock_flash_ddb; +	ddb_entry->ddb_change = qla4xxx_flash_ddb_change; + +	atomic_set(&ddb_entry->retry_relogin_timer, INVALID_ENTRY); +	atomic_set(&ddb_entry->relogin_timer, 0); +	atomic_set(&ddb_entry->relogin_retry_count, 0); + +	ddb_entry->default_relogin_timeout = +		le16_to_cpu(ddb_entry->fw_ddb_entry.def_timeout); +	ddb_entry->default_time2wait = +		le16_to_cpu(ddb_entry->fw_ddb_entry.iscsi_def_time2wait); +} + +static void qla4xxx_wait_for_ip_configuration(struct scsi_qla_host *ha) +{ +	uint32_t idx = 0; +	uint32_t ip_idx[IP_ADDR_COUNT] = {0, 1, 2, 3}; /* 4 IP interfaces */ +	uint32_t sts[MBOX_REG_COUNT]; +	uint32_t ip_state; +	unsigned long wtime; +	int ret; + +	wtime = jiffies + (HZ * IP_CONFIG_TOV); +	do { +		for (idx = 0; idx < IP_ADDR_COUNT; idx++) { +			if (ip_idx[idx] == -1) +				continue; + +			ret = qla4xxx_get_ip_state(ha, 0, ip_idx[idx], sts); + +			if (ret == QLA_ERROR) { +				ip_idx[idx] = -1; +				continue; +			} + +			ip_state = (sts[1] & IP_STATE_MASK) >> IP_STATE_SHIFT; + +			DEBUG2(ql4_printk(KERN_INFO, ha, +					  "Waiting for IP state for idx = %d, state = 0x%x\n", +					  ip_idx[idx], ip_state)); +			if (ip_state == IP_ADDRSTATE_UNCONFIGURED || +			    ip_state == IP_ADDRSTATE_INVALID || +			    ip_state == IP_ADDRSTATE_PREFERRED || +			    ip_state == IP_ADDRSTATE_DEPRICATED || +			    ip_state == IP_ADDRSTATE_DISABLING) +				ip_idx[idx] = -1; + +		} + +		/* Break if all IP states checked */ +		if ((ip_idx[0] == -1) && +		    (ip_idx[1] == -1) && +		    (ip_idx[2] == -1) && +		    (ip_idx[3] == -1)) +			break; +		schedule_timeout_uninterruptible(HZ); +	} while (time_after(wtime, jiffies)); +} + +void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset) +{ +	int max_ddbs; +	int ret; +	uint32_t idx = 0, next_idx = 0; +	uint32_t state = 0, conn_err = 0; +	uint16_t conn_id; +	struct dev_db_entry *fw_ddb_entry; +	struct ddb_entry *ddb_entry = NULL; +	dma_addr_t fw_ddb_dma; +	struct iscsi_cls_session *cls_sess; +	struct iscsi_session *sess; +	struct iscsi_cls_conn *cls_conn; +	struct iscsi_endpoint *ep; +	uint16_t cmds_max = 32, tmo = 0; +	uint32_t initial_cmdsn = 0; +	struct list_head list_st, list_nt; /* List of sendtargets */ +	struct qla_ddb_index  *st_ddb_idx, *st_ddb_idx_tmp; +	int fw_idx_size; +	unsigned long wtime; +	struct qla_ddb_index  *nt_ddb_idx; + +	if (!test_bit(AF_LINK_UP, &ha->flags)) { +		set_bit(AF_BUILD_DDB_LIST, &ha->flags); +		ha->is_reset = is_reset; +		return; +	} +	max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : +				     MAX_DEV_DB_ENTRIES; + +	fw_ddb_entry = dma_pool_alloc(ha->fw_ddb_dma_pool, GFP_KERNEL, +				      &fw_ddb_dma); +	if (fw_ddb_entry == NULL) { +		DEBUG2(ql4_printk(KERN_ERR, ha, "Out of memory\n")); +		goto exit_ddb_list; +	} + +	INIT_LIST_HEAD(&list_st); +	INIT_LIST_HEAD(&list_nt); +	fw_idx_size = sizeof(struct qla_ddb_index); + +	for (idx = 0; idx < max_ddbs; idx = next_idx) { +		ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, +					      fw_ddb_dma, NULL, +					      &next_idx, &state, &conn_err, +					      NULL, &conn_id); +		if (ret == QLA_ERROR) +			break; + +		if (qla4xxx_verify_boot_idx(ha, idx) != QLA_SUCCESS) +			goto continue_next_st; + +		/* Check if ST, add to the list_st */ +		if (strlen((char *) fw_ddb_entry->iscsi_name) != 0) +			goto continue_next_st; + +		st_ddb_idx = vzalloc(fw_idx_size); +		if (!st_ddb_idx) +			break; + +		st_ddb_idx->fw_ddb_idx = idx; + +		list_add_tail(&st_ddb_idx->list, &list_st); +continue_next_st: +		if (next_idx == 0) +			break; +	} + +	/* Before issuing conn open mbox, ensure all IPs states are configured +	 * Note, conn open fails if IPs are not configured +	 */ +	qla4xxx_wait_for_ip_configuration(ha); + +	/* Go thru the STs and fire the sendtargets by issuing conn open mbx */ +	list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, list) { +		qla4xxx_conn_open(ha, st_ddb_idx->fw_ddb_idx); +	} + +	/* Wait to ensure all sendtargets are done for min 12 sec wait */ +	tmo = ((ha->def_timeout < LOGIN_TOV) ? LOGIN_TOV : ha->def_timeout); +	DEBUG2(ql4_printk(KERN_INFO, ha, +			  "Default time to wait for build ddb %d\n", tmo)); + +	wtime = jiffies + (HZ * tmo); +	do { +		list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, +					 list) { +			ret = qla4xxx_get_fwddb_entry(ha, +						      st_ddb_idx->fw_ddb_idx, +						      NULL, 0, NULL, &next_idx, +						      &state, &conn_err, NULL, +						      NULL); +			if (ret == QLA_ERROR) +				continue; + +			if (state == DDB_DS_NO_CONNECTION_ACTIVE || +			    state == DDB_DS_SESSION_FAILED) { +				list_del_init(&st_ddb_idx->list); +				vfree(st_ddb_idx); +			} +		} +		schedule_timeout_uninterruptible(HZ / 10); +	} while (time_after(wtime, jiffies)); + +	/* Free up the sendtargets list */ +	list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, list) { +		list_del_init(&st_ddb_idx->list); +		vfree(st_ddb_idx); +	} + +	for (idx = 0; idx < max_ddbs; idx = next_idx) { +		ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, +					      fw_ddb_dma, NULL, +					      &next_idx, &state, &conn_err, +					      NULL, &conn_id); +		if (ret == QLA_ERROR) +			break; + +		if (qla4xxx_verify_boot_idx(ha, idx) != QLA_SUCCESS) +			goto continue_next_nt; + +		/* Check if NT, then add to list it */ +		if (strlen((char *) fw_ddb_entry->iscsi_name) == 0) +			goto continue_next_nt; + +		if (state == DDB_DS_NO_CONNECTION_ACTIVE || +		    state == DDB_DS_SESSION_FAILED) { +			DEBUG2(ql4_printk(KERN_INFO, ha, +					  "Adding  DDB to session = 0x%x\n", +					  idx)); +			if (is_reset == INIT_ADAPTER) { +				nt_ddb_idx = vmalloc(fw_idx_size); +				if (!nt_ddb_idx) +					break; + +				nt_ddb_idx->fw_ddb_idx = idx; + +				memcpy(&nt_ddb_idx->fw_ddb, fw_ddb_entry, +				       sizeof(struct dev_db_entry)); + +				if (qla4xxx_is_flash_ddb_exists(ha, &list_nt, +						fw_ddb_entry) == QLA_SUCCESS) { +					vfree(nt_ddb_idx); +					goto continue_next_nt; +				} +				list_add_tail(&nt_ddb_idx->list, &list_nt); +			} else if (is_reset == RESET_ADAPTER) { +				if (qla4xxx_is_session_exists(ha, +						   fw_ddb_entry) == QLA_SUCCESS) +					goto continue_next_nt; +			} + +			/* Create session object, with INVALID_ENTRY, +			 * the targer_id would get set when we issue the login +			 */ +			cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, +						ha->host, cmds_max, +						sizeof(struct ddb_entry), +						sizeof(struct ql4_task_data), +						initial_cmdsn, INVALID_ENTRY); +			if (!cls_sess) +				goto exit_ddb_list; + +			/* +			 * iscsi_session_setup increments the driver reference +			 * count which wouldn't let the driver to be unloaded. +			 * so calling module_put function to decrement the +			 * reference count. +			 **/ +			module_put(qla4xxx_iscsi_transport.owner); +			sess = cls_sess->dd_data; +			ddb_entry = sess->dd_data; +			ddb_entry->sess = cls_sess; + +			cls_sess->recovery_tmo = ql4xsess_recovery_tmo; +			memcpy(&ddb_entry->fw_ddb_entry, fw_ddb_entry, +			       sizeof(struct dev_db_entry)); + +			qla4xxx_setup_flash_ddb_entry(ha, ddb_entry); + +			cls_conn = iscsi_conn_setup(cls_sess, +						    sizeof(struct qla_conn), +						    conn_id); +			if (!cls_conn) +				goto exit_ddb_list; + +			ddb_entry->conn = cls_conn; + +			/* Setup ep, for displaying attributes in sysfs */ +			ep = qla4xxx_get_ep_fwdb(ha, fw_ddb_entry); +			if (ep) { +				ep->conn = cls_conn; +				cls_conn->ep = ep; +			} else { +				DEBUG2(ql4_printk(KERN_ERR, ha, +						  "Unable to get ep\n")); +			} + +			/* Update sess/conn params */ +			qla4xxx_copy_fwddb_param(ha, fw_ddb_entry, cls_sess, +						 cls_conn); + +			if (is_reset == RESET_ADAPTER) { +				iscsi_block_session(cls_sess); +				/* Use the relogin path to discover new devices +				 *  by short-circuting the logic of setting +				 *  timer to relogin - instead set the flags +				 *  to initiate login right away. +				 */ +				set_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags); +				set_bit(DF_RELOGIN, &ddb_entry->flags); +			} +		} +continue_next_nt: +		if (next_idx == 0) +			break; +	} +exit_ddb_list: +	qla4xxx_free_nt_list(&list_nt); +	if (fw_ddb_entry) +		dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma); + +	qla4xxx_free_ddb_index(ha); +} + +  /**   * qla4xxx_probe_adapter - callback function to probe HBA   * @pdev: pointer to pci_dev structure @@ -3298,7 +4236,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,  	 * firmware  	 * NOTE: interrupts enabled upon successful completion  	 */ -	status = qla4xxx_initialize_adapter(ha); +	status = qla4xxx_initialize_adapter(ha, INIT_ADAPTER);  	while ((!test_bit(AF_ONLINE, &ha->flags)) &&  	    init_retry_count++ < MAX_INIT_RETRIES) { @@ -3319,7 +4257,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,  		if (ha->isp_ops->reset_chip(ha) == QLA_ERROR)  			continue; -		status = qla4xxx_initialize_adapter(ha); +		status = qla4xxx_initialize_adapter(ha, INIT_ADAPTER);  	}  	if (!test_bit(AF_ONLINE, &ha->flags)) { @@ -3386,12 +4324,16 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,  	       ha->host_no, ha->firmware_version[0], ha->firmware_version[1],  	       ha->patch_number, ha->build_number); -	qla4xxx_create_chap_list(ha); -  	if (qla4xxx_setup_boot_info(ha))  		ql4_printk(KERN_ERR, ha, "%s:ISCSI boot info setup failed\n",  			   __func__); +		/* Perform the build ddb list and login to each */ +	qla4xxx_build_ddb_list(ha, INIT_ADAPTER); +	iscsi_host_for_each_session(ha->host, qla4xxx_login_flash_ddb); + +	qla4xxx_create_chap_list(ha); +  	qla4xxx_create_ifaces(ha);  	return 0; @@ -3449,6 +4391,38 @@ static void qla4xxx_prevent_other_port_reinit(struct scsi_qla_host *ha)  	}  } +static void qla4xxx_destroy_fw_ddb_session(struct scsi_qla_host *ha) +{ +	struct ddb_entry *ddb_entry; +	int options; +	int idx; + +	for (idx = 0; idx < MAX_DDB_ENTRIES; idx++) { + +		ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, idx); +		if ((ddb_entry != NULL) && +		    (ddb_entry->ddb_type == FLASH_DDB)) { + +			options = LOGOUT_OPTION_CLOSE_SESSION; +			if (qla4xxx_session_logout_ddb(ha, ddb_entry, options) +			    == QLA_ERROR) +				ql4_printk(KERN_ERR, ha, "%s: Logout failed\n", +					   __func__); + +			qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index); +			/* +			 * we have decremented the reference count of the driver +			 * when we setup the session to have the driver unload +			 * to be seamless without actually destroying the +			 * session +			 **/ +			try_module_get(qla4xxx_iscsi_transport.owner); +			iscsi_destroy_endpoint(ddb_entry->conn->ep); +			qla4xxx_free_ddb(ha, ddb_entry); +			iscsi_session_teardown(ddb_entry->sess); +		} +	} +}  /**   * qla4xxx_remove_adapter - calback function to remove adapter.   * @pci_dev: PCI device pointer @@ -3465,9 +4439,11 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)  	/* destroy iface from sysfs */  	qla4xxx_destroy_ifaces(ha); -	if (ha->boot_kset) +	if ((!ql4xdisablesysfsboot) && ha->boot_kset)  		iscsi_boot_destroy_kset(ha->boot_kset); +	qla4xxx_destroy_fw_ddb_session(ha); +  	scsi_remove_host(ha->host);  	qla4xxx_free_adapter(ha); @@ -4115,7 +5091,7 @@ static uint32_t qla4_8xxx_error_recovery(struct scsi_qla_host *ha)  		qla4_8xxx_idc_unlock(ha);  		clear_bit(AF_FW_RECOVERY, &ha->flags); -		rval = qla4xxx_initialize_adapter(ha); +		rval = qla4xxx_initialize_adapter(ha, RESET_ADAPTER);  		qla4_8xxx_idc_lock(ha);  		if (rval != QLA_SUCCESS) { @@ -4151,7 +5127,7 @@ static uint32_t qla4_8xxx_error_recovery(struct scsi_qla_host *ha)  		if ((qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==  		    QLA82XX_DEV_READY)) {  			clear_bit(AF_FW_RECOVERY, &ha->flags); -			rval = qla4xxx_initialize_adapter(ha); +			rval = qla4xxx_initialize_adapter(ha, RESET_ADAPTER);  			if (rval == QLA_SUCCESS) {  				ret = qla4xxx_request_irqs(ha);  				if (ret) {  |