[SCSI] qla4xxx: fix flash/ddb support
[pandora-kernel.git] / drivers / scsi / qla4xxx / ql4_init.c
index 3075fba..0497873 100644 (file)
@@ -773,14 +773,14 @@ int qla4xxx_start_firmware(struct scsi_qla_host *ha)
  * be freed so that when login happens from user space there are free DDB
  * indices available.
  **/
-static void qla4xxx_free_ddb_index(struct scsi_qla_host *ha)
+void qla4xxx_free_ddb_index(struct scsi_qla_host *ha)
 {
        int max_ddbs;
        int ret;
        uint32_t idx = 0, next_idx = 0;
        uint32_t state = 0, conn_err = 0;
 
-       max_ddbs =  is_qla40XX(ha) ? MAX_PRST_DEV_DB_ENTRIES :
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
                                     MAX_DEV_DB_ENTRIES;
 
        for (idx = 0; idx < max_ddbs; idx = next_idx) {
@@ -804,7 +804,6 @@ static void qla4xxx_free_ddb_index(struct scsi_qla_host *ha)
        }
 }
 
-
 /**
  * qla4xxx_initialize_adapter - initiailizes hba
  * @ha: Pointer to host adapter structure.
@@ -812,7 +811,7 @@ static void qla4xxx_free_ddb_index(struct scsi_qla_host *ha)
  * This routine parforms all of the steps necessary to initialize the adapter.
  *
  **/
-int qla4xxx_initialize_adapter(struct scsi_qla_host *ha)
+int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, int is_reset)
 {
        int status = QLA_ERROR;
 
@@ -840,7 +839,8 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha)
        if (status == QLA_ERROR)
                goto exit_init_hba;
 
-       qla4xxx_free_ddb_index(ha);
+       if (is_reset == RESET_ADAPTER)
+               qla4xxx_build_ddb_list(ha, is_reset);
 
        set_bit(AF_ONLINE, &ha->flags);
 exit_init_hba:
@@ -855,38 +855,12 @@ exit_init_hba:
        return status;
 }
 
-/**
- * qla4xxx_process_ddb_changed - process ddb state change
- * @ha - Pointer to host adapter structure.
- * @fw_ddb_index - Firmware's device database index
- * @state - Device state
- *
- * 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, uint32_t conn_err)
+int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
+                      struct ddb_entry *ddb_entry, uint32_t state)
 {
-       struct ddb_entry * ddb_entry;
        uint32_t old_fw_ddb_device_state;
        int status = QLA_ERROR;
 
-       /* check for out of range index */
-       if (fw_ddb_index >= MAX_DDB_ENTRIES)
-               goto exit_ddb_event;
-
-       /* Get the corresponging ddb entry */
-       ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index);
-       /* Device does not currently exist in our database. */
-       if (ddb_entry == NULL) {
-               ql4_printk(KERN_ERR, ha, "%s: No ddb_entry at FW index [%d]\n",
-                          __func__, fw_ddb_index);
-
-               if (state == DDB_DS_NO_CONNECTION_ACTIVE)
-                       clear_bit(fw_ddb_index, ha->ddb_idx_map);
-
-               goto exit_ddb_event;
-       }
-
        old_fw_ddb_device_state = ddb_entry->fw_ddb_device_state;
        DEBUG2(ql4_printk(KERN_INFO, ha,
                          "%s: DDB - old state = 0x%x, new state = 0x%x for "
@@ -900,9 +874,7 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                switch (state) {
                case DDB_DS_SESSION_ACTIVE:
                case DDB_DS_DISCOVERY:
-                       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);
                        qla4xxx_update_session_conn_param(ha, ddb_entry);
                        status = QLA_SUCCESS;
                        break;
@@ -936,9 +908,7 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                switch (state) {
                case DDB_DS_SESSION_ACTIVE:
                case DDB_DS_DISCOVERY:
-                       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);
                        qla4xxx_update_session_conn_param(ha, ddb_entry);
                        status = QLA_SUCCESS;
                        break;
@@ -954,7 +924,198 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                                __func__));
                break;
        }
