diff options
Diffstat (limited to 'drivers/scsi/qedi/qedi_main.c')
| -rw-r--r-- | drivers/scsi/qedi/qedi_main.c | 837 |
1 files changed, 640 insertions, 197 deletions
diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 2c3783684815..56685ee22fdf 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * QLogic iSCSI Offload Driver * Copyright (c) 2016 Cavium Inc. - * - * This software is available under the terms of the GNU General Public License - * (GPL) Version 2, available from the file COPYING in the main directory of - * this source tree. */ #include <linux/module.h> @@ -31,6 +28,10 @@ #include "qedi_gbl.h" #include "qedi_iscsi.h" +static uint qedi_qed_debug; +module_param(qedi_qed_debug, uint, 0644); +MODULE_PARM_DESC(qedi_qed_debug, " QED debug level 0 (default)"); + static uint qedi_fw_debug; module_param(qedi_fw_debug, uint, 0644); MODULE_PARM_DESC(qedi_fw_debug, " Firmware debug level 0(default) to 3"); @@ -44,6 +45,15 @@ module_param(qedi_io_tracing, uint, 0644); MODULE_PARM_DESC(qedi_io_tracing, " Enable logging of SCSI requests/completions into trace buffer. (default off)."); +static uint qedi_ll2_buf_size = 0x400; +module_param(qedi_ll2_buf_size, uint, 0644); +MODULE_PARM_DESC(qedi_ll2_buf_size, + "parameter to set ping packet size, default - 0x400, Jumbo packets - 0x2400."); + +static uint qedi_flags_override; +module_param(qedi_flags_override, uint, 0644); +MODULE_PARM_DESC(qedi_flags_override, "Disable/Enable MFW error flags bits action."); + const struct qed_iscsi_ops *qedi_ops; static struct scsi_transport_template *qedi_scsi_transport; static struct pci_driver qedi_pci_driver; @@ -55,12 +65,17 @@ static void qedi_free_global_queues(struct qedi_ctx *qedi); static struct qedi_cmd *qedi_get_cmd_from_tid(struct qedi_ctx *qedi, u32 tid); static void qedi_reset_uio_rings(struct qedi_uio_dev *udev); static void qedi_ll2_free_skbs(struct qedi_ctx *qedi); +static struct nvm_iscsi_block *qedi_get_nvram_block(struct qedi_ctx *qedi); +static void qedi_recovery_handler(struct work_struct *work); +static void qedi_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); +static int qedi_suspend(struct pci_dev *pdev, pm_message_t state); static int qedi_iscsi_event_cb(void *context, u8 fw_event_code, void *fw_handle) { struct qedi_ctx *qedi; struct qedi_endpoint *qedi_ep; - struct async_data *data; + struct iscsi_eqe_data *data; int rval = 0; if (!context || !fw_handle) { @@ -72,18 +87,18 @@ static int qedi_iscsi_event_cb(void *context, u8 fw_event_code, void *fw_handle) QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, "Recv Event %d fw_handle %p\n", fw_event_code, fw_handle); - data = (struct async_data *)fw_handle; + data = (struct iscsi_eqe_data *)fw_handle; QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, - "cid=0x%x tid=0x%x err-code=0x%x fw-dbg-param=0x%x\n", - data->cid, data->itid, data->error_code, - data->fw_debug_param); + "icid=0x%x conn_id=0x%x err-code=0x%x error-pdu-opcode-reserved=0x%x\n", + data->icid, data->conn_id, data->error_code, + data->error_pdu_opcode_reserved); - qedi_ep = qedi->ep_tbl[data->cid]; + qedi_ep = qedi->ep_tbl[data->icid]; if (!qedi_ep) { QEDI_WARN(&qedi->dbg_ctx, "Cannot process event, ep already disconnected, cid=0x%x\n", - data->cid); + data->icid); WARN_ON(1); return -ENODEV; } @@ -227,7 +242,7 @@ static int __qedi_alloc_uio_rings(struct qedi_uio_dev *udev) } /* Allocating memory for Tx/Rx pkt buffer */ - udev->ll2_buf_size = TX_RX_RING * LL2_SINGLE_BUF_SIZE; + udev->ll2_buf_size = TX_RX_RING * qedi_ll2_buf_size; udev->ll2_buf_size = QEDI_PAGE_ALIGN(udev->ll2_buf_size); udev->ll2_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_COMP | __GFP_ZERO, 2); @@ -262,10 +277,8 @@ static int qedi_alloc_uio_rings(struct qedi_ctx *qedi) } udev = kzalloc(sizeof(*udev), GFP_KERNEL); - if (!udev) { - rc = -ENOMEM; + if (!udev) goto err_udev; - } udev->uio_dev = -1; @@ -282,7 +295,7 @@ static int qedi_alloc_uio_rings(struct qedi_ctx *qedi) qedi->udev = udev; udev->tx_pkt = udev->ll2_buf; - udev->rx_pkt = udev->ll2_buf + LL2_SINGLE_BUF_SIZE; + udev->rx_pkt = udev->ll2_buf + qedi_ll2_buf_size; return 0; err_uctrl: @@ -356,6 +369,7 @@ static int qedi_alloc_and_init_sb(struct qedi_ctx *qedi, ret = qedi_ops->common->sb_init(qedi->cdev, sb_info, sb_virt, sb_phys, sb_id, QED_SB_TYPE_STORAGE); if (ret) { + dma_free_coherent(&qedi->pdev->dev, sizeof(*sb_virt), sb_virt, sb_phys); QEDI_ERR(&qedi->dbg_ctx, "Status block initialization failed for id = %d.\n", sb_id); @@ -523,7 +537,7 @@ static int qedi_init_id_tbl(struct qedi_portid_tbl *id_tbl, u16 size, id_tbl->max = size; id_tbl->next = next; spin_lock_init(&id_tbl->lock); - id_tbl->table = kzalloc(DIV_ROUND_UP(size, 32) * 4, GFP_KERNEL); + id_tbl->table = kcalloc(BITS_TO_LONGS(size), sizeof(long), GFP_KERNEL); if (!id_tbl->table) return -ENOMEM; @@ -606,7 +620,7 @@ static int qedi_cm_alloc_mem(struct qedi_ctx *qedi) sizeof(struct qedi_endpoint *)), GFP_KERNEL); if (!qedi->ep_tbl) return -ENOMEM; - port_id = prandom_u32() % QEDI_LOCAL_PORT_RANGE; + port_id = get_random_u32_below(QEDI_LOCAL_PORT_RANGE); if (qedi_init_id_tbl(&qedi->lcl_port_tbl, QEDI_LOCAL_PORT_RANGE, QEDI_LOCAL_PORT_MIN, port_id)) { qedi_cm_free_mem(qedi); @@ -628,7 +642,7 @@ static struct qedi_ctx *qedi_host_alloc(struct pci_dev *pdev) goto exit_setup_shost; } - shost->max_id = QEDI_MAX_ISCSI_CONNS_PER_HBA; + shost->max_id = QEDI_MAX_ISCSI_CONNS_PER_HBA - 1; shost->max_channel = 0; shost->max_lun = ~0; shost->max_cmd_len = 16; @@ -643,8 +657,7 @@ static struct qedi_ctx *qedi_host_alloc(struct pci_dev *pdev) qedi->max_active_conns = ISCSI_MAX_SESS_PER_HBA; qedi->max_sqes = QEDI_SQ_SIZE; - if (shost_use_blk_mq(shost)) - shost->nr_hw_queues = MIN_NUM_CPUS_MSIX(qedi); + shost->nr_hw_queues = MIN_NUM_CPUS_MSIX(qedi); pci_set_drvdata(pdev, qedi); @@ -655,10 +668,8 @@ exit_setup_shost: static int qedi_ll2_rx(void *cookie, struct sk_buff *skb, u32 arg1, u32 arg2) { struct qedi_ctx *qedi = (struct qedi_ctx *)cookie; - struct qedi_uio_dev *udev; - struct qedi_uio_ctrl *uctrl; struct skb_work_list *work; - u32 prod; + struct ethhdr *eh; if (!qedi) { QEDI_ERR(NULL, "qedi is NULL\n"); @@ -672,8 +683,28 @@ static int qedi_ll2_rx(void *cookie, struct sk_buff *skb, u32 arg1, u32 arg2) return 0; } - udev = qedi->udev; - uctrl = udev->uctrl; + eh = (struct ethhdr *)skb->data; + /* Undo VLAN encapsulation */ + if (eh->h_proto == htons(ETH_P_8021Q)) { + memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2); + eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN); + skb_reset_mac_header(skb); + } + + /* Filter out non FIP/FCoE frames here to free them faster */ + if (eh->h_proto != htons(ETH_P_ARP) && + eh->h_proto != htons(ETH_P_IP) && + eh->h_proto != htons(ETH_P_IPV6)) { + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_LL2, + "Dropping frame ethertype [0x%x] len [0x%x].\n", + eh->h_proto, skb->len); + kfree_skb(skb); + return 0; + } + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_LL2, + "Allowed frame ethertype [0x%x] len [0x%x].\n", + eh->h_proto, skb->len); work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { @@ -694,17 +725,10 @@ static int qedi_ll2_rx(void *cookie, struct sk_buff *skb, u32 arg1, u32 arg2) spin_lock_bh(&qedi->ll2_lock); list_add_tail(&work->list, &qedi->ll2_skb_list); + spin_unlock_bh(&qedi->ll2_lock); - ++uctrl->hw_rx_prod_cnt; - prod = (uctrl->hw_rx_prod + 1) % RX_RING; - if (prod != uctrl->host_rx_cons) { - uctrl->hw_rx_prod = prod; - spin_unlock_bh(&qedi->ll2_lock); - wake_up_process(qedi->ll2_recv_thread); - return 0; - } + wake_up_process(qedi->ll2_recv_thread); - spin_unlock_bh(&qedi->ll2_lock); return 0; } @@ -719,6 +743,7 @@ static int qedi_ll2_process_skb(struct qedi_ctx *qedi, struct sk_buff *skb, u32 rx_bd_prod; void *pkt; int len = 0; + u32 prod; if (!qedi) { QEDI_ERR(NULL, "qedi is NULL\n"); @@ -727,12 +752,16 @@ static int qedi_ll2_process_skb(struct qedi_ctx *qedi, struct sk_buff *skb, udev = qedi->udev; uctrl = udev->uctrl; - pkt = udev->rx_pkt + (uctrl->hw_rx_prod * LL2_SINGLE_BUF_SIZE); - len = min_t(u32, skb->len, (u32)LL2_SINGLE_BUF_SIZE); + + ++uctrl->hw_rx_prod_cnt; + prod = (uctrl->hw_rx_prod + 1) % RX_RING; + + pkt = udev->rx_pkt + (prod * qedi_ll2_buf_size); + len = min_t(u32, skb->len, (u32)qedi_ll2_buf_size); memcpy(pkt, skb->data, len); memset(&rxbd, 0, sizeof(rxbd)); - rxbd.rx_pkt_index = uctrl->hw_rx_prod; + rxbd.rx_pkt_index = prod; rxbd.rx_pkt_len = len; rxbd.vlan_id = vlan_id; @@ -743,6 +772,16 @@ static int qedi_ll2_process_skb(struct qedi_ctx *qedi, struct sk_buff *skb, memcpy(p_rxbd, &rxbd, sizeof(rxbd)); + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_LL2, + "hw_rx_prod [%d] prod [%d] hw_rx_bd_prod [%d] rx_pkt_idx [%d] rx_len [%d].\n", + uctrl->hw_rx_prod, prod, uctrl->hw_rx_bd_prod, + rxbd.rx_pkt_index, rxbd.rx_pkt_len); + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_LL2, + "host_rx_cons [%d] hw_rx_bd_cons [%d].\n", + uctrl->host_rx_cons, uctrl->host_rx_bd_cons); + + uctrl->hw_rx_prod = prod; + /* notify the iscsiuio about new packet */ uio_event_notify(&udev->qedi_uinfo); @@ -756,8 +795,7 @@ static void qedi_ll2_free_skbs(struct qedi_ctx *qedi) spin_lock_bh(&qedi->ll2_lock); list_for_each_entry_safe(work, work_tmp, &qedi->ll2_skb_list, list) { list_del(&work->list); - if (work->skb) - kfree_skb(work->skb); + kfree_skb(work->skb); kfree(work); } spin_unlock_bh(&qedi->ll2_lock); @@ -794,21 +832,22 @@ static int qedi_set_iscsi_pf_param(struct qedi_ctx *qedi) u32 log_page_size; int rval = 0; - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_DISC, "Min number of MSIX %d\n", - MIN_NUM_CPUS_MSIX(qedi)); - num_sq_pages = (MAX_OUSTANDING_TASKS_PER_CON * 8) / PAGE_SIZE; + num_sq_pages = (MAX_OUTSTANDING_TASKS_PER_CON * 8) / QEDI_PAGE_SIZE; qedi->num_queues = MIN_NUM_CPUS_MSIX(qedi); + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Number of CQ count is %d\n", qedi->num_queues); + memset(&qedi->pf_params.iscsi_pf_params, 0, sizeof(qedi->pf_params.iscsi_pf_params)); - qedi->p_cpuq = pci_alloc_consistent(qedi->pdev, + qedi->p_cpuq = dma_alloc_coherent(&qedi->pdev->dev, qedi->num_queues * sizeof(struct qedi_glbl_q_params), - &qedi->hw_p_cpuq); + &qedi->hw_p_cpuq, GFP_KERNEL); if (!qedi->p_cpuq) { - QEDI_ERR(&qedi->dbg_ctx, "pci_alloc_consistent fail\n"); + QEDI_ERR(&qedi->dbg_ctx, "dma_alloc_coherent fail\n"); rval = -1; goto err_alloc_mem; } @@ -828,11 +867,12 @@ static int qedi_set_iscsi_pf_param(struct qedi_ctx *qedi) qedi->pf_params.iscsi_pf_params.num_uhq_pages_in_ring = num_sq_pages; qedi->pf_params.iscsi_pf_params.num_queues = qedi->num_queues; qedi->pf_params.iscsi_pf_params.debug_mode = qedi_fw_debug; - qedi->pf_params.iscsi_pf_params.two_msl_timer = 4000; + qedi->pf_params.iscsi_pf_params.two_msl_timer = QED_TWO_MSL_TIMER_DFLT; + qedi->pf_params.iscsi_pf_params.tx_sws_timer = QED_TX_SWS_TIMER_DFLT; qedi->pf_params.iscsi_pf_params.max_fin_rt = 2; for (log_page_size = 0 ; log_page_size < 32 ; log_page_size++) { - if ((1 << log_page_size) == PAGE_SIZE) + if ((1 << log_page_size) == QEDI_PAGE_SIZE) break; } qedi->pf_params.iscsi_pf_params.log_page_size = log_page_size; @@ -857,7 +897,6 @@ static int qedi_set_iscsi_pf_param(struct qedi_ctx *qedi) qedi->pf_params.iscsi_pf_params.gl_rq_pi = QEDI_PROTO_CQ_PROD_IDX; qedi->pf_params.iscsi_pf_params.gl_cmd_pi = 1; - qedi->pf_params.iscsi_pf_params.ooo_enable = 1; err_alloc_mem: return rval; @@ -870,7 +909,7 @@ static void qedi_free_iscsi_pf_param(struct qedi_ctx *qedi) if (qedi->p_cpuq) { size = qedi->num_queues * sizeof(struct qedi_glbl_q_params); - pci_free_consistent(qedi->pdev, size, qedi->p_cpuq, + dma_free_coherent(&qedi->pdev->dev, size, qedi->p_cpuq, qedi->hw_p_cpuq); } @@ -879,6 +918,266 @@ static void qedi_free_iscsi_pf_param(struct qedi_ctx *qedi) kfree(qedi->global_queues); } +static void qedi_get_boot_tgt_info(struct nvm_iscsi_block *block, + struct qedi_boot_target *tgt, u8 index) +{ + u32 ipv6_en; + + ipv6_en = !!(block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_IPV6_ENABLED); + + snprintf(tgt->iscsi_name, sizeof(tgt->iscsi_name), "%s", + block->target[index].target_name.byte); + + tgt->ipv6_en = ipv6_en; + + if (ipv6_en) + snprintf(tgt->ip_addr, IPV6_LEN, "%pI6\n", + block->target[index].ipv6_addr.byte); + else + snprintf(tgt->ip_addr, IPV4_LEN, "%pI4\n", + block->target[index].ipv4_addr.byte); +} + +static int qedi_find_boot_info(struct qedi_ctx *qedi, + struct qed_mfw_tlv_iscsi *iscsi, + struct nvm_iscsi_block *block) +{ + struct qedi_boot_target *pri_tgt = NULL, *sec_tgt = NULL; + u32 pri_ctrl_flags = 0, sec_ctrl_flags = 0, found = 0; + struct iscsi_cls_session *cls_sess; + struct iscsi_cls_conn *cls_conn; + struct qedi_conn *qedi_conn; + struct iscsi_session *sess; + struct iscsi_conn *conn; + char ep_ip_addr[64]; + int i, ret = 0; + + pri_ctrl_flags = !!(block->target[0].ctrl_flags & + NVM_ISCSI_CFG_TARGET_ENABLED); + if (pri_ctrl_flags) { + pri_tgt = kzalloc(sizeof(*pri_tgt), GFP_KERNEL); + if (!pri_tgt) + return -1; + qedi_get_boot_tgt_info(block, pri_tgt, 0); + } + + sec_ctrl_flags = !!(block->target[1].ctrl_flags & + NVM_ISCSI_CFG_TARGET_ENABLED); + if (sec_ctrl_flags) { + sec_tgt = kzalloc(sizeof(*sec_tgt), GFP_KERNEL); + if (!sec_tgt) { + ret = -1; + goto free_tgt; + } + qedi_get_boot_tgt_info(block, sec_tgt, 1); + } + + for (i = 0; i < qedi->max_active_conns; i++) { + qedi_conn = qedi_get_conn_from_id(qedi, i); + if (!qedi_conn) + continue; + + if (qedi_conn->ep->ip_type == TCP_IPV4) + snprintf(ep_ip_addr, IPV4_LEN, "%pI4\n", + qedi_conn->ep->dst_addr); + else + snprintf(ep_ip_addr, IPV6_LEN, "%pI6\n", + qedi_conn->ep->dst_addr); + + cls_conn = qedi_conn->cls_conn; + conn = cls_conn->dd_data; + cls_sess = iscsi_conn_to_session(cls_conn); + sess = cls_sess->dd_data; + + if (!iscsi_is_session_online(cls_sess)) + continue; + + if (!sess->targetname) + continue; + + if (pri_ctrl_flags) { + if (!strcmp(pri_tgt->iscsi_name, sess->targetname) && + !strcmp(pri_tgt->ip_addr, ep_ip_addr)) { + found = 1; + break; + } + } + + if (sec_ctrl_flags) { + if (!strcmp(sec_tgt->iscsi_name, sess->targetname) && + !strcmp(sec_tgt->ip_addr, ep_ip_addr)) { + found = 1; + break; + } + } + } + + if (found) { + if (conn->hdrdgst_en) { + iscsi->header_digest_set = true; + iscsi->header_digest = 1; + } + + if (conn->datadgst_en) { + iscsi->data_digest_set = true; + iscsi->data_digest = 1; + } + iscsi->boot_taget_portal_set = true; + iscsi->boot_taget_portal = sess->tpgt; + + } else { + ret = -1; + } + + if (sec_ctrl_flags) + kfree(sec_tgt); +free_tgt: + if (pri_ctrl_flags) + kfree(pri_tgt); + + return ret; +} + +static void qedi_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data) +{ + struct qedi_ctx *qedi; + + if (!dev) { + QEDI_INFO(NULL, QEDI_LOG_EVT, + "dev is NULL so ignoring get_generic_tlv_data request.\n"); + return; + } + qedi = (struct qedi_ctx *)dev; + + memset(data, 0, sizeof(struct qed_generic_tlvs)); + ether_addr_copy(data->mac[0], qedi->mac); +} + +/* + * Protocol TLV handler + */ +static void qedi_get_protocol_tlv_data(void *dev, void *data) +{ + struct qed_mfw_tlv_iscsi *iscsi = data; + struct qed_iscsi_stats *fw_iscsi_stats; + struct nvm_iscsi_block *block = NULL; + u32 chap_en = 0, mchap_en = 0; + struct qedi_ctx *qedi = dev; + int rval = 0; + + fw_iscsi_stats = kmalloc(sizeof(*fw_iscsi_stats), GFP_KERNEL); + if (!fw_iscsi_stats) { + QEDI_ERR(&qedi->dbg_ctx, + "Could not allocate memory for fw_iscsi_stats.\n"); + goto exit_get_data; + } + + mutex_lock(&qedi->stats_lock); + /* Query firmware for offload stats */ + qedi_ops->get_stats(qedi->cdev, fw_iscsi_stats); + mutex_unlock(&qedi->stats_lock); + + iscsi->rx_frames_set = true; + iscsi->rx_frames = fw_iscsi_stats->iscsi_rx_packet_cnt; + iscsi->rx_bytes_set = true; + iscsi->rx_bytes = fw_iscsi_stats->iscsi_rx_bytes_cnt; + iscsi->tx_frames_set = true; + iscsi->tx_frames = fw_iscsi_stats->iscsi_tx_packet_cnt; + iscsi->tx_bytes_set = true; + iscsi->tx_bytes = fw_iscsi_stats->iscsi_tx_bytes_cnt; + iscsi->frame_size_set = true; + iscsi->frame_size = qedi->ll2_mtu; + block = qedi_get_nvram_block(qedi); + if (block) { + chap_en = !!(block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_CHAP_ENABLED); + mchap_en = !!(block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_CHAP_MUTUAL_ENABLED); + + iscsi->auth_method_set = (chap_en || mchap_en) ? true : false; + iscsi->auth_method = 1; + if (chap_en) + iscsi->auth_method = 2; + if (mchap_en) + iscsi->auth_method = 3; + + iscsi->tx_desc_size_set = true; + iscsi->tx_desc_size = QEDI_SQ_SIZE; + iscsi->rx_desc_size_set = true; + iscsi->rx_desc_size = QEDI_CQ_SIZE; + + /* tpgt, hdr digest, data digest */ + rval = qedi_find_boot_info(qedi, iscsi, block); + if (rval) + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Boot target not set"); + } + + kfree(fw_iscsi_stats); +exit_get_data: + return; +} + +void qedi_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type) +{ + struct qedi_ctx *qedi = (struct qedi_ctx *)dev; + unsigned long override_flags = qedi_flags_override; + + if (override_flags && test_bit(QEDI_ERR_OVERRIDE_EN, &override_flags)) + qedi->qedi_err_flags = qedi_flags_override; + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "HW error handler scheduled, err=%d err_flags=0x%x\n", + err_type, qedi->qedi_err_flags); + + switch (err_type) { + case QED_HW_ERR_FAN_FAIL: + schedule_delayed_work(&qedi->board_disable_work, 0); + break; + case QED_HW_ERR_MFW_RESP_FAIL: + case QED_HW_ERR_HW_ATTN: + case QED_HW_ERR_DMAE_FAIL: + case QED_HW_ERR_RAMROD_FAIL: + case QED_HW_ERR_FW_ASSERT: + /* Prevent HW attentions from being reasserted */ + if (test_bit(QEDI_ERR_ATTN_CLR_EN, &qedi->qedi_err_flags)) + qedi_ops->common->attn_clr_enable(qedi->cdev, true); + + if (err_type == QED_HW_ERR_RAMROD_FAIL && + test_bit(QEDI_ERR_IS_RECOVERABLE, &qedi->qedi_err_flags)) + qedi_ops->common->recovery_process(qedi->cdev); + + break; + default: + break; + } +} + +static void qedi_schedule_recovery_handler(void *dev) +{ + struct qedi_ctx *qedi = dev; + + QEDI_ERR(&qedi->dbg_ctx, "Recovery handler scheduled.\n"); + + if (test_and_set_bit(QEDI_IN_RECOVERY, &qedi->flags)) + return; + + atomic_set(&qedi->link_state, QEDI_LINK_DOWN); + + schedule_delayed_work(&qedi->recovery_work, 0); +} + +static void qedi_set_conn_recovery(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *session = cls_session->dd_data; + struct iscsi_conn *conn = session->leadconn; + struct qedi_conn *qedi_conn = conn->dd_data; + + qedi_start_conn_recovery(qedi_conn->qedi, qedi_conn); +} + static void qedi_link_update(void *dev, struct qed_link_output *link) { struct qedi_ctx *qedi = (struct qedi_ctx *)dev; @@ -890,12 +1189,17 @@ static void qedi_link_update(void *dev, struct qed_link_output *link) QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, "Link Down event.\n"); atomic_set(&qedi->link_state, QEDI_LINK_DOWN); + iscsi_host_for_each_session(qedi->shost, qedi_set_conn_recovery); } } static struct qed_iscsi_cb_ops qedi_cb_ops = { { .link_update = qedi_link_update, + .schedule_recovery_handler = qedi_schedule_recovery_handler, + .schedule_hw_err_handler = qedi_schedule_hw_err_handler, + .get_protocol_tlv_data = qedi_get_protocol_tlv_data, + .get_generic_tlv_data = qedi_get_generic_tlv_data, } }; @@ -904,7 +1208,6 @@ static int qedi_queue_cqe(struct qedi_ctx *qedi, union iscsi_cqe *cqe, { struct qedi_work *qedi_work; struct qedi_conn *q_conn; - struct iscsi_conn *conn; struct qedi_cmd *qedi_cmd; u32 iscsi_cid; int rc = 0; @@ -917,7 +1220,6 @@ static int qedi_queue_cqe(struct qedi_ctx *qedi, union iscsi_cqe *cqe, iscsi_cid); return -1; } - conn = q_conn->cls_conn->dd_data; switch (cqe->cqe_common.cqe_type) { case ISCSI_CQE_TYPE_SOLICITED: @@ -997,7 +1299,9 @@ static bool qedi_process_completions(struct qedi_fastpath *fp) ret = qedi_queue_cqe(qedi, cqe, fp->sb_id, p); if (ret) - continue; + QEDI_WARN(&qedi->dbg_ctx, + "Dropping CQE 0x%x for cid=0x%x.\n", + que->cq_cons_idx, cqe->cqe_common.conn_id); que->cq_cons_idx++; if (que->cq_cons_idx == QEDI_CQ_SIZE) @@ -1048,13 +1352,13 @@ process_again: "process already running\n"); } - if (qedi_fp_has_work(fp) == 0) + if (!qedi_fp_has_work(fp)) qed_sb_update_sb_idx(fp->sb_info); /* Check for more work */ rmb(); - if (qedi_fp_has_work(fp) == 0) + if (!qedi_fp_has_work(fp)) qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1); else goto process_again; @@ -1075,13 +1379,20 @@ static void qedi_simd_int_handler(void *cookie) static void qedi_sync_free_irqs(struct qedi_ctx *qedi) { int i; + u16 idx; if (qedi->int_info.msix_cnt) { for (i = 0; i < qedi->int_info.used_cnt; i++) { - synchronize_irq(qedi->int_info.msix[i].vector); - irq_set_affinity_hint(qedi->int_info.msix[i].vector, + idx = i * qedi->dev_info.common.num_hwfns + + qedi_ops->common->get_affin_hwfn_idx(qedi->cdev); + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Freeing IRQ #%d vector_idx=%d.\n", i, idx); + + synchronize_irq(qedi->int_info.msix[idx].vector); + irq_set_affinity_hint(qedi->int_info.msix[idx].vector, NULL); - free_irq(qedi->int_info.msix[i].vector, + free_irq(qedi->int_info.msix[idx].vector, &qedi->fp_array[i]); } } else { @@ -1096,20 +1407,28 @@ static void qedi_sync_free_irqs(struct qedi_ctx *qedi) static int qedi_request_msix_irq(struct qedi_ctx *qedi) { int i, rc, cpu; + u16 idx; cpu = cpumask_first(cpu_online_mask); - for (i = 0; i < MIN_NUM_CPUS_MSIX(qedi); i++) { - rc = request_irq(qedi->int_info.msix[i].vector, + for (i = 0; i < qedi->msix_count; i++) { + idx = i * qedi->dev_info.common.num_hwfns + + qedi_ops->common->get_affin_hwfn_idx(qedi->cdev); + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "dev_info: num_hwfns=%d affin_hwfn_idx=%d.\n", + qedi->dev_info.common.num_hwfns, + qedi_ops->common->get_affin_hwfn_idx(qedi->cdev)); + + rc = request_irq(qedi->int_info.msix[idx].vector, qedi_msix_handler, 0, "qedi", &qedi->fp_array[i]); - if (rc) { QEDI_WARN(&qedi->dbg_ctx, "request_irq failed.\n"); qedi_sync_free_irqs(qedi); return rc; } qedi->int_info.used_cnt++; - rc = irq_set_affinity_hint(qedi->int_info.msix[i].vector, + rc = irq_set_affinity_hint(qedi->int_info.msix[idx].vector, get_cpu_mask(cpu)); cpu = cpumask_next(cpu, cpu_online_mask); } @@ -1121,7 +1440,12 @@ static int qedi_setup_int(struct qedi_ctx *qedi) { int rc = 0; - rc = qedi_ops->common->set_fp_int(qedi->cdev, num_online_cpus()); + rc = qedi_ops->common->set_fp_int(qedi->cdev, qedi->num_queues); + if (rc < 0) + goto exit_setup_int; + + qedi->msix_count = rc; + rc = qedi_ops->common->get_fp_int(qedi->cdev, &qedi->int_info); if (rc) goto exit_setup_int; @@ -1146,23 +1470,23 @@ exit_setup_int: static void qedi_free_nvm_iscsi_cfg(struct qedi_ctx *qedi) { - if (qedi->iscsi_cfg) + if (qedi->iscsi_image) dma_free_coherent(&qedi->pdev->dev, - sizeof(struct nvm_iscsi_cfg), - qedi->iscsi_cfg, qedi->nvm_buf_dma); + sizeof(struct qedi_nvm_iscsi_image), + qedi->iscsi_image, qedi->nvm_buf_dma); } static int qedi_alloc_nvm_iscsi_cfg(struct qedi_ctx *qedi) { - qedi->iscsi_cfg = dma_zalloc_coherent(&qedi->pdev->dev, - sizeof(struct nvm_iscsi_cfg), - &qedi->nvm_buf_dma, GFP_KERNEL); - if (!qedi->iscsi_cfg) { + qedi->iscsi_image = dma_alloc_coherent(&qedi->pdev->dev, + sizeof(struct qedi_nvm_iscsi_image), + &qedi->nvm_buf_dma, GFP_KERNEL); + if (!qedi->iscsi_image) { QEDI_ERR(&qedi->dbg_ctx, "Could not allocate NVM BUF.\n"); return -ENOMEM; } QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, - "NVM BUF addr=0x%p dma=0x%llx.\n", qedi->iscsi_cfg, + "NVM BUF addr=0x%p dma=0x%llx.\n", qedi->iscsi_image, qedi->nvm_buf_dma); return 0; @@ -1173,7 +1497,7 @@ static void qedi_free_bdq(struct qedi_ctx *qedi) int i; if (qedi->bdq_pbl_list) - dma_free_coherent(&qedi->pdev->dev, PAGE_SIZE, + dma_free_coherent(&qedi->pdev->dev, QEDI_PAGE_SIZE, qedi->bdq_pbl_list, qedi->bdq_pbl_list_dma); if (qedi->bdq_pbl) @@ -1216,7 +1540,6 @@ static int qedi_alloc_bdq(struct qedi_ctx *qedi) int i; struct scsi_bd *pbl; u64 *list; - dma_addr_t page; /* Alloc dma memory for BDQ buffers */ for (i = 0; i < QEDI_BDQ_NUM; i++) { @@ -1234,7 +1557,7 @@ static int qedi_alloc_bdq(struct qedi_ctx *qedi) /* Alloc dma memory for BDQ page buffer list */ qedi->bdq_pbl_mem_size = QEDI_BDQ_NUM * sizeof(struct scsi_bd); - qedi->bdq_pbl_mem_size = ALIGN(qedi->bdq_pbl_mem_size, PAGE_SIZE); + qedi->bdq_pbl_mem_size = ALIGN(qedi->bdq_pbl_mem_size, QEDI_PAGE_SIZE); qedi->rq_num_entries = qedi->bdq_pbl_mem_size / sizeof(struct scsi_bd); QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_CONN, "rq_num_entries = %d.\n", @@ -1261,14 +1584,16 @@ static int qedi_alloc_bdq(struct qedi_ctx *qedi) QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_CONN, "pbl [0x%p] pbl->address hi [0x%llx] lo [0x%llx], idx [%d]\n", pbl, pbl->address.hi, pbl->address.lo, i); - pbl->opaque.hi = 0; - pbl->opaque.lo = cpu_to_le32(QEDI_U64_LO(i)); + pbl->opaque.iscsi_opaque.reserved_zero[0] = 0; + pbl->opaque.iscsi_opaque.reserved_zero[1] = 0; + pbl->opaque.iscsi_opaque.reserved_zero[2] = 0; + pbl->opaque.iscsi_opaque.opaque = cpu_to_le16(i); pbl++; } /* Allocate list of PBL pages */ qedi->bdq_pbl_list = dma_alloc_coherent(&qedi->pdev->dev, - PAGE_SIZE, + QEDI_PAGE_SIZE, &qedi->bdq_pbl_list_dma, GFP_KERNEL); if (!qedi->bdq_pbl_list) { @@ -1276,19 +1601,17 @@ static int qedi_alloc_bdq(struct qedi_ctx *qedi) "Could not allocate list of PBL pages.\n"); return -ENOMEM; } - memset(qedi->bdq_pbl_list, 0, PAGE_SIZE); /* * Now populate PBL list with pages that contain pointers to the * individual buffers. */ - qedi->bdq_pbl_list_num_entries = qedi->bdq_pbl_mem_size / PAGE_SIZE; + qedi->bdq_pbl_list_num_entries = qedi->bdq_pbl_mem_size / + QEDI_PAGE_SIZE; list = (u64 *)qedi->bdq_pbl_list; - page = qedi->bdq_pbl_list_dma; for (i = 0; i < qedi->bdq_pbl_list_num_entries; i++) { *list = qedi->bdq_pbl_dma; list++; - page += PAGE_SIZE; } return 0; @@ -1298,7 +1621,7 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) { u32 *list; int i; - int status = 0, rc; + int status; u32 *pbl; dma_addr_t page; int num_pages; @@ -1309,14 +1632,14 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) */ if (!qedi->num_queues) { QEDI_ERR(&qedi->dbg_ctx, "No MSI-X vectors available!\n"); - return 1; + return -ENOMEM; } /* Make sure we allocated the PBL that will contain the physical * addresses of our queues */ if (!qedi->p_cpuq) { - status = 1; + status = -EINVAL; goto mem_alloc_failure; } @@ -1331,13 +1654,13 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) "qedi->global_queues=%p.\n", qedi->global_queues); /* Allocate DMA coherent buffers for BDQ */ - rc = qedi_alloc_bdq(qedi); - if (rc) + status = qedi_alloc_bdq(qedi); + if (status) goto mem_alloc_failure; /* Allocate DMA coherent buffers for NVM_ISCSI_CFG */ - rc = qedi_alloc_nvm_iscsi_cfg(qedi); - if (rc) + status = qedi_alloc_nvm_iscsi_cfg(qedi); + if (status) goto mem_alloc_failure; /* Allocate a CQ and an associated PBL for each MSI-X @@ -1350,6 +1673,7 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) if (!qedi->global_queues[i]) { QEDI_ERR(&qedi->dbg_ctx, "Unable to allocation global queue %d.\n", i); + status = -ENOMEM; goto mem_alloc_failure; } @@ -1366,11 +1690,10 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) (qedi->global_queues[i]->cq_pbl_size + (QEDI_PAGE_SIZE - 1)); - qedi->global_queues[i]->cq = - dma_alloc_coherent(&qedi->pdev->dev, - qedi->global_queues[i]->cq_mem_size, - &qedi->global_queues[i]->cq_dma, - GFP_KERNEL); + qedi->global_queues[i]->cq = dma_alloc_coherent(&qedi->pdev->dev, + qedi->global_queues[i]->cq_mem_size, + &qedi->global_queues[i]->cq_dma, + GFP_KERNEL); if (!qedi->global_queues[i]->cq) { QEDI_WARN(&qedi->dbg_ctx, @@ -1378,14 +1701,10 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) status = -ENOMEM; goto mem_alloc_failure; } - memset(qedi->global_queues[i]->cq, 0, - qedi->global_queues[i]->cq_mem_size); - - qedi->global_queues[i]->cq_pbl = - dma_alloc_coherent(&qedi->pdev->dev, - qedi->global_queues[i]->cq_pbl_size, - &qedi->global_queues[i]->cq_pbl_dma, - GFP_KERNEL); + qedi->global_queues[i]->cq_pbl = dma_alloc_coherent(&qedi->pdev->dev, + qedi->global_queues[i]->cq_pbl_size, + &qedi->global_queues[i]->cq_pbl_dma, + GFP_KERNEL); if (!qedi->global_queues[i]->cq_pbl) { QEDI_WARN(&qedi->dbg_ctx, @@ -1393,8 +1712,6 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) status = -ENOMEM; goto mem_alloc_failure; } - memset(qedi->global_queues[i]->cq_pbl, 0, - qedi->global_queues[i]->cq_pbl_size); /* Create PBL */ num_pages = qedi->global_queues[i]->cq_mem_size / @@ -1463,8 +1780,6 @@ int qedi_alloc_sq(struct qedi_ctx *qedi, struct qedi_endpoint *ep) rval = -ENOMEM; goto out; } - memset(ep->sq, 0, ep->sq_mem_size); - ep->sq_pbl = dma_alloc_coherent(&qedi->pdev->dev, ep->sq_pbl_size, &ep->sq_pbl_dma, GFP_KERNEL); if (!ep->sq_pbl) { @@ -1473,7 +1788,6 @@ int qedi_alloc_sq(struct qedi_ctx *qedi, struct qedi_endpoint *ep) rval = -ENOMEM; goto out_free_sq; } - memset(ep->sq_pbl, 0, ep->sq_pbl_size); /* Create PBL */ num_pages = ep->sq_mem_size / QEDI_PAGE_SIZE; @@ -1563,19 +1877,11 @@ void qedi_get_task_tid(struct qedi_ctx *qedi, u32 itt, s16 *tid) WARN_ON(1); } -void qedi_get_proto_itt(struct qedi_ctx *qedi, u32 tid, u32 *proto_itt) -{ - *proto_itt = qedi->itt_map[tid].itt; - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_CONN, - "Get itt map tid [0x%x with proto itt[0x%x]", - tid, *proto_itt); -} - struct qedi_cmd *qedi_get_cmd_from_tid(struct qedi_ctx *qedi, u32 tid) { struct qedi_cmd *cmd = NULL; - if (tid > MAX_ISCSI_TASK_ENTRIES) + if (tid >= MAX_ISCSI_TASK_ENTRIES) return NULL; cmd = qedi->itt_map[tid].p_cmd; @@ -1647,13 +1953,11 @@ static int qedi_cpu_online(unsigned int cpu) struct qedi_percpu_s *p = this_cpu_ptr(&qedi_percpu); struct task_struct *thread; - thread = kthread_create_on_node(qedi_percpu_io_thread, (void *)p, - cpu_to_node(cpu), - "qedi_thread/%d", cpu); + thread = kthread_create_on_cpu(qedi_percpu_io_thread, (void *)p, + cpu, "qedi_thread/%d"); if (IS_ERR(thread)) return PTR_ERR(thread); - kthread_bind(thread, cpu); p->iothread = thread; wake_up_process(thread); return 0; @@ -1664,8 +1968,9 @@ static int qedi_cpu_offline(unsigned int cpu) struct qedi_percpu_s *p = this_cpu_ptr(&qedi_percpu); struct qedi_work *work, *tmp; struct task_struct *thread; + unsigned long flags; - spin_lock_bh(&p->p_work_lock); + spin_lock_irqsave(&p->p_work_lock, flags); thread = p->iothread; p->iothread = NULL; @@ -1676,7 +1981,7 @@ static int qedi_cpu_offline(unsigned int cpu) kfree(work); } - spin_unlock_bh(&p->p_work_lock); + spin_unlock_irqrestore(&p->p_work_lock, flags); if (thread) kthread_stop(thread); return 0; @@ -1702,7 +2007,7 @@ void qedi_reset_host_mtu(struct qedi_ctx *qedi, u16 mtu) qedi_ops->ll2->start(qedi->cdev, ¶ms); } -/** +/* * qedi_get_nvram_block: - Scan through the iSCSI NVRAM block (while accounting * for gaps) for the matching absolute-pf-id of the QEDI device. */ @@ -1715,7 +2020,7 @@ qedi_get_nvram_block(struct qedi_ctx *qedi) struct nvm_iscsi_block *block; pf = qedi->dev_info.common.abs_pf_id; - block = &qedi->iscsi_cfg->block[0]; + block = &qedi->iscsi_image->iscsi_cfg.block[0]; for (i = 0; i < NUM_OF_ISCSI_PF_SUPPORTED; i++, block++) { flags = ((block->id) & NVM_ISCSI_CFG_BLK_CTRL_FLAG_MASK) >> NVM_ISCSI_CFG_BLK_CTRL_FLAG_OFFSET; @@ -1732,7 +2037,6 @@ static ssize_t qedi_show_boot_eth_info(void *data, int type, char *buf) { struct qedi_ctx *qedi = data; struct nvm_iscsi_initiator *initiator; - char *str = buf; int rc = 1; u32 ipv6_en, dhcp_en, ip_len; struct nvm_iscsi_block *block; @@ -1766,32 +2070,31 @@ static ssize_t qedi_show_boot_eth_info(void *data, int type, char *buf) switch (type) { case ISCSI_BOOT_ETH_IP_ADDR: - rc = snprintf(str, ip_len, fmt, ip); + rc = snprintf(buf, ip_len, fmt, ip); break; case ISCSI_BOOT_ETH_SUBNET_MASK: - rc = snprintf(str, ip_len, fmt, sub); + rc = snprintf(buf, ip_len, fmt, sub); break; case ISCSI_BOOT_ETH_GATEWAY: - rc = snprintf(str, ip_len, fmt, gw); + rc = snprintf(buf, ip_len, fmt, gw); break; case ISCSI_BOOT_ETH_FLAGS: - rc = snprintf(str, 3, "%hhd\n", - SYSFS_FLAG_FW_SEL_BOOT); + rc = snprintf(buf, 3, "%d\n", (char)SYSFS_FLAG_FW_SEL_BOOT); break; case ISCSI_BOOT_ETH_INDEX: - rc = snprintf(str, 3, "0\n"); + rc = snprintf(buf, 3, "0\n"); break; case ISCSI_BOOT_ETH_MAC: - rc = sysfs_format_mac(str, qedi->mac, ETH_ALEN); + rc = sysfs_format_mac(buf, qedi->mac, ETH_ALEN); break; case ISCSI_BOOT_ETH_VLAN: - rc = snprintf(str, 12, "%d\n", + rc = snprintf(buf, 12, "%d\n", GET_FIELD2(initiator->generic_cont0, NVM_ISCSI_CFG_INITIATOR_VLAN)); break; case ISCSI_BOOT_ETH_ORIGIN: if (dhcp_en) - rc = snprintf(str, 3, "3\n"); + rc = snprintf(buf, 3, "3\n"); break; default: rc = 0; @@ -1827,7 +2130,6 @@ static ssize_t qedi_show_boot_ini_info(void *data, int type, char *buf) { struct qedi_ctx *qedi = data; struct nvm_iscsi_initiator *initiator; - char *str = buf; int rc; struct nvm_iscsi_block *block; @@ -1839,8 +2141,8 @@ static ssize_t qedi_show_boot_ini_info(void *data, int type, char *buf) switch (type) { case ISCSI_BOOT_INI_INITIATOR_NAME: - rc = snprintf(str, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n", - initiator->initiator_name.byte); + rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, + initiator->initiator_name.byte); break; default: rc = 0; @@ -1868,7 +2170,6 @@ static ssize_t qedi_show_boot_tgt_info(struct qedi_ctx *qedi, int type, char *buf, enum qedi_nvm_tgts idx) { - char *str = buf; int rc = 1; u32 ctrl_flags, ipv6_en, chap_en, mchap_en, ip_len; struct nvm_iscsi_block *block; @@ -1907,48 +2208,48 @@ qedi_show_boot_tgt_info(struct qedi_ctx *qedi, int type, switch (type) { case ISCSI_BOOT_TGT_NAME: - rc = snprintf(str, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n", - block->target[idx].target_name.byte); + rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, + block->target[idx].target_name.byte); break; case ISCSI_BOOT_TGT_IP_ADDR: if (ipv6_en) - rc = snprintf(str, ip_len, "%pI6\n", + rc = snprintf(buf, ip_len, "%pI6\n", block->target[idx].ipv6_addr.byte); else - rc = snprintf(str, ip_len, "%pI4\n", + rc = snprintf(buf, ip_len, "%pI4\n", block->target[idx].ipv4_addr.byte); break; case ISCSI_BOOT_TGT_PORT: - rc = snprintf(str, 12, "%d\n", + rc = snprintf(buf, 12, "%d\n", GET_FIELD2(block->target[idx].generic_cont0, NVM_ISCSI_CFG_TARGET_TCP_PORT)); break; case ISCSI_BOOT_TGT_LUN: - rc = snprintf(str, 22, "%.*d\n", + rc = snprintf(buf, 22, "%.*d\n", block->target[idx].lun.value[1], block->target[idx].lun.value[0]); break; case ISCSI_BOOT_TGT_CHAP_NAME: - rc = snprintf(str, NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, "%s\n", - chap_name); + rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, + chap_name); break; case ISCSI_BOOT_TGT_CHAP_SECRET: - rc = snprintf(str, NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, "%s\n", - chap_secret); + rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, + chap_secret); break; case ISCSI_BOOT_TGT_REV_CHAP_NAME: - rc = snprintf(str, NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, "%s\n", - mchap_name); + rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, + mchap_name); break; case ISCSI_BOOT_TGT_REV_CHAP_SECRET: - rc = snprintf(str, NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, "%s\n", - mchap_secret); + rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, + mchap_secret); break; case ISCSI_BOOT_TGT_FLAGS: - rc = snprintf(str, 3, "%hhd\n", SYSFS_FLAG_FW_SEL_BOOT); + rc = snprintf(buf, 3, "%d\n", (char)SYSFS_FLAG_FW_SEL_BOOT); break; case ISCSI_BOOT_TGT_NIC_ASSOC: - rc = snprintf(str, 3, "0\n"); + rc = snprintf(buf, 3, "0\n"); break; default: rc = 0; @@ -2007,15 +2308,13 @@ static void qedi_boot_release(void *data) static int qedi_get_boot_info(struct qedi_ctx *qedi) { int ret = 1; - u16 len; - - len = sizeof(struct nvm_iscsi_cfg); QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, "Get NVM iSCSI CFG image\n"); ret = qedi_ops->common->nvm_get_image(qedi->cdev, QED_NVM_IMAGE_ISCSI_CFG, - (char *)qedi->iscsi_cfg, len); + (char *)qedi->iscsi_image, + sizeof(struct qedi_nvm_iscsi_image)); if (ret) QEDI_ERR(&qedi->dbg_ctx, "Could not get NVM image. ret = %d\n", ret); @@ -2083,20 +2382,46 @@ kset_free: return -ENOMEM; } -static void __qedi_remove(struct pci_dev *pdev, int mode) +static pci_ers_result_t qedi_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) { struct qedi_ctx *qedi = pci_get_drvdata(pdev); - if (qedi->tmf_thread) { - flush_workqueue(qedi->tmf_thread); - destroy_workqueue(qedi->tmf_thread); - qedi->tmf_thread = NULL; + QEDI_ERR(&qedi->dbg_ctx, "%s: PCI error detected [%d]\n", + __func__, state); + + if (test_and_set_bit(QEDI_IN_RECOVERY, &qedi->flags)) { + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Recovery already in progress.\n"); + return PCI_ERS_RESULT_NONE; } - if (qedi->offload_thread) { - flush_workqueue(qedi->offload_thread); - destroy_workqueue(qedi->offload_thread); - qedi->offload_thread = NULL; + qedi_ops->common->recovery_process(qedi->cdev); + + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static void __qedi_remove(struct pci_dev *pdev, int mode) +{ + struct qedi_ctx *qedi = pci_get_drvdata(pdev); + int rval; + u16 retry = 10; + + if (mode == QEDI_MODE_NORMAL) + iscsi_host_remove(qedi->shost, false); + else if (mode == QEDI_MODE_SHUTDOWN) + iscsi_host_remove(qedi->shost, true); + + if (mode == QEDI_MODE_NORMAL || mode == QEDI_MODE_SHUTDOWN) { + if (qedi->tmf_thread) { + destroy_workqueue(qedi->tmf_thread); + qedi->tmf_thread = NULL; + } + + if (qedi->offload_thread) { + destroy_workqueue(qedi->offload_thread); + qedi->offload_thread = NULL; + } } #ifdef CONFIG_DEBUG_FS @@ -2108,12 +2433,24 @@ static void __qedi_remove(struct pci_dev *pdev, int mode) qedi_sync_free_irqs(qedi); if (!test_bit(QEDI_IN_OFFLINE, &qedi->flags)) { - qedi_ops->stop(qedi->cdev); + while (retry--) { + rval = qedi_ops->stop(qedi->cdev); + if (rval < 0) + msleep(1000); + else + break; + } qedi_ops->ll2->stop(qedi->cdev); } - if (mode == QEDI_MODE_NORMAL) - qedi_free_iscsi_pf_param(qedi); + cancel_delayed_work_sync(&qedi->recovery_work); + cancel_delayed_work_sync(&qedi->board_disable_work); + + qedi_free_iscsi_pf_param(qedi); + + rval = qedi_ops->common->update_drv_state(qedi->cdev, false); + if (rval) + QEDI_ERR(&qedi->dbg_ctx, "Failed to send drv state to MFW\n"); if (!test_bit(QEDI_IN_OFFLINE, &qedi->flags)) { qedi_ops->common->slowpath_stop(qedi->cdev); @@ -2122,15 +2459,12 @@ static void __qedi_remove(struct pci_dev *pdev, int mode) qedi_destroy_fp(qedi); - if (mode == QEDI_MODE_NORMAL) { + if (mode == QEDI_MODE_NORMAL || mode == QEDI_MODE_SHUTDOWN) { qedi_release_cid_que(qedi); qedi_cm_free_mem(qedi); qedi_free_uio(qedi->udev); qedi_free_itt(qedi); - iscsi_host_remove(qedi->shost); - iscsi_host_free(qedi->shost); - if (qedi->ll2_recv_thread) { kthread_stop(qedi->ll2_recv_thread); qedi->ll2_recv_thread = NULL; @@ -2139,14 +2473,56 @@ static void __qedi_remove(struct pci_dev *pdev, int mode) if (qedi->boot_kset) iscsi_boot_destroy_kset(qedi->boot_kset); + + iscsi_host_free(qedi->shost); } } +static void qedi_board_disable_work(struct work_struct *work) +{ + struct qedi_ctx *qedi = + container_of(work, struct qedi_ctx, + board_disable_work.work); + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Fan failure, Unloading firmware context.\n"); + + if (test_and_set_bit(QEDI_IN_SHUTDOWN, &qedi->flags)) + return; + + __qedi_remove(qedi->pdev, QEDI_MODE_NORMAL); +} + +static void qedi_shutdown(struct pci_dev *pdev) +{ + struct qedi_ctx *qedi = pci_get_drvdata(pdev); + + QEDI_ERR(&qedi->dbg_ctx, "%s: Shutdown qedi\n", __func__); + if (test_and_set_bit(QEDI_IN_SHUTDOWN, &qedi->flags)) + return; + __qedi_remove(pdev, QEDI_MODE_SHUTDOWN); +} + +static int qedi_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct qedi_ctx *qedi; + + if (!pdev) { + QEDI_ERR(NULL, "pdev is NULL.\n"); + return -ENODEV; + } + + qedi = pci_get_drvdata(pdev); + + QEDI_ERR(&qedi->dbg_ctx, "%s: Device does not support suspend operation\n", __func__); + + return -EPERM; +} + static int __qedi_probe(struct pci_dev *pdev, int mode) { struct qedi_ctx *qedi; struct qed_ll2_params params; - u32 dp_module = 0; u8 dp_level = 0; bool is_vf = false; char host_buf[16]; @@ -2155,7 +2531,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) struct qed_probe_params qed_params; void *task_start, *task_end; int rc; - u16 tmp; + u16 retry = 10; if (mode != QEDI_MODE_RECOVERY) { qedi = qedi_host_alloc(pdev); @@ -2167,29 +2543,48 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) qedi = pci_get_drvdata(pdev); } +retry_probe: + if (mode == QEDI_MODE_RECOVERY) + msleep(2000); + memset(&qed_params, 0, sizeof(qed_params)); qed_params.protocol = QED_PROTOCOL_ISCSI; - qed_params.dp_module = dp_module; + qed_params.dp_module = qedi_qed_debug; qed_params.dp_level = dp_level; qed_params.is_vf = is_vf; qedi->cdev = qedi_ops->common->probe(pdev, &qed_params); if (!qedi->cdev) { + if (mode == QEDI_MODE_RECOVERY && retry) { + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Retry %d initialize hardware\n", retry); + retry--; + goto retry_probe; + } + rc = -ENODEV; QEDI_ERR(&qedi->dbg_ctx, "Cannot initialize hardware\n"); goto free_host; } - qedi->msix_count = MAX_NUM_MSIX_PF; + set_bit(QEDI_ERR_ATTN_CLR_EN, &qedi->qedi_err_flags); + set_bit(QEDI_ERR_IS_RECOVERABLE, &qedi->qedi_err_flags); atomic_set(&qedi->link_state, QEDI_LINK_DOWN); - if (mode != QEDI_MODE_RECOVERY) { - rc = qedi_set_iscsi_pf_param(qedi); - if (rc) { - rc = -ENOMEM; - QEDI_ERR(&qedi->dbg_ctx, - "Set iSCSI pf param fail\n"); - goto free_host; - } + rc = qedi_ops->fill_dev_info(qedi->cdev, &qedi->dev_info); + if (rc) + goto free_host; + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "dev_info: num_hwfns=%d affin_hwfn_idx=%d.\n", + qedi->dev_info.common.num_hwfns, + qedi_ops->common->get_affin_hwfn_idx(qedi->cdev)); + + rc = qedi_set_iscsi_pf_param(qedi); + if (rc) { + rc = -ENOMEM; + QEDI_ERR(&qedi->dbg_ctx, + "Set iSCSI pf param fail\n"); + goto free_host; } qedi_ops->common->update_pf_params(qedi->cdev, &qedi->pf_params); @@ -2207,7 +2602,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) sp_params.drv_minor = QEDI_DRIVER_MINOR_VER; sp_params.drv_rev = QEDI_DRIVER_REV_VER; sp_params.drv_eng = QEDI_DRIVER_ENG_VER; - strlcpy(sp_params.name, "qedi iSCSI", QED_DRV_VER_STR_SIZE); + strscpy(sp_params.name, "qedi iSCSI", QED_DRV_VER_STR_SIZE); rc = qedi_ops->common->slowpath_start(qedi->cdev, &sp_params); if (rc) { QEDI_ERR(&qedi->dbg_ctx, "Cannot start slowpath\n"); @@ -2248,15 +2643,15 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) "Writing %d to primary and secondary BDQ doorbell registers.\n", qedi->bdq_prod_idx); writew(qedi->bdq_prod_idx, qedi->bdq_primary_prod); - tmp = readw(qedi->bdq_primary_prod); + readw(qedi->bdq_primary_prod); writew(qedi->bdq_prod_idx, qedi->bdq_secondary_prod); - tmp = readw(qedi->bdq_secondary_prod); + readw(qedi->bdq_secondary_prod); ether_addr_copy(qedi->mac, qedi->dev_info.common.hw_mac); QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_DISC, "MAC address is %pM.\n", qedi->mac); - sprintf(host_buf, "host_%d", qedi->shost->host_no); + snprintf(host_buf, sizeof(host_buf), "host_%d", qedi->shost->host_no); qedi_ops->common->set_name(qedi->cdev, host_buf); qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi); @@ -2275,6 +2670,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) /* start qedi context */ spin_lock_init(&qedi->hba_lock); spin_lock_init(&qedi->task_idx_lock); + mutex_init(&qedi->stats_lock); } qedi_ops->ll2->register_cb_ops(qedi->cdev, &qedi_ll2_cb_ops, qedi); qedi_ops->ll2->start(qedi->cdev, ¶ms); @@ -2308,8 +2704,8 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) } #ifdef CONFIG_DEBUG_FS - qedi_dbg_host_init(&qedi->dbg_ctx, &qedi_debugfs_ops, - &qedi_dbg_fops); + qedi_dbg_host_init(&qedi->dbg_ctx, qedi_debugfs_ops, + qedi_dbg_fops); #endif QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, "QLogic FastLinQ iSCSI Module qedi %s, FW %d.%d.%d.%d\n", @@ -2362,7 +2758,8 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) } sprintf(host_buf, "host_%d", qedi->shost->host_no); - qedi->tmf_thread = create_singlethread_workqueue(host_buf); + qedi->tmf_thread = + alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, host_buf); if (!qedi->tmf_thread) { QEDI_ERR(&qedi->dbg_ctx, "Unable to start tmf thread!\n"); @@ -2370,15 +2767,20 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) goto free_cid_que; } - sprintf(host_buf, "qedi_ofld%d", qedi->shost->host_no); - qedi->offload_thread = create_workqueue(host_buf); + qedi->offload_thread = alloc_workqueue("qedi_ofld%d", + WQ_MEM_RECLAIM | WQ_PERCPU, + 1, qedi->shost->host_no); if (!qedi->offload_thread) { QEDI_ERR(&qedi->dbg_ctx, "Unable to start offload thread!\n"); rc = -ENODEV; - goto free_cid_que; + goto free_tmf_thread; } + INIT_DELAYED_WORK(&qedi->recovery_work, qedi_recovery_handler); + INIT_DELAYED_WORK(&qedi->board_disable_work, + qedi_board_disable_work); + /* F/w needs 1st task context memory entry for performance */ set_bit(QEDI_RESERVE_TASK_ID, qedi->task_idx_map); atomic_set(&qedi->num_offloads, 0); @@ -2386,10 +2788,18 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) if (qedi_setup_boot_info(qedi)) QEDI_ERR(&qedi->dbg_ctx, "No iSCSI boot target configured\n"); + + rc = qedi_ops->common->update_drv_state(qedi->cdev, true); + if (rc) + QEDI_ERR(&qedi->dbg_ctx, + "Failed to send drv state to MFW\n"); + } return 0; +free_tmf_thread: + destroy_workqueue(qedi->tmf_thread); free_cid_que: qedi_release_cid_que(qedi); free_uio: @@ -2398,7 +2808,7 @@ remove_host: #ifdef CONFIG_DEBUG_FS qedi_dbg_host_exit(&qedi->dbg_ctx); #endif - iscsi_host_remove(qedi->shost); + iscsi_host_remove(qedi->shost, false); stop_iscsi_func: qedi_ops->stop(qedi->cdev); stop_slowpath: @@ -2413,6 +2823,32 @@ exit_probe: return rc; } +static void qedi_mark_conn_recovery(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *session = cls_session->dd_data; + struct iscsi_conn *conn = session->leadconn; + struct qedi_conn *qedi_conn = conn->dd_data; + + iscsi_conn_failure(qedi_conn->cls_conn->dd_data, ISCSI_ERR_CONN_FAILED); +} + +static void qedi_recovery_handler(struct work_struct *work) +{ + struct qedi_ctx *qedi = + container_of(work, struct qedi_ctx, recovery_work.work); + + iscsi_host_for_each_session(qedi->shost, qedi_mark_conn_recovery); + + /* Call common_ops->recovery_prolog to allow the MFW to quiesce + * any PCI transactions. + */ + qedi_ops->common->recovery_prolog(qedi->cdev); + + __qedi_remove(qedi->pdev, QEDI_MODE_RECOVERY); + __qedi_probe(qedi->pdev, QEDI_MODE_RECOVERY); + clear_bit(QEDI_IN_RECOVERY, &qedi->flags); +} + static int qedi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { return __qedi_probe(pdev, QEDI_MODE_NORMAL); @@ -2423,7 +2859,7 @@ static void qedi_remove(struct pci_dev *pdev) __qedi_remove(pdev, QEDI_MODE_NORMAL); } -static struct pci_device_id qedi_pci_tbl[] = { +static const struct pci_device_id qedi_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x165E) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x8084) }, { 0 }, @@ -2432,11 +2868,18 @@ MODULE_DEVICE_TABLE(pci, qedi_pci_tbl); static enum cpuhp_state qedi_cpuhp_state; +static const struct pci_error_handlers qedi_err_handler = { + .error_detected = qedi_io_error_detected, +}; + static struct pci_driver qedi_pci_driver = { .name = QEDI_MODULE_NAME, .id_table = qedi_pci_tbl, .probe = qedi_probe, .remove = qedi_remove, + .shutdown = qedi_shutdown, + .err_handler = &qedi_err_handler, + .suspend = qedi_suspend, }; static int __init qedi_init(void) |
