18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2008 Cisco Systems, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright 2007 Nuova Systems, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify 68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 78c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 108c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 118c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 128c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 138c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 158c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 168c2ecf20Sopenharmony_ci * SOFTWARE. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/mempool.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/errno.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 288c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 298c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 308c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fip.h> 318c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 328c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h> 338c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_fc.h> 348c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 358c2ecf20Sopenharmony_ci#include <scsi/libfc.h> 368c2ecf20Sopenharmony_ci#include <scsi/fc_frame.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "vnic_dev.h" 398c2ecf20Sopenharmony_ci#include "vnic_intr.h" 408c2ecf20Sopenharmony_ci#include "vnic_stats.h" 418c2ecf20Sopenharmony_ci#include "fnic_io.h" 428c2ecf20Sopenharmony_ci#include "fnic_fip.h" 438c2ecf20Sopenharmony_ci#include "fnic.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_CISCO_FNIC 0x0045 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Timer to poll notification area for events. Used for MSI interrupts */ 488c2ecf20Sopenharmony_ci#define FNIC_NOTIFY_TIMER_PERIOD (2 * HZ) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct kmem_cache *fnic_sgl_cache[FNIC_SGL_NUM_CACHES]; 518c2ecf20Sopenharmony_cistatic struct kmem_cache *fnic_io_req_cache; 528c2ecf20Sopenharmony_cistatic LIST_HEAD(fnic_list); 538c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(fnic_list_lock); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Supported devices by fnic module */ 568c2ecf20Sopenharmony_cistatic struct pci_device_id fnic_id_table[] = { 578c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_CISCO_FNIC) }, 588c2ecf20Sopenharmony_ci { 0, } 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Abhijeet Joglekar <abjoglek@cisco.com>, " 638c2ecf20Sopenharmony_ci "Joseph R. Eykholt <jeykholt@cisco.com>"); 648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 658c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fnic_id_table); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciunsigned int fnic_log_level; 698c2ecf20Sopenharmony_cimodule_param(fnic_log_level, int, S_IRUGO|S_IWUSR); 708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels"); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciunsigned int io_completions = FNIC_DFLT_IO_COMPLETIONS; 748c2ecf20Sopenharmony_cimodule_param(io_completions, int, S_IRUGO|S_IWUSR); 758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io_completions, "Max CQ entries to process at a time"); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciunsigned int fnic_trace_max_pages = 16; 788c2ecf20Sopenharmony_cimodule_param(fnic_trace_max_pages, uint, S_IRUGO|S_IWUSR); 798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fnic_trace_max_pages, "Total allocated memory pages " 808c2ecf20Sopenharmony_ci "for fnic trace buffer"); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciunsigned int fnic_fc_trace_max_pages = 64; 838c2ecf20Sopenharmony_cimodule_param(fnic_fc_trace_max_pages, uint, S_IRUGO|S_IWUSR); 848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fnic_fc_trace_max_pages, 858c2ecf20Sopenharmony_ci "Total allocated memory pages for fc trace buffer"); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic unsigned int fnic_max_qdepth = FNIC_DFLT_QUEUE_DEPTH; 888c2ecf20Sopenharmony_cimodule_param(fnic_max_qdepth, uint, S_IRUGO|S_IWUSR); 898c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fnic_max_qdepth, "Queue depth to report for each LUN"); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic struct libfc_function_template fnic_transport_template = { 928c2ecf20Sopenharmony_ci .frame_send = fnic_send, 938c2ecf20Sopenharmony_ci .lport_set_port_id = fnic_set_port_id, 948c2ecf20Sopenharmony_ci .fcp_abort_io = fnic_empty_scsi_cleanup, 958c2ecf20Sopenharmony_ci .fcp_cleanup = fnic_empty_scsi_cleanup, 968c2ecf20Sopenharmony_ci .exch_mgr_reset = fnic_exch_mgr_reset 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int fnic_slave_alloc(struct scsi_device *sdev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!rport || fc_remote_port_chkready(rport)) 1048c2ecf20Sopenharmony_ci return -ENXIO; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci scsi_change_queue_depth(sdev, fnic_max_qdepth); 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic struct scsi_host_template fnic_host_template = { 1118c2ecf20Sopenharmony_ci .module = THIS_MODULE, 1128c2ecf20Sopenharmony_ci .name = DRV_NAME, 1138c2ecf20Sopenharmony_ci .queuecommand = fnic_queuecommand, 1148c2ecf20Sopenharmony_ci .eh_timed_out = fc_eh_timed_out, 1158c2ecf20Sopenharmony_ci .eh_abort_handler = fnic_abort_cmd, 1168c2ecf20Sopenharmony_ci .eh_device_reset_handler = fnic_device_reset, 1178c2ecf20Sopenharmony_ci .eh_host_reset_handler = fnic_host_reset, 1188c2ecf20Sopenharmony_ci .slave_alloc = fnic_slave_alloc, 1198c2ecf20Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 1208c2ecf20Sopenharmony_ci .this_id = -1, 1218c2ecf20Sopenharmony_ci .cmd_per_lun = 3, 1228c2ecf20Sopenharmony_ci .can_queue = FNIC_DFLT_IO_REQ, 1238c2ecf20Sopenharmony_ci .sg_tablesize = FNIC_MAX_SG_DESC_CNT, 1248c2ecf20Sopenharmony_ci .max_sectors = 0xffff, 1258c2ecf20Sopenharmony_ci .shost_attrs = fnic_attrs, 1268c2ecf20Sopenharmony_ci .track_queue_depth = 1, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void 1308c2ecf20Sopenharmony_cifnic_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci if (timeout) 1338c2ecf20Sopenharmony_ci rport->dev_loss_tmo = timeout; 1348c2ecf20Sopenharmony_ci else 1358c2ecf20Sopenharmony_ci rport->dev_loss_tmo = 1; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void fnic_get_host_speed(struct Scsi_Host *shost); 1398c2ecf20Sopenharmony_cistatic struct scsi_transport_template *fnic_fc_transport; 1408c2ecf20Sopenharmony_cistatic struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *); 1418c2ecf20Sopenharmony_cistatic void fnic_reset_host_stats(struct Scsi_Host *); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct fc_function_template fnic_fc_functions = { 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci .show_host_node_name = 1, 1468c2ecf20Sopenharmony_ci .show_host_port_name = 1, 1478c2ecf20Sopenharmony_ci .show_host_supported_classes = 1, 1488c2ecf20Sopenharmony_ci .show_host_supported_fc4s = 1, 1498c2ecf20Sopenharmony_ci .show_host_active_fc4s = 1, 1508c2ecf20Sopenharmony_ci .show_host_maxframe_size = 1, 1518c2ecf20Sopenharmony_ci .show_host_port_id = 1, 1528c2ecf20Sopenharmony_ci .show_host_supported_speeds = 1, 1538c2ecf20Sopenharmony_ci .get_host_speed = fnic_get_host_speed, 1548c2ecf20Sopenharmony_ci .show_host_speed = 1, 1558c2ecf20Sopenharmony_ci .show_host_port_type = 1, 1568c2ecf20Sopenharmony_ci .get_host_port_state = fc_get_host_port_state, 1578c2ecf20Sopenharmony_ci .show_host_port_state = 1, 1588c2ecf20Sopenharmony_ci .show_host_symbolic_name = 1, 1598c2ecf20Sopenharmony_ci .show_rport_maxframe_size = 1, 1608c2ecf20Sopenharmony_ci .show_rport_supported_classes = 1, 1618c2ecf20Sopenharmony_ci .show_host_fabric_name = 1, 1628c2ecf20Sopenharmony_ci .show_starget_node_name = 1, 1638c2ecf20Sopenharmony_ci .show_starget_port_name = 1, 1648c2ecf20Sopenharmony_ci .show_starget_port_id = 1, 1658c2ecf20Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 1668c2ecf20Sopenharmony_ci .set_rport_dev_loss_tmo = fnic_set_rport_dev_loss_tmo, 1678c2ecf20Sopenharmony_ci .issue_fc_host_lip = fnic_reset, 1688c2ecf20Sopenharmony_ci .get_fc_host_stats = fnic_get_stats, 1698c2ecf20Sopenharmony_ci .reset_fc_host_stats = fnic_reset_host_stats, 1708c2ecf20Sopenharmony_ci .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), 1718c2ecf20Sopenharmony_ci .terminate_rport_io = fnic_terminate_rport_io, 1728c2ecf20Sopenharmony_ci .bsg_request = fc_lport_bsg_request, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void fnic_get_host_speed(struct Scsi_Host *shost) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct fc_lport *lp = shost_priv(shost); 1788c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lp); 1798c2ecf20Sopenharmony_ci u32 port_speed = vnic_dev_port_speed(fnic->vdev); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Add in other values as they get defined in fw */ 1828c2ecf20Sopenharmony_ci switch (port_speed) { 1838c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_10G: 1848c2ecf20Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_10GBIT; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_20G: 1878c2ecf20Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_20GBIT; 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_25G: 1908c2ecf20Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_25GBIT; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_40G: 1938c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_4x10G: 1948c2ecf20Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_40GBIT; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_100G: 1978c2ecf20Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_100GBIT; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int ret; 2088c2ecf20Sopenharmony_ci struct fc_lport *lp = shost_priv(host); 2098c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lp); 2108c2ecf20Sopenharmony_ci struct fc_host_statistics *stats = &lp->host_stats; 2118c2ecf20Sopenharmony_ci struct vnic_stats *vs; 2128c2ecf20Sopenharmony_ci unsigned long flags; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (time_before(jiffies, fnic->stats_time + HZ / FNIC_STATS_RATE_LIMIT)) 2158c2ecf20Sopenharmony_ci return stats; 2168c2ecf20Sopenharmony_ci fnic->stats_time = jiffies; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 2198c2ecf20Sopenharmony_ci ret = vnic_dev_stats_dump(fnic->vdev, &fnic->stats); 2208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (ret) { 2238c2ecf20Sopenharmony_ci FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host, 2248c2ecf20Sopenharmony_ci "fnic: Get vnic stats failed" 2258c2ecf20Sopenharmony_ci " 0x%x", ret); 2268c2ecf20Sopenharmony_ci return stats; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci vs = fnic->stats; 2298c2ecf20Sopenharmony_ci stats->tx_frames = vs->tx.tx_unicast_frames_ok; 2308c2ecf20Sopenharmony_ci stats->tx_words = vs->tx.tx_unicast_bytes_ok / 4; 2318c2ecf20Sopenharmony_ci stats->rx_frames = vs->rx.rx_unicast_frames_ok; 2328c2ecf20Sopenharmony_ci stats->rx_words = vs->rx.rx_unicast_bytes_ok / 4; 2338c2ecf20Sopenharmony_ci stats->error_frames = vs->tx.tx_errors + vs->rx.rx_errors; 2348c2ecf20Sopenharmony_ci stats->dumped_frames = vs->tx.tx_drops + vs->rx.rx_drop; 2358c2ecf20Sopenharmony_ci stats->invalid_crc_count = vs->rx.rx_crc_errors; 2368c2ecf20Sopenharmony_ci stats->seconds_since_last_reset = 2378c2ecf20Sopenharmony_ci (jiffies - fnic->stats_reset_time) / HZ; 2388c2ecf20Sopenharmony_ci stats->fcp_input_megabytes = div_u64(fnic->fcp_input_bytes, 1000000); 2398c2ecf20Sopenharmony_ci stats->fcp_output_megabytes = div_u64(fnic->fcp_output_bytes, 1000000); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return stats; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * fnic_dump_fchost_stats 2468c2ecf20Sopenharmony_ci * note : dumps fc_statistics into system logs 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_civoid fnic_dump_fchost_stats(struct Scsi_Host *host, 2498c2ecf20Sopenharmony_ci struct fc_host_statistics *stats) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2528c2ecf20Sopenharmony_ci "fnic: seconds since last reset = %llu\n", 2538c2ecf20Sopenharmony_ci stats->seconds_since_last_reset); 2548c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2558c2ecf20Sopenharmony_ci "fnic: tx frames = %llu\n", 2568c2ecf20Sopenharmony_ci stats->tx_frames); 2578c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2588c2ecf20Sopenharmony_ci "fnic: tx words = %llu\n", 2598c2ecf20Sopenharmony_ci stats->tx_words); 2608c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2618c2ecf20Sopenharmony_ci "fnic: rx frames = %llu\n", 2628c2ecf20Sopenharmony_ci stats->rx_frames); 2638c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2648c2ecf20Sopenharmony_ci "fnic: rx words = %llu\n", 2658c2ecf20Sopenharmony_ci stats->rx_words); 2668c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2678c2ecf20Sopenharmony_ci "fnic: lip count = %llu\n", 2688c2ecf20Sopenharmony_ci stats->lip_count); 2698c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2708c2ecf20Sopenharmony_ci "fnic: nos count = %llu\n", 2718c2ecf20Sopenharmony_ci stats->nos_count); 2728c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2738c2ecf20Sopenharmony_ci "fnic: error frames = %llu\n", 2748c2ecf20Sopenharmony_ci stats->error_frames); 2758c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2768c2ecf20Sopenharmony_ci "fnic: dumped frames = %llu\n", 2778c2ecf20Sopenharmony_ci stats->dumped_frames); 2788c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2798c2ecf20Sopenharmony_ci "fnic: link failure count = %llu\n", 2808c2ecf20Sopenharmony_ci stats->link_failure_count); 2818c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2828c2ecf20Sopenharmony_ci "fnic: loss of sync count = %llu\n", 2838c2ecf20Sopenharmony_ci stats->loss_of_sync_count); 2848c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2858c2ecf20Sopenharmony_ci "fnic: loss of signal count = %llu\n", 2868c2ecf20Sopenharmony_ci stats->loss_of_signal_count); 2878c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2888c2ecf20Sopenharmony_ci "fnic: prim seq protocol err count = %llu\n", 2898c2ecf20Sopenharmony_ci stats->prim_seq_protocol_err_count); 2908c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2918c2ecf20Sopenharmony_ci "fnic: invalid tx word count= %llu\n", 2928c2ecf20Sopenharmony_ci stats->invalid_tx_word_count); 2938c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2948c2ecf20Sopenharmony_ci "fnic: invalid crc count = %llu\n", 2958c2ecf20Sopenharmony_ci stats->invalid_crc_count); 2968c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 2978c2ecf20Sopenharmony_ci "fnic: fcp input requests = %llu\n", 2988c2ecf20Sopenharmony_ci stats->fcp_input_requests); 2998c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 3008c2ecf20Sopenharmony_ci "fnic: fcp output requests = %llu\n", 3018c2ecf20Sopenharmony_ci stats->fcp_output_requests); 3028c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 3038c2ecf20Sopenharmony_ci "fnic: fcp control requests = %llu\n", 3048c2ecf20Sopenharmony_ci stats->fcp_control_requests); 3058c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 3068c2ecf20Sopenharmony_ci "fnic: fcp input megabytes = %llu\n", 3078c2ecf20Sopenharmony_ci stats->fcp_input_megabytes); 3088c2ecf20Sopenharmony_ci FNIC_MAIN_NOTE(KERN_NOTICE, host, 3098c2ecf20Sopenharmony_ci "fnic: fcp output megabytes = %llu\n", 3108c2ecf20Sopenharmony_ci stats->fcp_output_megabytes); 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * fnic_reset_host_stats : clears host stats 3168c2ecf20Sopenharmony_ci * note : called when reset_statistics set under sysfs dir 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_cistatic void fnic_reset_host_stats(struct Scsi_Host *host) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int ret; 3218c2ecf20Sopenharmony_ci struct fc_lport *lp = shost_priv(host); 3228c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lp); 3238c2ecf20Sopenharmony_ci struct fc_host_statistics *stats; 3248c2ecf20Sopenharmony_ci unsigned long flags; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* dump current stats, before clearing them */ 3278c2ecf20Sopenharmony_ci stats = fnic_get_stats(host); 3288c2ecf20Sopenharmony_ci fnic_dump_fchost_stats(host, stats); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 3318c2ecf20Sopenharmony_ci ret = vnic_dev_stats_clear(fnic->vdev); 3328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (ret) { 3358c2ecf20Sopenharmony_ci FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host, 3368c2ecf20Sopenharmony_ci "fnic: Reset vnic stats failed" 3378c2ecf20Sopenharmony_ci " 0x%x", ret); 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci fnic->stats_reset_time = jiffies; 3418c2ecf20Sopenharmony_ci memset(stats, 0, sizeof(*stats)); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_civoid fnic_log_q_error(struct fnic *fnic) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci unsigned int i; 3498c2ecf20Sopenharmony_ci u32 error_status; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < fnic->raw_wq_count; i++) { 3528c2ecf20Sopenharmony_ci error_status = ioread32(&fnic->wq[i].ctrl->error_status); 3538c2ecf20Sopenharmony_ci if (error_status) 3548c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 3558c2ecf20Sopenharmony_ci "WQ[%d] error_status" 3568c2ecf20Sopenharmony_ci " %d\n", i, error_status); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for (i = 0; i < fnic->rq_count; i++) { 3608c2ecf20Sopenharmony_ci error_status = ioread32(&fnic->rq[i].ctrl->error_status); 3618c2ecf20Sopenharmony_ci if (error_status) 3628c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 3638c2ecf20Sopenharmony_ci "RQ[%d] error_status" 3648c2ecf20Sopenharmony_ci " %d\n", i, error_status); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci for (i = 0; i < fnic->wq_copy_count; i++) { 3688c2ecf20Sopenharmony_ci error_status = ioread32(&fnic->wq_copy[i].ctrl->error_status); 3698c2ecf20Sopenharmony_ci if (error_status) 3708c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 3718c2ecf20Sopenharmony_ci "CWQ[%d] error_status" 3728c2ecf20Sopenharmony_ci " %d\n", i, error_status); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_civoid fnic_handle_link_event(struct fnic *fnic) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci unsigned long flags; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 3818c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 3828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci queue_work(fnic_event_queue, &fnic->link_work); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int fnic_notify_set(struct fnic *fnic) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci int err; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci switch (vnic_dev_get_intr_mode(fnic->vdev)) { 3968c2ecf20Sopenharmony_ci case VNIC_DEV_INTR_MODE_INTX: 3978c2ecf20Sopenharmony_ci err = vnic_dev_notify_set(fnic->vdev, FNIC_INTX_NOTIFY); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 4008c2ecf20Sopenharmony_ci err = vnic_dev_notify_set(fnic->vdev, -1); 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 4038c2ecf20Sopenharmony_ci err = vnic_dev_notify_set(fnic->vdev, FNIC_MSIX_ERR_NOTIFY); 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 4078c2ecf20Sopenharmony_ci "Interrupt mode should be set up" 4088c2ecf20Sopenharmony_ci " before devcmd notify set %d\n", 4098c2ecf20Sopenharmony_ci vnic_dev_get_intr_mode(fnic->vdev)); 4108c2ecf20Sopenharmony_ci err = -1; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return err; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void fnic_notify_timer(struct timer_list *t) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct fnic *fnic = from_timer(fnic, t, notify_timer); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci fnic_handle_link_event(fnic); 4228c2ecf20Sopenharmony_ci mod_timer(&fnic->notify_timer, 4238c2ecf20Sopenharmony_ci round_jiffies(jiffies + FNIC_NOTIFY_TIMER_PERIOD)); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void fnic_fip_notify_timer(struct timer_list *t) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct fnic *fnic = from_timer(fnic, t, fip_timer); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci fnic_handle_fip_timer(fnic); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void fnic_notify_timer_start(struct fnic *fnic) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci switch (vnic_dev_get_intr_mode(fnic->vdev)) { 4368c2ecf20Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * Schedule first timeout immediately. The driver is 4398c2ecf20Sopenharmony_ci * initiatialized and ready to look for link up notification 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci mod_timer(&fnic->notify_timer, jiffies); 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci default: 4448c2ecf20Sopenharmony_ci /* Using intr for notification for INTx/MSI-X */ 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int fnic_dev_wait(struct vnic_dev *vdev, 4508c2ecf20Sopenharmony_ci int (*start)(struct vnic_dev *, int), 4518c2ecf20Sopenharmony_ci int (*finished)(struct vnic_dev *, int *), 4528c2ecf20Sopenharmony_ci int arg) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci unsigned long time; 4558c2ecf20Sopenharmony_ci int done; 4568c2ecf20Sopenharmony_ci int err; 4578c2ecf20Sopenharmony_ci int count; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci count = 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci err = start(vdev, arg); 4628c2ecf20Sopenharmony_ci if (err) 4638c2ecf20Sopenharmony_ci return err; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Wait for func to complete. 4668c2ecf20Sopenharmony_ci * Sometime schedule_timeout_uninterruptible take long time 4678c2ecf20Sopenharmony_ci * to wake up so we do not retry as we are only waiting for 4688c2ecf20Sopenharmony_ci * 2 seconds in while loop. By adding count, we make sure 4698c2ecf20Sopenharmony_ci * we try atleast three times before returning -ETIMEDOUT 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci time = jiffies + (HZ * 2); 4728c2ecf20Sopenharmony_ci do { 4738c2ecf20Sopenharmony_ci err = finished(vdev, &done); 4748c2ecf20Sopenharmony_ci count++; 4758c2ecf20Sopenharmony_ci if (err) 4768c2ecf20Sopenharmony_ci return err; 4778c2ecf20Sopenharmony_ci if (done) 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(HZ / 10); 4808c2ecf20Sopenharmony_ci } while (time_after(time, jiffies) || (count < 3)); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int fnic_cleanup(struct fnic *fnic) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci unsigned int i; 4888c2ecf20Sopenharmony_ci int err; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci vnic_dev_disable(fnic->vdev); 4918c2ecf20Sopenharmony_ci for (i = 0; i < fnic->intr_count; i++) 4928c2ecf20Sopenharmony_ci vnic_intr_mask(&fnic->intr[i]); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci for (i = 0; i < fnic->rq_count; i++) { 4958c2ecf20Sopenharmony_ci err = vnic_rq_disable(&fnic->rq[i]); 4968c2ecf20Sopenharmony_ci if (err) 4978c2ecf20Sopenharmony_ci return err; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci for (i = 0; i < fnic->raw_wq_count; i++) { 5008c2ecf20Sopenharmony_ci err = vnic_wq_disable(&fnic->wq[i]); 5018c2ecf20Sopenharmony_ci if (err) 5028c2ecf20Sopenharmony_ci return err; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci for (i = 0; i < fnic->wq_copy_count; i++) { 5058c2ecf20Sopenharmony_ci err = vnic_wq_copy_disable(&fnic->wq_copy[i]); 5068c2ecf20Sopenharmony_ci if (err) 5078c2ecf20Sopenharmony_ci return err; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Clean up completed IOs and FCS frames */ 5118c2ecf20Sopenharmony_ci fnic_wq_copy_cmpl_handler(fnic, io_completions); 5128c2ecf20Sopenharmony_ci fnic_wq_cmpl_handler(fnic, -1); 5138c2ecf20Sopenharmony_ci fnic_rq_cmpl_handler(fnic, -1); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Clean up the IOs and FCS frames that have not completed */ 5168c2ecf20Sopenharmony_ci for (i = 0; i < fnic->raw_wq_count; i++) 5178c2ecf20Sopenharmony_ci vnic_wq_clean(&fnic->wq[i], fnic_free_wq_buf); 5188c2ecf20Sopenharmony_ci for (i = 0; i < fnic->rq_count; i++) 5198c2ecf20Sopenharmony_ci vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf); 5208c2ecf20Sopenharmony_ci for (i = 0; i < fnic->wq_copy_count; i++) 5218c2ecf20Sopenharmony_ci vnic_wq_copy_clean(&fnic->wq_copy[i], 5228c2ecf20Sopenharmony_ci fnic_wq_copy_cleanup_handler); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for (i = 0; i < fnic->cq_count; i++) 5258c2ecf20Sopenharmony_ci vnic_cq_clean(&fnic->cq[i]); 5268c2ecf20Sopenharmony_ci for (i = 0; i < fnic->intr_count; i++) 5278c2ecf20Sopenharmony_ci vnic_intr_clean(&fnic->intr[i]); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci mempool_destroy(fnic->io_req_pool); 5308c2ecf20Sopenharmony_ci for (i = 0; i < FNIC_SGL_NUM_CACHES; i++) 5318c2ecf20Sopenharmony_ci mempool_destroy(fnic->io_sgl_pool[i]); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic void fnic_iounmap(struct fnic *fnic) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci if (fnic->bar0.vaddr) 5398c2ecf20Sopenharmony_ci iounmap(fnic->bar0.vaddr); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci/** 5438c2ecf20Sopenharmony_ci * fnic_get_mac() - get assigned data MAC address for FIP code. 5448c2ecf20Sopenharmony_ci * @lport: local port. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_cistatic u8 *fnic_get_mac(struct fc_lport *lport) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lport); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return fnic->data_src_addr; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic void fnic_set_vlan(struct fnic *fnic, u16 vlan_id) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci vnic_dev_set_default_vlan(fnic->vdev, vlan_id); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct Scsi_Host *host; 5618c2ecf20Sopenharmony_ci struct fc_lport *lp; 5628c2ecf20Sopenharmony_ci struct fnic *fnic; 5638c2ecf20Sopenharmony_ci mempool_t *pool; 5648c2ecf20Sopenharmony_ci int err; 5658c2ecf20Sopenharmony_ci int i; 5668c2ecf20Sopenharmony_ci unsigned long flags; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * Allocate SCSI Host and set up association between host, 5708c2ecf20Sopenharmony_ci * local port, and fnic 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_ci lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic)); 5738c2ecf20Sopenharmony_ci if (!lp) { 5748c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Unable to alloc libfc local port\n"); 5758c2ecf20Sopenharmony_ci err = -ENOMEM; 5768c2ecf20Sopenharmony_ci goto err_out; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci host = lp->host; 5798c2ecf20Sopenharmony_ci fnic = lport_priv(lp); 5808c2ecf20Sopenharmony_ci fnic->lport = lp; 5818c2ecf20Sopenharmony_ci fnic->ctlr.lp = lp; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME, 5848c2ecf20Sopenharmony_ci host->host_no); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci host->transportt = fnic_fc_transport; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci fnic_stats_debugfs_init(fnic); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Setup PCI resources */ 5918c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, fnic); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci fnic->pdev = pdev; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 5968c2ecf20Sopenharmony_ci if (err) { 5978c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 5988c2ecf20Sopenharmony_ci "Cannot enable PCI device, aborting.\n"); 5998c2ecf20Sopenharmony_ci goto err_out_free_hba; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 6038c2ecf20Sopenharmony_ci if (err) { 6048c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6058c2ecf20Sopenharmony_ci "Cannot enable PCI resources, aborting\n"); 6068c2ecf20Sopenharmony_ci goto err_out_disable_device; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci pci_set_master(pdev); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Query PCI controller on system for DMA addressing 6128c2ecf20Sopenharmony_ci * limitation for the device. Try 64-bit first, and 6138c2ecf20Sopenharmony_ci * fail to 32-bit. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 6168c2ecf20Sopenharmony_ci if (err) { 6178c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 6188c2ecf20Sopenharmony_ci if (err) { 6198c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6208c2ecf20Sopenharmony_ci "No usable DMA configuration " 6218c2ecf20Sopenharmony_ci "aborting\n"); 6228c2ecf20Sopenharmony_ci goto err_out_release_regions; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* Map vNIC resources from BAR0 */ 6278c2ecf20Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 6288c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6298c2ecf20Sopenharmony_ci "BAR0 not memory-map'able, aborting.\n"); 6308c2ecf20Sopenharmony_ci err = -ENODEV; 6318c2ecf20Sopenharmony_ci goto err_out_release_regions; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci fnic->bar0.vaddr = pci_iomap(pdev, 0, 0); 6358c2ecf20Sopenharmony_ci fnic->bar0.bus_addr = pci_resource_start(pdev, 0); 6368c2ecf20Sopenharmony_ci fnic->bar0.len = pci_resource_len(pdev, 0); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!fnic->bar0.vaddr) { 6398c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6408c2ecf20Sopenharmony_ci "Cannot memory-map BAR0 res hdr, " 6418c2ecf20Sopenharmony_ci "aborting.\n"); 6428c2ecf20Sopenharmony_ci err = -ENODEV; 6438c2ecf20Sopenharmony_ci goto err_out_release_regions; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci fnic->vdev = vnic_dev_register(NULL, fnic, pdev, &fnic->bar0); 6478c2ecf20Sopenharmony_ci if (!fnic->vdev) { 6488c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6498c2ecf20Sopenharmony_ci "vNIC registration failed, " 6508c2ecf20Sopenharmony_ci "aborting.\n"); 6518c2ecf20Sopenharmony_ci err = -ENODEV; 6528c2ecf20Sopenharmony_ci goto err_out_iounmap; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci err = vnic_dev_cmd_init(fnic->vdev); 6568c2ecf20Sopenharmony_ci if (err) { 6578c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6588c2ecf20Sopenharmony_ci "vnic_dev_cmd_init() returns %d, aborting\n", 6598c2ecf20Sopenharmony_ci err); 6608c2ecf20Sopenharmony_ci goto err_out_vnic_unregister; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci err = fnic_dev_wait(fnic->vdev, vnic_dev_open, 6648c2ecf20Sopenharmony_ci vnic_dev_open_done, CMD_OPENF_RQ_ENABLE_THEN_POST); 6658c2ecf20Sopenharmony_ci if (err) { 6668c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6678c2ecf20Sopenharmony_ci "vNIC dev open failed, aborting.\n"); 6688c2ecf20Sopenharmony_ci goto err_out_dev_cmd_deinit; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci err = vnic_dev_init(fnic->vdev, 0); 6728c2ecf20Sopenharmony_ci if (err) { 6738c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6748c2ecf20Sopenharmony_ci "vNIC dev init failed, aborting.\n"); 6758c2ecf20Sopenharmony_ci goto err_out_dev_close; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr); 6798c2ecf20Sopenharmony_ci if (err) { 6808c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6818c2ecf20Sopenharmony_ci "vNIC get MAC addr failed \n"); 6828c2ecf20Sopenharmony_ci goto err_out_dev_close; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci /* set data_src for point-to-point mode and to keep it non-zero */ 6858c2ecf20Sopenharmony_ci memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* Get vNIC configuration */ 6888c2ecf20Sopenharmony_ci err = fnic_get_vnic_config(fnic); 6898c2ecf20Sopenharmony_ci if (err) { 6908c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 6918c2ecf20Sopenharmony_ci "Get vNIC configuration failed, " 6928c2ecf20Sopenharmony_ci "aborting.\n"); 6938c2ecf20Sopenharmony_ci goto err_out_dev_close; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Configure Maximum Outstanding IO reqs*/ 6978c2ecf20Sopenharmony_ci if (fnic->config.io_throttle_count != FNIC_UCSM_DFLT_THROTTLE_CNT_BLD) { 6988c2ecf20Sopenharmony_ci host->can_queue = min_t(u32, FNIC_MAX_IO_REQ, 6998c2ecf20Sopenharmony_ci max_t(u32, FNIC_MIN_IO_REQ, 7008c2ecf20Sopenharmony_ci fnic->config.io_throttle_count)); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci fnic->fnic_max_tag_id = host->can_queue; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci host->max_lun = fnic->config.luns_per_tgt; 7058c2ecf20Sopenharmony_ci host->max_id = FNIC_MAX_FCP_TARGET; 7068c2ecf20Sopenharmony_ci host->max_cmd_len = FCOE_MAX_CMD_LEN; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci fnic_get_res_counts(fnic); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci err = fnic_set_intr_mode(fnic); 7118c2ecf20Sopenharmony_ci if (err) { 7128c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 7138c2ecf20Sopenharmony_ci "Failed to set intr mode, " 7148c2ecf20Sopenharmony_ci "aborting.\n"); 7158c2ecf20Sopenharmony_ci goto err_out_dev_close; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci err = fnic_alloc_vnic_resources(fnic); 7198c2ecf20Sopenharmony_ci if (err) { 7208c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 7218c2ecf20Sopenharmony_ci "Failed to alloc vNIC resources, " 7228c2ecf20Sopenharmony_ci "aborting.\n"); 7238c2ecf20Sopenharmony_ci goto err_out_clear_intr; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* initialize all fnic locks */ 7288c2ecf20Sopenharmony_ci spin_lock_init(&fnic->fnic_lock); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci for (i = 0; i < FNIC_WQ_MAX; i++) 7318c2ecf20Sopenharmony_ci spin_lock_init(&fnic->wq_lock[i]); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci for (i = 0; i < FNIC_WQ_COPY_MAX; i++) { 7348c2ecf20Sopenharmony_ci spin_lock_init(&fnic->wq_copy_lock[i]); 7358c2ecf20Sopenharmony_ci fnic->wq_copy_desc_low[i] = DESC_CLEAN_LOW_WATERMARK; 7368c2ecf20Sopenharmony_ci fnic->fw_ack_recd[i] = 0; 7378c2ecf20Sopenharmony_ci fnic->fw_ack_index[i] = -1; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci for (i = 0; i < FNIC_IO_LOCKS; i++) 7418c2ecf20Sopenharmony_ci spin_lock_init(&fnic->io_req_lock[i]); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci err = -ENOMEM; 7448c2ecf20Sopenharmony_ci fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache); 7458c2ecf20Sopenharmony_ci if (!fnic->io_req_pool) 7468c2ecf20Sopenharmony_ci goto err_out_free_resources; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci pool = mempool_create_slab_pool(2, fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]); 7498c2ecf20Sopenharmony_ci if (!pool) 7508c2ecf20Sopenharmony_ci goto err_out_free_ioreq_pool; 7518c2ecf20Sopenharmony_ci fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT] = pool; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci pool = mempool_create_slab_pool(2, fnic_sgl_cache[FNIC_SGL_CACHE_MAX]); 7548c2ecf20Sopenharmony_ci if (!pool) 7558c2ecf20Sopenharmony_ci goto err_out_free_dflt_pool; 7568c2ecf20Sopenharmony_ci fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX] = pool; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* setup vlan config, hw inserts vlan header */ 7598c2ecf20Sopenharmony_ci fnic->vlan_hw_insert = 1; 7608c2ecf20Sopenharmony_ci fnic->vlan_id = 0; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* Initialize the FIP fcoe_ctrl struct */ 7638c2ecf20Sopenharmony_ci fnic->ctlr.send = fnic_eth_send; 7648c2ecf20Sopenharmony_ci fnic->ctlr.update_mac = fnic_update_mac; 7658c2ecf20Sopenharmony_ci fnic->ctlr.get_src_addr = fnic_get_mac; 7668c2ecf20Sopenharmony_ci if (fnic->config.flags & VFCF_FIP_CAPABLE) { 7678c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 7688c2ecf20Sopenharmony_ci "firmware supports FIP\n"); 7698c2ecf20Sopenharmony_ci /* enable directed and multicast */ 7708c2ecf20Sopenharmony_ci vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0); 7718c2ecf20Sopenharmony_ci vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS); 7728c2ecf20Sopenharmony_ci vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr); 7738c2ecf20Sopenharmony_ci fnic->set_vlan = fnic_set_vlan; 7748c2ecf20Sopenharmony_ci fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO); 7758c2ecf20Sopenharmony_ci timer_setup(&fnic->fip_timer, fnic_fip_notify_timer, 0); 7768c2ecf20Sopenharmony_ci spin_lock_init(&fnic->vlans_lock); 7778c2ecf20Sopenharmony_ci INIT_WORK(&fnic->fip_frame_work, fnic_handle_fip_frame); 7788c2ecf20Sopenharmony_ci INIT_WORK(&fnic->event_work, fnic_handle_event); 7798c2ecf20Sopenharmony_ci skb_queue_head_init(&fnic->fip_frame_queue); 7808c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&fnic->evlist); 7818c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&fnic->vlans); 7828c2ecf20Sopenharmony_ci } else { 7838c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 7848c2ecf20Sopenharmony_ci "firmware uses non-FIP mode\n"); 7858c2ecf20Sopenharmony_ci fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_NON_FIP); 7868c2ecf20Sopenharmony_ci fnic->ctlr.state = FIP_ST_NON_FIP; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci fnic->state = FNIC_IN_FC_MODE; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci atomic_set(&fnic->in_flight, 0); 7918c2ecf20Sopenharmony_ci fnic->state_flags = FNIC_FLAGS_NONE; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Enable hardware stripping of vlan header on ingress */ 7948c2ecf20Sopenharmony_ci fnic_set_nic_config(fnic, 0, 0, 0, 0, 0, 0, 1); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Setup notification buffer area */ 7978c2ecf20Sopenharmony_ci err = fnic_notify_set(fnic); 7988c2ecf20Sopenharmony_ci if (err) { 7998c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 8008c2ecf20Sopenharmony_ci "Failed to alloc notify buffer, aborting.\n"); 8018c2ecf20Sopenharmony_ci goto err_out_free_max_pool; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Setup notify timer when using MSI interrupts */ 8058c2ecf20Sopenharmony_ci if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI) 8068c2ecf20Sopenharmony_ci timer_setup(&fnic->notify_timer, fnic_notify_timer, 0); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* allocate RQ buffers and post them to RQ*/ 8098c2ecf20Sopenharmony_ci for (i = 0; i < fnic->rq_count; i++) { 8108c2ecf20Sopenharmony_ci vnic_rq_enable(&fnic->rq[i]); 8118c2ecf20Sopenharmony_ci err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame); 8128c2ecf20Sopenharmony_ci if (err) { 8138c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 8148c2ecf20Sopenharmony_ci "fnic_alloc_rq_frame can't alloc " 8158c2ecf20Sopenharmony_ci "frame\n"); 8168c2ecf20Sopenharmony_ci goto err_out_free_rq_buf; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* 8218c2ecf20Sopenharmony_ci * Initialization done with PCI system, hardware, firmware. 8228c2ecf20Sopenharmony_ci * Add host to SCSI 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci err = scsi_add_host(lp->host, &pdev->dev); 8258c2ecf20Sopenharmony_ci if (err) { 8268c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 8278c2ecf20Sopenharmony_ci "fnic: scsi_add_host failed...exiting\n"); 8288c2ecf20Sopenharmony_ci goto err_out_free_rq_buf; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* Start local port initiatialization */ 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci lp->link_up = 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci lp->max_retry_count = fnic->config.flogi_retries; 8368c2ecf20Sopenharmony_ci lp->max_rport_retry_count = fnic->config.plogi_retries; 8378c2ecf20Sopenharmony_ci lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | 8388c2ecf20Sopenharmony_ci FCP_SPPF_CONF_COMPL); 8398c2ecf20Sopenharmony_ci if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR) 8408c2ecf20Sopenharmony_ci lp->service_params |= FCP_SPPF_RETRY; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci lp->boot_time = jiffies; 8438c2ecf20Sopenharmony_ci lp->e_d_tov = fnic->config.ed_tov; 8448c2ecf20Sopenharmony_ci lp->r_a_tov = fnic->config.ra_tov; 8458c2ecf20Sopenharmony_ci lp->link_supported_speeds = FC_PORTSPEED_10GBIT; 8468c2ecf20Sopenharmony_ci fc_set_wwnn(lp, fnic->config.node_wwn); 8478c2ecf20Sopenharmony_ci fc_set_wwpn(lp, fnic->config.port_wwn); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template, 0); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START, 8528c2ecf20Sopenharmony_ci FCPIO_HOST_EXCH_RANGE_END, NULL)) { 8538c2ecf20Sopenharmony_ci err = -ENOMEM; 8548c2ecf20Sopenharmony_ci goto err_out_remove_scsi_host; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci fc_lport_init_stats(lp); 8588c2ecf20Sopenharmony_ci fnic->stats_reset_time = jiffies; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci fc_lport_config(lp); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (fc_set_mfs(lp, fnic->config.maxdatafieldsize + 8638c2ecf20Sopenharmony_ci sizeof(struct fc_frame_header))) { 8648c2ecf20Sopenharmony_ci err = -EINVAL; 8658c2ecf20Sopenharmony_ci goto err_out_free_exch_mgr; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci fc_host_maxframe_size(lp->host) = lp->mfs; 8688c2ecf20Sopenharmony_ci fc_host_dev_loss_tmo(lp->host) = fnic->config.port_down_timeout / 1000; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci sprintf(fc_host_symbolic_name(lp->host), 8718c2ecf20Sopenharmony_ci DRV_NAME " v" DRV_VERSION " over %s", fnic->name); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic_list_lock, flags); 8748c2ecf20Sopenharmony_ci list_add_tail(&fnic->list, &fnic_list); 8758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic_list_lock, flags); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci INIT_WORK(&fnic->link_work, fnic_handle_link); 8788c2ecf20Sopenharmony_ci INIT_WORK(&fnic->frame_work, fnic_handle_frame); 8798c2ecf20Sopenharmony_ci skb_queue_head_init(&fnic->frame_queue); 8808c2ecf20Sopenharmony_ci skb_queue_head_init(&fnic->tx_queue); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* Enable all queues */ 8838c2ecf20Sopenharmony_ci for (i = 0; i < fnic->raw_wq_count; i++) 8848c2ecf20Sopenharmony_ci vnic_wq_enable(&fnic->wq[i]); 8858c2ecf20Sopenharmony_ci for (i = 0; i < fnic->wq_copy_count; i++) 8868c2ecf20Sopenharmony_ci vnic_wq_copy_enable(&fnic->wq_copy[i]); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci fc_fabric_login(lp); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci err = fnic_request_intr(fnic); 8918c2ecf20Sopenharmony_ci if (err) { 8928c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 8938c2ecf20Sopenharmony_ci "Unable to request irq.\n"); 8948c2ecf20Sopenharmony_ci goto err_out_free_exch_mgr; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci vnic_dev_enable(fnic->vdev); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci for (i = 0; i < fnic->intr_count; i++) 9008c2ecf20Sopenharmony_ci vnic_intr_unmask(&fnic->intr[i]); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci fnic_notify_timer_start(fnic); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return 0; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cierr_out_free_exch_mgr: 9078c2ecf20Sopenharmony_ci fc_exch_mgr_free(lp); 9088c2ecf20Sopenharmony_cierr_out_remove_scsi_host: 9098c2ecf20Sopenharmony_ci fc_remove_host(lp->host); 9108c2ecf20Sopenharmony_ci scsi_remove_host(lp->host); 9118c2ecf20Sopenharmony_cierr_out_free_rq_buf: 9128c2ecf20Sopenharmony_ci for (i = 0; i < fnic->rq_count; i++) 9138c2ecf20Sopenharmony_ci vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf); 9148c2ecf20Sopenharmony_ci vnic_dev_notify_unset(fnic->vdev); 9158c2ecf20Sopenharmony_cierr_out_free_max_pool: 9168c2ecf20Sopenharmony_ci mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX]); 9178c2ecf20Sopenharmony_cierr_out_free_dflt_pool: 9188c2ecf20Sopenharmony_ci mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT]); 9198c2ecf20Sopenharmony_cierr_out_free_ioreq_pool: 9208c2ecf20Sopenharmony_ci mempool_destroy(fnic->io_req_pool); 9218c2ecf20Sopenharmony_cierr_out_free_resources: 9228c2ecf20Sopenharmony_ci fnic_free_vnic_resources(fnic); 9238c2ecf20Sopenharmony_cierr_out_clear_intr: 9248c2ecf20Sopenharmony_ci fnic_clear_intr_mode(fnic); 9258c2ecf20Sopenharmony_cierr_out_dev_close: 9268c2ecf20Sopenharmony_ci vnic_dev_close(fnic->vdev); 9278c2ecf20Sopenharmony_cierr_out_dev_cmd_deinit: 9288c2ecf20Sopenharmony_cierr_out_vnic_unregister: 9298c2ecf20Sopenharmony_ci vnic_dev_unregister(fnic->vdev); 9308c2ecf20Sopenharmony_cierr_out_iounmap: 9318c2ecf20Sopenharmony_ci fnic_iounmap(fnic); 9328c2ecf20Sopenharmony_cierr_out_release_regions: 9338c2ecf20Sopenharmony_ci pci_release_regions(pdev); 9348c2ecf20Sopenharmony_cierr_out_disable_device: 9358c2ecf20Sopenharmony_ci pci_disable_device(pdev); 9368c2ecf20Sopenharmony_cierr_out_free_hba: 9378c2ecf20Sopenharmony_ci fnic_stats_debugfs_remove(fnic); 9388c2ecf20Sopenharmony_ci scsi_host_put(lp->host); 9398c2ecf20Sopenharmony_cierr_out: 9408c2ecf20Sopenharmony_ci return err; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic void fnic_remove(struct pci_dev *pdev) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct fnic *fnic = pci_get_drvdata(pdev); 9468c2ecf20Sopenharmony_ci struct fc_lport *lp = fnic->lport; 9478c2ecf20Sopenharmony_ci unsigned long flags; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* 9508c2ecf20Sopenharmony_ci * Mark state so that the workqueue thread stops forwarding 9518c2ecf20Sopenharmony_ci * received frames and link events to the local port. ISR and 9528c2ecf20Sopenharmony_ci * other threads that can queue work items will also stop 9538c2ecf20Sopenharmony_ci * creating work items on the fnic workqueue 9548c2ecf20Sopenharmony_ci */ 9558c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 9568c2ecf20Sopenharmony_ci fnic->stop_rx_link_events = 1; 9578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI) 9608c2ecf20Sopenharmony_ci del_timer_sync(&fnic->notify_timer); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * Flush the fnic event queue. After this call, there should 9648c2ecf20Sopenharmony_ci * be no event queued for this fnic device in the workqueue 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci flush_workqueue(fnic_event_queue); 9678c2ecf20Sopenharmony_ci skb_queue_purge(&fnic->frame_queue); 9688c2ecf20Sopenharmony_ci skb_queue_purge(&fnic->tx_queue); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (fnic->config.flags & VFCF_FIP_CAPABLE) { 9718c2ecf20Sopenharmony_ci del_timer_sync(&fnic->fip_timer); 9728c2ecf20Sopenharmony_ci skb_queue_purge(&fnic->fip_frame_queue); 9738c2ecf20Sopenharmony_ci fnic_fcoe_reset_vlans(fnic); 9748c2ecf20Sopenharmony_ci fnic_fcoe_evlist_free(fnic); 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* 9788c2ecf20Sopenharmony_ci * Log off the fabric. This stops all remote ports, dns port, 9798c2ecf20Sopenharmony_ci * logs off the fabric. This flushes all rport, disc, lport work 9808c2ecf20Sopenharmony_ci * before returning 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci fc_fabric_logoff(fnic->lport); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 9858c2ecf20Sopenharmony_ci fnic->in_remove = 1; 9868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci fcoe_ctlr_destroy(&fnic->ctlr); 9898c2ecf20Sopenharmony_ci fc_lport_destroy(lp); 9908c2ecf20Sopenharmony_ci fnic_stats_debugfs_remove(fnic); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* 9938c2ecf20Sopenharmony_ci * This stops the fnic device, masks all interrupts. Completed 9948c2ecf20Sopenharmony_ci * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are 9958c2ecf20Sopenharmony_ci * cleaned up 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ci fnic_cleanup(fnic); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci BUG_ON(!skb_queue_empty(&fnic->frame_queue)); 10008c2ecf20Sopenharmony_ci BUG_ON(!skb_queue_empty(&fnic->tx_queue)); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic_list_lock, flags); 10038c2ecf20Sopenharmony_ci list_del(&fnic->list); 10048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic_list_lock, flags); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci fc_remove_host(fnic->lport->host); 10078c2ecf20Sopenharmony_ci scsi_remove_host(fnic->lport->host); 10088c2ecf20Sopenharmony_ci fc_exch_mgr_free(fnic->lport); 10098c2ecf20Sopenharmony_ci vnic_dev_notify_unset(fnic->vdev); 10108c2ecf20Sopenharmony_ci fnic_free_intr(fnic); 10118c2ecf20Sopenharmony_ci fnic_free_vnic_resources(fnic); 10128c2ecf20Sopenharmony_ci fnic_clear_intr_mode(fnic); 10138c2ecf20Sopenharmony_ci vnic_dev_close(fnic->vdev); 10148c2ecf20Sopenharmony_ci vnic_dev_unregister(fnic->vdev); 10158c2ecf20Sopenharmony_ci fnic_iounmap(fnic); 10168c2ecf20Sopenharmony_ci pci_release_regions(pdev); 10178c2ecf20Sopenharmony_ci pci_disable_device(pdev); 10188c2ecf20Sopenharmony_ci scsi_host_put(lp->host); 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic struct pci_driver fnic_driver = { 10228c2ecf20Sopenharmony_ci .name = DRV_NAME, 10238c2ecf20Sopenharmony_ci .id_table = fnic_id_table, 10248c2ecf20Sopenharmony_ci .probe = fnic_probe, 10258c2ecf20Sopenharmony_ci .remove = fnic_remove, 10268c2ecf20Sopenharmony_ci}; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic int __init fnic_init_module(void) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci size_t len; 10318c2ecf20Sopenharmony_ci int err = 0; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* Create debugfs entries for fnic */ 10368c2ecf20Sopenharmony_ci err = fnic_debugfs_init(); 10378c2ecf20Sopenharmony_ci if (err < 0) { 10388c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Failed to create fnic directory " 10398c2ecf20Sopenharmony_ci "for tracing and stats logging\n"); 10408c2ecf20Sopenharmony_ci fnic_debugfs_terminate(); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* Allocate memory for trace buffer */ 10448c2ecf20Sopenharmony_ci err = fnic_trace_buf_init(); 10458c2ecf20Sopenharmony_ci if (err < 0) { 10468c2ecf20Sopenharmony_ci printk(KERN_ERR PFX 10478c2ecf20Sopenharmony_ci "Trace buffer initialization Failed. " 10488c2ecf20Sopenharmony_ci "Fnic Tracing utility is disabled\n"); 10498c2ecf20Sopenharmony_ci fnic_trace_free(); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Allocate memory for fc trace buffer */ 10538c2ecf20Sopenharmony_ci err = fnic_fc_trace_init(); 10548c2ecf20Sopenharmony_ci if (err < 0) { 10558c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "FC trace buffer initialization Failed " 10568c2ecf20Sopenharmony_ci "FC frame tracing utility is disabled\n"); 10578c2ecf20Sopenharmony_ci fnic_fc_trace_free(); 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Create a cache for allocation of default size sgls */ 10618c2ecf20Sopenharmony_ci len = sizeof(struct fnic_dflt_sgl_list); 10628c2ecf20Sopenharmony_ci fnic_sgl_cache[FNIC_SGL_CACHE_DFLT] = kmem_cache_create 10638c2ecf20Sopenharmony_ci ("fnic_sgl_dflt", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN, 10648c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN, 10658c2ecf20Sopenharmony_ci NULL); 10668c2ecf20Sopenharmony_ci if (!fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]) { 10678c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "failed to create fnic dflt sgl slab\n"); 10688c2ecf20Sopenharmony_ci err = -ENOMEM; 10698c2ecf20Sopenharmony_ci goto err_create_fnic_sgl_slab_dflt; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* Create a cache for allocation of max size sgls*/ 10738c2ecf20Sopenharmony_ci len = sizeof(struct fnic_sgl_list); 10748c2ecf20Sopenharmony_ci fnic_sgl_cache[FNIC_SGL_CACHE_MAX] = kmem_cache_create 10758c2ecf20Sopenharmony_ci ("fnic_sgl_max", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN, 10768c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN, 10778c2ecf20Sopenharmony_ci NULL); 10788c2ecf20Sopenharmony_ci if (!fnic_sgl_cache[FNIC_SGL_CACHE_MAX]) { 10798c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "failed to create fnic max sgl slab\n"); 10808c2ecf20Sopenharmony_ci err = -ENOMEM; 10818c2ecf20Sopenharmony_ci goto err_create_fnic_sgl_slab_max; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* Create a cache of io_req structs for use via mempool */ 10858c2ecf20Sopenharmony_ci fnic_io_req_cache = kmem_cache_create("fnic_io_req", 10868c2ecf20Sopenharmony_ci sizeof(struct fnic_io_req), 10878c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, NULL); 10888c2ecf20Sopenharmony_ci if (!fnic_io_req_cache) { 10898c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "failed to create fnic io_req slab\n"); 10908c2ecf20Sopenharmony_ci err = -ENOMEM; 10918c2ecf20Sopenharmony_ci goto err_create_fnic_ioreq_slab; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci fnic_event_queue = create_singlethread_workqueue("fnic_event_wq"); 10958c2ecf20Sopenharmony_ci if (!fnic_event_queue) { 10968c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "fnic work queue create failed\n"); 10978c2ecf20Sopenharmony_ci err = -ENOMEM; 10988c2ecf20Sopenharmony_ci goto err_create_fnic_workq; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci spin_lock_init(&fnic_list_lock); 11028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&fnic_list); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci fnic_fip_queue = create_singlethread_workqueue("fnic_fip_q"); 11058c2ecf20Sopenharmony_ci if (!fnic_fip_queue) { 11068c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "fnic FIP work queue create failed\n"); 11078c2ecf20Sopenharmony_ci err = -ENOMEM; 11088c2ecf20Sopenharmony_ci goto err_create_fip_workq; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci fnic_fc_transport = fc_attach_transport(&fnic_fc_functions); 11128c2ecf20Sopenharmony_ci if (!fnic_fc_transport) { 11138c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "fc_attach_transport error\n"); 11148c2ecf20Sopenharmony_ci err = -ENOMEM; 11158c2ecf20Sopenharmony_ci goto err_fc_transport; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* register the driver with PCI system */ 11198c2ecf20Sopenharmony_ci err = pci_register_driver(&fnic_driver); 11208c2ecf20Sopenharmony_ci if (err < 0) { 11218c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "pci register error\n"); 11228c2ecf20Sopenharmony_ci goto err_pci_register; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci return err; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cierr_pci_register: 11278c2ecf20Sopenharmony_ci fc_release_transport(fnic_fc_transport); 11288c2ecf20Sopenharmony_cierr_fc_transport: 11298c2ecf20Sopenharmony_ci destroy_workqueue(fnic_fip_queue); 11308c2ecf20Sopenharmony_cierr_create_fip_workq: 11318c2ecf20Sopenharmony_ci destroy_workqueue(fnic_event_queue); 11328c2ecf20Sopenharmony_cierr_create_fnic_workq: 11338c2ecf20Sopenharmony_ci kmem_cache_destroy(fnic_io_req_cache); 11348c2ecf20Sopenharmony_cierr_create_fnic_ioreq_slab: 11358c2ecf20Sopenharmony_ci kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]); 11368c2ecf20Sopenharmony_cierr_create_fnic_sgl_slab_max: 11378c2ecf20Sopenharmony_ci kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]); 11388c2ecf20Sopenharmony_cierr_create_fnic_sgl_slab_dflt: 11398c2ecf20Sopenharmony_ci fnic_trace_free(); 11408c2ecf20Sopenharmony_ci fnic_fc_trace_free(); 11418c2ecf20Sopenharmony_ci fnic_debugfs_terminate(); 11428c2ecf20Sopenharmony_ci return err; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic void __exit fnic_cleanup_module(void) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci pci_unregister_driver(&fnic_driver); 11488c2ecf20Sopenharmony_ci destroy_workqueue(fnic_event_queue); 11498c2ecf20Sopenharmony_ci if (fnic_fip_queue) { 11508c2ecf20Sopenharmony_ci flush_workqueue(fnic_fip_queue); 11518c2ecf20Sopenharmony_ci destroy_workqueue(fnic_fip_queue); 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]); 11548c2ecf20Sopenharmony_ci kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]); 11558c2ecf20Sopenharmony_ci kmem_cache_destroy(fnic_io_req_cache); 11568c2ecf20Sopenharmony_ci fc_release_transport(fnic_fc_transport); 11578c2ecf20Sopenharmony_ci fnic_trace_free(); 11588c2ecf20Sopenharmony_ci fnic_fc_trace_free(); 11598c2ecf20Sopenharmony_ci fnic_debugfs_terminate(); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cimodule_init(fnic_init_module); 11638c2ecf20Sopenharmony_cimodule_exit(fnic_cleanup_module); 11648c2ecf20Sopenharmony_ci 1165