Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / drivers / scsi / qla2xxx / qla_os.c
index 726ad4c..8b84bc4 100644 (file)
@@ -698,11 +698,6 @@ void qla2x00_sp_compl(srb_t *sp, int res)
        struct scsi_cmnd *cmd = GET_CMD_SP(sp);
        struct completion *comp = sp->comp;
 
-       if (WARN_ON_ONCE(atomic_read(&sp->ref_count) == 0))
-               return;
-
-       atomic_dec(&sp->ref_count);
-
        sp->free(sp);
        cmd->result = res;
        CMD_SP(cmd) = NULL;
@@ -794,11 +789,6 @@ void qla2xxx_qpair_sp_compl(srb_t *sp, int res)
        struct scsi_cmnd *cmd = GET_CMD_SP(sp);
        struct completion *comp = sp->comp;
 
-       if (WARN_ON_ONCE(atomic_read(&sp->ref_count) == 0))
-               return;
-
-       atomic_dec(&sp->ref_count);
-
        sp->free(sp);
        cmd->result = res;
        CMD_SP(cmd) = NULL;
@@ -903,7 +893,7 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 
        sp->u.scmd.cmd = cmd;
        sp->type = SRB_SCSI_CMD;
-       atomic_set(&sp->ref_count, 1);
+
        CMD_SP(cmd) = (void *)sp;
        sp->free = qla2x00_sp_free_dma;
        sp->done = qla2x00_sp_compl;
@@ -985,18 +975,16 @@ qla2xxx_mqueuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd,
 
        sp->u.scmd.cmd = cmd;
        sp->type = SRB_SCSI_CMD;
-       atomic_set(&sp->ref_count, 1);
        CMD_SP(cmd) = (void *)sp;
        sp->free = qla2xxx_qpair_sp_free_dma;
        sp->done = qla2xxx_qpair_sp_compl;
-       sp->qpair = qpair;
 
        rval = ha->isp_ops->start_scsi_mq(sp);
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3078,
                    "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
                if (rval == QLA_INTERFACE_ERROR)
-                       goto qc24_fail_command;
+                       goto qc24_free_sp_fail_command;
                goto qc24_host_busy_free_sp;
        }
 
@@ -1008,6 +996,11 @@ qc24_host_busy_free_sp:
 qc24_target_busy:
        return SCSI_MLQUEUE_TARGET_BUSY;
 
+qc24_free_sp_fail_command:
+       sp->free(sp);
+       CMD_SP(cmd) = NULL;
+       qla2xxx_rel_qpair_sp(sp->qpair, sp);
+
 qc24_fail_command:
        cmd->scsi_done(cmd);
 
@@ -1184,16 +1177,6 @@ qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha)
        return return_status;
 }
 
-static int
-sp_get(struct srb *sp)
-{
-       if (!refcount_inc_not_zero((refcount_t *)&sp->ref_count))
-               /* kref get fail */
-               return ENXIO;
-       else
-               return 0;
-}
-
 #define ISP_REG_DISCONNECT 0xffffffffU
 /**************************************************************************
 * qla2x00_isp_reg_stat
@@ -1249,6 +1232,9 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        uint64_t lun;
        int rval;
        struct qla_hw_data *ha = vha->hw;
+       uint32_t ratov_j;
+       struct qla_qpair *qpair;
+       unsigned long flags;
 
        if (qla2x00_isp_reg_stat(ha)) {
                ql_log(ql_log_info, vha, 0x8042,
@@ -1261,13 +1247,26 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
                return ret;
 
        sp = scsi_cmd_priv(cmd);
+       qpair = sp->qpair;
 
-       if (sp->fcport && sp->fcport->deleted)
+       if ((sp->fcport && sp->fcport->deleted) || !qpair)
                return SUCCESS;
 
-       /* Return if the command has already finished. */
-       if (sp_get(sp))
+       spin_lock_irqsave(qpair->qp_lock_ptr, flags);
+       if (sp->completed) {
+               spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
                return SUCCESS;
+       }
+
+       if (sp->abort || sp->aborted) {
+               spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
+               return FAILED;
+       }
+
+       sp->abort = 1;
+       sp->comp = ∁
+       spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
+
 
        id = cmd->device->id;
        lun = cmd->device->lun;
@@ -1276,47 +1275,37 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
            "Aborting from RISC nexus=%ld:%d:%llu sp=%p cmd=%p handle=%x\n",
            vha->host_no, id, lun, sp, cmd, sp->handle);
 
