diff options
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_init.c')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_init.c | 374 |
1 files changed, 285 insertions, 89 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 92329a4..5510df8 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -189,6 +189,78 @@ static int qla4xxx_init_local_data(struct scsi_qla_host *ha) return qla4xxx_get_firmware_status(ha); } +static uint8_t +qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha) +{ + uint8_t ipv4_wait = 0; + uint8_t ipv6_wait = 0; + int8_t ip_address[IPv6_ADDR_LEN] = {0} ; + + /* If both IPv4 & IPv6 are enabled, possibly only one + * IP address may be acquired, so check to see if we + * need to wait for another */ + if (is_ipv4_enabled(ha) && is_ipv6_enabled(ha)) { + if (((ha->addl_fw_state & FW_ADDSTATE_DHCPv4_ENABLED) != 0) && + ((ha->addl_fw_state & + FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED) == 0)) { + ipv4_wait = 1; + } + if (((ha->ipv6_addl_options & + IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE) != 0) && + ((ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) || + (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) || + (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING))) { + + ipv6_wait = 1; + + if ((ha->ipv6_link_local_state == + IP_ADDRSTATE_PREFERRED) || + (ha->ipv6_addr0_state == IP_ADDRSTATE_PREFERRED) || + (ha->ipv6_addr1_state == IP_ADDRSTATE_PREFERRED)) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: " + "Preferred IP configured." + " Don't wait!\n", ha->host_no, + __func__)); + ipv6_wait = 0; + } + if (memcmp(&ha->ipv6_default_router_addr, ip_address, + IPv6_ADDR_LEN) == 0) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: " + "No Router configured. " + "Don't wait!\n", ha->host_no, + __func__)); + ipv6_wait = 0; + } + if ((ha->ipv6_default_router_state == + IPV6_RTRSTATE_MANUAL) && + (ha->ipv6_link_local_state == + IP_ADDRSTATE_TENTATIVE) && + (memcmp(&ha->ipv6_link_local_addr, + &ha->ipv6_default_router_addr, 4) == 0)) { + DEBUG2(printk("scsi%ld: %s: LinkLocal Router & " + "IP configured. Don't wait!\n", + ha->host_no, __func__)); + ipv6_wait = 0; + } + } + if (ipv4_wait || ipv6_wait) { + DEBUG2(printk("scsi%ld: %s: Wait for additional " + "IP(s) \"", ha->host_no, __func__)); + if (ipv4_wait) + DEBUG2(printk("IPv4 ")); + if (ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6LinkLocal ")); + if (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6Addr0 ")); + if (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6Addr1 ")); + DEBUG2(printk("\"\n")); + } + } + + return ipv4_wait|ipv6_wait; +} + static int qla4xxx_fw_ready(struct scsi_qla_host *ha) { uint32_t timeout_count; @@ -226,38 +298,80 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha) continue; } + if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: fwstate:" + "AUTOCONNECT in progress\n", + ha->host_no, __func__)); + } + + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: fwstate:" + " CONFIGURING IP\n", + ha->host_no, __func__)); + /* + * Check for link state after 15 secs and if link is + * still DOWN then, cable is unplugged. Ignore "DHCP + * in Progress/CONFIGURING IP" bit to check if firmware + * is in ready state or not after 15 secs. + * This is applicable for both 2.x & 3.x firmware + */ + if (timeout_count <= (ADAPTER_INIT_TOV - 15)) { + if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s:" + " LINK UP (Cable plugged)\n", + ha->host_no, __func__)); + } else if (ha->firmware_state & + (FW_STATE_CONFIGURING_IP | + FW_STATE_READY)) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: " + "LINK DOWN (Cable unplugged)\n", + ha->host_no, __func__)); + ha->firmware_state = FW_STATE_READY; + } + } + } + if (ha->firmware_state == FW_STATE_READY) { - DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); - /* The firmware is ready to process SCSI commands. */ - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: MEDIA TYPE - %s\n", - ha->host_no, - __func__, (ha->addl_fw_state & - FW_ADDSTATE_OPTICAL_MEDIA) - != 0 ? "OPTICAL" : "COPPER")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: DHCP STATE Enabled " - "%s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_DHCP_ENABLED) != 0 ? - "YES" : "NO")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: LINK %s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_LINK_UP) != 0 ? - "UP" : "DOWN")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: iSNS Service " - "Started %s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? - "YES" : "NO")); - - ready = 1; - break; + /* If DHCP IP Addr is available, retrieve it now. */ + if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, + &ha->dpc_flags)) + qla4xxx_get_dhcp_ip_address(ha); + + if (!qla4xxx_wait_for_ip_config(ha) || + timeout_count == 1) { + DEBUG2(dev_info(&ha->pdev->dev, + "Firmware Ready..\n")); + /* The firmware is ready to process SCSI + commands. */ + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: MEDIA TYPE" + " - %s\n", ha->host_no, + __func__, (ha->addl_fw_state & + FW_ADDSTATE_OPTICAL_MEDIA) + != 0 ? "OPTICAL" : "COPPER")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: DHCPv4 STATE" + " Enabled %s\n", ha->host_no, + __func__, (ha->addl_fw_state & + FW_ADDSTATE_DHCPv4_ENABLED) != 0 ? + "YES" : "NO")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: LINK %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_LINK_UP) != 0 ? + "UP" : "DOWN")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: iSNS Service " + "Started %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? + "YES" : "NO")); + + ready = 1; + break; + } } DEBUG2(printk("scsi%ld: %s: waiting on fw, state=%x:%x - " "seconds expired= %d\n", ha->host_no, __func__, @@ -272,15 +386,19 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha) msleep(1000); } /* end of for */ - if (timeout_count == 0) + if (timeout_count <= 0) DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n", ha->host_no, __func__)); - if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) { - DEBUG2(printk("scsi%ld: %s: FW is reporting its waiting to" - " grab an IP address from DHCP server\n", - ha->host_no, __func__)); + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { + DEBUG2(printk("scsi%ld: %s: FW initialized, but is reporting " + "it's waiting to configure an IP address\n", + ha->host_no, __func__)); ready = 1; + } else if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { + DEBUG2(printk("scsi%ld: %s: FW initialized, but " + "auto-discovery still in process\n", + ha->host_no, __func__)); } return ready; @@ -387,6 +505,7 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, struct dev_db_entry *fw_ddb_entry = NULL; dma_addr_t fw_ddb_entry_dma; int status = QLA_ERROR; + uint32_t conn_err; if (ddb_entry == NULL) { DEBUG2(printk("scsi%ld: %s: ddb_entry is NULL\n", ha->host_no, @@ -407,7 +526,7 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, fw_ddb_entry_dma, NULL, NULL, - &ddb_entry->fw_ddb_device_state, NULL, + &ddb_entry->fw_ddb_device_state, &conn_err, &ddb_entry->tcp_source_port_num, &ddb_entry->connection_id) == QLA_ERROR) { @@ -419,6 +538,7 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, } status = QLA_SUCCESS; + ddb_entry->options = le16_to_cpu(fw_ddb_entry->options); ddb_entry->target_session_id = le16_to_cpu(fw_ddb_entry->tsid); ddb_entry->task_mgmt_timeout = le16_to_cpu(fw_ddb_entry->def_timeout); @@ -442,11 +562,44 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, memcpy(&ddb_entry->ip_addr[0], &fw_ddb_entry->ip_addr[0], min(sizeof(ddb_entry->ip_addr), sizeof(fw_ddb_entry->ip_addr))); - DEBUG2(printk("scsi%ld: %s: ddb[%d] - State= %x status= %d.\n", - ha->host_no, __func__, fw_ddb_index, - ddb_entry->fw_ddb_device_state, status)); - - exit_update_ddb: + ddb_entry->iscsi_max_burst_len = fw_ddb_entry->iscsi_max_burst_len; + ddb_entry->iscsi_max_outsnd_r2t = fw_ddb_entry->iscsi_max_outsnd_r2t; + ddb_entry->iscsi_first_burst_len = fw_ddb_entry->iscsi_first_burst_len; + ddb_entry->iscsi_max_rcv_data_seg_len = + fw_ddb_entry->iscsi_max_rcv_data_seg_len; + ddb_entry->iscsi_max_snd_data_seg_len = + fw_ddb_entry->iscsi_max_snd_data_seg_len; + + if (ddb_entry->options & DDB_OPT_IPV6_DEVICE) { + memcpy(&ddb_entry->remote_ipv6_addr, + fw_ddb_entry->ip_addr, + min(sizeof(ddb_entry->remote_ipv6_addr), + sizeof(fw_ddb_entry->ip_addr))); + memcpy(&ddb_entry->link_local_ipv6_addr, + fw_ddb_entry->link_local_ipv6_addr, + min(sizeof(ddb_entry->link_local_ipv6_addr), + sizeof(fw_ddb_entry->link_local_ipv6_addr))); + + DEBUG2(dev_info(&ha->pdev->dev, "%s: DDB[%d] osIdx = %d " + "State %04x ConnErr %08x IP %pI6 " + ":%04d \"%s\"\n", + __func__, fw_ddb_index, + ddb_entry->os_target_id, + ddb_entry->fw_ddb_device_state, + conn_err, fw_ddb_entry->ip_addr, + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name)); + } else + DEBUG2(dev_info(&ha->pdev->dev, "%s: DDB[%d] osIdx = %d " + "State %04x ConnErr %08x IP %pI4 " + ":%04d \"%s\"\n", + __func__, fw_ddb_index, + ddb_entry->os_target_id, + ddb_entry->fw_ddb_device_state, + conn_err, fw_ddb_entry->ip_addr, + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name)); +exit_update_ddb: if (fw_ddb_entry) dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry, fw_ddb_entry_dma); @@ -492,6 +645,40 @@ static struct ddb_entry * qla4xxx_alloc_ddb(struct scsi_qla_host *ha, } /** + * qla4_is_relogin_allowed - Are we allowed to login? + * @ha: Pointer to host adapter structure. + * @conn_err: Last connection error associated with the ddb + * + * This routine tests the given connection error to determine if + * we are allowed to login. + **/ +int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err) +{ + uint32_t err_code, login_rsp_sts_class; + int relogin = 1; + + err_code = ((conn_err & 0x00ff0000) >> 16); + login_rsp_sts_class = ((conn_err & 0x0000ff00) >> 8); + if (err_code == 0x1c || err_code == 0x06) { + DEBUG2(dev_info(&ha->pdev->dev, + ": conn_err=0x%08x, send target completed" + " or access denied failure\n", conn_err)); + relogin = 0; + } + if ((err_code == 0x08) && (login_rsp_sts_class == 0x02)) { + /* Login Response PDU returned an error. + Login Response Status in Error Code Detail + indicates login should not be retried.*/ + DEBUG2(dev_info(&ha->pdev->dev, + ": conn_err=0x%08x, do not retry relogin\n", + conn_err)); + relogin = 0; + } + + return relogin; +} + +/** * qla4xxx_configure_ddbs - builds driver ddb list * @ha: Pointer to host adapter structure. * @@ -505,18 +692,30 @@ static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) uint32_t fw_ddb_index = 0; uint32_t next_fw_ddb_index = 0; uint32_t ddb_state; - uint32_t conn_err, err_code; + uint32_t conn_err; struct ddb_entry *ddb_entry; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + uint32_t ipv6_device; uint32_t new_tgt; + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (fw_ddb_entry == NULL) { + DEBUG2(dev_info(&ha->pdev->dev, "%s: DMA alloc failed\n", + __func__)); + return QLA_ERROR; + } + dev_info(&ha->pdev->dev, "Initializing DDBs ...\n"); for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; fw_ddb_index = next_fw_ddb_index) { /* First, let's see if a device exists here */ - if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, - &next_fw_ddb_index, &ddb_state, - &conn_err, NULL, NULL) == - QLA_ERROR) { + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, + 0, NULL, &next_fw_ddb_index, + &ddb_state, &conn_err, + NULL, NULL) == + QLA_ERROR) { DEBUG2(printk("scsi%ld: %s: get_ddb_entry, " "fw_ddb_index %d failed", ha->host_no, __func__, fw_ddb_index)); @@ -533,18 +732,19 @@ static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) /* Try and login to device */ DEBUG2(printk("scsi%ld: %s: Login to DDB[%d]\n", ha->host_no, __func__, fw_ddb_index)); - err_code = ((conn_err & 0x00ff0000) >> 16); - if (err_code == 0x1c || err_code == 0x06) { - DEBUG2(printk("scsi%ld: %s send target " - "completed " - "or access denied failure\n", - ha->host_no, __func__)); - } else { + ipv6_device = le16_to_cpu(fw_ddb_entry->options) & + DDB_OPT_IPV6_DEVICE; + if (qla4_is_relogin_allowed(ha, conn_err) && + ((!ipv6_device && + *((uint32_t *)fw_ddb_entry->ip_addr)) + || ipv6_device)) { qla4xxx_set_ddb_entry(ha, fw_ddb_index, 0); if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, - NULL, 0, NULL, &next_fw_ddb_index, - &ddb_state, &conn_err, NULL, NULL) - == QLA_ERROR) { + NULL, 0, NULL, + &next_fw_ddb_index, + &ddb_state, &conn_err, + NULL, NULL) + == QLA_ERROR) { DEBUG2(printk("scsi%ld: %s:" "get_ddb_entry %d failed\n", ha->host_no, @@ -599,7 +799,6 @@ next_one: struct qla4_relog_scan { int halt_wait; uint32_t conn_err; - uint32_t err_code; uint32_t fw_ddb_index; uint32_t next_fw_ddb_index; uint32_t fw_ddb_device_state; @@ -609,18 +808,7 @@ static int qla4_test_rdy(struct scsi_qla_host *ha, struct qla4_relog_scan *rs) { struct ddb_entry *ddb_entry; - /* - * Don't want to do a relogin if connection - * error is 0x1c. - */ - rs->err_code = ((rs->conn_err & 0x00ff0000) >> 16); - if (rs->err_code == 0x1c || rs->err_code == 0x06) { - DEBUG2(printk( - "scsi%ld: %s send target" - " completed or " - "access denied failure\n", - ha->host_no, __func__)); - } else { + if (qla4_is_relogin_allowed(ha, rs->conn_err)) { /* We either have a device that is in * the process of relogging in or a * device that is waiting to be @@ -908,7 +1096,7 @@ static void qla4x00_pci_config(struct scsi_qla_host *ha) static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) { int status = QLA_ERROR; - uint32_t max_wait_time; + unsigned long max_wait_time; unsigned long flags; uint32_t mbox_status; @@ -940,7 +1128,10 @@ static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Wait for firmware to come UP. */ - max_wait_time = FIRMWARE_UP_TOV * 4; + DEBUG2(printk(KERN_INFO "scsi%ld: %s: Wait up to %d seconds for " + "boot firmware to complete...\n", + ha->host_no, __func__, FIRMWARE_UP_TOV)); + max_wait_time = jiffies + (FIRMWARE_UP_TOV * HZ); do { uint32_t ctrl_status; @@ -954,16 +1145,15 @@ static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) if (mbox_status == MBOX_STS_COMMAND_COMPLETE) break; - DEBUG2(printk("scsi%ld: %s: Waiting for boot firmware to " - "complete... ctrl_sts=0x%x, remaining=%d\n", - ha->host_no, __func__, ctrl_status, - max_wait_time)); + DEBUG2(printk(KERN_INFO "scsi%ld: %s: Waiting for boot " + "firmware to complete... ctrl_sts=0x%x\n", + ha->host_no, __func__, ctrl_status)); - msleep(250); - } while ((max_wait_time--)); + msleep_interruptible(250); + } while (!time_after_eq(jiffies, max_wait_time)); if (mbox_status == MBOX_STS_COMMAND_COMPLETE) { - DEBUG(printk("scsi%ld: %s: Firmware has started\n", + DEBUG(printk(KERN_INFO "scsi%ld: %s: Firmware has started\n", ha->host_no, __func__)); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1141,6 +1331,7 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, int status = QLA_ERROR; int8_t ip_address[IP_ADDR_LEN] = {0} ; + clear_bit(AF_ONLINE, &ha->flags); ha->eeprom_cmd_data = 0; qla4x00_pci_config(ha); @@ -1166,7 +1357,7 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, * the ddb_list and wait for DHCP lease acquired aen to come in * followed by 0x8014 aen" to trigger the tgt discovery process. */ - if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) goto exit_init_online; /* Skip device discovery if ip and subnet is zero */ @@ -1270,8 +1461,8 @@ static void qla4xxx_add_device_dynamically(struct scsi_qla_host *ha, * * This routine processes a Decive Database Changed AEN Event. **/ -int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, - uint32_t fw_ddb_index, uint32_t state) +int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, + uint32_t state, uint32_t conn_err) { struct ddb_entry * ddb_entry; uint32_t old_fw_ddb_device_state; @@ -1318,19 +1509,24 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, * the device came back. */ } else { - /* Device went away, try to relogin. */ - /* Mark device missing */ - if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + /* Device went away, mark device missing */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) { + DEBUG2(dev_info(&ha->pdev->dev, "%s mark missing " + "ddb_entry 0x%p sess 0x%p conn 0x%p\n", + __func__, ddb_entry, + ddb_entry->sess, ddb_entry->conn)); qla4xxx_mark_device_missing(ha, ddb_entry); + } + /* * Relogin if device state changed to a not active state. - * However, do not relogin if this aen is a result of an IOCTL - * logout (DF_NO_RELOGIN) or if this is a discovered device. + * However, do not relogin if a RELOGIN is in process, or + * we are not allowed to relogin to this DDB. */ if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_FAILED && !test_bit(DF_RELOGIN, &ddb_entry->flags) && !test_bit(DF_NO_RELOGIN, &ddb_entry->flags) && - !test_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags)) { + qla4_is_relogin_allowed(ha, conn_err)) { /* * This triggers a relogin. After the relogin_timer * expires, the relogin gets scheduled. We must wait a @@ -1338,7 +1534,7 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, * with failed device_state or a logout response before * we can issue another relogin. */ - /* Firmware padds this timeout: (time2wait +1). + /* Firmware pads this timeout: (time2wait +1). * Driver retry to login should be longer than F/W. * Otherwise F/W will fail * set_ddb() mbx cmd with 0x4005 since it still |