diff options
Diffstat (limited to 'drivers/scsi/qedf')
-rw-r--r-- | drivers/scsi/qedf/Kconfig | 2 | ||||
-rw-r--r-- | drivers/scsi/qedf/qedf.h | 6 | ||||
-rw-r--r-- | drivers/scsi/qedf/qedf_els.c | 10 | ||||
-rw-r--r-- | drivers/scsi/qedf/qedf_fip.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qedf/qedf_io.c | 48 | ||||
-rw-r--r-- | drivers/scsi/qedf/qedf_main.c | 135 |
6 files changed, 179 insertions, 24 deletions
diff --git a/drivers/scsi/qedf/Kconfig b/drivers/scsi/qedf/Kconfig index 7cd993be4e57..eb81a1b0374a 100644 --- a/drivers/scsi/qedf/Kconfig +++ b/drivers/scsi/qedf/Kconfig @@ -7,6 +7,6 @@ config QEDF depends on LIBFCOE select QED_LL2 select QED_FCOE - ---help--- + help This driver supports FCoE offload for the QLogic FastLinQ 41000 Series Converged Network Adapters. diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index f3f399fe10c8..e163be8af965 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -355,6 +355,7 @@ struct qedf_ctx { #define QEDF_GRCDUMP_CAPTURE 4 #define QEDF_IN_RECOVERY 5 #define QEDF_DBG_STOP_IO 6 +#define QEDF_PROBING 8 unsigned long flags; /* Miscellaneous state flags */ int fipvlan_retries; u8 num_queues; @@ -387,7 +388,9 @@ struct qedf_ctx { #define QEDF_IO_WORK_MIN 64 mempool_t *io_mempool; struct workqueue_struct *dpc_wq; + struct delayed_work recovery_work; struct delayed_work grcdump_work; + struct delayed_work stag_work; u32 slow_sge_ios; u32 fast_sge_ios; @@ -403,6 +406,7 @@ struct qedf_ctx { u32 flogi_cnt; u32 flogi_failed; + u32 flogi_pending; /* Used for fc statistics */ struct mutex stats_mutex; @@ -468,7 +472,7 @@ extern uint qedf_dump_frames; extern uint qedf_io_tracing; extern uint qedf_stop_io_on_error; extern uint qedf_link_down_tmo; -#define QEDF_RETRY_DELAY_MAX 20 /* 2 seconds */ +#define QEDF_RETRY_DELAY_MAX 600 /* 60 seconds */ extern bool qedf_retry_delay; extern uint qedf_debug; diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index 87e169dcebdb..542ba9454257 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -388,14 +388,10 @@ void qedf_restart_rport(struct qedf_rport *fcport) mutex_lock(&lport->disc.disc_mutex); /* Recreate the rport and log back in */ rdata = fc_rport_create(lport, port_id); - if (rdata) { - mutex_unlock(&lport->disc.disc_mutex); + mutex_unlock(&lport->disc.disc_mutex); + if (rdata) fc_rport_login(rdata); - fcport->rdata = rdata; - } else { - mutex_unlock(&lport->disc.disc_mutex); - fcport->rdata = NULL; - } + fcport->rdata = rdata; } clear_bit(QEDF_RPORT_IN_RESET, &fcport->flags); } diff --git a/drivers/scsi/qedf/qedf_fip.c b/drivers/scsi/qedf/qedf_fip.c index bb82f0875eca..ad6a56ce72a8 100644 --- a/drivers/scsi/qedf/qedf_fip.c +++ b/drivers/scsi/qedf/qedf_fip.c @@ -20,7 +20,7 @@ void qedf_fcoe_send_vlan_req(struct qedf_ctx *qedf) #define MY_FIP_ALL_FCF_MACS ((__u8[6]) { 1, 0x10, 0x18, 1, 0, 2 }) static u8 my_fcoe_all_fcfs[ETH_ALEN] = MY_FIP_ALL_FCF_MACS; unsigned long flags = 0; - int rc = -1; + int rc; skb = dev_alloc_skb(sizeof(struct fip_vlan)); if (!skb) { diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index e749a2dcaad7..0f6a15c1a04b 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1021,14 +1021,18 @@ qedf_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd) atomic_inc(&fcport->ios_to_queue); if (fcport->retry_delay_timestamp) { + /* Take fcport->rport_lock for resetting the delay_timestamp */ + spin_lock_irqsave(&fcport->rport_lock, flags); if (time_after(jiffies, fcport->retry_delay_timestamp)) { fcport->retry_delay_timestamp = 0; } else { + spin_unlock_irqrestore(&fcport->rport_lock, flags); /* If retry_delay timer is active, flow off the ML */ rc = SCSI_MLQUEUE_TARGET_BUSY; atomic_dec(&fcport->ios_to_queue); goto exit_qcmd; } + spin_unlock_irqrestore(&fcport->rport_lock, flags); } io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD); @@ -1134,6 +1138,8 @@ void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, int refcount; u16 scope, qualifier = 0; u8 fw_residual_flag = 0; + unsigned long flags = 0; + u16 chk_scope = 0; if (!io_req) return; @@ -1267,16 +1273,8 @@ void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, /* Lower 14 bits */ qualifier = fcp_rsp->retry_delay_timer & 0x3FFF; - if (qedf_retry_delay && - scope > 0 && qualifier > 0 && - qualifier <= 0x3FEF) { - /* Check we don't go over the max */ - if (qualifier > QEDF_RETRY_DELAY_MAX) - qualifier = - QEDF_RETRY_DELAY_MAX; - fcport->retry_delay_timestamp = - jiffies + (qualifier * HZ / 10); - } + if (qedf_retry_delay) + chk_scope = 1; /* Record stats */ if (io_req->cdb_status == SAM_STAT_TASK_SET_FULL) @@ -1287,6 +1285,36 @@ void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, } if (io_req->fcp_resid) scsi_set_resid(sc_cmd, io_req->fcp_resid); + + if (chk_scope == 1) { + if ((scope == 1 || scope == 2) && + (qualifier > 0 && qualifier <= 0x3FEF)) { + /* Check we don't go over the max */ + if (qualifier > QEDF_RETRY_DELAY_MAX) { + qualifier = QEDF_RETRY_DELAY_MAX; + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, + "qualifier = %d\n", + (fcp_rsp->retry_delay_timer & + 0x3FFF)); + } + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, + "Scope = %d and qualifier = %d", + scope, qualifier); + /* Take fcport->rport_lock to + * update the retry_delay_timestamp + */ + spin_lock_irqsave(&fcport->rport_lock, flags); + fcport->retry_delay_timestamp = + jiffies + (qualifier * HZ / 10); + spin_unlock_irqrestore(&fcport->rport_lock, + flags); + + } else { + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, + "combination of scope = %d and qualifier = %d is not handled in qedf.\n", + scope, qualifier); + } + } break; default: QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "fcp_status=%d.\n", diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 5b19f5175c5c..36b1ca2dadbb 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -28,6 +28,8 @@ const struct qed_fcoe_ops *qed_ops; static int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id); static void qedf_remove(struct pci_dev *pdev); static void qedf_shutdown(struct pci_dev *pdev); +static void qedf_schedule_recovery_handler(void *dev); +static void qedf_recovery_handler(struct work_struct *work); /* * Driver module parameters. @@ -282,6 +284,7 @@ static void qedf_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, else if (fc_frame_payload_op(fp) == ELS_LS_ACC) { /* Set the source MAC we will use for FCoE traffic */ qedf_set_data_src_addr(qedf, fp); + qedf->flogi_pending = 0; } /* Complete flogi_compl so we can proceed to sending ADISCs */ @@ -307,6 +310,11 @@ static struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did, */ if (resp == fc_lport_flogi_resp) { qedf->flogi_cnt++; + if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) { + schedule_delayed_work(&qedf->stag_work, 2); + return NULL; + } + qedf->flogi_pending++; return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp, arg, timeout); } @@ -503,6 +511,32 @@ static void qedf_update_link_speed(struct qedf_ctx *qedf, fc_host_supported_speeds(lport->host) = lport->link_supported_speeds; } +static void qedf_bw_update(void *dev) +{ + struct qedf_ctx *qedf = (struct qedf_ctx *)dev; + struct qed_link_output link; + + /* Get the latest status of the link */ + qed_ops->common->get_link(qedf->cdev, &link); + + if (test_bit(QEDF_UNLOADING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, + "Ignore link update, driver getting unload.\n"); + return; + } + + if (link.link_up) { + if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) + qedf_update_link_speed(qedf, &link); + else + QEDF_ERR(&qedf->dbg_ctx, + "Ignore bw update, link is down.\n"); + + } else { + QEDF_ERR(&qedf->dbg_ctx, "link_up is not set.\n"); + } +} + static void qedf_link_update(void *dev, struct qed_link_output *link) { struct qedf_ctx *qedf = (struct qedf_ctx *)dev; @@ -629,6 +663,8 @@ static u32 qedf_get_login_failures(void *cookie) static struct qed_fcoe_cb_ops qedf_cb_ops = { { .link_update = qedf_link_update, + .bw_update = qedf_bw_update, + .schedule_recovery_handler = qedf_schedule_recovery_handler, .dcbx_aen = qedf_dcbx_handler, .get_generic_tlv_data = qedf_get_generic_tlv_data, .get_protocol_tlv_data = qedf_get_protocol_tlv_data, @@ -850,6 +886,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) qedf = lport_priv(lport); + qedf->flogi_pending = 0; /* For host reset, essentially do a soft link up/down */ atomic_set(&qedf->link_state, QEDF_LINK_DOWN); QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, @@ -3153,7 +3190,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) { int rc = -EINVAL; struct fc_lport *lport; - struct qedf_ctx *qedf; + struct qedf_ctx *qedf = NULL; struct Scsi_Host *host; bool is_vf = false; struct qed_ll2_params params; @@ -3183,6 +3220,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) /* Initialize qedf_ctx */ qedf = lport_priv(lport); + set_bit(QEDF_PROBING, &qedf->flags); qedf->lport = lport; qedf->ctlr.lp = lport; qedf->pdev = pdev; @@ -3197,6 +3235,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) init_completion(&qedf->fipvlan_compl); mutex_init(&qedf->stats_mutex); mutex_init(&qedf->flush_mutex); + qedf->flogi_pending = 0; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, "QLogic FastLinQ FCoE Module qedf %s, " @@ -3206,9 +3245,12 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) } else { /* Init pointers during recovery */ qedf = pci_get_drvdata(pdev); + set_bit(QEDF_PROBING, &qedf->flags); lport = qedf->lport; } + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe started.\n"); + host = lport->host; /* Allocate mempool for qedf_io_work structs */ @@ -3227,6 +3269,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update); INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery); INIT_DELAYED_WORK(&qedf->grcdump_work, qedf_wq_grcdump); + INIT_DELAYED_WORK(&qedf->stag_work, qedf_stag_change_work); qedf->fipvlan_retries = qedf_fipvlan_retries; /* Set a default prio in case DCBX doesn't converge */ if (qedf_default_prio > -1) { @@ -3281,6 +3324,13 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) } qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params); + /* Learn information crucial for qedf to progress */ + rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info); + if (rc) { + QEDF_ERR(&qedf->dbg_ctx, "Failed to fill dev info.\n"); + goto err2; + } + /* Record BDQ producer doorbell addresses */ qedf->bdq_primary_prod = qedf->dev_info.primary_dbq_rq_addr; qedf->bdq_secondary_prod = qedf->dev_info.secondary_bdq_rq_addr; @@ -3466,6 +3516,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) qedf->lport->host->host_no); qedf->dpc_wq = create_workqueue(host_buf); } + INIT_DELAYED_WORK(&qedf->recovery_work, qedf_recovery_handler); /* * GRC dump and sysfs parameters are not reaped during the recovery @@ -3513,6 +3564,10 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) else fc_fabric_login(lport); + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n"); + + clear_bit(QEDF_PROBING, &qedf->flags); + /* All good */ return 0; @@ -3538,6 +3593,11 @@ err2: err1: scsi_host_put(lport->host); err0: + if (qedf) { + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n"); + + clear_bit(QEDF_PROBING, &qedf->flags); + } return rc; } @@ -3687,11 +3747,25 @@ void qedf_get_protocol_tlv_data(void *dev, void *data) { struct qedf_ctx *qedf = dev; struct qed_mfw_tlv_fcoe *fcoe = data; - struct fc_lport *lport = qedf->lport; - struct Scsi_Host *host = lport->host; - struct fc_host_attrs *fc_host = shost_to_fc_host(host); + struct fc_lport *lport; + struct Scsi_Host *host; + struct fc_host_attrs *fc_host; struct fc_host_statistics *hst; + if (!qedf) { + QEDF_ERR(NULL, "qedf is null.\n"); + return; + } + + if (test_bit(QEDF_PROBING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Function is still probing.\n"); + return; + } + + lport = qedf->lport; + host = lport->host; + fc_host = shost_to_fc_host(host); + /* Force a refresh of the fc_host stats including offload stats */ hst = qedf_fc_get_host_stats(host); @@ -3762,11 +3836,64 @@ void qedf_get_protocol_tlv_data(void *dev, void *data) fcoe->scsi_tsk_full = qedf->task_set_fulls; } +/* Deferred work function to perform soft context reset on STAG change */ +void qedf_stag_change_work(struct work_struct *work) +{ + struct qedf_ctx *qedf = + container_of(work, struct qedf_ctx, stag_work.work); + + if (!qedf) { + QEDF_ERR(&qedf->dbg_ctx, "qedf is NULL"); + return; + } + QEDF_ERR(&qedf->dbg_ctx, "Performing software context reset.\n"); + qedf_ctx_soft_reset(qedf->lport); +} + static void qedf_shutdown(struct pci_dev *pdev) { __qedf_remove(pdev, QEDF_MODE_NORMAL); } +/* + * Recovery handler code + */ +static void qedf_schedule_recovery_handler(void *dev) +{ + struct qedf_ctx *qedf = dev; + + QEDF_ERR(&qedf->dbg_ctx, "Recovery handler scheduled.\n"); + schedule_delayed_work(&qedf->recovery_work, 0); +} + +static void qedf_recovery_handler(struct work_struct *work) +{ + struct qedf_ctx *qedf = + container_of(work, struct qedf_ctx, recovery_work.work); + + if (test_and_set_bit(QEDF_IN_RECOVERY, &qedf->flags)) + return; + + /* + * Call common_ops->recovery_prolog to allow the MFW to quiesce + * any PCI transactions. + */ + qed_ops->common->recovery_prolog(qedf->cdev); + + QEDF_ERR(&qedf->dbg_ctx, "Recovery work start.\n"); + __qedf_remove(qedf->pdev, QEDF_MODE_RECOVERY); + /* + * Reset link and dcbx to down state since we will not get a link down + * event from the MFW but calling __qedf_remove will essentially be a + * link down event. + */ + atomic_set(&qedf->link_state, QEDF_LINK_DOWN); + atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING); + __qedf_probe(qedf->pdev, QEDF_MODE_RECOVERY); + clear_bit(QEDF_IN_RECOVERY, &qedf->flags); + QEDF_ERR(&qedf->dbg_ctx, "Recovery work complete.\n"); +} + /* Generic TLV data callback */ void qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data) { |