+       return status;
+}
+
+void qla4xxx_arm_relogin_timer(struct ddb_entry *ddb_entry)
+{
+       /*
+        * This triggers a relogin.  After the relogin_timer
+        * expires, the relogin gets scheduled.  We must wait a
+        * minimum amount of time since receiving an 0x8014 AEN
+        * with failed device_state or a logout response before
+        * we can issue another relogin.
+        *
+        * 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
+        * counting down its time2wait.
+        */
+       atomic_set(&ddb_entry->relogin_timer, 0);
+       atomic_set(&ddb_entry->retry_relogin_timer,
+                  ddb_entry->default_time2wait + 4);
+
+}
+
+int qla4xxx_flash_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
+                            struct ddb_entry *ddb_entry, uint32_t state)
+{
+       uint32_t old_fw_ddb_device_state;
+       int status = QLA_ERROR;
+
+       old_fw_ddb_device_state = ddb_entry->fw_ddb_device_state;
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "%s: DDB - old state = 0x%x, new state = 0x%x for "
+                         "index [%d]\n", __func__,
+                         ddb_entry->fw_ddb_device_state, state, fw_ddb_index));
+
+       ddb_entry->fw_ddb_device_state = state;
+
+       switch (old_fw_ddb_device_state) {
+       case DDB_DS_LOGIN_IN_PROCESS:
+       case DDB_DS_NO_CONNECTION_ACTIVE:
+               switch (state) {
+               case DDB_DS_SESSION_ACTIVE:
+                       ddb_entry->unblock_sess(ddb_entry->sess);
+                       qla4xxx_update_session_conn_fwddb_param(ha, ddb_entry);
+                       status = QLA_SUCCESS;
+                       break;
+               case DDB_DS_SESSION_FAILED:
+                       iscsi_block_session(ddb_entry->sess);
+                       if (!test_bit(DF_RELOGIN, &ddb_entry->flags))
+                               qla4xxx_arm_relogin_timer(ddb_entry);
+                       status = QLA_SUCCESS;
+                       break;
+               }
+               break;
+       case DDB_DS_SESSION_ACTIVE:
+               switch (state) {
+               case DDB_DS_SESSION_FAILED:
+                       iscsi_block_session(ddb_entry->sess);
+                       if (!test_bit(DF_RELOGIN, &ddb_entry->flags))
+                               qla4xxx_arm_relogin_timer(ddb_entry);
+                       status = QLA_SUCCESS;
+                       break;
+               }
+               break;
+       case DDB_DS_SESSION_FAILED:
+               switch (state) {
+               case DDB_DS_SESSION_ACTIVE:
+                       ddb_entry->unblock_sess(ddb_entry->sess);
+                       qla4xxx_update_session_conn_fwddb_param(ha, ddb_entry);
+                       status = QLA_SUCCESS;
+                       break;
+               case DDB_DS_SESSION_FAILED:
+                       if (!test_bit(DF_RELOGIN, &ddb_entry->flags))
+                               qla4xxx_arm_relogin_timer(ddb_entry);
+                       status = QLA_SUCCESS;
+                       break;
+               }
+               break;
+       default:
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Unknown Event\n",
+                                 __func__));
+               break;
+       }
+       return status;
+}
+
+/**
+ * qla4xxx_process_ddb_changed - process ddb state change
+ * @ha - Pointer to host adapter structure.
+ * @fw_ddb_index - Firmware's device database index
+ * @state - Device state
+ *
+ * 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, uint32_t conn_err)
+{
+       struct ddb_entry *ddb_entry;
+       int status = QLA_ERROR;
+
+       /* check for out of range index */
+       if (fw_ddb_index >= MAX_DDB_ENTRIES)
+               goto exit_ddb_event;
+
+       /* Get the corresponging ddb entry */
+       ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index);
+       /* Device does not currently exist in our database. */
+       if (ddb_entry == NULL) {
+               ql4_printk(KERN_ERR, ha, "%s: No ddb_entry at FW index [%d]\n",
+                          __func__, fw_ddb_index);
+
+               if (state == DDB_DS_NO_CONNECTION_ACTIVE)
+                       clear_bit(fw_ddb_index, ha->ddb_idx_map);
+
+               goto exit_ddb_event;
+       }
+
+       ddb_entry->ddb_change(ha, fw_ddb_index, ddb_entry, state);
 
 exit_ddb_event:
        return status;
 }
+
+/**
+ * qla4xxx_login_flash_ddb - Login to target (DDB)
+ * @cls_session: Pointer to the session to login
+ *
+ * This routine logins to the target.
+ * Issues setddb and conn open mbx
+ **/
+void qla4xxx_login_flash_ddb(struct iscsi_cls_session *cls_session)
+{
+       struct iscsi_session *sess;
+       struct ddb_entry *ddb_entry;
+       struct scsi_qla_host *ha;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_dma;
+       uint32_t mbx_sts = 0;
+       int ret;
+
+       sess = cls_session->dd_data;
+       ddb_entry = sess->dd_data;
+       ha =  ddb_entry->ha;
+
+       if (!test_bit(AF_LINK_UP, &ha->flags))
+               return;
+
+       if (ddb_entry->ddb_type != FLASH_DDB) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "Skipping login to non FLASH DB"));
+               goto exit_login;
+       }
+
+       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_login;
+       }
+
+       if (ddb_entry->fw_ddb_index == INVALID_ENTRY) {
+               ret = qla4xxx_get_ddb_index(ha, &ddb_entry->fw_ddb_index);
+               if (ret == QLA_ERROR)
+                       goto exit_login;
+
+               ha->fw_ddb_index_map[ddb_entry->fw_ddb_index] = ddb_entry;
+               ha->tot_ddbs++;
+       }
+
+       memcpy(fw_ddb_entry, &ddb_entry->fw_ddb_entry,
+              sizeof(struct dev_db_entry));
+       ddb_entry->sess->target_id = ddb_entry->fw_ddb_index;
+
+       ret = qla4xxx_set_ddb_entry(ha, ddb_entry->fw_ddb_index,
+                                   fw_ddb_dma, &mbx_sts);
+       if (ret == QLA_ERROR) {
+               DEBUG2(ql4_printk(KERN_ERR, ha, "Set DDB failed\n"));
+               goto exit_login;
+       }
+
+       ddb_entry->fw_ddb_device_state = DDB_DS_LOGIN_IN_PROCESS;
+       ret = qla4xxx_conn_open(ha, ddb_entry->fw_ddb_index);
+       if (ret == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: Login failed: %s\n", __func__,
+                          sess->targetname);
+               goto exit_login;
+       }
+
+exit_login:
+       if (fw_ddb_entry)
+               dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma);
+}
+