162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
462306a36Sopenharmony_ci * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/mempool.h>
862306a36Sopenharmony_ci#include <linux/string.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include <linux/skbuff.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/spinlock.h>
1662306a36Sopenharmony_ci#include <linux/workqueue.h>
1762306a36Sopenharmony_ci#include <linux/if_ether.h>
1862306a36Sopenharmony_ci#include <scsi/fc/fc_fip.h>
1962306a36Sopenharmony_ci#include <scsi/scsi_host.h>
2062306a36Sopenharmony_ci#include <scsi/scsi_transport.h>
2162306a36Sopenharmony_ci#include <scsi/scsi_transport_fc.h>
2262306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
2362306a36Sopenharmony_ci#include <scsi/libfc.h>
2462306a36Sopenharmony_ci#include <scsi/fc_frame.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "vnic_dev.h"
2762306a36Sopenharmony_ci#include "vnic_intr.h"
2862306a36Sopenharmony_ci#include "vnic_stats.h"
2962306a36Sopenharmony_ci#include "fnic_io.h"
3062306a36Sopenharmony_ci#include "fnic_fip.h"
3162306a36Sopenharmony_ci#include "fnic.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define PCI_DEVICE_ID_CISCO_FNIC	0x0045
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* Timer to poll notification area for events. Used for MSI interrupts */
3662306a36Sopenharmony_ci#define FNIC_NOTIFY_TIMER_PERIOD	(2 * HZ)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct kmem_cache *fnic_sgl_cache[FNIC_SGL_NUM_CACHES];
3962306a36Sopenharmony_cistatic struct kmem_cache *fnic_io_req_cache;
4062306a36Sopenharmony_cistatic LIST_HEAD(fnic_list);
4162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(fnic_list_lock);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Supported devices by fnic module */
4462306a36Sopenharmony_cistatic struct pci_device_id fnic_id_table[] = {
4562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_CISCO_FNIC) },
4662306a36Sopenharmony_ci	{ 0, }
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
5062306a36Sopenharmony_ciMODULE_AUTHOR("Abhijeet Joglekar <abjoglek@cisco.com>, "
5162306a36Sopenharmony_ci	      "Joseph R. Eykholt <jeykholt@cisco.com>");
5262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
5362306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
5462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fnic_id_table);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciunsigned int fnic_log_level;
5762306a36Sopenharmony_cimodule_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
5862306a36Sopenharmony_ciMODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciunsigned int io_completions = FNIC_DFLT_IO_COMPLETIONS;
6262306a36Sopenharmony_cimodule_param(io_completions, int, S_IRUGO|S_IWUSR);
6362306a36Sopenharmony_ciMODULE_PARM_DESC(io_completions, "Max CQ entries to process at a time");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciunsigned int fnic_trace_max_pages = 16;
6662306a36Sopenharmony_cimodule_param(fnic_trace_max_pages, uint, S_IRUGO|S_IWUSR);
6762306a36Sopenharmony_ciMODULE_PARM_DESC(fnic_trace_max_pages, "Total allocated memory pages "
6862306a36Sopenharmony_ci					"for fnic trace buffer");
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciunsigned int fnic_fc_trace_max_pages = 64;
7162306a36Sopenharmony_cimodule_param(fnic_fc_trace_max_pages, uint, S_IRUGO|S_IWUSR);
7262306a36Sopenharmony_ciMODULE_PARM_DESC(fnic_fc_trace_max_pages,
7362306a36Sopenharmony_ci		 "Total allocated memory pages for fc trace buffer");
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic unsigned int fnic_max_qdepth = FNIC_DFLT_QUEUE_DEPTH;
7662306a36Sopenharmony_cimodule_param(fnic_max_qdepth, uint, S_IRUGO|S_IWUSR);
7762306a36Sopenharmony_ciMODULE_PARM_DESC(fnic_max_qdepth, "Queue depth to report for each LUN");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct libfc_function_template fnic_transport_template = {
8062306a36Sopenharmony_ci	.frame_send = fnic_send,
8162306a36Sopenharmony_ci	.lport_set_port_id = fnic_set_port_id,
8262306a36Sopenharmony_ci	.fcp_abort_io = fnic_empty_scsi_cleanup,
8362306a36Sopenharmony_ci	.fcp_cleanup = fnic_empty_scsi_cleanup,
8462306a36Sopenharmony_ci	.exch_mgr_reset = fnic_exch_mgr_reset
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int fnic_slave_alloc(struct scsi_device *sdev)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (!rport || fc_remote_port_chkready(rport))
9262306a36Sopenharmony_ci		return -ENXIO;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	scsi_change_queue_depth(sdev, fnic_max_qdepth);
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct scsi_host_template fnic_host_template = {
9962306a36Sopenharmony_ci	.module = THIS_MODULE,
10062306a36Sopenharmony_ci	.name = DRV_NAME,
10162306a36Sopenharmony_ci	.queuecommand = fnic_queuecommand,
10262306a36Sopenharmony_ci	.eh_timed_out = fc_eh_timed_out,
10362306a36Sopenharmony_ci	.eh_abort_handler = fnic_abort_cmd,
10462306a36Sopenharmony_ci	.eh_device_reset_handler = fnic_device_reset,
10562306a36Sopenharmony_ci	.eh_host_reset_handler = fnic_host_reset,
10662306a36Sopenharmony_ci	.slave_alloc = fnic_slave_alloc,
10762306a36Sopenharmony_ci	.change_queue_depth = scsi_change_queue_depth,
10862306a36Sopenharmony_ci	.this_id = -1,
10962306a36Sopenharmony_ci	.cmd_per_lun = 3,
11062306a36Sopenharmony_ci	.can_queue = FNIC_DFLT_IO_REQ,
11162306a36Sopenharmony_ci	.sg_tablesize = FNIC_MAX_SG_DESC_CNT,
11262306a36Sopenharmony_ci	.max_sectors = 0xffff,
11362306a36Sopenharmony_ci	.shost_groups = fnic_host_groups,
11462306a36Sopenharmony_ci	.track_queue_depth = 1,
11562306a36Sopenharmony_ci	.cmd_size = sizeof(struct fnic_cmd_priv),
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void
11962306a36Sopenharmony_cifnic_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	if (timeout)
12262306a36Sopenharmony_ci		rport->dev_loss_tmo = timeout;
12362306a36Sopenharmony_ci	else
12462306a36Sopenharmony_ci		rport->dev_loss_tmo = 1;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void fnic_get_host_speed(struct Scsi_Host *shost);
12862306a36Sopenharmony_cistatic struct scsi_transport_template *fnic_fc_transport;
12962306a36Sopenharmony_cistatic struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *);
13062306a36Sopenharmony_cistatic void fnic_reset_host_stats(struct Scsi_Host *);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct fc_function_template fnic_fc_functions = {
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	.show_host_node_name = 1,
13562306a36Sopenharmony_ci	.show_host_port_name = 1,
13662306a36Sopenharmony_ci	.show_host_supported_classes = 1,
13762306a36Sopenharmony_ci	.show_host_supported_fc4s = 1,
13862306a36Sopenharmony_ci	.show_host_active_fc4s = 1,
13962306a36Sopenharmony_ci	.show_host_maxframe_size = 1,
14062306a36Sopenharmony_ci	.show_host_port_id = 1,
14162306a36Sopenharmony_ci	.show_host_supported_speeds = 1,
14262306a36Sopenharmony_ci	.get_host_speed = fnic_get_host_speed,
14362306a36Sopenharmony_ci	.show_host_speed = 1,
14462306a36Sopenharmony_ci	.show_host_port_type = 1,
14562306a36Sopenharmony_ci	.get_host_port_state = fc_get_host_port_state,
14662306a36Sopenharmony_ci	.show_host_port_state = 1,
14762306a36Sopenharmony_ci	.show_host_symbolic_name = 1,
14862306a36Sopenharmony_ci	.show_rport_maxframe_size = 1,
14962306a36Sopenharmony_ci	.show_rport_supported_classes = 1,
15062306a36Sopenharmony_ci	.show_host_fabric_name = 1,
15162306a36Sopenharmony_ci	.show_starget_node_name = 1,
15262306a36Sopenharmony_ci	.show_starget_port_name = 1,
15362306a36Sopenharmony_ci	.show_starget_port_id = 1,
15462306a36Sopenharmony_ci	.show_rport_dev_loss_tmo = 1,
15562306a36Sopenharmony_ci	.set_rport_dev_loss_tmo = fnic_set_rport_dev_loss_tmo,
15662306a36Sopenharmony_ci	.issue_fc_host_lip = fnic_reset,
15762306a36Sopenharmony_ci	.get_fc_host_stats = fnic_get_stats,
15862306a36Sopenharmony_ci	.reset_fc_host_stats = fnic_reset_host_stats,
15962306a36Sopenharmony_ci	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
16062306a36Sopenharmony_ci	.terminate_rport_io = fnic_terminate_rport_io,
16162306a36Sopenharmony_ci	.bsg_request = fc_lport_bsg_request,
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void fnic_get_host_speed(struct Scsi_Host *shost)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct fc_lport *lp = shost_priv(shost);
16762306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lp);
16862306a36Sopenharmony_ci	u32 port_speed = vnic_dev_port_speed(fnic->vdev);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Add in other values as they get defined in fw */
17162306a36Sopenharmony_ci	switch (port_speed) {
17262306a36Sopenharmony_ci	case DCEM_PORTSPEED_10G:
17362306a36Sopenharmony_ci		fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case DCEM_PORTSPEED_20G:
17662306a36Sopenharmony_ci		fc_host_speed(shost) = FC_PORTSPEED_20GBIT;
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case DCEM_PORTSPEED_25G:
17962306a36Sopenharmony_ci		fc_host_speed(shost) = FC_PORTSPEED_25GBIT;
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case DCEM_PORTSPEED_40G:
18262306a36Sopenharmony_ci	case DCEM_PORTSPEED_4x10G:
18362306a36Sopenharmony_ci		fc_host_speed(shost) = FC_PORTSPEED_40GBIT;
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	case DCEM_PORTSPEED_100G:
18662306a36Sopenharmony_ci		fc_host_speed(shost) = FC_PORTSPEED_100GBIT;
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	default:
18962306a36Sopenharmony_ci		fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	int ret;
19762306a36Sopenharmony_ci	struct fc_lport *lp = shost_priv(host);
19862306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lp);
19962306a36Sopenharmony_ci	struct fc_host_statistics *stats = &lp->host_stats;
20062306a36Sopenharmony_ci	struct vnic_stats *vs;
20162306a36Sopenharmony_ci	unsigned long flags;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (time_before(jiffies, fnic->stats_time + HZ / FNIC_STATS_RATE_LIMIT))
20462306a36Sopenharmony_ci		return stats;
20562306a36Sopenharmony_ci	fnic->stats_time = jiffies;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
20862306a36Sopenharmony_ci	ret = vnic_dev_stats_dump(fnic->vdev, &fnic->stats);
20962306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (ret) {
21262306a36Sopenharmony_ci		FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host,
21362306a36Sopenharmony_ci			      "fnic: Get vnic stats failed"
21462306a36Sopenharmony_ci			      " 0x%x", ret);
21562306a36Sopenharmony_ci		return stats;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	vs = fnic->stats;
21862306a36Sopenharmony_ci	stats->tx_frames = vs->tx.tx_unicast_frames_ok;
21962306a36Sopenharmony_ci	stats->tx_words  = vs->tx.tx_unicast_bytes_ok / 4;
22062306a36Sopenharmony_ci	stats->rx_frames = vs->rx.rx_unicast_frames_ok;
22162306a36Sopenharmony_ci	stats->rx_words  = vs->rx.rx_unicast_bytes_ok / 4;
22262306a36Sopenharmony_ci	stats->error_frames = vs->tx.tx_errors + vs->rx.rx_errors;
22362306a36Sopenharmony_ci	stats->dumped_frames = vs->tx.tx_drops + vs->rx.rx_drop;
22462306a36Sopenharmony_ci	stats->invalid_crc_count = vs->rx.rx_crc_errors;
22562306a36Sopenharmony_ci	stats->seconds_since_last_reset =
22662306a36Sopenharmony_ci			(jiffies - fnic->stats_reset_time) / HZ;
22762306a36Sopenharmony_ci	stats->fcp_input_megabytes = div_u64(fnic->fcp_input_bytes, 1000000);
22862306a36Sopenharmony_ci	stats->fcp_output_megabytes = div_u64(fnic->fcp_output_bytes, 1000000);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return stats;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * fnic_dump_fchost_stats
23562306a36Sopenharmony_ci * note : dumps fc_statistics into system logs
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_civoid fnic_dump_fchost_stats(struct Scsi_Host *host,
23862306a36Sopenharmony_ci				struct fc_host_statistics *stats)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
24162306a36Sopenharmony_ci			"fnic: seconds since last reset = %llu\n",
24262306a36Sopenharmony_ci			stats->seconds_since_last_reset);
24362306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
24462306a36Sopenharmony_ci			"fnic: tx frames		= %llu\n",
24562306a36Sopenharmony_ci			stats->tx_frames);
24662306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
24762306a36Sopenharmony_ci			"fnic: tx words		= %llu\n",
24862306a36Sopenharmony_ci			stats->tx_words);
24962306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
25062306a36Sopenharmony_ci			"fnic: rx frames		= %llu\n",
25162306a36Sopenharmony_ci			stats->rx_frames);
25262306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
25362306a36Sopenharmony_ci			"fnic: rx words		= %llu\n",
25462306a36Sopenharmony_ci			stats->rx_words);
25562306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
25662306a36Sopenharmony_ci			"fnic: lip count		= %llu\n",
25762306a36Sopenharmony_ci			stats->lip_count);
25862306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
25962306a36Sopenharmony_ci			"fnic: nos count		= %llu\n",
26062306a36Sopenharmony_ci			stats->nos_count);
26162306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
26262306a36Sopenharmony_ci			"fnic: error frames		= %llu\n",
26362306a36Sopenharmony_ci			stats->error_frames);
26462306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
26562306a36Sopenharmony_ci			"fnic: dumped frames	= %llu\n",
26662306a36Sopenharmony_ci			stats->dumped_frames);
26762306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
26862306a36Sopenharmony_ci			"fnic: link failure count	= %llu\n",
26962306a36Sopenharmony_ci			stats->link_failure_count);
27062306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
27162306a36Sopenharmony_ci			"fnic: loss of sync count	= %llu\n",
27262306a36Sopenharmony_ci			stats->loss_of_sync_count);
27362306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
27462306a36Sopenharmony_ci			"fnic: loss of signal count	= %llu\n",
27562306a36Sopenharmony_ci			stats->loss_of_signal_count);
27662306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
27762306a36Sopenharmony_ci			"fnic: prim seq protocol err count = %llu\n",
27862306a36Sopenharmony_ci			stats->prim_seq_protocol_err_count);
27962306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
28062306a36Sopenharmony_ci			"fnic: invalid tx word count= %llu\n",
28162306a36Sopenharmony_ci			stats->invalid_tx_word_count);
28262306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
28362306a36Sopenharmony_ci			"fnic: invalid crc count	= %llu\n",
28462306a36Sopenharmony_ci			stats->invalid_crc_count);
28562306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
28662306a36Sopenharmony_ci			"fnic: fcp input requests	= %llu\n",
28762306a36Sopenharmony_ci			stats->fcp_input_requests);
28862306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
28962306a36Sopenharmony_ci			"fnic: fcp output requests	= %llu\n",
29062306a36Sopenharmony_ci			stats->fcp_output_requests);
29162306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
29262306a36Sopenharmony_ci			"fnic: fcp control requests	= %llu\n",
29362306a36Sopenharmony_ci			stats->fcp_control_requests);
29462306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
29562306a36Sopenharmony_ci			"fnic: fcp input megabytes	= %llu\n",
29662306a36Sopenharmony_ci			stats->fcp_input_megabytes);
29762306a36Sopenharmony_ci	FNIC_MAIN_NOTE(KERN_NOTICE, host,
29862306a36Sopenharmony_ci			"fnic: fcp output megabytes	= %llu\n",
29962306a36Sopenharmony_ci			stats->fcp_output_megabytes);
30062306a36Sopenharmony_ci	return;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * fnic_reset_host_stats : clears host stats
30562306a36Sopenharmony_ci * note : called when reset_statistics set under sysfs dir
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_cistatic void fnic_reset_host_stats(struct Scsi_Host *host)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	int ret;
31062306a36Sopenharmony_ci	struct fc_lport *lp = shost_priv(host);
31162306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lp);
31262306a36Sopenharmony_ci	struct fc_host_statistics *stats;
31362306a36Sopenharmony_ci	unsigned long flags;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* dump current stats, before clearing them */
31662306a36Sopenharmony_ci	stats = fnic_get_stats(host);
31762306a36Sopenharmony_ci	fnic_dump_fchost_stats(host, stats);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
32062306a36Sopenharmony_ci	ret = vnic_dev_stats_clear(fnic->vdev);
32162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (ret) {
32462306a36Sopenharmony_ci		FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host,
32562306a36Sopenharmony_ci				"fnic: Reset vnic stats failed"
32662306a36Sopenharmony_ci				" 0x%x", ret);
32762306a36Sopenharmony_ci		return;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	fnic->stats_reset_time = jiffies;
33062306a36Sopenharmony_ci	memset(stats, 0, sizeof(*stats));
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_civoid fnic_log_q_error(struct fnic *fnic)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	unsigned int i;
33862306a36Sopenharmony_ci	u32 error_status;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0; i < fnic->raw_wq_count; i++) {
34162306a36Sopenharmony_ci		error_status = ioread32(&fnic->wq[i].ctrl->error_status);
34262306a36Sopenharmony_ci		if (error_status)
34362306a36Sopenharmony_ci			shost_printk(KERN_ERR, fnic->lport->host,
34462306a36Sopenharmony_ci				     "WQ[%d] error_status"
34562306a36Sopenharmony_ci				     " %d\n", i, error_status);
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++) {
34962306a36Sopenharmony_ci		error_status = ioread32(&fnic->rq[i].ctrl->error_status);
35062306a36Sopenharmony_ci		if (error_status)
35162306a36Sopenharmony_ci			shost_printk(KERN_ERR, fnic->lport->host,
35262306a36Sopenharmony_ci				     "RQ[%d] error_status"
35362306a36Sopenharmony_ci				     " %d\n", i, error_status);
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	for (i = 0; i < fnic->wq_copy_count; i++) {
35762306a36Sopenharmony_ci		error_status = ioread32(&fnic->wq_copy[i].ctrl->error_status);
35862306a36Sopenharmony_ci		if (error_status)
35962306a36Sopenharmony_ci			shost_printk(KERN_ERR, fnic->lport->host,
36062306a36Sopenharmony_ci				     "CWQ[%d] error_status"
36162306a36Sopenharmony_ci				     " %d\n", i, error_status);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_civoid fnic_handle_link_event(struct fnic *fnic)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	unsigned long flags;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
37062306a36Sopenharmony_ci	if (fnic->stop_rx_link_events) {
37162306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
37262306a36Sopenharmony_ci		return;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	queue_work(fnic_event_queue, &fnic->link_work);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int fnic_notify_set(struct fnic *fnic)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	int err;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	switch (vnic_dev_get_intr_mode(fnic->vdev)) {
38562306a36Sopenharmony_ci	case VNIC_DEV_INTR_MODE_INTX:
38662306a36Sopenharmony_ci		err = vnic_dev_notify_set(fnic->vdev, FNIC_INTX_NOTIFY);
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci	case VNIC_DEV_INTR_MODE_MSI:
38962306a36Sopenharmony_ci		err = vnic_dev_notify_set(fnic->vdev, -1);
39062306a36Sopenharmony_ci		break;
39162306a36Sopenharmony_ci	case VNIC_DEV_INTR_MODE_MSIX:
39262306a36Sopenharmony_ci		err = vnic_dev_notify_set(fnic->vdev, FNIC_MSIX_ERR_NOTIFY);
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	default:
39562306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
39662306a36Sopenharmony_ci			     "Interrupt mode should be set up"
39762306a36Sopenharmony_ci			     " before devcmd notify set %d\n",
39862306a36Sopenharmony_ci			     vnic_dev_get_intr_mode(fnic->vdev));
39962306a36Sopenharmony_ci		err = -1;
40062306a36Sopenharmony_ci		break;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return err;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic void fnic_notify_timer(struct timer_list *t)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct fnic *fnic = from_timer(fnic, t, notify_timer);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	fnic_handle_link_event(fnic);
41162306a36Sopenharmony_ci	mod_timer(&fnic->notify_timer,
41262306a36Sopenharmony_ci		  round_jiffies(jiffies + FNIC_NOTIFY_TIMER_PERIOD));
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void fnic_fip_notify_timer(struct timer_list *t)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct fnic *fnic = from_timer(fnic, t, fip_timer);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	fnic_handle_fip_timer(fnic);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void fnic_notify_timer_start(struct fnic *fnic)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	switch (vnic_dev_get_intr_mode(fnic->vdev)) {
42562306a36Sopenharmony_ci	case VNIC_DEV_INTR_MODE_MSI:
42662306a36Sopenharmony_ci		/*
42762306a36Sopenharmony_ci		 * Schedule first timeout immediately. The driver is
42862306a36Sopenharmony_ci		 * initiatialized and ready to look for link up notification
42962306a36Sopenharmony_ci		 */
43062306a36Sopenharmony_ci		mod_timer(&fnic->notify_timer, jiffies);
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	default:
43362306a36Sopenharmony_ci		/* Using intr for notification for INTx/MSI-X */
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int fnic_dev_wait(struct vnic_dev *vdev,
43962306a36Sopenharmony_ci			 int (*start)(struct vnic_dev *, int),
44062306a36Sopenharmony_ci			 int (*finished)(struct vnic_dev *, int *),
44162306a36Sopenharmony_ci			 int arg)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	unsigned long time;
44462306a36Sopenharmony_ci	int done;
44562306a36Sopenharmony_ci	int err;
44662306a36Sopenharmony_ci	int count;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	count = 0;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	err = start(vdev, arg);
45162306a36Sopenharmony_ci	if (err)
45262306a36Sopenharmony_ci		return err;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* Wait for func to complete.
45562306a36Sopenharmony_ci	* Sometime schedule_timeout_uninterruptible take long time
45662306a36Sopenharmony_ci	* to wake up so we do not retry as we are only waiting for
45762306a36Sopenharmony_ci	* 2 seconds in while loop. By adding count, we make sure
45862306a36Sopenharmony_ci	* we try atleast three times before returning -ETIMEDOUT
45962306a36Sopenharmony_ci	*/
46062306a36Sopenharmony_ci	time = jiffies + (HZ * 2);
46162306a36Sopenharmony_ci	do {
46262306a36Sopenharmony_ci		err = finished(vdev, &done);
46362306a36Sopenharmony_ci		count++;
46462306a36Sopenharmony_ci		if (err)
46562306a36Sopenharmony_ci			return err;
46662306a36Sopenharmony_ci		if (done)
46762306a36Sopenharmony_ci			return 0;
46862306a36Sopenharmony_ci		schedule_timeout_uninterruptible(HZ / 10);
46962306a36Sopenharmony_ci	} while (time_after(time, jiffies) || (count < 3));
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return -ETIMEDOUT;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int fnic_cleanup(struct fnic *fnic)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	unsigned int i;
47762306a36Sopenharmony_ci	int err;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	vnic_dev_disable(fnic->vdev);
48062306a36Sopenharmony_ci	for (i = 0; i < fnic->intr_count; i++)
48162306a36Sopenharmony_ci		vnic_intr_mask(&fnic->intr[i]);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++) {
48462306a36Sopenharmony_ci		err = vnic_rq_disable(&fnic->rq[i]);
48562306a36Sopenharmony_ci		if (err)
48662306a36Sopenharmony_ci			return err;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci	for (i = 0; i < fnic->raw_wq_count; i++) {
48962306a36Sopenharmony_ci		err = vnic_wq_disable(&fnic->wq[i]);
49062306a36Sopenharmony_ci		if (err)
49162306a36Sopenharmony_ci			return err;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci	for (i = 0; i < fnic->wq_copy_count; i++) {
49462306a36Sopenharmony_ci		err = vnic_wq_copy_disable(&fnic->wq_copy[i]);
49562306a36Sopenharmony_ci		if (err)
49662306a36Sopenharmony_ci			return err;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* Clean up completed IOs and FCS frames */
50062306a36Sopenharmony_ci	fnic_wq_copy_cmpl_handler(fnic, io_completions);
50162306a36Sopenharmony_ci	fnic_wq_cmpl_handler(fnic, -1);
50262306a36Sopenharmony_ci	fnic_rq_cmpl_handler(fnic, -1);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Clean up the IOs and FCS frames that have not completed */
50562306a36Sopenharmony_ci	for (i = 0; i < fnic->raw_wq_count; i++)
50662306a36Sopenharmony_ci		vnic_wq_clean(&fnic->wq[i], fnic_free_wq_buf);
50762306a36Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++)
50862306a36Sopenharmony_ci		vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
50962306a36Sopenharmony_ci	for (i = 0; i < fnic->wq_copy_count; i++)
51062306a36Sopenharmony_ci		vnic_wq_copy_clean(&fnic->wq_copy[i],
51162306a36Sopenharmony_ci				   fnic_wq_copy_cleanup_handler);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	for (i = 0; i < fnic->cq_count; i++)
51462306a36Sopenharmony_ci		vnic_cq_clean(&fnic->cq[i]);
51562306a36Sopenharmony_ci	for (i = 0; i < fnic->intr_count; i++)
51662306a36Sopenharmony_ci		vnic_intr_clean(&fnic->intr[i]);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	mempool_destroy(fnic->io_req_pool);
51962306a36Sopenharmony_ci	for (i = 0; i < FNIC_SGL_NUM_CACHES; i++)
52062306a36Sopenharmony_ci		mempool_destroy(fnic->io_sgl_pool[i]);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return 0;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic void fnic_iounmap(struct fnic *fnic)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	if (fnic->bar0.vaddr)
52862306a36Sopenharmony_ci		iounmap(fnic->bar0.vaddr);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/**
53262306a36Sopenharmony_ci * fnic_get_mac() - get assigned data MAC address for FIP code.
53362306a36Sopenharmony_ci * @lport: 	local port.
53462306a36Sopenharmony_ci */
53562306a36Sopenharmony_cistatic u8 *fnic_get_mac(struct fc_lport *lport)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lport);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return fnic->data_src_addr;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic void fnic_set_vlan(struct fnic *fnic, u16 vlan_id)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	vnic_dev_set_default_vlan(fnic->vdev, vlan_id);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int fnic_scsi_drv_init(struct fnic *fnic)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct Scsi_Host *host = fnic->lport->host;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Configure maximum outstanding IO reqs*/
55262306a36Sopenharmony_ci	if (fnic->config.io_throttle_count != FNIC_UCSM_DFLT_THROTTLE_CNT_BLD)
55362306a36Sopenharmony_ci		host->can_queue = min_t(u32, FNIC_MAX_IO_REQ,
55462306a36Sopenharmony_ci					max_t(u32, FNIC_MIN_IO_REQ,
55562306a36Sopenharmony_ci					fnic->config.io_throttle_count));
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	fnic->fnic_max_tag_id = host->can_queue;
55862306a36Sopenharmony_ci	host->max_lun = fnic->config.luns_per_tgt;
55962306a36Sopenharmony_ci	host->max_id = FNIC_MAX_FCP_TARGET;
56062306a36Sopenharmony_ci	host->max_cmd_len = FCOE_MAX_CMD_LEN;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	host->nr_hw_queues = fnic->wq_copy_count;
56362306a36Sopenharmony_ci	if (host->nr_hw_queues > 1)
56462306a36Sopenharmony_ci		shost_printk(KERN_ERR, host,
56562306a36Sopenharmony_ci				"fnic: blk-mq is not supported");
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	host->nr_hw_queues = fnic->wq_copy_count = 1;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	shost_printk(KERN_INFO, host,
57062306a36Sopenharmony_ci			"fnic: can_queue: %d max_lun: %llu",
57162306a36Sopenharmony_ci			host->can_queue, host->max_lun);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	shost_printk(KERN_INFO, host,
57462306a36Sopenharmony_ci			"fnic: max_id: %d max_cmd_len: %d nr_hw_queues: %d",
57562306a36Sopenharmony_ci			host->max_id, host->max_cmd_len, host->nr_hw_queues);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return 0;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct Scsi_Host *host;
58362306a36Sopenharmony_ci	struct fc_lport *lp;
58462306a36Sopenharmony_ci	struct fnic *fnic;
58562306a36Sopenharmony_ci	mempool_t *pool;
58662306a36Sopenharmony_ci	int err;
58762306a36Sopenharmony_ci	int i;
58862306a36Sopenharmony_ci	unsigned long flags;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/*
59162306a36Sopenharmony_ci	 * Allocate SCSI Host and set up association between host,
59262306a36Sopenharmony_ci	 * local port, and fnic
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic));
59562306a36Sopenharmony_ci	if (!lp) {
59662306a36Sopenharmony_ci		printk(KERN_ERR PFX "Unable to alloc libfc local port\n");
59762306a36Sopenharmony_ci		err = -ENOMEM;
59862306a36Sopenharmony_ci		goto err_out;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci	host = lp->host;
60162306a36Sopenharmony_ci	fnic = lport_priv(lp);
60262306a36Sopenharmony_ci	fnic->lport = lp;
60362306a36Sopenharmony_ci	fnic->ctlr.lp = lp;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	fnic->link_events = 0;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
60862306a36Sopenharmony_ci		 host->host_no);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	host->transportt = fnic_fc_transport;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	fnic_stats_debugfs_init(fnic);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Setup PCI resources */
61562306a36Sopenharmony_ci	pci_set_drvdata(pdev, fnic);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	fnic->pdev = pdev;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	err = pci_enable_device(pdev);
62062306a36Sopenharmony_ci	if (err) {
62162306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
62262306a36Sopenharmony_ci			     "Cannot enable PCI device, aborting.\n");
62362306a36Sopenharmony_ci		goto err_out_free_hba;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
62762306a36Sopenharmony_ci	if (err) {
62862306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
62962306a36Sopenharmony_ci			     "Cannot enable PCI resources, aborting\n");
63062306a36Sopenharmony_ci		goto err_out_disable_device;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	pci_set_master(pdev);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	/* Query PCI controller on system for DMA addressing
63662306a36Sopenharmony_ci	 * limitation for the device.  Try 47-bit first, and
63762306a36Sopenharmony_ci	 * fail to 32-bit. Cisco VIC supports 47 bits only.
63862306a36Sopenharmony_ci	 */
63962306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(47));
64062306a36Sopenharmony_ci	if (err) {
64162306a36Sopenharmony_ci		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
64262306a36Sopenharmony_ci		if (err) {
64362306a36Sopenharmony_ci			shost_printk(KERN_ERR, fnic->lport->host,
64462306a36Sopenharmony_ci				     "No usable DMA configuration "
64562306a36Sopenharmony_ci				     "aborting\n");
64662306a36Sopenharmony_ci			goto err_out_release_regions;
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Map vNIC resources from BAR0 */
65162306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
65262306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
65362306a36Sopenharmony_ci			     "BAR0 not memory-map'able, aborting.\n");
65462306a36Sopenharmony_ci		err = -ENODEV;
65562306a36Sopenharmony_ci		goto err_out_release_regions;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	fnic->bar0.vaddr = pci_iomap(pdev, 0, 0);
65962306a36Sopenharmony_ci	fnic->bar0.bus_addr = pci_resource_start(pdev, 0);
66062306a36Sopenharmony_ci	fnic->bar0.len = pci_resource_len(pdev, 0);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!fnic->bar0.vaddr) {
66362306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
66462306a36Sopenharmony_ci			     "Cannot memory-map BAR0 res hdr, "
66562306a36Sopenharmony_ci			     "aborting.\n");
66662306a36Sopenharmony_ci		err = -ENODEV;
66762306a36Sopenharmony_ci		goto err_out_release_regions;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	fnic->vdev = vnic_dev_register(NULL, fnic, pdev, &fnic->bar0);
67162306a36Sopenharmony_ci	if (!fnic->vdev) {
67262306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
67362306a36Sopenharmony_ci			     "vNIC registration failed, "
67462306a36Sopenharmony_ci			     "aborting.\n");
67562306a36Sopenharmony_ci		err = -ENODEV;
67662306a36Sopenharmony_ci		goto err_out_iounmap;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	err = vnic_dev_cmd_init(fnic->vdev);
68062306a36Sopenharmony_ci	if (err) {
68162306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
68262306a36Sopenharmony_ci				"vnic_dev_cmd_init() returns %d, aborting\n",
68362306a36Sopenharmony_ci				err);
68462306a36Sopenharmony_ci		goto err_out_vnic_unregister;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
68862306a36Sopenharmony_ci			    vnic_dev_open_done, CMD_OPENF_RQ_ENABLE_THEN_POST);
68962306a36Sopenharmony_ci	if (err) {
69062306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
69162306a36Sopenharmony_ci			     "vNIC dev open failed, aborting.\n");
69262306a36Sopenharmony_ci		goto err_out_dev_cmd_deinit;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	err = vnic_dev_init(fnic->vdev, 0);
69662306a36Sopenharmony_ci	if (err) {
69762306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
69862306a36Sopenharmony_ci			     "vNIC dev init failed, aborting.\n");
69962306a36Sopenharmony_ci		goto err_out_dev_close;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
70362306a36Sopenharmony_ci	if (err) {
70462306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
70562306a36Sopenharmony_ci			     "vNIC get MAC addr failed \n");
70662306a36Sopenharmony_ci		goto err_out_dev_close;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci	/* set data_src for point-to-point mode and to keep it non-zero */
70962306a36Sopenharmony_ci	memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* Get vNIC configuration */
71262306a36Sopenharmony_ci	err = fnic_get_vnic_config(fnic);
71362306a36Sopenharmony_ci	if (err) {
71462306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
71562306a36Sopenharmony_ci			     "Get vNIC configuration failed, "
71662306a36Sopenharmony_ci			     "aborting.\n");
71762306a36Sopenharmony_ci		goto err_out_dev_close;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	fnic_scsi_drv_init(fnic);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	fnic_get_res_counts(fnic);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	err = fnic_set_intr_mode(fnic);
72562306a36Sopenharmony_ci	if (err) {
72662306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
72762306a36Sopenharmony_ci			     "Failed to set intr mode, "
72862306a36Sopenharmony_ci			     "aborting.\n");
72962306a36Sopenharmony_ci		goto err_out_dev_close;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	err = fnic_alloc_vnic_resources(fnic);
73362306a36Sopenharmony_ci	if (err) {
73462306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
73562306a36Sopenharmony_ci			     "Failed to alloc vNIC resources, "
73662306a36Sopenharmony_ci			     "aborting.\n");
73762306a36Sopenharmony_ci		goto err_out_clear_intr;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/* initialize all fnic locks */
74262306a36Sopenharmony_ci	spin_lock_init(&fnic->fnic_lock);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	for (i = 0; i < FNIC_WQ_MAX; i++)
74562306a36Sopenharmony_ci		spin_lock_init(&fnic->wq_lock[i]);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	for (i = 0; i < FNIC_WQ_COPY_MAX; i++) {
74862306a36Sopenharmony_ci		spin_lock_init(&fnic->wq_copy_lock[i]);
74962306a36Sopenharmony_ci		fnic->wq_copy_desc_low[i] = DESC_CLEAN_LOW_WATERMARK;
75062306a36Sopenharmony_ci		fnic->fw_ack_recd[i] = 0;
75162306a36Sopenharmony_ci		fnic->fw_ack_index[i] = -1;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	for (i = 0; i < FNIC_IO_LOCKS; i++)
75562306a36Sopenharmony_ci		spin_lock_init(&fnic->io_req_lock[i]);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	spin_lock_init(&fnic->sgreset_lock);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	err = -ENOMEM;
76062306a36Sopenharmony_ci	fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache);
76162306a36Sopenharmony_ci	if (!fnic->io_req_pool)
76262306a36Sopenharmony_ci		goto err_out_free_resources;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	pool = mempool_create_slab_pool(2, fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
76562306a36Sopenharmony_ci	if (!pool)
76662306a36Sopenharmony_ci		goto err_out_free_ioreq_pool;
76762306a36Sopenharmony_ci	fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT] = pool;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	pool = mempool_create_slab_pool(2, fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
77062306a36Sopenharmony_ci	if (!pool)
77162306a36Sopenharmony_ci		goto err_out_free_dflt_pool;
77262306a36Sopenharmony_ci	fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX] = pool;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	/* setup vlan config, hw inserts vlan header */
77562306a36Sopenharmony_ci	fnic->vlan_hw_insert = 1;
77662306a36Sopenharmony_ci	fnic->vlan_id = 0;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* Initialize the FIP fcoe_ctrl struct */
77962306a36Sopenharmony_ci	fnic->ctlr.send = fnic_eth_send;
78062306a36Sopenharmony_ci	fnic->ctlr.update_mac = fnic_update_mac;
78162306a36Sopenharmony_ci	fnic->ctlr.get_src_addr = fnic_get_mac;
78262306a36Sopenharmony_ci	if (fnic->config.flags & VFCF_FIP_CAPABLE) {
78362306a36Sopenharmony_ci		shost_printk(KERN_INFO, fnic->lport->host,
78462306a36Sopenharmony_ci			     "firmware supports FIP\n");
78562306a36Sopenharmony_ci		/* enable directed and multicast */
78662306a36Sopenharmony_ci		vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0);
78762306a36Sopenharmony_ci		vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
78862306a36Sopenharmony_ci		vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
78962306a36Sopenharmony_ci		fnic->set_vlan = fnic_set_vlan;
79062306a36Sopenharmony_ci		fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO);
79162306a36Sopenharmony_ci		timer_setup(&fnic->fip_timer, fnic_fip_notify_timer, 0);
79262306a36Sopenharmony_ci		spin_lock_init(&fnic->vlans_lock);
79362306a36Sopenharmony_ci		INIT_WORK(&fnic->fip_frame_work, fnic_handle_fip_frame);
79462306a36Sopenharmony_ci		INIT_WORK(&fnic->event_work, fnic_handle_event);
79562306a36Sopenharmony_ci		skb_queue_head_init(&fnic->fip_frame_queue);
79662306a36Sopenharmony_ci		INIT_LIST_HEAD(&fnic->evlist);
79762306a36Sopenharmony_ci		INIT_LIST_HEAD(&fnic->vlans);
79862306a36Sopenharmony_ci	} else {
79962306a36Sopenharmony_ci		shost_printk(KERN_INFO, fnic->lport->host,
80062306a36Sopenharmony_ci			     "firmware uses non-FIP mode\n");
80162306a36Sopenharmony_ci		fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_NON_FIP);
80262306a36Sopenharmony_ci		fnic->ctlr.state = FIP_ST_NON_FIP;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	fnic->state = FNIC_IN_FC_MODE;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	atomic_set(&fnic->in_flight, 0);
80762306a36Sopenharmony_ci	fnic->state_flags = FNIC_FLAGS_NONE;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* Enable hardware stripping of vlan header on ingress */
81062306a36Sopenharmony_ci	fnic_set_nic_config(fnic, 0, 0, 0, 0, 0, 0, 1);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* Setup notification buffer area */
81362306a36Sopenharmony_ci	err = fnic_notify_set(fnic);
81462306a36Sopenharmony_ci	if (err) {
81562306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
81662306a36Sopenharmony_ci			     "Failed to alloc notify buffer, aborting.\n");
81762306a36Sopenharmony_ci		goto err_out_free_max_pool;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* Setup notify timer when using MSI interrupts */
82162306a36Sopenharmony_ci	if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
82262306a36Sopenharmony_ci		timer_setup(&fnic->notify_timer, fnic_notify_timer, 0);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* allocate RQ buffers and post them to RQ*/
82562306a36Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++) {
82662306a36Sopenharmony_ci		vnic_rq_enable(&fnic->rq[i]);
82762306a36Sopenharmony_ci		err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
82862306a36Sopenharmony_ci		if (err) {
82962306a36Sopenharmony_ci			shost_printk(KERN_ERR, fnic->lport->host,
83062306a36Sopenharmony_ci				     "fnic_alloc_rq_frame can't alloc "
83162306a36Sopenharmony_ci				     "frame\n");
83262306a36Sopenharmony_ci			goto err_out_free_rq_buf;
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/*
83762306a36Sopenharmony_ci	 * Initialization done with PCI system, hardware, firmware.
83862306a36Sopenharmony_ci	 * Add host to SCSI
83962306a36Sopenharmony_ci	 */
84062306a36Sopenharmony_ci	err = scsi_add_host(lp->host, &pdev->dev);
84162306a36Sopenharmony_ci	if (err) {
84262306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
84362306a36Sopenharmony_ci			     "fnic: scsi_add_host failed...exiting\n");
84462306a36Sopenharmony_ci		goto err_out_free_rq_buf;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Start local port initiatialization */
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	lp->link_up = 0;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	lp->max_retry_count = fnic->config.flogi_retries;
85262306a36Sopenharmony_ci	lp->max_rport_retry_count = fnic->config.plogi_retries;
85362306a36Sopenharmony_ci	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
85462306a36Sopenharmony_ci			      FCP_SPPF_CONF_COMPL);
85562306a36Sopenharmony_ci	if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
85662306a36Sopenharmony_ci		lp->service_params |= FCP_SPPF_RETRY;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	lp->boot_time = jiffies;
85962306a36Sopenharmony_ci	lp->e_d_tov = fnic->config.ed_tov;
86062306a36Sopenharmony_ci	lp->r_a_tov = fnic->config.ra_tov;
86162306a36Sopenharmony_ci	lp->link_supported_speeds = FC_PORTSPEED_10GBIT;
86262306a36Sopenharmony_ci	fc_set_wwnn(lp, fnic->config.node_wwn);
86362306a36Sopenharmony_ci	fc_set_wwpn(lp, fnic->config.port_wwn);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template, 0);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START,
86862306a36Sopenharmony_ci			       FCPIO_HOST_EXCH_RANGE_END, NULL)) {
86962306a36Sopenharmony_ci		err = -ENOMEM;
87062306a36Sopenharmony_ci		goto err_out_remove_scsi_host;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	fc_lport_init_stats(lp);
87462306a36Sopenharmony_ci	fnic->stats_reset_time = jiffies;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	fc_lport_config(lp);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (fc_set_mfs(lp, fnic->config.maxdatafieldsize +
87962306a36Sopenharmony_ci		       sizeof(struct fc_frame_header))) {
88062306a36Sopenharmony_ci		err = -EINVAL;
88162306a36Sopenharmony_ci		goto err_out_free_exch_mgr;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci	fc_host_maxframe_size(lp->host) = lp->mfs;
88462306a36Sopenharmony_ci	fc_host_dev_loss_tmo(lp->host) = fnic->config.port_down_timeout / 1000;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	sprintf(fc_host_symbolic_name(lp->host),
88762306a36Sopenharmony_ci		DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	spin_lock_irqsave(&fnic_list_lock, flags);
89062306a36Sopenharmony_ci	list_add_tail(&fnic->list, &fnic_list);
89162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic_list_lock, flags);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	INIT_WORK(&fnic->link_work, fnic_handle_link);
89462306a36Sopenharmony_ci	INIT_WORK(&fnic->frame_work, fnic_handle_frame);
89562306a36Sopenharmony_ci	skb_queue_head_init(&fnic->frame_queue);
89662306a36Sopenharmony_ci	skb_queue_head_init(&fnic->tx_queue);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/* Enable all queues */
89962306a36Sopenharmony_ci	for (i = 0; i < fnic->raw_wq_count; i++)
90062306a36Sopenharmony_ci		vnic_wq_enable(&fnic->wq[i]);
90162306a36Sopenharmony_ci	for (i = 0; i < fnic->wq_copy_count; i++)
90262306a36Sopenharmony_ci		vnic_wq_copy_enable(&fnic->wq_copy[i]);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	fc_fabric_login(lp);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	err = fnic_request_intr(fnic);
90762306a36Sopenharmony_ci	if (err) {
90862306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
90962306a36Sopenharmony_ci			     "Unable to request irq.\n");
91062306a36Sopenharmony_ci		goto err_out_free_exch_mgr;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	vnic_dev_enable(fnic->vdev);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	for (i = 0; i < fnic->intr_count; i++)
91662306a36Sopenharmony_ci		vnic_intr_unmask(&fnic->intr[i]);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	fnic_notify_timer_start(fnic);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	return 0;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cierr_out_free_exch_mgr:
92362306a36Sopenharmony_ci	fc_exch_mgr_free(lp);
92462306a36Sopenharmony_cierr_out_remove_scsi_host:
92562306a36Sopenharmony_ci	fc_remove_host(lp->host);
92662306a36Sopenharmony_ci	scsi_remove_host(lp->host);
92762306a36Sopenharmony_cierr_out_free_rq_buf:
92862306a36Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++)
92962306a36Sopenharmony_ci		vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
93062306a36Sopenharmony_ci	vnic_dev_notify_unset(fnic->vdev);
93162306a36Sopenharmony_cierr_out_free_max_pool:
93262306a36Sopenharmony_ci	mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX]);
93362306a36Sopenharmony_cierr_out_free_dflt_pool:
93462306a36Sopenharmony_ci	mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT]);
93562306a36Sopenharmony_cierr_out_free_ioreq_pool:
93662306a36Sopenharmony_ci	mempool_destroy(fnic->io_req_pool);
93762306a36Sopenharmony_cierr_out_free_resources:
93862306a36Sopenharmony_ci	fnic_free_vnic_resources(fnic);
93962306a36Sopenharmony_cierr_out_clear_intr:
94062306a36Sopenharmony_ci	fnic_clear_intr_mode(fnic);
94162306a36Sopenharmony_cierr_out_dev_close:
94262306a36Sopenharmony_ci	vnic_dev_close(fnic->vdev);
94362306a36Sopenharmony_cierr_out_dev_cmd_deinit:
94462306a36Sopenharmony_cierr_out_vnic_unregister:
94562306a36Sopenharmony_ci	vnic_dev_unregister(fnic->vdev);
94662306a36Sopenharmony_cierr_out_iounmap:
94762306a36Sopenharmony_ci	fnic_iounmap(fnic);
94862306a36Sopenharmony_cierr_out_release_regions:
94962306a36Sopenharmony_ci	pci_release_regions(pdev);
95062306a36Sopenharmony_cierr_out_disable_device:
95162306a36Sopenharmony_ci	pci_disable_device(pdev);
95262306a36Sopenharmony_cierr_out_free_hba:
95362306a36Sopenharmony_ci	fnic_stats_debugfs_remove(fnic);
95462306a36Sopenharmony_ci	scsi_host_put(lp->host);
95562306a36Sopenharmony_cierr_out:
95662306a36Sopenharmony_ci	return err;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic void fnic_remove(struct pci_dev *pdev)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	struct fnic *fnic = pci_get_drvdata(pdev);
96262306a36Sopenharmony_ci	struct fc_lport *lp = fnic->lport;
96362306a36Sopenharmony_ci	unsigned long flags;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/*
96662306a36Sopenharmony_ci	 * Mark state so that the workqueue thread stops forwarding
96762306a36Sopenharmony_ci	 * received frames and link events to the local port. ISR and
96862306a36Sopenharmony_ci	 * other threads that can queue work items will also stop
96962306a36Sopenharmony_ci	 * creating work items on the fnic workqueue
97062306a36Sopenharmony_ci	 */
97162306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
97262306a36Sopenharmony_ci	fnic->stop_rx_link_events = 1;
97362306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
97662306a36Sopenharmony_ci		del_timer_sync(&fnic->notify_timer);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/*
97962306a36Sopenharmony_ci	 * Flush the fnic event queue. After this call, there should
98062306a36Sopenharmony_ci	 * be no event queued for this fnic device in the workqueue
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	flush_workqueue(fnic_event_queue);
98362306a36Sopenharmony_ci	skb_queue_purge(&fnic->frame_queue);
98462306a36Sopenharmony_ci	skb_queue_purge(&fnic->tx_queue);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (fnic->config.flags & VFCF_FIP_CAPABLE) {
98762306a36Sopenharmony_ci		del_timer_sync(&fnic->fip_timer);
98862306a36Sopenharmony_ci		skb_queue_purge(&fnic->fip_frame_queue);
98962306a36Sopenharmony_ci		fnic_fcoe_reset_vlans(fnic);
99062306a36Sopenharmony_ci		fnic_fcoe_evlist_free(fnic);
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/*
99462306a36Sopenharmony_ci	 * Log off the fabric. This stops all remote ports, dns port,
99562306a36Sopenharmony_ci	 * logs off the fabric. This flushes all rport, disc, lport work
99662306a36Sopenharmony_ci	 * before returning
99762306a36Sopenharmony_ci	 */
99862306a36Sopenharmony_ci	fc_fabric_logoff(fnic->lport);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
100162306a36Sopenharmony_ci	fnic->in_remove = 1;
100262306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	fcoe_ctlr_destroy(&fnic->ctlr);
100562306a36Sopenharmony_ci	fc_lport_destroy(lp);
100662306a36Sopenharmony_ci	fnic_stats_debugfs_remove(fnic);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/*
100962306a36Sopenharmony_ci	 * This stops the fnic device, masks all interrupts. Completed
101062306a36Sopenharmony_ci	 * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are
101162306a36Sopenharmony_ci	 * cleaned up
101262306a36Sopenharmony_ci	 */
101362306a36Sopenharmony_ci	fnic_cleanup(fnic);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	BUG_ON(!skb_queue_empty(&fnic->frame_queue));
101662306a36Sopenharmony_ci	BUG_ON(!skb_queue_empty(&fnic->tx_queue));
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	spin_lock_irqsave(&fnic_list_lock, flags);
101962306a36Sopenharmony_ci	list_del(&fnic->list);
102062306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic_list_lock, flags);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	fc_remove_host(fnic->lport->host);
102362306a36Sopenharmony_ci	scsi_remove_host(fnic->lport->host);
102462306a36Sopenharmony_ci	fc_exch_mgr_free(fnic->lport);
102562306a36Sopenharmony_ci	vnic_dev_notify_unset(fnic->vdev);
102662306a36Sopenharmony_ci	fnic_free_intr(fnic);
102762306a36Sopenharmony_ci	fnic_free_vnic_resources(fnic);
102862306a36Sopenharmony_ci	fnic_clear_intr_mode(fnic);
102962306a36Sopenharmony_ci	vnic_dev_close(fnic->vdev);
103062306a36Sopenharmony_ci	vnic_dev_unregister(fnic->vdev);
103162306a36Sopenharmony_ci	fnic_iounmap(fnic);
103262306a36Sopenharmony_ci	pci_release_regions(pdev);
103362306a36Sopenharmony_ci	pci_disable_device(pdev);
103462306a36Sopenharmony_ci	scsi_host_put(lp->host);
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic struct pci_driver fnic_driver = {
103862306a36Sopenharmony_ci	.name = DRV_NAME,
103962306a36Sopenharmony_ci	.id_table = fnic_id_table,
104062306a36Sopenharmony_ci	.probe = fnic_probe,
104162306a36Sopenharmony_ci	.remove = fnic_remove,
104262306a36Sopenharmony_ci};
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic int __init fnic_init_module(void)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	size_t len;
104762306a36Sopenharmony_ci	int err = 0;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	printk(KERN_INFO PFX "%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* Create debugfs entries for fnic */
105262306a36Sopenharmony_ci	err = fnic_debugfs_init();
105362306a36Sopenharmony_ci	if (err < 0) {
105462306a36Sopenharmony_ci		printk(KERN_ERR PFX "Failed to create fnic directory "
105562306a36Sopenharmony_ci				"for tracing and stats logging\n");
105662306a36Sopenharmony_ci		fnic_debugfs_terminate();
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	/* Allocate memory for trace buffer */
106062306a36Sopenharmony_ci	err = fnic_trace_buf_init();
106162306a36Sopenharmony_ci	if (err < 0) {
106262306a36Sopenharmony_ci		printk(KERN_ERR PFX
106362306a36Sopenharmony_ci		       "Trace buffer initialization Failed. "
106462306a36Sopenharmony_ci		       "Fnic Tracing utility is disabled\n");
106562306a36Sopenharmony_ci		fnic_trace_free();
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci    /* Allocate memory for fc trace buffer */
106962306a36Sopenharmony_ci	err = fnic_fc_trace_init();
107062306a36Sopenharmony_ci	if (err < 0) {
107162306a36Sopenharmony_ci		printk(KERN_ERR PFX "FC trace buffer initialization Failed "
107262306a36Sopenharmony_ci		       "FC frame tracing utility is disabled\n");
107362306a36Sopenharmony_ci		fnic_fc_trace_free();
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	/* Create a cache for allocation of default size sgls */
107762306a36Sopenharmony_ci	len = sizeof(struct fnic_dflt_sgl_list);
107862306a36Sopenharmony_ci	fnic_sgl_cache[FNIC_SGL_CACHE_DFLT] = kmem_cache_create
107962306a36Sopenharmony_ci		("fnic_sgl_dflt", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
108062306a36Sopenharmony_ci		 SLAB_HWCACHE_ALIGN,
108162306a36Sopenharmony_ci		 NULL);
108262306a36Sopenharmony_ci	if (!fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]) {
108362306a36Sopenharmony_ci		printk(KERN_ERR PFX "failed to create fnic dflt sgl slab\n");
108462306a36Sopenharmony_ci		err = -ENOMEM;
108562306a36Sopenharmony_ci		goto err_create_fnic_sgl_slab_dflt;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/* Create a cache for allocation of max size sgls*/
108962306a36Sopenharmony_ci	len = sizeof(struct fnic_sgl_list);
109062306a36Sopenharmony_ci	fnic_sgl_cache[FNIC_SGL_CACHE_MAX] = kmem_cache_create
109162306a36Sopenharmony_ci		("fnic_sgl_max", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
109262306a36Sopenharmony_ci		  SLAB_HWCACHE_ALIGN,
109362306a36Sopenharmony_ci		  NULL);
109462306a36Sopenharmony_ci	if (!fnic_sgl_cache[FNIC_SGL_CACHE_MAX]) {
109562306a36Sopenharmony_ci		printk(KERN_ERR PFX "failed to create fnic max sgl slab\n");
109662306a36Sopenharmony_ci		err = -ENOMEM;
109762306a36Sopenharmony_ci		goto err_create_fnic_sgl_slab_max;
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/* Create a cache of io_req structs for use via mempool */
110162306a36Sopenharmony_ci	fnic_io_req_cache = kmem_cache_create("fnic_io_req",
110262306a36Sopenharmony_ci					      sizeof(struct fnic_io_req),
110362306a36Sopenharmony_ci					      0, SLAB_HWCACHE_ALIGN, NULL);
110462306a36Sopenharmony_ci	if (!fnic_io_req_cache) {
110562306a36Sopenharmony_ci		printk(KERN_ERR PFX "failed to create fnic io_req slab\n");
110662306a36Sopenharmony_ci		err = -ENOMEM;
110762306a36Sopenharmony_ci		goto err_create_fnic_ioreq_slab;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	fnic_event_queue = create_singlethread_workqueue("fnic_event_wq");
111162306a36Sopenharmony_ci	if (!fnic_event_queue) {
111262306a36Sopenharmony_ci		printk(KERN_ERR PFX "fnic work queue create failed\n");
111362306a36Sopenharmony_ci		err = -ENOMEM;
111462306a36Sopenharmony_ci		goto err_create_fnic_workq;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	fnic_fip_queue = create_singlethread_workqueue("fnic_fip_q");
111862306a36Sopenharmony_ci	if (!fnic_fip_queue) {
111962306a36Sopenharmony_ci		printk(KERN_ERR PFX "fnic FIP work queue create failed\n");
112062306a36Sopenharmony_ci		err = -ENOMEM;
112162306a36Sopenharmony_ci		goto err_create_fip_workq;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	fnic_fc_transport = fc_attach_transport(&fnic_fc_functions);
112562306a36Sopenharmony_ci	if (!fnic_fc_transport) {
112662306a36Sopenharmony_ci		printk(KERN_ERR PFX "fc_attach_transport error\n");
112762306a36Sopenharmony_ci		err = -ENOMEM;
112862306a36Sopenharmony_ci		goto err_fc_transport;
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* register the driver with PCI system */
113262306a36Sopenharmony_ci	err = pci_register_driver(&fnic_driver);
113362306a36Sopenharmony_ci	if (err < 0) {
113462306a36Sopenharmony_ci		printk(KERN_ERR PFX "pci register error\n");
113562306a36Sopenharmony_ci		goto err_pci_register;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci	return err;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cierr_pci_register:
114062306a36Sopenharmony_ci	fc_release_transport(fnic_fc_transport);
114162306a36Sopenharmony_cierr_fc_transport:
114262306a36Sopenharmony_ci	destroy_workqueue(fnic_fip_queue);
114362306a36Sopenharmony_cierr_create_fip_workq:
114462306a36Sopenharmony_ci	destroy_workqueue(fnic_event_queue);
114562306a36Sopenharmony_cierr_create_fnic_workq:
114662306a36Sopenharmony_ci	kmem_cache_destroy(fnic_io_req_cache);
114762306a36Sopenharmony_cierr_create_fnic_ioreq_slab:
114862306a36Sopenharmony_ci	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
114962306a36Sopenharmony_cierr_create_fnic_sgl_slab_max:
115062306a36Sopenharmony_ci	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
115162306a36Sopenharmony_cierr_create_fnic_sgl_slab_dflt:
115262306a36Sopenharmony_ci	fnic_trace_free();
115362306a36Sopenharmony_ci	fnic_fc_trace_free();
115462306a36Sopenharmony_ci	fnic_debugfs_terminate();
115562306a36Sopenharmony_ci	return err;
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic void __exit fnic_cleanup_module(void)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	pci_unregister_driver(&fnic_driver);
116162306a36Sopenharmony_ci	destroy_workqueue(fnic_event_queue);
116262306a36Sopenharmony_ci	if (fnic_fip_queue)
116362306a36Sopenharmony_ci		destroy_workqueue(fnic_fip_queue);
116462306a36Sopenharmony_ci	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
116562306a36Sopenharmony_ci	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
116662306a36Sopenharmony_ci	kmem_cache_destroy(fnic_io_req_cache);
116762306a36Sopenharmony_ci	fc_release_transport(fnic_fc_transport);
116862306a36Sopenharmony_ci	fnic_trace_free();
116962306a36Sopenharmony_ci	fnic_fc_trace_free();
117062306a36Sopenharmony_ci	fnic_debugfs_terminate();
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cimodule_init(fnic_init_module);
117462306a36Sopenharmony_cimodule_exit(fnic_cleanup_module);
1175