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/errno.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/skbuff.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/spinlock.h>
1262306a36Sopenharmony_ci#include <linux/if_ether.h>
1362306a36Sopenharmony_ci#include <linux/if_vlan.h>
1462306a36Sopenharmony_ci#include <linux/workqueue.h>
1562306a36Sopenharmony_ci#include <scsi/fc/fc_fip.h>
1662306a36Sopenharmony_ci#include <scsi/fc/fc_els.h>
1762306a36Sopenharmony_ci#include <scsi/fc/fc_fcoe.h>
1862306a36Sopenharmony_ci#include <scsi/fc_frame.h>
1962306a36Sopenharmony_ci#include <scsi/libfc.h>
2062306a36Sopenharmony_ci#include "fnic_io.h"
2162306a36Sopenharmony_ci#include "fnic.h"
2262306a36Sopenharmony_ci#include "fnic_fip.h"
2362306a36Sopenharmony_ci#include "cq_enet_desc.h"
2462306a36Sopenharmony_ci#include "cq_exch_desc.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
2762306a36Sopenharmony_cistruct workqueue_struct *fnic_fip_queue;
2862306a36Sopenharmony_cistruct workqueue_struct *fnic_event_queue;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void fnic_set_eth_mode(struct fnic *);
3162306a36Sopenharmony_cistatic void fnic_fcoe_send_vlan_req(struct fnic *fnic);
3262306a36Sopenharmony_cistatic void fnic_fcoe_start_fcf_disc(struct fnic *fnic);
3362306a36Sopenharmony_cistatic void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *);
3462306a36Sopenharmony_cistatic int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag);
3562306a36Sopenharmony_cistatic int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_civoid fnic_handle_link(struct work_struct *work)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, link_work);
4062306a36Sopenharmony_ci	unsigned long flags;
4162306a36Sopenharmony_ci	int old_link_status;
4262306a36Sopenharmony_ci	u32 old_link_down_cnt;
4362306a36Sopenharmony_ci	u64 old_port_speed, new_port_speed;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	fnic->link_events = 1;      /* less work to just set everytime*/
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (fnic->stop_rx_link_events) {
5062306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	old_link_down_cnt = fnic->link_down_cnt;
5562306a36Sopenharmony_ci	old_link_status = fnic->link_status;
5662306a36Sopenharmony_ci	old_port_speed = atomic64_read(
5762306a36Sopenharmony_ci			&fnic->fnic_stats.misc_stats.current_port_speed);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	fnic->link_status = vnic_dev_link_status(fnic->vdev);
6062306a36Sopenharmony_ci	fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	new_port_speed = vnic_dev_port_speed(fnic->vdev);
6362306a36Sopenharmony_ci	atomic64_set(&fnic->fnic_stats.misc_stats.current_port_speed,
6462306a36Sopenharmony_ci			new_port_speed);
6562306a36Sopenharmony_ci	if (old_port_speed != new_port_speed)
6662306a36Sopenharmony_ci		FNIC_MAIN_DBG(KERN_INFO, fnic->lport->host,
6762306a36Sopenharmony_ci				"Current vnic speed set to :  %llu\n",
6862306a36Sopenharmony_ci				new_port_speed);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	switch (vnic_dev_port_speed(fnic->vdev)) {
7162306a36Sopenharmony_ci	case DCEM_PORTSPEED_10G:
7262306a36Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_10GBIT;
7362306a36Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_10GBIT;
7462306a36Sopenharmony_ci		break;
7562306a36Sopenharmony_ci	case DCEM_PORTSPEED_20G:
7662306a36Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_20GBIT;
7762306a36Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_20GBIT;
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci	case DCEM_PORTSPEED_25G:
8062306a36Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_25GBIT;
8162306a36Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_25GBIT;
8262306a36Sopenharmony_ci		break;
8362306a36Sopenharmony_ci	case DCEM_PORTSPEED_40G:
8462306a36Sopenharmony_ci	case DCEM_PORTSPEED_4x10G:
8562306a36Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_40GBIT;
8662306a36Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_40GBIT;
8762306a36Sopenharmony_ci		break;
8862306a36Sopenharmony_ci	case DCEM_PORTSPEED_100G:
8962306a36Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_100GBIT;
9062306a36Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_100GBIT;
9162306a36Sopenharmony_ci		break;
9262306a36Sopenharmony_ci	default:
9362306a36Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_UNKNOWN;
9462306a36Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (old_link_status == fnic->link_status) {
9962306a36Sopenharmony_ci		if (!fnic->link_status) {
10062306a36Sopenharmony_ci			/* DOWN -> DOWN */
10162306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
10262306a36Sopenharmony_ci			fnic_fc_trace_set_data(fnic->lport->host->host_no,
10362306a36Sopenharmony_ci				FNIC_FC_LE, "Link Status: DOWN->DOWN",
10462306a36Sopenharmony_ci				strlen("Link Status: DOWN->DOWN"));
10562306a36Sopenharmony_ci		} else {
10662306a36Sopenharmony_ci			if (old_link_down_cnt != fnic->link_down_cnt) {
10762306a36Sopenharmony_ci				/* UP -> DOWN -> UP */
10862306a36Sopenharmony_ci				fnic->lport->host_stats.link_failure_count++;
10962306a36Sopenharmony_ci				spin_unlock_irqrestore(&fnic->fnic_lock, flags);
11062306a36Sopenharmony_ci				fnic_fc_trace_set_data(
11162306a36Sopenharmony_ci					fnic->lport->host->host_no,
11262306a36Sopenharmony_ci					FNIC_FC_LE,
11362306a36Sopenharmony_ci					"Link Status:UP_DOWN_UP",
11462306a36Sopenharmony_ci					strlen("Link_Status:UP_DOWN_UP")
11562306a36Sopenharmony_ci					);
11662306a36Sopenharmony_ci				FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
11762306a36Sopenharmony_ci					     "link down\n");
11862306a36Sopenharmony_ci				fcoe_ctlr_link_down(&fnic->ctlr);
11962306a36Sopenharmony_ci				if (fnic->config.flags & VFCF_FIP_CAPABLE) {
12062306a36Sopenharmony_ci					/* start FCoE VLAN discovery */
12162306a36Sopenharmony_ci					fnic_fc_trace_set_data(
12262306a36Sopenharmony_ci						fnic->lport->host->host_no,
12362306a36Sopenharmony_ci						FNIC_FC_LE,
12462306a36Sopenharmony_ci						"Link Status: UP_DOWN_UP_VLAN",
12562306a36Sopenharmony_ci						strlen(
12662306a36Sopenharmony_ci						"Link Status: UP_DOWN_UP_VLAN")
12762306a36Sopenharmony_ci						);
12862306a36Sopenharmony_ci					fnic_fcoe_send_vlan_req(fnic);
12962306a36Sopenharmony_ci					return;
13062306a36Sopenharmony_ci				}
13162306a36Sopenharmony_ci				FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
13262306a36Sopenharmony_ci					     "link up\n");
13362306a36Sopenharmony_ci				fcoe_ctlr_link_up(&fnic->ctlr);
13462306a36Sopenharmony_ci			} else {
13562306a36Sopenharmony_ci				/* UP -> UP */
13662306a36Sopenharmony_ci				spin_unlock_irqrestore(&fnic->fnic_lock, flags);
13762306a36Sopenharmony_ci				fnic_fc_trace_set_data(
13862306a36Sopenharmony_ci					fnic->lport->host->host_no, FNIC_FC_LE,
13962306a36Sopenharmony_ci					"Link Status: UP_UP",
14062306a36Sopenharmony_ci					strlen("Link Status: UP_UP"));
14162306a36Sopenharmony_ci			}
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci	} else if (fnic->link_status) {
14462306a36Sopenharmony_ci		/* DOWN -> UP */
14562306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
14662306a36Sopenharmony_ci		if (fnic->config.flags & VFCF_FIP_CAPABLE) {
14762306a36Sopenharmony_ci			/* start FCoE VLAN discovery */
14862306a36Sopenharmony_ci				fnic_fc_trace_set_data(
14962306a36Sopenharmony_ci				fnic->lport->host->host_no,
15062306a36Sopenharmony_ci				FNIC_FC_LE, "Link Status: DOWN_UP_VLAN",
15162306a36Sopenharmony_ci				strlen("Link Status: DOWN_UP_VLAN"));
15262306a36Sopenharmony_ci			fnic_fcoe_send_vlan_req(fnic);
15362306a36Sopenharmony_ci			return;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
15662306a36Sopenharmony_ci		fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_LE,
15762306a36Sopenharmony_ci			"Link Status: DOWN_UP", strlen("Link Status: DOWN_UP"));
15862306a36Sopenharmony_ci		fcoe_ctlr_link_up(&fnic->ctlr);
15962306a36Sopenharmony_ci	} else {
16062306a36Sopenharmony_ci		/* UP -> DOWN */
16162306a36Sopenharmony_ci		fnic->lport->host_stats.link_failure_count++;
16262306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
16362306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
16462306a36Sopenharmony_ci		fnic_fc_trace_set_data(
16562306a36Sopenharmony_ci			fnic->lport->host->host_no, FNIC_FC_LE,
16662306a36Sopenharmony_ci			"Link Status: UP_DOWN",
16762306a36Sopenharmony_ci			strlen("Link Status: UP_DOWN"));
16862306a36Sopenharmony_ci		if (fnic->config.flags & VFCF_FIP_CAPABLE) {
16962306a36Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
17062306a36Sopenharmony_ci				"deleting fip-timer during link-down\n");
17162306a36Sopenharmony_ci			del_timer_sync(&fnic->fip_timer);
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci		fcoe_ctlr_link_down(&fnic->ctlr);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/*
17962306a36Sopenharmony_ci * This function passes incoming fabric frames to libFC
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_civoid fnic_handle_frame(struct work_struct *work)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, frame_work);
18462306a36Sopenharmony_ci	struct fc_lport *lp = fnic->lport;
18562306a36Sopenharmony_ci	unsigned long flags;
18662306a36Sopenharmony_ci	struct sk_buff *skb;
18762306a36Sopenharmony_ci	struct fc_frame *fp;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	while ((skb = skb_dequeue(&fnic->frame_queue))) {
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		spin_lock_irqsave(&fnic->fnic_lock, flags);
19262306a36Sopenharmony_ci		if (fnic->stop_rx_link_events) {
19362306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
19462306a36Sopenharmony_ci			dev_kfree_skb(skb);
19562306a36Sopenharmony_ci			return;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		fp = (struct fc_frame *)skb;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		/*
20062306a36Sopenharmony_ci		 * If we're in a transitional state, just re-queue and return.
20162306a36Sopenharmony_ci		 * The queue will be serviced when we get to a stable state.
20262306a36Sopenharmony_ci		 */
20362306a36Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_MODE &&
20462306a36Sopenharmony_ci		    fnic->state != FNIC_IN_ETH_MODE) {
20562306a36Sopenharmony_ci			skb_queue_head(&fnic->frame_queue, skb);
20662306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
20762306a36Sopenharmony_ci			return;
20862306a36Sopenharmony_ci		}
20962306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		fc_exch_recv(lp, fp);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_civoid fnic_fcoe_evlist_free(struct fnic *fnic)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct fnic_event *fevt = NULL;
21862306a36Sopenharmony_ci	struct fnic_event *next = NULL;
21962306a36Sopenharmony_ci	unsigned long flags;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
22262306a36Sopenharmony_ci	if (list_empty(&fnic->evlist)) {
22362306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
22462306a36Sopenharmony_ci		return;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	list_for_each_entry_safe(fevt, next, &fnic->evlist, list) {
22862306a36Sopenharmony_ci		list_del(&fevt->list);
22962306a36Sopenharmony_ci		kfree(fevt);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_civoid fnic_handle_event(struct work_struct *work)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, event_work);
23762306a36Sopenharmony_ci	struct fnic_event *fevt = NULL;
23862306a36Sopenharmony_ci	struct fnic_event *next = NULL;
23962306a36Sopenharmony_ci	unsigned long flags;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
24262306a36Sopenharmony_ci	if (list_empty(&fnic->evlist)) {
24362306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	list_for_each_entry_safe(fevt, next, &fnic->evlist, list) {
24862306a36Sopenharmony_ci		if (fnic->stop_rx_link_events) {
24962306a36Sopenharmony_ci			list_del(&fevt->list);
25062306a36Sopenharmony_ci			kfree(fevt);
25162306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
25262306a36Sopenharmony_ci			return;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci		/*
25562306a36Sopenharmony_ci		 * If we're in a transitional state, just re-queue and return.
25662306a36Sopenharmony_ci		 * The queue will be serviced when we get to a stable state.
25762306a36Sopenharmony_ci		 */
25862306a36Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_MODE &&
25962306a36Sopenharmony_ci		    fnic->state != FNIC_IN_ETH_MODE) {
26062306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
26162306a36Sopenharmony_ci			return;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		list_del(&fevt->list);
26562306a36Sopenharmony_ci		switch (fevt->event) {
26662306a36Sopenharmony_ci		case FNIC_EVT_START_VLAN_DISC:
26762306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
26862306a36Sopenharmony_ci			fnic_fcoe_send_vlan_req(fnic);
26962306a36Sopenharmony_ci			spin_lock_irqsave(&fnic->fnic_lock, flags);
27062306a36Sopenharmony_ci			break;
27162306a36Sopenharmony_ci		case FNIC_EVT_START_FCF_DISC:
27262306a36Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
27362306a36Sopenharmony_ci				  "Start FCF Discovery\n");
27462306a36Sopenharmony_ci			fnic_fcoe_start_fcf_disc(fnic);
27562306a36Sopenharmony_ci			break;
27662306a36Sopenharmony_ci		default:
27762306a36Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
27862306a36Sopenharmony_ci				  "Unknown event 0x%x\n", fevt->event);
27962306a36Sopenharmony_ci			break;
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci		kfree(fevt);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/**
28762306a36Sopenharmony_ci * is_fnic_fip_flogi_reject() - Check if the Received FIP FLOGI frame is rejected
28862306a36Sopenharmony_ci * @fip: The FCoE controller that received the frame
28962306a36Sopenharmony_ci * @skb: The received FIP frame
29062306a36Sopenharmony_ci *
29162306a36Sopenharmony_ci * Returns non-zero if the frame is rejected with unsupported cmd with
29262306a36Sopenharmony_ci * insufficient resource els explanation.
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic inline int is_fnic_fip_flogi_reject(struct fcoe_ctlr *fip,
29562306a36Sopenharmony_ci					 struct sk_buff *skb)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
29862306a36Sopenharmony_ci	struct fip_header *fiph;
29962306a36Sopenharmony_ci	struct fc_frame_header *fh = NULL;
30062306a36Sopenharmony_ci	struct fip_desc *desc;
30162306a36Sopenharmony_ci	struct fip_encaps *els;
30262306a36Sopenharmony_ci	u16 op;
30362306a36Sopenharmony_ci	u8 els_op;
30462306a36Sopenharmony_ci	u8 sub;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	size_t rlen;
30762306a36Sopenharmony_ci	size_t dlen = 0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (skb_linearize(skb))
31062306a36Sopenharmony_ci		return 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (skb->len < sizeof(*fiph))
31362306a36Sopenharmony_ci		return 0;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
31662306a36Sopenharmony_ci	op = ntohs(fiph->fip_op);
31762306a36Sopenharmony_ci	sub = fiph->fip_subcode;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (op != FIP_OP_LS)
32062306a36Sopenharmony_ci		return 0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (sub != FIP_SC_REP)
32362306a36Sopenharmony_ci		return 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
32662306a36Sopenharmony_ci	if (rlen + sizeof(*fiph) > skb->len)
32762306a36Sopenharmony_ci		return 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
33062306a36Sopenharmony_ci	dlen = desc->fip_dlen * FIP_BPW;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (desc->fip_dtype == FIP_DT_FLOGI) {
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		if (dlen < sizeof(*els) + sizeof(*fh) + 1)
33562306a36Sopenharmony_ci			return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		els = (struct fip_encaps *)desc;
33862306a36Sopenharmony_ci		fh = (struct fc_frame_header *)(els + 1);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		if (!fh)
34162306a36Sopenharmony_ci			return 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		/*
34462306a36Sopenharmony_ci		 * ELS command code, reason and explanation should be = Reject,
34562306a36Sopenharmony_ci		 * unsupported command and insufficient resource
34662306a36Sopenharmony_ci		 */
34762306a36Sopenharmony_ci		els_op = *(u8 *)(fh + 1);
34862306a36Sopenharmony_ci		if (els_op == ELS_LS_RJT) {
34962306a36Sopenharmony_ci			shost_printk(KERN_INFO, lport->host,
35062306a36Sopenharmony_ci				  "Flogi Request Rejected by Switch\n");
35162306a36Sopenharmony_ci			return 1;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci		shost_printk(KERN_INFO, lport->host,
35462306a36Sopenharmony_ci				"Flogi Request Accepted by Switch\n");
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci	return 0;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void fnic_fcoe_send_vlan_req(struct fnic *fnic)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct fcoe_ctlr *fip = &fnic->ctlr;
36262306a36Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
36362306a36Sopenharmony_ci	struct sk_buff *skb;
36462306a36Sopenharmony_ci	char *eth_fr;
36562306a36Sopenharmony_ci	struct fip_vlan *vlan;
36662306a36Sopenharmony_ci	u64 vlan_tov;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	fnic_fcoe_reset_vlans(fnic);
36962306a36Sopenharmony_ci	fnic->set_vlan(fnic, 0);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (printk_ratelimit())
37262306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
37362306a36Sopenharmony_ci			  "Sending VLAN request...\n");
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	skb = dev_alloc_skb(sizeof(struct fip_vlan));
37662306a36Sopenharmony_ci	if (!skb)
37762306a36Sopenharmony_ci		return;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	eth_fr = (char *)skb->data;
38062306a36Sopenharmony_ci	vlan = (struct fip_vlan *)eth_fr;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	memset(vlan, 0, sizeof(*vlan));
38362306a36Sopenharmony_ci	memcpy(vlan->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
38462306a36Sopenharmony_ci	memcpy(vlan->eth.h_dest, fcoe_all_fcfs, ETH_ALEN);
38562306a36Sopenharmony_ci	vlan->eth.h_proto = htons(ETH_P_FIP);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	vlan->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
38862306a36Sopenharmony_ci	vlan->fip.fip_op = htons(FIP_OP_VLAN);
38962306a36Sopenharmony_ci	vlan->fip.fip_subcode = FIP_SC_VL_REQ;
39062306a36Sopenharmony_ci	vlan->fip.fip_dl_len = htons(sizeof(vlan->desc) / FIP_BPW);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	vlan->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC;
39362306a36Sopenharmony_ci	vlan->desc.mac.fd_desc.fip_dlen = sizeof(vlan->desc.mac) / FIP_BPW;
39462306a36Sopenharmony_ci	memcpy(&vlan->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
39762306a36Sopenharmony_ci	vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW;
39862306a36Sopenharmony_ci	put_unaligned_be64(fip->lp->wwnn, &vlan->desc.wwnn.fd_wwn);
39962306a36Sopenharmony_ci	atomic64_inc(&fnic_stats->vlan_stats.vlan_disc_reqs);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	skb_put(skb, sizeof(*vlan));
40262306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
40362306a36Sopenharmony_ci	skb_reset_mac_header(skb);
40462306a36Sopenharmony_ci	skb_reset_network_header(skb);
40562306a36Sopenharmony_ci	fip->send(fip, skb);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* set a timer so that we can retry if there no response */
40862306a36Sopenharmony_ci	vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV);
40962306a36Sopenharmony_ci	mod_timer(&fnic->fip_timer, round_jiffies(vlan_tov));
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *skb)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct fcoe_ctlr *fip = &fnic->ctlr;
41562306a36Sopenharmony_ci	struct fip_header *fiph;
41662306a36Sopenharmony_ci	struct fip_desc *desc;
41762306a36Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
41862306a36Sopenharmony_ci	u16 vid;
41962306a36Sopenharmony_ci	size_t rlen;
42062306a36Sopenharmony_ci	size_t dlen;
42162306a36Sopenharmony_ci	struct fcoe_vlan *vlan;
42262306a36Sopenharmony_ci	u64 sol_time;
42362306a36Sopenharmony_ci	unsigned long flags;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
42662306a36Sopenharmony_ci		  "Received VLAN response...\n");
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	fiph = (struct fip_header *) skb->data;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
43162306a36Sopenharmony_ci		  "Received VLAN response... OP 0x%x SUB_OP 0x%x\n",
43262306a36Sopenharmony_ci		  ntohs(fiph->fip_op), fiph->fip_subcode);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
43562306a36Sopenharmony_ci	fnic_fcoe_reset_vlans(fnic);
43662306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
43762306a36Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
43862306a36Sopenharmony_ci	while (rlen > 0) {
43962306a36Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
44062306a36Sopenharmony_ci		switch (desc->fip_dtype) {
44162306a36Sopenharmony_ci		case FIP_DT_VLAN:
44262306a36Sopenharmony_ci			vid = ntohs(((struct fip_vlan_desc *)desc)->fd_vlan);
44362306a36Sopenharmony_ci			shost_printk(KERN_INFO, fnic->lport->host,
44462306a36Sopenharmony_ci				  "process_vlan_resp: FIP VLAN %d\n", vid);
44562306a36Sopenharmony_ci			vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
44662306a36Sopenharmony_ci			if (!vlan) {
44762306a36Sopenharmony_ci				/* retry from timer */
44862306a36Sopenharmony_ci				spin_unlock_irqrestore(&fnic->vlans_lock,
44962306a36Sopenharmony_ci							flags);
45062306a36Sopenharmony_ci				goto out;
45162306a36Sopenharmony_ci			}
45262306a36Sopenharmony_ci			vlan->vid = vid & 0x0fff;
45362306a36Sopenharmony_ci			vlan->state = FIP_VLAN_AVAIL;
45462306a36Sopenharmony_ci			list_add_tail(&vlan->list, &fnic->vlans);
45562306a36Sopenharmony_ci			break;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
45862306a36Sopenharmony_ci		rlen -= dlen;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* any VLAN descriptors present ? */
46262306a36Sopenharmony_ci	if (list_empty(&fnic->vlans)) {
46362306a36Sopenharmony_ci		/* retry from timer */
46462306a36Sopenharmony_ci		atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID);
46562306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
46662306a36Sopenharmony_ci			  "No VLAN descriptors in FIP VLAN response\n");
46762306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
46862306a36Sopenharmony_ci		goto out;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
47262306a36Sopenharmony_ci	fnic->set_vlan(fnic, vlan->vid);
47362306a36Sopenharmony_ci	vlan->state = FIP_VLAN_SENT; /* sent now */
47462306a36Sopenharmony_ci	vlan->sol_count++;
47562306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* start the solicitation */
47862306a36Sopenharmony_ci	fcoe_ctlr_link_up(fip);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY);
48162306a36Sopenharmony_ci	mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
48262306a36Sopenharmony_ciout:
48362306a36Sopenharmony_ci	return;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void fnic_fcoe_start_fcf_disc(struct fnic *fnic)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	unsigned long flags;
48962306a36Sopenharmony_ci	struct fcoe_vlan *vlan;
49062306a36Sopenharmony_ci	u64 sol_time;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
49362306a36Sopenharmony_ci	vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
49462306a36Sopenharmony_ci	fnic->set_vlan(fnic, vlan->vid);
49562306a36Sopenharmony_ci	vlan->state = FIP_VLAN_SENT; /* sent now */
49662306a36Sopenharmony_ci	vlan->sol_count = 1;
49762306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* start the solicitation */
50062306a36Sopenharmony_ci	fcoe_ctlr_link_up(&fnic->ctlr);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY);
50362306a36Sopenharmony_ci	mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	unsigned long flags;
50962306a36Sopenharmony_ci	struct fcoe_vlan *fvlan;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
51262306a36Sopenharmony_ci	if (list_empty(&fnic->vlans)) {
51362306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	fvlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
51862306a36Sopenharmony_ci	if (fvlan->state == FIP_VLAN_USED) {
51962306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
52062306a36Sopenharmony_ci		return 0;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (fvlan->state == FIP_VLAN_SENT) {
52462306a36Sopenharmony_ci		fvlan->state = FIP_VLAN_USED;
52562306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
52662306a36Sopenharmony_ci		return 0;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
52962306a36Sopenharmony_ci	return -EINVAL;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void fnic_event_enq(struct fnic *fnic, enum fnic_evt ev)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct fnic_event *fevt;
53562306a36Sopenharmony_ci	unsigned long flags;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	fevt = kmalloc(sizeof(*fevt), GFP_ATOMIC);
53862306a36Sopenharmony_ci	if (!fevt)
53962306a36Sopenharmony_ci		return;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	fevt->fnic = fnic;
54262306a36Sopenharmony_ci	fevt->event = ev;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
54562306a36Sopenharmony_ci	list_add_tail(&fevt->list, &fnic->evlist);
54662306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	schedule_work(&fnic->event_work);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct fip_header *fiph;
55462306a36Sopenharmony_ci	int ret = 1;
55562306a36Sopenharmony_ci	u16 op;
55662306a36Sopenharmony_ci	u8 sub;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (!skb || !(skb->data))
55962306a36Sopenharmony_ci		return -1;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (skb_linearize(skb))
56262306a36Sopenharmony_ci		goto drop;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
56562306a36Sopenharmony_ci	op = ntohs(fiph->fip_op);
56662306a36Sopenharmony_ci	sub = fiph->fip_subcode;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER)
56962306a36Sopenharmony_ci		goto drop;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
57262306a36Sopenharmony_ci		goto drop;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (op == FIP_OP_DISC && sub == FIP_SC_ADV) {
57562306a36Sopenharmony_ci		if (fnic_fcoe_vlan_check(fnic, ntohs(fiph->fip_flags)))
57662306a36Sopenharmony_ci			goto drop;
57762306a36Sopenharmony_ci		/* pass it on to fcoe */
57862306a36Sopenharmony_ci		ret = 1;
57962306a36Sopenharmony_ci	} else if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) {
58062306a36Sopenharmony_ci		/* set the vlan as used */
58162306a36Sopenharmony_ci		fnic_fcoe_process_vlan_resp(fnic, skb);
58262306a36Sopenharmony_ci		ret = 0;
58362306a36Sopenharmony_ci	} else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) {
58462306a36Sopenharmony_ci		/* received CVL request, restart vlan disc */
58562306a36Sopenharmony_ci		fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
58662306a36Sopenharmony_ci		/* pass it on to fcoe */
58762306a36Sopenharmony_ci		ret = 1;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_cidrop:
59062306a36Sopenharmony_ci	return ret;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_civoid fnic_handle_fip_frame(struct work_struct *work)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, fip_frame_work);
59662306a36Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
59762306a36Sopenharmony_ci	unsigned long flags;
59862306a36Sopenharmony_ci	struct sk_buff *skb;
59962306a36Sopenharmony_ci	struct ethhdr *eh;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	while ((skb = skb_dequeue(&fnic->fip_frame_queue))) {
60262306a36Sopenharmony_ci		spin_lock_irqsave(&fnic->fnic_lock, flags);
60362306a36Sopenharmony_ci		if (fnic->stop_rx_link_events) {
60462306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
60562306a36Sopenharmony_ci			dev_kfree_skb(skb);
60662306a36Sopenharmony_ci			return;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci		/*
60962306a36Sopenharmony_ci		 * If we're in a transitional state, just re-queue and return.
61062306a36Sopenharmony_ci		 * The queue will be serviced when we get to a stable state.
61162306a36Sopenharmony_ci		 */
61262306a36Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_MODE &&
61362306a36Sopenharmony_ci		    fnic->state != FNIC_IN_ETH_MODE) {
61462306a36Sopenharmony_ci			skb_queue_head(&fnic->fip_frame_queue, skb);
61562306a36Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
61662306a36Sopenharmony_ci			return;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
61962306a36Sopenharmony_ci		eh = (struct ethhdr *)skb->data;
62062306a36Sopenharmony_ci		if (eh->h_proto == htons(ETH_P_FIP)) {
62162306a36Sopenharmony_ci			skb_pull(skb, sizeof(*eh));
62262306a36Sopenharmony_ci			if (fnic_fcoe_handle_fip_frame(fnic, skb) <= 0) {
62362306a36Sopenharmony_ci				dev_kfree_skb(skb);
62462306a36Sopenharmony_ci				continue;
62562306a36Sopenharmony_ci			}
62662306a36Sopenharmony_ci			/*
62762306a36Sopenharmony_ci			 * If there's FLOGI rejects - clear all
62862306a36Sopenharmony_ci			 * fcf's & restart from scratch
62962306a36Sopenharmony_ci			 */
63062306a36Sopenharmony_ci			if (is_fnic_fip_flogi_reject(&fnic->ctlr, skb)) {
63162306a36Sopenharmony_ci				atomic64_inc(
63262306a36Sopenharmony_ci					&fnic_stats->vlan_stats.flogi_rejects);
63362306a36Sopenharmony_ci				shost_printk(KERN_INFO, fnic->lport->host,
63462306a36Sopenharmony_ci					  "Trigger a Link down - VLAN Disc\n");
63562306a36Sopenharmony_ci				fcoe_ctlr_link_down(&fnic->ctlr);
63662306a36Sopenharmony_ci				/* start FCoE VLAN discovery */
63762306a36Sopenharmony_ci				fnic_fcoe_send_vlan_req(fnic);
63862306a36Sopenharmony_ci				dev_kfree_skb(skb);
63962306a36Sopenharmony_ci				continue;
64062306a36Sopenharmony_ci			}
64162306a36Sopenharmony_ci			fcoe_ctlr_recv(&fnic->ctlr, skb);
64262306a36Sopenharmony_ci			continue;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci/**
64862306a36Sopenharmony_ci * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
64962306a36Sopenharmony_ci * @fnic:	fnic instance.
65062306a36Sopenharmony_ci * @skb:	Ethernet Frame.
65162306a36Sopenharmony_ci */
65262306a36Sopenharmony_cistatic inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct fc_frame *fp;
65562306a36Sopenharmony_ci	struct ethhdr *eh;
65662306a36Sopenharmony_ci	struct fcoe_hdr *fcoe_hdr;
65762306a36Sopenharmony_ci	struct fcoe_crc_eof *ft;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/*
66062306a36Sopenharmony_ci	 * Undo VLAN encapsulation if present.
66162306a36Sopenharmony_ci	 */
66262306a36Sopenharmony_ci	eh = (struct ethhdr *)skb->data;
66362306a36Sopenharmony_ci	if (eh->h_proto == htons(ETH_P_8021Q)) {
66462306a36Sopenharmony_ci		memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
66562306a36Sopenharmony_ci		eh = skb_pull(skb, VLAN_HLEN);
66662306a36Sopenharmony_ci		skb_reset_mac_header(skb);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci	if (eh->h_proto == htons(ETH_P_FIP)) {
66962306a36Sopenharmony_ci		if (!(fnic->config.flags & VFCF_FIP_CAPABLE)) {
67062306a36Sopenharmony_ci			printk(KERN_ERR "Dropped FIP frame, as firmware "
67162306a36Sopenharmony_ci					"uses non-FIP mode, Enable FIP "
67262306a36Sopenharmony_ci					"using UCSM\n");
67362306a36Sopenharmony_ci			goto drop;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci		if ((fnic_fc_trace_set_data(fnic->lport->host->host_no,
67662306a36Sopenharmony_ci			FNIC_FC_RECV|0x80, (char *)skb->data, skb->len)) != 0) {
67762306a36Sopenharmony_ci			printk(KERN_ERR "fnic ctlr frame trace error!!!");
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci		skb_queue_tail(&fnic->fip_frame_queue, skb);
68062306a36Sopenharmony_ci		queue_work(fnic_fip_queue, &fnic->fip_frame_work);
68162306a36Sopenharmony_ci		return 1;		/* let caller know packet was used */
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci	if (eh->h_proto != htons(ETH_P_FCOE))
68462306a36Sopenharmony_ci		goto drop;
68562306a36Sopenharmony_ci	skb_set_network_header(skb, sizeof(*eh));
68662306a36Sopenharmony_ci	skb_pull(skb, sizeof(*eh));
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	fcoe_hdr = (struct fcoe_hdr *)skb->data;
68962306a36Sopenharmony_ci	if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
69062306a36Sopenharmony_ci		goto drop;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	fp = (struct fc_frame *)skb;
69362306a36Sopenharmony_ci	fc_frame_init(fp);
69462306a36Sopenharmony_ci	fr_sof(fp) = fcoe_hdr->fcoe_sof;
69562306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct fcoe_hdr));
69662306a36Sopenharmony_ci	skb_reset_transport_header(skb);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft));
69962306a36Sopenharmony_ci	fr_eof(fp) = ft->fcoe_eof;
70062306a36Sopenharmony_ci	skb_trim(skb, skb->len - sizeof(*ft));
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_cidrop:
70362306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
70462306a36Sopenharmony_ci	return -1;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci/**
70862306a36Sopenharmony_ci * fnic_update_mac_locked() - set data MAC address and filters.
70962306a36Sopenharmony_ci * @fnic:	fnic instance.
71062306a36Sopenharmony_ci * @new:	newly-assigned FCoE MAC address.
71162306a36Sopenharmony_ci *
71262306a36Sopenharmony_ci * Called with the fnic lock held.
71362306a36Sopenharmony_ci */
71462306a36Sopenharmony_civoid fnic_update_mac_locked(struct fnic *fnic, u8 *new)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	u8 *ctl = fnic->ctlr.ctl_src_addr;
71762306a36Sopenharmony_ci	u8 *data = fnic->data_src_addr;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (is_zero_ether_addr(new))
72062306a36Sopenharmony_ci		new = ctl;
72162306a36Sopenharmony_ci	if (ether_addr_equal(data, new))
72262306a36Sopenharmony_ci		return;
72362306a36Sopenharmony_ci	FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
72462306a36Sopenharmony_ci	if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl))
72562306a36Sopenharmony_ci		vnic_dev_del_addr(fnic->vdev, data);
72662306a36Sopenharmony_ci	memcpy(data, new, ETH_ALEN);
72762306a36Sopenharmony_ci	if (!ether_addr_equal(new, ctl))
72862306a36Sopenharmony_ci		vnic_dev_add_addr(fnic->vdev, new);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/**
73262306a36Sopenharmony_ci * fnic_update_mac() - set data MAC address and filters.
73362306a36Sopenharmony_ci * @lport:	local port.
73462306a36Sopenharmony_ci * @new:	newly-assigned FCoE MAC address.
73562306a36Sopenharmony_ci */
73662306a36Sopenharmony_civoid fnic_update_mac(struct fc_lport *lport, u8 *new)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lport);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	spin_lock_irq(&fnic->fnic_lock);
74162306a36Sopenharmony_ci	fnic_update_mac_locked(fnic, new);
74262306a36Sopenharmony_ci	spin_unlock_irq(&fnic->fnic_lock);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci/**
74662306a36Sopenharmony_ci * fnic_set_port_id() - set the port_ID after successful FLOGI.
74762306a36Sopenharmony_ci * @lport:	local port.
74862306a36Sopenharmony_ci * @port_id:	assigned FC_ID.
74962306a36Sopenharmony_ci * @fp:		received frame containing the FLOGI accept or NULL.
75062306a36Sopenharmony_ci *
75162306a36Sopenharmony_ci * This is called from libfc when a new FC_ID has been assigned.
75262306a36Sopenharmony_ci * This causes us to reset the firmware to FC_MODE and setup the new MAC
75362306a36Sopenharmony_ci * address and FC_ID.
75462306a36Sopenharmony_ci *
75562306a36Sopenharmony_ci * It is also called with FC_ID 0 when we're logged off.
75662306a36Sopenharmony_ci *
75762306a36Sopenharmony_ci * If the FC_ID is due to point-to-point, fp may be NULL.
75862306a36Sopenharmony_ci */
75962306a36Sopenharmony_civoid fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lport);
76262306a36Sopenharmony_ci	u8 *mac;
76362306a36Sopenharmony_ci	int ret;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
76662306a36Sopenharmony_ci		     port_id, fp);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/*
76962306a36Sopenharmony_ci	 * If we're clearing the FC_ID, change to use the ctl_src_addr.
77062306a36Sopenharmony_ci	 * Set ethernet mode to send FLOGI.
77162306a36Sopenharmony_ci	 */
77262306a36Sopenharmony_ci	if (!port_id) {
77362306a36Sopenharmony_ci		fnic_update_mac(lport, fnic->ctlr.ctl_src_addr);
77462306a36Sopenharmony_ci		fnic_set_eth_mode(fnic);
77562306a36Sopenharmony_ci		return;
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (fp) {
77962306a36Sopenharmony_ci		mac = fr_cb(fp)->granted_mac;
78062306a36Sopenharmony_ci		if (is_zero_ether_addr(mac)) {
78162306a36Sopenharmony_ci			/* non-FIP - FLOGI already accepted - ignore return */
78262306a36Sopenharmony_ci			fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp);
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci		fnic_update_mac(lport, mac);
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Change state to reflect transition to FC mode */
78862306a36Sopenharmony_ci	spin_lock_irq(&fnic->fnic_lock);
78962306a36Sopenharmony_ci	if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
79062306a36Sopenharmony_ci		fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
79162306a36Sopenharmony_ci	else {
79262306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
79362306a36Sopenharmony_ci			     "Unexpected fnic state %s while"
79462306a36Sopenharmony_ci			     " processing flogi resp\n",
79562306a36Sopenharmony_ci			     fnic_state_to_str(fnic->state));
79662306a36Sopenharmony_ci		spin_unlock_irq(&fnic->fnic_lock);
79762306a36Sopenharmony_ci		return;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci	spin_unlock_irq(&fnic->fnic_lock);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	/*
80262306a36Sopenharmony_ci	 * Send FLOGI registration to firmware to set up FC mode.
80362306a36Sopenharmony_ci	 * The new address will be set up when registration completes.
80462306a36Sopenharmony_ci	 */
80562306a36Sopenharmony_ci	ret = fnic_flogi_reg_handler(fnic, port_id);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (ret < 0) {
80862306a36Sopenharmony_ci		spin_lock_irq(&fnic->fnic_lock);
80962306a36Sopenharmony_ci		if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
81062306a36Sopenharmony_ci			fnic->state = FNIC_IN_ETH_MODE;
81162306a36Sopenharmony_ci		spin_unlock_irq(&fnic->fnic_lock);
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
81662306a36Sopenharmony_ci				    *cq_desc, struct vnic_rq_buf *buf,
81762306a36Sopenharmony_ci				    int skipped __attribute__((unused)),
81862306a36Sopenharmony_ci				    void *opaque)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(rq->vdev);
82162306a36Sopenharmony_ci	struct sk_buff *skb;
82262306a36Sopenharmony_ci	struct fc_frame *fp;
82362306a36Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
82462306a36Sopenharmony_ci	u8 type, color, eop, sop, ingress_port, vlan_stripped;
82562306a36Sopenharmony_ci	u8 fcoe = 0, fcoe_sof, fcoe_eof;
82662306a36Sopenharmony_ci	u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0;
82762306a36Sopenharmony_ci	u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok;
82862306a36Sopenharmony_ci	u8 ipv6, ipv4, ipv4_fragment, rss_type, csum_not_calc;
82962306a36Sopenharmony_ci	u8 fcs_ok = 1, packet_error = 0;
83062306a36Sopenharmony_ci	u16 q_number, completed_index, bytes_written = 0, vlan, checksum;
83162306a36Sopenharmony_ci	u32 rss_hash;
83262306a36Sopenharmony_ci	u16 exchange_id, tmpl;
83362306a36Sopenharmony_ci	u8 sof = 0;
83462306a36Sopenharmony_ci	u8 eof = 0;
83562306a36Sopenharmony_ci	u32 fcp_bytes_written = 0;
83662306a36Sopenharmony_ci	unsigned long flags;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
83962306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
84062306a36Sopenharmony_ci	skb = buf->os_buf;
84162306a36Sopenharmony_ci	fp = (struct fc_frame *)skb;
84262306a36Sopenharmony_ci	buf->os_buf = NULL;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
84562306a36Sopenharmony_ci	if (type == CQ_DESC_TYPE_RQ_FCP) {
84662306a36Sopenharmony_ci		cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *)cq_desc,
84762306a36Sopenharmony_ci				   &type, &color, &q_number, &completed_index,
84862306a36Sopenharmony_ci				   &eop, &sop, &fcoe_fc_crc_ok, &exchange_id,
84962306a36Sopenharmony_ci				   &tmpl, &fcp_bytes_written, &sof, &eof,
85062306a36Sopenharmony_ci				   &ingress_port, &packet_error,
85162306a36Sopenharmony_ci				   &fcoe_enc_error, &fcs_ok, &vlan_stripped,
85262306a36Sopenharmony_ci				   &vlan);
85362306a36Sopenharmony_ci		skb_trim(skb, fcp_bytes_written);
85462306a36Sopenharmony_ci		fr_sof(fp) = sof;
85562306a36Sopenharmony_ci		fr_eof(fp) = eof;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	} else if (type == CQ_DESC_TYPE_RQ_ENET) {
85862306a36Sopenharmony_ci		cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
85962306a36Sopenharmony_ci				    &type, &color, &q_number, &completed_index,
86062306a36Sopenharmony_ci				    &ingress_port, &fcoe, &eop, &sop,
86162306a36Sopenharmony_ci				    &rss_type, &csum_not_calc, &rss_hash,
86262306a36Sopenharmony_ci				    &bytes_written, &packet_error,
86362306a36Sopenharmony_ci				    &vlan_stripped, &vlan, &checksum,
86462306a36Sopenharmony_ci				    &fcoe_sof, &fcoe_fc_crc_ok,
86562306a36Sopenharmony_ci				    &fcoe_enc_error, &fcoe_eof,
86662306a36Sopenharmony_ci				    &tcp_udp_csum_ok, &udp, &tcp,
86762306a36Sopenharmony_ci				    &ipv4_csum_ok, &ipv6, &ipv4,
86862306a36Sopenharmony_ci				    &ipv4_fragment, &fcs_ok);
86962306a36Sopenharmony_ci		skb_trim(skb, bytes_written);
87062306a36Sopenharmony_ci		if (!fcs_ok) {
87162306a36Sopenharmony_ci			atomic64_inc(&fnic_stats->misc_stats.frame_errors);
87262306a36Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
87362306a36Sopenharmony_ci				     "fcs error.  dropping packet.\n");
87462306a36Sopenharmony_ci			goto drop;
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci		if (fnic_import_rq_eth_pkt(fnic, skb))
87762306a36Sopenharmony_ci			return;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	} else {
88062306a36Sopenharmony_ci		/* wrong CQ type*/
88162306a36Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
88262306a36Sopenharmony_ci			     "fnic rq_cmpl wrong cq type x%x\n", type);
88362306a36Sopenharmony_ci		goto drop;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
88762306a36Sopenharmony_ci		atomic64_inc(&fnic_stats->misc_stats.frame_errors);
88862306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
88962306a36Sopenharmony_ci			     "fnic rq_cmpl fcoe x%x fcsok x%x"
89062306a36Sopenharmony_ci			     " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
89162306a36Sopenharmony_ci			     " x%x\n",
89262306a36Sopenharmony_ci			     fcoe, fcs_ok, packet_error,
89362306a36Sopenharmony_ci			     fcoe_fc_crc_ok, fcoe_enc_error);
89462306a36Sopenharmony_ci		goto drop;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
89862306a36Sopenharmony_ci	if (fnic->stop_rx_link_events) {
89962306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
90062306a36Sopenharmony_ci		goto drop;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci	fr_dev(fp) = fnic->lport;
90362306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
90462306a36Sopenharmony_ci	if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_RECV,
90562306a36Sopenharmony_ci					(char *)skb->data, skb->len)) != 0) {
90662306a36Sopenharmony_ci		printk(KERN_ERR "fnic ctlr frame trace error!!!");
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	skb_queue_tail(&fnic->frame_queue, skb);
91062306a36Sopenharmony_ci	queue_work(fnic_event_queue, &fnic->frame_work);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return;
91362306a36Sopenharmony_cidrop:
91462306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic int fnic_rq_cmpl_handler_cont(struct vnic_dev *vdev,
91862306a36Sopenharmony_ci				     struct cq_desc *cq_desc, u8 type,
91962306a36Sopenharmony_ci				     u16 q_number, u16 completed_index,
92062306a36Sopenharmony_ci				     void *opaque)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(vdev);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	vnic_rq_service(&fnic->rq[q_number], cq_desc, completed_index,
92562306a36Sopenharmony_ci			VNIC_RQ_RETURN_DESC, fnic_rq_cmpl_frame_recv,
92662306a36Sopenharmony_ci			NULL);
92762306a36Sopenharmony_ci	return 0;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ciint fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	unsigned int tot_rq_work_done = 0, cur_work_done;
93362306a36Sopenharmony_ci	unsigned int i;
93462306a36Sopenharmony_ci	int err;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++) {
93762306a36Sopenharmony_ci		cur_work_done = vnic_cq_service(&fnic->cq[i], rq_work_to_do,
93862306a36Sopenharmony_ci						fnic_rq_cmpl_handler_cont,
93962306a36Sopenharmony_ci						NULL);
94062306a36Sopenharmony_ci		if (cur_work_done) {
94162306a36Sopenharmony_ci			err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
94262306a36Sopenharmony_ci			if (err)
94362306a36Sopenharmony_ci				shost_printk(KERN_ERR, fnic->lport->host,
94462306a36Sopenharmony_ci					     "fnic_alloc_rq_frame can't alloc"
94562306a36Sopenharmony_ci					     " frame\n");
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci		tot_rq_work_done += cur_work_done;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	return tot_rq_work_done;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci/*
95462306a36Sopenharmony_ci * This function is called once at init time to allocate and fill RQ
95562306a36Sopenharmony_ci * buffers. Subsequently, it is called in the interrupt context after RQ
95662306a36Sopenharmony_ci * buffer processing to replenish the buffers in the RQ
95762306a36Sopenharmony_ci */
95862306a36Sopenharmony_ciint fnic_alloc_rq_frame(struct vnic_rq *rq)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(rq->vdev);
96162306a36Sopenharmony_ci	struct sk_buff *skb;
96262306a36Sopenharmony_ci	u16 len;
96362306a36Sopenharmony_ci	dma_addr_t pa;
96462306a36Sopenharmony_ci	int r;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM;
96762306a36Sopenharmony_ci	skb = dev_alloc_skb(len);
96862306a36Sopenharmony_ci	if (!skb) {
96962306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
97062306a36Sopenharmony_ci			     "Unable to allocate RQ sk_buff\n");
97162306a36Sopenharmony_ci		return -ENOMEM;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci	skb_reset_mac_header(skb);
97462306a36Sopenharmony_ci	skb_reset_transport_header(skb);
97562306a36Sopenharmony_ci	skb_reset_network_header(skb);
97662306a36Sopenharmony_ci	skb_put(skb, len);
97762306a36Sopenharmony_ci	pa = dma_map_single(&fnic->pdev->dev, skb->data, len, DMA_FROM_DEVICE);
97862306a36Sopenharmony_ci	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
97962306a36Sopenharmony_ci		r = -ENOMEM;
98062306a36Sopenharmony_ci		printk(KERN_ERR "PCI mapping failed with error %d\n", r);
98162306a36Sopenharmony_ci		goto free_skb;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	fnic_queue_rq_desc(rq, skb, pa, len);
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cifree_skb:
98862306a36Sopenharmony_ci	kfree_skb(skb);
98962306a36Sopenharmony_ci	return r;
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_civoid fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct fc_frame *fp = buf->os_buf;
99562306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(rq->vdev);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
99862306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	dev_kfree_skb(fp_skb(fp));
100162306a36Sopenharmony_ci	buf->os_buf = NULL;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci/**
100562306a36Sopenharmony_ci * fnic_eth_send() - Send Ethernet frame.
100662306a36Sopenharmony_ci * @fip:	fcoe_ctlr instance.
100762306a36Sopenharmony_ci * @skb:	Ethernet Frame, FIP, without VLAN encapsulation.
100862306a36Sopenharmony_ci */
100962306a36Sopenharmony_civoid fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct fnic *fnic = fnic_from_ctlr(fip);
101262306a36Sopenharmony_ci	struct vnic_wq *wq = &fnic->wq[0];
101362306a36Sopenharmony_ci	dma_addr_t pa;
101462306a36Sopenharmony_ci	struct ethhdr *eth_hdr;
101562306a36Sopenharmony_ci	struct vlan_ethhdr *vlan_hdr;
101662306a36Sopenharmony_ci	unsigned long flags;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (!fnic->vlan_hw_insert) {
101962306a36Sopenharmony_ci		eth_hdr = (struct ethhdr *)skb_mac_header(skb);
102062306a36Sopenharmony_ci		vlan_hdr = skb_push(skb, sizeof(*vlan_hdr) - sizeof(*eth_hdr));
102162306a36Sopenharmony_ci		memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
102262306a36Sopenharmony_ci		vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
102362306a36Sopenharmony_ci		vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
102462306a36Sopenharmony_ci		vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
102562306a36Sopenharmony_ci		if ((fnic_fc_trace_set_data(fnic->lport->host->host_no,
102662306a36Sopenharmony_ci			FNIC_FC_SEND|0x80, (char *)eth_hdr, skb->len)) != 0) {
102762306a36Sopenharmony_ci			printk(KERN_ERR "fnic ctlr frame trace error!!!");
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci	} else {
103062306a36Sopenharmony_ci		if ((fnic_fc_trace_set_data(fnic->lport->host->host_no,
103162306a36Sopenharmony_ci			FNIC_FC_SEND|0x80, (char *)skb->data, skb->len)) != 0) {
103262306a36Sopenharmony_ci			printk(KERN_ERR "fnic ctlr frame trace error!!!");
103362306a36Sopenharmony_ci		}
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	pa = dma_map_single(&fnic->pdev->dev, skb->data, skb->len,
103762306a36Sopenharmony_ci			DMA_TO_DEVICE);
103862306a36Sopenharmony_ci	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
103962306a36Sopenharmony_ci		printk(KERN_ERR "DMA mapping failed\n");
104062306a36Sopenharmony_ci		goto free_skb;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->wq_lock[0], flags);
104462306a36Sopenharmony_ci	if (!vnic_wq_desc_avail(wq))
104562306a36Sopenharmony_ci		goto irq_restore;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	fnic_queue_wq_eth_desc(wq, skb, pa, skb->len,
104862306a36Sopenharmony_ci			       0 /* hw inserts cos value */,
104962306a36Sopenharmony_ci			       fnic->vlan_id, 1);
105062306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
105162306a36Sopenharmony_ci	return;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ciirq_restore:
105462306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
105562306a36Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, pa, skb->len, DMA_TO_DEVICE);
105662306a36Sopenharmony_cifree_skb:
105762306a36Sopenharmony_ci	kfree_skb(skb);
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci/*
106162306a36Sopenharmony_ci * Send FC frame.
106262306a36Sopenharmony_ci */
106362306a36Sopenharmony_cistatic int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct vnic_wq *wq = &fnic->wq[0];
106662306a36Sopenharmony_ci	struct sk_buff *skb;
106762306a36Sopenharmony_ci	dma_addr_t pa;
106862306a36Sopenharmony_ci	struct ethhdr *eth_hdr;
106962306a36Sopenharmony_ci	struct vlan_ethhdr *vlan_hdr;
107062306a36Sopenharmony_ci	struct fcoe_hdr *fcoe_hdr;
107162306a36Sopenharmony_ci	struct fc_frame_header *fh;
107262306a36Sopenharmony_ci	u32 tot_len, eth_hdr_len;
107362306a36Sopenharmony_ci	int ret = 0;
107462306a36Sopenharmony_ci	unsigned long flags;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	fh = fc_frame_header_get(fp);
107762306a36Sopenharmony_ci	skb = fp_skb(fp);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
108062306a36Sopenharmony_ci	    fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb))
108162306a36Sopenharmony_ci		return 0;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (!fnic->vlan_hw_insert) {
108462306a36Sopenharmony_ci		eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
108562306a36Sopenharmony_ci		vlan_hdr = skb_push(skb, eth_hdr_len);
108662306a36Sopenharmony_ci		eth_hdr = (struct ethhdr *)vlan_hdr;
108762306a36Sopenharmony_ci		vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
108862306a36Sopenharmony_ci		vlan_hdr->h_vlan_encapsulated_proto = htons(ETH_P_FCOE);
108962306a36Sopenharmony_ci		vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
109062306a36Sopenharmony_ci		fcoe_hdr = (struct fcoe_hdr *)(vlan_hdr + 1);
109162306a36Sopenharmony_ci	} else {
109262306a36Sopenharmony_ci		eth_hdr_len = sizeof(*eth_hdr) + sizeof(*fcoe_hdr);
109362306a36Sopenharmony_ci		eth_hdr = skb_push(skb, eth_hdr_len);
109462306a36Sopenharmony_ci		eth_hdr->h_proto = htons(ETH_P_FCOE);
109562306a36Sopenharmony_ci		fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (fnic->ctlr.map_dest)
109962306a36Sopenharmony_ci		fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
110062306a36Sopenharmony_ci	else
110162306a36Sopenharmony_ci		memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN);
110262306a36Sopenharmony_ci	memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	tot_len = skb->len;
110562306a36Sopenharmony_ci	BUG_ON(tot_len % 4);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	memset(fcoe_hdr, 0, sizeof(*fcoe_hdr));
110862306a36Sopenharmony_ci	fcoe_hdr->fcoe_sof = fr_sof(fp);
110962306a36Sopenharmony_ci	if (FC_FCOE_VER)
111062306a36Sopenharmony_ci		FC_FCOE_ENCAPS_VER(fcoe_hdr, FC_FCOE_VER);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	pa = dma_map_single(&fnic->pdev->dev, eth_hdr, tot_len, DMA_TO_DEVICE);
111362306a36Sopenharmony_ci	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
111462306a36Sopenharmony_ci		ret = -ENOMEM;
111562306a36Sopenharmony_ci		printk(KERN_ERR "DMA map failed with error %d\n", ret);
111662306a36Sopenharmony_ci		goto free_skb_on_err;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_SEND,
112062306a36Sopenharmony_ci				(char *)eth_hdr, tot_len)) != 0) {
112162306a36Sopenharmony_ci		printk(KERN_ERR "fnic ctlr frame trace error!!!");
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->wq_lock[0], flags);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (!vnic_wq_desc_avail(wq)) {
112762306a36Sopenharmony_ci		dma_unmap_single(&fnic->pdev->dev, pa, tot_len, DMA_TO_DEVICE);
112862306a36Sopenharmony_ci		ret = -1;
112962306a36Sopenharmony_ci		goto irq_restore;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp),
113362306a36Sopenharmony_ci			   0 /* hw inserts cos value */,
113462306a36Sopenharmony_ci			   fnic->vlan_id, 1, 1, 1);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ciirq_restore:
113762306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cifree_skb_on_err:
114062306a36Sopenharmony_ci	if (ret)
114162306a36Sopenharmony_ci		dev_kfree_skb_any(fp_skb(fp));
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return ret;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/*
114762306a36Sopenharmony_ci * fnic_send
114862306a36Sopenharmony_ci * Routine to send a raw frame
114962306a36Sopenharmony_ci */
115062306a36Sopenharmony_ciint fnic_send(struct fc_lport *lp, struct fc_frame *fp)
115162306a36Sopenharmony_ci{
115262306a36Sopenharmony_ci	struct fnic *fnic = lport_priv(lp);
115362306a36Sopenharmony_ci	unsigned long flags;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (fnic->in_remove) {
115662306a36Sopenharmony_ci		dev_kfree_skb(fp_skb(fp));
115762306a36Sopenharmony_ci		return -1;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	/*
116162306a36Sopenharmony_ci	 * Queue frame if in a transitional state.
116262306a36Sopenharmony_ci	 * This occurs while registering the Port_ID / MAC address after FLOGI.
116362306a36Sopenharmony_ci	 */
116462306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
116562306a36Sopenharmony_ci	if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) {
116662306a36Sopenharmony_ci		skb_queue_tail(&fnic->tx_queue, fp_skb(fp));
116762306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
116862306a36Sopenharmony_ci		return 0;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	return fnic_send_frame(fnic, fp);
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci/**
117662306a36Sopenharmony_ci * fnic_flush_tx() - send queued frames.
117762306a36Sopenharmony_ci * @fnic: fnic device
117862306a36Sopenharmony_ci *
117962306a36Sopenharmony_ci * Send frames that were waiting to go out in FC or Ethernet mode.
118062306a36Sopenharmony_ci * Whenever changing modes we purge queued frames, so these frames should
118162306a36Sopenharmony_ci * be queued for the stable mode that we're in, either FC or Ethernet.
118262306a36Sopenharmony_ci *
118362306a36Sopenharmony_ci * Called without fnic_lock held.
118462306a36Sopenharmony_ci */
118562306a36Sopenharmony_civoid fnic_flush_tx(struct fnic *fnic)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	struct sk_buff *skb;
118862306a36Sopenharmony_ci	struct fc_frame *fp;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	while ((skb = skb_dequeue(&fnic->tx_queue))) {
119162306a36Sopenharmony_ci		fp = (struct fc_frame *)skb;
119262306a36Sopenharmony_ci		fnic_send_frame(fnic, fp);
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci/**
119762306a36Sopenharmony_ci * fnic_set_eth_mode() - put fnic into ethernet mode.
119862306a36Sopenharmony_ci * @fnic: fnic device
119962306a36Sopenharmony_ci *
120062306a36Sopenharmony_ci * Called without fnic lock held.
120162306a36Sopenharmony_ci */
120262306a36Sopenharmony_cistatic void fnic_set_eth_mode(struct fnic *fnic)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	unsigned long flags;
120562306a36Sopenharmony_ci	enum fnic_state old_state;
120662306a36Sopenharmony_ci	int ret;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
120962306a36Sopenharmony_ciagain:
121062306a36Sopenharmony_ci	old_state = fnic->state;
121162306a36Sopenharmony_ci	switch (old_state) {
121262306a36Sopenharmony_ci	case FNIC_IN_FC_MODE:
121362306a36Sopenharmony_ci	case FNIC_IN_ETH_TRANS_FC_MODE:
121462306a36Sopenharmony_ci	default:
121562306a36Sopenharmony_ci		fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
121662306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		ret = fnic_fw_reset_handler(fnic);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		spin_lock_irqsave(&fnic->fnic_lock, flags);
122162306a36Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
122262306a36Sopenharmony_ci			goto again;
122362306a36Sopenharmony_ci		if (ret)
122462306a36Sopenharmony_ci			fnic->state = old_state;
122562306a36Sopenharmony_ci		break;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	case FNIC_IN_FC_TRANS_ETH_MODE:
122862306a36Sopenharmony_ci	case FNIC_IN_ETH_MODE:
122962306a36Sopenharmony_ci		break;
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic void fnic_wq_complete_frame_send(struct vnic_wq *wq,
123562306a36Sopenharmony_ci					struct cq_desc *cq_desc,
123662306a36Sopenharmony_ci					struct vnic_wq_buf *buf, void *opaque)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct sk_buff *skb = buf->os_buf;
123962306a36Sopenharmony_ci	struct fc_frame *fp = (struct fc_frame *)skb;
124062306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(wq->vdev);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
124362306a36Sopenharmony_ci			 DMA_TO_DEVICE);
124462306a36Sopenharmony_ci	dev_kfree_skb_irq(fp_skb(fp));
124562306a36Sopenharmony_ci	buf->os_buf = NULL;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic int fnic_wq_cmpl_handler_cont(struct vnic_dev *vdev,
124962306a36Sopenharmony_ci				     struct cq_desc *cq_desc, u8 type,
125062306a36Sopenharmony_ci				     u16 q_number, u16 completed_index,
125162306a36Sopenharmony_ci				     void *opaque)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(vdev);
125462306a36Sopenharmony_ci	unsigned long flags;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->wq_lock[q_number], flags);
125762306a36Sopenharmony_ci	vnic_wq_service(&fnic->wq[q_number], cq_desc, completed_index,
125862306a36Sopenharmony_ci			fnic_wq_complete_frame_send, NULL);
125962306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[q_number], flags);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return 0;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ciint fnic_wq_cmpl_handler(struct fnic *fnic, int work_to_do)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	unsigned int wq_work_done = 0;
126762306a36Sopenharmony_ci	unsigned int i;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	for (i = 0; i < fnic->raw_wq_count; i++) {
127062306a36Sopenharmony_ci		wq_work_done  += vnic_cq_service(&fnic->cq[fnic->rq_count+i],
127162306a36Sopenharmony_ci						 work_to_do,
127262306a36Sopenharmony_ci						 fnic_wq_cmpl_handler_cont,
127362306a36Sopenharmony_ci						 NULL);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	return wq_work_done;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_civoid fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	struct fc_frame *fp = buf->os_buf;
128362306a36Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(wq->vdev);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
128662306a36Sopenharmony_ci			 DMA_TO_DEVICE);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	dev_kfree_skb(fp_skb(fp));
128962306a36Sopenharmony_ci	buf->os_buf = NULL;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_civoid fnic_fcoe_reset_vlans(struct fnic *fnic)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	unsigned long flags;
129562306a36Sopenharmony_ci	struct fcoe_vlan *vlan;
129662306a36Sopenharmony_ci	struct fcoe_vlan *next;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/*
129962306a36Sopenharmony_ci	 * indicate a link down to fcoe so that all fcf's are free'd
130062306a36Sopenharmony_ci	 * might not be required since we did this before sending vlan
130162306a36Sopenharmony_ci	 * discovery request
130262306a36Sopenharmony_ci	 */
130362306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
130462306a36Sopenharmony_ci	if (!list_empty(&fnic->vlans)) {
130562306a36Sopenharmony_ci		list_for_each_entry_safe(vlan, next, &fnic->vlans, list) {
130662306a36Sopenharmony_ci			list_del(&vlan->list);
130762306a36Sopenharmony_ci			kfree(vlan);
130862306a36Sopenharmony_ci		}
130962306a36Sopenharmony_ci	}
131062306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_civoid fnic_handle_fip_timer(struct fnic *fnic)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	unsigned long flags;
131662306a36Sopenharmony_ci	struct fcoe_vlan *vlan;
131762306a36Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
131862306a36Sopenharmony_ci	u64 sol_time;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
132162306a36Sopenharmony_ci	if (fnic->stop_rx_link_events) {
132262306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
132362306a36Sopenharmony_ci		return;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (fnic->ctlr.mode == FIP_MODE_NON_FIP)
132862306a36Sopenharmony_ci		return;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
133162306a36Sopenharmony_ci	if (list_empty(&fnic->vlans)) {
133262306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
133362306a36Sopenharmony_ci		/* no vlans available, try again */
133462306a36Sopenharmony_ci		if (unlikely(fnic_log_level & FNIC_FCS_LOGGING))
133562306a36Sopenharmony_ci			if (printk_ratelimit())
133662306a36Sopenharmony_ci				shost_printk(KERN_DEBUG, fnic->lport->host,
133762306a36Sopenharmony_ci						"Start VLAN Discovery\n");
133862306a36Sopenharmony_ci		fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
133962306a36Sopenharmony_ci		return;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
134362306a36Sopenharmony_ci	FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
134462306a36Sopenharmony_ci		  "fip_timer: vlan %d state %d sol_count %d\n",
134562306a36Sopenharmony_ci		  vlan->vid, vlan->state, vlan->sol_count);
134662306a36Sopenharmony_ci	switch (vlan->state) {
134762306a36Sopenharmony_ci	case FIP_VLAN_USED:
134862306a36Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
134962306a36Sopenharmony_ci			  "FIP VLAN is selected for FC transaction\n");
135062306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
135162306a36Sopenharmony_ci		break;
135262306a36Sopenharmony_ci	case FIP_VLAN_FAILED:
135362306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
135462306a36Sopenharmony_ci		/* if all vlans are in failed state, restart vlan disc */
135562306a36Sopenharmony_ci		if (unlikely(fnic_log_level & FNIC_FCS_LOGGING))
135662306a36Sopenharmony_ci			if (printk_ratelimit())
135762306a36Sopenharmony_ci				shost_printk(KERN_DEBUG, fnic->lport->host,
135862306a36Sopenharmony_ci					  "Start VLAN Discovery\n");
135962306a36Sopenharmony_ci		fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
136062306a36Sopenharmony_ci		break;
136162306a36Sopenharmony_ci	case FIP_VLAN_SENT:
136262306a36Sopenharmony_ci		if (vlan->sol_count >= FCOE_CTLR_MAX_SOL) {
136362306a36Sopenharmony_ci			/*
136462306a36Sopenharmony_ci			 * no response on this vlan, remove  from the list.
136562306a36Sopenharmony_ci			 * Try the next vlan
136662306a36Sopenharmony_ci			 */
136762306a36Sopenharmony_ci			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
136862306a36Sopenharmony_ci				  "Dequeue this VLAN ID %d from list\n",
136962306a36Sopenharmony_ci				  vlan->vid);
137062306a36Sopenharmony_ci			list_del(&vlan->list);
137162306a36Sopenharmony_ci			kfree(vlan);
137262306a36Sopenharmony_ci			vlan = NULL;
137362306a36Sopenharmony_ci			if (list_empty(&fnic->vlans)) {
137462306a36Sopenharmony_ci				/* we exhausted all vlans, restart vlan disc */
137562306a36Sopenharmony_ci				spin_unlock_irqrestore(&fnic->vlans_lock,
137662306a36Sopenharmony_ci							flags);
137762306a36Sopenharmony_ci				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
137862306a36Sopenharmony_ci					  "fip_timer: vlan list empty, "
137962306a36Sopenharmony_ci					  "trigger vlan disc\n");
138062306a36Sopenharmony_ci				fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
138162306a36Sopenharmony_ci				return;
138262306a36Sopenharmony_ci			}
138362306a36Sopenharmony_ci			/* check the next vlan */
138462306a36Sopenharmony_ci			vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan,
138562306a36Sopenharmony_ci							list);
138662306a36Sopenharmony_ci			fnic->set_vlan(fnic, vlan->vid);
138762306a36Sopenharmony_ci			vlan->state = FIP_VLAN_SENT; /* sent now */
138862306a36Sopenharmony_ci		}
138962306a36Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
139062306a36Sopenharmony_ci		atomic64_inc(&fnic_stats->vlan_stats.sol_expiry_count);
139162306a36Sopenharmony_ci		vlan->sol_count++;
139262306a36Sopenharmony_ci		sol_time = jiffies + msecs_to_jiffies
139362306a36Sopenharmony_ci					(FCOE_CTLR_START_DELAY);
139462306a36Sopenharmony_ci		mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
139562306a36Sopenharmony_ci		break;
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ci}
1398