+       /*
+        * Abort will release the original Command/sp from FW. Let the
+        * original command call scsi_done. In return, he will wakeup
+        * this sleeping thread.
+        */
        rval = ha->isp_ops->abort_command(sp);
+
        ql_dbg(ql_dbg_taskm, vha, 0x8003,
               "Abort command mbx cmd=%p, rval=%x.\n", cmd, rval);
 
+       /* Wait for the command completion. */
+       ratov_j = ha->r_a_tov/10 * 4 * 1000;
+       ratov_j = msecs_to_jiffies(ratov_j);
        switch (rval) {
        case QLA_SUCCESS:
-               /*
-                * The command has been aborted. That means that the firmware
-                * won't report a completion.
-                */
-               sp->done(sp, DID_ABORT << 16);
-               ret = SUCCESS;
-               break;
-       case QLA_FUNCTION_PARAMETER_ERROR: {
-               /* Wait for the command completion. */
-               uint32_t ratov = ha->r_a_tov/10;
-               uint32_t ratov_j = msecs_to_jiffies(4 * ratov * 1000);
-
-               WARN_ON_ONCE(sp->comp);
-               sp->comp = &comp;
                if (!wait_for_completion_timeout(&comp, ratov_j)) {
                        ql_dbg(ql_dbg_taskm, vha, 0xffff,
                            "%s: Abort wait timer (4 * R_A_TOV[%d]) expired\n",
-                           __func__, ha->r_a_tov);
+                           __func__, ha->r_a_tov/10);
                        ret = FAILED;
                } else {
                        ret = SUCCESS;
                }
                break;
-       }
        default:
-               /*
-                * Either abort failed or abort and completion raced. Let
-                * the SCSI core retry the abort in the former case.
-                */
                ret = FAILED;
                break;
        }
 
        sp->comp = NULL;
-       atomic_dec(&sp->ref_count);
+
        ql_log(ql_log_info, vha, 0x801c,
            "Abort command issued nexus=%ld:%d:%llu -- %x.\n",
            vha->host_no, id, lun, ret);
@@ -1708,32 +1697,53 @@ static void qla2x00_abort_srb(struct qla_qpair *qp, srb_t *sp, const int res,
        scsi_qla_host_t *vha = qp->vha;
        struct qla_hw_data *ha = vha->hw;
        int rval;
+       bool ret_cmd;
+       uint32_t ratov_j;
 
-       if (sp_get(sp))
+       if (qla2x00_chip_is_down(vha)) {
+               sp->done(sp, res);
                return;
+       }
 
        if (sp->type == SRB_NVME_CMD || sp->type == SRB_NVME_LS ||
            (sp->type == SRB_SCSI_CMD && !ha->flags.eeh_busy &&
             !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
             !qla2x00_isp_reg_stat(ha))) {
+               if (sp->comp) {
+                       sp->done(sp, res);
+                       return;
+               }
+
                sp->comp = &comp;
+               sp->abort =  1;
                spin_unlock_irqrestore(qp->qp_lock_ptr, *flags);
-               rval = ha->isp_ops->abort_command(sp);
 
+               rval = ha->isp_ops->abort_command(sp);
+               /* Wait for command completion. */
+               ret_cmd = false;
+               ratov_j = ha->r_a_tov/10 * 4 * 1000;
+               ratov_j = msecs_to_jiffies(ratov_j);
                switch (rval) {
                case QLA_SUCCESS:
-                       sp->done(sp, res);
+                       if (wait_for_completion_timeout(&comp, ratov_j)) {
+                               ql_dbg(ql_dbg_taskm, vha, 0xffff,
+                                   "%s: Abort wait timer (4 * R_A_TOV[%d]) expired\n",
+                                   __func__, ha->r_a_tov/10);
+                               ret_cmd = true;
+                       }
+                       /* else FW return SP to driver */
                        break;
-               case QLA_FUNCTION_PARAMETER_ERROR:
-                       wait_for_completion(&comp);
+               default:
+                       ret_cmd = true;
                        break;
                }
 
                spin_lock_irqsave(qp->qp_lock_ptr, *flags);
-               sp->comp = NULL;
+               if (ret_cmd && (!sp->completed || !sp->aborted))
+                       sp->done(sp, res);
+       } else {
+               sp->done(sp, res);
        }
-
-       atomic_dec(&sp->ref_count);
 }
 
 static void
