18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify
68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
78c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
108c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
118c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
128c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
138c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
158c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
168c2ecf20Sopenharmony_ci * SOFTWARE.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
228c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
238c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
248c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
258c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
268c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
278c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fip.h>
288c2ecf20Sopenharmony_ci#include <scsi/fc/fc_els.h>
298c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fcoe.h>
308c2ecf20Sopenharmony_ci#include <scsi/fc_frame.h>
318c2ecf20Sopenharmony_ci#include <scsi/libfc.h>
328c2ecf20Sopenharmony_ci#include "fnic_io.h"
338c2ecf20Sopenharmony_ci#include "fnic.h"
348c2ecf20Sopenharmony_ci#include "fnic_fip.h"
358c2ecf20Sopenharmony_ci#include "cq_enet_desc.h"
368c2ecf20Sopenharmony_ci#include "cq_exch_desc.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
398c2ecf20Sopenharmony_cistruct workqueue_struct *fnic_fip_queue;
408c2ecf20Sopenharmony_cistruct workqueue_struct *fnic_event_queue;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void fnic_set_eth_mode(struct fnic *);
438c2ecf20Sopenharmony_cistatic void fnic_fcoe_send_vlan_req(struct fnic *fnic);
448c2ecf20Sopenharmony_cistatic void fnic_fcoe_start_fcf_disc(struct fnic *fnic);
458c2ecf20Sopenharmony_cistatic void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *);
468c2ecf20Sopenharmony_cistatic int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag);
478c2ecf20Sopenharmony_cistatic int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_civoid fnic_handle_link(struct work_struct *work)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, link_work);
528c2ecf20Sopenharmony_ci	unsigned long flags;
538c2ecf20Sopenharmony_ci	int old_link_status;
548c2ecf20Sopenharmony_ci	u32 old_link_down_cnt;
558c2ecf20Sopenharmony_ci	u64 old_port_speed, new_port_speed;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (fnic->stop_rx_link_events) {
608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
618c2ecf20Sopenharmony_ci		return;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	old_link_down_cnt = fnic->link_down_cnt;
658c2ecf20Sopenharmony_ci	old_link_status = fnic->link_status;
668c2ecf20Sopenharmony_ci	old_port_speed = atomic64_read(
678c2ecf20Sopenharmony_ci			&fnic->fnic_stats.misc_stats.current_port_speed);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	fnic->link_status = vnic_dev_link_status(fnic->vdev);
708c2ecf20Sopenharmony_ci	fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	new_port_speed = vnic_dev_port_speed(fnic->vdev);
738c2ecf20Sopenharmony_ci	atomic64_set(&fnic->fnic_stats.misc_stats.current_port_speed,
748c2ecf20Sopenharmony_ci			new_port_speed);
758c2ecf20Sopenharmony_ci	if (old_port_speed != new_port_speed)
768c2ecf20Sopenharmony_ci		shost_printk(KERN_INFO, fnic->lport->host,
778c2ecf20Sopenharmony_ci				"Current vnic speed set to :  %llu\n",
788c2ecf20Sopenharmony_ci				new_port_speed);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	switch (vnic_dev_port_speed(fnic->vdev)) {
818c2ecf20Sopenharmony_ci	case DCEM_PORTSPEED_10G:
828c2ecf20Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_10GBIT;
838c2ecf20Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_10GBIT;
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	case DCEM_PORTSPEED_20G:
868c2ecf20Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_20GBIT;
878c2ecf20Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_20GBIT;
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci	case DCEM_PORTSPEED_25G:
908c2ecf20Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_25GBIT;
918c2ecf20Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_25GBIT;
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci	case DCEM_PORTSPEED_40G:
948c2ecf20Sopenharmony_ci	case DCEM_PORTSPEED_4x10G:
958c2ecf20Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_40GBIT;
968c2ecf20Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_40GBIT;
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	case DCEM_PORTSPEED_100G:
998c2ecf20Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_100GBIT;
1008c2ecf20Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_100GBIT;
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci	default:
1038c2ecf20Sopenharmony_ci		fc_host_speed(fnic->lport->host)   = FC_PORTSPEED_UNKNOWN;
1048c2ecf20Sopenharmony_ci		fnic->lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN;
1058c2ecf20Sopenharmony_ci		break;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (old_link_status == fnic->link_status) {
1098c2ecf20Sopenharmony_ci		if (!fnic->link_status) {
1108c2ecf20Sopenharmony_ci			/* DOWN -> DOWN */
1118c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
1128c2ecf20Sopenharmony_ci			fnic_fc_trace_set_data(fnic->lport->host->host_no,
1138c2ecf20Sopenharmony_ci				FNIC_FC_LE, "Link Status: DOWN->DOWN",
1148c2ecf20Sopenharmony_ci				strlen("Link Status: DOWN->DOWN"));
1158c2ecf20Sopenharmony_ci		} else {
1168c2ecf20Sopenharmony_ci			if (old_link_down_cnt != fnic->link_down_cnt) {
1178c2ecf20Sopenharmony_ci				/* UP -> DOWN -> UP */
1188c2ecf20Sopenharmony_ci				fnic->lport->host_stats.link_failure_count++;
1198c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&fnic->fnic_lock, flags);
1208c2ecf20Sopenharmony_ci				fnic_fc_trace_set_data(
1218c2ecf20Sopenharmony_ci					fnic->lport->host->host_no,
1228c2ecf20Sopenharmony_ci					FNIC_FC_LE,
1238c2ecf20Sopenharmony_ci					"Link Status:UP_DOWN_UP",
1248c2ecf20Sopenharmony_ci					strlen("Link_Status:UP_DOWN_UP")
1258c2ecf20Sopenharmony_ci					);
1268c2ecf20Sopenharmony_ci				FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
1278c2ecf20Sopenharmony_ci					     "link down\n");
1288c2ecf20Sopenharmony_ci				fcoe_ctlr_link_down(&fnic->ctlr);
1298c2ecf20Sopenharmony_ci				if (fnic->config.flags & VFCF_FIP_CAPABLE) {
1308c2ecf20Sopenharmony_ci					/* start FCoE VLAN discovery */
1318c2ecf20Sopenharmony_ci					fnic_fc_trace_set_data(
1328c2ecf20Sopenharmony_ci						fnic->lport->host->host_no,
1338c2ecf20Sopenharmony_ci						FNIC_FC_LE,
1348c2ecf20Sopenharmony_ci						"Link Status: UP_DOWN_UP_VLAN",
1358c2ecf20Sopenharmony_ci						strlen(
1368c2ecf20Sopenharmony_ci						"Link Status: UP_DOWN_UP_VLAN")
1378c2ecf20Sopenharmony_ci						);
1388c2ecf20Sopenharmony_ci					fnic_fcoe_send_vlan_req(fnic);
1398c2ecf20Sopenharmony_ci					return;
1408c2ecf20Sopenharmony_ci				}
1418c2ecf20Sopenharmony_ci				FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
1428c2ecf20Sopenharmony_ci					     "link up\n");
1438c2ecf20Sopenharmony_ci				fcoe_ctlr_link_up(&fnic->ctlr);
1448c2ecf20Sopenharmony_ci			} else {
1458c2ecf20Sopenharmony_ci				/* UP -> UP */
1468c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&fnic->fnic_lock, flags);
1478c2ecf20Sopenharmony_ci				fnic_fc_trace_set_data(
1488c2ecf20Sopenharmony_ci					fnic->lport->host->host_no, FNIC_FC_LE,
1498c2ecf20Sopenharmony_ci					"Link Status: UP_UP",
1508c2ecf20Sopenharmony_ci					strlen("Link Status: UP_UP"));
1518c2ecf20Sopenharmony_ci			}
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci	} else if (fnic->link_status) {
1548c2ecf20Sopenharmony_ci		/* DOWN -> UP */
1558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
1568c2ecf20Sopenharmony_ci		if (fnic->config.flags & VFCF_FIP_CAPABLE) {
1578c2ecf20Sopenharmony_ci			/* start FCoE VLAN discovery */
1588c2ecf20Sopenharmony_ci				fnic_fc_trace_set_data(
1598c2ecf20Sopenharmony_ci				fnic->lport->host->host_no,
1608c2ecf20Sopenharmony_ci				FNIC_FC_LE, "Link Status: DOWN_UP_VLAN",
1618c2ecf20Sopenharmony_ci				strlen("Link Status: DOWN_UP_VLAN"));
1628c2ecf20Sopenharmony_ci			fnic_fcoe_send_vlan_req(fnic);
1638c2ecf20Sopenharmony_ci			return;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
1668c2ecf20Sopenharmony_ci		fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_LE,
1678c2ecf20Sopenharmony_ci			"Link Status: DOWN_UP", strlen("Link Status: DOWN_UP"));
1688c2ecf20Sopenharmony_ci		fcoe_ctlr_link_up(&fnic->ctlr);
1698c2ecf20Sopenharmony_ci	} else {
1708c2ecf20Sopenharmony_ci		/* UP -> DOWN */
1718c2ecf20Sopenharmony_ci		fnic->lport->host_stats.link_failure_count++;
1728c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
1738c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
1748c2ecf20Sopenharmony_ci		fnic_fc_trace_set_data(
1758c2ecf20Sopenharmony_ci			fnic->lport->host->host_no, FNIC_FC_LE,
1768c2ecf20Sopenharmony_ci			"Link Status: UP_DOWN",
1778c2ecf20Sopenharmony_ci			strlen("Link Status: UP_DOWN"));
1788c2ecf20Sopenharmony_ci		if (fnic->config.flags & VFCF_FIP_CAPABLE) {
1798c2ecf20Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
1808c2ecf20Sopenharmony_ci				"deleting fip-timer during link-down\n");
1818c2ecf20Sopenharmony_ci			del_timer_sync(&fnic->fip_timer);
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci		fcoe_ctlr_link_down(&fnic->ctlr);
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/*
1898c2ecf20Sopenharmony_ci * This function passes incoming fabric frames to libFC
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_civoid fnic_handle_frame(struct work_struct *work)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, frame_work);
1948c2ecf20Sopenharmony_ci	struct fc_lport *lp = fnic->lport;
1958c2ecf20Sopenharmony_ci	unsigned long flags;
1968c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1978c2ecf20Sopenharmony_ci	struct fc_frame *fp;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&fnic->frame_queue))) {
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fnic->fnic_lock, flags);
2028c2ecf20Sopenharmony_ci		if (fnic->stop_rx_link_events) {
2038c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2048c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
2058c2ecf20Sopenharmony_ci			return;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci		fp = (struct fc_frame *)skb;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		/*
2108c2ecf20Sopenharmony_ci		 * If we're in a transitional state, just re-queue and return.
2118c2ecf20Sopenharmony_ci		 * The queue will be serviced when we get to a stable state.
2128c2ecf20Sopenharmony_ci		 */
2138c2ecf20Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_MODE &&
2148c2ecf20Sopenharmony_ci		    fnic->state != FNIC_IN_ETH_MODE) {
2158c2ecf20Sopenharmony_ci			skb_queue_head(&fnic->frame_queue, skb);
2168c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2178c2ecf20Sopenharmony_ci			return;
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		fc_exch_recv(lp, fp);
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_civoid fnic_fcoe_evlist_free(struct fnic *fnic)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct fnic_event *fevt = NULL;
2288c2ecf20Sopenharmony_ci	struct fnic_event *next = NULL;
2298c2ecf20Sopenharmony_ci	unsigned long flags;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
2328c2ecf20Sopenharmony_ci	if (list_empty(&fnic->evlist)) {
2338c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2348c2ecf20Sopenharmony_ci		return;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	list_for_each_entry_safe(fevt, next, &fnic->evlist, list) {
2388c2ecf20Sopenharmony_ci		list_del(&fevt->list);
2398c2ecf20Sopenharmony_ci		kfree(fevt);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_civoid fnic_handle_event(struct work_struct *work)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, event_work);
2478c2ecf20Sopenharmony_ci	struct fnic_event *fevt = NULL;
2488c2ecf20Sopenharmony_ci	struct fnic_event *next = NULL;
2498c2ecf20Sopenharmony_ci	unsigned long flags;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
2528c2ecf20Sopenharmony_ci	if (list_empty(&fnic->evlist)) {
2538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2548c2ecf20Sopenharmony_ci		return;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	list_for_each_entry_safe(fevt, next, &fnic->evlist, list) {
2588c2ecf20Sopenharmony_ci		if (fnic->stop_rx_link_events) {
2598c2ecf20Sopenharmony_ci			list_del(&fevt->list);
2608c2ecf20Sopenharmony_ci			kfree(fevt);
2618c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2628c2ecf20Sopenharmony_ci			return;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci		/*
2658c2ecf20Sopenharmony_ci		 * If we're in a transitional state, just re-queue and return.
2668c2ecf20Sopenharmony_ci		 * The queue will be serviced when we get to a stable state.
2678c2ecf20Sopenharmony_ci		 */
2688c2ecf20Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_MODE &&
2698c2ecf20Sopenharmony_ci		    fnic->state != FNIC_IN_ETH_MODE) {
2708c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2718c2ecf20Sopenharmony_ci			return;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		list_del(&fevt->list);
2758c2ecf20Sopenharmony_ci		switch (fevt->event) {
2768c2ecf20Sopenharmony_ci		case FNIC_EVT_START_VLAN_DISC:
2778c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2788c2ecf20Sopenharmony_ci			fnic_fcoe_send_vlan_req(fnic);
2798c2ecf20Sopenharmony_ci			spin_lock_irqsave(&fnic->fnic_lock, flags);
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci		case FNIC_EVT_START_FCF_DISC:
2828c2ecf20Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
2838c2ecf20Sopenharmony_ci				  "Start FCF Discovery\n");
2848c2ecf20Sopenharmony_ci			fnic_fcoe_start_fcf_disc(fnic);
2858c2ecf20Sopenharmony_ci			break;
2868c2ecf20Sopenharmony_ci		default:
2878c2ecf20Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
2888c2ecf20Sopenharmony_ci				  "Unknown event 0x%x\n", fevt->event);
2898c2ecf20Sopenharmony_ci			break;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci		kfree(fevt);
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci/**
2978c2ecf20Sopenharmony_ci * Check if the Received FIP FLOGI frame is rejected
2988c2ecf20Sopenharmony_ci * @fip: The FCoE controller that received the frame
2998c2ecf20Sopenharmony_ci * @skb: The received FIP frame
3008c2ecf20Sopenharmony_ci *
3018c2ecf20Sopenharmony_ci * Returns non-zero if the frame is rejected with unsupported cmd with
3028c2ecf20Sopenharmony_ci * insufficient resource els explanation.
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_cistatic inline int is_fnic_fip_flogi_reject(struct fcoe_ctlr *fip,
3058c2ecf20Sopenharmony_ci					 struct sk_buff *skb)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct fc_lport *lport = fip->lp;
3088c2ecf20Sopenharmony_ci	struct fip_header *fiph;
3098c2ecf20Sopenharmony_ci	struct fc_frame_header *fh = NULL;
3108c2ecf20Sopenharmony_ci	struct fip_desc *desc;
3118c2ecf20Sopenharmony_ci	struct fip_encaps *els;
3128c2ecf20Sopenharmony_ci	u16 op;
3138c2ecf20Sopenharmony_ci	u8 els_op;
3148c2ecf20Sopenharmony_ci	u8 sub;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	size_t rlen;
3178c2ecf20Sopenharmony_ci	size_t dlen = 0;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (skb_linearize(skb))
3208c2ecf20Sopenharmony_ci		return 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (skb->len < sizeof(*fiph))
3238c2ecf20Sopenharmony_ci		return 0;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
3268c2ecf20Sopenharmony_ci	op = ntohs(fiph->fip_op);
3278c2ecf20Sopenharmony_ci	sub = fiph->fip_subcode;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (op != FIP_OP_LS)
3308c2ecf20Sopenharmony_ci		return 0;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (sub != FIP_SC_REP)
3338c2ecf20Sopenharmony_ci		return 0;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
3368c2ecf20Sopenharmony_ci	if (rlen + sizeof(*fiph) > skb->len)
3378c2ecf20Sopenharmony_ci		return 0;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
3408c2ecf20Sopenharmony_ci	dlen = desc->fip_dlen * FIP_BPW;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (desc->fip_dtype == FIP_DT_FLOGI) {
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		if (dlen < sizeof(*els) + sizeof(*fh) + 1)
3458c2ecf20Sopenharmony_ci			return 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		els = (struct fip_encaps *)desc;
3488c2ecf20Sopenharmony_ci		fh = (struct fc_frame_header *)(els + 1);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		if (!fh)
3518c2ecf20Sopenharmony_ci			return 0;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		/*
3548c2ecf20Sopenharmony_ci		 * ELS command code, reason and explanation should be = Reject,
3558c2ecf20Sopenharmony_ci		 * unsupported command and insufficient resource
3568c2ecf20Sopenharmony_ci		 */
3578c2ecf20Sopenharmony_ci		els_op = *(u8 *)(fh + 1);
3588c2ecf20Sopenharmony_ci		if (els_op == ELS_LS_RJT) {
3598c2ecf20Sopenharmony_ci			shost_printk(KERN_INFO, lport->host,
3608c2ecf20Sopenharmony_ci				  "Flogi Request Rejected by Switch\n");
3618c2ecf20Sopenharmony_ci			return 1;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci		shost_printk(KERN_INFO, lport->host,
3648c2ecf20Sopenharmony_ci				"Flogi Request Accepted by Switch\n");
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void fnic_fcoe_send_vlan_req(struct fnic *fnic)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct fcoe_ctlr *fip = &fnic->ctlr;
3728c2ecf20Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
3738c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3748c2ecf20Sopenharmony_ci	char *eth_fr;
3758c2ecf20Sopenharmony_ci	struct fip_vlan *vlan;
3768c2ecf20Sopenharmony_ci	u64 vlan_tov;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	fnic_fcoe_reset_vlans(fnic);
3798c2ecf20Sopenharmony_ci	fnic->set_vlan(fnic, 0);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (printk_ratelimit())
3828c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
3838c2ecf20Sopenharmony_ci			  "Sending VLAN request...\n");
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(sizeof(struct fip_vlan));
3868c2ecf20Sopenharmony_ci	if (!skb)
3878c2ecf20Sopenharmony_ci		return;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	eth_fr = (char *)skb->data;
3908c2ecf20Sopenharmony_ci	vlan = (struct fip_vlan *)eth_fr;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	memset(vlan, 0, sizeof(*vlan));
3938c2ecf20Sopenharmony_ci	memcpy(vlan->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
3948c2ecf20Sopenharmony_ci	memcpy(vlan->eth.h_dest, fcoe_all_fcfs, ETH_ALEN);
3958c2ecf20Sopenharmony_ci	vlan->eth.h_proto = htons(ETH_P_FIP);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	vlan->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
3988c2ecf20Sopenharmony_ci	vlan->fip.fip_op = htons(FIP_OP_VLAN);
3998c2ecf20Sopenharmony_ci	vlan->fip.fip_subcode = FIP_SC_VL_REQ;
4008c2ecf20Sopenharmony_ci	vlan->fip.fip_dl_len = htons(sizeof(vlan->desc) / FIP_BPW);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	vlan->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC;
4038c2ecf20Sopenharmony_ci	vlan->desc.mac.fd_desc.fip_dlen = sizeof(vlan->desc.mac) / FIP_BPW;
4048c2ecf20Sopenharmony_ci	memcpy(&vlan->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
4078c2ecf20Sopenharmony_ci	vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW;
4088c2ecf20Sopenharmony_ci	put_unaligned_be64(fip->lp->wwnn, &vlan->desc.wwnn.fd_wwn);
4098c2ecf20Sopenharmony_ci	atomic64_inc(&fnic_stats->vlan_stats.vlan_disc_reqs);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*vlan));
4128c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
4138c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
4148c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
4158c2ecf20Sopenharmony_ci	fip->send(fip, skb);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* set a timer so that we can retry if there no response */
4188c2ecf20Sopenharmony_ci	vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV);
4198c2ecf20Sopenharmony_ci	mod_timer(&fnic->fip_timer, round_jiffies(vlan_tov));
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *skb)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct fcoe_ctlr *fip = &fnic->ctlr;
4258c2ecf20Sopenharmony_ci	struct fip_header *fiph;
4268c2ecf20Sopenharmony_ci	struct fip_desc *desc;
4278c2ecf20Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
4288c2ecf20Sopenharmony_ci	u16 vid;
4298c2ecf20Sopenharmony_ci	size_t rlen;
4308c2ecf20Sopenharmony_ci	size_t dlen;
4318c2ecf20Sopenharmony_ci	struct fcoe_vlan *vlan;
4328c2ecf20Sopenharmony_ci	u64 sol_time;
4338c2ecf20Sopenharmony_ci	unsigned long flags;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
4368c2ecf20Sopenharmony_ci		  "Received VLAN response...\n");
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	fiph = (struct fip_header *) skb->data;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
4418c2ecf20Sopenharmony_ci		  "Received VLAN response... OP 0x%x SUB_OP 0x%x\n",
4428c2ecf20Sopenharmony_ci		  ntohs(fiph->fip_op), fiph->fip_subcode);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
4458c2ecf20Sopenharmony_ci	fnic_fcoe_reset_vlans(fnic);
4468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
4478c2ecf20Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
4488c2ecf20Sopenharmony_ci	while (rlen > 0) {
4498c2ecf20Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
4508c2ecf20Sopenharmony_ci		switch (desc->fip_dtype) {
4518c2ecf20Sopenharmony_ci		case FIP_DT_VLAN:
4528c2ecf20Sopenharmony_ci			vid = ntohs(((struct fip_vlan_desc *)desc)->fd_vlan);
4538c2ecf20Sopenharmony_ci			shost_printk(KERN_INFO, fnic->lport->host,
4548c2ecf20Sopenharmony_ci				  "process_vlan_resp: FIP VLAN %d\n", vid);
4558c2ecf20Sopenharmony_ci			vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
4568c2ecf20Sopenharmony_ci			if (!vlan) {
4578c2ecf20Sopenharmony_ci				/* retry from timer */
4588c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&fnic->vlans_lock,
4598c2ecf20Sopenharmony_ci							flags);
4608c2ecf20Sopenharmony_ci				goto out;
4618c2ecf20Sopenharmony_ci			}
4628c2ecf20Sopenharmony_ci			vlan->vid = vid & 0x0fff;
4638c2ecf20Sopenharmony_ci			vlan->state = FIP_VLAN_AVAIL;
4648c2ecf20Sopenharmony_ci			list_add_tail(&vlan->list, &fnic->vlans);
4658c2ecf20Sopenharmony_ci			break;
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
4688c2ecf20Sopenharmony_ci		rlen -= dlen;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* any VLAN descriptors present ? */
4728c2ecf20Sopenharmony_ci	if (list_empty(&fnic->vlans)) {
4738c2ecf20Sopenharmony_ci		/* retry from timer */
4748c2ecf20Sopenharmony_ci		atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID);
4758c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
4768c2ecf20Sopenharmony_ci			  "No VLAN descriptors in FIP VLAN response\n");
4778c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
4788c2ecf20Sopenharmony_ci		goto out;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
4828c2ecf20Sopenharmony_ci	fnic->set_vlan(fnic, vlan->vid);
4838c2ecf20Sopenharmony_ci	vlan->state = FIP_VLAN_SENT; /* sent now */
4848c2ecf20Sopenharmony_ci	vlan->sol_count++;
4858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* start the solicitation */
4888c2ecf20Sopenharmony_ci	fcoe_ctlr_link_up(fip);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY);
4918c2ecf20Sopenharmony_ci	mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
4928c2ecf20Sopenharmony_ciout:
4938c2ecf20Sopenharmony_ci	return;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic void fnic_fcoe_start_fcf_disc(struct fnic *fnic)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	unsigned long flags;
4998c2ecf20Sopenharmony_ci	struct fcoe_vlan *vlan;
5008c2ecf20Sopenharmony_ci	u64 sol_time;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
5038c2ecf20Sopenharmony_ci	vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
5048c2ecf20Sopenharmony_ci	fnic->set_vlan(fnic, vlan->vid);
5058c2ecf20Sopenharmony_ci	vlan->state = FIP_VLAN_SENT; /* sent now */
5068c2ecf20Sopenharmony_ci	vlan->sol_count = 1;
5078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* start the solicitation */
5108c2ecf20Sopenharmony_ci	fcoe_ctlr_link_up(&fnic->ctlr);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY);
5138c2ecf20Sopenharmony_ci	mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	unsigned long flags;
5198c2ecf20Sopenharmony_ci	struct fcoe_vlan *fvlan;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
5228c2ecf20Sopenharmony_ci	if (list_empty(&fnic->vlans)) {
5238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
5248c2ecf20Sopenharmony_ci		return -EINVAL;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	fvlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
5288c2ecf20Sopenharmony_ci	if (fvlan->state == FIP_VLAN_USED) {
5298c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
5308c2ecf20Sopenharmony_ci		return 0;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (fvlan->state == FIP_VLAN_SENT) {
5348c2ecf20Sopenharmony_ci		fvlan->state = FIP_VLAN_USED;
5358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
5368c2ecf20Sopenharmony_ci		return 0;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
5398c2ecf20Sopenharmony_ci	return -EINVAL;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic void fnic_event_enq(struct fnic *fnic, enum fnic_evt ev)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct fnic_event *fevt;
5458c2ecf20Sopenharmony_ci	unsigned long flags;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	fevt = kmalloc(sizeof(*fevt), GFP_ATOMIC);
5488c2ecf20Sopenharmony_ci	if (!fevt)
5498c2ecf20Sopenharmony_ci		return;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	fevt->fnic = fnic;
5528c2ecf20Sopenharmony_ci	fevt->event = ev;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
5558c2ecf20Sopenharmony_ci	list_add_tail(&fevt->list, &fnic->evlist);
5568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	schedule_work(&fnic->event_work);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct fip_header *fiph;
5648c2ecf20Sopenharmony_ci	int ret = 1;
5658c2ecf20Sopenharmony_ci	u16 op;
5668c2ecf20Sopenharmony_ci	u8 sub;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!skb || !(skb->data))
5698c2ecf20Sopenharmony_ci		return -1;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (skb_linearize(skb))
5728c2ecf20Sopenharmony_ci		goto drop;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
5758c2ecf20Sopenharmony_ci	op = ntohs(fiph->fip_op);
5768c2ecf20Sopenharmony_ci	sub = fiph->fip_subcode;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER)
5798c2ecf20Sopenharmony_ci		goto drop;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
5828c2ecf20Sopenharmony_ci		goto drop;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (op == FIP_OP_DISC && sub == FIP_SC_ADV) {
5858c2ecf20Sopenharmony_ci		if (fnic_fcoe_vlan_check(fnic, ntohs(fiph->fip_flags)))
5868c2ecf20Sopenharmony_ci			goto drop;
5878c2ecf20Sopenharmony_ci		/* pass it on to fcoe */
5888c2ecf20Sopenharmony_ci		ret = 1;
5898c2ecf20Sopenharmony_ci	} else if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) {
5908c2ecf20Sopenharmony_ci		/* set the vlan as used */
5918c2ecf20Sopenharmony_ci		fnic_fcoe_process_vlan_resp(fnic, skb);
5928c2ecf20Sopenharmony_ci		ret = 0;
5938c2ecf20Sopenharmony_ci	} else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) {
5948c2ecf20Sopenharmony_ci		/* received CVL request, restart vlan disc */
5958c2ecf20Sopenharmony_ci		fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
5968c2ecf20Sopenharmony_ci		/* pass it on to fcoe */
5978c2ecf20Sopenharmony_ci		ret = 1;
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_cidrop:
6008c2ecf20Sopenharmony_ci	return ret;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_civoid fnic_handle_fip_frame(struct work_struct *work)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	struct fnic *fnic = container_of(work, struct fnic, fip_frame_work);
6068c2ecf20Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
6078c2ecf20Sopenharmony_ci	unsigned long flags;
6088c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6098c2ecf20Sopenharmony_ci	struct ethhdr *eh;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&fnic->fip_frame_queue))) {
6128c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fnic->fnic_lock, flags);
6138c2ecf20Sopenharmony_ci		if (fnic->stop_rx_link_events) {
6148c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
6158c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
6168c2ecf20Sopenharmony_ci			return;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci		/*
6198c2ecf20Sopenharmony_ci		 * If we're in a transitional state, just re-queue and return.
6208c2ecf20Sopenharmony_ci		 * The queue will be serviced when we get to a stable state.
6218c2ecf20Sopenharmony_ci		 */
6228c2ecf20Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_MODE &&
6238c2ecf20Sopenharmony_ci		    fnic->state != FNIC_IN_ETH_MODE) {
6248c2ecf20Sopenharmony_ci			skb_queue_head(&fnic->fip_frame_queue, skb);
6258c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
6268c2ecf20Sopenharmony_ci			return;
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
6298c2ecf20Sopenharmony_ci		eh = (struct ethhdr *)skb->data;
6308c2ecf20Sopenharmony_ci		if (eh->h_proto == htons(ETH_P_FIP)) {
6318c2ecf20Sopenharmony_ci			skb_pull(skb, sizeof(*eh));
6328c2ecf20Sopenharmony_ci			if (fnic_fcoe_handle_fip_frame(fnic, skb) <= 0) {
6338c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
6348c2ecf20Sopenharmony_ci				continue;
6358c2ecf20Sopenharmony_ci			}
6368c2ecf20Sopenharmony_ci			/*
6378c2ecf20Sopenharmony_ci			 * If there's FLOGI rejects - clear all
6388c2ecf20Sopenharmony_ci			 * fcf's & restart from scratch
6398c2ecf20Sopenharmony_ci			 */
6408c2ecf20Sopenharmony_ci			if (is_fnic_fip_flogi_reject(&fnic->ctlr, skb)) {
6418c2ecf20Sopenharmony_ci				atomic64_inc(
6428c2ecf20Sopenharmony_ci					&fnic_stats->vlan_stats.flogi_rejects);
6438c2ecf20Sopenharmony_ci				shost_printk(KERN_INFO, fnic->lport->host,
6448c2ecf20Sopenharmony_ci					  "Trigger a Link down - VLAN Disc\n");
6458c2ecf20Sopenharmony_ci				fcoe_ctlr_link_down(&fnic->ctlr);
6468c2ecf20Sopenharmony_ci				/* start FCoE VLAN discovery */
6478c2ecf20Sopenharmony_ci				fnic_fcoe_send_vlan_req(fnic);
6488c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
6498c2ecf20Sopenharmony_ci				continue;
6508c2ecf20Sopenharmony_ci			}
6518c2ecf20Sopenharmony_ci			fcoe_ctlr_recv(&fnic->ctlr, skb);
6528c2ecf20Sopenharmony_ci			continue;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci/**
6588c2ecf20Sopenharmony_ci * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
6598c2ecf20Sopenharmony_ci * @fnic:	fnic instance.
6608c2ecf20Sopenharmony_ci * @skb:	Ethernet Frame.
6618c2ecf20Sopenharmony_ci */
6628c2ecf20Sopenharmony_cistatic inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	struct fc_frame *fp;
6658c2ecf20Sopenharmony_ci	struct ethhdr *eh;
6668c2ecf20Sopenharmony_ci	struct fcoe_hdr *fcoe_hdr;
6678c2ecf20Sopenharmony_ci	struct fcoe_crc_eof *ft;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/*
6708c2ecf20Sopenharmony_ci	 * Undo VLAN encapsulation if present.
6718c2ecf20Sopenharmony_ci	 */
6728c2ecf20Sopenharmony_ci	eh = (struct ethhdr *)skb->data;
6738c2ecf20Sopenharmony_ci	if (eh->h_proto == htons(ETH_P_8021Q)) {
6748c2ecf20Sopenharmony_ci		memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
6758c2ecf20Sopenharmony_ci		eh = skb_pull(skb, VLAN_HLEN);
6768c2ecf20Sopenharmony_ci		skb_reset_mac_header(skb);
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci	if (eh->h_proto == htons(ETH_P_FIP)) {
6798c2ecf20Sopenharmony_ci		if (!(fnic->config.flags & VFCF_FIP_CAPABLE)) {
6808c2ecf20Sopenharmony_ci			printk(KERN_ERR "Dropped FIP frame, as firmware "
6818c2ecf20Sopenharmony_ci					"uses non-FIP mode, Enable FIP "
6828c2ecf20Sopenharmony_ci					"using UCSM\n");
6838c2ecf20Sopenharmony_ci			goto drop;
6848c2ecf20Sopenharmony_ci		}
6858c2ecf20Sopenharmony_ci		if ((fnic_fc_trace_set_data(fnic->lport->host->host_no,
6868c2ecf20Sopenharmony_ci			FNIC_FC_RECV|0x80, (char *)skb->data, skb->len)) != 0) {
6878c2ecf20Sopenharmony_ci			printk(KERN_ERR "fnic ctlr frame trace error!!!");
6888c2ecf20Sopenharmony_ci		}
6898c2ecf20Sopenharmony_ci		skb_queue_tail(&fnic->fip_frame_queue, skb);
6908c2ecf20Sopenharmony_ci		queue_work(fnic_fip_queue, &fnic->fip_frame_work);
6918c2ecf20Sopenharmony_ci		return 1;		/* let caller know packet was used */
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci	if (eh->h_proto != htons(ETH_P_FCOE))
6948c2ecf20Sopenharmony_ci		goto drop;
6958c2ecf20Sopenharmony_ci	skb_set_network_header(skb, sizeof(*eh));
6968c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(*eh));
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	fcoe_hdr = (struct fcoe_hdr *)skb->data;
6998c2ecf20Sopenharmony_ci	if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
7008c2ecf20Sopenharmony_ci		goto drop;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	fp = (struct fc_frame *)skb;
7038c2ecf20Sopenharmony_ci	fc_frame_init(fp);
7048c2ecf20Sopenharmony_ci	fr_sof(fp) = fcoe_hdr->fcoe_sof;
7058c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(struct fcoe_hdr));
7068c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft));
7098c2ecf20Sopenharmony_ci	fr_eof(fp) = ft->fcoe_eof;
7108c2ecf20Sopenharmony_ci	skb_trim(skb, skb->len - sizeof(*ft));
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_cidrop:
7138c2ecf20Sopenharmony_ci	dev_kfree_skb_irq(skb);
7148c2ecf20Sopenharmony_ci	return -1;
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci/**
7188c2ecf20Sopenharmony_ci * fnic_update_mac_locked() - set data MAC address and filters.
7198c2ecf20Sopenharmony_ci * @fnic:	fnic instance.
7208c2ecf20Sopenharmony_ci * @new:	newly-assigned FCoE MAC address.
7218c2ecf20Sopenharmony_ci *
7228c2ecf20Sopenharmony_ci * Called with the fnic lock held.
7238c2ecf20Sopenharmony_ci */
7248c2ecf20Sopenharmony_civoid fnic_update_mac_locked(struct fnic *fnic, u8 *new)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	u8 *ctl = fnic->ctlr.ctl_src_addr;
7278c2ecf20Sopenharmony_ci	u8 *data = fnic->data_src_addr;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(new))
7308c2ecf20Sopenharmony_ci		new = ctl;
7318c2ecf20Sopenharmony_ci	if (ether_addr_equal(data, new))
7328c2ecf20Sopenharmony_ci		return;
7338c2ecf20Sopenharmony_ci	FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
7348c2ecf20Sopenharmony_ci	if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl))
7358c2ecf20Sopenharmony_ci		vnic_dev_del_addr(fnic->vdev, data);
7368c2ecf20Sopenharmony_ci	memcpy(data, new, ETH_ALEN);
7378c2ecf20Sopenharmony_ci	if (!ether_addr_equal(new, ctl))
7388c2ecf20Sopenharmony_ci		vnic_dev_add_addr(fnic->vdev, new);
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci/**
7428c2ecf20Sopenharmony_ci * fnic_update_mac() - set data MAC address and filters.
7438c2ecf20Sopenharmony_ci * @lport:	local port.
7448c2ecf20Sopenharmony_ci * @new:	newly-assigned FCoE MAC address.
7458c2ecf20Sopenharmony_ci */
7468c2ecf20Sopenharmony_civoid fnic_update_mac(struct fc_lport *lport, u8 *new)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct fnic *fnic = lport_priv(lport);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	spin_lock_irq(&fnic->fnic_lock);
7518c2ecf20Sopenharmony_ci	fnic_update_mac_locked(fnic, new);
7528c2ecf20Sopenharmony_ci	spin_unlock_irq(&fnic->fnic_lock);
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci/**
7568c2ecf20Sopenharmony_ci * fnic_set_port_id() - set the port_ID after successful FLOGI.
7578c2ecf20Sopenharmony_ci * @lport:	local port.
7588c2ecf20Sopenharmony_ci * @port_id:	assigned FC_ID.
7598c2ecf20Sopenharmony_ci * @fp:		received frame containing the FLOGI accept or NULL.
7608c2ecf20Sopenharmony_ci *
7618c2ecf20Sopenharmony_ci * This is called from libfc when a new FC_ID has been assigned.
7628c2ecf20Sopenharmony_ci * This causes us to reset the firmware to FC_MODE and setup the new MAC
7638c2ecf20Sopenharmony_ci * address and FC_ID.
7648c2ecf20Sopenharmony_ci *
7658c2ecf20Sopenharmony_ci * It is also called with FC_ID 0 when we're logged off.
7668c2ecf20Sopenharmony_ci *
7678c2ecf20Sopenharmony_ci * If the FC_ID is due to point-to-point, fp may be NULL.
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_civoid fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	struct fnic *fnic = lport_priv(lport);
7728c2ecf20Sopenharmony_ci	u8 *mac;
7738c2ecf20Sopenharmony_ci	int ret;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
7768c2ecf20Sopenharmony_ci		     port_id, fp);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	/*
7798c2ecf20Sopenharmony_ci	 * If we're clearing the FC_ID, change to use the ctl_src_addr.
7808c2ecf20Sopenharmony_ci	 * Set ethernet mode to send FLOGI.
7818c2ecf20Sopenharmony_ci	 */
7828c2ecf20Sopenharmony_ci	if (!port_id) {
7838c2ecf20Sopenharmony_ci		fnic_update_mac(lport, fnic->ctlr.ctl_src_addr);
7848c2ecf20Sopenharmony_ci		fnic_set_eth_mode(fnic);
7858c2ecf20Sopenharmony_ci		return;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (fp) {
7898c2ecf20Sopenharmony_ci		mac = fr_cb(fp)->granted_mac;
7908c2ecf20Sopenharmony_ci		if (is_zero_ether_addr(mac)) {
7918c2ecf20Sopenharmony_ci			/* non-FIP - FLOGI already accepted - ignore return */
7928c2ecf20Sopenharmony_ci			fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp);
7938c2ecf20Sopenharmony_ci		}
7948c2ecf20Sopenharmony_ci		fnic_update_mac(lport, mac);
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	/* Change state to reflect transition to FC mode */
7988c2ecf20Sopenharmony_ci	spin_lock_irq(&fnic->fnic_lock);
7998c2ecf20Sopenharmony_ci	if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
8008c2ecf20Sopenharmony_ci		fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
8018c2ecf20Sopenharmony_ci	else {
8028c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
8038c2ecf20Sopenharmony_ci			     "Unexpected fnic state %s while"
8048c2ecf20Sopenharmony_ci			     " processing flogi resp\n",
8058c2ecf20Sopenharmony_ci			     fnic_state_to_str(fnic->state));
8068c2ecf20Sopenharmony_ci		spin_unlock_irq(&fnic->fnic_lock);
8078c2ecf20Sopenharmony_ci		return;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci	spin_unlock_irq(&fnic->fnic_lock);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	/*
8128c2ecf20Sopenharmony_ci	 * Send FLOGI registration to firmware to set up FC mode.
8138c2ecf20Sopenharmony_ci	 * The new address will be set up when registration completes.
8148c2ecf20Sopenharmony_ci	 */
8158c2ecf20Sopenharmony_ci	ret = fnic_flogi_reg_handler(fnic, port_id);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (ret < 0) {
8188c2ecf20Sopenharmony_ci		spin_lock_irq(&fnic->fnic_lock);
8198c2ecf20Sopenharmony_ci		if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
8208c2ecf20Sopenharmony_ci			fnic->state = FNIC_IN_ETH_MODE;
8218c2ecf20Sopenharmony_ci		spin_unlock_irq(&fnic->fnic_lock);
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
8268c2ecf20Sopenharmony_ci				    *cq_desc, struct vnic_rq_buf *buf,
8278c2ecf20Sopenharmony_ci				    int skipped __attribute__((unused)),
8288c2ecf20Sopenharmony_ci				    void *opaque)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(rq->vdev);
8318c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8328c2ecf20Sopenharmony_ci	struct fc_frame *fp;
8338c2ecf20Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
8348c2ecf20Sopenharmony_ci	u8 type, color, eop, sop, ingress_port, vlan_stripped;
8358c2ecf20Sopenharmony_ci	u8 fcoe = 0, fcoe_sof, fcoe_eof;
8368c2ecf20Sopenharmony_ci	u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0;
8378c2ecf20Sopenharmony_ci	u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok;
8388c2ecf20Sopenharmony_ci	u8 ipv6, ipv4, ipv4_fragment, rss_type, csum_not_calc;
8398c2ecf20Sopenharmony_ci	u8 fcs_ok = 1, packet_error = 0;
8408c2ecf20Sopenharmony_ci	u16 q_number, completed_index, bytes_written = 0, vlan, checksum;
8418c2ecf20Sopenharmony_ci	u32 rss_hash;
8428c2ecf20Sopenharmony_ci	u16 exchange_id, tmpl;
8438c2ecf20Sopenharmony_ci	u8 sof = 0;
8448c2ecf20Sopenharmony_ci	u8 eof = 0;
8458c2ecf20Sopenharmony_ci	u32 fcp_bytes_written = 0;
8468c2ecf20Sopenharmony_ci	unsigned long flags;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
8498c2ecf20Sopenharmony_ci			 DMA_FROM_DEVICE);
8508c2ecf20Sopenharmony_ci	skb = buf->os_buf;
8518c2ecf20Sopenharmony_ci	fp = (struct fc_frame *)skb;
8528c2ecf20Sopenharmony_ci	buf->os_buf = NULL;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
8558c2ecf20Sopenharmony_ci	if (type == CQ_DESC_TYPE_RQ_FCP) {
8568c2ecf20Sopenharmony_ci		cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *)cq_desc,
8578c2ecf20Sopenharmony_ci				   &type, &color, &q_number, &completed_index,
8588c2ecf20Sopenharmony_ci				   &eop, &sop, &fcoe_fc_crc_ok, &exchange_id,
8598c2ecf20Sopenharmony_ci				   &tmpl, &fcp_bytes_written, &sof, &eof,
8608c2ecf20Sopenharmony_ci				   &ingress_port, &packet_error,
8618c2ecf20Sopenharmony_ci				   &fcoe_enc_error, &fcs_ok, &vlan_stripped,
8628c2ecf20Sopenharmony_ci				   &vlan);
8638c2ecf20Sopenharmony_ci		skb_trim(skb, fcp_bytes_written);
8648c2ecf20Sopenharmony_ci		fr_sof(fp) = sof;
8658c2ecf20Sopenharmony_ci		fr_eof(fp) = eof;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	} else if (type == CQ_DESC_TYPE_RQ_ENET) {
8688c2ecf20Sopenharmony_ci		cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
8698c2ecf20Sopenharmony_ci				    &type, &color, &q_number, &completed_index,
8708c2ecf20Sopenharmony_ci				    &ingress_port, &fcoe, &eop, &sop,
8718c2ecf20Sopenharmony_ci				    &rss_type, &csum_not_calc, &rss_hash,
8728c2ecf20Sopenharmony_ci				    &bytes_written, &packet_error,
8738c2ecf20Sopenharmony_ci				    &vlan_stripped, &vlan, &checksum,
8748c2ecf20Sopenharmony_ci				    &fcoe_sof, &fcoe_fc_crc_ok,
8758c2ecf20Sopenharmony_ci				    &fcoe_enc_error, &fcoe_eof,
8768c2ecf20Sopenharmony_ci				    &tcp_udp_csum_ok, &udp, &tcp,
8778c2ecf20Sopenharmony_ci				    &ipv4_csum_ok, &ipv6, &ipv4,
8788c2ecf20Sopenharmony_ci				    &ipv4_fragment, &fcs_ok);
8798c2ecf20Sopenharmony_ci		skb_trim(skb, bytes_written);
8808c2ecf20Sopenharmony_ci		if (!fcs_ok) {
8818c2ecf20Sopenharmony_ci			atomic64_inc(&fnic_stats->misc_stats.frame_errors);
8828c2ecf20Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
8838c2ecf20Sopenharmony_ci				     "fcs error.  dropping packet.\n");
8848c2ecf20Sopenharmony_ci			goto drop;
8858c2ecf20Sopenharmony_ci		}
8868c2ecf20Sopenharmony_ci		if (fnic_import_rq_eth_pkt(fnic, skb))
8878c2ecf20Sopenharmony_ci			return;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	} else {
8908c2ecf20Sopenharmony_ci		/* wrong CQ type*/
8918c2ecf20Sopenharmony_ci		shost_printk(KERN_ERR, fnic->lport->host,
8928c2ecf20Sopenharmony_ci			     "fnic rq_cmpl wrong cq type x%x\n", type);
8938c2ecf20Sopenharmony_ci		goto drop;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
8978c2ecf20Sopenharmony_ci		atomic64_inc(&fnic_stats->misc_stats.frame_errors);
8988c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
8998c2ecf20Sopenharmony_ci			     "fnic rq_cmpl fcoe x%x fcsok x%x"
9008c2ecf20Sopenharmony_ci			     " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
9018c2ecf20Sopenharmony_ci			     " x%x\n",
9028c2ecf20Sopenharmony_ci			     fcoe, fcs_ok, packet_error,
9038c2ecf20Sopenharmony_ci			     fcoe_fc_crc_ok, fcoe_enc_error);
9048c2ecf20Sopenharmony_ci		goto drop;
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
9088c2ecf20Sopenharmony_ci	if (fnic->stop_rx_link_events) {
9098c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
9108c2ecf20Sopenharmony_ci		goto drop;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci	fr_dev(fp) = fnic->lport;
9138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
9148c2ecf20Sopenharmony_ci	if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_RECV,
9158c2ecf20Sopenharmony_ci					(char *)skb->data, skb->len)) != 0) {
9168c2ecf20Sopenharmony_ci		printk(KERN_ERR "fnic ctlr frame trace error!!!");
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	skb_queue_tail(&fnic->frame_queue, skb);
9208c2ecf20Sopenharmony_ci	queue_work(fnic_event_queue, &fnic->frame_work);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	return;
9238c2ecf20Sopenharmony_cidrop:
9248c2ecf20Sopenharmony_ci	dev_kfree_skb_irq(skb);
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_cistatic int fnic_rq_cmpl_handler_cont(struct vnic_dev *vdev,
9288c2ecf20Sopenharmony_ci				     struct cq_desc *cq_desc, u8 type,
9298c2ecf20Sopenharmony_ci				     u16 q_number, u16 completed_index,
9308c2ecf20Sopenharmony_ci				     void *opaque)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(vdev);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	vnic_rq_service(&fnic->rq[q_number], cq_desc, completed_index,
9358c2ecf20Sopenharmony_ci			VNIC_RQ_RETURN_DESC, fnic_rq_cmpl_frame_recv,
9368c2ecf20Sopenharmony_ci			NULL);
9378c2ecf20Sopenharmony_ci	return 0;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ciint fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	unsigned int tot_rq_work_done = 0, cur_work_done;
9438c2ecf20Sopenharmony_ci	unsigned int i;
9448c2ecf20Sopenharmony_ci	int err;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	for (i = 0; i < fnic->rq_count; i++) {
9478c2ecf20Sopenharmony_ci		cur_work_done = vnic_cq_service(&fnic->cq[i], rq_work_to_do,
9488c2ecf20Sopenharmony_ci						fnic_rq_cmpl_handler_cont,
9498c2ecf20Sopenharmony_ci						NULL);
9508c2ecf20Sopenharmony_ci		if (cur_work_done) {
9518c2ecf20Sopenharmony_ci			err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
9528c2ecf20Sopenharmony_ci			if (err)
9538c2ecf20Sopenharmony_ci				shost_printk(KERN_ERR, fnic->lport->host,
9548c2ecf20Sopenharmony_ci					     "fnic_alloc_rq_frame can't alloc"
9558c2ecf20Sopenharmony_ci					     " frame\n");
9568c2ecf20Sopenharmony_ci		}
9578c2ecf20Sopenharmony_ci		tot_rq_work_done += cur_work_done;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	return tot_rq_work_done;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci/*
9648c2ecf20Sopenharmony_ci * This function is called once at init time to allocate and fill RQ
9658c2ecf20Sopenharmony_ci * buffers. Subsequently, it is called in the interrupt context after RQ
9668c2ecf20Sopenharmony_ci * buffer processing to replenish the buffers in the RQ
9678c2ecf20Sopenharmony_ci */
9688c2ecf20Sopenharmony_ciint fnic_alloc_rq_frame(struct vnic_rq *rq)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(rq->vdev);
9718c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9728c2ecf20Sopenharmony_ci	u16 len;
9738c2ecf20Sopenharmony_ci	dma_addr_t pa;
9748c2ecf20Sopenharmony_ci	int r;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM;
9778c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(len);
9788c2ecf20Sopenharmony_ci	if (!skb) {
9798c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
9808c2ecf20Sopenharmony_ci			     "Unable to allocate RQ sk_buff\n");
9818c2ecf20Sopenharmony_ci		return -ENOMEM;
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
9848c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
9858c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
9868c2ecf20Sopenharmony_ci	skb_put(skb, len);
9878c2ecf20Sopenharmony_ci	pa = dma_map_single(&fnic->pdev->dev, skb->data, len, DMA_FROM_DEVICE);
9888c2ecf20Sopenharmony_ci	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
9898c2ecf20Sopenharmony_ci		r = -ENOMEM;
9908c2ecf20Sopenharmony_ci		printk(KERN_ERR "PCI mapping failed with error %d\n", r);
9918c2ecf20Sopenharmony_ci		goto free_skb;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	fnic_queue_rq_desc(rq, skb, pa, len);
9958c2ecf20Sopenharmony_ci	return 0;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cifree_skb:
9988c2ecf20Sopenharmony_ci	kfree_skb(skb);
9998c2ecf20Sopenharmony_ci	return r;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_civoid fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	struct fc_frame *fp = buf->os_buf;
10058c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(rq->vdev);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
10088c2ecf20Sopenharmony_ci			 DMA_FROM_DEVICE);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	dev_kfree_skb(fp_skb(fp));
10118c2ecf20Sopenharmony_ci	buf->os_buf = NULL;
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci/**
10158c2ecf20Sopenharmony_ci * fnic_eth_send() - Send Ethernet frame.
10168c2ecf20Sopenharmony_ci * @fip:	fcoe_ctlr instance.
10178c2ecf20Sopenharmony_ci * @skb:	Ethernet Frame, FIP, without VLAN encapsulation.
10188c2ecf20Sopenharmony_ci */
10198c2ecf20Sopenharmony_civoid fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	struct fnic *fnic = fnic_from_ctlr(fip);
10228c2ecf20Sopenharmony_ci	struct vnic_wq *wq = &fnic->wq[0];
10238c2ecf20Sopenharmony_ci	dma_addr_t pa;
10248c2ecf20Sopenharmony_ci	struct ethhdr *eth_hdr;
10258c2ecf20Sopenharmony_ci	struct vlan_ethhdr *vlan_hdr;
10268c2ecf20Sopenharmony_ci	unsigned long flags;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	if (!fnic->vlan_hw_insert) {
10298c2ecf20Sopenharmony_ci		eth_hdr = (struct ethhdr *)skb_mac_header(skb);
10308c2ecf20Sopenharmony_ci		vlan_hdr = skb_push(skb, sizeof(*vlan_hdr) - sizeof(*eth_hdr));
10318c2ecf20Sopenharmony_ci		memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
10328c2ecf20Sopenharmony_ci		vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
10338c2ecf20Sopenharmony_ci		vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
10348c2ecf20Sopenharmony_ci		vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
10358c2ecf20Sopenharmony_ci		if ((fnic_fc_trace_set_data(fnic->lport->host->host_no,
10368c2ecf20Sopenharmony_ci			FNIC_FC_SEND|0x80, (char *)eth_hdr, skb->len)) != 0) {
10378c2ecf20Sopenharmony_ci			printk(KERN_ERR "fnic ctlr frame trace error!!!");
10388c2ecf20Sopenharmony_ci		}
10398c2ecf20Sopenharmony_ci	} else {
10408c2ecf20Sopenharmony_ci		if ((fnic_fc_trace_set_data(fnic->lport->host->host_no,
10418c2ecf20Sopenharmony_ci			FNIC_FC_SEND|0x80, (char *)skb->data, skb->len)) != 0) {
10428c2ecf20Sopenharmony_ci			printk(KERN_ERR "fnic ctlr frame trace error!!!");
10438c2ecf20Sopenharmony_ci		}
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	pa = dma_map_single(&fnic->pdev->dev, skb->data, skb->len,
10478c2ecf20Sopenharmony_ci			DMA_TO_DEVICE);
10488c2ecf20Sopenharmony_ci	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
10498c2ecf20Sopenharmony_ci		printk(KERN_ERR "DMA mapping failed\n");
10508c2ecf20Sopenharmony_ci		goto free_skb;
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->wq_lock[0], flags);
10548c2ecf20Sopenharmony_ci	if (!vnic_wq_desc_avail(wq))
10558c2ecf20Sopenharmony_ci		goto irq_restore;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	fnic_queue_wq_eth_desc(wq, skb, pa, skb->len,
10588c2ecf20Sopenharmony_ci			       0 /* hw inserts cos value */,
10598c2ecf20Sopenharmony_ci			       fnic->vlan_id, 1);
10608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
10618c2ecf20Sopenharmony_ci	return;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ciirq_restore:
10648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
10658c2ecf20Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, pa, skb->len, DMA_TO_DEVICE);
10668c2ecf20Sopenharmony_cifree_skb:
10678c2ecf20Sopenharmony_ci	kfree_skb(skb);
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci/*
10718c2ecf20Sopenharmony_ci * Send FC frame.
10728c2ecf20Sopenharmony_ci */
10738c2ecf20Sopenharmony_cistatic int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	struct vnic_wq *wq = &fnic->wq[0];
10768c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10778c2ecf20Sopenharmony_ci	dma_addr_t pa;
10788c2ecf20Sopenharmony_ci	struct ethhdr *eth_hdr;
10798c2ecf20Sopenharmony_ci	struct vlan_ethhdr *vlan_hdr;
10808c2ecf20Sopenharmony_ci	struct fcoe_hdr *fcoe_hdr;
10818c2ecf20Sopenharmony_ci	struct fc_frame_header *fh;
10828c2ecf20Sopenharmony_ci	u32 tot_len, eth_hdr_len;
10838c2ecf20Sopenharmony_ci	int ret = 0;
10848c2ecf20Sopenharmony_ci	unsigned long flags;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	fh = fc_frame_header_get(fp);
10878c2ecf20Sopenharmony_ci	skb = fp_skb(fp);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
10908c2ecf20Sopenharmony_ci	    fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb))
10918c2ecf20Sopenharmony_ci		return 0;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (!fnic->vlan_hw_insert) {
10948c2ecf20Sopenharmony_ci		eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
10958c2ecf20Sopenharmony_ci		vlan_hdr = skb_push(skb, eth_hdr_len);
10968c2ecf20Sopenharmony_ci		eth_hdr = (struct ethhdr *)vlan_hdr;
10978c2ecf20Sopenharmony_ci		vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
10988c2ecf20Sopenharmony_ci		vlan_hdr->h_vlan_encapsulated_proto = htons(ETH_P_FCOE);
10998c2ecf20Sopenharmony_ci		vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
11008c2ecf20Sopenharmony_ci		fcoe_hdr = (struct fcoe_hdr *)(vlan_hdr + 1);
11018c2ecf20Sopenharmony_ci	} else {
11028c2ecf20Sopenharmony_ci		eth_hdr_len = sizeof(*eth_hdr) + sizeof(*fcoe_hdr);
11038c2ecf20Sopenharmony_ci		eth_hdr = skb_push(skb, eth_hdr_len);
11048c2ecf20Sopenharmony_ci		eth_hdr->h_proto = htons(ETH_P_FCOE);
11058c2ecf20Sopenharmony_ci		fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	if (fnic->ctlr.map_dest)
11098c2ecf20Sopenharmony_ci		fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
11108c2ecf20Sopenharmony_ci	else
11118c2ecf20Sopenharmony_ci		memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN);
11128c2ecf20Sopenharmony_ci	memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	tot_len = skb->len;
11158c2ecf20Sopenharmony_ci	BUG_ON(tot_len % 4);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	memset(fcoe_hdr, 0, sizeof(*fcoe_hdr));
11188c2ecf20Sopenharmony_ci	fcoe_hdr->fcoe_sof = fr_sof(fp);
11198c2ecf20Sopenharmony_ci	if (FC_FCOE_VER)
11208c2ecf20Sopenharmony_ci		FC_FCOE_ENCAPS_VER(fcoe_hdr, FC_FCOE_VER);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	pa = dma_map_single(&fnic->pdev->dev, eth_hdr, tot_len, DMA_TO_DEVICE);
11238c2ecf20Sopenharmony_ci	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
11248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11258c2ecf20Sopenharmony_ci		printk(KERN_ERR "DMA map failed with error %d\n", ret);
11268c2ecf20Sopenharmony_ci		goto free_skb_on_err;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_SEND,
11308c2ecf20Sopenharmony_ci				(char *)eth_hdr, tot_len)) != 0) {
11318c2ecf20Sopenharmony_ci		printk(KERN_ERR "fnic ctlr frame trace error!!!");
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->wq_lock[0], flags);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	if (!vnic_wq_desc_avail(wq)) {
11378c2ecf20Sopenharmony_ci		dma_unmap_single(&fnic->pdev->dev, pa, tot_len, DMA_TO_DEVICE);
11388c2ecf20Sopenharmony_ci		ret = -1;
11398c2ecf20Sopenharmony_ci		goto irq_restore;
11408c2ecf20Sopenharmony_ci	}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp),
11438c2ecf20Sopenharmony_ci			   0 /* hw inserts cos value */,
11448c2ecf20Sopenharmony_ci			   fnic->vlan_id, 1, 1, 1);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ciirq_restore:
11478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cifree_skb_on_err:
11508c2ecf20Sopenharmony_ci	if (ret)
11518c2ecf20Sopenharmony_ci		dev_kfree_skb_any(fp_skb(fp));
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	return ret;
11548c2ecf20Sopenharmony_ci}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci/*
11578c2ecf20Sopenharmony_ci * fnic_send
11588c2ecf20Sopenharmony_ci * Routine to send a raw frame
11598c2ecf20Sopenharmony_ci */
11608c2ecf20Sopenharmony_ciint fnic_send(struct fc_lport *lp, struct fc_frame *fp)
11618c2ecf20Sopenharmony_ci{
11628c2ecf20Sopenharmony_ci	struct fnic *fnic = lport_priv(lp);
11638c2ecf20Sopenharmony_ci	unsigned long flags;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	if (fnic->in_remove) {
11668c2ecf20Sopenharmony_ci		dev_kfree_skb(fp_skb(fp));
11678c2ecf20Sopenharmony_ci		return -1;
11688c2ecf20Sopenharmony_ci	}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/*
11718c2ecf20Sopenharmony_ci	 * Queue frame if in a transitional state.
11728c2ecf20Sopenharmony_ci	 * This occurs while registering the Port_ID / MAC address after FLOGI.
11738c2ecf20Sopenharmony_ci	 */
11748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
11758c2ecf20Sopenharmony_ci	if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) {
11768c2ecf20Sopenharmony_ci		skb_queue_tail(&fnic->tx_queue, fp_skb(fp));
11778c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
11788c2ecf20Sopenharmony_ci		return 0;
11798c2ecf20Sopenharmony_ci	}
11808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	return fnic_send_frame(fnic, fp);
11838c2ecf20Sopenharmony_ci}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci/**
11868c2ecf20Sopenharmony_ci * fnic_flush_tx() - send queued frames.
11878c2ecf20Sopenharmony_ci * @fnic: fnic device
11888c2ecf20Sopenharmony_ci *
11898c2ecf20Sopenharmony_ci * Send frames that were waiting to go out in FC or Ethernet mode.
11908c2ecf20Sopenharmony_ci * Whenever changing modes we purge queued frames, so these frames should
11918c2ecf20Sopenharmony_ci * be queued for the stable mode that we're in, either FC or Ethernet.
11928c2ecf20Sopenharmony_ci *
11938c2ecf20Sopenharmony_ci * Called without fnic_lock held.
11948c2ecf20Sopenharmony_ci */
11958c2ecf20Sopenharmony_civoid fnic_flush_tx(struct fnic *fnic)
11968c2ecf20Sopenharmony_ci{
11978c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11988c2ecf20Sopenharmony_ci	struct fc_frame *fp;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&fnic->tx_queue))) {
12018c2ecf20Sopenharmony_ci		fp = (struct fc_frame *)skb;
12028c2ecf20Sopenharmony_ci		fnic_send_frame(fnic, fp);
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci/**
12078c2ecf20Sopenharmony_ci * fnic_set_eth_mode() - put fnic into ethernet mode.
12088c2ecf20Sopenharmony_ci * @fnic: fnic device
12098c2ecf20Sopenharmony_ci *
12108c2ecf20Sopenharmony_ci * Called without fnic lock held.
12118c2ecf20Sopenharmony_ci */
12128c2ecf20Sopenharmony_cistatic void fnic_set_eth_mode(struct fnic *fnic)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	unsigned long flags;
12158c2ecf20Sopenharmony_ci	enum fnic_state old_state;
12168c2ecf20Sopenharmony_ci	int ret;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
12198c2ecf20Sopenharmony_ciagain:
12208c2ecf20Sopenharmony_ci	old_state = fnic->state;
12218c2ecf20Sopenharmony_ci	switch (old_state) {
12228c2ecf20Sopenharmony_ci	case FNIC_IN_FC_MODE:
12238c2ecf20Sopenharmony_ci	case FNIC_IN_ETH_TRANS_FC_MODE:
12248c2ecf20Sopenharmony_ci	default:
12258c2ecf20Sopenharmony_ci		fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
12268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci		ret = fnic_fw_reset_handler(fnic);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fnic->fnic_lock, flags);
12318c2ecf20Sopenharmony_ci		if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
12328c2ecf20Sopenharmony_ci			goto again;
12338c2ecf20Sopenharmony_ci		if (ret)
12348c2ecf20Sopenharmony_ci			fnic->state = old_state;
12358c2ecf20Sopenharmony_ci		break;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	case FNIC_IN_FC_TRANS_ETH_MODE:
12388c2ecf20Sopenharmony_ci	case FNIC_IN_ETH_MODE:
12398c2ecf20Sopenharmony_ci		break;
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic void fnic_wq_complete_frame_send(struct vnic_wq *wq,
12458c2ecf20Sopenharmony_ci					struct cq_desc *cq_desc,
12468c2ecf20Sopenharmony_ci					struct vnic_wq_buf *buf, void *opaque)
12478c2ecf20Sopenharmony_ci{
12488c2ecf20Sopenharmony_ci	struct sk_buff *skb = buf->os_buf;
12498c2ecf20Sopenharmony_ci	struct fc_frame *fp = (struct fc_frame *)skb;
12508c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(wq->vdev);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
12538c2ecf20Sopenharmony_ci			 DMA_TO_DEVICE);
12548c2ecf20Sopenharmony_ci	dev_kfree_skb_irq(fp_skb(fp));
12558c2ecf20Sopenharmony_ci	buf->os_buf = NULL;
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cistatic int fnic_wq_cmpl_handler_cont(struct vnic_dev *vdev,
12598c2ecf20Sopenharmony_ci				     struct cq_desc *cq_desc, u8 type,
12608c2ecf20Sopenharmony_ci				     u16 q_number, u16 completed_index,
12618c2ecf20Sopenharmony_ci				     void *opaque)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(vdev);
12648c2ecf20Sopenharmony_ci	unsigned long flags;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->wq_lock[q_number], flags);
12678c2ecf20Sopenharmony_ci	vnic_wq_service(&fnic->wq[q_number], cq_desc, completed_index,
12688c2ecf20Sopenharmony_ci			fnic_wq_complete_frame_send, NULL);
12698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->wq_lock[q_number], flags);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	return 0;
12728c2ecf20Sopenharmony_ci}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ciint fnic_wq_cmpl_handler(struct fnic *fnic, int work_to_do)
12758c2ecf20Sopenharmony_ci{
12768c2ecf20Sopenharmony_ci	unsigned int wq_work_done = 0;
12778c2ecf20Sopenharmony_ci	unsigned int i;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	for (i = 0; i < fnic->raw_wq_count; i++) {
12808c2ecf20Sopenharmony_ci		wq_work_done  += vnic_cq_service(&fnic->cq[fnic->rq_count+i],
12818c2ecf20Sopenharmony_ci						 work_to_do,
12828c2ecf20Sopenharmony_ci						 fnic_wq_cmpl_handler_cont,
12838c2ecf20Sopenharmony_ci						 NULL);
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	return wq_work_done;
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_civoid fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct fc_frame *fp = buf->os_buf;
12938c2ecf20Sopenharmony_ci	struct fnic *fnic = vnic_dev_priv(wq->vdev);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
12968c2ecf20Sopenharmony_ci			 DMA_TO_DEVICE);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	dev_kfree_skb(fp_skb(fp));
12998c2ecf20Sopenharmony_ci	buf->os_buf = NULL;
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_civoid fnic_fcoe_reset_vlans(struct fnic *fnic)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	unsigned long flags;
13058c2ecf20Sopenharmony_ci	struct fcoe_vlan *vlan;
13068c2ecf20Sopenharmony_ci	struct fcoe_vlan *next;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	/*
13098c2ecf20Sopenharmony_ci	 * indicate a link down to fcoe so that all fcf's are free'd
13108c2ecf20Sopenharmony_ci	 * might not be required since we did this before sending vlan
13118c2ecf20Sopenharmony_ci	 * discovery request
13128c2ecf20Sopenharmony_ci	 */
13138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
13148c2ecf20Sopenharmony_ci	if (!list_empty(&fnic->vlans)) {
13158c2ecf20Sopenharmony_ci		list_for_each_entry_safe(vlan, next, &fnic->vlans, list) {
13168c2ecf20Sopenharmony_ci			list_del(&vlan->list);
13178c2ecf20Sopenharmony_ci			kfree(vlan);
13188c2ecf20Sopenharmony_ci		}
13198c2ecf20Sopenharmony_ci	}
13208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
13218c2ecf20Sopenharmony_ci}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_civoid fnic_handle_fip_timer(struct fnic *fnic)
13248c2ecf20Sopenharmony_ci{
13258c2ecf20Sopenharmony_ci	unsigned long flags;
13268c2ecf20Sopenharmony_ci	struct fcoe_vlan *vlan;
13278c2ecf20Sopenharmony_ci	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
13288c2ecf20Sopenharmony_ci	u64 sol_time;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->fnic_lock, flags);
13318c2ecf20Sopenharmony_ci	if (fnic->stop_rx_link_events) {
13328c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
13338c2ecf20Sopenharmony_ci		return;
13348c2ecf20Sopenharmony_ci	}
13358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	if (fnic->ctlr.mode == FIP_MODE_NON_FIP)
13388c2ecf20Sopenharmony_ci		return;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fnic->vlans_lock, flags);
13418c2ecf20Sopenharmony_ci	if (list_empty(&fnic->vlans)) {
13428c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
13438c2ecf20Sopenharmony_ci		/* no vlans available, try again */
13448c2ecf20Sopenharmony_ci		if (printk_ratelimit())
13458c2ecf20Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
13468c2ecf20Sopenharmony_ci				  "Start VLAN Discovery\n");
13478c2ecf20Sopenharmony_ci		fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
13488c2ecf20Sopenharmony_ci		return;
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
13528c2ecf20Sopenharmony_ci	shost_printk(KERN_DEBUG, fnic->lport->host,
13538c2ecf20Sopenharmony_ci		  "fip_timer: vlan %d state %d sol_count %d\n",
13548c2ecf20Sopenharmony_ci		  vlan->vid, vlan->state, vlan->sol_count);
13558c2ecf20Sopenharmony_ci	switch (vlan->state) {
13568c2ecf20Sopenharmony_ci	case FIP_VLAN_USED:
13578c2ecf20Sopenharmony_ci		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
13588c2ecf20Sopenharmony_ci			  "FIP VLAN is selected for FC transaction\n");
13598c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
13608c2ecf20Sopenharmony_ci		break;
13618c2ecf20Sopenharmony_ci	case FIP_VLAN_FAILED:
13628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
13638c2ecf20Sopenharmony_ci		/* if all vlans are in failed state, restart vlan disc */
13648c2ecf20Sopenharmony_ci		if (printk_ratelimit())
13658c2ecf20Sopenharmony_ci			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
13668c2ecf20Sopenharmony_ci				  "Start VLAN Discovery\n");
13678c2ecf20Sopenharmony_ci		fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
13688c2ecf20Sopenharmony_ci		break;
13698c2ecf20Sopenharmony_ci	case FIP_VLAN_SENT:
13708c2ecf20Sopenharmony_ci		if (vlan->sol_count >= FCOE_CTLR_MAX_SOL) {
13718c2ecf20Sopenharmony_ci			/*
13728c2ecf20Sopenharmony_ci			 * no response on this vlan, remove  from the list.
13738c2ecf20Sopenharmony_ci			 * Try the next vlan
13748c2ecf20Sopenharmony_ci			 */
13758c2ecf20Sopenharmony_ci			shost_printk(KERN_INFO, fnic->lport->host,
13768c2ecf20Sopenharmony_ci				  "Dequeue this VLAN ID %d from list\n",
13778c2ecf20Sopenharmony_ci				  vlan->vid);
13788c2ecf20Sopenharmony_ci			list_del(&vlan->list);
13798c2ecf20Sopenharmony_ci			kfree(vlan);
13808c2ecf20Sopenharmony_ci			vlan = NULL;
13818c2ecf20Sopenharmony_ci			if (list_empty(&fnic->vlans)) {
13828c2ecf20Sopenharmony_ci				/* we exhausted all vlans, restart vlan disc */
13838c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&fnic->vlans_lock,
13848c2ecf20Sopenharmony_ci							flags);
13858c2ecf20Sopenharmony_ci				shost_printk(KERN_INFO, fnic->lport->host,
13868c2ecf20Sopenharmony_ci					  "fip_timer: vlan list empty, "
13878c2ecf20Sopenharmony_ci					  "trigger vlan disc\n");
13888c2ecf20Sopenharmony_ci				fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
13898c2ecf20Sopenharmony_ci				return;
13908c2ecf20Sopenharmony_ci			}
13918c2ecf20Sopenharmony_ci			/* check the next vlan */
13928c2ecf20Sopenharmony_ci			vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan,
13938c2ecf20Sopenharmony_ci							list);
13948c2ecf20Sopenharmony_ci			fnic->set_vlan(fnic, vlan->vid);
13958c2ecf20Sopenharmony_ci			vlan->state = FIP_VLAN_SENT; /* sent now */
13968c2ecf20Sopenharmony_ci		}
13978c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fnic->vlans_lock, flags);
13988c2ecf20Sopenharmony_ci		atomic64_inc(&fnic_stats->vlan_stats.sol_expiry_count);
13998c2ecf20Sopenharmony_ci		vlan->sol_count++;
14008c2ecf20Sopenharmony_ci		sol_time = jiffies + msecs_to_jiffies
14018c2ecf20Sopenharmony_ci					(FCOE_CTLR_START_DELAY);
14028c2ecf20Sopenharmony_ci		mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
14038c2ecf20Sopenharmony_ci		break;
14048c2ecf20Sopenharmony_ci	}
14058c2ecf20Sopenharmony_ci}
1406