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