@@ -1755,7 +1765,6 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res)
        for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) {
                sp = req->outstanding_cmds[cnt];
                if (sp) {
-                       req->outstanding_cmds[cnt] = NULL;
                        switch (sp->cmd_type) {
                        case TYPE_SRB:
                                qla2x00_abort_srb(qp, sp, res, &flags);
@@ -1777,6 +1786,7 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res)
                        default:
                                break;
                        }
+                       req->outstanding_cmds[cnt] = NULL;
                }
        }
        spin_unlock_irqrestore(qp->qp_lock_ptr, flags);
@@ -3492,6 +3502,29 @@ disable_device:
        return ret;
 }
 
+static void __qla_set_remove_flag(scsi_qla_host_t *base_vha)
+{
+       scsi_qla_host_t *vp;
+       unsigned long flags;
+       struct qla_hw_data *ha;
+
+       if (!base_vha)
+               return;
+
+       ha = base_vha->hw;
+
+       spin_lock_irqsave(&ha->vport_slock, flags);
+       list_for_each_entry(vp, &ha->vp_list, list)
+               set_bit(PFLG_DRIVER_REMOVING, &vp->pci_flags);
+
+       /*
+        * Indicate device removal to prevent future board_disable
+        * and wait until any pending board_disable has completed.
+        */
+       set_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags);
+       spin_unlock_irqrestore(&ha->vport_slock, flags);
+}
+
 static void
 qla2x00_shutdown(struct pci_dev *pdev)
 {
@@ -3508,7 +3541,7 @@ qla2x00_shutdown(struct pci_dev *pdev)
         * Prevent future board_disable and wait
         * until any pending board_disable has completed.
         */
-       set_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags);
+       __qla_set_remove_flag(vha);
        cancel_work_sync(&ha->board_disable);
 
        if (!atomic_read(&pdev->enable_cnt))
@@ -3668,10 +3701,7 @@ qla2x00_remove_one(struct pci_dev *pdev)
        ha = base_vha->hw;
        ql_log(ql_log_info, base_vha, 0xb079,
            "Removing driver\n");
-
-       /* Indicate device removal to prevent future board_disable and wait
-        * until any pending board_disable has completed. */
-       set_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags);
+       __qla_set_remove_flag(base_vha);
        cancel_work_sync(&ha->board_disable);
 
        /*
@@ -4666,7 +4696,8 @@ qla2x00_mem_free(struct qla_hw_data *ha)
        ha->sfp_data = NULL;
 
        if (ha->flt)
-               dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
+               dma_free_coherent(&ha->pdev->dev,
+                   sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
                    ha->flt, ha->flt_dma);
        ha->flt = NULL;
        ha->flt_dma = 0;
@@ -5042,19 +5073,17 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e)
                        fcport->d_id = e->u.new_sess.id;
                        fcport->flags |= FCF_FABRIC_DEVICE;
                        fcport->fw_login_state = DSC_LS_PLOGI_PEND;
-                       if (e->u.new_sess.fc4_type == FS_FC4TYPE_FCP)
-                               fcport->fc4_type = FC4_TYPE_FCP_SCSI;
-
-                       if (e->u.new_sess.fc4_type == FS_FC4TYPE_NVME) {
-                               fcport->fc4_type = FC4_TYPE_OTHER;
-                               fcport->fc4f_nvme = FC4_TYPE_NVME;
-                       }
 
                        memcpy(fcport->port_name, e->u.new_sess.port_name,
                            WWN_SIZE);
 
-                       if (e->u.new_sess.fc4_type & FS_FCP_IS_N2N)
+                       fcport->fc4_type = e->u.new_sess.fc4_type;
+                       if (e->u.new_sess.fc4_type & FS_FCP_IS_N2N) {
+                               fcport->fc4_type = FS_FC4TYPE_FCP;
                                fcport->n2n_flag = 1;
+                               if (vha->flags.nvme_enabled)
+                                       fcport->fc4_type |= FS_FC4TYPE_NVME;
+                       }
 
                } else {
                        ql_dbg(ql_dbg_disc, vha, 0xffff,
@@ -5158,7 +5187,8 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e)
                                fcport->flags &= ~FCF_FABRIC_DEVICE;
                                fcport->keep_nport_handle = 1;
                                if (vha->flags.nvme_enabled) {
-                                       fcport->fc4f_nvme = 1;
+                                       fcport->fc4_type =
+                                           (FS_FC4TYPE_NVME | FS_FC4TYPE_FCP);
                                        fcport->n2n_flag = 1;
                                }
                                fcport->fw_login_state = 0;