18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * QLogic FCoE Offload Driver 48c2ecf20Sopenharmony_ci * Copyright (c) 2016-2018 Cavium Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/highmem.h> 128c2ecf20Sopenharmony_ci#include <linux/crc32.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/list.h> 158c2ecf20Sopenharmony_ci#include <linux/kthread.h> 168c2ecf20Sopenharmony_ci#include <linux/phylink.h> 178c2ecf20Sopenharmony_ci#include <scsi/libfc.h> 188c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 198c2ecf20Sopenharmony_ci#include <scsi/fc_frame.h> 208c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 218c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 228c2ecf20Sopenharmony_ci#include <linux/cpu.h> 238c2ecf20Sopenharmony_ci#include "qedf.h" 248c2ecf20Sopenharmony_ci#include "qedf_dbg.h" 258c2ecf20Sopenharmony_ci#include <uapi/linux/pci_regs.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciconst struct qed_fcoe_ops *qed_ops; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id); 308c2ecf20Sopenharmony_cistatic void qedf_remove(struct pci_dev *pdev); 318c2ecf20Sopenharmony_cistatic void qedf_shutdown(struct pci_dev *pdev); 328c2ecf20Sopenharmony_cistatic void qedf_schedule_recovery_handler(void *dev); 338c2ecf20Sopenharmony_cistatic void qedf_recovery_handler(struct work_struct *work); 348c2ecf20Sopenharmony_cistatic int qedf_suspend(struct pci_dev *pdev, pm_message_t state); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Driver module parameters. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic unsigned int qedf_dev_loss_tmo = 60; 408c2ecf20Sopenharmony_cimodule_param_named(dev_loss_tmo, qedf_dev_loss_tmo, int, S_IRUGO); 418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev_loss_tmo, " dev_loss_tmo setting for attached " 428c2ecf20Sopenharmony_ci "remote ports (default 60)"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciuint qedf_debug = QEDF_LOG_INFO; 458c2ecf20Sopenharmony_cimodule_param_named(debug, qedf_debug, uint, S_IRUGO|S_IWUSR); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, " Debug mask. Pass '1' to enable default debugging" 478c2ecf20Sopenharmony_ci " mask"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic uint qedf_fipvlan_retries = 60; 508c2ecf20Sopenharmony_cimodule_param_named(fipvlan_retries, qedf_fipvlan_retries, int, S_IRUGO); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fipvlan_retries, " Number of FIP VLAN requests to attempt " 528c2ecf20Sopenharmony_ci "before giving up (default 60)"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic uint qedf_fallback_vlan = QEDF_FALLBACK_VLAN; 558c2ecf20Sopenharmony_cimodule_param_named(fallback_vlan, qedf_fallback_vlan, int, S_IRUGO); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fallback_vlan, " VLAN ID to try if fip vlan request fails " 578c2ecf20Sopenharmony_ci "(default 1002)."); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int qedf_default_prio = -1; 608c2ecf20Sopenharmony_cimodule_param_named(default_prio, qedf_default_prio, int, S_IRUGO); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_prio, " Override 802.1q priority for FIP and FCoE" 628c2ecf20Sopenharmony_ci " traffic (value between 0 and 7, default 3)."); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciuint qedf_dump_frames; 658c2ecf20Sopenharmony_cimodule_param_named(dump_frames, qedf_dump_frames, int, S_IRUGO | S_IWUSR); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dump_frames, " Print the skb data of FIP and FCoE frames " 678c2ecf20Sopenharmony_ci "(default off)"); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic uint qedf_queue_depth; 708c2ecf20Sopenharmony_cimodule_param_named(queue_depth, qedf_queue_depth, int, S_IRUGO); 718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(queue_depth, " Sets the queue depth for all LUNs discovered " 728c2ecf20Sopenharmony_ci "by the qedf driver. Default is 0 (use OS default)."); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciuint qedf_io_tracing; 758c2ecf20Sopenharmony_cimodule_param_named(io_tracing, qedf_io_tracing, int, S_IRUGO | S_IWUSR); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io_tracing, " Enable logging of SCSI requests/completions " 778c2ecf20Sopenharmony_ci "into trace buffer. (default off)."); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic uint qedf_max_lun = MAX_FIBRE_LUNS; 808c2ecf20Sopenharmony_cimodule_param_named(max_lun, qedf_max_lun, int, S_IRUGO); 818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_lun, " Sets the maximum luns per target that the driver " 828c2ecf20Sopenharmony_ci "supports. (default 0xffffffff)"); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciuint qedf_link_down_tmo; 858c2ecf20Sopenharmony_cimodule_param_named(link_down_tmo, qedf_link_down_tmo, int, S_IRUGO); 868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(link_down_tmo, " Delays informing the fcoe transport that the " 878c2ecf20Sopenharmony_ci "link is down by N seconds."); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cibool qedf_retry_delay; 908c2ecf20Sopenharmony_cimodule_param_named(retry_delay, qedf_retry_delay, bool, S_IRUGO | S_IWUSR); 918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(retry_delay, " Enable/disable handling of FCP_RSP IU retry " 928c2ecf20Sopenharmony_ci "delay handling (default off)."); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic bool qedf_dcbx_no_wait; 958c2ecf20Sopenharmony_cimodule_param_named(dcbx_no_wait, qedf_dcbx_no_wait, bool, S_IRUGO | S_IWUSR); 968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dcbx_no_wait, " Do not wait for DCBX convergence to start " 978c2ecf20Sopenharmony_ci "sending FIP VLAN requests on link up (Default: off)."); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic uint qedf_dp_module; 1008c2ecf20Sopenharmony_cimodule_param_named(dp_module, qedf_dp_module, uint, S_IRUGO); 1018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dp_module, " bit flags control for verbose printk passed " 1028c2ecf20Sopenharmony_ci "qed module during probe."); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic uint qedf_dp_level = QED_LEVEL_NOTICE; 1058c2ecf20Sopenharmony_cimodule_param_named(dp_level, qedf_dp_level, uint, S_IRUGO); 1068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dp_level, " printk verbosity control passed to qed module " 1078c2ecf20Sopenharmony_ci "during probe (0-3: 0 more verbose)."); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic bool qedf_enable_recovery = true; 1108c2ecf20Sopenharmony_cimodule_param_named(enable_recovery, qedf_enable_recovery, 1118c2ecf20Sopenharmony_ci bool, S_IRUGO | S_IWUSR); 1128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_recovery, "Enable/disable recovery on driver/firmware " 1138c2ecf20Sopenharmony_ci "interface level errors 0 = Disabled, 1 = Enabled (Default: 1)."); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct workqueue_struct *qedf_io_wq; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct fcoe_percpu_s qedf_global; 1188c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(qedf_global_lock); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct kmem_cache *qedf_io_work_cache; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_civoid qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int vlan_id_tmp = 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci vlan_id_tmp = vlan_id | (qedf->prio << VLAN_PRIO_SHIFT); 1278c2ecf20Sopenharmony_ci qedf->vlan_id = vlan_id_tmp; 1288c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 1298c2ecf20Sopenharmony_ci "Setting vlan_id=0x%04x prio=%d.\n", 1308c2ecf20Sopenharmony_ci vlan_id_tmp, qedf->prio); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* Returns true if we have a valid vlan, false otherwise */ 1348c2ecf20Sopenharmony_cistatic bool qedf_initiate_fipvlan_req(struct qedf_ctx *qedf) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci while (qedf->fipvlan_retries--) { 1388c2ecf20Sopenharmony_ci /* This is to catch if link goes down during fipvlan retries */ 1398c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) { 1408c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Link not up.\n"); 1418c2ecf20Sopenharmony_ci return false; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (test_bit(QEDF_UNLOADING, &qedf->flags)) { 1458c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Driver unloading.\n"); 1468c2ecf20Sopenharmony_ci return false; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (qedf->vlan_id > 0) { 1508c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 1518c2ecf20Sopenharmony_ci "vlan = 0x%x already set, calling ctlr_link_up.\n", 1528c2ecf20Sopenharmony_ci qedf->vlan_id); 1538c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) 1548c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&qedf->ctlr); 1558c2ecf20Sopenharmony_ci return true; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 1598c2ecf20Sopenharmony_ci "Retry %d.\n", qedf->fipvlan_retries); 1608c2ecf20Sopenharmony_ci init_completion(&qedf->fipvlan_compl); 1618c2ecf20Sopenharmony_ci qedf_fcoe_send_vlan_req(qedf); 1628c2ecf20Sopenharmony_ci wait_for_completion_timeout(&qedf->fipvlan_compl, 1 * HZ); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return false; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void qedf_handle_link_update(struct work_struct *work) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = 1718c2ecf20Sopenharmony_ci container_of(work, struct qedf_ctx, link_update.work); 1728c2ecf20Sopenharmony_ci int rc; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Entered. link_state=%d.\n", 1758c2ecf20Sopenharmony_ci atomic_read(&qedf->link_state)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) { 1788c2ecf20Sopenharmony_ci rc = qedf_initiate_fipvlan_req(qedf); 1798c2ecf20Sopenharmony_ci if (rc) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) { 1838c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 1848c2ecf20Sopenharmony_ci "Link is down, resetting vlan_id.\n"); 1858c2ecf20Sopenharmony_ci qedf->vlan_id = 0; 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * If we get here then we never received a repsonse to our 1918c2ecf20Sopenharmony_ci * fip vlan request so set the vlan_id to the default and 1928c2ecf20Sopenharmony_ci * tell FCoE that the link is up 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Did not receive FIP VLAN " 1958c2ecf20Sopenharmony_ci "response, falling back to default VLAN %d.\n", 1968c2ecf20Sopenharmony_ci qedf_fallback_vlan); 1978c2ecf20Sopenharmony_ci qedf_set_vlan_id(qedf, qedf_fallback_vlan); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * Zero out data_src_addr so we'll update it with the new 2018c2ecf20Sopenharmony_ci * lport port_id 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci eth_zero_addr(qedf->data_src_addr); 2048c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&qedf->ctlr); 2058c2ecf20Sopenharmony_ci } else if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) { 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * If we hit here and link_down_tmo_valid is still 1 it means 2088c2ecf20Sopenharmony_ci * that link_down_tmo timed out so set it to 0 to make sure any 2098c2ecf20Sopenharmony_ci * other readers have accurate state. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci atomic_set(&qedf->link_down_tmo_valid, 0); 2128c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 2138c2ecf20Sopenharmony_ci "Calling fcoe_ctlr_link_down().\n"); 2148c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(&qedf->ctlr); 2158c2ecf20Sopenharmony_ci if (qedf_wait_for_upload(qedf) == false) 2168c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 2178c2ecf20Sopenharmony_ci "Could not upload all sessions.\n"); 2188c2ecf20Sopenharmony_ci /* Reset the number of FIP VLAN retries */ 2198c2ecf20Sopenharmony_ci qedf->fipvlan_retries = qedf_fipvlan_retries; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#define QEDF_FCOE_MAC_METHOD_GRANGED_MAC 1 2248c2ecf20Sopenharmony_ci#define QEDF_FCOE_MAC_METHOD_FCF_MAP 2 2258c2ecf20Sopenharmony_ci#define QEDF_FCOE_MAC_METHOD_FCOE_SET_MAC 3 2268c2ecf20Sopenharmony_cistatic void qedf_set_data_src_addr(struct qedf_ctx *qedf, struct fc_frame *fp) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci u8 *granted_mac; 2298c2ecf20Sopenharmony_ci struct fc_frame_header *fh = fc_frame_header_get(fp); 2308c2ecf20Sopenharmony_ci u8 fc_map[3]; 2318c2ecf20Sopenharmony_ci int method = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Get granted MAC address from FIP FLOGI payload */ 2348c2ecf20Sopenharmony_ci granted_mac = fr_cb(fp)->granted_mac; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * We set the source MAC for FCoE traffic based on the Granted MAC 2388c2ecf20Sopenharmony_ci * address from the switch. 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * If granted_mac is non-zero, we used that. 2418c2ecf20Sopenharmony_ci * If the granted_mac is zeroed out, created the FCoE MAC based on 2428c2ecf20Sopenharmony_ci * the sel_fcf->fc_map and the d_id fo the FLOGI frame. 2438c2ecf20Sopenharmony_ci * If sel_fcf->fc_map is 0 then we use the default FCF-MAC plus the 2448c2ecf20Sopenharmony_ci * d_id of the FLOGI frame. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(granted_mac)) { 2478c2ecf20Sopenharmony_ci ether_addr_copy(qedf->data_src_addr, granted_mac); 2488c2ecf20Sopenharmony_ci method = QEDF_FCOE_MAC_METHOD_GRANGED_MAC; 2498c2ecf20Sopenharmony_ci } else if (qedf->ctlr.sel_fcf->fc_map != 0) { 2508c2ecf20Sopenharmony_ci hton24(fc_map, qedf->ctlr.sel_fcf->fc_map); 2518c2ecf20Sopenharmony_ci qedf->data_src_addr[0] = fc_map[0]; 2528c2ecf20Sopenharmony_ci qedf->data_src_addr[1] = fc_map[1]; 2538c2ecf20Sopenharmony_ci qedf->data_src_addr[2] = fc_map[2]; 2548c2ecf20Sopenharmony_ci qedf->data_src_addr[3] = fh->fh_d_id[0]; 2558c2ecf20Sopenharmony_ci qedf->data_src_addr[4] = fh->fh_d_id[1]; 2568c2ecf20Sopenharmony_ci qedf->data_src_addr[5] = fh->fh_d_id[2]; 2578c2ecf20Sopenharmony_ci method = QEDF_FCOE_MAC_METHOD_FCF_MAP; 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci fc_fcoe_set_mac(qedf->data_src_addr, fh->fh_d_id); 2608c2ecf20Sopenharmony_ci method = QEDF_FCOE_MAC_METHOD_FCOE_SET_MAC; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 2648c2ecf20Sopenharmony_ci "QEDF data_src_mac=%pM method=%d.\n", qedf->data_src_addr, method); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void qedf_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, 2688c2ecf20Sopenharmony_ci void *arg) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct fc_exch *exch = fc_seq_exch(seq); 2718c2ecf20Sopenharmony_ci struct fc_lport *lport = exch->lp; 2728c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = lport_priv(lport); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (!qedf) { 2758c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "qedf is NULL.\n"); 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * If ERR_PTR is set then don't try to stat anything as it will cause 2818c2ecf20Sopenharmony_ci * a crash when we access fp. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci if (IS_ERR(fp)) { 2848c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 2858c2ecf20Sopenharmony_ci "fp has IS_ERR() set.\n"); 2868c2ecf20Sopenharmony_ci goto skip_stat; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Log stats for FLOGI reject */ 2908c2ecf20Sopenharmony_ci if (fc_frame_payload_op(fp) == ELS_LS_RJT) 2918c2ecf20Sopenharmony_ci qedf->flogi_failed++; 2928c2ecf20Sopenharmony_ci else if (fc_frame_payload_op(fp) == ELS_LS_ACC) { 2938c2ecf20Sopenharmony_ci /* Set the source MAC we will use for FCoE traffic */ 2948c2ecf20Sopenharmony_ci qedf_set_data_src_addr(qedf, fp); 2958c2ecf20Sopenharmony_ci qedf->flogi_pending = 0; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Complete flogi_compl so we can proceed to sending ADISCs */ 2998c2ecf20Sopenharmony_ci complete(&qedf->flogi_compl); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciskip_stat: 3028c2ecf20Sopenharmony_ci /* Report response to libfc */ 3038c2ecf20Sopenharmony_ci fc_lport_flogi_resp(seq, fp, lport); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did, 3078c2ecf20Sopenharmony_ci struct fc_frame *fp, unsigned int op, 3088c2ecf20Sopenharmony_ci void (*resp)(struct fc_seq *, 3098c2ecf20Sopenharmony_ci struct fc_frame *, 3108c2ecf20Sopenharmony_ci void *), 3118c2ecf20Sopenharmony_ci void *arg, u32 timeout) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = lport_priv(lport); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * Intercept FLOGI for statistic purposes. Note we use the resp 3178c2ecf20Sopenharmony_ci * callback to tell if this is really a flogi. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci if (resp == fc_lport_flogi_resp) { 3208c2ecf20Sopenharmony_ci qedf->flogi_cnt++; 3218c2ecf20Sopenharmony_ci if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) { 3228c2ecf20Sopenharmony_ci schedule_delayed_work(&qedf->stag_work, 2); 3238c2ecf20Sopenharmony_ci return NULL; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci qedf->flogi_pending++; 3268c2ecf20Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp, 3278c2ecf20Sopenharmony_ci arg, timeout); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ciint qedf_send_flogi(struct qedf_ctx *qedf) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct fc_lport *lport; 3368c2ecf20Sopenharmony_ci struct fc_frame *fp; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci lport = qedf->lport; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!lport->tt.elsct_send) { 3418c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "tt.elsct_send not set.\n"); 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); 3468c2ecf20Sopenharmony_ci if (!fp) { 3478c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "fc_frame_alloc failed.\n"); 3488c2ecf20Sopenharmony_ci return -ENOMEM; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 3528c2ecf20Sopenharmony_ci "Sending FLOGI to reestablish session with switch.\n"); 3538c2ecf20Sopenharmony_ci lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, 3548c2ecf20Sopenharmony_ci ELS_FLOGI, qedf_flogi_resp, lport, lport->r_a_tov); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci init_completion(&qedf->flogi_compl); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 3628c2ecf20Sopenharmony_ci * This function is called if link_down_tmo is in use. If we get a link up and 3638c2ecf20Sopenharmony_ci * link_down_tmo has not expired then use just FLOGI/ADISC to recover our 3648c2ecf20Sopenharmony_ci * sessions with targets. Otherwise, just call fcoe_ctlr_link_up(). 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic void qedf_link_recovery(struct work_struct *work) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = 3698c2ecf20Sopenharmony_ci container_of(work, struct qedf_ctx, link_recovery.work); 3708c2ecf20Sopenharmony_ci struct fc_lport *lport = qedf->lport; 3718c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata; 3728c2ecf20Sopenharmony_ci bool rc; 3738c2ecf20Sopenharmony_ci int retries = 30; 3748c2ecf20Sopenharmony_ci int rval, i; 3758c2ecf20Sopenharmony_ci struct list_head rdata_login_list; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdata_login_list); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 3808c2ecf20Sopenharmony_ci "Link down tmo did not expire.\n"); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * Essentially reset the fcoe_ctlr here without affecting the state 3848c2ecf20Sopenharmony_ci * of the libfc structs. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci qedf->ctlr.state = FIP_ST_LINK_WAIT; 3878c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(&qedf->ctlr); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Bring the link up before we send the fipvlan request so libfcoe 3918c2ecf20Sopenharmony_ci * can select a new fcf in parallel 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&qedf->ctlr); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Since the link when down and up to verify which vlan we're on */ 3968c2ecf20Sopenharmony_ci qedf->fipvlan_retries = qedf_fipvlan_retries; 3978c2ecf20Sopenharmony_ci rc = qedf_initiate_fipvlan_req(qedf); 3988c2ecf20Sopenharmony_ci /* If getting the VLAN fails, set the VLAN to the fallback one */ 3998c2ecf20Sopenharmony_ci if (!rc) 4008c2ecf20Sopenharmony_ci qedf_set_vlan_id(qedf, qedf_fallback_vlan); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * We need to wait for an FCF to be selected due to the 4048c2ecf20Sopenharmony_ci * fcoe_ctlr_link_up other the FLOGI will be rejected. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci while (retries > 0) { 4078c2ecf20Sopenharmony_ci if (qedf->ctlr.sel_fcf) { 4088c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 4098c2ecf20Sopenharmony_ci "FCF reselected, proceeding with FLOGI.\n"); 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci msleep(500); 4138c2ecf20Sopenharmony_ci retries--; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (retries < 1) { 4178c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Exhausted retries waiting for " 4188c2ecf20Sopenharmony_ci "FCF selection.\n"); 4198c2ecf20Sopenharmony_ci return; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci rval = qedf_send_flogi(qedf); 4238c2ecf20Sopenharmony_ci if (rval) 4248c2ecf20Sopenharmony_ci return; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Wait for FLOGI completion before proceeding with sending ADISCs */ 4278c2ecf20Sopenharmony_ci i = wait_for_completion_timeout(&qedf->flogi_compl, 4288c2ecf20Sopenharmony_ci qedf->lport->r_a_tov); 4298c2ecf20Sopenharmony_ci if (i == 0) { 4308c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "FLOGI timed out.\n"); 4318c2ecf20Sopenharmony_ci return; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * Call lport->tt.rport_login which will cause libfc to send an 4368c2ecf20Sopenharmony_ci * ADISC since the rport is in state ready. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci mutex_lock(&lport->disc.disc_mutex); 4398c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) { 4408c2ecf20Sopenharmony_ci if (kref_get_unless_zero(&rdata->kref)) { 4418c2ecf20Sopenharmony_ci fc_rport_login(rdata); 4428c2ecf20Sopenharmony_ci kref_put(&rdata->kref, fc_rport_destroy); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci mutex_unlock(&lport->disc.disc_mutex); 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void qedf_update_link_speed(struct qedf_ctx *qedf, 4498c2ecf20Sopenharmony_ci struct qed_link_output *link) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(sup_caps); 4528c2ecf20Sopenharmony_ci struct fc_lport *lport = qedf->lport; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_UNKNOWN; 4558c2ecf20Sopenharmony_ci lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Set fc_host link speed */ 4588c2ecf20Sopenharmony_ci switch (link->speed) { 4598c2ecf20Sopenharmony_ci case 10000: 4608c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_10GBIT; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci case 25000: 4638c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_25GBIT; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case 40000: 4668c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_40GBIT; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case 50000: 4698c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_50GBIT; 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci case 100000: 4728c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_100GBIT; 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci case 20000: 4758c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_20GBIT; 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci default: 4788c2ecf20Sopenharmony_ci lport->link_speed = FC_PORTSPEED_UNKNOWN; 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* 4838c2ecf20Sopenharmony_ci * Set supported link speed by querying the supported 4848c2ecf20Sopenharmony_ci * capabilities of the link. 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci phylink_zero(sup_caps); 4888c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseT_Full); 4898c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseKX4_Full); 4908c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseR_FEC); 4918c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseCR_Full); 4928c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseSR_Full); 4938c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseLR_Full); 4948c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseLRM_Full); 4958c2ecf20Sopenharmony_ci phylink_set(sup_caps, 10000baseKR_Full); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (linkmode_intersects(link->supported_caps, sup_caps)) 4988c2ecf20Sopenharmony_ci lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci phylink_zero(sup_caps); 5018c2ecf20Sopenharmony_ci phylink_set(sup_caps, 25000baseKR_Full); 5028c2ecf20Sopenharmony_ci phylink_set(sup_caps, 25000baseCR_Full); 5038c2ecf20Sopenharmony_ci phylink_set(sup_caps, 25000baseSR_Full); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (linkmode_intersects(link->supported_caps, sup_caps)) 5068c2ecf20Sopenharmony_ci lport->link_supported_speeds |= FC_PORTSPEED_25GBIT; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci phylink_zero(sup_caps); 5098c2ecf20Sopenharmony_ci phylink_set(sup_caps, 40000baseLR4_Full); 5108c2ecf20Sopenharmony_ci phylink_set(sup_caps, 40000baseKR4_Full); 5118c2ecf20Sopenharmony_ci phylink_set(sup_caps, 40000baseCR4_Full); 5128c2ecf20Sopenharmony_ci phylink_set(sup_caps, 40000baseSR4_Full); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (linkmode_intersects(link->supported_caps, sup_caps)) 5158c2ecf20Sopenharmony_ci lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci phylink_zero(sup_caps); 5188c2ecf20Sopenharmony_ci phylink_set(sup_caps, 50000baseKR2_Full); 5198c2ecf20Sopenharmony_ci phylink_set(sup_caps, 50000baseCR2_Full); 5208c2ecf20Sopenharmony_ci phylink_set(sup_caps, 50000baseSR2_Full); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (linkmode_intersects(link->supported_caps, sup_caps)) 5238c2ecf20Sopenharmony_ci lport->link_supported_speeds |= FC_PORTSPEED_50GBIT; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci phylink_zero(sup_caps); 5268c2ecf20Sopenharmony_ci phylink_set(sup_caps, 100000baseKR4_Full); 5278c2ecf20Sopenharmony_ci phylink_set(sup_caps, 100000baseSR4_Full); 5288c2ecf20Sopenharmony_ci phylink_set(sup_caps, 100000baseCR4_Full); 5298c2ecf20Sopenharmony_ci phylink_set(sup_caps, 100000baseLR4_ER4_Full); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (linkmode_intersects(link->supported_caps, sup_caps)) 5328c2ecf20Sopenharmony_ci lport->link_supported_speeds |= FC_PORTSPEED_100GBIT; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci phylink_zero(sup_caps); 5358c2ecf20Sopenharmony_ci phylink_set(sup_caps, 20000baseKR2_Full); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (linkmode_intersects(link->supported_caps, sup_caps)) 5388c2ecf20Sopenharmony_ci lport->link_supported_speeds |= FC_PORTSPEED_20GBIT; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (lport->host && lport->host->shost_data) 5418c2ecf20Sopenharmony_ci fc_host_supported_speeds(lport->host) = 5428c2ecf20Sopenharmony_ci lport->link_supported_speeds; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void qedf_bw_update(void *dev) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = (struct qedf_ctx *)dev; 5488c2ecf20Sopenharmony_ci struct qed_link_output link; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Get the latest status of the link */ 5518c2ecf20Sopenharmony_ci qed_ops->common->get_link(qedf->cdev, &link); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (test_bit(QEDF_UNLOADING, &qedf->flags)) { 5548c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 5558c2ecf20Sopenharmony_ci "Ignore link update, driver getting unload.\n"); 5568c2ecf20Sopenharmony_ci return; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (link.link_up) { 5608c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) 5618c2ecf20Sopenharmony_ci qedf_update_link_speed(qedf, &link); 5628c2ecf20Sopenharmony_ci else 5638c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 5648c2ecf20Sopenharmony_ci "Ignore bw update, link is down.\n"); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci } else { 5678c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "link_up is not set.\n"); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void qedf_link_update(void *dev, struct qed_link_output *link) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = (struct qedf_ctx *)dev; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* 5768c2ecf20Sopenharmony_ci * Prevent race where we're removing the module and we get link update 5778c2ecf20Sopenharmony_ci * for qed. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci if (test_bit(QEDF_UNLOADING, &qedf->flags)) { 5808c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 5818c2ecf20Sopenharmony_ci "Ignore link update, driver getting unload.\n"); 5828c2ecf20Sopenharmony_ci return; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (link->link_up) { 5868c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) { 5878c2ecf20Sopenharmony_ci QEDF_INFO((&qedf->dbg_ctx), QEDF_LOG_DISC, 5888c2ecf20Sopenharmony_ci "Ignoring link up event as link is already up.\n"); 5898c2ecf20Sopenharmony_ci return; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "LINK UP (%d GB/s).\n", 5928c2ecf20Sopenharmony_ci link->speed / 1000); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Cancel any pending link down work */ 5958c2ecf20Sopenharmony_ci cancel_delayed_work(&qedf->link_update); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci atomic_set(&qedf->link_state, QEDF_LINK_UP); 5988c2ecf20Sopenharmony_ci qedf_update_link_speed(qedf, link); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE || 6018c2ecf20Sopenharmony_ci qedf_dcbx_no_wait) { 6028c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 6038c2ecf20Sopenharmony_ci "DCBx done.\n"); 6048c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_down_tmo_valid) > 0) 6058c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, 6068c2ecf20Sopenharmony_ci &qedf->link_recovery, 0); 6078c2ecf20Sopenharmony_ci else 6088c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, 6098c2ecf20Sopenharmony_ci &qedf->link_update, 0); 6108c2ecf20Sopenharmony_ci atomic_set(&qedf->link_down_tmo_valid, 0); 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci } else { 6148c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "LINK DOWN.\n"); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci atomic_set(&qedf->link_state, QEDF_LINK_DOWN); 6178c2ecf20Sopenharmony_ci atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING); 6188c2ecf20Sopenharmony_ci /* 6198c2ecf20Sopenharmony_ci * Flag that we're waiting for the link to come back up before 6208c2ecf20Sopenharmony_ci * informing the fcoe layer of the event. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci if (qedf_link_down_tmo > 0) { 6238c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 6248c2ecf20Sopenharmony_ci "Starting link down tmo.\n"); 6258c2ecf20Sopenharmony_ci atomic_set(&qedf->link_down_tmo_valid, 1); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci qedf->vlan_id = 0; 6288c2ecf20Sopenharmony_ci qedf_update_link_speed(qedf, link); 6298c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, &qedf->link_update, 6308c2ecf20Sopenharmony_ci qedf_link_down_tmo * HZ); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = (struct qedf_ctx *)dev; 6388c2ecf20Sopenharmony_ci u8 tmp_prio; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "DCBx event valid=%d enabled=%d fcoe " 6418c2ecf20Sopenharmony_ci "prio=%d.\n", get->operational.valid, get->operational.enabled, 6428c2ecf20Sopenharmony_ci get->operational.app_prio.fcoe); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (get->operational.enabled && get->operational.valid) { 6458c2ecf20Sopenharmony_ci /* If DCBX was already negotiated on link up then just exit */ 6468c2ecf20Sopenharmony_ci if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE) { 6478c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 6488c2ecf20Sopenharmony_ci "DCBX already set on link up.\n"); 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci atomic_set(&qedf->dcbx, QEDF_DCBX_DONE); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Set the 8021q priority in the following manner: 6568c2ecf20Sopenharmony_ci * 6578c2ecf20Sopenharmony_ci * 1. If a modparam is set use that 6588c2ecf20Sopenharmony_ci * 2. If the value is not between 0..7 use the default 6598c2ecf20Sopenharmony_ci * 3. Use the priority we get from the DCBX app tag 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_ci tmp_prio = get->operational.app_prio.fcoe; 6628c2ecf20Sopenharmony_ci if (qedf_default_prio > -1) 6638c2ecf20Sopenharmony_ci qedf->prio = qedf_default_prio; 6648c2ecf20Sopenharmony_ci else if (tmp_prio > 7) { 6658c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 6668c2ecf20Sopenharmony_ci "FIP/FCoE prio %d out of range, setting to %d.\n", 6678c2ecf20Sopenharmony_ci tmp_prio, QEDF_DEFAULT_PRIO); 6688c2ecf20Sopenharmony_ci qedf->prio = QEDF_DEFAULT_PRIO; 6698c2ecf20Sopenharmony_ci } else 6708c2ecf20Sopenharmony_ci qedf->prio = tmp_prio; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_UP && 6738c2ecf20Sopenharmony_ci !qedf_dcbx_no_wait) { 6748c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_down_tmo_valid) > 0) 6758c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, 6768c2ecf20Sopenharmony_ci &qedf->link_recovery, 0); 6778c2ecf20Sopenharmony_ci else 6788c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, 6798c2ecf20Sopenharmony_ci &qedf->link_update, 0); 6808c2ecf20Sopenharmony_ci atomic_set(&qedf->link_down_tmo_valid, 0); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic u32 qedf_get_login_failures(void *cookie) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci qedf = (struct qedf_ctx *)cookie; 6918c2ecf20Sopenharmony_ci return qedf->flogi_failed; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic struct qed_fcoe_cb_ops qedf_cb_ops = { 6958c2ecf20Sopenharmony_ci { 6968c2ecf20Sopenharmony_ci .link_update = qedf_link_update, 6978c2ecf20Sopenharmony_ci .bw_update = qedf_bw_update, 6988c2ecf20Sopenharmony_ci .schedule_recovery_handler = qedf_schedule_recovery_handler, 6998c2ecf20Sopenharmony_ci .dcbx_aen = qedf_dcbx_handler, 7008c2ecf20Sopenharmony_ci .get_generic_tlv_data = qedf_get_generic_tlv_data, 7018c2ecf20Sopenharmony_ci .get_protocol_tlv_data = qedf_get_protocol_tlv_data, 7028c2ecf20Sopenharmony_ci .schedule_hw_err_handler = qedf_schedule_hw_err_handler, 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci}; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci/* 7078c2ecf20Sopenharmony_ci * Various transport templates. 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic struct scsi_transport_template *qedf_fc_transport_template; 7118c2ecf20Sopenharmony_cistatic struct scsi_transport_template *qedf_fc_vport_transport_template; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* 7148c2ecf20Sopenharmony_ci * SCSI EH handlers 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_cistatic int qedf_eh_abort(struct scsi_cmnd *sc_cmd) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); 7198c2ecf20Sopenharmony_ci struct fc_lport *lport; 7208c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 7218c2ecf20Sopenharmony_ci struct qedf_ioreq *io_req; 7228c2ecf20Sopenharmony_ci struct fc_rport_libfc_priv *rp = rport->dd_data; 7238c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata; 7248c2ecf20Sopenharmony_ci struct qedf_rport *fcport = NULL; 7258c2ecf20Sopenharmony_ci int rc = FAILED; 7268c2ecf20Sopenharmony_ci int wait_count = 100; 7278c2ecf20Sopenharmony_ci int refcount = 0; 7288c2ecf20Sopenharmony_ci int rval; 7298c2ecf20Sopenharmony_ci int got_ref = 0; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci lport = shost_priv(sc_cmd->device->host); 7328c2ecf20Sopenharmony_ci qedf = (struct qedf_ctx *)lport_priv(lport); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* rport and tgt are allocated together, so tgt should be non-NULL */ 7358c2ecf20Sopenharmony_ci fcport = (struct qedf_rport *)&rp[1]; 7368c2ecf20Sopenharmony_ci rdata = fcport->rdata; 7378c2ecf20Sopenharmony_ci if (!rdata || !kref_get_unless_zero(&rdata->kref)) { 7388c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "stale rport, sc_cmd=%p\n", sc_cmd); 7398c2ecf20Sopenharmony_ci rc = SUCCESS; 7408c2ecf20Sopenharmony_ci goto out; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci io_req = (struct qedf_ioreq *)sc_cmd->SCp.ptr; 7458c2ecf20Sopenharmony_ci if (!io_req) { 7468c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 7478c2ecf20Sopenharmony_ci "sc_cmd not queued with lld, sc_cmd=%p op=0x%02x, port_id=%06x\n", 7488c2ecf20Sopenharmony_ci sc_cmd, sc_cmd->cmnd[0], 7498c2ecf20Sopenharmony_ci rdata->ids.port_id); 7508c2ecf20Sopenharmony_ci rc = SUCCESS; 7518c2ecf20Sopenharmony_ci goto drop_rdata_kref; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci rval = kref_get_unless_zero(&io_req->refcount); /* ID: 005 */ 7558c2ecf20Sopenharmony_ci if (rval) 7568c2ecf20Sopenharmony_ci got_ref = 1; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* If we got a valid io_req, confirm it belongs to this sc_cmd. */ 7598c2ecf20Sopenharmony_ci if (!rval || io_req->sc_cmd != sc_cmd) { 7608c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 7618c2ecf20Sopenharmony_ci "Freed/Incorrect io_req, io_req->sc_cmd=%p, sc_cmd=%p, port_id=%06x, bailing out.\n", 7628c2ecf20Sopenharmony_ci io_req->sc_cmd, sc_cmd, rdata->ids.port_id); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci goto drop_rdata_kref; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (fc_remote_port_chkready(rport)) { 7688c2ecf20Sopenharmony_ci refcount = kref_read(&io_req->refcount); 7698c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 7708c2ecf20Sopenharmony_ci "rport not ready, io_req=%p, xid=0x%x sc_cmd=%p op=0x%02x, refcount=%d, port_id=%06x\n", 7718c2ecf20Sopenharmony_ci io_req, io_req->xid, sc_cmd, sc_cmd->cmnd[0], 7728c2ecf20Sopenharmony_ci refcount, rdata->ids.port_id); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci goto drop_rdata_kref; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci rc = fc_block_scsi_eh(sc_cmd); 7788c2ecf20Sopenharmony_ci if (rc) 7798c2ecf20Sopenharmony_ci goto drop_rdata_kref; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) { 7828c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 7838c2ecf20Sopenharmony_ci "Connection uploading, xid=0x%x., port_id=%06x\n", 7848c2ecf20Sopenharmony_ci io_req->xid, rdata->ids.port_id); 7858c2ecf20Sopenharmony_ci while (io_req->sc_cmd && (wait_count != 0)) { 7868c2ecf20Sopenharmony_ci msleep(100); 7878c2ecf20Sopenharmony_ci wait_count--; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci if (wait_count) { 7908c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "ABTS succeeded\n"); 7918c2ecf20Sopenharmony_ci rc = SUCCESS; 7928c2ecf20Sopenharmony_ci } else { 7938c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "ABTS failed\n"); 7948c2ecf20Sopenharmony_ci rc = FAILED; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci goto drop_rdata_kref; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (lport->state != LPORT_ST_READY || !(lport->link_up)) { 8008c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "link not ready.\n"); 8018c2ecf20Sopenharmony_ci goto drop_rdata_kref; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 8058c2ecf20Sopenharmony_ci "Aborting io_req=%p sc_cmd=%p xid=0x%x fp_idx=%d, port_id=%06x.\n", 8068c2ecf20Sopenharmony_ci io_req, sc_cmd, io_req->xid, io_req->fp_idx, 8078c2ecf20Sopenharmony_ci rdata->ids.port_id); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (qedf->stop_io_on_error) { 8108c2ecf20Sopenharmony_ci qedf_stop_all_io(qedf); 8118c2ecf20Sopenharmony_ci rc = SUCCESS; 8128c2ecf20Sopenharmony_ci goto drop_rdata_kref; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci init_completion(&io_req->abts_done); 8168c2ecf20Sopenharmony_ci rval = qedf_initiate_abts(io_req, true); 8178c2ecf20Sopenharmony_ci if (rval) { 8188c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n"); 8198c2ecf20Sopenharmony_ci /* 8208c2ecf20Sopenharmony_ci * If we fail to queue the ABTS then return this command to 8218c2ecf20Sopenharmony_ci * the SCSI layer as it will own and free the xid 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci rc = SUCCESS; 8248c2ecf20Sopenharmony_ci qedf_scsi_done(qedf, io_req, DID_ERROR); 8258c2ecf20Sopenharmony_ci goto drop_rdata_kref; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci wait_for_completion(&io_req->abts_done); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (io_req->event == QEDF_IOREQ_EV_ABORT_SUCCESS || 8318c2ecf20Sopenharmony_ci io_req->event == QEDF_IOREQ_EV_ABORT_FAILED || 8328c2ecf20Sopenharmony_ci io_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS) { 8338c2ecf20Sopenharmony_ci /* 8348c2ecf20Sopenharmony_ci * If we get a reponse to the abort this is success from 8358c2ecf20Sopenharmony_ci * the perspective that all references to the command have 8368c2ecf20Sopenharmony_ci * been removed from the driver and firmware 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ci rc = SUCCESS; 8398c2ecf20Sopenharmony_ci } else { 8408c2ecf20Sopenharmony_ci /* If the abort and cleanup failed then return a failure */ 8418c2ecf20Sopenharmony_ci rc = FAILED; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (rc == SUCCESS) 8458c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "ABTS succeeded, xid=0x%x.\n", 8468c2ecf20Sopenharmony_ci io_req->xid); 8478c2ecf20Sopenharmony_ci else 8488c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "ABTS failed, xid=0x%x.\n", 8498c2ecf20Sopenharmony_ci io_req->xid); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cidrop_rdata_kref: 8528c2ecf20Sopenharmony_ci kref_put(&rdata->kref, fc_rport_destroy); 8538c2ecf20Sopenharmony_ciout: 8548c2ecf20Sopenharmony_ci if (got_ref) 8558c2ecf20Sopenharmony_ci kref_put(&io_req->refcount, qedf_release_cmd); 8568c2ecf20Sopenharmony_ci return rc; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int qedf_eh_target_reset(struct scsi_cmnd *sc_cmd) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "%d:0:%d:%lld: TARGET RESET Issued...", 8628c2ecf20Sopenharmony_ci sc_cmd->device->host->host_no, sc_cmd->device->id, 8638c2ecf20Sopenharmony_ci sc_cmd->device->lun); 8648c2ecf20Sopenharmony_ci return qedf_initiate_tmf(sc_cmd, FCP_TMF_TGT_RESET); 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic int qedf_eh_device_reset(struct scsi_cmnd *sc_cmd) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "%d:0:%d:%lld: LUN RESET Issued... ", 8708c2ecf20Sopenharmony_ci sc_cmd->device->host->host_no, sc_cmd->device->id, 8718c2ecf20Sopenharmony_ci sc_cmd->device->lun); 8728c2ecf20Sopenharmony_ci return qedf_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cibool qedf_wait_for_upload(struct qedf_ctx *qedf) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct qedf_rport *fcport = NULL; 8788c2ecf20Sopenharmony_ci int wait_cnt = 120; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci while (wait_cnt--) { 8818c2ecf20Sopenharmony_ci if (atomic_read(&qedf->num_offloads)) 8828c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 8838c2ecf20Sopenharmony_ci "Waiting for all uploads to complete num_offloads = 0x%x.\n", 8848c2ecf20Sopenharmony_ci atomic_read(&qedf->num_offloads)); 8858c2ecf20Sopenharmony_ci else 8868c2ecf20Sopenharmony_ci return true; 8878c2ecf20Sopenharmony_ci msleep(500); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci rcu_read_lock(); 8918c2ecf20Sopenharmony_ci list_for_each_entry_rcu(fcport, &qedf->fcports, peers) { 8928c2ecf20Sopenharmony_ci if (fcport && test_bit(QEDF_RPORT_SESSION_READY, 8938c2ecf20Sopenharmony_ci &fcport->flags)) { 8948c2ecf20Sopenharmony_ci if (fcport->rdata) 8958c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 8968c2ecf20Sopenharmony_ci "Waiting for fcport %p portid=%06x.\n", 8978c2ecf20Sopenharmony_ci fcport, fcport->rdata->ids.port_id); 8988c2ecf20Sopenharmony_ci } else { 8998c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 9008c2ecf20Sopenharmony_ci "Waiting for fcport %p.\n", fcport); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci rcu_read_unlock(); 9048c2ecf20Sopenharmony_ci return false; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci/* Performs soft reset of qedf_ctx by simulating a link down/up */ 9098c2ecf20Sopenharmony_civoid qedf_ctx_soft_reset(struct fc_lport *lport) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 9128c2ecf20Sopenharmony_ci struct qed_link_output if_link; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (lport->vport) { 9158c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Cannot issue host reset on NPIV port.\n"); 9168c2ecf20Sopenharmony_ci return; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci qedf = lport_priv(lport); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci qedf->flogi_pending = 0; 9228c2ecf20Sopenharmony_ci /* For host reset, essentially do a soft link up/down */ 9238c2ecf20Sopenharmony_ci atomic_set(&qedf->link_state, QEDF_LINK_DOWN); 9248c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 9258c2ecf20Sopenharmony_ci "Queuing link down work.\n"); 9268c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, &qedf->link_update, 9278c2ecf20Sopenharmony_ci 0); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (qedf_wait_for_upload(qedf) == false) { 9308c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Could not upload all sessions.\n"); 9318c2ecf20Sopenharmony_ci WARN_ON(atomic_read(&qedf->num_offloads)); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Before setting link up query physical link state */ 9358c2ecf20Sopenharmony_ci qed_ops->common->get_link(qedf->cdev, &if_link); 9368c2ecf20Sopenharmony_ci /* Bail if the physical link is not up */ 9378c2ecf20Sopenharmony_ci if (!if_link.link_up) { 9388c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 9398c2ecf20Sopenharmony_ci "Physical link is not up.\n"); 9408c2ecf20Sopenharmony_ci return; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci /* Flush and wait to make sure link down is processed */ 9438c2ecf20Sopenharmony_ci flush_delayed_work(&qedf->link_update); 9448c2ecf20Sopenharmony_ci msleep(500); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci atomic_set(&qedf->link_state, QEDF_LINK_UP); 9478c2ecf20Sopenharmony_ci qedf->vlan_id = 0; 9488c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 9498c2ecf20Sopenharmony_ci "Queue link up work.\n"); 9508c2ecf20Sopenharmony_ci queue_delayed_work(qedf->link_update_wq, &qedf->link_update, 9518c2ecf20Sopenharmony_ci 0); 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci/* Reset the host by gracefully logging out and then logging back in */ 9558c2ecf20Sopenharmony_cistatic int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct fc_lport *lport; 9588c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci lport = shost_priv(sc_cmd->device->host); 9618c2ecf20Sopenharmony_ci qedf = lport_priv(lport); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN || 9648c2ecf20Sopenharmony_ci test_bit(QEDF_UNLOADING, &qedf->flags)) 9658c2ecf20Sopenharmony_ci return FAILED; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "HOST RESET Issued..."); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci qedf_ctx_soft_reset(lport); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci return SUCCESS; 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic int qedf_slave_configure(struct scsi_device *sdev) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci if (qedf_queue_depth) { 9778c2ecf20Sopenharmony_ci scsi_change_queue_depth(sdev, qedf_queue_depth); 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci return 0; 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic struct scsi_host_template qedf_host_template = { 9848c2ecf20Sopenharmony_ci .module = THIS_MODULE, 9858c2ecf20Sopenharmony_ci .name = QEDF_MODULE_NAME, 9868c2ecf20Sopenharmony_ci .this_id = -1, 9878c2ecf20Sopenharmony_ci .cmd_per_lun = 32, 9888c2ecf20Sopenharmony_ci .max_sectors = 0xffff, 9898c2ecf20Sopenharmony_ci .queuecommand = qedf_queuecommand, 9908c2ecf20Sopenharmony_ci .shost_attrs = qedf_host_attrs, 9918c2ecf20Sopenharmony_ci .eh_abort_handler = qedf_eh_abort, 9928c2ecf20Sopenharmony_ci .eh_device_reset_handler = qedf_eh_device_reset, /* lun reset */ 9938c2ecf20Sopenharmony_ci .eh_target_reset_handler = qedf_eh_target_reset, /* target reset */ 9948c2ecf20Sopenharmony_ci .eh_host_reset_handler = qedf_eh_host_reset, 9958c2ecf20Sopenharmony_ci .slave_configure = qedf_slave_configure, 9968c2ecf20Sopenharmony_ci .dma_boundary = QED_HW_DMA_BOUNDARY, 9978c2ecf20Sopenharmony_ci .sg_tablesize = QEDF_MAX_BDS_PER_CMD, 9988c2ecf20Sopenharmony_ci .can_queue = FCOE_PARAMS_NUM_TASKS, 9998c2ecf20Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 10008c2ecf20Sopenharmony_ci}; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic int qedf_get_paged_crc_eof(struct sk_buff *skb, int tlen) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci int rc; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci spin_lock(&qedf_global_lock); 10078c2ecf20Sopenharmony_ci rc = fcoe_get_paged_crc_eof(skb, tlen, &qedf_global); 10088c2ecf20Sopenharmony_ci spin_unlock(&qedf_global_lock); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci return rc; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic struct qedf_rport *qedf_fcport_lookup(struct qedf_ctx *qedf, u32 port_id) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 10168c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci rcu_read_lock(); 10198c2ecf20Sopenharmony_ci list_for_each_entry_rcu(fcport, &qedf->fcports, peers) { 10208c2ecf20Sopenharmony_ci rdata = fcport->rdata; 10218c2ecf20Sopenharmony_ci if (rdata == NULL) 10228c2ecf20Sopenharmony_ci continue; 10238c2ecf20Sopenharmony_ci if (rdata->ids.port_id == port_id) { 10248c2ecf20Sopenharmony_ci rcu_read_unlock(); 10258c2ecf20Sopenharmony_ci return fcport; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci rcu_read_unlock(); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Return NULL to caller to let them know fcport was not found */ 10318c2ecf20Sopenharmony_ci return NULL; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci/* Transmits an ELS frame over an offloaded session */ 10358c2ecf20Sopenharmony_cistatic int qedf_xmit_l2_frame(struct qedf_rport *fcport, struct fc_frame *fp) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 10388c2ecf20Sopenharmony_ci int rc = 0; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 10418c2ecf20Sopenharmony_ci if ((fh->fh_type == FC_TYPE_ELS) && 10428c2ecf20Sopenharmony_ci (fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { 10438c2ecf20Sopenharmony_ci switch (fc_frame_payload_op(fp)) { 10448c2ecf20Sopenharmony_ci case ELS_ADISC: 10458c2ecf20Sopenharmony_ci qedf_send_adisc(fcport, fp); 10468c2ecf20Sopenharmony_ci rc = 1; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci return rc; 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci/* 10558c2ecf20Sopenharmony_ci * qedf_xmit - qedf FCoE frame transmit function 10568c2ecf20Sopenharmony_ci */ 10578c2ecf20Sopenharmony_cistatic int qedf_xmit(struct fc_lport *lport, struct fc_frame *fp) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct fc_lport *base_lport; 10608c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 10618c2ecf20Sopenharmony_ci struct ethhdr *eh; 10628c2ecf20Sopenharmony_ci struct fcoe_crc_eof *cp; 10638c2ecf20Sopenharmony_ci struct sk_buff *skb; 10648c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 10658c2ecf20Sopenharmony_ci struct fcoe_hdr *hp; 10668c2ecf20Sopenharmony_ci u8 sof, eof; 10678c2ecf20Sopenharmony_ci u32 crc; 10688c2ecf20Sopenharmony_ci unsigned int hlen, tlen, elen; 10698c2ecf20Sopenharmony_ci int wlen; 10708c2ecf20Sopenharmony_ci struct fc_stats *stats; 10718c2ecf20Sopenharmony_ci struct fc_lport *tmp_lport; 10728c2ecf20Sopenharmony_ci struct fc_lport *vn_port = NULL; 10738c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 10748c2ecf20Sopenharmony_ci int rc; 10758c2ecf20Sopenharmony_ci u16 vlan_tci = 0; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci qedf = (struct qedf_ctx *)lport_priv(lport); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 10808c2ecf20Sopenharmony_ci skb = fp_skb(fp); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* Filter out traffic to other NPIV ports on the same host */ 10838c2ecf20Sopenharmony_ci if (lport->vport) 10848c2ecf20Sopenharmony_ci base_lport = shost_priv(vport_to_shost(lport->vport)); 10858c2ecf20Sopenharmony_ci else 10868c2ecf20Sopenharmony_ci base_lport = lport; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Flag if the destination is the base port */ 10898c2ecf20Sopenharmony_ci if (base_lport->port_id == ntoh24(fh->fh_d_id)) { 10908c2ecf20Sopenharmony_ci vn_port = base_lport; 10918c2ecf20Sopenharmony_ci } else { 10928c2ecf20Sopenharmony_ci /* Got through the list of vports attached to the base_lport 10938c2ecf20Sopenharmony_ci * and see if we have a match with the destination address. 10948c2ecf20Sopenharmony_ci */ 10958c2ecf20Sopenharmony_ci list_for_each_entry(tmp_lport, &base_lport->vports, list) { 10968c2ecf20Sopenharmony_ci if (tmp_lport->port_id == ntoh24(fh->fh_d_id)) { 10978c2ecf20Sopenharmony_ci vn_port = tmp_lport; 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci if (vn_port && ntoh24(fh->fh_d_id) != FC_FID_FLOGI) { 11038c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata = NULL; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, 11068c2ecf20Sopenharmony_ci "Dropping FCoE frame to %06x.\n", ntoh24(fh->fh_d_id)); 11078c2ecf20Sopenharmony_ci kfree_skb(skb); 11088c2ecf20Sopenharmony_ci rdata = fc_rport_lookup(lport, ntoh24(fh->fh_d_id)); 11098c2ecf20Sopenharmony_ci if (rdata) { 11108c2ecf20Sopenharmony_ci rdata->retries = lport->max_rport_retry_count; 11118c2ecf20Sopenharmony_ci kref_put(&rdata->kref, fc_rport_destroy); 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci return -EINVAL; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci /* End NPIV filtering */ 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (!qedf->ctlr.sel_fcf) { 11188c2ecf20Sopenharmony_ci kfree_skb(skb); 11198c2ecf20Sopenharmony_ci return 0; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) { 11238c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n"); 11248c2ecf20Sopenharmony_ci kfree_skb(skb); 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) { 11298c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "qedf link down\n"); 11308c2ecf20Sopenharmony_ci kfree_skb(skb); 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { 11358c2ecf20Sopenharmony_ci if (fcoe_ctlr_els_send(&qedf->ctlr, lport, skb)) 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* Check to see if this needs to be sent on an offloaded session */ 11408c2ecf20Sopenharmony_ci fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id)); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (fcport && test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 11438c2ecf20Sopenharmony_ci rc = qedf_xmit_l2_frame(fcport, fp); 11448c2ecf20Sopenharmony_ci /* 11458c2ecf20Sopenharmony_ci * If the frame was successfully sent over the middle path 11468c2ecf20Sopenharmony_ci * then do not try to also send it over the LL2 path 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_ci if (rc) 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci sof = fr_sof(fp); 11538c2ecf20Sopenharmony_ci eof = fr_eof(fp); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci elen = sizeof(struct ethhdr); 11568c2ecf20Sopenharmony_ci hlen = sizeof(struct fcoe_hdr); 11578c2ecf20Sopenharmony_ci tlen = sizeof(struct fcoe_crc_eof); 11588c2ecf20Sopenharmony_ci wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 11618c2ecf20Sopenharmony_ci crc = fcoe_fc_crc(fp); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* copy port crc and eof to the skb buff */ 11648c2ecf20Sopenharmony_ci if (skb_is_nonlinear(skb)) { 11658c2ecf20Sopenharmony_ci skb_frag_t *frag; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (qedf_get_paged_crc_eof(skb, tlen)) { 11688c2ecf20Sopenharmony_ci kfree_skb(skb); 11698c2ecf20Sopenharmony_ci return -ENOMEM; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; 11728c2ecf20Sopenharmony_ci cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag); 11738c2ecf20Sopenharmony_ci } else { 11748c2ecf20Sopenharmony_ci cp = skb_put(skb, tlen); 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci memset(cp, 0, sizeof(*cp)); 11788c2ecf20Sopenharmony_ci cp->fcoe_eof = eof; 11798c2ecf20Sopenharmony_ci cp->fcoe_crc32 = cpu_to_le32(~crc); 11808c2ecf20Sopenharmony_ci if (skb_is_nonlinear(skb)) { 11818c2ecf20Sopenharmony_ci kunmap_atomic(cp); 11828c2ecf20Sopenharmony_ci cp = NULL; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* adjust skb network/transport offsets to match mac/fcoe/port */ 11878c2ecf20Sopenharmony_ci skb_push(skb, elen + hlen); 11888c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 11898c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 11908c2ecf20Sopenharmony_ci skb->mac_len = elen; 11918c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_FCOE); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * Add VLAN tag to non-offload FCoE frame based on current stored VLAN 11958c2ecf20Sopenharmony_ci * for FIP/FCoE traffic. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), qedf->vlan_id); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* fill up mac and fcoe headers */ 12008c2ecf20Sopenharmony_ci eh = eth_hdr(skb); 12018c2ecf20Sopenharmony_ci eh->h_proto = htons(ETH_P_FCOE); 12028c2ecf20Sopenharmony_ci if (qedf->ctlr.map_dest) 12038c2ecf20Sopenharmony_ci fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); 12048c2ecf20Sopenharmony_ci else 12058c2ecf20Sopenharmony_ci /* insert GW address */ 12068c2ecf20Sopenharmony_ci ether_addr_copy(eh->h_dest, qedf->ctlr.dest_addr); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* Set the source MAC address */ 12098c2ecf20Sopenharmony_ci ether_addr_copy(eh->h_source, qedf->data_src_addr); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci hp = (struct fcoe_hdr *)(eh + 1); 12128c2ecf20Sopenharmony_ci memset(hp, 0, sizeof(*hp)); 12138c2ecf20Sopenharmony_ci if (FC_FCOE_VER) 12148c2ecf20Sopenharmony_ci FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); 12158c2ecf20Sopenharmony_ci hp->fcoe_sof = sof; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /*update tx stats */ 12188c2ecf20Sopenharmony_ci stats = per_cpu_ptr(lport->stats, get_cpu()); 12198c2ecf20Sopenharmony_ci stats->TxFrames++; 12208c2ecf20Sopenharmony_ci stats->TxWords += wlen; 12218c2ecf20Sopenharmony_ci put_cpu(); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Get VLAN ID from skb for printing purposes */ 12248c2ecf20Sopenharmony_ci __vlan_hwaccel_get_tag(skb, &vlan_tci); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* send down to lld */ 12278c2ecf20Sopenharmony_ci fr_dev(fp) = lport; 12288c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame send: " 12298c2ecf20Sopenharmony_ci "src=%06x dest=%06x r_ctl=%x type=%x vlan=%04x.\n", 12308c2ecf20Sopenharmony_ci ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl, fh->fh_type, 12318c2ecf20Sopenharmony_ci vlan_tci); 12328c2ecf20Sopenharmony_ci if (qedf_dump_frames) 12338c2ecf20Sopenharmony_ci print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16, 12348c2ecf20Sopenharmony_ci 1, skb->data, skb->len, false); 12358c2ecf20Sopenharmony_ci rc = qed_ops->ll2->start_xmit(qedf->cdev, skb, 0); 12368c2ecf20Sopenharmony_ci if (rc) { 12378c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "start_xmit failed rc = %d.\n", rc); 12388c2ecf20Sopenharmony_ci kfree_skb(skb); 12398c2ecf20Sopenharmony_ci return rc; 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic int qedf_alloc_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci int rval = 0; 12488c2ecf20Sopenharmony_ci u32 *pbl; 12498c2ecf20Sopenharmony_ci dma_addr_t page; 12508c2ecf20Sopenharmony_ci int num_pages; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* Calculate appropriate queue and PBL sizes */ 12538c2ecf20Sopenharmony_ci fcport->sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe); 12548c2ecf20Sopenharmony_ci fcport->sq_mem_size = ALIGN(fcport->sq_mem_size, QEDF_PAGE_SIZE); 12558c2ecf20Sopenharmony_ci fcport->sq_pbl_size = (fcport->sq_mem_size / QEDF_PAGE_SIZE) * 12568c2ecf20Sopenharmony_ci sizeof(void *); 12578c2ecf20Sopenharmony_ci fcport->sq_pbl_size = fcport->sq_pbl_size + QEDF_PAGE_SIZE; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci fcport->sq = dma_alloc_coherent(&qedf->pdev->dev, fcport->sq_mem_size, 12608c2ecf20Sopenharmony_ci &fcport->sq_dma, GFP_KERNEL); 12618c2ecf20Sopenharmony_ci if (!fcport->sq) { 12628c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send queue.\n"); 12638c2ecf20Sopenharmony_ci rval = 1; 12648c2ecf20Sopenharmony_ci goto out; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci fcport->sq_pbl = dma_alloc_coherent(&qedf->pdev->dev, 12688c2ecf20Sopenharmony_ci fcport->sq_pbl_size, 12698c2ecf20Sopenharmony_ci &fcport->sq_pbl_dma, GFP_KERNEL); 12708c2ecf20Sopenharmony_ci if (!fcport->sq_pbl) { 12718c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send queue PBL.\n"); 12728c2ecf20Sopenharmony_ci rval = 1; 12738c2ecf20Sopenharmony_ci goto out_free_sq; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* Create PBL */ 12778c2ecf20Sopenharmony_ci num_pages = fcport->sq_mem_size / QEDF_PAGE_SIZE; 12788c2ecf20Sopenharmony_ci page = fcport->sq_dma; 12798c2ecf20Sopenharmony_ci pbl = (u32 *)fcport->sq_pbl; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci while (num_pages--) { 12828c2ecf20Sopenharmony_ci *pbl = U64_LO(page); 12838c2ecf20Sopenharmony_ci pbl++; 12848c2ecf20Sopenharmony_ci *pbl = U64_HI(page); 12858c2ecf20Sopenharmony_ci pbl++; 12868c2ecf20Sopenharmony_ci page += QEDF_PAGE_SIZE; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci return rval; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ciout_free_sq: 12928c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size, fcport->sq, 12938c2ecf20Sopenharmony_ci fcport->sq_dma); 12948c2ecf20Sopenharmony_ciout: 12958c2ecf20Sopenharmony_ci return rval; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void qedf_free_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci if (fcport->sq_pbl) 13018c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, fcport->sq_pbl_size, 13028c2ecf20Sopenharmony_ci fcport->sq_pbl, fcport->sq_pbl_dma); 13038c2ecf20Sopenharmony_ci if (fcport->sq) 13048c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size, 13058c2ecf20Sopenharmony_ci fcport->sq, fcport->sq_dma); 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic int qedf_offload_connection(struct qedf_ctx *qedf, 13098c2ecf20Sopenharmony_ci struct qedf_rport *fcport) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct qed_fcoe_params_offload conn_info; 13128c2ecf20Sopenharmony_ci u32 port_id; 13138c2ecf20Sopenharmony_ci int rval; 13148c2ecf20Sopenharmony_ci uint16_t total_sqe = (fcport->sq_mem_size / sizeof(struct fcoe_wqe)); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offloading connection " 13178c2ecf20Sopenharmony_ci "portid=%06x.\n", fcport->rdata->ids.port_id); 13188c2ecf20Sopenharmony_ci rval = qed_ops->acquire_conn(qedf->cdev, &fcport->handle, 13198c2ecf20Sopenharmony_ci &fcport->fw_cid, &fcport->p_doorbell); 13208c2ecf20Sopenharmony_ci if (rval) { 13218c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not acquire connection " 13228c2ecf20Sopenharmony_ci "for portid=%06x.\n", fcport->rdata->ids.port_id); 13238c2ecf20Sopenharmony_ci rval = 1; /* For some reason qed returns 0 on failure here */ 13248c2ecf20Sopenharmony_ci goto out; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "portid=%06x " 13288c2ecf20Sopenharmony_ci "fw_cid=%08x handle=%d.\n", fcport->rdata->ids.port_id, 13298c2ecf20Sopenharmony_ci fcport->fw_cid, fcport->handle); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci memset(&conn_info, 0, sizeof(struct qed_fcoe_params_offload)); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* Fill in the offload connection info */ 13348c2ecf20Sopenharmony_ci conn_info.sq_pbl_addr = fcport->sq_pbl_dma; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci conn_info.sq_curr_page_addr = (dma_addr_t)(*(u64 *)fcport->sq_pbl); 13378c2ecf20Sopenharmony_ci conn_info.sq_next_page_addr = 13388c2ecf20Sopenharmony_ci (dma_addr_t)(*(u64 *)(fcport->sq_pbl + 8)); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* Need to use our FCoE MAC for the offload session */ 13418c2ecf20Sopenharmony_ci ether_addr_copy(conn_info.src_mac, qedf->data_src_addr); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci ether_addr_copy(conn_info.dst_mac, qedf->ctlr.dest_addr); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci conn_info.tx_max_fc_pay_len = fcport->rdata->maxframe_size; 13468c2ecf20Sopenharmony_ci conn_info.e_d_tov_timer_val = qedf->lport->e_d_tov; 13478c2ecf20Sopenharmony_ci conn_info.rec_tov_timer_val = 3; /* I think this is what E3 was */ 13488c2ecf20Sopenharmony_ci conn_info.rx_max_fc_pay_len = fcport->rdata->maxframe_size; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci /* Set VLAN data */ 13518c2ecf20Sopenharmony_ci conn_info.vlan_tag = qedf->vlan_id << 13528c2ecf20Sopenharmony_ci FCOE_CONN_OFFLOAD_RAMROD_DATA_VLAN_ID_SHIFT; 13538c2ecf20Sopenharmony_ci conn_info.vlan_tag |= 13548c2ecf20Sopenharmony_ci qedf->prio << FCOE_CONN_OFFLOAD_RAMROD_DATA_PRIORITY_SHIFT; 13558c2ecf20Sopenharmony_ci conn_info.flags |= (FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_MASK << 13568c2ecf20Sopenharmony_ci FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_SHIFT); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci /* Set host port source id */ 13598c2ecf20Sopenharmony_ci port_id = fc_host_port_id(qedf->lport->host); 13608c2ecf20Sopenharmony_ci fcport->sid = port_id; 13618c2ecf20Sopenharmony_ci conn_info.s_id.addr_hi = (port_id & 0x000000FF); 13628c2ecf20Sopenharmony_ci conn_info.s_id.addr_mid = (port_id & 0x0000FF00) >> 8; 13638c2ecf20Sopenharmony_ci conn_info.s_id.addr_lo = (port_id & 0x00FF0000) >> 16; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci conn_info.max_conc_seqs_c3 = fcport->rdata->max_seq; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* Set remote port destination id */ 13688c2ecf20Sopenharmony_ci port_id = fcport->rdata->rport->port_id; 13698c2ecf20Sopenharmony_ci conn_info.d_id.addr_hi = (port_id & 0x000000FF); 13708c2ecf20Sopenharmony_ci conn_info.d_id.addr_mid = (port_id & 0x0000FF00) >> 8; 13718c2ecf20Sopenharmony_ci conn_info.d_id.addr_lo = (port_id & 0x00FF0000) >> 16; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci conn_info.def_q_idx = 0; /* Default index for send queue? */ 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* Set FC-TAPE specific flags if needed */ 13768c2ecf20Sopenharmony_ci if (fcport->dev_type == QEDF_RPORT_TYPE_TAPE) { 13778c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, 13788c2ecf20Sopenharmony_ci "Enable CONF, REC for portid=%06x.\n", 13798c2ecf20Sopenharmony_ci fcport->rdata->ids.port_id); 13808c2ecf20Sopenharmony_ci conn_info.flags |= 1 << 13818c2ecf20Sopenharmony_ci FCOE_CONN_OFFLOAD_RAMROD_DATA_B_CONF_REQ_SHIFT; 13828c2ecf20Sopenharmony_ci conn_info.flags |= 13838c2ecf20Sopenharmony_ci ((fcport->rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) << 13848c2ecf20Sopenharmony_ci FCOE_CONN_OFFLOAD_RAMROD_DATA_B_REC_VALID_SHIFT; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci rval = qed_ops->offload_conn(qedf->cdev, fcport->handle, &conn_info); 13888c2ecf20Sopenharmony_ci if (rval) { 13898c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not offload connection " 13908c2ecf20Sopenharmony_ci "for portid=%06x.\n", fcport->rdata->ids.port_id); 13918c2ecf20Sopenharmony_ci goto out_free_conn; 13928c2ecf20Sopenharmony_ci } else 13938c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offload " 13948c2ecf20Sopenharmony_ci "succeeded portid=%06x total_sqe=%d.\n", 13958c2ecf20Sopenharmony_ci fcport->rdata->ids.port_id, total_sqe); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci spin_lock_init(&fcport->rport_lock); 13988c2ecf20Sopenharmony_ci atomic_set(&fcport->free_sqes, total_sqe); 13998c2ecf20Sopenharmony_ci return 0; 14008c2ecf20Sopenharmony_ciout_free_conn: 14018c2ecf20Sopenharmony_ci qed_ops->release_conn(qedf->cdev, fcport->handle); 14028c2ecf20Sopenharmony_ciout: 14038c2ecf20Sopenharmony_ci return rval; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci#define QEDF_TERM_BUFF_SIZE 10 14078c2ecf20Sopenharmony_cistatic void qedf_upload_connection(struct qedf_ctx *qedf, 14088c2ecf20Sopenharmony_ci struct qedf_rport *fcport) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci void *term_params; 14118c2ecf20Sopenharmony_ci dma_addr_t term_params_dma; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* Term params needs to be a DMA coherent buffer as qed shared the 14148c2ecf20Sopenharmony_ci * physical DMA address with the firmware. The buffer may be used in 14158c2ecf20Sopenharmony_ci * the receive path so we may eventually have to move this. 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_ci term_params = dma_alloc_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE, 14188c2ecf20Sopenharmony_ci &term_params_dma, GFP_KERNEL); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Uploading connection " 14218c2ecf20Sopenharmony_ci "port_id=%06x.\n", fcport->rdata->ids.port_id); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci qed_ops->destroy_conn(qedf->cdev, fcport->handle, term_params_dma); 14248c2ecf20Sopenharmony_ci qed_ops->release_conn(qedf->cdev, fcport->handle); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE, term_params, 14278c2ecf20Sopenharmony_ci term_params_dma); 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cistatic void qedf_cleanup_fcport(struct qedf_ctx *qedf, 14318c2ecf20Sopenharmony_ci struct qedf_rport *fcport) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata = fcport->rdata; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Cleaning up portid=%06x.\n", 14368c2ecf20Sopenharmony_ci fcport->rdata->ids.port_id); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci /* Flush any remaining i/o's before we upload the connection */ 14398c2ecf20Sopenharmony_ci qedf_flush_active_ios(fcport, -1); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (test_and_clear_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) 14428c2ecf20Sopenharmony_ci qedf_upload_connection(qedf, fcport); 14438c2ecf20Sopenharmony_ci qedf_free_sq(qedf, fcport); 14448c2ecf20Sopenharmony_ci fcport->rdata = NULL; 14458c2ecf20Sopenharmony_ci fcport->qedf = NULL; 14468c2ecf20Sopenharmony_ci kref_put(&rdata->kref, fc_rport_destroy); 14478c2ecf20Sopenharmony_ci} 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci/* 14508c2ecf20Sopenharmony_ci * This event_callback is called after successful completion of libfc 14518c2ecf20Sopenharmony_ci * initiated target login. qedf can proceed with initiating the session 14528c2ecf20Sopenharmony_ci * establishment. 14538c2ecf20Sopenharmony_ci */ 14548c2ecf20Sopenharmony_cistatic void qedf_rport_event_handler(struct fc_lport *lport, 14558c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata, 14568c2ecf20Sopenharmony_ci enum fc_rport_event event) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = lport_priv(lport); 14598c2ecf20Sopenharmony_ci struct fc_rport *rport = rdata->rport; 14608c2ecf20Sopenharmony_ci struct fc_rport_libfc_priv *rp; 14618c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 14628c2ecf20Sopenharmony_ci u32 port_id; 14638c2ecf20Sopenharmony_ci int rval; 14648c2ecf20Sopenharmony_ci unsigned long flags; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "event = %d, " 14678c2ecf20Sopenharmony_ci "port_id = 0x%x\n", event, rdata->ids.port_id); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci switch (event) { 14708c2ecf20Sopenharmony_ci case RPORT_EV_READY: 14718c2ecf20Sopenharmony_ci if (!rport) { 14728c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "rport is NULL.\n"); 14738c2ecf20Sopenharmony_ci break; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci rp = rport->dd_data; 14778c2ecf20Sopenharmony_ci fcport = (struct qedf_rport *)&rp[1]; 14788c2ecf20Sopenharmony_ci fcport->qedf = qedf; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (atomic_read(&qedf->num_offloads) >= QEDF_MAX_SESSIONS) { 14818c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Not offloading " 14828c2ecf20Sopenharmony_ci "portid=0x%x as max number of offloaded sessions " 14838c2ecf20Sopenharmony_ci "reached.\n", rdata->ids.port_id); 14848c2ecf20Sopenharmony_ci return; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* 14888c2ecf20Sopenharmony_ci * Don't try to offload the session again. Can happen when we 14898c2ecf20Sopenharmony_ci * get an ADISC 14908c2ecf20Sopenharmony_ci */ 14918c2ecf20Sopenharmony_ci if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 14928c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Session already " 14938c2ecf20Sopenharmony_ci "offloaded, portid=0x%x.\n", 14948c2ecf20Sopenharmony_ci rdata->ids.port_id); 14958c2ecf20Sopenharmony_ci return; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (rport->port_id == FC_FID_DIR_SERV) { 14998c2ecf20Sopenharmony_ci /* 15008c2ecf20Sopenharmony_ci * qedf_rport structure doesn't exist for 15018c2ecf20Sopenharmony_ci * directory server. 15028c2ecf20Sopenharmony_ci * We should not come here, as lport will 15038c2ecf20Sopenharmony_ci * take care of fabric login 15048c2ecf20Sopenharmony_ci */ 15058c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "rport struct does not " 15068c2ecf20Sopenharmony_ci "exist for dir server port_id=%x\n", 15078c2ecf20Sopenharmony_ci rdata->ids.port_id); 15088c2ecf20Sopenharmony_ci break; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (rdata->spp_type != FC_TYPE_FCP) { 15128c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 15138c2ecf20Sopenharmony_ci "Not offloading since spp type isn't FCP\n"); 15148c2ecf20Sopenharmony_ci break; 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) { 15178c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 15188c2ecf20Sopenharmony_ci "Not FCP target so not offloading\n"); 15198c2ecf20Sopenharmony_ci break; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci /* Initial reference held on entry, so this can't fail */ 15238c2ecf20Sopenharmony_ci kref_get(&rdata->kref); 15248c2ecf20Sopenharmony_ci fcport->rdata = rdata; 15258c2ecf20Sopenharmony_ci fcport->rport = rport; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci rval = qedf_alloc_sq(qedf, fcport); 15288c2ecf20Sopenharmony_ci if (rval) { 15298c2ecf20Sopenharmony_ci qedf_cleanup_fcport(qedf, fcport); 15308c2ecf20Sopenharmony_ci break; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci /* Set device type */ 15348c2ecf20Sopenharmony_ci if (rdata->flags & FC_RP_FLAGS_RETRY && 15358c2ecf20Sopenharmony_ci rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET && 15368c2ecf20Sopenharmony_ci !(rdata->ids.roles & FC_RPORT_ROLE_FCP_INITIATOR)) { 15378c2ecf20Sopenharmony_ci fcport->dev_type = QEDF_RPORT_TYPE_TAPE; 15388c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 15398c2ecf20Sopenharmony_ci "portid=%06x is a TAPE device.\n", 15408c2ecf20Sopenharmony_ci rdata->ids.port_id); 15418c2ecf20Sopenharmony_ci } else { 15428c2ecf20Sopenharmony_ci fcport->dev_type = QEDF_RPORT_TYPE_DISK; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci rval = qedf_offload_connection(qedf, fcport); 15468c2ecf20Sopenharmony_ci if (rval) { 15478c2ecf20Sopenharmony_ci qedf_cleanup_fcport(qedf, fcport); 15488c2ecf20Sopenharmony_ci break; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* Add fcport to list of qedf_ctx list of offloaded ports */ 15528c2ecf20Sopenharmony_ci spin_lock_irqsave(&qedf->hba_lock, flags); 15538c2ecf20Sopenharmony_ci list_add_rcu(&fcport->peers, &qedf->fcports); 15548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qedf->hba_lock, flags); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* 15578c2ecf20Sopenharmony_ci * Set the session ready bit to let everyone know that this 15588c2ecf20Sopenharmony_ci * connection is ready for I/O 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_ci set_bit(QEDF_RPORT_SESSION_READY, &fcport->flags); 15618c2ecf20Sopenharmony_ci atomic_inc(&qedf->num_offloads); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci break; 15648c2ecf20Sopenharmony_ci case RPORT_EV_LOGO: 15658c2ecf20Sopenharmony_ci case RPORT_EV_FAILED: 15668c2ecf20Sopenharmony_ci case RPORT_EV_STOP: 15678c2ecf20Sopenharmony_ci port_id = rdata->ids.port_id; 15688c2ecf20Sopenharmony_ci if (port_id == FC_FID_DIR_SERV) 15698c2ecf20Sopenharmony_ci break; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (rdata->spp_type != FC_TYPE_FCP) { 15728c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 15738c2ecf20Sopenharmony_ci "No action since spp type isn't FCP\n"); 15748c2ecf20Sopenharmony_ci break; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) { 15778c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 15788c2ecf20Sopenharmony_ci "Not FCP target so no action\n"); 15798c2ecf20Sopenharmony_ci break; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (!rport) { 15838c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 15848c2ecf20Sopenharmony_ci "port_id=%x - rport notcreated Yet!!\n", port_id); 15858c2ecf20Sopenharmony_ci break; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci rp = rport->dd_data; 15888c2ecf20Sopenharmony_ci /* 15898c2ecf20Sopenharmony_ci * Perform session upload. Note that rdata->peers is already 15908c2ecf20Sopenharmony_ci * removed from disc->rports list before we get this event. 15918c2ecf20Sopenharmony_ci */ 15928c2ecf20Sopenharmony_ci fcport = (struct qedf_rport *)&rp[1]; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci spin_lock_irqsave(&fcport->rport_lock, flags); 15958c2ecf20Sopenharmony_ci /* Only free this fcport if it is offloaded already */ 15968c2ecf20Sopenharmony_ci if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) && 15978c2ecf20Sopenharmony_ci !test_bit(QEDF_RPORT_UPLOADING_CONNECTION, 15988c2ecf20Sopenharmony_ci &fcport->flags)) { 15998c2ecf20Sopenharmony_ci set_bit(QEDF_RPORT_UPLOADING_CONNECTION, 16008c2ecf20Sopenharmony_ci &fcport->flags); 16018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 16028c2ecf20Sopenharmony_ci qedf_cleanup_fcport(qedf, fcport); 16038c2ecf20Sopenharmony_ci /* 16048c2ecf20Sopenharmony_ci * Remove fcport to list of qedf_ctx list of offloaded 16058c2ecf20Sopenharmony_ci * ports 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_ci spin_lock_irqsave(&qedf->hba_lock, flags); 16088c2ecf20Sopenharmony_ci list_del_rcu(&fcport->peers); 16098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qedf->hba_lock, flags); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci clear_bit(QEDF_RPORT_UPLOADING_CONNECTION, 16128c2ecf20Sopenharmony_ci &fcport->flags); 16138c2ecf20Sopenharmony_ci atomic_dec(&qedf->num_offloads); 16148c2ecf20Sopenharmony_ci } else { 16158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci break; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci case RPORT_EV_NONE: 16208c2ecf20Sopenharmony_ci break; 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic void qedf_abort_io(struct fc_lport *lport) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci /* NO-OP but need to fill in the template */ 16278c2ecf20Sopenharmony_ci} 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_cistatic void qedf_fcp_cleanup(struct fc_lport *lport) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci /* 16328c2ecf20Sopenharmony_ci * NO-OP but need to fill in template to prevent a NULL 16338c2ecf20Sopenharmony_ci * function pointer dereference during link down. I/Os 16348c2ecf20Sopenharmony_ci * will be flushed when port is uploaded. 16358c2ecf20Sopenharmony_ci */ 16368c2ecf20Sopenharmony_ci} 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_cistatic struct libfc_function_template qedf_lport_template = { 16398c2ecf20Sopenharmony_ci .frame_send = qedf_xmit, 16408c2ecf20Sopenharmony_ci .fcp_abort_io = qedf_abort_io, 16418c2ecf20Sopenharmony_ci .fcp_cleanup = qedf_fcp_cleanup, 16428c2ecf20Sopenharmony_ci .rport_event_callback = qedf_rport_event_handler, 16438c2ecf20Sopenharmony_ci .elsct_send = qedf_elsct_send, 16448c2ecf20Sopenharmony_ci}; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_cistatic void qedf_fcoe_ctlr_setup(struct qedf_ctx *qedf) 16478c2ecf20Sopenharmony_ci{ 16488c2ecf20Sopenharmony_ci fcoe_ctlr_init(&qedf->ctlr, FIP_MODE_AUTO); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci qedf->ctlr.send = qedf_fip_send; 16518c2ecf20Sopenharmony_ci qedf->ctlr.get_src_addr = qedf_get_src_mac; 16528c2ecf20Sopenharmony_ci ether_addr_copy(qedf->ctlr.ctl_src_addr, qedf->mac); 16538c2ecf20Sopenharmony_ci} 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_cistatic void qedf_setup_fdmi(struct qedf_ctx *qedf) 16568c2ecf20Sopenharmony_ci{ 16578c2ecf20Sopenharmony_ci struct fc_lport *lport = qedf->lport; 16588c2ecf20Sopenharmony_ci u8 buf[8]; 16598c2ecf20Sopenharmony_ci int pos; 16608c2ecf20Sopenharmony_ci uint32_t i; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci /* 16638c2ecf20Sopenharmony_ci * fdmi_enabled needs to be set for libfc 16648c2ecf20Sopenharmony_ci * to execute FDMI registration 16658c2ecf20Sopenharmony_ci */ 16668c2ecf20Sopenharmony_ci lport->fdmi_enabled = 1; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* 16698c2ecf20Sopenharmony_ci * Setup the necessary fc_host attributes to that will be used to fill 16708c2ecf20Sopenharmony_ci * in the FDMI information. 16718c2ecf20Sopenharmony_ci */ 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* Get the PCI-e Device Serial Number Capability */ 16748c2ecf20Sopenharmony_ci pos = pci_find_ext_capability(qedf->pdev, PCI_EXT_CAP_ID_DSN); 16758c2ecf20Sopenharmony_ci if (pos) { 16768c2ecf20Sopenharmony_ci pos += 4; 16778c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 16788c2ecf20Sopenharmony_ci pci_read_config_byte(qedf->pdev, pos + i, &buf[i]); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci snprintf(fc_host_serial_number(lport->host), 16818c2ecf20Sopenharmony_ci FC_SERIAL_NUMBER_SIZE, 16828c2ecf20Sopenharmony_ci "%02X%02X%02X%02X%02X%02X%02X%02X", 16838c2ecf20Sopenharmony_ci buf[7], buf[6], buf[5], buf[4], 16848c2ecf20Sopenharmony_ci buf[3], buf[2], buf[1], buf[0]); 16858c2ecf20Sopenharmony_ci } else 16868c2ecf20Sopenharmony_ci snprintf(fc_host_serial_number(lport->host), 16878c2ecf20Sopenharmony_ci FC_SERIAL_NUMBER_SIZE, "Unknown"); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci snprintf(fc_host_manufacturer(lport->host), 16908c2ecf20Sopenharmony_ci FC_SERIAL_NUMBER_SIZE, "%s", "Marvell Semiconductor Inc."); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci if (qedf->pdev->device == QL45xxx) { 16938c2ecf20Sopenharmony_ci snprintf(fc_host_model(lport->host), 16948c2ecf20Sopenharmony_ci FC_SYMBOLIC_NAME_SIZE, "%s", "QL45xxx"); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci snprintf(fc_host_model_description(lport->host), 16978c2ecf20Sopenharmony_ci FC_SYMBOLIC_NAME_SIZE, "%s", 16988c2ecf20Sopenharmony_ci "Marvell FastLinQ QL45xxx FCoE Adapter"); 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (qedf->pdev->device == QL41xxx) { 17028c2ecf20Sopenharmony_ci snprintf(fc_host_model(lport->host), 17038c2ecf20Sopenharmony_ci FC_SYMBOLIC_NAME_SIZE, "%s", "QL41xxx"); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci snprintf(fc_host_model_description(lport->host), 17068c2ecf20Sopenharmony_ci FC_SYMBOLIC_NAME_SIZE, "%s", 17078c2ecf20Sopenharmony_ci "Marvell FastLinQ QL41xxx FCoE Adapter"); 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci snprintf(fc_host_hardware_version(lport->host), 17118c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, "Rev %d", qedf->pdev->revision); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci snprintf(fc_host_driver_version(lport->host), 17148c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, "%s", QEDF_VERSION); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci snprintf(fc_host_firmware_version(lport->host), 17178c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, "%d.%d.%d.%d", 17188c2ecf20Sopenharmony_ci FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION, 17198c2ecf20Sopenharmony_ci FW_ENGINEERING_VERSION); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci} 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_cistatic int qedf_lport_setup(struct qedf_ctx *qedf) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci struct fc_lport *lport = qedf->lport; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci lport->link_up = 0; 17288c2ecf20Sopenharmony_ci lport->max_retry_count = QEDF_FLOGI_RETRY_CNT; 17298c2ecf20Sopenharmony_ci lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT; 17308c2ecf20Sopenharmony_ci lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | 17318c2ecf20Sopenharmony_ci FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); 17328c2ecf20Sopenharmony_ci lport->boot_time = jiffies; 17338c2ecf20Sopenharmony_ci lport->e_d_tov = 2 * 1000; 17348c2ecf20Sopenharmony_ci lport->r_a_tov = 10 * 1000; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* Set NPIV support */ 17378c2ecf20Sopenharmony_ci lport->does_npiv = 1; 17388c2ecf20Sopenharmony_ci fc_host_max_npiv_vports(lport->host) = QEDF_MAX_NPIV; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci fc_set_wwnn(lport, qedf->wwnn); 17418c2ecf20Sopenharmony_ci fc_set_wwpn(lport, qedf->wwpn); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci if (fcoe_libfc_config(lport, &qedf->ctlr, &qedf_lport_template, 0)) { 17448c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 17458c2ecf20Sopenharmony_ci "fcoe_libfc_config failed.\n"); 17468c2ecf20Sopenharmony_ci return -ENOMEM; 17478c2ecf20Sopenharmony_ci } 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci /* Allocate the exchange manager */ 17508c2ecf20Sopenharmony_ci fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_PARAMS_NUM_TASKS, 17518c2ecf20Sopenharmony_ci 0xfffe, NULL); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci if (fc_lport_init_stats(lport)) 17548c2ecf20Sopenharmony_ci return -ENOMEM; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* Finish lport config */ 17578c2ecf20Sopenharmony_ci fc_lport_config(lport); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci /* Set max frame size */ 17608c2ecf20Sopenharmony_ci fc_set_mfs(lport, QEDF_MFS); 17618c2ecf20Sopenharmony_ci fc_host_maxframe_size(lport->host) = lport->mfs; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci /* Set default dev_loss_tmo based on module parameter */ 17648c2ecf20Sopenharmony_ci fc_host_dev_loss_tmo(lport->host) = qedf_dev_loss_tmo; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci /* Set symbolic node name */ 17678c2ecf20Sopenharmony_ci if (qedf->pdev->device == QL45xxx) 17688c2ecf20Sopenharmony_ci snprintf(fc_host_symbolic_name(lport->host), 256, 17698c2ecf20Sopenharmony_ci "Marvell FastLinQ 45xxx FCoE v%s", QEDF_VERSION); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (qedf->pdev->device == QL41xxx) 17728c2ecf20Sopenharmony_ci snprintf(fc_host_symbolic_name(lport->host), 256, 17738c2ecf20Sopenharmony_ci "Marvell FastLinQ 41xxx FCoE v%s", QEDF_VERSION); 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci qedf_setup_fdmi(qedf); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci return 0; 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci/* 17818c2ecf20Sopenharmony_ci * NPIV functions 17828c2ecf20Sopenharmony_ci */ 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_cistatic int qedf_vport_libfc_config(struct fc_vport *vport, 17858c2ecf20Sopenharmony_ci struct fc_lport *lport) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci lport->link_up = 0; 17888c2ecf20Sopenharmony_ci lport->qfull = 0; 17898c2ecf20Sopenharmony_ci lport->max_retry_count = QEDF_FLOGI_RETRY_CNT; 17908c2ecf20Sopenharmony_ci lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT; 17918c2ecf20Sopenharmony_ci lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | 17928c2ecf20Sopenharmony_ci FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); 17938c2ecf20Sopenharmony_ci lport->boot_time = jiffies; 17948c2ecf20Sopenharmony_ci lport->e_d_tov = 2 * 1000; 17958c2ecf20Sopenharmony_ci lport->r_a_tov = 10 * 1000; 17968c2ecf20Sopenharmony_ci lport->does_npiv = 1; /* Temporary until we add NPIV support */ 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci /* Allocate stats for vport */ 17998c2ecf20Sopenharmony_ci if (fc_lport_init_stats(lport)) 18008c2ecf20Sopenharmony_ci return -ENOMEM; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci /* Finish lport config */ 18038c2ecf20Sopenharmony_ci fc_lport_config(lport); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* offload related configuration */ 18068c2ecf20Sopenharmony_ci lport->crc_offload = 0; 18078c2ecf20Sopenharmony_ci lport->seq_offload = 0; 18088c2ecf20Sopenharmony_ci lport->lro_enabled = 0; 18098c2ecf20Sopenharmony_ci lport->lro_xid = 0; 18108c2ecf20Sopenharmony_ci lport->lso_max = 0; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci return 0; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_cistatic int qedf_vport_create(struct fc_vport *vport, bool disabled) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci struct Scsi_Host *shost = vport_to_shost(vport); 18188c2ecf20Sopenharmony_ci struct fc_lport *n_port = shost_priv(shost); 18198c2ecf20Sopenharmony_ci struct fc_lport *vn_port; 18208c2ecf20Sopenharmony_ci struct qedf_ctx *base_qedf = lport_priv(n_port); 18218c2ecf20Sopenharmony_ci struct qedf_ctx *vport_qedf; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci char buf[32]; 18248c2ecf20Sopenharmony_ci int rc = 0; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci rc = fcoe_validate_vport_create(vport); 18278c2ecf20Sopenharmony_ci if (rc) { 18288c2ecf20Sopenharmony_ci fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); 18298c2ecf20Sopenharmony_ci QEDF_WARN(&(base_qedf->dbg_ctx), "Failed to create vport, " 18308c2ecf20Sopenharmony_ci "WWPN (0x%s) already exists.\n", buf); 18318c2ecf20Sopenharmony_ci return rc; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci if (atomic_read(&base_qedf->link_state) != QEDF_LINK_UP) { 18358c2ecf20Sopenharmony_ci QEDF_WARN(&(base_qedf->dbg_ctx), "Cannot create vport " 18368c2ecf20Sopenharmony_ci "because link is not up.\n"); 18378c2ecf20Sopenharmony_ci return -EIO; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci vn_port = libfc_vport_create(vport, sizeof(struct qedf_ctx)); 18418c2ecf20Sopenharmony_ci if (!vn_port) { 18428c2ecf20Sopenharmony_ci QEDF_WARN(&(base_qedf->dbg_ctx), "Could not create lport " 18438c2ecf20Sopenharmony_ci "for vport.\n"); 18448c2ecf20Sopenharmony_ci return -ENOMEM; 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); 18488c2ecf20Sopenharmony_ci QEDF_ERR(&(base_qedf->dbg_ctx), "Creating NPIV port, WWPN=%s.\n", 18498c2ecf20Sopenharmony_ci buf); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* Copy some fields from base_qedf */ 18528c2ecf20Sopenharmony_ci vport_qedf = lport_priv(vn_port); 18538c2ecf20Sopenharmony_ci memcpy(vport_qedf, base_qedf, sizeof(struct qedf_ctx)); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* Set qedf data specific to this vport */ 18568c2ecf20Sopenharmony_ci vport_qedf->lport = vn_port; 18578c2ecf20Sopenharmony_ci /* Use same hba_lock as base_qedf */ 18588c2ecf20Sopenharmony_ci vport_qedf->hba_lock = base_qedf->hba_lock; 18598c2ecf20Sopenharmony_ci vport_qedf->pdev = base_qedf->pdev; 18608c2ecf20Sopenharmony_ci vport_qedf->cmd_mgr = base_qedf->cmd_mgr; 18618c2ecf20Sopenharmony_ci init_completion(&vport_qedf->flogi_compl); 18628c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vport_qedf->fcports); 18638c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&vport_qedf->stag_work, qedf_stag_change_work); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci rc = qedf_vport_libfc_config(vport, vn_port); 18668c2ecf20Sopenharmony_ci if (rc) { 18678c2ecf20Sopenharmony_ci QEDF_ERR(&(base_qedf->dbg_ctx), "Could not allocate memory " 18688c2ecf20Sopenharmony_ci "for lport stats.\n"); 18698c2ecf20Sopenharmony_ci goto err; 18708c2ecf20Sopenharmony_ci } 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci fc_set_wwnn(vn_port, vport->node_name); 18738c2ecf20Sopenharmony_ci fc_set_wwpn(vn_port, vport->port_name); 18748c2ecf20Sopenharmony_ci vport_qedf->wwnn = vn_port->wwnn; 18758c2ecf20Sopenharmony_ci vport_qedf->wwpn = vn_port->wwpn; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci vn_port->host->transportt = qedf_fc_vport_transport_template; 18788c2ecf20Sopenharmony_ci vn_port->host->can_queue = FCOE_PARAMS_NUM_TASKS; 18798c2ecf20Sopenharmony_ci vn_port->host->max_lun = qedf_max_lun; 18808c2ecf20Sopenharmony_ci vn_port->host->sg_tablesize = QEDF_MAX_BDS_PER_CMD; 18818c2ecf20Sopenharmony_ci vn_port->host->max_cmd_len = QEDF_MAX_CDB_LEN; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci rc = scsi_add_host(vn_port->host, &vport->dev); 18848c2ecf20Sopenharmony_ci if (rc) { 18858c2ecf20Sopenharmony_ci QEDF_WARN(&base_qedf->dbg_ctx, 18868c2ecf20Sopenharmony_ci "Error adding Scsi_Host rc=0x%x.\n", rc); 18878c2ecf20Sopenharmony_ci goto err; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci /* Set default dev_loss_tmo based on module parameter */ 18918c2ecf20Sopenharmony_ci fc_host_dev_loss_tmo(vn_port->host) = qedf_dev_loss_tmo; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* Init libfc stuffs */ 18948c2ecf20Sopenharmony_ci memcpy(&vn_port->tt, &qedf_lport_template, 18958c2ecf20Sopenharmony_ci sizeof(qedf_lport_template)); 18968c2ecf20Sopenharmony_ci fc_exch_init(vn_port); 18978c2ecf20Sopenharmony_ci fc_elsct_init(vn_port); 18988c2ecf20Sopenharmony_ci fc_lport_init(vn_port); 18998c2ecf20Sopenharmony_ci fc_disc_init(vn_port); 19008c2ecf20Sopenharmony_ci fc_disc_config(vn_port, vn_port); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci /* Allocate the exchange manager */ 19048c2ecf20Sopenharmony_ci shost = vport_to_shost(vport); 19058c2ecf20Sopenharmony_ci n_port = shost_priv(shost); 19068c2ecf20Sopenharmony_ci fc_exch_mgr_list_clone(n_port, vn_port); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci /* Set max frame size */ 19098c2ecf20Sopenharmony_ci fc_set_mfs(vn_port, QEDF_MFS); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci fc_host_port_type(vn_port->host) = FC_PORTTYPE_UNKNOWN; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (disabled) { 19148c2ecf20Sopenharmony_ci fc_vport_set_state(vport, FC_VPORT_DISABLED); 19158c2ecf20Sopenharmony_ci } else { 19168c2ecf20Sopenharmony_ci vn_port->boot_time = jiffies; 19178c2ecf20Sopenharmony_ci fc_fabric_login(vn_port); 19188c2ecf20Sopenharmony_ci fc_vport_setlink(vn_port); 19198c2ecf20Sopenharmony_ci } 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci /* Set symbolic node name */ 19228c2ecf20Sopenharmony_ci if (base_qedf->pdev->device == QL45xxx) 19238c2ecf20Sopenharmony_ci snprintf(fc_host_symbolic_name(vn_port->host), 256, 19248c2ecf20Sopenharmony_ci "Marvell FastLinQ 45xxx FCoE v%s", QEDF_VERSION); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci if (base_qedf->pdev->device == QL41xxx) 19278c2ecf20Sopenharmony_ci snprintf(fc_host_symbolic_name(vn_port->host), 256, 19288c2ecf20Sopenharmony_ci "Marvell FastLinQ 41xxx FCoE v%s", QEDF_VERSION); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* Set supported speed */ 19318c2ecf20Sopenharmony_ci fc_host_supported_speeds(vn_port->host) = n_port->link_supported_speeds; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* Set speed */ 19348c2ecf20Sopenharmony_ci vn_port->link_speed = n_port->link_speed; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci /* Set port type */ 19378c2ecf20Sopenharmony_ci fc_host_port_type(vn_port->host) = FC_PORTTYPE_NPIV; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci /* Set maxframe size */ 19408c2ecf20Sopenharmony_ci fc_host_maxframe_size(vn_port->host) = n_port->mfs; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_NPIV, "vn_port=%p.\n", 19438c2ecf20Sopenharmony_ci vn_port); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci /* Set up debug context for vport */ 19468c2ecf20Sopenharmony_ci vport_qedf->dbg_ctx.host_no = vn_port->host->host_no; 19478c2ecf20Sopenharmony_ci vport_qedf->dbg_ctx.pdev = base_qedf->pdev; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci return 0; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cierr: 19528c2ecf20Sopenharmony_ci scsi_host_put(vn_port->host); 19538c2ecf20Sopenharmony_ci return rc; 19548c2ecf20Sopenharmony_ci} 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_cistatic int qedf_vport_destroy(struct fc_vport *vport) 19578c2ecf20Sopenharmony_ci{ 19588c2ecf20Sopenharmony_ci struct Scsi_Host *shost = vport_to_shost(vport); 19598c2ecf20Sopenharmony_ci struct fc_lport *n_port = shost_priv(shost); 19608c2ecf20Sopenharmony_ci struct fc_lport *vn_port = vport->dd_data; 19618c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = lport_priv(vn_port); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (!qedf) { 19648c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "qedf is NULL.\n"); 19658c2ecf20Sopenharmony_ci goto out; 19668c2ecf20Sopenharmony_ci } 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci /* Set unloading bit on vport qedf_ctx to prevent more I/O */ 19698c2ecf20Sopenharmony_ci set_bit(QEDF_UNLOADING, &qedf->flags); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci mutex_lock(&n_port->lp_mutex); 19728c2ecf20Sopenharmony_ci list_del(&vn_port->list); 19738c2ecf20Sopenharmony_ci mutex_unlock(&n_port->lp_mutex); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci fc_fabric_logoff(vn_port); 19768c2ecf20Sopenharmony_ci fc_lport_destroy(vn_port); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* Detach from scsi-ml */ 19798c2ecf20Sopenharmony_ci fc_remove_host(vn_port->host); 19808c2ecf20Sopenharmony_ci scsi_remove_host(vn_port->host); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* 19838c2ecf20Sopenharmony_ci * Only try to release the exchange manager if the vn_port 19848c2ecf20Sopenharmony_ci * configuration is complete. 19858c2ecf20Sopenharmony_ci */ 19868c2ecf20Sopenharmony_ci if (vn_port->state == LPORT_ST_READY) 19878c2ecf20Sopenharmony_ci fc_exch_mgr_free(vn_port); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci /* Free memory used by statistical counters */ 19908c2ecf20Sopenharmony_ci fc_lport_free_stats(vn_port); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci /* Release Scsi_Host */ 19938c2ecf20Sopenharmony_ci scsi_host_put(vn_port->host); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ciout: 19968c2ecf20Sopenharmony_ci return 0; 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_cistatic int qedf_vport_disable(struct fc_vport *vport, bool disable) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci struct fc_lport *lport = vport->dd_data; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci if (disable) { 20048c2ecf20Sopenharmony_ci fc_vport_set_state(vport, FC_VPORT_DISABLED); 20058c2ecf20Sopenharmony_ci fc_fabric_logoff(lport); 20068c2ecf20Sopenharmony_ci } else { 20078c2ecf20Sopenharmony_ci lport->boot_time = jiffies; 20088c2ecf20Sopenharmony_ci fc_fabric_login(lport); 20098c2ecf20Sopenharmony_ci fc_vport_setlink(lport); 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci return 0; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci/* 20158c2ecf20Sopenharmony_ci * During removal we need to wait for all the vports associated with a port 20168c2ecf20Sopenharmony_ci * to be destroyed so we avoid a race condition where libfc is still trying 20178c2ecf20Sopenharmony_ci * to reap vports while the driver remove function has already reaped the 20188c2ecf20Sopenharmony_ci * driver contexts associated with the physical port. 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_cistatic void qedf_wait_for_vport_destroy(struct qedf_ctx *qedf) 20218c2ecf20Sopenharmony_ci{ 20228c2ecf20Sopenharmony_ci struct fc_host_attrs *fc_host = shost_to_fc_host(qedf->lport->host); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV, 20258c2ecf20Sopenharmony_ci "Entered.\n"); 20268c2ecf20Sopenharmony_ci while (fc_host->npiv_vports_inuse > 0) { 20278c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV, 20288c2ecf20Sopenharmony_ci "Waiting for all vports to be reaped.\n"); 20298c2ecf20Sopenharmony_ci msleep(1000); 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci} 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci/** 20348c2ecf20Sopenharmony_ci * qedf_fcoe_reset - Resets the fcoe 20358c2ecf20Sopenharmony_ci * 20368c2ecf20Sopenharmony_ci * @shost: shost the reset is from 20378c2ecf20Sopenharmony_ci * 20388c2ecf20Sopenharmony_ci * Returns: always 0 20398c2ecf20Sopenharmony_ci */ 20408c2ecf20Sopenharmony_cistatic int qedf_fcoe_reset(struct Scsi_Host *shost) 20418c2ecf20Sopenharmony_ci{ 20428c2ecf20Sopenharmony_ci struct fc_lport *lport = shost_priv(shost); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci qedf_ctx_soft_reset(lport); 20458c2ecf20Sopenharmony_ci return 0; 20468c2ecf20Sopenharmony_ci} 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_cistatic void qedf_get_host_port_id(struct Scsi_Host *shost) 20498c2ecf20Sopenharmony_ci{ 20508c2ecf20Sopenharmony_ci struct fc_lport *lport = shost_priv(shost); 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci fc_host_port_id(shost) = lport->port_id; 20538c2ecf20Sopenharmony_ci} 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic struct fc_host_statistics *qedf_fc_get_host_stats(struct Scsi_Host 20568c2ecf20Sopenharmony_ci *shost) 20578c2ecf20Sopenharmony_ci{ 20588c2ecf20Sopenharmony_ci struct fc_host_statistics *qedf_stats; 20598c2ecf20Sopenharmony_ci struct fc_lport *lport = shost_priv(shost); 20608c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = lport_priv(lport); 20618c2ecf20Sopenharmony_ci struct qed_fcoe_stats *fw_fcoe_stats; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci qedf_stats = fc_get_host_stats(shost); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci /* We don't collect offload stats for specific NPIV ports */ 20668c2ecf20Sopenharmony_ci if (lport->vport) 20678c2ecf20Sopenharmony_ci goto out; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci fw_fcoe_stats = kmalloc(sizeof(struct qed_fcoe_stats), GFP_KERNEL); 20708c2ecf20Sopenharmony_ci if (!fw_fcoe_stats) { 20718c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for " 20728c2ecf20Sopenharmony_ci "fw_fcoe_stats.\n"); 20738c2ecf20Sopenharmony_ci goto out; 20748c2ecf20Sopenharmony_ci } 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci mutex_lock(&qedf->stats_mutex); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci /* Query firmware for offload stats */ 20798c2ecf20Sopenharmony_ci qed_ops->get_stats(qedf->cdev, fw_fcoe_stats); 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci /* 20828c2ecf20Sopenharmony_ci * The expectation is that we add our offload stats to the stats 20838c2ecf20Sopenharmony_ci * being maintained by libfc each time the fc_get_host_status callback 20848c2ecf20Sopenharmony_ci * is invoked. The additions are not carried over for each call to 20858c2ecf20Sopenharmony_ci * the fc_get_host_stats callback. 20868c2ecf20Sopenharmony_ci */ 20878c2ecf20Sopenharmony_ci qedf_stats->tx_frames += fw_fcoe_stats->fcoe_tx_data_pkt_cnt + 20888c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt + 20898c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_tx_other_pkt_cnt; 20908c2ecf20Sopenharmony_ci qedf_stats->rx_frames += fw_fcoe_stats->fcoe_rx_data_pkt_cnt + 20918c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt + 20928c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_rx_other_pkt_cnt; 20938c2ecf20Sopenharmony_ci qedf_stats->fcp_input_megabytes += 20948c2ecf20Sopenharmony_ci do_div(fw_fcoe_stats->fcoe_rx_byte_cnt, 1000000); 20958c2ecf20Sopenharmony_ci qedf_stats->fcp_output_megabytes += 20968c2ecf20Sopenharmony_ci do_div(fw_fcoe_stats->fcoe_tx_byte_cnt, 1000000); 20978c2ecf20Sopenharmony_ci qedf_stats->rx_words += fw_fcoe_stats->fcoe_rx_byte_cnt / 4; 20988c2ecf20Sopenharmony_ci qedf_stats->tx_words += fw_fcoe_stats->fcoe_tx_byte_cnt / 4; 20998c2ecf20Sopenharmony_ci qedf_stats->invalid_crc_count += 21008c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt; 21018c2ecf20Sopenharmony_ci qedf_stats->dumped_frames = 21028c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt; 21038c2ecf20Sopenharmony_ci qedf_stats->error_frames += 21048c2ecf20Sopenharmony_ci fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt; 21058c2ecf20Sopenharmony_ci qedf_stats->fcp_input_requests += qedf->input_requests; 21068c2ecf20Sopenharmony_ci qedf_stats->fcp_output_requests += qedf->output_requests; 21078c2ecf20Sopenharmony_ci qedf_stats->fcp_control_requests += qedf->control_requests; 21088c2ecf20Sopenharmony_ci qedf_stats->fcp_packet_aborts += qedf->packet_aborts; 21098c2ecf20Sopenharmony_ci qedf_stats->fcp_frame_alloc_failures += qedf->alloc_failures; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci mutex_unlock(&qedf->stats_mutex); 21128c2ecf20Sopenharmony_ci kfree(fw_fcoe_stats); 21138c2ecf20Sopenharmony_ciout: 21148c2ecf20Sopenharmony_ci return qedf_stats; 21158c2ecf20Sopenharmony_ci} 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_cistatic struct fc_function_template qedf_fc_transport_fn = { 21188c2ecf20Sopenharmony_ci .show_host_node_name = 1, 21198c2ecf20Sopenharmony_ci .show_host_port_name = 1, 21208c2ecf20Sopenharmony_ci .show_host_supported_classes = 1, 21218c2ecf20Sopenharmony_ci .show_host_supported_fc4s = 1, 21228c2ecf20Sopenharmony_ci .show_host_active_fc4s = 1, 21238c2ecf20Sopenharmony_ci .show_host_maxframe_size = 1, 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci .get_host_port_id = qedf_get_host_port_id, 21268c2ecf20Sopenharmony_ci .show_host_port_id = 1, 21278c2ecf20Sopenharmony_ci .show_host_supported_speeds = 1, 21288c2ecf20Sopenharmony_ci .get_host_speed = fc_get_host_speed, 21298c2ecf20Sopenharmony_ci .show_host_speed = 1, 21308c2ecf20Sopenharmony_ci .show_host_port_type = 1, 21318c2ecf20Sopenharmony_ci .get_host_port_state = fc_get_host_port_state, 21328c2ecf20Sopenharmony_ci .show_host_port_state = 1, 21338c2ecf20Sopenharmony_ci .show_host_symbolic_name = 1, 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci /* 21368c2ecf20Sopenharmony_ci * Tell FC transport to allocate enough space to store the backpointer 21378c2ecf20Sopenharmony_ci * for the associate qedf_rport struct. 21388c2ecf20Sopenharmony_ci */ 21398c2ecf20Sopenharmony_ci .dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) + 21408c2ecf20Sopenharmony_ci sizeof(struct qedf_rport)), 21418c2ecf20Sopenharmony_ci .show_rport_maxframe_size = 1, 21428c2ecf20Sopenharmony_ci .show_rport_supported_classes = 1, 21438c2ecf20Sopenharmony_ci .show_host_fabric_name = 1, 21448c2ecf20Sopenharmony_ci .show_starget_node_name = 1, 21458c2ecf20Sopenharmony_ci .show_starget_port_name = 1, 21468c2ecf20Sopenharmony_ci .show_starget_port_id = 1, 21478c2ecf20Sopenharmony_ci .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, 21488c2ecf20Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 21498c2ecf20Sopenharmony_ci .get_fc_host_stats = qedf_fc_get_host_stats, 21508c2ecf20Sopenharmony_ci .issue_fc_host_lip = qedf_fcoe_reset, 21518c2ecf20Sopenharmony_ci .vport_create = qedf_vport_create, 21528c2ecf20Sopenharmony_ci .vport_delete = qedf_vport_destroy, 21538c2ecf20Sopenharmony_ci .vport_disable = qedf_vport_disable, 21548c2ecf20Sopenharmony_ci .bsg_request = fc_lport_bsg_request, 21558c2ecf20Sopenharmony_ci}; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_cistatic struct fc_function_template qedf_fc_vport_transport_fn = { 21588c2ecf20Sopenharmony_ci .show_host_node_name = 1, 21598c2ecf20Sopenharmony_ci .show_host_port_name = 1, 21608c2ecf20Sopenharmony_ci .show_host_supported_classes = 1, 21618c2ecf20Sopenharmony_ci .show_host_supported_fc4s = 1, 21628c2ecf20Sopenharmony_ci .show_host_active_fc4s = 1, 21638c2ecf20Sopenharmony_ci .show_host_maxframe_size = 1, 21648c2ecf20Sopenharmony_ci .show_host_port_id = 1, 21658c2ecf20Sopenharmony_ci .show_host_supported_speeds = 1, 21668c2ecf20Sopenharmony_ci .get_host_speed = fc_get_host_speed, 21678c2ecf20Sopenharmony_ci .show_host_speed = 1, 21688c2ecf20Sopenharmony_ci .show_host_port_type = 1, 21698c2ecf20Sopenharmony_ci .get_host_port_state = fc_get_host_port_state, 21708c2ecf20Sopenharmony_ci .show_host_port_state = 1, 21718c2ecf20Sopenharmony_ci .show_host_symbolic_name = 1, 21728c2ecf20Sopenharmony_ci .dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) + 21738c2ecf20Sopenharmony_ci sizeof(struct qedf_rport)), 21748c2ecf20Sopenharmony_ci .show_rport_maxframe_size = 1, 21758c2ecf20Sopenharmony_ci .show_rport_supported_classes = 1, 21768c2ecf20Sopenharmony_ci .show_host_fabric_name = 1, 21778c2ecf20Sopenharmony_ci .show_starget_node_name = 1, 21788c2ecf20Sopenharmony_ci .show_starget_port_name = 1, 21798c2ecf20Sopenharmony_ci .show_starget_port_id = 1, 21808c2ecf20Sopenharmony_ci .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, 21818c2ecf20Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 21828c2ecf20Sopenharmony_ci .get_fc_host_stats = fc_get_host_stats, 21838c2ecf20Sopenharmony_ci .issue_fc_host_lip = qedf_fcoe_reset, 21848c2ecf20Sopenharmony_ci .bsg_request = fc_lport_bsg_request, 21858c2ecf20Sopenharmony_ci}; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_cistatic bool qedf_fp_has_work(struct qedf_fastpath *fp) 21888c2ecf20Sopenharmony_ci{ 21898c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = fp->qedf; 21908c2ecf20Sopenharmony_ci struct global_queue *que; 21918c2ecf20Sopenharmony_ci struct qed_sb_info *sb_info = fp->sb_info; 21928c2ecf20Sopenharmony_ci struct status_block_e4 *sb = sb_info->sb_virt; 21938c2ecf20Sopenharmony_ci u16 prod_idx; 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci /* Get the pointer to the global CQ this completion is on */ 21968c2ecf20Sopenharmony_ci que = qedf->global_queues[fp->sb_id]; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci /* Be sure all responses have been written to PI */ 21998c2ecf20Sopenharmony_ci rmb(); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci /* Get the current firmware producer index */ 22028c2ecf20Sopenharmony_ci prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI]; 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci return (que->cq_prod_idx != prod_idx); 22058c2ecf20Sopenharmony_ci} 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci/* 22088c2ecf20Sopenharmony_ci * Interrupt handler code. 22098c2ecf20Sopenharmony_ci */ 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci/* Process completion queue and copy CQE contents for deferred processesing 22128c2ecf20Sopenharmony_ci * 22138c2ecf20Sopenharmony_ci * Return true if we should wake the I/O thread, false if not. 22148c2ecf20Sopenharmony_ci */ 22158c2ecf20Sopenharmony_cistatic bool qedf_process_completions(struct qedf_fastpath *fp) 22168c2ecf20Sopenharmony_ci{ 22178c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = fp->qedf; 22188c2ecf20Sopenharmony_ci struct qed_sb_info *sb_info = fp->sb_info; 22198c2ecf20Sopenharmony_ci struct status_block_e4 *sb = sb_info->sb_virt; 22208c2ecf20Sopenharmony_ci struct global_queue *que; 22218c2ecf20Sopenharmony_ci u16 prod_idx; 22228c2ecf20Sopenharmony_ci struct fcoe_cqe *cqe; 22238c2ecf20Sopenharmony_ci struct qedf_io_work *io_work; 22248c2ecf20Sopenharmony_ci int num_handled = 0; 22258c2ecf20Sopenharmony_ci unsigned int cpu; 22268c2ecf20Sopenharmony_ci struct qedf_ioreq *io_req = NULL; 22278c2ecf20Sopenharmony_ci u16 xid; 22288c2ecf20Sopenharmony_ci u16 new_cqes; 22298c2ecf20Sopenharmony_ci u32 comp_type; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci /* Get the current firmware producer index */ 22328c2ecf20Sopenharmony_ci prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI]; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci /* Get the pointer to the global CQ this completion is on */ 22358c2ecf20Sopenharmony_ci que = qedf->global_queues[fp->sb_id]; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci /* Calculate the amount of new elements since last processing */ 22388c2ecf20Sopenharmony_ci new_cqes = (prod_idx >= que->cq_prod_idx) ? 22398c2ecf20Sopenharmony_ci (prod_idx - que->cq_prod_idx) : 22408c2ecf20Sopenharmony_ci 0x10000 - que->cq_prod_idx + prod_idx; 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci /* Save producer index */ 22438c2ecf20Sopenharmony_ci que->cq_prod_idx = prod_idx; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci while (new_cqes) { 22468c2ecf20Sopenharmony_ci fp->completions++; 22478c2ecf20Sopenharmony_ci num_handled++; 22488c2ecf20Sopenharmony_ci cqe = &que->cq[que->cq_cons_idx]; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) & 22518c2ecf20Sopenharmony_ci FCOE_CQE_CQE_TYPE_MASK; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci /* 22548c2ecf20Sopenharmony_ci * Process unsolicited CQEs directly in the interrupt handler 22558c2ecf20Sopenharmony_ci * sine we need the fastpath ID 22568c2ecf20Sopenharmony_ci */ 22578c2ecf20Sopenharmony_ci if (comp_type == FCOE_UNSOLIC_CQE_TYPE) { 22588c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL, 22598c2ecf20Sopenharmony_ci "Unsolicated CQE.\n"); 22608c2ecf20Sopenharmony_ci qedf_process_unsol_compl(qedf, fp->sb_id, cqe); 22618c2ecf20Sopenharmony_ci /* 22628c2ecf20Sopenharmony_ci * Don't add a work list item. Increment consumer 22638c2ecf20Sopenharmony_ci * consumer index and move on. 22648c2ecf20Sopenharmony_ci */ 22658c2ecf20Sopenharmony_ci goto inc_idx; 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK; 22698c2ecf20Sopenharmony_ci io_req = &qedf->cmd_mgr->cmds[xid]; 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci /* 22728c2ecf20Sopenharmony_ci * Figure out which percpu thread we should queue this I/O 22738c2ecf20Sopenharmony_ci * on. 22748c2ecf20Sopenharmony_ci */ 22758c2ecf20Sopenharmony_ci if (!io_req) 22768c2ecf20Sopenharmony_ci /* If there is not io_req assocated with this CQE 22778c2ecf20Sopenharmony_ci * just queue it on CPU 0 22788c2ecf20Sopenharmony_ci */ 22798c2ecf20Sopenharmony_ci cpu = 0; 22808c2ecf20Sopenharmony_ci else { 22818c2ecf20Sopenharmony_ci cpu = io_req->cpu; 22828c2ecf20Sopenharmony_ci io_req->int_cpu = smp_processor_id(); 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci io_work = mempool_alloc(qedf->io_mempool, GFP_ATOMIC); 22868c2ecf20Sopenharmony_ci if (!io_work) { 22878c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate " 22888c2ecf20Sopenharmony_ci "work for I/O completion.\n"); 22898c2ecf20Sopenharmony_ci continue; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci memset(io_work, 0, sizeof(struct qedf_io_work)); 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci INIT_WORK(&io_work->work, qedf_fp_io_handler); 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci /* Copy contents of CQE for deferred processing */ 22968c2ecf20Sopenharmony_ci memcpy(&io_work->cqe, cqe, sizeof(struct fcoe_cqe)); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci io_work->qedf = fp->qedf; 22998c2ecf20Sopenharmony_ci io_work->fp = NULL; /* Only used for unsolicited frames */ 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci queue_work_on(cpu, qedf_io_wq, &io_work->work); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ciinc_idx: 23048c2ecf20Sopenharmony_ci que->cq_cons_idx++; 23058c2ecf20Sopenharmony_ci if (que->cq_cons_idx == fp->cq_num_entries) 23068c2ecf20Sopenharmony_ci que->cq_cons_idx = 0; 23078c2ecf20Sopenharmony_ci new_cqes--; 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci return true; 23118c2ecf20Sopenharmony_ci} 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci/* MSI-X fastpath handler code */ 23158c2ecf20Sopenharmony_cistatic irqreturn_t qedf_msix_handler(int irq, void *dev_id) 23168c2ecf20Sopenharmony_ci{ 23178c2ecf20Sopenharmony_ci struct qedf_fastpath *fp = dev_id; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if (!fp) { 23208c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fp is null.\n"); 23218c2ecf20Sopenharmony_ci return IRQ_HANDLED; 23228c2ecf20Sopenharmony_ci } 23238c2ecf20Sopenharmony_ci if (!fp->sb_info) { 23248c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fp->sb_info in null."); 23258c2ecf20Sopenharmony_ci return IRQ_HANDLED; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci /* 23298c2ecf20Sopenharmony_ci * Disable interrupts for this status block while we process new 23308c2ecf20Sopenharmony_ci * completions 23318c2ecf20Sopenharmony_ci */ 23328c2ecf20Sopenharmony_ci qed_sb_ack(fp->sb_info, IGU_INT_DISABLE, 0 /*do not update*/); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci while (1) { 23358c2ecf20Sopenharmony_ci qedf_process_completions(fp); 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci if (qedf_fp_has_work(fp) == 0) { 23388c2ecf20Sopenharmony_ci /* Update the sb information */ 23398c2ecf20Sopenharmony_ci qed_sb_update_sb_idx(fp->sb_info); 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci /* Check for more work */ 23428c2ecf20Sopenharmony_ci rmb(); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci if (qedf_fp_has_work(fp) == 0) { 23458c2ecf20Sopenharmony_ci /* Re-enable interrupts */ 23468c2ecf20Sopenharmony_ci qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1); 23478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 23488c2ecf20Sopenharmony_ci } 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci /* Do we ever want to break out of above loop? */ 23538c2ecf20Sopenharmony_ci return IRQ_HANDLED; 23548c2ecf20Sopenharmony_ci} 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci/* simd handler for MSI/INTa */ 23578c2ecf20Sopenharmony_cistatic void qedf_simd_int_handler(void *cookie) 23588c2ecf20Sopenharmony_ci{ 23598c2ecf20Sopenharmony_ci /* Cookie is qedf_ctx struct */ 23608c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = (struct qedf_ctx *)cookie; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "qedf=%p.\n", qedf); 23638c2ecf20Sopenharmony_ci} 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci#define QEDF_SIMD_HANDLER_NUM 0 23668c2ecf20Sopenharmony_cistatic void qedf_sync_free_irqs(struct qedf_ctx *qedf) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci int i; 23698c2ecf20Sopenharmony_ci u16 vector_idx = 0; 23708c2ecf20Sopenharmony_ci u32 vector; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci if (qedf->int_info.msix_cnt) { 23738c2ecf20Sopenharmony_ci for (i = 0; i < qedf->int_info.used_cnt; i++) { 23748c2ecf20Sopenharmony_ci vector_idx = i * qedf->dev_info.common.num_hwfns + 23758c2ecf20Sopenharmony_ci qed_ops->common->get_affin_hwfn_idx(qedf->cdev); 23768c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 23778c2ecf20Sopenharmony_ci "Freeing IRQ #%d vector_idx=%d.\n", 23788c2ecf20Sopenharmony_ci i, vector_idx); 23798c2ecf20Sopenharmony_ci vector = qedf->int_info.msix[vector_idx].vector; 23808c2ecf20Sopenharmony_ci synchronize_irq(vector); 23818c2ecf20Sopenharmony_ci irq_set_affinity_hint(vector, NULL); 23828c2ecf20Sopenharmony_ci irq_set_affinity_notifier(vector, NULL); 23838c2ecf20Sopenharmony_ci free_irq(vector, &qedf->fp_array[i]); 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci } else 23868c2ecf20Sopenharmony_ci qed_ops->common->simd_handler_clean(qedf->cdev, 23878c2ecf20Sopenharmony_ci QEDF_SIMD_HANDLER_NUM); 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci qedf->int_info.used_cnt = 0; 23908c2ecf20Sopenharmony_ci qed_ops->common->set_fp_int(qedf->cdev, 0); 23918c2ecf20Sopenharmony_ci} 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_cistatic int qedf_request_msix_irq(struct qedf_ctx *qedf) 23948c2ecf20Sopenharmony_ci{ 23958c2ecf20Sopenharmony_ci int i, rc, cpu; 23968c2ecf20Sopenharmony_ci u16 vector_idx = 0; 23978c2ecf20Sopenharmony_ci u32 vector; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci cpu = cpumask_first(cpu_online_mask); 24008c2ecf20Sopenharmony_ci for (i = 0; i < qedf->num_queues; i++) { 24018c2ecf20Sopenharmony_ci vector_idx = i * qedf->dev_info.common.num_hwfns + 24028c2ecf20Sopenharmony_ci qed_ops->common->get_affin_hwfn_idx(qedf->cdev); 24038c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 24048c2ecf20Sopenharmony_ci "Requesting IRQ #%d vector_idx=%d.\n", 24058c2ecf20Sopenharmony_ci i, vector_idx); 24068c2ecf20Sopenharmony_ci vector = qedf->int_info.msix[vector_idx].vector; 24078c2ecf20Sopenharmony_ci rc = request_irq(vector, qedf_msix_handler, 0, "qedf", 24088c2ecf20Sopenharmony_ci &qedf->fp_array[i]); 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci if (rc) { 24118c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "request_irq failed.\n"); 24128c2ecf20Sopenharmony_ci qedf_sync_free_irqs(qedf); 24138c2ecf20Sopenharmony_ci return rc; 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci qedf->int_info.used_cnt++; 24178c2ecf20Sopenharmony_ci rc = irq_set_affinity_hint(vector, get_cpu_mask(cpu)); 24188c2ecf20Sopenharmony_ci cpu = cpumask_next(cpu, cpu_online_mask); 24198c2ecf20Sopenharmony_ci } 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci return 0; 24228c2ecf20Sopenharmony_ci} 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_cistatic int qedf_setup_int(struct qedf_ctx *qedf) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci int rc = 0; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci /* 24298c2ecf20Sopenharmony_ci * Learn interrupt configuration 24308c2ecf20Sopenharmony_ci */ 24318c2ecf20Sopenharmony_ci rc = qed_ops->common->set_fp_int(qedf->cdev, num_online_cpus()); 24328c2ecf20Sopenharmony_ci if (rc <= 0) 24338c2ecf20Sopenharmony_ci return 0; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci rc = qed_ops->common->get_fp_int(qedf->cdev, &qedf->int_info); 24368c2ecf20Sopenharmony_ci if (rc) 24378c2ecf20Sopenharmony_ci return 0; 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of msix_cnt = " 24408c2ecf20Sopenharmony_ci "0x%x num of cpus = 0x%x\n", qedf->int_info.msix_cnt, 24418c2ecf20Sopenharmony_ci num_online_cpus()); 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci if (qedf->int_info.msix_cnt) 24448c2ecf20Sopenharmony_ci return qedf_request_msix_irq(qedf); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci qed_ops->common->simd_handler_config(qedf->cdev, &qedf, 24478c2ecf20Sopenharmony_ci QEDF_SIMD_HANDLER_NUM, qedf_simd_int_handler); 24488c2ecf20Sopenharmony_ci qedf->int_info.used_cnt = 1; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 24518c2ecf20Sopenharmony_ci "Cannot load driver due to a lack of MSI-X vectors.\n"); 24528c2ecf20Sopenharmony_ci return -EINVAL; 24538c2ecf20Sopenharmony_ci} 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci/* Main function for libfc frame reception */ 24568c2ecf20Sopenharmony_cistatic void qedf_recv_frame(struct qedf_ctx *qedf, 24578c2ecf20Sopenharmony_ci struct sk_buff *skb) 24588c2ecf20Sopenharmony_ci{ 24598c2ecf20Sopenharmony_ci u32 fr_len; 24608c2ecf20Sopenharmony_ci struct fc_lport *lport; 24618c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 24628c2ecf20Sopenharmony_ci struct fcoe_crc_eof crc_eof; 24638c2ecf20Sopenharmony_ci struct fc_frame *fp; 24648c2ecf20Sopenharmony_ci u8 *mac = NULL; 24658c2ecf20Sopenharmony_ci u8 *dest_mac = NULL; 24668c2ecf20Sopenharmony_ci struct fcoe_hdr *hp; 24678c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 24688c2ecf20Sopenharmony_ci struct fc_lport *vn_port; 24698c2ecf20Sopenharmony_ci u32 f_ctl; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci lport = qedf->lport; 24728c2ecf20Sopenharmony_ci if (lport == NULL || lport->state == LPORT_ST_DISABLED) { 24738c2ecf20Sopenharmony_ci QEDF_WARN(NULL, "Invalid lport struct or lport disabled.\n"); 24748c2ecf20Sopenharmony_ci kfree_skb(skb); 24758c2ecf20Sopenharmony_ci return; 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci if (skb_is_nonlinear(skb)) 24798c2ecf20Sopenharmony_ci skb_linearize(skb); 24808c2ecf20Sopenharmony_ci mac = eth_hdr(skb)->h_source; 24818c2ecf20Sopenharmony_ci dest_mac = eth_hdr(skb)->h_dest; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci /* Pull the header */ 24848c2ecf20Sopenharmony_ci hp = (struct fcoe_hdr *)skb->data; 24858c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *) skb_transport_header(skb); 24868c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct fcoe_hdr)); 24878c2ecf20Sopenharmony_ci fr_len = skb->len - sizeof(struct fcoe_crc_eof); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci fp = (struct fc_frame *)skb; 24908c2ecf20Sopenharmony_ci fc_frame_init(fp); 24918c2ecf20Sopenharmony_ci fr_dev(fp) = lport; 24928c2ecf20Sopenharmony_ci fr_sof(fp) = hp->fcoe_sof; 24938c2ecf20Sopenharmony_ci if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { 24948c2ecf20Sopenharmony_ci QEDF_INFO(NULL, QEDF_LOG_LL2, "skb_copy_bits failed.\n"); 24958c2ecf20Sopenharmony_ci kfree_skb(skb); 24968c2ecf20Sopenharmony_ci return; 24978c2ecf20Sopenharmony_ci } 24988c2ecf20Sopenharmony_ci fr_eof(fp) = crc_eof.fcoe_eof; 24998c2ecf20Sopenharmony_ci fr_crc(fp) = crc_eof.fcoe_crc32; 25008c2ecf20Sopenharmony_ci if (pskb_trim(skb, fr_len)) { 25018c2ecf20Sopenharmony_ci QEDF_INFO(NULL, QEDF_LOG_LL2, "pskb_trim failed.\n"); 25028c2ecf20Sopenharmony_ci kfree_skb(skb); 25038c2ecf20Sopenharmony_ci return; 25048c2ecf20Sopenharmony_ci } 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci /* 25098c2ecf20Sopenharmony_ci * Invalid frame filters. 25108c2ecf20Sopenharmony_ci */ 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && 25138c2ecf20Sopenharmony_ci fh->fh_type == FC_TYPE_FCP) { 25148c2ecf20Sopenharmony_ci /* Drop FCP data. We dont this in L2 path */ 25158c2ecf20Sopenharmony_ci kfree_skb(skb); 25168c2ecf20Sopenharmony_ci return; 25178c2ecf20Sopenharmony_ci } 25188c2ecf20Sopenharmony_ci if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && 25198c2ecf20Sopenharmony_ci fh->fh_type == FC_TYPE_ELS) { 25208c2ecf20Sopenharmony_ci switch (fc_frame_payload_op(fp)) { 25218c2ecf20Sopenharmony_ci case ELS_LOGO: 25228c2ecf20Sopenharmony_ci if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { 25238c2ecf20Sopenharmony_ci /* drop non-FIP LOGO */ 25248c2ecf20Sopenharmony_ci kfree_skb(skb); 25258c2ecf20Sopenharmony_ci return; 25268c2ecf20Sopenharmony_ci } 25278c2ecf20Sopenharmony_ci break; 25288c2ecf20Sopenharmony_ci } 25298c2ecf20Sopenharmony_ci } 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) { 25328c2ecf20Sopenharmony_ci /* Drop incoming ABTS */ 25338c2ecf20Sopenharmony_ci kfree_skb(skb); 25348c2ecf20Sopenharmony_ci return; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci if (ntoh24(&dest_mac[3]) != ntoh24(fh->fh_d_id)) { 25388c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, 25398c2ecf20Sopenharmony_ci "FC frame d_id mismatch with MAC %pM.\n", dest_mac); 25408c2ecf20Sopenharmony_ci kfree_skb(skb); 25418c2ecf20Sopenharmony_ci return; 25428c2ecf20Sopenharmony_ci } 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (qedf->ctlr.state) { 25458c2ecf20Sopenharmony_ci if (!ether_addr_equal(mac, qedf->ctlr.dest_addr)) { 25468c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, 25478c2ecf20Sopenharmony_ci "Wrong source address: mac:%pM dest_addr:%pM.\n", 25488c2ecf20Sopenharmony_ci mac, qedf->ctlr.dest_addr); 25498c2ecf20Sopenharmony_ci kfree_skb(skb); 25508c2ecf20Sopenharmony_ci return; 25518c2ecf20Sopenharmony_ci } 25528c2ecf20Sopenharmony_ci } 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci vn_port = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id)); 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci /* 25578c2ecf20Sopenharmony_ci * If the destination ID from the frame header does not match what we 25588c2ecf20Sopenharmony_ci * have on record for lport and the search for a NPIV port came up 25598c2ecf20Sopenharmony_ci * empty then this is not addressed to our port so simply drop it. 25608c2ecf20Sopenharmony_ci */ 25618c2ecf20Sopenharmony_ci if (lport->port_id != ntoh24(fh->fh_d_id) && !vn_port) { 25628c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2, 25638c2ecf20Sopenharmony_ci "Dropping frame due to destination mismatch: lport->port_id=0x%x fh->d_id=0x%x.\n", 25648c2ecf20Sopenharmony_ci lport->port_id, ntoh24(fh->fh_d_id)); 25658c2ecf20Sopenharmony_ci kfree_skb(skb); 25668c2ecf20Sopenharmony_ci return; 25678c2ecf20Sopenharmony_ci } 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci f_ctl = ntoh24(fh->fh_f_ctl); 25708c2ecf20Sopenharmony_ci if ((fh->fh_type == FC_TYPE_BLS) && (f_ctl & FC_FC_SEQ_CTX) && 25718c2ecf20Sopenharmony_ci (f_ctl & FC_FC_EX_CTX)) { 25728c2ecf20Sopenharmony_ci /* Drop incoming ABTS response that has both SEQ/EX CTX set */ 25738c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2, 25748c2ecf20Sopenharmony_ci "Dropping ABTS response as both SEQ/EX CTX set.\n"); 25758c2ecf20Sopenharmony_ci kfree_skb(skb); 25768c2ecf20Sopenharmony_ci return; 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci /* 25808c2ecf20Sopenharmony_ci * If a connection is uploading, drop incoming FCoE frames as there 25818c2ecf20Sopenharmony_ci * is a small window where we could try to return a frame while libfc 25828c2ecf20Sopenharmony_ci * is trying to clean things up. 25838c2ecf20Sopenharmony_ci */ 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci /* Get fcport associated with d_id if it exists */ 25868c2ecf20Sopenharmony_ci fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id)); 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci if (fcport && test_bit(QEDF_RPORT_UPLOADING_CONNECTION, 25898c2ecf20Sopenharmony_ci &fcport->flags)) { 25908c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, 25918c2ecf20Sopenharmony_ci "Connection uploading, dropping fp=%p.\n", fp); 25928c2ecf20Sopenharmony_ci kfree_skb(skb); 25938c2ecf20Sopenharmony_ci return; 25948c2ecf20Sopenharmony_ci } 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame receive: " 25978c2ecf20Sopenharmony_ci "skb=%p fp=%p src=%06x dest=%06x r_ctl=%x fh_type=%x.\n", skb, fp, 25988c2ecf20Sopenharmony_ci ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl, 25998c2ecf20Sopenharmony_ci fh->fh_type); 26008c2ecf20Sopenharmony_ci if (qedf_dump_frames) 26018c2ecf20Sopenharmony_ci print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16, 26028c2ecf20Sopenharmony_ci 1, skb->data, skb->len, false); 26038c2ecf20Sopenharmony_ci fc_exch_recv(lport, fp); 26048c2ecf20Sopenharmony_ci} 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_cistatic void qedf_ll2_process_skb(struct work_struct *work) 26078c2ecf20Sopenharmony_ci{ 26088c2ecf20Sopenharmony_ci struct qedf_skb_work *skb_work = 26098c2ecf20Sopenharmony_ci container_of(work, struct qedf_skb_work, work); 26108c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = skb_work->qedf; 26118c2ecf20Sopenharmony_ci struct sk_buff *skb = skb_work->skb; 26128c2ecf20Sopenharmony_ci struct ethhdr *eh; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci if (!qedf) { 26158c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "qedf is NULL\n"); 26168c2ecf20Sopenharmony_ci goto err_out; 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci eh = (struct ethhdr *)skb->data; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci /* Undo VLAN encapsulation */ 26228c2ecf20Sopenharmony_ci if (eh->h_proto == htons(ETH_P_8021Q)) { 26238c2ecf20Sopenharmony_ci memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2); 26248c2ecf20Sopenharmony_ci eh = skb_pull(skb, VLAN_HLEN); 26258c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 26268c2ecf20Sopenharmony_ci } 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci /* 26298c2ecf20Sopenharmony_ci * Process either a FIP frame or FCoE frame based on the 26308c2ecf20Sopenharmony_ci * protocol value. If it's not either just drop the 26318c2ecf20Sopenharmony_ci * frame. 26328c2ecf20Sopenharmony_ci */ 26338c2ecf20Sopenharmony_ci if (eh->h_proto == htons(ETH_P_FIP)) { 26348c2ecf20Sopenharmony_ci qedf_fip_recv(qedf, skb); 26358c2ecf20Sopenharmony_ci goto out; 26368c2ecf20Sopenharmony_ci } else if (eh->h_proto == htons(ETH_P_FCOE)) { 26378c2ecf20Sopenharmony_ci __skb_pull(skb, ETH_HLEN); 26388c2ecf20Sopenharmony_ci qedf_recv_frame(qedf, skb); 26398c2ecf20Sopenharmony_ci goto out; 26408c2ecf20Sopenharmony_ci } else 26418c2ecf20Sopenharmony_ci goto err_out; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_cierr_out: 26448c2ecf20Sopenharmony_ci kfree_skb(skb); 26458c2ecf20Sopenharmony_ciout: 26468c2ecf20Sopenharmony_ci kfree(skb_work); 26478c2ecf20Sopenharmony_ci return; 26488c2ecf20Sopenharmony_ci} 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_cistatic int qedf_ll2_rx(void *cookie, struct sk_buff *skb, 26518c2ecf20Sopenharmony_ci u32 arg1, u32 arg2) 26528c2ecf20Sopenharmony_ci{ 26538c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = (struct qedf_ctx *)cookie; 26548c2ecf20Sopenharmony_ci struct qedf_skb_work *skb_work; 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) { 26578c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2, 26588c2ecf20Sopenharmony_ci "Dropping frame as link state is down.\n"); 26598c2ecf20Sopenharmony_ci kfree_skb(skb); 26608c2ecf20Sopenharmony_ci return 0; 26618c2ecf20Sopenharmony_ci } 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci skb_work = kzalloc(sizeof(struct qedf_skb_work), GFP_ATOMIC); 26648c2ecf20Sopenharmony_ci if (!skb_work) { 26658c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate skb_work so " 26668c2ecf20Sopenharmony_ci "dropping frame.\n"); 26678c2ecf20Sopenharmony_ci kfree_skb(skb); 26688c2ecf20Sopenharmony_ci return 0; 26698c2ecf20Sopenharmony_ci } 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci INIT_WORK(&skb_work->work, qedf_ll2_process_skb); 26728c2ecf20Sopenharmony_ci skb_work->skb = skb; 26738c2ecf20Sopenharmony_ci skb_work->qedf = qedf; 26748c2ecf20Sopenharmony_ci queue_work(qedf->ll2_recv_wq, &skb_work->work); 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci return 0; 26778c2ecf20Sopenharmony_ci} 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_cistatic struct qed_ll2_cb_ops qedf_ll2_cb_ops = { 26808c2ecf20Sopenharmony_ci .rx_cb = qedf_ll2_rx, 26818c2ecf20Sopenharmony_ci .tx_cb = NULL, 26828c2ecf20Sopenharmony_ci}; 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci/* Main thread to process I/O completions */ 26858c2ecf20Sopenharmony_civoid qedf_fp_io_handler(struct work_struct *work) 26868c2ecf20Sopenharmony_ci{ 26878c2ecf20Sopenharmony_ci struct qedf_io_work *io_work = 26888c2ecf20Sopenharmony_ci container_of(work, struct qedf_io_work, work); 26898c2ecf20Sopenharmony_ci u32 comp_type; 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci /* 26928c2ecf20Sopenharmony_ci * Deferred part of unsolicited CQE sends 26938c2ecf20Sopenharmony_ci * frame to libfc. 26948c2ecf20Sopenharmony_ci */ 26958c2ecf20Sopenharmony_ci comp_type = (io_work->cqe.cqe_data >> 26968c2ecf20Sopenharmony_ci FCOE_CQE_CQE_TYPE_SHIFT) & 26978c2ecf20Sopenharmony_ci FCOE_CQE_CQE_TYPE_MASK; 26988c2ecf20Sopenharmony_ci if (comp_type == FCOE_UNSOLIC_CQE_TYPE && 26998c2ecf20Sopenharmony_ci io_work->fp) 27008c2ecf20Sopenharmony_ci fc_exch_recv(io_work->qedf->lport, io_work->fp); 27018c2ecf20Sopenharmony_ci else 27028c2ecf20Sopenharmony_ci qedf_process_cqe(io_work->qedf, &io_work->cqe); 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci kfree(io_work); 27058c2ecf20Sopenharmony_ci} 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_cistatic int qedf_alloc_and_init_sb(struct qedf_ctx *qedf, 27088c2ecf20Sopenharmony_ci struct qed_sb_info *sb_info, u16 sb_id) 27098c2ecf20Sopenharmony_ci{ 27108c2ecf20Sopenharmony_ci struct status_block_e4 *sb_virt; 27118c2ecf20Sopenharmony_ci dma_addr_t sb_phys; 27128c2ecf20Sopenharmony_ci int ret; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci sb_virt = dma_alloc_coherent(&qedf->pdev->dev, 27158c2ecf20Sopenharmony_ci sizeof(struct status_block_e4), &sb_phys, GFP_KERNEL); 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci if (!sb_virt) { 27188c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 27198c2ecf20Sopenharmony_ci "Status block allocation failed for id = %d.\n", 27208c2ecf20Sopenharmony_ci sb_id); 27218c2ecf20Sopenharmony_ci return -ENOMEM; 27228c2ecf20Sopenharmony_ci } 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci ret = qed_ops->common->sb_init(qedf->cdev, sb_info, sb_virt, sb_phys, 27258c2ecf20Sopenharmony_ci sb_id, QED_SB_TYPE_STORAGE); 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci if (ret) { 27288c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 27298c2ecf20Sopenharmony_ci "Status block initialization failed (0x%x) for id = %d.\n", 27308c2ecf20Sopenharmony_ci ret, sb_id); 27318c2ecf20Sopenharmony_ci return ret; 27328c2ecf20Sopenharmony_ci } 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci return 0; 27358c2ecf20Sopenharmony_ci} 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_cistatic void qedf_free_sb(struct qedf_ctx *qedf, struct qed_sb_info *sb_info) 27388c2ecf20Sopenharmony_ci{ 27398c2ecf20Sopenharmony_ci if (sb_info->sb_virt) 27408c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, sizeof(*sb_info->sb_virt), 27418c2ecf20Sopenharmony_ci (void *)sb_info->sb_virt, sb_info->sb_phys); 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_cistatic void qedf_destroy_sb(struct qedf_ctx *qedf) 27458c2ecf20Sopenharmony_ci{ 27468c2ecf20Sopenharmony_ci int id; 27478c2ecf20Sopenharmony_ci struct qedf_fastpath *fp = NULL; 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci for (id = 0; id < qedf->num_queues; id++) { 27508c2ecf20Sopenharmony_ci fp = &(qedf->fp_array[id]); 27518c2ecf20Sopenharmony_ci if (fp->sb_id == QEDF_SB_ID_NULL) 27528c2ecf20Sopenharmony_ci break; 27538c2ecf20Sopenharmony_ci qedf_free_sb(qedf, fp->sb_info); 27548c2ecf20Sopenharmony_ci kfree(fp->sb_info); 27558c2ecf20Sopenharmony_ci } 27568c2ecf20Sopenharmony_ci kfree(qedf->fp_array); 27578c2ecf20Sopenharmony_ci} 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_cistatic int qedf_prepare_sb(struct qedf_ctx *qedf) 27608c2ecf20Sopenharmony_ci{ 27618c2ecf20Sopenharmony_ci int id; 27628c2ecf20Sopenharmony_ci struct qedf_fastpath *fp; 27638c2ecf20Sopenharmony_ci int ret; 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci qedf->fp_array = 27668c2ecf20Sopenharmony_ci kcalloc(qedf->num_queues, sizeof(struct qedf_fastpath), 27678c2ecf20Sopenharmony_ci GFP_KERNEL); 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci if (!qedf->fp_array) { 27708c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "fastpath array allocation " 27718c2ecf20Sopenharmony_ci "failed.\n"); 27728c2ecf20Sopenharmony_ci return -ENOMEM; 27738c2ecf20Sopenharmony_ci } 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci for (id = 0; id < qedf->num_queues; id++) { 27768c2ecf20Sopenharmony_ci fp = &(qedf->fp_array[id]); 27778c2ecf20Sopenharmony_ci fp->sb_id = QEDF_SB_ID_NULL; 27788c2ecf20Sopenharmony_ci fp->sb_info = kcalloc(1, sizeof(*fp->sb_info), GFP_KERNEL); 27798c2ecf20Sopenharmony_ci if (!fp->sb_info) { 27808c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "SB info struct " 27818c2ecf20Sopenharmony_ci "allocation failed.\n"); 27828c2ecf20Sopenharmony_ci goto err; 27838c2ecf20Sopenharmony_ci } 27848c2ecf20Sopenharmony_ci ret = qedf_alloc_and_init_sb(qedf, fp->sb_info, id); 27858c2ecf20Sopenharmony_ci if (ret) { 27868c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "SB allocation and " 27878c2ecf20Sopenharmony_ci "initialization failed.\n"); 27888c2ecf20Sopenharmony_ci goto err; 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci fp->sb_id = id; 27918c2ecf20Sopenharmony_ci fp->qedf = qedf; 27928c2ecf20Sopenharmony_ci fp->cq_num_entries = 27938c2ecf20Sopenharmony_ci qedf->global_queues[id]->cq_mem_size / 27948c2ecf20Sopenharmony_ci sizeof(struct fcoe_cqe); 27958c2ecf20Sopenharmony_ci } 27968c2ecf20Sopenharmony_cierr: 27978c2ecf20Sopenharmony_ci return 0; 27988c2ecf20Sopenharmony_ci} 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_civoid qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe) 28018c2ecf20Sopenharmony_ci{ 28028c2ecf20Sopenharmony_ci u16 xid; 28038c2ecf20Sopenharmony_ci struct qedf_ioreq *io_req; 28048c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 28058c2ecf20Sopenharmony_ci u32 comp_type; 28068c2ecf20Sopenharmony_ci u8 io_comp_type; 28078c2ecf20Sopenharmony_ci unsigned long flags; 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) & 28108c2ecf20Sopenharmony_ci FCOE_CQE_CQE_TYPE_MASK; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK; 28138c2ecf20Sopenharmony_ci io_req = &qedf->cmd_mgr->cmds[xid]; 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_ci /* Completion not for a valid I/O anymore so just return */ 28168c2ecf20Sopenharmony_ci if (!io_req) { 28178c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 28188c2ecf20Sopenharmony_ci "io_req is NULL for xid=0x%x.\n", xid); 28198c2ecf20Sopenharmony_ci return; 28208c2ecf20Sopenharmony_ci } 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci fcport = io_req->fcport; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci if (fcport == NULL) { 28258c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 28268c2ecf20Sopenharmony_ci "fcport is NULL for xid=0x%x io_req=%p.\n", 28278c2ecf20Sopenharmony_ci xid, io_req); 28288c2ecf20Sopenharmony_ci return; 28298c2ecf20Sopenharmony_ci } 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci /* 28328c2ecf20Sopenharmony_ci * Check that fcport is offloaded. If it isn't then the spinlock 28338c2ecf20Sopenharmony_ci * isn't valid and shouldn't be taken. We should just return. 28348c2ecf20Sopenharmony_ci */ 28358c2ecf20Sopenharmony_ci if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 28368c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 28378c2ecf20Sopenharmony_ci "Session not offloaded yet, fcport = %p.\n", fcport); 28388c2ecf20Sopenharmony_ci return; 28398c2ecf20Sopenharmony_ci } 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci spin_lock_irqsave(&fcport->rport_lock, flags); 28428c2ecf20Sopenharmony_ci io_comp_type = io_req->cmd_type; 28438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci switch (comp_type) { 28468c2ecf20Sopenharmony_ci case FCOE_GOOD_COMPLETION_CQE_TYPE: 28478c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28488c2ecf20Sopenharmony_ci switch (io_comp_type) { 28498c2ecf20Sopenharmony_ci case QEDF_SCSI_CMD: 28508c2ecf20Sopenharmony_ci qedf_scsi_completion(qedf, cqe, io_req); 28518c2ecf20Sopenharmony_ci break; 28528c2ecf20Sopenharmony_ci case QEDF_ELS: 28538c2ecf20Sopenharmony_ci qedf_process_els_compl(qedf, cqe, io_req); 28548c2ecf20Sopenharmony_ci break; 28558c2ecf20Sopenharmony_ci case QEDF_TASK_MGMT_CMD: 28568c2ecf20Sopenharmony_ci qedf_process_tmf_compl(qedf, cqe, io_req); 28578c2ecf20Sopenharmony_ci break; 28588c2ecf20Sopenharmony_ci case QEDF_SEQ_CLEANUP: 28598c2ecf20Sopenharmony_ci qedf_process_seq_cleanup_compl(qedf, cqe, io_req); 28608c2ecf20Sopenharmony_ci break; 28618c2ecf20Sopenharmony_ci } 28628c2ecf20Sopenharmony_ci break; 28638c2ecf20Sopenharmony_ci case FCOE_ERROR_DETECTION_CQE_TYPE: 28648c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28658c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 28668c2ecf20Sopenharmony_ci "Error detect CQE.\n"); 28678c2ecf20Sopenharmony_ci qedf_process_error_detect(qedf, cqe, io_req); 28688c2ecf20Sopenharmony_ci break; 28698c2ecf20Sopenharmony_ci case FCOE_EXCH_CLEANUP_CQE_TYPE: 28708c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28718c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 28728c2ecf20Sopenharmony_ci "Cleanup CQE.\n"); 28738c2ecf20Sopenharmony_ci qedf_process_cleanup_compl(qedf, cqe, io_req); 28748c2ecf20Sopenharmony_ci break; 28758c2ecf20Sopenharmony_ci case FCOE_ABTS_CQE_TYPE: 28768c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28778c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 28788c2ecf20Sopenharmony_ci "Abort CQE.\n"); 28798c2ecf20Sopenharmony_ci qedf_process_abts_compl(qedf, cqe, io_req); 28808c2ecf20Sopenharmony_ci break; 28818c2ecf20Sopenharmony_ci case FCOE_DUMMY_CQE_TYPE: 28828c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28838c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 28848c2ecf20Sopenharmony_ci "Dummy CQE.\n"); 28858c2ecf20Sopenharmony_ci break; 28868c2ecf20Sopenharmony_ci case FCOE_LOCAL_COMP_CQE_TYPE: 28878c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28888c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 28898c2ecf20Sopenharmony_ci "Local completion CQE.\n"); 28908c2ecf20Sopenharmony_ci break; 28918c2ecf20Sopenharmony_ci case FCOE_WARNING_CQE_TYPE: 28928c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28938c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 28948c2ecf20Sopenharmony_ci "Warning CQE.\n"); 28958c2ecf20Sopenharmony_ci qedf_process_warning_compl(qedf, cqe, io_req); 28968c2ecf20Sopenharmony_ci break; 28978c2ecf20Sopenharmony_ci case MAX_FCOE_CQE_TYPE: 28988c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 28998c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 29008c2ecf20Sopenharmony_ci "Max FCoE CQE.\n"); 29018c2ecf20Sopenharmony_ci break; 29028c2ecf20Sopenharmony_ci default: 29038c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, 29048c2ecf20Sopenharmony_ci "Default CQE.\n"); 29058c2ecf20Sopenharmony_ci break; 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci} 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_cistatic void qedf_free_bdq(struct qedf_ctx *qedf) 29108c2ecf20Sopenharmony_ci{ 29118c2ecf20Sopenharmony_ci int i; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci if (qedf->bdq_pbl_list) 29148c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE, 29158c2ecf20Sopenharmony_ci qedf->bdq_pbl_list, qedf->bdq_pbl_list_dma); 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci if (qedf->bdq_pbl) 29188c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, qedf->bdq_pbl_mem_size, 29198c2ecf20Sopenharmony_ci qedf->bdq_pbl, qedf->bdq_pbl_dma); 29208c2ecf20Sopenharmony_ci 29218c2ecf20Sopenharmony_ci for (i = 0; i < QEDF_BDQ_SIZE; i++) { 29228c2ecf20Sopenharmony_ci if (qedf->bdq[i].buf_addr) { 29238c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, QEDF_BDQ_BUF_SIZE, 29248c2ecf20Sopenharmony_ci qedf->bdq[i].buf_addr, qedf->bdq[i].buf_dma); 29258c2ecf20Sopenharmony_ci } 29268c2ecf20Sopenharmony_ci } 29278c2ecf20Sopenharmony_ci} 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_cistatic void qedf_free_global_queues(struct qedf_ctx *qedf) 29308c2ecf20Sopenharmony_ci{ 29318c2ecf20Sopenharmony_ci int i; 29328c2ecf20Sopenharmony_ci struct global_queue **gl = qedf->global_queues; 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_ci for (i = 0; i < qedf->num_queues; i++) { 29358c2ecf20Sopenharmony_ci if (!gl[i]) 29368c2ecf20Sopenharmony_ci continue; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci if (gl[i]->cq) 29398c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, 29408c2ecf20Sopenharmony_ci gl[i]->cq_mem_size, gl[i]->cq, gl[i]->cq_dma); 29418c2ecf20Sopenharmony_ci if (gl[i]->cq_pbl) 29428c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, gl[i]->cq_pbl_size, 29438c2ecf20Sopenharmony_ci gl[i]->cq_pbl, gl[i]->cq_pbl_dma); 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci kfree(gl[i]); 29468c2ecf20Sopenharmony_ci } 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci qedf_free_bdq(qedf); 29498c2ecf20Sopenharmony_ci} 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_cistatic int qedf_alloc_bdq(struct qedf_ctx *qedf) 29528c2ecf20Sopenharmony_ci{ 29538c2ecf20Sopenharmony_ci int i; 29548c2ecf20Sopenharmony_ci struct scsi_bd *pbl; 29558c2ecf20Sopenharmony_ci u64 *list; 29568c2ecf20Sopenharmony_ci dma_addr_t page; 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci /* Alloc dma memory for BDQ buffers */ 29598c2ecf20Sopenharmony_ci for (i = 0; i < QEDF_BDQ_SIZE; i++) { 29608c2ecf20Sopenharmony_ci qedf->bdq[i].buf_addr = dma_alloc_coherent(&qedf->pdev->dev, 29618c2ecf20Sopenharmony_ci QEDF_BDQ_BUF_SIZE, &qedf->bdq[i].buf_dma, GFP_KERNEL); 29628c2ecf20Sopenharmony_ci if (!qedf->bdq[i].buf_addr) { 29638c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate BDQ " 29648c2ecf20Sopenharmony_ci "buffer %d.\n", i); 29658c2ecf20Sopenharmony_ci return -ENOMEM; 29668c2ecf20Sopenharmony_ci } 29678c2ecf20Sopenharmony_ci } 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci /* Alloc dma memory for BDQ page buffer list */ 29708c2ecf20Sopenharmony_ci qedf->bdq_pbl_mem_size = 29718c2ecf20Sopenharmony_ci QEDF_BDQ_SIZE * sizeof(struct scsi_bd); 29728c2ecf20Sopenharmony_ci qedf->bdq_pbl_mem_size = 29738c2ecf20Sopenharmony_ci ALIGN(qedf->bdq_pbl_mem_size, QEDF_PAGE_SIZE); 29748c2ecf20Sopenharmony_ci 29758c2ecf20Sopenharmony_ci qedf->bdq_pbl = dma_alloc_coherent(&qedf->pdev->dev, 29768c2ecf20Sopenharmony_ci qedf->bdq_pbl_mem_size, &qedf->bdq_pbl_dma, GFP_KERNEL); 29778c2ecf20Sopenharmony_ci if (!qedf->bdq_pbl) { 29788c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate BDQ PBL.\n"); 29798c2ecf20Sopenharmony_ci return -ENOMEM; 29808c2ecf20Sopenharmony_ci } 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 29838c2ecf20Sopenharmony_ci "BDQ PBL addr=0x%p dma=%pad\n", 29848c2ecf20Sopenharmony_ci qedf->bdq_pbl, &qedf->bdq_pbl_dma); 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci /* 29878c2ecf20Sopenharmony_ci * Populate BDQ PBL with physical and virtual address of individual 29888c2ecf20Sopenharmony_ci * BDQ buffers 29898c2ecf20Sopenharmony_ci */ 29908c2ecf20Sopenharmony_ci pbl = (struct scsi_bd *)qedf->bdq_pbl; 29918c2ecf20Sopenharmony_ci for (i = 0; i < QEDF_BDQ_SIZE; i++) { 29928c2ecf20Sopenharmony_ci pbl->address.hi = cpu_to_le32(U64_HI(qedf->bdq[i].buf_dma)); 29938c2ecf20Sopenharmony_ci pbl->address.lo = cpu_to_le32(U64_LO(qedf->bdq[i].buf_dma)); 29948c2ecf20Sopenharmony_ci pbl->opaque.fcoe_opaque.hi = 0; 29958c2ecf20Sopenharmony_ci /* Opaque lo data is an index into the BDQ array */ 29968c2ecf20Sopenharmony_ci pbl->opaque.fcoe_opaque.lo = cpu_to_le32(i); 29978c2ecf20Sopenharmony_ci pbl++; 29988c2ecf20Sopenharmony_ci } 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci /* Allocate list of PBL pages */ 30018c2ecf20Sopenharmony_ci qedf->bdq_pbl_list = dma_alloc_coherent(&qedf->pdev->dev, 30028c2ecf20Sopenharmony_ci QEDF_PAGE_SIZE, 30038c2ecf20Sopenharmony_ci &qedf->bdq_pbl_list_dma, 30048c2ecf20Sopenharmony_ci GFP_KERNEL); 30058c2ecf20Sopenharmony_ci if (!qedf->bdq_pbl_list) { 30068c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate list of PBL pages.\n"); 30078c2ecf20Sopenharmony_ci return -ENOMEM; 30088c2ecf20Sopenharmony_ci } 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci /* 30118c2ecf20Sopenharmony_ci * Now populate PBL list with pages that contain pointers to the 30128c2ecf20Sopenharmony_ci * individual buffers. 30138c2ecf20Sopenharmony_ci */ 30148c2ecf20Sopenharmony_ci qedf->bdq_pbl_list_num_entries = qedf->bdq_pbl_mem_size / 30158c2ecf20Sopenharmony_ci QEDF_PAGE_SIZE; 30168c2ecf20Sopenharmony_ci list = (u64 *)qedf->bdq_pbl_list; 30178c2ecf20Sopenharmony_ci page = qedf->bdq_pbl_list_dma; 30188c2ecf20Sopenharmony_ci for (i = 0; i < qedf->bdq_pbl_list_num_entries; i++) { 30198c2ecf20Sopenharmony_ci *list = qedf->bdq_pbl_dma; 30208c2ecf20Sopenharmony_ci list++; 30218c2ecf20Sopenharmony_ci page += QEDF_PAGE_SIZE; 30228c2ecf20Sopenharmony_ci } 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci return 0; 30258c2ecf20Sopenharmony_ci} 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_cistatic int qedf_alloc_global_queues(struct qedf_ctx *qedf) 30288c2ecf20Sopenharmony_ci{ 30298c2ecf20Sopenharmony_ci u32 *list; 30308c2ecf20Sopenharmony_ci int i; 30318c2ecf20Sopenharmony_ci int status; 30328c2ecf20Sopenharmony_ci u32 *pbl; 30338c2ecf20Sopenharmony_ci dma_addr_t page; 30348c2ecf20Sopenharmony_ci int num_pages; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci /* Allocate and map CQs, RQs */ 30378c2ecf20Sopenharmony_ci /* 30388c2ecf20Sopenharmony_ci * Number of global queues (CQ / RQ). This should 30398c2ecf20Sopenharmony_ci * be <= number of available MSIX vectors for the PF 30408c2ecf20Sopenharmony_ci */ 30418c2ecf20Sopenharmony_ci if (!qedf->num_queues) { 30428c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "No MSI-X vectors available!\n"); 30438c2ecf20Sopenharmony_ci return -ENOMEM; 30448c2ecf20Sopenharmony_ci } 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci /* 30478c2ecf20Sopenharmony_ci * Make sure we allocated the PBL that will contain the physical 30488c2ecf20Sopenharmony_ci * addresses of our queues 30498c2ecf20Sopenharmony_ci */ 30508c2ecf20Sopenharmony_ci if (!qedf->p_cpuq) { 30518c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "p_cpuq is NULL.\n"); 30528c2ecf20Sopenharmony_ci return -EINVAL; 30538c2ecf20Sopenharmony_ci } 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci qedf->global_queues = kzalloc((sizeof(struct global_queue *) 30568c2ecf20Sopenharmony_ci * qedf->num_queues), GFP_KERNEL); 30578c2ecf20Sopenharmony_ci if (!qedf->global_queues) { 30588c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate global " 30598c2ecf20Sopenharmony_ci "queues array ptr memory\n"); 30608c2ecf20Sopenharmony_ci return -ENOMEM; 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 30638c2ecf20Sopenharmony_ci "qedf->global_queues=%p.\n", qedf->global_queues); 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci /* Allocate DMA coherent buffers for BDQ */ 30668c2ecf20Sopenharmony_ci status = qedf_alloc_bdq(qedf); 30678c2ecf20Sopenharmony_ci if (status) { 30688c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Unable to allocate bdq.\n"); 30698c2ecf20Sopenharmony_ci goto mem_alloc_failure; 30708c2ecf20Sopenharmony_ci } 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci /* Allocate a CQ and an associated PBL for each MSI-X vector */ 30738c2ecf20Sopenharmony_ci for (i = 0; i < qedf->num_queues; i++) { 30748c2ecf20Sopenharmony_ci qedf->global_queues[i] = kzalloc(sizeof(struct global_queue), 30758c2ecf20Sopenharmony_ci GFP_KERNEL); 30768c2ecf20Sopenharmony_ci if (!qedf->global_queues[i]) { 30778c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Unable to allocate " 30788c2ecf20Sopenharmony_ci "global queue %d.\n", i); 30798c2ecf20Sopenharmony_ci status = -ENOMEM; 30808c2ecf20Sopenharmony_ci goto mem_alloc_failure; 30818c2ecf20Sopenharmony_ci } 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_mem_size = 30848c2ecf20Sopenharmony_ci FCOE_PARAMS_CQ_NUM_ENTRIES * sizeof(struct fcoe_cqe); 30858c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_mem_size = 30868c2ecf20Sopenharmony_ci ALIGN(qedf->global_queues[i]->cq_mem_size, QEDF_PAGE_SIZE); 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_pbl_size = 30898c2ecf20Sopenharmony_ci (qedf->global_queues[i]->cq_mem_size / 30908c2ecf20Sopenharmony_ci PAGE_SIZE) * sizeof(void *); 30918c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_pbl_size = 30928c2ecf20Sopenharmony_ci ALIGN(qedf->global_queues[i]->cq_pbl_size, QEDF_PAGE_SIZE); 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq = 30958c2ecf20Sopenharmony_ci dma_alloc_coherent(&qedf->pdev->dev, 30968c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_mem_size, 30978c2ecf20Sopenharmony_ci &qedf->global_queues[i]->cq_dma, 30988c2ecf20Sopenharmony_ci GFP_KERNEL); 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci if (!qedf->global_queues[i]->cq) { 31018c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate cq.\n"); 31028c2ecf20Sopenharmony_ci status = -ENOMEM; 31038c2ecf20Sopenharmony_ci goto mem_alloc_failure; 31048c2ecf20Sopenharmony_ci } 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_pbl = 31078c2ecf20Sopenharmony_ci dma_alloc_coherent(&qedf->pdev->dev, 31088c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_pbl_size, 31098c2ecf20Sopenharmony_ci &qedf->global_queues[i]->cq_pbl_dma, 31108c2ecf20Sopenharmony_ci GFP_KERNEL); 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci if (!qedf->global_queues[i]->cq_pbl) { 31138c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate cq PBL.\n"); 31148c2ecf20Sopenharmony_ci status = -ENOMEM; 31158c2ecf20Sopenharmony_ci goto mem_alloc_failure; 31168c2ecf20Sopenharmony_ci } 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci /* Create PBL */ 31198c2ecf20Sopenharmony_ci num_pages = qedf->global_queues[i]->cq_mem_size / 31208c2ecf20Sopenharmony_ci QEDF_PAGE_SIZE; 31218c2ecf20Sopenharmony_ci page = qedf->global_queues[i]->cq_dma; 31228c2ecf20Sopenharmony_ci pbl = (u32 *)qedf->global_queues[i]->cq_pbl; 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci while (num_pages--) { 31258c2ecf20Sopenharmony_ci *pbl = U64_LO(page); 31268c2ecf20Sopenharmony_ci pbl++; 31278c2ecf20Sopenharmony_ci *pbl = U64_HI(page); 31288c2ecf20Sopenharmony_ci pbl++; 31298c2ecf20Sopenharmony_ci page += QEDF_PAGE_SIZE; 31308c2ecf20Sopenharmony_ci } 31318c2ecf20Sopenharmony_ci /* Set the initial consumer index for cq */ 31328c2ecf20Sopenharmony_ci qedf->global_queues[i]->cq_cons_idx = 0; 31338c2ecf20Sopenharmony_ci } 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci list = (u32 *)qedf->p_cpuq; 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci /* 31388c2ecf20Sopenharmony_ci * The list is built as follows: CQ#0 PBL pointer, RQ#0 PBL pointer, 31398c2ecf20Sopenharmony_ci * CQ#1 PBL pointer, RQ#1 PBL pointer, etc. Each PBL pointer points 31408c2ecf20Sopenharmony_ci * to the physical address which contains an array of pointers to 31418c2ecf20Sopenharmony_ci * the physical addresses of the specific queue pages. 31428c2ecf20Sopenharmony_ci */ 31438c2ecf20Sopenharmony_ci for (i = 0; i < qedf->num_queues; i++) { 31448c2ecf20Sopenharmony_ci *list = U64_LO(qedf->global_queues[i]->cq_pbl_dma); 31458c2ecf20Sopenharmony_ci list++; 31468c2ecf20Sopenharmony_ci *list = U64_HI(qedf->global_queues[i]->cq_pbl_dma); 31478c2ecf20Sopenharmony_ci list++; 31488c2ecf20Sopenharmony_ci *list = U64_LO(0); 31498c2ecf20Sopenharmony_ci list++; 31508c2ecf20Sopenharmony_ci *list = U64_HI(0); 31518c2ecf20Sopenharmony_ci list++; 31528c2ecf20Sopenharmony_ci } 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci return 0; 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_cimem_alloc_failure: 31578c2ecf20Sopenharmony_ci qedf_free_global_queues(qedf); 31588c2ecf20Sopenharmony_ci return status; 31598c2ecf20Sopenharmony_ci} 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_cistatic int qedf_set_fcoe_pf_param(struct qedf_ctx *qedf) 31628c2ecf20Sopenharmony_ci{ 31638c2ecf20Sopenharmony_ci u8 sq_num_pbl_pages; 31648c2ecf20Sopenharmony_ci u32 sq_mem_size; 31658c2ecf20Sopenharmony_ci u32 cq_mem_size; 31668c2ecf20Sopenharmony_ci u32 cq_num_entries; 31678c2ecf20Sopenharmony_ci int rval; 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci /* 31708c2ecf20Sopenharmony_ci * The number of completion queues/fastpath interrupts/status blocks 31718c2ecf20Sopenharmony_ci * we allocation is the minimum off: 31728c2ecf20Sopenharmony_ci * 31738c2ecf20Sopenharmony_ci * Number of CPUs 31748c2ecf20Sopenharmony_ci * Number allocated by qed for our PCI function 31758c2ecf20Sopenharmony_ci */ 31768c2ecf20Sopenharmony_ci qedf->num_queues = MIN_NUM_CPUS_MSIX(qedf); 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of CQs is %d.\n", 31798c2ecf20Sopenharmony_ci qedf->num_queues); 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_ci qedf->p_cpuq = dma_alloc_coherent(&qedf->pdev->dev, 31828c2ecf20Sopenharmony_ci qedf->num_queues * sizeof(struct qedf_glbl_q_params), 31838c2ecf20Sopenharmony_ci &qedf->hw_p_cpuq, GFP_KERNEL); 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci if (!qedf->p_cpuq) { 31868c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "dma_alloc_coherent failed.\n"); 31878c2ecf20Sopenharmony_ci return 1; 31888c2ecf20Sopenharmony_ci } 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci rval = qedf_alloc_global_queues(qedf); 31918c2ecf20Sopenharmony_ci if (rval) { 31928c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Global queue allocation " 31938c2ecf20Sopenharmony_ci "failed.\n"); 31948c2ecf20Sopenharmony_ci return 1; 31958c2ecf20Sopenharmony_ci } 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_ci /* Calculate SQ PBL size in the same manner as in qedf_sq_alloc() */ 31988c2ecf20Sopenharmony_ci sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe); 31998c2ecf20Sopenharmony_ci sq_mem_size = ALIGN(sq_mem_size, QEDF_PAGE_SIZE); 32008c2ecf20Sopenharmony_ci sq_num_pbl_pages = (sq_mem_size / QEDF_PAGE_SIZE); 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci /* Calculate CQ num entries */ 32038c2ecf20Sopenharmony_ci cq_mem_size = FCOE_PARAMS_CQ_NUM_ENTRIES * sizeof(struct fcoe_cqe); 32048c2ecf20Sopenharmony_ci cq_mem_size = ALIGN(cq_mem_size, QEDF_PAGE_SIZE); 32058c2ecf20Sopenharmony_ci cq_num_entries = cq_mem_size / sizeof(struct fcoe_cqe); 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ci memset(&(qedf->pf_params), 0, sizeof(qedf->pf_params)); 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci /* Setup the value for fcoe PF */ 32108c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.num_cons = QEDF_MAX_SESSIONS; 32118c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.num_tasks = FCOE_PARAMS_NUM_TASKS; 32128c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.glbl_q_params_addr = 32138c2ecf20Sopenharmony_ci (u64)qedf->hw_p_cpuq; 32148c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.sq_num_pbl_pages = sq_num_pbl_pages; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.rq_buffer_log_size = 0; 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.cq_num_entries = cq_num_entries; 32198c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.num_cqs = qedf->num_queues; 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci /* log_page_size: 12 for 4KB pages */ 32228c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.log_page_size = ilog2(QEDF_PAGE_SIZE); 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.mtu = 9000; 32258c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.gl_rq_pi = QEDF_FCOE_PARAMS_GL_RQ_PI; 32268c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.gl_cmd_pi = QEDF_FCOE_PARAMS_GL_CMD_PI; 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci /* BDQ address and size */ 32298c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.bdq_pbl_base_addr[0] = 32308c2ecf20Sopenharmony_ci qedf->bdq_pbl_list_dma; 32318c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.bdq_pbl_num_entries[0] = 32328c2ecf20Sopenharmony_ci qedf->bdq_pbl_list_num_entries; 32338c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.rq_buffer_size = QEDF_BDQ_BUF_SIZE; 32348c2ecf20Sopenharmony_ci 32358c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 32368c2ecf20Sopenharmony_ci "bdq_list=%p bdq_pbl_list_dma=%llx bdq_pbl_list_entries=%d.\n", 32378c2ecf20Sopenharmony_ci qedf->bdq_pbl_list, 32388c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.bdq_pbl_base_addr[0], 32398c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.bdq_pbl_num_entries[0]); 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 32428c2ecf20Sopenharmony_ci "cq_num_entries=%d.\n", 32438c2ecf20Sopenharmony_ci qedf->pf_params.fcoe_pf_params.cq_num_entries); 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_ci return 0; 32468c2ecf20Sopenharmony_ci} 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci/* Free DMA coherent memory for array of queue pointers we pass to qed */ 32498c2ecf20Sopenharmony_cistatic void qedf_free_fcoe_pf_param(struct qedf_ctx *qedf) 32508c2ecf20Sopenharmony_ci{ 32518c2ecf20Sopenharmony_ci size_t size = 0; 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_ci if (qedf->p_cpuq) { 32548c2ecf20Sopenharmony_ci size = qedf->num_queues * sizeof(struct qedf_glbl_q_params); 32558c2ecf20Sopenharmony_ci dma_free_coherent(&qedf->pdev->dev, size, qedf->p_cpuq, 32568c2ecf20Sopenharmony_ci qedf->hw_p_cpuq); 32578c2ecf20Sopenharmony_ci } 32588c2ecf20Sopenharmony_ci 32598c2ecf20Sopenharmony_ci qedf_free_global_queues(qedf); 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_ci kfree(qedf->global_queues); 32628c2ecf20Sopenharmony_ci} 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci/* 32658c2ecf20Sopenharmony_ci * PCI driver functions 32668c2ecf20Sopenharmony_ci */ 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_cistatic const struct pci_device_id qedf_pci_tbl[] = { 32698c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x165c) }, 32708c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x8080) }, 32718c2ecf20Sopenharmony_ci {0} 32728c2ecf20Sopenharmony_ci}; 32738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, qedf_pci_tbl); 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_cistatic struct pci_driver qedf_pci_driver = { 32768c2ecf20Sopenharmony_ci .name = QEDF_MODULE_NAME, 32778c2ecf20Sopenharmony_ci .id_table = qedf_pci_tbl, 32788c2ecf20Sopenharmony_ci .probe = qedf_probe, 32798c2ecf20Sopenharmony_ci .remove = qedf_remove, 32808c2ecf20Sopenharmony_ci .shutdown = qedf_shutdown, 32818c2ecf20Sopenharmony_ci .suspend = qedf_suspend, 32828c2ecf20Sopenharmony_ci}; 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_cistatic int __qedf_probe(struct pci_dev *pdev, int mode) 32858c2ecf20Sopenharmony_ci{ 32868c2ecf20Sopenharmony_ci int rc = -EINVAL; 32878c2ecf20Sopenharmony_ci struct fc_lport *lport; 32888c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = NULL; 32898c2ecf20Sopenharmony_ci struct Scsi_Host *host; 32908c2ecf20Sopenharmony_ci bool is_vf = false; 32918c2ecf20Sopenharmony_ci struct qed_ll2_params params; 32928c2ecf20Sopenharmony_ci char host_buf[20]; 32938c2ecf20Sopenharmony_ci struct qed_link_params link_params; 32948c2ecf20Sopenharmony_ci int status; 32958c2ecf20Sopenharmony_ci void *task_start, *task_end; 32968c2ecf20Sopenharmony_ci struct qed_slowpath_params slowpath_params; 32978c2ecf20Sopenharmony_ci struct qed_probe_params qed_params; 32988c2ecf20Sopenharmony_ci u16 retry_cnt = 10; 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_ci /* 33018c2ecf20Sopenharmony_ci * When doing error recovery we didn't reap the lport so don't try 33028c2ecf20Sopenharmony_ci * to reallocate it. 33038c2ecf20Sopenharmony_ci */ 33048c2ecf20Sopenharmony_ciretry_probe: 33058c2ecf20Sopenharmony_ci if (mode == QEDF_MODE_RECOVERY) 33068c2ecf20Sopenharmony_ci msleep(2000); 33078c2ecf20Sopenharmony_ci 33088c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 33098c2ecf20Sopenharmony_ci lport = libfc_host_alloc(&qedf_host_template, 33108c2ecf20Sopenharmony_ci sizeof(struct qedf_ctx)); 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci if (!lport) { 33138c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Could not allocate lport.\n"); 33148c2ecf20Sopenharmony_ci rc = -ENOMEM; 33158c2ecf20Sopenharmony_ci goto err0; 33168c2ecf20Sopenharmony_ci } 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci fc_disc_init(lport); 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci /* Initialize qedf_ctx */ 33218c2ecf20Sopenharmony_ci qedf = lport_priv(lport); 33228c2ecf20Sopenharmony_ci set_bit(QEDF_PROBING, &qedf->flags); 33238c2ecf20Sopenharmony_ci qedf->lport = lport; 33248c2ecf20Sopenharmony_ci qedf->ctlr.lp = lport; 33258c2ecf20Sopenharmony_ci qedf->pdev = pdev; 33268c2ecf20Sopenharmony_ci qedf->dbg_ctx.pdev = pdev; 33278c2ecf20Sopenharmony_ci qedf->dbg_ctx.host_no = lport->host->host_no; 33288c2ecf20Sopenharmony_ci spin_lock_init(&qedf->hba_lock); 33298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&qedf->fcports); 33308c2ecf20Sopenharmony_ci qedf->curr_conn_id = QEDF_MAX_SESSIONS - 1; 33318c2ecf20Sopenharmony_ci atomic_set(&qedf->num_offloads, 0); 33328c2ecf20Sopenharmony_ci qedf->stop_io_on_error = false; 33338c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, qedf); 33348c2ecf20Sopenharmony_ci init_completion(&qedf->fipvlan_compl); 33358c2ecf20Sopenharmony_ci mutex_init(&qedf->stats_mutex); 33368c2ecf20Sopenharmony_ci mutex_init(&qedf->flush_mutex); 33378c2ecf20Sopenharmony_ci qedf->flogi_pending = 0; 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, 33408c2ecf20Sopenharmony_ci "QLogic FastLinQ FCoE Module qedf %s, " 33418c2ecf20Sopenharmony_ci "FW %d.%d.%d.%d\n", QEDF_VERSION, 33428c2ecf20Sopenharmony_ci FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION, 33438c2ecf20Sopenharmony_ci FW_ENGINEERING_VERSION); 33448c2ecf20Sopenharmony_ci } else { 33458c2ecf20Sopenharmony_ci /* Init pointers during recovery */ 33468c2ecf20Sopenharmony_ci qedf = pci_get_drvdata(pdev); 33478c2ecf20Sopenharmony_ci set_bit(QEDF_PROBING, &qedf->flags); 33488c2ecf20Sopenharmony_ci lport = qedf->lport; 33498c2ecf20Sopenharmony_ci } 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe started.\n"); 33528c2ecf20Sopenharmony_ci 33538c2ecf20Sopenharmony_ci host = lport->host; 33548c2ecf20Sopenharmony_ci 33558c2ecf20Sopenharmony_ci /* Allocate mempool for qedf_io_work structs */ 33568c2ecf20Sopenharmony_ci qedf->io_mempool = mempool_create_slab_pool(QEDF_IO_WORK_MIN, 33578c2ecf20Sopenharmony_ci qedf_io_work_cache); 33588c2ecf20Sopenharmony_ci if (qedf->io_mempool == NULL) { 33598c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "qedf->io_mempool is NULL.\n"); 33608c2ecf20Sopenharmony_ci goto err1; 33618c2ecf20Sopenharmony_ci } 33628c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, "qedf->io_mempool=%p.\n", 33638c2ecf20Sopenharmony_ci qedf->io_mempool); 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci sprintf(host_buf, "qedf_%u_link", 33668c2ecf20Sopenharmony_ci qedf->lport->host->host_no); 33678c2ecf20Sopenharmony_ci qedf->link_update_wq = create_workqueue(host_buf); 33688c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update); 33698c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery); 33708c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&qedf->grcdump_work, qedf_wq_grcdump); 33718c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&qedf->stag_work, qedf_stag_change_work); 33728c2ecf20Sopenharmony_ci qedf->fipvlan_retries = qedf_fipvlan_retries; 33738c2ecf20Sopenharmony_ci /* Set a default prio in case DCBX doesn't converge */ 33748c2ecf20Sopenharmony_ci if (qedf_default_prio > -1) { 33758c2ecf20Sopenharmony_ci /* 33768c2ecf20Sopenharmony_ci * This is the case where we pass a modparam in so we want to 33778c2ecf20Sopenharmony_ci * honor it even if dcbx doesn't converge. 33788c2ecf20Sopenharmony_ci */ 33798c2ecf20Sopenharmony_ci qedf->prio = qedf_default_prio; 33808c2ecf20Sopenharmony_ci } else 33818c2ecf20Sopenharmony_ci qedf->prio = QEDF_DEFAULT_PRIO; 33828c2ecf20Sopenharmony_ci 33838c2ecf20Sopenharmony_ci /* 33848c2ecf20Sopenharmony_ci * Common probe. Takes care of basic hardware init and pci_* 33858c2ecf20Sopenharmony_ci * functions. 33868c2ecf20Sopenharmony_ci */ 33878c2ecf20Sopenharmony_ci memset(&qed_params, 0, sizeof(qed_params)); 33888c2ecf20Sopenharmony_ci qed_params.protocol = QED_PROTOCOL_FCOE; 33898c2ecf20Sopenharmony_ci qed_params.dp_module = qedf_dp_module; 33908c2ecf20Sopenharmony_ci qed_params.dp_level = qedf_dp_level; 33918c2ecf20Sopenharmony_ci qed_params.is_vf = is_vf; 33928c2ecf20Sopenharmony_ci qedf->cdev = qed_ops->common->probe(pdev, &qed_params); 33938c2ecf20Sopenharmony_ci if (!qedf->cdev) { 33948c2ecf20Sopenharmony_ci if ((mode == QEDF_MODE_RECOVERY) && retry_cnt) { 33958c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 33968c2ecf20Sopenharmony_ci "Retry %d initialize hardware\n", retry_cnt); 33978c2ecf20Sopenharmony_ci retry_cnt--; 33988c2ecf20Sopenharmony_ci goto retry_probe; 33998c2ecf20Sopenharmony_ci } 34008c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "common probe failed.\n"); 34018c2ecf20Sopenharmony_ci rc = -ENODEV; 34028c2ecf20Sopenharmony_ci goto err1; 34038c2ecf20Sopenharmony_ci } 34048c2ecf20Sopenharmony_ci 34058c2ecf20Sopenharmony_ci /* Learn information crucial for qedf to progress */ 34068c2ecf20Sopenharmony_ci rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info); 34078c2ecf20Sopenharmony_ci if (rc) { 34088c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Failed to dev info.\n"); 34098c2ecf20Sopenharmony_ci goto err1; 34108c2ecf20Sopenharmony_ci } 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, 34138c2ecf20Sopenharmony_ci "dev_info: num_hwfns=%d affin_hwfn_idx=%d.\n", 34148c2ecf20Sopenharmony_ci qedf->dev_info.common.num_hwfns, 34158c2ecf20Sopenharmony_ci qed_ops->common->get_affin_hwfn_idx(qedf->cdev)); 34168c2ecf20Sopenharmony_ci 34178c2ecf20Sopenharmony_ci /* queue allocation code should come here 34188c2ecf20Sopenharmony_ci * order should be 34198c2ecf20Sopenharmony_ci * slowpath_start 34208c2ecf20Sopenharmony_ci * status block allocation 34218c2ecf20Sopenharmony_ci * interrupt registration (to get min number of queues) 34228c2ecf20Sopenharmony_ci * set_fcoe_pf_param 34238c2ecf20Sopenharmony_ci * qed_sp_fcoe_func_start 34248c2ecf20Sopenharmony_ci */ 34258c2ecf20Sopenharmony_ci rc = qedf_set_fcoe_pf_param(qedf); 34268c2ecf20Sopenharmony_ci if (rc) { 34278c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Cannot set fcoe pf param.\n"); 34288c2ecf20Sopenharmony_ci goto err2; 34298c2ecf20Sopenharmony_ci } 34308c2ecf20Sopenharmony_ci qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params); 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci /* Learn information crucial for qedf to progress */ 34338c2ecf20Sopenharmony_ci rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info); 34348c2ecf20Sopenharmony_ci if (rc) { 34358c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Failed to fill dev info.\n"); 34368c2ecf20Sopenharmony_ci goto err2; 34378c2ecf20Sopenharmony_ci } 34388c2ecf20Sopenharmony_ci 34398c2ecf20Sopenharmony_ci /* Record BDQ producer doorbell addresses */ 34408c2ecf20Sopenharmony_ci qedf->bdq_primary_prod = qedf->dev_info.primary_dbq_rq_addr; 34418c2ecf20Sopenharmony_ci qedf->bdq_secondary_prod = qedf->dev_info.secondary_bdq_rq_addr; 34428c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 34438c2ecf20Sopenharmony_ci "BDQ primary_prod=%p secondary_prod=%p.\n", qedf->bdq_primary_prod, 34448c2ecf20Sopenharmony_ci qedf->bdq_secondary_prod); 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci qed_ops->register_ops(qedf->cdev, &qedf_cb_ops, qedf); 34478c2ecf20Sopenharmony_ci 34488c2ecf20Sopenharmony_ci rc = qedf_prepare_sb(qedf); 34498c2ecf20Sopenharmony_ci if (rc) { 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n"); 34528c2ecf20Sopenharmony_ci goto err2; 34538c2ecf20Sopenharmony_ci } 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci /* Start the Slowpath-process */ 34568c2ecf20Sopenharmony_ci slowpath_params.int_mode = QED_INT_MODE_MSIX; 34578c2ecf20Sopenharmony_ci slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER; 34588c2ecf20Sopenharmony_ci slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER; 34598c2ecf20Sopenharmony_ci slowpath_params.drv_rev = QEDF_DRIVER_REV_VER; 34608c2ecf20Sopenharmony_ci slowpath_params.drv_eng = QEDF_DRIVER_ENG_VER; 34618c2ecf20Sopenharmony_ci strncpy(slowpath_params.name, "qedf", QED_DRV_VER_STR_SIZE); 34628c2ecf20Sopenharmony_ci rc = qed_ops->common->slowpath_start(qedf->cdev, &slowpath_params); 34638c2ecf20Sopenharmony_ci if (rc) { 34648c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n"); 34658c2ecf20Sopenharmony_ci goto err2; 34668c2ecf20Sopenharmony_ci } 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci /* 34698c2ecf20Sopenharmony_ci * update_pf_params needs to be called before and after slowpath 34708c2ecf20Sopenharmony_ci * start 34718c2ecf20Sopenharmony_ci */ 34728c2ecf20Sopenharmony_ci qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params); 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci /* Setup interrupts */ 34758c2ecf20Sopenharmony_ci rc = qedf_setup_int(qedf); 34768c2ecf20Sopenharmony_ci if (rc) { 34778c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Setup interrupts failed.\n"); 34788c2ecf20Sopenharmony_ci goto err3; 34798c2ecf20Sopenharmony_ci } 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci rc = qed_ops->start(qedf->cdev, &qedf->tasks); 34828c2ecf20Sopenharmony_ci if (rc) { 34838c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Cannot start FCoE function.\n"); 34848c2ecf20Sopenharmony_ci goto err4; 34858c2ecf20Sopenharmony_ci } 34868c2ecf20Sopenharmony_ci task_start = qedf_get_task_mem(&qedf->tasks, 0); 34878c2ecf20Sopenharmony_ci task_end = qedf_get_task_mem(&qedf->tasks, MAX_TID_BLOCKS_FCOE - 1); 34888c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Task context start=%p, " 34898c2ecf20Sopenharmony_ci "end=%p block_size=%u.\n", task_start, task_end, 34908c2ecf20Sopenharmony_ci qedf->tasks.size); 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_ci /* 34938c2ecf20Sopenharmony_ci * We need to write the number of BDs in the BDQ we've preallocated so 34948c2ecf20Sopenharmony_ci * the f/w will do a prefetch and we'll get an unsolicited CQE when a 34958c2ecf20Sopenharmony_ci * packet arrives. 34968c2ecf20Sopenharmony_ci */ 34978c2ecf20Sopenharmony_ci qedf->bdq_prod_idx = QEDF_BDQ_SIZE; 34988c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 34998c2ecf20Sopenharmony_ci "Writing %d to primary and secondary BDQ doorbell registers.\n", 35008c2ecf20Sopenharmony_ci qedf->bdq_prod_idx); 35018c2ecf20Sopenharmony_ci writew(qedf->bdq_prod_idx, qedf->bdq_primary_prod); 35028c2ecf20Sopenharmony_ci readw(qedf->bdq_primary_prod); 35038c2ecf20Sopenharmony_ci writew(qedf->bdq_prod_idx, qedf->bdq_secondary_prod); 35048c2ecf20Sopenharmony_ci readw(qedf->bdq_secondary_prod); 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci qed_ops->common->set_power_state(qedf->cdev, PCI_D0); 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_ci /* Now that the dev_info struct has been filled in set the MAC 35098c2ecf20Sopenharmony_ci * address 35108c2ecf20Sopenharmony_ci */ 35118c2ecf20Sopenharmony_ci ether_addr_copy(qedf->mac, qedf->dev_info.common.hw_mac); 35128c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "MAC address is %pM.\n", 35138c2ecf20Sopenharmony_ci qedf->mac); 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci /* 35168c2ecf20Sopenharmony_ci * Set the WWNN and WWPN in the following way: 35178c2ecf20Sopenharmony_ci * 35188c2ecf20Sopenharmony_ci * If the info we get from qed is non-zero then use that to set the 35198c2ecf20Sopenharmony_ci * WWPN and WWNN. Otherwise fall back to use fcoe_wwn_from_mac() based 35208c2ecf20Sopenharmony_ci * on the MAC address. 35218c2ecf20Sopenharmony_ci */ 35228c2ecf20Sopenharmony_ci if (qedf->dev_info.wwnn != 0 && qedf->dev_info.wwpn != 0) { 35238c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 35248c2ecf20Sopenharmony_ci "Setting WWPN and WWNN from qed dev_info.\n"); 35258c2ecf20Sopenharmony_ci qedf->wwnn = qedf->dev_info.wwnn; 35268c2ecf20Sopenharmony_ci qedf->wwpn = qedf->dev_info.wwpn; 35278c2ecf20Sopenharmony_ci } else { 35288c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 35298c2ecf20Sopenharmony_ci "Setting WWPN and WWNN using fcoe_wwn_from_mac().\n"); 35308c2ecf20Sopenharmony_ci qedf->wwnn = fcoe_wwn_from_mac(qedf->mac, 1, 0); 35318c2ecf20Sopenharmony_ci qedf->wwpn = fcoe_wwn_from_mac(qedf->mac, 2, 0); 35328c2ecf20Sopenharmony_ci } 35338c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "WWNN=%016llx " 35348c2ecf20Sopenharmony_ci "WWPN=%016llx.\n", qedf->wwnn, qedf->wwpn); 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci sprintf(host_buf, "host_%d", host->host_no); 35378c2ecf20Sopenharmony_ci qed_ops->common->set_name(qedf->cdev, host_buf); 35388c2ecf20Sopenharmony_ci 35398c2ecf20Sopenharmony_ci /* Allocate cmd mgr */ 35408c2ecf20Sopenharmony_ci qedf->cmd_mgr = qedf_cmd_mgr_alloc(qedf); 35418c2ecf20Sopenharmony_ci if (!qedf->cmd_mgr) { 35428c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Failed to allocate cmd mgr.\n"); 35438c2ecf20Sopenharmony_ci rc = -ENOMEM; 35448c2ecf20Sopenharmony_ci goto err5; 35458c2ecf20Sopenharmony_ci } 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 35488c2ecf20Sopenharmony_ci host->transportt = qedf_fc_transport_template; 35498c2ecf20Sopenharmony_ci host->max_lun = qedf_max_lun; 35508c2ecf20Sopenharmony_ci host->max_cmd_len = QEDF_MAX_CDB_LEN; 35518c2ecf20Sopenharmony_ci host->can_queue = FCOE_PARAMS_NUM_TASKS; 35528c2ecf20Sopenharmony_ci rc = scsi_add_host(host, &pdev->dev); 35538c2ecf20Sopenharmony_ci if (rc) { 35548c2ecf20Sopenharmony_ci QEDF_WARN(&qedf->dbg_ctx, 35558c2ecf20Sopenharmony_ci "Error adding Scsi_Host rc=0x%x.\n", rc); 35568c2ecf20Sopenharmony_ci goto err6; 35578c2ecf20Sopenharmony_ci } 35588c2ecf20Sopenharmony_ci } 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 35618c2ecf20Sopenharmony_ci params.mtu = QEDF_LL2_BUF_SIZE; 35628c2ecf20Sopenharmony_ci ether_addr_copy(params.ll2_mac_address, qedf->mac); 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci /* Start LL2 processing thread */ 35658c2ecf20Sopenharmony_ci snprintf(host_buf, 20, "qedf_%d_ll2", host->host_no); 35668c2ecf20Sopenharmony_ci qedf->ll2_recv_wq = 35678c2ecf20Sopenharmony_ci create_workqueue(host_buf); 35688c2ecf20Sopenharmony_ci if (!qedf->ll2_recv_wq) { 35698c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Failed to LL2 workqueue.\n"); 35708c2ecf20Sopenharmony_ci rc = -ENOMEM; 35718c2ecf20Sopenharmony_ci goto err7; 35728c2ecf20Sopenharmony_ci } 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 35758c2ecf20Sopenharmony_ci qedf_dbg_host_init(&(qedf->dbg_ctx), qedf_debugfs_ops, 35768c2ecf20Sopenharmony_ci qedf_dbg_fops); 35778c2ecf20Sopenharmony_ci#endif 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci /* Start LL2 */ 35808c2ecf20Sopenharmony_ci qed_ops->ll2->register_cb_ops(qedf->cdev, &qedf_ll2_cb_ops, qedf); 35818c2ecf20Sopenharmony_ci rc = qed_ops->ll2->start(qedf->cdev, ¶ms); 35828c2ecf20Sopenharmony_ci if (rc) { 35838c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Could not start Light L2.\n"); 35848c2ecf20Sopenharmony_ci goto err7; 35858c2ecf20Sopenharmony_ci } 35868c2ecf20Sopenharmony_ci set_bit(QEDF_LL2_STARTED, &qedf->flags); 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci /* Set initial FIP/FCoE VLAN to NULL */ 35898c2ecf20Sopenharmony_ci qedf->vlan_id = 0; 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_ci /* 35928c2ecf20Sopenharmony_ci * No need to setup fcoe_ctlr or fc_lport objects during recovery since 35938c2ecf20Sopenharmony_ci * they were not reaped during the unload process. 35948c2ecf20Sopenharmony_ci */ 35958c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 35968c2ecf20Sopenharmony_ci /* Setup imbedded fcoe controller */ 35978c2ecf20Sopenharmony_ci qedf_fcoe_ctlr_setup(qedf); 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci /* Setup lport */ 36008c2ecf20Sopenharmony_ci rc = qedf_lport_setup(qedf); 36018c2ecf20Sopenharmony_ci if (rc) { 36028c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 36038c2ecf20Sopenharmony_ci "qedf_lport_setup failed.\n"); 36048c2ecf20Sopenharmony_ci goto err7; 36058c2ecf20Sopenharmony_ci } 36068c2ecf20Sopenharmony_ci } 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci sprintf(host_buf, "qedf_%u_timer", qedf->lport->host->host_no); 36098c2ecf20Sopenharmony_ci qedf->timer_work_queue = 36108c2ecf20Sopenharmony_ci create_workqueue(host_buf); 36118c2ecf20Sopenharmony_ci if (!qedf->timer_work_queue) { 36128c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Failed to start timer " 36138c2ecf20Sopenharmony_ci "workqueue.\n"); 36148c2ecf20Sopenharmony_ci rc = -ENOMEM; 36158c2ecf20Sopenharmony_ci goto err7; 36168c2ecf20Sopenharmony_ci } 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci /* DPC workqueue is not reaped during recovery unload */ 36198c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 36208c2ecf20Sopenharmony_ci sprintf(host_buf, "qedf_%u_dpc", 36218c2ecf20Sopenharmony_ci qedf->lport->host->host_no); 36228c2ecf20Sopenharmony_ci qedf->dpc_wq = create_workqueue(host_buf); 36238c2ecf20Sopenharmony_ci } 36248c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&qedf->recovery_work, qedf_recovery_handler); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci /* 36278c2ecf20Sopenharmony_ci * GRC dump and sysfs parameters are not reaped during the recovery 36288c2ecf20Sopenharmony_ci * unload process. 36298c2ecf20Sopenharmony_ci */ 36308c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 36318c2ecf20Sopenharmony_ci qedf->grcdump_size = 36328c2ecf20Sopenharmony_ci qed_ops->common->dbg_all_data_size(qedf->cdev); 36338c2ecf20Sopenharmony_ci if (qedf->grcdump_size) { 36348c2ecf20Sopenharmony_ci rc = qedf_alloc_grc_dump_buf(&qedf->grcdump, 36358c2ecf20Sopenharmony_ci qedf->grcdump_size); 36368c2ecf20Sopenharmony_ci if (rc) { 36378c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 36388c2ecf20Sopenharmony_ci "GRC Dump buffer alloc failed.\n"); 36398c2ecf20Sopenharmony_ci qedf->grcdump = NULL; 36408c2ecf20Sopenharmony_ci } 36418c2ecf20Sopenharmony_ci 36428c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, 36438c2ecf20Sopenharmony_ci "grcdump: addr=%p, size=%u.\n", 36448c2ecf20Sopenharmony_ci qedf->grcdump, qedf->grcdump_size); 36458c2ecf20Sopenharmony_ci } 36468c2ecf20Sopenharmony_ci qedf_create_sysfs_ctx_attr(qedf); 36478c2ecf20Sopenharmony_ci 36488c2ecf20Sopenharmony_ci /* Initialize I/O tracing for this adapter */ 36498c2ecf20Sopenharmony_ci spin_lock_init(&qedf->io_trace_lock); 36508c2ecf20Sopenharmony_ci qedf->io_trace_idx = 0; 36518c2ecf20Sopenharmony_ci } 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ci init_completion(&qedf->flogi_compl); 36548c2ecf20Sopenharmony_ci 36558c2ecf20Sopenharmony_ci status = qed_ops->common->update_drv_state(qedf->cdev, true); 36568c2ecf20Sopenharmony_ci if (status) 36578c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 36588c2ecf20Sopenharmony_ci "Failed to send drv state to MFW.\n"); 36598c2ecf20Sopenharmony_ci 36608c2ecf20Sopenharmony_ci memset(&link_params, 0, sizeof(struct qed_link_params)); 36618c2ecf20Sopenharmony_ci link_params.link_up = true; 36628c2ecf20Sopenharmony_ci status = qed_ops->common->set_link(qedf->cdev, &link_params); 36638c2ecf20Sopenharmony_ci if (status) 36648c2ecf20Sopenharmony_ci QEDF_WARN(&(qedf->dbg_ctx), "set_link failed.\n"); 36658c2ecf20Sopenharmony_ci 36668c2ecf20Sopenharmony_ci /* Start/restart discovery */ 36678c2ecf20Sopenharmony_ci if (mode == QEDF_MODE_RECOVERY) 36688c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&qedf->ctlr); 36698c2ecf20Sopenharmony_ci else 36708c2ecf20Sopenharmony_ci fc_fabric_login(lport); 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n"); 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci clear_bit(QEDF_PROBING, &qedf->flags); 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci /* All good */ 36778c2ecf20Sopenharmony_ci return 0; 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_cierr7: 36808c2ecf20Sopenharmony_ci if (qedf->ll2_recv_wq) 36818c2ecf20Sopenharmony_ci destroy_workqueue(qedf->ll2_recv_wq); 36828c2ecf20Sopenharmony_ci fc_remove_host(qedf->lport->host); 36838c2ecf20Sopenharmony_ci scsi_remove_host(qedf->lport->host); 36848c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 36858c2ecf20Sopenharmony_ci qedf_dbg_host_exit(&(qedf->dbg_ctx)); 36868c2ecf20Sopenharmony_ci#endif 36878c2ecf20Sopenharmony_cierr6: 36888c2ecf20Sopenharmony_ci qedf_cmd_mgr_free(qedf->cmd_mgr); 36898c2ecf20Sopenharmony_cierr5: 36908c2ecf20Sopenharmony_ci qed_ops->stop(qedf->cdev); 36918c2ecf20Sopenharmony_cierr4: 36928c2ecf20Sopenharmony_ci qedf_free_fcoe_pf_param(qedf); 36938c2ecf20Sopenharmony_ci qedf_sync_free_irqs(qedf); 36948c2ecf20Sopenharmony_cierr3: 36958c2ecf20Sopenharmony_ci qed_ops->common->slowpath_stop(qedf->cdev); 36968c2ecf20Sopenharmony_cierr2: 36978c2ecf20Sopenharmony_ci qed_ops->common->remove(qedf->cdev); 36988c2ecf20Sopenharmony_cierr1: 36998c2ecf20Sopenharmony_ci scsi_host_put(lport->host); 37008c2ecf20Sopenharmony_cierr0: 37018c2ecf20Sopenharmony_ci return rc; 37028c2ecf20Sopenharmony_ci} 37038c2ecf20Sopenharmony_ci 37048c2ecf20Sopenharmony_cistatic int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id) 37058c2ecf20Sopenharmony_ci{ 37068c2ecf20Sopenharmony_ci return __qedf_probe(pdev, QEDF_MODE_NORMAL); 37078c2ecf20Sopenharmony_ci} 37088c2ecf20Sopenharmony_ci 37098c2ecf20Sopenharmony_cistatic void __qedf_remove(struct pci_dev *pdev, int mode) 37108c2ecf20Sopenharmony_ci{ 37118c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 37128c2ecf20Sopenharmony_ci int rc; 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci if (!pdev) { 37158c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "pdev is NULL.\n"); 37168c2ecf20Sopenharmony_ci return; 37178c2ecf20Sopenharmony_ci } 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci qedf = pci_get_drvdata(pdev); 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci /* 37228c2ecf20Sopenharmony_ci * Prevent race where we're in board disable work and then try to 37238c2ecf20Sopenharmony_ci * rmmod the module. 37248c2ecf20Sopenharmony_ci */ 37258c2ecf20Sopenharmony_ci if (test_bit(QEDF_UNLOADING, &qedf->flags)) { 37268c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Already removing PCI function.\n"); 37278c2ecf20Sopenharmony_ci return; 37288c2ecf20Sopenharmony_ci } 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) 37318c2ecf20Sopenharmony_ci set_bit(QEDF_UNLOADING, &qedf->flags); 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci /* Logoff the fabric to upload all connections */ 37348c2ecf20Sopenharmony_ci if (mode == QEDF_MODE_RECOVERY) 37358c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(&qedf->ctlr); 37368c2ecf20Sopenharmony_ci else 37378c2ecf20Sopenharmony_ci fc_fabric_logoff(qedf->lport); 37388c2ecf20Sopenharmony_ci 37398c2ecf20Sopenharmony_ci if (qedf_wait_for_upload(qedf) == false) 37408c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Could not upload all sessions.\n"); 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 37438c2ecf20Sopenharmony_ci qedf_dbg_host_exit(&(qedf->dbg_ctx)); 37448c2ecf20Sopenharmony_ci#endif 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci /* Stop any link update handling */ 37478c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&qedf->link_update); 37488c2ecf20Sopenharmony_ci destroy_workqueue(qedf->link_update_wq); 37498c2ecf20Sopenharmony_ci qedf->link_update_wq = NULL; 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci if (qedf->timer_work_queue) 37528c2ecf20Sopenharmony_ci destroy_workqueue(qedf->timer_work_queue); 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ci /* Stop Light L2 */ 37558c2ecf20Sopenharmony_ci clear_bit(QEDF_LL2_STARTED, &qedf->flags); 37568c2ecf20Sopenharmony_ci qed_ops->ll2->stop(qedf->cdev); 37578c2ecf20Sopenharmony_ci if (qedf->ll2_recv_wq) 37588c2ecf20Sopenharmony_ci destroy_workqueue(qedf->ll2_recv_wq); 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci /* Stop fastpath */ 37618c2ecf20Sopenharmony_ci qedf_sync_free_irqs(qedf); 37628c2ecf20Sopenharmony_ci qedf_destroy_sb(qedf); 37638c2ecf20Sopenharmony_ci 37648c2ecf20Sopenharmony_ci /* 37658c2ecf20Sopenharmony_ci * During recovery don't destroy OS constructs that represent the 37668c2ecf20Sopenharmony_ci * physical port. 37678c2ecf20Sopenharmony_ci */ 37688c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 37698c2ecf20Sopenharmony_ci qedf_free_grc_dump_buf(&qedf->grcdump); 37708c2ecf20Sopenharmony_ci qedf_remove_sysfs_ctx_attr(qedf); 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci /* Remove all SCSI/libfc/libfcoe structures */ 37738c2ecf20Sopenharmony_ci fcoe_ctlr_destroy(&qedf->ctlr); 37748c2ecf20Sopenharmony_ci fc_lport_destroy(qedf->lport); 37758c2ecf20Sopenharmony_ci fc_remove_host(qedf->lport->host); 37768c2ecf20Sopenharmony_ci scsi_remove_host(qedf->lport->host); 37778c2ecf20Sopenharmony_ci } 37788c2ecf20Sopenharmony_ci 37798c2ecf20Sopenharmony_ci qedf_cmd_mgr_free(qedf->cmd_mgr); 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 37828c2ecf20Sopenharmony_ci fc_exch_mgr_free(qedf->lport); 37838c2ecf20Sopenharmony_ci fc_lport_free_stats(qedf->lport); 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_ci /* Wait for all vports to be reaped */ 37868c2ecf20Sopenharmony_ci qedf_wait_for_vport_destroy(qedf); 37878c2ecf20Sopenharmony_ci } 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci /* 37908c2ecf20Sopenharmony_ci * Now that all connections have been uploaded we can stop the 37918c2ecf20Sopenharmony_ci * rest of the qed operations 37928c2ecf20Sopenharmony_ci */ 37938c2ecf20Sopenharmony_ci qed_ops->stop(qedf->cdev); 37948c2ecf20Sopenharmony_ci 37958c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 37968c2ecf20Sopenharmony_ci if (qedf->dpc_wq) { 37978c2ecf20Sopenharmony_ci /* Stop general DPC handling */ 37988c2ecf20Sopenharmony_ci destroy_workqueue(qedf->dpc_wq); 37998c2ecf20Sopenharmony_ci qedf->dpc_wq = NULL; 38008c2ecf20Sopenharmony_ci } 38018c2ecf20Sopenharmony_ci } 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci /* Final shutdown for the board */ 38048c2ecf20Sopenharmony_ci qedf_free_fcoe_pf_param(qedf); 38058c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) { 38068c2ecf20Sopenharmony_ci qed_ops->common->set_power_state(qedf->cdev, PCI_D0); 38078c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 38088c2ecf20Sopenharmony_ci } 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci rc = qed_ops->common->update_drv_state(qedf->cdev, false); 38118c2ecf20Sopenharmony_ci if (rc) 38128c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 38138c2ecf20Sopenharmony_ci "Failed to send drv state to MFW.\n"); 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ci qed_ops->common->slowpath_stop(qedf->cdev); 38168c2ecf20Sopenharmony_ci qed_ops->common->remove(qedf->cdev); 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ci mempool_destroy(qedf->io_mempool); 38198c2ecf20Sopenharmony_ci 38208c2ecf20Sopenharmony_ci /* Only reap the Scsi_host on a real removal */ 38218c2ecf20Sopenharmony_ci if (mode != QEDF_MODE_RECOVERY) 38228c2ecf20Sopenharmony_ci scsi_host_put(qedf->lport->host); 38238c2ecf20Sopenharmony_ci} 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_cistatic void qedf_remove(struct pci_dev *pdev) 38268c2ecf20Sopenharmony_ci{ 38278c2ecf20Sopenharmony_ci /* Check to make sure this function wasn't already disabled */ 38288c2ecf20Sopenharmony_ci if (!atomic_read(&pdev->enable_cnt)) 38298c2ecf20Sopenharmony_ci return; 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci __qedf_remove(pdev, QEDF_MODE_NORMAL); 38328c2ecf20Sopenharmony_ci} 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_civoid qedf_wq_grcdump(struct work_struct *work) 38358c2ecf20Sopenharmony_ci{ 38368c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = 38378c2ecf20Sopenharmony_ci container_of(work, struct qedf_ctx, grcdump_work.work); 38388c2ecf20Sopenharmony_ci 38398c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Collecting GRC dump.\n"); 38408c2ecf20Sopenharmony_ci qedf_capture_grc_dump(qedf); 38418c2ecf20Sopenharmony_ci} 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_civoid qedf_schedule_hw_err_handler(void *dev, enum qed_hw_err_type err_type) 38448c2ecf20Sopenharmony_ci{ 38458c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = dev; 38468c2ecf20Sopenharmony_ci 38478c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 38488c2ecf20Sopenharmony_ci "Hardware error handler scheduled, event=%d.\n", 38498c2ecf20Sopenharmony_ci err_type); 38508c2ecf20Sopenharmony_ci 38518c2ecf20Sopenharmony_ci if (test_bit(QEDF_IN_RECOVERY, &qedf->flags)) { 38528c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 38538c2ecf20Sopenharmony_ci "Already in recovery, not scheduling board disable work.\n"); 38548c2ecf20Sopenharmony_ci return; 38558c2ecf20Sopenharmony_ci } 38568c2ecf20Sopenharmony_ci 38578c2ecf20Sopenharmony_ci switch (err_type) { 38588c2ecf20Sopenharmony_ci case QED_HW_ERR_FAN_FAIL: 38598c2ecf20Sopenharmony_ci schedule_delayed_work(&qedf->board_disable_work, 0); 38608c2ecf20Sopenharmony_ci break; 38618c2ecf20Sopenharmony_ci case QED_HW_ERR_MFW_RESP_FAIL: 38628c2ecf20Sopenharmony_ci case QED_HW_ERR_HW_ATTN: 38638c2ecf20Sopenharmony_ci case QED_HW_ERR_DMAE_FAIL: 38648c2ecf20Sopenharmony_ci case QED_HW_ERR_FW_ASSERT: 38658c2ecf20Sopenharmony_ci /* Prevent HW attentions from being reasserted */ 38668c2ecf20Sopenharmony_ci qed_ops->common->attn_clr_enable(qedf->cdev, true); 38678c2ecf20Sopenharmony_ci break; 38688c2ecf20Sopenharmony_ci case QED_HW_ERR_RAMROD_FAIL: 38698c2ecf20Sopenharmony_ci /* Prevent HW attentions from being reasserted */ 38708c2ecf20Sopenharmony_ci qed_ops->common->attn_clr_enable(qedf->cdev, true); 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_ci if (qedf_enable_recovery) 38738c2ecf20Sopenharmony_ci qed_ops->common->recovery_process(qedf->cdev); 38748c2ecf20Sopenharmony_ci 38758c2ecf20Sopenharmony_ci break; 38768c2ecf20Sopenharmony_ci default: 38778c2ecf20Sopenharmony_ci break; 38788c2ecf20Sopenharmony_ci } 38798c2ecf20Sopenharmony_ci} 38808c2ecf20Sopenharmony_ci 38818c2ecf20Sopenharmony_ci/* 38828c2ecf20Sopenharmony_ci * Protocol TLV handler 38838c2ecf20Sopenharmony_ci */ 38848c2ecf20Sopenharmony_civoid qedf_get_protocol_tlv_data(void *dev, void *data) 38858c2ecf20Sopenharmony_ci{ 38868c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = dev; 38878c2ecf20Sopenharmony_ci struct qed_mfw_tlv_fcoe *fcoe = data; 38888c2ecf20Sopenharmony_ci struct fc_lport *lport; 38898c2ecf20Sopenharmony_ci struct Scsi_Host *host; 38908c2ecf20Sopenharmony_ci struct fc_host_attrs *fc_host; 38918c2ecf20Sopenharmony_ci struct fc_host_statistics *hst; 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_ci if (!qedf) { 38948c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "qedf is null.\n"); 38958c2ecf20Sopenharmony_ci return; 38968c2ecf20Sopenharmony_ci } 38978c2ecf20Sopenharmony_ci 38988c2ecf20Sopenharmony_ci if (test_bit(QEDF_PROBING, &qedf->flags)) { 38998c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Function is still probing.\n"); 39008c2ecf20Sopenharmony_ci return; 39018c2ecf20Sopenharmony_ci } 39028c2ecf20Sopenharmony_ci 39038c2ecf20Sopenharmony_ci lport = qedf->lport; 39048c2ecf20Sopenharmony_ci host = lport->host; 39058c2ecf20Sopenharmony_ci fc_host = shost_to_fc_host(host); 39068c2ecf20Sopenharmony_ci 39078c2ecf20Sopenharmony_ci /* Force a refresh of the fc_host stats including offload stats */ 39088c2ecf20Sopenharmony_ci hst = qedf_fc_get_host_stats(host); 39098c2ecf20Sopenharmony_ci 39108c2ecf20Sopenharmony_ci fcoe->qos_pri_set = true; 39118c2ecf20Sopenharmony_ci fcoe->qos_pri = 3; /* Hard coded to 3 in driver */ 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci fcoe->ra_tov_set = true; 39148c2ecf20Sopenharmony_ci fcoe->ra_tov = lport->r_a_tov; 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_ci fcoe->ed_tov_set = true; 39178c2ecf20Sopenharmony_ci fcoe->ed_tov = lport->e_d_tov; 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ci fcoe->npiv_state_set = true; 39208c2ecf20Sopenharmony_ci fcoe->npiv_state = 1; /* NPIV always enabled */ 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci fcoe->num_npiv_ids_set = true; 39238c2ecf20Sopenharmony_ci fcoe->num_npiv_ids = fc_host->npiv_vports_inuse; 39248c2ecf20Sopenharmony_ci 39258c2ecf20Sopenharmony_ci /* Certain attributes we only want to set if we've selected an FCF */ 39268c2ecf20Sopenharmony_ci if (qedf->ctlr.sel_fcf) { 39278c2ecf20Sopenharmony_ci fcoe->switch_name_set = true; 39288c2ecf20Sopenharmony_ci u64_to_wwn(qedf->ctlr.sel_fcf->switch_name, fcoe->switch_name); 39298c2ecf20Sopenharmony_ci } 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci fcoe->port_state_set = true; 39328c2ecf20Sopenharmony_ci /* For qedf we're either link down or fabric attach */ 39338c2ecf20Sopenharmony_ci if (lport->link_up) 39348c2ecf20Sopenharmony_ci fcoe->port_state = QED_MFW_TLV_PORT_STATE_FABRIC; 39358c2ecf20Sopenharmony_ci else 39368c2ecf20Sopenharmony_ci fcoe->port_state = QED_MFW_TLV_PORT_STATE_OFFLINE; 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci fcoe->link_failures_set = true; 39398c2ecf20Sopenharmony_ci fcoe->link_failures = (u16)hst->link_failure_count; 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ci fcoe->fcoe_txq_depth_set = true; 39428c2ecf20Sopenharmony_ci fcoe->fcoe_rxq_depth_set = true; 39438c2ecf20Sopenharmony_ci fcoe->fcoe_rxq_depth = FCOE_PARAMS_NUM_TASKS; 39448c2ecf20Sopenharmony_ci fcoe->fcoe_txq_depth = FCOE_PARAMS_NUM_TASKS; 39458c2ecf20Sopenharmony_ci 39468c2ecf20Sopenharmony_ci fcoe->fcoe_rx_frames_set = true; 39478c2ecf20Sopenharmony_ci fcoe->fcoe_rx_frames = hst->rx_frames; 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci fcoe->fcoe_tx_frames_set = true; 39508c2ecf20Sopenharmony_ci fcoe->fcoe_tx_frames = hst->tx_frames; 39518c2ecf20Sopenharmony_ci 39528c2ecf20Sopenharmony_ci fcoe->fcoe_rx_bytes_set = true; 39538c2ecf20Sopenharmony_ci fcoe->fcoe_rx_bytes = hst->fcp_input_megabytes * 1000000; 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_ci fcoe->fcoe_tx_bytes_set = true; 39568c2ecf20Sopenharmony_ci fcoe->fcoe_tx_bytes = hst->fcp_output_megabytes * 1000000; 39578c2ecf20Sopenharmony_ci 39588c2ecf20Sopenharmony_ci fcoe->crc_count_set = true; 39598c2ecf20Sopenharmony_ci fcoe->crc_count = hst->invalid_crc_count; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci fcoe->tx_abts_set = true; 39628c2ecf20Sopenharmony_ci fcoe->tx_abts = hst->fcp_packet_aborts; 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci fcoe->tx_lun_rst_set = true; 39658c2ecf20Sopenharmony_ci fcoe->tx_lun_rst = qedf->lun_resets; 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci fcoe->abort_task_sets_set = true; 39688c2ecf20Sopenharmony_ci fcoe->abort_task_sets = qedf->packet_aborts; 39698c2ecf20Sopenharmony_ci 39708c2ecf20Sopenharmony_ci fcoe->scsi_busy_set = true; 39718c2ecf20Sopenharmony_ci fcoe->scsi_busy = qedf->busy; 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci fcoe->scsi_tsk_full_set = true; 39748c2ecf20Sopenharmony_ci fcoe->scsi_tsk_full = qedf->task_set_fulls; 39758c2ecf20Sopenharmony_ci} 39768c2ecf20Sopenharmony_ci 39778c2ecf20Sopenharmony_ci/* Deferred work function to perform soft context reset on STAG change */ 39788c2ecf20Sopenharmony_civoid qedf_stag_change_work(struct work_struct *work) 39798c2ecf20Sopenharmony_ci{ 39808c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = 39818c2ecf20Sopenharmony_ci container_of(work, struct qedf_ctx, stag_work.work); 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_ci if (!qedf) { 39848c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "qedf is NULL"); 39858c2ecf20Sopenharmony_ci return; 39868c2ecf20Sopenharmony_ci } 39878c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Performing software context reset.\n"); 39888c2ecf20Sopenharmony_ci qedf_ctx_soft_reset(qedf->lport); 39898c2ecf20Sopenharmony_ci} 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_cistatic void qedf_shutdown(struct pci_dev *pdev) 39928c2ecf20Sopenharmony_ci{ 39938c2ecf20Sopenharmony_ci __qedf_remove(pdev, QEDF_MODE_NORMAL); 39948c2ecf20Sopenharmony_ci} 39958c2ecf20Sopenharmony_ci 39968c2ecf20Sopenharmony_cistatic int qedf_suspend(struct pci_dev *pdev, pm_message_t state) 39978c2ecf20Sopenharmony_ci{ 39988c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci if (!pdev) { 40018c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "pdev is NULL.\n"); 40028c2ecf20Sopenharmony_ci return -ENODEV; 40038c2ecf20Sopenharmony_ci } 40048c2ecf20Sopenharmony_ci 40058c2ecf20Sopenharmony_ci qedf = pci_get_drvdata(pdev); 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "%s: Device does not support suspend operation\n", __func__); 40088c2ecf20Sopenharmony_ci 40098c2ecf20Sopenharmony_ci return -EPERM; 40108c2ecf20Sopenharmony_ci} 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ci/* 40138c2ecf20Sopenharmony_ci * Recovery handler code 40148c2ecf20Sopenharmony_ci */ 40158c2ecf20Sopenharmony_cistatic void qedf_schedule_recovery_handler(void *dev) 40168c2ecf20Sopenharmony_ci{ 40178c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = dev; 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Recovery handler scheduled.\n"); 40208c2ecf20Sopenharmony_ci schedule_delayed_work(&qedf->recovery_work, 0); 40218c2ecf20Sopenharmony_ci} 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_cistatic void qedf_recovery_handler(struct work_struct *work) 40248c2ecf20Sopenharmony_ci{ 40258c2ecf20Sopenharmony_ci struct qedf_ctx *qedf = 40268c2ecf20Sopenharmony_ci container_of(work, struct qedf_ctx, recovery_work.work); 40278c2ecf20Sopenharmony_ci 40288c2ecf20Sopenharmony_ci if (test_and_set_bit(QEDF_IN_RECOVERY, &qedf->flags)) 40298c2ecf20Sopenharmony_ci return; 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_ci /* 40328c2ecf20Sopenharmony_ci * Call common_ops->recovery_prolog to allow the MFW to quiesce 40338c2ecf20Sopenharmony_ci * any PCI transactions. 40348c2ecf20Sopenharmony_ci */ 40358c2ecf20Sopenharmony_ci qed_ops->common->recovery_prolog(qedf->cdev); 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Recovery work start.\n"); 40388c2ecf20Sopenharmony_ci __qedf_remove(qedf->pdev, QEDF_MODE_RECOVERY); 40398c2ecf20Sopenharmony_ci /* 40408c2ecf20Sopenharmony_ci * Reset link and dcbx to down state since we will not get a link down 40418c2ecf20Sopenharmony_ci * event from the MFW but calling __qedf_remove will essentially be a 40428c2ecf20Sopenharmony_ci * link down event. 40438c2ecf20Sopenharmony_ci */ 40448c2ecf20Sopenharmony_ci atomic_set(&qedf->link_state, QEDF_LINK_DOWN); 40458c2ecf20Sopenharmony_ci atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING); 40468c2ecf20Sopenharmony_ci __qedf_probe(qedf->pdev, QEDF_MODE_RECOVERY); 40478c2ecf20Sopenharmony_ci clear_bit(QEDF_IN_RECOVERY, &qedf->flags); 40488c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "Recovery work complete.\n"); 40498c2ecf20Sopenharmony_ci} 40508c2ecf20Sopenharmony_ci 40518c2ecf20Sopenharmony_ci/* Generic TLV data callback */ 40528c2ecf20Sopenharmony_civoid qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data) 40538c2ecf20Sopenharmony_ci{ 40548c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci if (!dev) { 40578c2ecf20Sopenharmony_ci QEDF_INFO(NULL, QEDF_LOG_EVT, 40588c2ecf20Sopenharmony_ci "dev is NULL so ignoring get_generic_tlv_data request.\n"); 40598c2ecf20Sopenharmony_ci return; 40608c2ecf20Sopenharmony_ci } 40618c2ecf20Sopenharmony_ci qedf = (struct qedf_ctx *)dev; 40628c2ecf20Sopenharmony_ci 40638c2ecf20Sopenharmony_ci memset(data, 0, sizeof(struct qed_generic_tlvs)); 40648c2ecf20Sopenharmony_ci ether_addr_copy(data->mac[0], qedf->mac); 40658c2ecf20Sopenharmony_ci} 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_ci/* 40688c2ecf20Sopenharmony_ci * Module Init/Remove 40698c2ecf20Sopenharmony_ci */ 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_cistatic int __init qedf_init(void) 40728c2ecf20Sopenharmony_ci{ 40738c2ecf20Sopenharmony_ci int ret; 40748c2ecf20Sopenharmony_ci 40758c2ecf20Sopenharmony_ci /* If debug=1 passed, set the default log mask */ 40768c2ecf20Sopenharmony_ci if (qedf_debug == QEDF_LOG_DEFAULT) 40778c2ecf20Sopenharmony_ci qedf_debug = QEDF_DEFAULT_LOG_MASK; 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_ci /* 40808c2ecf20Sopenharmony_ci * Check that default prio for FIP/FCoE traffic is between 0..7 if a 40818c2ecf20Sopenharmony_ci * value has been set 40828c2ecf20Sopenharmony_ci */ 40838c2ecf20Sopenharmony_ci if (qedf_default_prio > -1) 40848c2ecf20Sopenharmony_ci if (qedf_default_prio > 7) { 40858c2ecf20Sopenharmony_ci qedf_default_prio = QEDF_DEFAULT_PRIO; 40868c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "FCoE/FIP priority out of range, resetting to %d.\n", 40878c2ecf20Sopenharmony_ci QEDF_DEFAULT_PRIO); 40888c2ecf20Sopenharmony_ci } 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci /* Print driver banner */ 40918c2ecf20Sopenharmony_ci QEDF_INFO(NULL, QEDF_LOG_INFO, "%s v%s.\n", QEDF_DESCR, 40928c2ecf20Sopenharmony_ci QEDF_VERSION); 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_ci /* Create kmem_cache for qedf_io_work structs */ 40958c2ecf20Sopenharmony_ci qedf_io_work_cache = kmem_cache_create("qedf_io_work_cache", 40968c2ecf20Sopenharmony_ci sizeof(struct qedf_io_work), 0, SLAB_HWCACHE_ALIGN, NULL); 40978c2ecf20Sopenharmony_ci if (qedf_io_work_cache == NULL) { 40988c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "qedf_io_work_cache is NULL.\n"); 40998c2ecf20Sopenharmony_ci goto err1; 41008c2ecf20Sopenharmony_ci } 41018c2ecf20Sopenharmony_ci QEDF_INFO(NULL, QEDF_LOG_DISC, "qedf_io_work_cache=%p.\n", 41028c2ecf20Sopenharmony_ci qedf_io_work_cache); 41038c2ecf20Sopenharmony_ci 41048c2ecf20Sopenharmony_ci qed_ops = qed_get_fcoe_ops(); 41058c2ecf20Sopenharmony_ci if (!qed_ops) { 41068c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Failed to get qed fcoe operations\n"); 41078c2ecf20Sopenharmony_ci goto err1; 41088c2ecf20Sopenharmony_ci } 41098c2ecf20Sopenharmony_ci 41108c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 41118c2ecf20Sopenharmony_ci qedf_dbg_init("qedf"); 41128c2ecf20Sopenharmony_ci#endif 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci qedf_fc_transport_template = 41158c2ecf20Sopenharmony_ci fc_attach_transport(&qedf_fc_transport_fn); 41168c2ecf20Sopenharmony_ci if (!qedf_fc_transport_template) { 41178c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Could not register with FC transport\n"); 41188c2ecf20Sopenharmony_ci goto err2; 41198c2ecf20Sopenharmony_ci } 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci qedf_fc_vport_transport_template = 41228c2ecf20Sopenharmony_ci fc_attach_transport(&qedf_fc_vport_transport_fn); 41238c2ecf20Sopenharmony_ci if (!qedf_fc_vport_transport_template) { 41248c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Could not register vport template with FC " 41258c2ecf20Sopenharmony_ci "transport\n"); 41268c2ecf20Sopenharmony_ci goto err3; 41278c2ecf20Sopenharmony_ci } 41288c2ecf20Sopenharmony_ci 41298c2ecf20Sopenharmony_ci qedf_io_wq = create_workqueue("qedf_io_wq"); 41308c2ecf20Sopenharmony_ci if (!qedf_io_wq) { 41318c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Could not create qedf_io_wq.\n"); 41328c2ecf20Sopenharmony_ci goto err4; 41338c2ecf20Sopenharmony_ci } 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci qedf_cb_ops.get_login_failures = qedf_get_login_failures; 41368c2ecf20Sopenharmony_ci 41378c2ecf20Sopenharmony_ci ret = pci_register_driver(&qedf_pci_driver); 41388c2ecf20Sopenharmony_ci if (ret) { 41398c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "Failed to register driver\n"); 41408c2ecf20Sopenharmony_ci goto err5; 41418c2ecf20Sopenharmony_ci } 41428c2ecf20Sopenharmony_ci 41438c2ecf20Sopenharmony_ci return 0; 41448c2ecf20Sopenharmony_ci 41458c2ecf20Sopenharmony_cierr5: 41468c2ecf20Sopenharmony_ci destroy_workqueue(qedf_io_wq); 41478c2ecf20Sopenharmony_cierr4: 41488c2ecf20Sopenharmony_ci fc_release_transport(qedf_fc_vport_transport_template); 41498c2ecf20Sopenharmony_cierr3: 41508c2ecf20Sopenharmony_ci fc_release_transport(qedf_fc_transport_template); 41518c2ecf20Sopenharmony_cierr2: 41528c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 41538c2ecf20Sopenharmony_ci qedf_dbg_exit(); 41548c2ecf20Sopenharmony_ci#endif 41558c2ecf20Sopenharmony_ci qed_put_fcoe_ops(); 41568c2ecf20Sopenharmony_cierr1: 41578c2ecf20Sopenharmony_ci return -EINVAL; 41588c2ecf20Sopenharmony_ci} 41598c2ecf20Sopenharmony_ci 41608c2ecf20Sopenharmony_cistatic void __exit qedf_cleanup(void) 41618c2ecf20Sopenharmony_ci{ 41628c2ecf20Sopenharmony_ci pci_unregister_driver(&qedf_pci_driver); 41638c2ecf20Sopenharmony_ci 41648c2ecf20Sopenharmony_ci destroy_workqueue(qedf_io_wq); 41658c2ecf20Sopenharmony_ci 41668c2ecf20Sopenharmony_ci fc_release_transport(qedf_fc_vport_transport_template); 41678c2ecf20Sopenharmony_ci fc_release_transport(qedf_fc_transport_template); 41688c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 41698c2ecf20Sopenharmony_ci qedf_dbg_exit(); 41708c2ecf20Sopenharmony_ci#endif 41718c2ecf20Sopenharmony_ci qed_put_fcoe_ops(); 41728c2ecf20Sopenharmony_ci 41738c2ecf20Sopenharmony_ci kmem_cache_destroy(qedf_io_work_cache); 41748c2ecf20Sopenharmony_ci} 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 41778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QLogic FastLinQ 4xxxx FCoE Module"); 41788c2ecf20Sopenharmony_ciMODULE_AUTHOR("QLogic Corporation"); 41798c2ecf20Sopenharmony_ciMODULE_VERSION(QEDF_VERSION); 41808c2ecf20Sopenharmony_cimodule_init(qedf_init); 41818c2ecf20Sopenharmony_cimodule_exit(qedf_cleanup); 4182