162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2008-2009 Cisco Systems, Inc.  All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2009 Intel Corporation.  All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Maintained at www.Open-FCoE.org
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/timer.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <linux/etherdevice.h>
1762306a36Sopenharmony_ci#include <linux/ethtool.h>
1862306a36Sopenharmony_ci#include <linux/if_ether.h>
1962306a36Sopenharmony_ci#include <linux/if_vlan.h>
2062306a36Sopenharmony_ci#include <linux/errno.h>
2162306a36Sopenharmony_ci#include <linux/bitops.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <net/rtnetlink.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <scsi/fc/fc_els.h>
2662306a36Sopenharmony_ci#include <scsi/fc/fc_fs.h>
2762306a36Sopenharmony_ci#include <scsi/fc/fc_fip.h>
2862306a36Sopenharmony_ci#include <scsi/fc/fc_encaps.h>
2962306a36Sopenharmony_ci#include <scsi/fc/fc_fcoe.h>
3062306a36Sopenharmony_ci#include <scsi/fc/fc_fcp.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <scsi/libfc.h>
3362306a36Sopenharmony_ci#include <scsi/libfcoe.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "libfcoe.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define	FCOE_CTLR_MIN_FKA	500		/* min keep alive (mS) */
3862306a36Sopenharmony_ci#define	FCOE_CTLR_DEF_FKA	FIP_DEF_FKA	/* default keep alive (mS) */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void fcoe_ctlr_timeout(struct timer_list *);
4162306a36Sopenharmony_cistatic void fcoe_ctlr_timer_work(struct work_struct *);
4262306a36Sopenharmony_cistatic void fcoe_ctlr_recv_work(struct work_struct *);
4362306a36Sopenharmony_cistatic int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void fcoe_ctlr_vn_start(struct fcoe_ctlr *);
4662306a36Sopenharmony_cistatic int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *);
4762306a36Sopenharmony_cistatic void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *);
4862306a36Sopenharmony_cistatic int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int fcoe_ctlr_vlan_recv(struct fcoe_ctlr *, struct sk_buff *);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
5362306a36Sopenharmony_cistatic u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
5462306a36Sopenharmony_cistatic u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
5562306a36Sopenharmony_cistatic u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const char * const fcoe_ctlr_states[] = {
5862306a36Sopenharmony_ci	[FIP_ST_DISABLED] =	"DISABLED",
5962306a36Sopenharmony_ci	[FIP_ST_LINK_WAIT] =	"LINK_WAIT",
6062306a36Sopenharmony_ci	[FIP_ST_AUTO] =		"AUTO",
6162306a36Sopenharmony_ci	[FIP_ST_NON_FIP] =	"NON_FIP",
6262306a36Sopenharmony_ci	[FIP_ST_ENABLED] =	"ENABLED",
6362306a36Sopenharmony_ci	[FIP_ST_VNMP_START] =	"VNMP_START",
6462306a36Sopenharmony_ci	[FIP_ST_VNMP_PROBE1] =	"VNMP_PROBE1",
6562306a36Sopenharmony_ci	[FIP_ST_VNMP_PROBE2] =	"VNMP_PROBE2",
6662306a36Sopenharmony_ci	[FIP_ST_VNMP_CLAIM] =	"VNMP_CLAIM",
6762306a36Sopenharmony_ci	[FIP_ST_VNMP_UP] =	"VNMP_UP",
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic const char *fcoe_ctlr_state(enum fip_state state)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	const char *cp = "unknown";
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (state < ARRAY_SIZE(fcoe_ctlr_states))
7562306a36Sopenharmony_ci		cp = fcoe_ctlr_states[state];
7662306a36Sopenharmony_ci	if (!cp)
7762306a36Sopenharmony_ci		cp = "unknown";
7862306a36Sopenharmony_ci	return cp;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * fcoe_ctlr_set_state() - Set and do debug printing for the new FIP state.
8362306a36Sopenharmony_ci * @fip: The FCoE controller
8462306a36Sopenharmony_ci * @state: The new state
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic void fcoe_ctlr_set_state(struct fcoe_ctlr *fip, enum fip_state state)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	if (state == fip->state)
8962306a36Sopenharmony_ci		return;
9062306a36Sopenharmony_ci	if (fip->lp)
9162306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "state %s -> %s\n",
9262306a36Sopenharmony_ci			fcoe_ctlr_state(fip->state), fcoe_ctlr_state(state));
9362306a36Sopenharmony_ci	fip->state = state;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
9862306a36Sopenharmony_ci * @fcf: The FCF to check
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * Return non-zero if FCF fcoe_size has been validated.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	return (fcf->flags & FIP_FL_SOL) != 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/**
10862306a36Sopenharmony_ci * fcoe_ctlr_fcf_usable() - Check if a FCF is usable
10962306a36Sopenharmony_ci * @fcf: The FCF to check
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Return non-zero if the FCF is usable.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cistatic inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	u16 flags = FIP_FL_SOL | FIP_FL_AVAIL;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return (fcf->flags & flags) == flags;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/**
12162306a36Sopenharmony_ci * fcoe_ctlr_map_dest() - Set flag and OUI for mapping destination addresses
12262306a36Sopenharmony_ci * @fip: The FCoE controller
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	if (fip->mode == FIP_MODE_VN2VN)
12762306a36Sopenharmony_ci		hton24(fip->dest_addr, FIP_VN_FC_MAP);
12862306a36Sopenharmony_ci	else
12962306a36Sopenharmony_ci		hton24(fip->dest_addr, FIP_DEF_FC_MAP);
13062306a36Sopenharmony_ci	hton24(fip->dest_addr + 3, 0);
13162306a36Sopenharmony_ci	fip->map_dest = 1;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * fcoe_ctlr_init() - Initialize the FCoE Controller instance
13662306a36Sopenharmony_ci * @fip: The FCoE controller to initialize
13762306a36Sopenharmony_ci * @mode: FIP mode to set
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_civoid fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_mode mode)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
14262306a36Sopenharmony_ci	fip->mode = mode;
14362306a36Sopenharmony_ci	fip->fip_resp = false;
14462306a36Sopenharmony_ci	INIT_LIST_HEAD(&fip->fcfs);
14562306a36Sopenharmony_ci	mutex_init(&fip->ctlr_mutex);
14662306a36Sopenharmony_ci	spin_lock_init(&fip->ctlr_lock);
14762306a36Sopenharmony_ci	fip->flogi_oxid = FC_XID_UNKNOWN;
14862306a36Sopenharmony_ci	timer_setup(&fip->timer, fcoe_ctlr_timeout, 0);
14962306a36Sopenharmony_ci	INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_work);
15062306a36Sopenharmony_ci	INIT_WORK(&fip->recv_work, fcoe_ctlr_recv_work);
15162306a36Sopenharmony_ci	skb_queue_head_init(&fip->fip_recv_list);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_init);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * fcoe_sysfs_fcf_add() - Add a fcoe_fcf{,_device} to a fcoe_ctlr{,_device}
15762306a36Sopenharmony_ci * @new: The newly discovered FCF
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * Called with fip->ctlr_mutex held
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic int fcoe_sysfs_fcf_add(struct fcoe_fcf *new)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct fcoe_ctlr *fip = new->fip;
16462306a36Sopenharmony_ci	struct fcoe_ctlr_device *ctlr_dev;
16562306a36Sopenharmony_ci	struct fcoe_fcf_device *temp, *fcf_dev;
16662306a36Sopenharmony_ci	int rc = -ENOMEM;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
16962306a36Sopenharmony_ci			new->fabric_name, new->fcf_mac);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	temp = kzalloc(sizeof(*temp), GFP_KERNEL);
17262306a36Sopenharmony_ci	if (!temp)
17362306a36Sopenharmony_ci		goto out;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	temp->fabric_name = new->fabric_name;
17662306a36Sopenharmony_ci	temp->switch_name = new->switch_name;
17762306a36Sopenharmony_ci	temp->fc_map = new->fc_map;
17862306a36Sopenharmony_ci	temp->vfid = new->vfid;
17962306a36Sopenharmony_ci	memcpy(temp->mac, new->fcf_mac, ETH_ALEN);
18062306a36Sopenharmony_ci	temp->priority = new->pri;
18162306a36Sopenharmony_ci	temp->fka_period = new->fka_period;
18262306a36Sopenharmony_ci	temp->selected = 0; /* default to unselected */
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * If ctlr_dev doesn't exist then it means we're a libfcoe user
18662306a36Sopenharmony_ci	 * who doesn't use fcoe_syfs and didn't allocate a fcoe_ctlr_device.
18762306a36Sopenharmony_ci	 * fnic would be an example of a driver with this behavior. In this
18862306a36Sopenharmony_ci	 * case we want to add the fcoe_fcf to the fcoe_ctlr list, but we
18962306a36Sopenharmony_ci	 * don't want to make sysfs changes.
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
19362306a36Sopenharmony_ci	if (ctlr_dev) {
19462306a36Sopenharmony_ci		mutex_lock(&ctlr_dev->lock);
19562306a36Sopenharmony_ci		fcf_dev = fcoe_fcf_device_add(ctlr_dev, temp);
19662306a36Sopenharmony_ci		if (unlikely(!fcf_dev)) {
19762306a36Sopenharmony_ci			rc = -ENOMEM;
19862306a36Sopenharmony_ci			mutex_unlock(&ctlr_dev->lock);
19962306a36Sopenharmony_ci			goto out;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		/*
20362306a36Sopenharmony_ci		 * The fcoe_sysfs layer can return a CONNECTED fcf that
20462306a36Sopenharmony_ci		 * has a priv (fcf was never deleted) or a CONNECTED fcf
20562306a36Sopenharmony_ci		 * that doesn't have a priv (fcf was deleted). However,
20662306a36Sopenharmony_ci		 * libfcoe will always delete FCFs before trying to add
20762306a36Sopenharmony_ci		 * them. This is ensured because both recv_adv and
20862306a36Sopenharmony_ci		 * age_fcfs are protected by the the fcoe_ctlr's mutex.
20962306a36Sopenharmony_ci		 * This means that we should never get a FCF with a
21062306a36Sopenharmony_ci		 * non-NULL priv pointer.
21162306a36Sopenharmony_ci		 */
21262306a36Sopenharmony_ci		BUG_ON(fcf_dev->priv);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		fcf_dev->priv = new;
21562306a36Sopenharmony_ci		new->fcf_dev = fcf_dev;
21662306a36Sopenharmony_ci		mutex_unlock(&ctlr_dev->lock);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	list_add(&new->list, &fip->fcfs);
22062306a36Sopenharmony_ci	fip->fcf_count++;
22162306a36Sopenharmony_ci	rc = 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ciout:
22462306a36Sopenharmony_ci	kfree(temp);
22562306a36Sopenharmony_ci	return rc;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/**
22962306a36Sopenharmony_ci * fcoe_sysfs_fcf_del() - Remove a fcoe_fcf{,_device} to a fcoe_ctlr{,_device}
23062306a36Sopenharmony_ci * @new: The FCF to be removed
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * Called with fip->ctlr_mutex held
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_cistatic void fcoe_sysfs_fcf_del(struct fcoe_fcf *new)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct fcoe_ctlr *fip = new->fip;
23762306a36Sopenharmony_ci	struct fcoe_ctlr_device *cdev;
23862306a36Sopenharmony_ci	struct fcoe_fcf_device *fcf_dev;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	list_del(&new->list);
24162306a36Sopenharmony_ci	fip->fcf_count--;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/*
24462306a36Sopenharmony_ci	 * If ctlr_dev doesn't exist then it means we're a libfcoe user
24562306a36Sopenharmony_ci	 * who doesn't use fcoe_syfs and didn't allocate a fcoe_ctlr_device
24662306a36Sopenharmony_ci	 * or a fcoe_fcf_device.
24762306a36Sopenharmony_ci	 *
24862306a36Sopenharmony_ci	 * fnic would be an example of a driver with this behavior. In this
24962306a36Sopenharmony_ci	 * case we want to remove the fcoe_fcf from the fcoe_ctlr list (above),
25062306a36Sopenharmony_ci	 * but we don't want to make sysfs changes.
25162306a36Sopenharmony_ci	 */
25262306a36Sopenharmony_ci	cdev = fcoe_ctlr_to_ctlr_dev(fip);
25362306a36Sopenharmony_ci	if (cdev) {
25462306a36Sopenharmony_ci		mutex_lock(&cdev->lock);
25562306a36Sopenharmony_ci		fcf_dev = fcoe_fcf_to_fcf_dev(new);
25662306a36Sopenharmony_ci		WARN_ON(!fcf_dev);
25762306a36Sopenharmony_ci		new->fcf_dev = NULL;
25862306a36Sopenharmony_ci		fcoe_fcf_device_delete(fcf_dev);
25962306a36Sopenharmony_ci		mutex_unlock(&cdev->lock);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci	kfree(new);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/**
26562306a36Sopenharmony_ci * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
26662306a36Sopenharmony_ci * @fip: The FCoE controller whose FCFs are to be reset
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci * Called with &fcoe_ctlr lock held.
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_cistatic void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
27362306a36Sopenharmony_ci	struct fcoe_fcf *next;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	fip->sel_fcf = NULL;
27662306a36Sopenharmony_ci	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
27762306a36Sopenharmony_ci		fcoe_sysfs_fcf_del(fcf);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	WARN_ON(fip->fcf_count);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	fip->sel_time = 0;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci/**
28562306a36Sopenharmony_ci * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller
28662306a36Sopenharmony_ci * @fip: The FCoE controller to tear down
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * This is called by FCoE drivers before freeing the &fcoe_ctlr.
28962306a36Sopenharmony_ci *
29062306a36Sopenharmony_ci * The receive handler will have been deleted before this to guarantee
29162306a36Sopenharmony_ci * that no more recv_work will be scheduled.
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci * The timer routine will simply return once we set FIP_ST_DISABLED.
29462306a36Sopenharmony_ci * This guarantees that no further timeouts or work will be scheduled.
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_civoid fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	cancel_work_sync(&fip->recv_work);
29962306a36Sopenharmony_ci	skb_queue_purge(&fip->fip_recv_list);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
30262306a36Sopenharmony_ci	fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
30362306a36Sopenharmony_ci	fcoe_ctlr_reset_fcfs(fip);
30462306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
30562306a36Sopenharmony_ci	del_timer_sync(&fip->timer);
30662306a36Sopenharmony_ci	cancel_work_sync(&fip->timer_work);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_destroy);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/**
31162306a36Sopenharmony_ci * fcoe_ctlr_announce() - announce new FCF selection
31262306a36Sopenharmony_ci * @fip: The FCoE controller
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * Also sets the destination MAC for FCoE and control packets
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci * Called with neither ctlr_mutex nor ctlr_lock held.
31762306a36Sopenharmony_ci */
31862306a36Sopenharmony_cistatic void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct fcoe_fcf *sel;
32162306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
32462306a36Sopenharmony_ci	spin_lock_bh(&fip->ctlr_lock);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	kfree_skb(fip->flogi_req);
32762306a36Sopenharmony_ci	fip->flogi_req = NULL;
32862306a36Sopenharmony_ci	list_for_each_entry(fcf, &fip->fcfs, list)
32962306a36Sopenharmony_ci		fcf->flogi_sent = 0;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	spin_unlock_bh(&fip->ctlr_lock);
33262306a36Sopenharmony_ci	sel = fip->sel_fcf;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (sel && ether_addr_equal(sel->fcf_mac, fip->dest_addr))
33562306a36Sopenharmony_ci		goto unlock;
33662306a36Sopenharmony_ci	if (!is_zero_ether_addr(fip->dest_addr)) {
33762306a36Sopenharmony_ci		printk(KERN_NOTICE "libfcoe: host%d: "
33862306a36Sopenharmony_ci		       "FIP Fibre-Channel Forwarder MAC %pM deselected\n",
33962306a36Sopenharmony_ci		       fip->lp->host->host_no, fip->dest_addr);
34062306a36Sopenharmony_ci		eth_zero_addr(fip->dest_addr);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	if (sel) {
34362306a36Sopenharmony_ci		printk(KERN_INFO "libfcoe: host%d: FIP selected "
34462306a36Sopenharmony_ci		       "Fibre-Channel Forwarder MAC %pM\n",
34562306a36Sopenharmony_ci		       fip->lp->host->host_no, sel->fcf_mac);
34662306a36Sopenharmony_ci		memcpy(fip->dest_addr, sel->fcoe_mac, ETH_ALEN);
34762306a36Sopenharmony_ci		fip->map_dest = 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ciunlock:
35062306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/**
35462306a36Sopenharmony_ci * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port
35562306a36Sopenharmony_ci * @fip: The FCoE controller to get the maximum FCoE size from
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * Returns the maximum packet size including the FCoE header and trailer,
35862306a36Sopenharmony_ci * but not including any Ethernet or VLAN headers.
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_cistatic inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	/*
36362306a36Sopenharmony_ci	 * Determine the max FCoE frame size allowed, including
36462306a36Sopenharmony_ci	 * FCoE header and trailer.
36562306a36Sopenharmony_ci	 * Note:  lp->mfs is currently the payload size, not the frame size.
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	return fip->lp->mfs + sizeof(struct fc_frame_header) +
36862306a36Sopenharmony_ci		sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/**
37262306a36Sopenharmony_ci * fcoe_ctlr_solicit() - Send a FIP solicitation
37362306a36Sopenharmony_ci * @fip: The FCoE controller to send the solicitation on
37462306a36Sopenharmony_ci * @fcf: The destination FCF (if NULL, a multicast solicitation is sent)
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistatic void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct sk_buff *skb;
37962306a36Sopenharmony_ci	struct fip_sol {
38062306a36Sopenharmony_ci		struct ethhdr eth;
38162306a36Sopenharmony_ci		struct fip_header fip;
38262306a36Sopenharmony_ci		struct {
38362306a36Sopenharmony_ci			struct fip_mac_desc mac;
38462306a36Sopenharmony_ci			struct fip_wwn_desc wwnn;
38562306a36Sopenharmony_ci			struct fip_size_desc size;
38662306a36Sopenharmony_ci		} __packed desc;
38762306a36Sopenharmony_ci	}  __packed * sol;
38862306a36Sopenharmony_ci	u32 fcoe_size;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	skb = dev_alloc_skb(sizeof(*sol));
39162306a36Sopenharmony_ci	if (!skb)
39262306a36Sopenharmony_ci		return;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	sol = (struct fip_sol *)skb->data;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	memset(sol, 0, sizeof(*sol));
39762306a36Sopenharmony_ci	memcpy(sol->eth.h_dest, fcf ? fcf->fcf_mac : fcoe_all_fcfs, ETH_ALEN);
39862306a36Sopenharmony_ci	memcpy(sol->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
39962306a36Sopenharmony_ci	sol->eth.h_proto = htons(ETH_P_FIP);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	sol->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
40262306a36Sopenharmony_ci	sol->fip.fip_op = htons(FIP_OP_DISC);
40362306a36Sopenharmony_ci	sol->fip.fip_subcode = FIP_SC_SOL;
40462306a36Sopenharmony_ci	sol->fip.fip_dl_len = htons(sizeof(sol->desc) / FIP_BPW);
40562306a36Sopenharmony_ci	sol->fip.fip_flags = htons(FIP_FL_FPMA);
40662306a36Sopenharmony_ci	if (fip->spma)
40762306a36Sopenharmony_ci		sol->fip.fip_flags |= htons(FIP_FL_SPMA);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	sol->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC;
41062306a36Sopenharmony_ci	sol->desc.mac.fd_desc.fip_dlen = sizeof(sol->desc.mac) / FIP_BPW;
41162306a36Sopenharmony_ci	memcpy(sol->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	sol->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
41462306a36Sopenharmony_ci	sol->desc.wwnn.fd_desc.fip_dlen = sizeof(sol->desc.wwnn) / FIP_BPW;
41562306a36Sopenharmony_ci	put_unaligned_be64(fip->lp->wwnn, &sol->desc.wwnn.fd_wwn);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	fcoe_size = fcoe_ctlr_fcoe_size(fip);
41862306a36Sopenharmony_ci	sol->desc.size.fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
41962306a36Sopenharmony_ci	sol->desc.size.fd_desc.fip_dlen = sizeof(sol->desc.size) / FIP_BPW;
42062306a36Sopenharmony_ci	sol->desc.size.fd_size = htons(fcoe_size);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	skb_put(skb, sizeof(*sol));
42362306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
42462306a36Sopenharmony_ci	skb->priority = fip->priority;
42562306a36Sopenharmony_ci	skb_reset_mac_header(skb);
42662306a36Sopenharmony_ci	skb_reset_network_header(skb);
42762306a36Sopenharmony_ci	fip->send(fip, skb);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!fcf)
43062306a36Sopenharmony_ci		fip->sol_time = jiffies;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/**
43462306a36Sopenharmony_ci * fcoe_ctlr_link_up() - Start FCoE controller
43562306a36Sopenharmony_ci * @fip: The FCoE controller to start
43662306a36Sopenharmony_ci *
43762306a36Sopenharmony_ci * Called from the LLD when the network link is ready.
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_civoid fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
44262306a36Sopenharmony_ci	if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) {
44362306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
44462306a36Sopenharmony_ci		fc_linkup(fip->lp);
44562306a36Sopenharmony_ci	} else if (fip->state == FIP_ST_LINK_WAIT) {
44662306a36Sopenharmony_ci		if (fip->mode == FIP_MODE_NON_FIP)
44762306a36Sopenharmony_ci			fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
44862306a36Sopenharmony_ci		else
44962306a36Sopenharmony_ci			fcoe_ctlr_set_state(fip, FIP_ST_AUTO);
45062306a36Sopenharmony_ci		switch (fip->mode) {
45162306a36Sopenharmony_ci		default:
45262306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode);
45362306a36Sopenharmony_ci			fallthrough;
45462306a36Sopenharmony_ci		case FIP_MODE_AUTO:
45562306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
45662306a36Sopenharmony_ci			fallthrough;
45762306a36Sopenharmony_ci		case FIP_MODE_FABRIC:
45862306a36Sopenharmony_ci		case FIP_MODE_NON_FIP:
45962306a36Sopenharmony_ci			mutex_unlock(&fip->ctlr_mutex);
46062306a36Sopenharmony_ci			fc_linkup(fip->lp);
46162306a36Sopenharmony_ci			fcoe_ctlr_solicit(fip, NULL);
46262306a36Sopenharmony_ci			break;
46362306a36Sopenharmony_ci		case FIP_MODE_VN2VN:
46462306a36Sopenharmony_ci			fcoe_ctlr_vn_start(fip);
46562306a36Sopenharmony_ci			mutex_unlock(&fip->ctlr_mutex);
46662306a36Sopenharmony_ci			fc_linkup(fip->lp);
46762306a36Sopenharmony_ci			break;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci	} else
47062306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_link_up);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/**
47562306a36Sopenharmony_ci * fcoe_ctlr_reset() - Reset a FCoE controller
47662306a36Sopenharmony_ci * @fip:       The FCoE controller to reset
47762306a36Sopenharmony_ci */
47862306a36Sopenharmony_cistatic void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	fcoe_ctlr_reset_fcfs(fip);
48162306a36Sopenharmony_ci	del_timer(&fip->timer);
48262306a36Sopenharmony_ci	fip->ctlr_ka_time = 0;
48362306a36Sopenharmony_ci	fip->port_ka_time = 0;
48462306a36Sopenharmony_ci	fip->sol_time = 0;
48562306a36Sopenharmony_ci	fip->flogi_oxid = FC_XID_UNKNOWN;
48662306a36Sopenharmony_ci	fcoe_ctlr_map_dest(fip);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/**
49062306a36Sopenharmony_ci * fcoe_ctlr_link_down() - Stop a FCoE controller
49162306a36Sopenharmony_ci * @fip: The FCoE controller to be stopped
49262306a36Sopenharmony_ci *
49362306a36Sopenharmony_ci * Returns non-zero if the link was up and now isn't.
49462306a36Sopenharmony_ci *
49562306a36Sopenharmony_ci * Called from the LLD when the network link is not ready.
49662306a36Sopenharmony_ci * There may be multiple calls while the link is down.
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_ciint fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	int link_dropped;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "link down.\n");
50362306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
50462306a36Sopenharmony_ci	fcoe_ctlr_reset(fip);
50562306a36Sopenharmony_ci	link_dropped = fip->state != FIP_ST_LINK_WAIT;
50662306a36Sopenharmony_ci	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
50762306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (link_dropped)
51062306a36Sopenharmony_ci		fc_linkdown(fip->lp);
51162306a36Sopenharmony_ci	return link_dropped;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_link_down);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci/**
51662306a36Sopenharmony_ci * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF
51762306a36Sopenharmony_ci * @fip:   The FCoE controller to send the FKA on
51862306a36Sopenharmony_ci * @lport: libfc fc_lport to send from
51962306a36Sopenharmony_ci * @ports: 0 for controller keep-alive, 1 for port keep-alive
52062306a36Sopenharmony_ci * @sa:	   The source MAC address
52162306a36Sopenharmony_ci *
52262306a36Sopenharmony_ci * A controller keep-alive is sent every fka_period (typically 8 seconds).
52362306a36Sopenharmony_ci * The source MAC is the native MAC address.
52462306a36Sopenharmony_ci *
52562306a36Sopenharmony_ci * A port keep-alive is sent every 90 seconds while logged in.
52662306a36Sopenharmony_ci * The source MAC is the assigned mapped source address.
52762306a36Sopenharmony_ci * The destination is the FCF's F-port.
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cistatic void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
53062306a36Sopenharmony_ci				      struct fc_lport *lport,
53162306a36Sopenharmony_ci				      int ports, u8 *sa)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct sk_buff *skb;
53462306a36Sopenharmony_ci	struct fip_kal {
53562306a36Sopenharmony_ci		struct ethhdr eth;
53662306a36Sopenharmony_ci		struct fip_header fip;
53762306a36Sopenharmony_ci		struct fip_mac_desc mac;
53862306a36Sopenharmony_ci	} __packed * kal;
53962306a36Sopenharmony_ci	struct fip_vn_desc *vn;
54062306a36Sopenharmony_ci	u32 len;
54162306a36Sopenharmony_ci	struct fc_lport *lp;
54262306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	fcf = fip->sel_fcf;
54562306a36Sopenharmony_ci	lp = fip->lp;
54662306a36Sopenharmony_ci	if (!fcf || (ports && !lp->port_id))
54762306a36Sopenharmony_ci		return;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	len = sizeof(*kal) + ports * sizeof(*vn);
55062306a36Sopenharmony_ci	skb = dev_alloc_skb(len);
55162306a36Sopenharmony_ci	if (!skb)
55262306a36Sopenharmony_ci		return;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	kal = (struct fip_kal *)skb->data;
55562306a36Sopenharmony_ci	memset(kal, 0, len);
55662306a36Sopenharmony_ci	memcpy(kal->eth.h_dest, fcf->fcf_mac, ETH_ALEN);
55762306a36Sopenharmony_ci	memcpy(kal->eth.h_source, sa, ETH_ALEN);
55862306a36Sopenharmony_ci	kal->eth.h_proto = htons(ETH_P_FIP);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	kal->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
56162306a36Sopenharmony_ci	kal->fip.fip_op = htons(FIP_OP_CTRL);
56262306a36Sopenharmony_ci	kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE;
56362306a36Sopenharmony_ci	kal->fip.fip_dl_len = htons((sizeof(kal->mac) +
56462306a36Sopenharmony_ci				     ports * sizeof(*vn)) / FIP_BPW);
56562306a36Sopenharmony_ci	kal->fip.fip_flags = htons(FIP_FL_FPMA);
56662306a36Sopenharmony_ci	if (fip->spma)
56762306a36Sopenharmony_ci		kal->fip.fip_flags |= htons(FIP_FL_SPMA);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	kal->mac.fd_desc.fip_dtype = FIP_DT_MAC;
57062306a36Sopenharmony_ci	kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW;
57162306a36Sopenharmony_ci	memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
57262306a36Sopenharmony_ci	if (ports) {
57362306a36Sopenharmony_ci		vn = (struct fip_vn_desc *)(kal + 1);
57462306a36Sopenharmony_ci		vn->fd_desc.fip_dtype = FIP_DT_VN_ID;
57562306a36Sopenharmony_ci		vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW;
57662306a36Sopenharmony_ci		memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
57762306a36Sopenharmony_ci		hton24(vn->fd_fc_id, lport->port_id);
57862306a36Sopenharmony_ci		put_unaligned_be64(lport->wwpn, &vn->fd_wwpn);
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci	skb_put(skb, len);
58162306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
58262306a36Sopenharmony_ci	skb->priority = fip->priority;
58362306a36Sopenharmony_ci	skb_reset_mac_header(skb);
58462306a36Sopenharmony_ci	skb_reset_network_header(skb);
58562306a36Sopenharmony_ci	fip->send(fip, skb);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/**
58962306a36Sopenharmony_ci * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it
59062306a36Sopenharmony_ci * @fip:   The FCoE controller for the ELS frame
59162306a36Sopenharmony_ci * @lport: The local port
59262306a36Sopenharmony_ci * @dtype: The FIP descriptor type for the frame
59362306a36Sopenharmony_ci * @skb:   The FCoE ELS frame including FC header but no FCoE headers
59462306a36Sopenharmony_ci * @d_id:  The destination port ID.
59562306a36Sopenharmony_ci *
59662306a36Sopenharmony_ci * Returns non-zero error code on failure.
59762306a36Sopenharmony_ci *
59862306a36Sopenharmony_ci * The caller must check that the length is a multiple of 4.
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * The @skb must have enough headroom (28 bytes) and tailroom (8 bytes).
60162306a36Sopenharmony_ci * Headroom includes the FIP encapsulation description, FIP header, and
60262306a36Sopenharmony_ci * Ethernet header.  The tailroom is for the FIP MAC descriptor.
60362306a36Sopenharmony_ci */
60462306a36Sopenharmony_cistatic int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
60562306a36Sopenharmony_ci			    u8 dtype, struct sk_buff *skb, u32 d_id)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct fip_encaps_head {
60862306a36Sopenharmony_ci		struct ethhdr eth;
60962306a36Sopenharmony_ci		struct fip_header fip;
61062306a36Sopenharmony_ci		struct fip_encaps encaps;
61162306a36Sopenharmony_ci	} __packed * cap;
61262306a36Sopenharmony_ci	struct fc_frame_header *fh;
61362306a36Sopenharmony_ci	struct fip_mac_desc *mac;
61462306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
61562306a36Sopenharmony_ci	size_t dlen;
61662306a36Sopenharmony_ci	u16 fip_flags;
61762306a36Sopenharmony_ci	u8 op;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	fh = (struct fc_frame_header *)skb->data;
62062306a36Sopenharmony_ci	op = *(u8 *)(fh + 1);
62162306a36Sopenharmony_ci	dlen = sizeof(struct fip_encaps) + skb->len;	/* len before push */
62262306a36Sopenharmony_ci	cap = skb_push(skb, sizeof(*cap));
62362306a36Sopenharmony_ci	memset(cap, 0, sizeof(*cap));
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (lport->point_to_multipoint) {
62662306a36Sopenharmony_ci		if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest))
62762306a36Sopenharmony_ci			return -ENODEV;
62862306a36Sopenharmony_ci		fip_flags = 0;
62962306a36Sopenharmony_ci	} else {
63062306a36Sopenharmony_ci		fcf = fip->sel_fcf;
63162306a36Sopenharmony_ci		if (!fcf)
63262306a36Sopenharmony_ci			return -ENODEV;
63362306a36Sopenharmony_ci		fip_flags = fcf->flags;
63462306a36Sopenharmony_ci		fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA :
63562306a36Sopenharmony_ci					 FIP_FL_FPMA;
63662306a36Sopenharmony_ci		if (!fip_flags)
63762306a36Sopenharmony_ci			return -ENODEV;
63862306a36Sopenharmony_ci		memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN);
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci	memcpy(cap->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
64162306a36Sopenharmony_ci	cap->eth.h_proto = htons(ETH_P_FIP);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	cap->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
64462306a36Sopenharmony_ci	cap->fip.fip_op = htons(FIP_OP_LS);
64562306a36Sopenharmony_ci	if (op == ELS_LS_ACC || op == ELS_LS_RJT)
64662306a36Sopenharmony_ci		cap->fip.fip_subcode = FIP_SC_REP;
64762306a36Sopenharmony_ci	else
64862306a36Sopenharmony_ci		cap->fip.fip_subcode = FIP_SC_REQ;
64962306a36Sopenharmony_ci	cap->fip.fip_flags = htons(fip_flags);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	cap->encaps.fd_desc.fip_dtype = dtype;
65262306a36Sopenharmony_ci	cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (op != ELS_LS_RJT) {
65562306a36Sopenharmony_ci		dlen += sizeof(*mac);
65662306a36Sopenharmony_ci		mac = skb_put_zero(skb, sizeof(*mac));
65762306a36Sopenharmony_ci		mac->fd_desc.fip_dtype = FIP_DT_MAC;
65862306a36Sopenharmony_ci		mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
65962306a36Sopenharmony_ci		if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) {
66062306a36Sopenharmony_ci			memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
66162306a36Sopenharmony_ci		} else if (fip->mode == FIP_MODE_VN2VN) {
66262306a36Sopenharmony_ci			hton24(mac->fd_mac, FIP_VN_FC_MAP);
66362306a36Sopenharmony_ci			hton24(mac->fd_mac + 3, fip->port_id);
66462306a36Sopenharmony_ci		} else if (fip_flags & FIP_FL_SPMA) {
66562306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n");
66662306a36Sopenharmony_ci			memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
66762306a36Sopenharmony_ci		} else {
66862306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n");
66962306a36Sopenharmony_ci			/* FPMA only FLOGI.  Must leave the MAC desc zeroed. */
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci	cap->fip.fip_dl_len = htons(dlen / FIP_BPW);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
67562306a36Sopenharmony_ci	skb->priority = fip->priority;
67662306a36Sopenharmony_ci	skb_reset_mac_header(skb);
67762306a36Sopenharmony_ci	skb_reset_network_header(skb);
67862306a36Sopenharmony_ci	return 0;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/**
68262306a36Sopenharmony_ci * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate.
68362306a36Sopenharmony_ci * @fip:	FCoE controller.
68462306a36Sopenharmony_ci * @lport:	libfc fc_lport to send from
68562306a36Sopenharmony_ci * @skb:	FCoE ELS frame including FC header but no FCoE headers.
68662306a36Sopenharmony_ci *
68762306a36Sopenharmony_ci * Returns a non-zero error code if the frame should not be sent.
68862306a36Sopenharmony_ci * Returns zero if the caller should send the frame with FCoE encapsulation.
68962306a36Sopenharmony_ci *
69062306a36Sopenharmony_ci * The caller must check that the length is a multiple of 4.
69162306a36Sopenharmony_ci * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
69262306a36Sopenharmony_ci * The the skb must also be an fc_frame.
69362306a36Sopenharmony_ci *
69462306a36Sopenharmony_ci * This is called from the lower-level driver with spinlocks held,
69562306a36Sopenharmony_ci * so we must not take a mutex here.
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_ciint fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
69862306a36Sopenharmony_ci		       struct sk_buff *skb)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct fc_frame *fp;
70162306a36Sopenharmony_ci	struct fc_frame_header *fh;
70262306a36Sopenharmony_ci	u16 old_xid;
70362306a36Sopenharmony_ci	u8 op;
70462306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	fp = container_of(skb, struct fc_frame, skb);
70762306a36Sopenharmony_ci	fh = (struct fc_frame_header *)skb->data;
70862306a36Sopenharmony_ci	op = *(u8 *)(fh + 1);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (op == ELS_FLOGI && fip->mode != FIP_MODE_VN2VN) {
71162306a36Sopenharmony_ci		old_xid = fip->flogi_oxid;
71262306a36Sopenharmony_ci		fip->flogi_oxid = ntohs(fh->fh_ox_id);
71362306a36Sopenharmony_ci		if (fip->state == FIP_ST_AUTO) {
71462306a36Sopenharmony_ci			if (old_xid == FC_XID_UNKNOWN)
71562306a36Sopenharmony_ci				fip->flogi_count = 0;
71662306a36Sopenharmony_ci			fip->flogi_count++;
71762306a36Sopenharmony_ci			if (fip->flogi_count < 3)
71862306a36Sopenharmony_ci				goto drop;
71962306a36Sopenharmony_ci			fcoe_ctlr_map_dest(fip);
72062306a36Sopenharmony_ci			return 0;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci		if (fip->state == FIP_ST_NON_FIP)
72362306a36Sopenharmony_ci			fcoe_ctlr_map_dest(fip);
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (fip->state == FIP_ST_NON_FIP)
72762306a36Sopenharmony_ci		return 0;
72862306a36Sopenharmony_ci	if (!fip->sel_fcf && fip->mode != FIP_MODE_VN2VN)
72962306a36Sopenharmony_ci		goto drop;
73062306a36Sopenharmony_ci	switch (op) {
73162306a36Sopenharmony_ci	case ELS_FLOGI:
73262306a36Sopenharmony_ci		op = FIP_DT_FLOGI;
73362306a36Sopenharmony_ci		if (fip->mode == FIP_MODE_VN2VN)
73462306a36Sopenharmony_ci			break;
73562306a36Sopenharmony_ci		spin_lock_bh(&fip->ctlr_lock);
73662306a36Sopenharmony_ci		kfree_skb(fip->flogi_req);
73762306a36Sopenharmony_ci		fip->flogi_req = skb;
73862306a36Sopenharmony_ci		fip->flogi_req_send = 1;
73962306a36Sopenharmony_ci		spin_unlock_bh(&fip->ctlr_lock);
74062306a36Sopenharmony_ci		schedule_work(&fip->timer_work);
74162306a36Sopenharmony_ci		return -EINPROGRESS;
74262306a36Sopenharmony_ci	case ELS_FDISC:
74362306a36Sopenharmony_ci		if (ntoh24(fh->fh_s_id))
74462306a36Sopenharmony_ci			return 0;
74562306a36Sopenharmony_ci		op = FIP_DT_FDISC;
74662306a36Sopenharmony_ci		break;
74762306a36Sopenharmony_ci	case ELS_LOGO:
74862306a36Sopenharmony_ci		if (fip->mode == FIP_MODE_VN2VN) {
74962306a36Sopenharmony_ci			if (fip->state != FIP_ST_VNMP_UP)
75062306a36Sopenharmony_ci				goto drop;
75162306a36Sopenharmony_ci			if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI)
75262306a36Sopenharmony_ci				goto drop;
75362306a36Sopenharmony_ci		} else {
75462306a36Sopenharmony_ci			if (fip->state != FIP_ST_ENABLED)
75562306a36Sopenharmony_ci				return 0;
75662306a36Sopenharmony_ci			if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
75762306a36Sopenharmony_ci				return 0;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci		op = FIP_DT_LOGO;
76062306a36Sopenharmony_ci		break;
76162306a36Sopenharmony_ci	case ELS_LS_ACC:
76262306a36Sopenharmony_ci		/*
76362306a36Sopenharmony_ci		 * If non-FIP, we may have gotten an SID by accepting an FLOGI
76462306a36Sopenharmony_ci		 * from a point-to-point connection.  Switch to using
76562306a36Sopenharmony_ci		 * the source mac based on the SID.  The destination
76662306a36Sopenharmony_ci		 * MAC in this case would have been set by receiving the
76762306a36Sopenharmony_ci		 * FLOGI.
76862306a36Sopenharmony_ci		 */
76962306a36Sopenharmony_ci		if (fip->state == FIP_ST_NON_FIP) {
77062306a36Sopenharmony_ci			if (fip->flogi_oxid == FC_XID_UNKNOWN)
77162306a36Sopenharmony_ci				return 0;
77262306a36Sopenharmony_ci			fip->flogi_oxid = FC_XID_UNKNOWN;
77362306a36Sopenharmony_ci			fc_fcoe_set_mac(mac, fh->fh_d_id);
77462306a36Sopenharmony_ci			fip->update_mac(lport, mac);
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci		fallthrough;
77762306a36Sopenharmony_ci	case ELS_LS_RJT:
77862306a36Sopenharmony_ci		op = fr_encaps(fp);
77962306a36Sopenharmony_ci		if (op)
78062306a36Sopenharmony_ci			break;
78162306a36Sopenharmony_ci		return 0;
78262306a36Sopenharmony_ci	default:
78362306a36Sopenharmony_ci		if (fip->state != FIP_ST_ENABLED &&
78462306a36Sopenharmony_ci		    fip->state != FIP_ST_VNMP_UP)
78562306a36Sopenharmony_ci			goto drop;
78662306a36Sopenharmony_ci		return 0;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "els_send op %u d_id %x\n",
78962306a36Sopenharmony_ci			op, ntoh24(fh->fh_d_id));
79062306a36Sopenharmony_ci	if (fcoe_ctlr_encaps(fip, lport, op, skb, ntoh24(fh->fh_d_id)))
79162306a36Sopenharmony_ci		goto drop;
79262306a36Sopenharmony_ci	fip->send(fip, skb);
79362306a36Sopenharmony_ci	return -EINPROGRESS;
79462306a36Sopenharmony_cidrop:
79562306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "drop els_send op %u d_id %x\n",
79662306a36Sopenharmony_ci			op, ntoh24(fh->fh_d_id));
79762306a36Sopenharmony_ci	kfree_skb(skb);
79862306a36Sopenharmony_ci	return -EINVAL;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_els_send);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/**
80362306a36Sopenharmony_ci * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller
80462306a36Sopenharmony_ci * @fip: The FCoE controller to free FCFs on
80562306a36Sopenharmony_ci *
80662306a36Sopenharmony_ci * Called with lock held and preemption disabled.
80762306a36Sopenharmony_ci *
80862306a36Sopenharmony_ci * An FCF is considered old if we have missed two advertisements.
80962306a36Sopenharmony_ci * That is, there have been no valid advertisement from it for 2.5
81062306a36Sopenharmony_ci * times its keep-alive period.
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * In addition, determine the time when an FCF selection can occur.
81362306a36Sopenharmony_ci *
81462306a36Sopenharmony_ci * Also, increment the MissDiscAdvCount when no advertisement is received
81562306a36Sopenharmony_ci * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
81662306a36Sopenharmony_ci *
81762306a36Sopenharmony_ci * Returns the time in jiffies for the next call.
81862306a36Sopenharmony_ci */
81962306a36Sopenharmony_cistatic unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
82262306a36Sopenharmony_ci	struct fcoe_fcf *next;
82362306a36Sopenharmony_ci	unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
82462306a36Sopenharmony_ci	unsigned long deadline;
82562306a36Sopenharmony_ci	unsigned long sel_time = 0;
82662306a36Sopenharmony_ci	struct list_head del_list;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	INIT_LIST_HEAD(&del_list);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
83162306a36Sopenharmony_ci		deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
83262306a36Sopenharmony_ci		if (fip->sel_fcf == fcf) {
83362306a36Sopenharmony_ci			if (time_after(jiffies, deadline)) {
83462306a36Sopenharmony_ci				u64 miss_cnt;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci				miss_cnt = this_cpu_inc_return(fip->lp->stats->MissDiscAdvCount);
83762306a36Sopenharmony_ci				printk(KERN_INFO "libfcoe: host%d: "
83862306a36Sopenharmony_ci				       "Missing Discovery Advertisement "
83962306a36Sopenharmony_ci				       "for fab %16.16llx count %lld\n",
84062306a36Sopenharmony_ci				       fip->lp->host->host_no, fcf->fabric_name,
84162306a36Sopenharmony_ci				       miss_cnt);
84262306a36Sopenharmony_ci			} else if (time_after(next_timer, deadline))
84362306a36Sopenharmony_ci				next_timer = deadline;
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		deadline += fcf->fka_period;
84762306a36Sopenharmony_ci		if (time_after_eq(jiffies, deadline)) {
84862306a36Sopenharmony_ci			if (fip->sel_fcf == fcf)
84962306a36Sopenharmony_ci				fip->sel_fcf = NULL;
85062306a36Sopenharmony_ci			/*
85162306a36Sopenharmony_ci			 * Move to delete list so we can call
85262306a36Sopenharmony_ci			 * fcoe_sysfs_fcf_del (which can sleep)
85362306a36Sopenharmony_ci			 * after the put_cpu().
85462306a36Sopenharmony_ci			 */
85562306a36Sopenharmony_ci			list_del(&fcf->list);
85662306a36Sopenharmony_ci			list_add(&fcf->list, &del_list);
85762306a36Sopenharmony_ci			this_cpu_inc(fip->lp->stats->VLinkFailureCount);
85862306a36Sopenharmony_ci		} else {
85962306a36Sopenharmony_ci			if (time_after(next_timer, deadline))
86062306a36Sopenharmony_ci				next_timer = deadline;
86162306a36Sopenharmony_ci			if (fcoe_ctlr_mtu_valid(fcf) &&
86262306a36Sopenharmony_ci			    (!sel_time || time_before(sel_time, fcf->time)))
86362306a36Sopenharmony_ci				sel_time = fcf->time;
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	list_for_each_entry_safe(fcf, next, &del_list, list) {
86862306a36Sopenharmony_ci		/* Removes fcf from current list */
86962306a36Sopenharmony_ci		fcoe_sysfs_fcf_del(fcf);
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (sel_time && !fip->sel_fcf && !fip->sel_time) {
87362306a36Sopenharmony_ci		sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
87462306a36Sopenharmony_ci		fip->sel_time = sel_time;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return next_timer;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci/**
88162306a36Sopenharmony_ci * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry
88262306a36Sopenharmony_ci * @fip: The FCoE controller receiving the advertisement
88362306a36Sopenharmony_ci * @skb: The received FIP advertisement frame
88462306a36Sopenharmony_ci * @fcf: The resulting FCF entry
88562306a36Sopenharmony_ci *
88662306a36Sopenharmony_ci * Returns zero on a valid parsed advertisement,
88762306a36Sopenharmony_ci * otherwise returns non zero value.
88862306a36Sopenharmony_ci */
88962306a36Sopenharmony_cistatic int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
89062306a36Sopenharmony_ci			       struct sk_buff *skb, struct fcoe_fcf *fcf)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct fip_header *fiph;
89362306a36Sopenharmony_ci	struct fip_desc *desc = NULL;
89462306a36Sopenharmony_ci	struct fip_wwn_desc *wwn;
89562306a36Sopenharmony_ci	struct fip_fab_desc *fab;
89662306a36Sopenharmony_ci	struct fip_fka_desc *fka;
89762306a36Sopenharmony_ci	unsigned long t;
89862306a36Sopenharmony_ci	size_t rlen;
89962306a36Sopenharmony_ci	size_t dlen;
90062306a36Sopenharmony_ci	u32 desc_mask;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	memset(fcf, 0, sizeof(*fcf));
90362306a36Sopenharmony_ci	fcf->fka_period = msecs_to_jiffies(FCOE_CTLR_DEF_FKA);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
90662306a36Sopenharmony_ci	fcf->flags = ntohs(fiph->fip_flags);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/*
90962306a36Sopenharmony_ci	 * mask of required descriptors. validating each one clears its bit.
91062306a36Sopenharmony_ci	 */
91162306a36Sopenharmony_ci	desc_mask = BIT(FIP_DT_PRI) | BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
91262306a36Sopenharmony_ci			BIT(FIP_DT_FAB) | BIT(FIP_DT_FKA);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
91562306a36Sopenharmony_ci	if (rlen + sizeof(*fiph) > skb->len)
91662306a36Sopenharmony_ci		return -EINVAL;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
91962306a36Sopenharmony_ci	while (rlen > 0) {
92062306a36Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
92162306a36Sopenharmony_ci		if (dlen < sizeof(*desc) || dlen > rlen)
92262306a36Sopenharmony_ci			return -EINVAL;
92362306a36Sopenharmony_ci		/* Drop Adv if there are duplicate critical descriptors */
92462306a36Sopenharmony_ci		if ((desc->fip_dtype < 32) &&
92562306a36Sopenharmony_ci		    !(desc_mask & 1U << desc->fip_dtype)) {
92662306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
92762306a36Sopenharmony_ci					"Descriptors in FIP adv\n");
92862306a36Sopenharmony_ci			return -EINVAL;
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci		switch (desc->fip_dtype) {
93162306a36Sopenharmony_ci		case FIP_DT_PRI:
93262306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_pri_desc))
93362306a36Sopenharmony_ci				goto len_err;
93462306a36Sopenharmony_ci			fcf->pri = ((struct fip_pri_desc *)desc)->fd_pri;
93562306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_PRI);
93662306a36Sopenharmony_ci			break;
93762306a36Sopenharmony_ci		case FIP_DT_MAC:
93862306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_mac_desc))
93962306a36Sopenharmony_ci				goto len_err;
94062306a36Sopenharmony_ci			memcpy(fcf->fcf_mac,
94162306a36Sopenharmony_ci			       ((struct fip_mac_desc *)desc)->fd_mac,
94262306a36Sopenharmony_ci			       ETH_ALEN);
94362306a36Sopenharmony_ci			memcpy(fcf->fcoe_mac, fcf->fcf_mac, ETH_ALEN);
94462306a36Sopenharmony_ci			if (!is_valid_ether_addr(fcf->fcf_mac)) {
94562306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip,
94662306a36Sopenharmony_ci					"Invalid MAC addr %pM in FIP adv\n",
94762306a36Sopenharmony_ci					fcf->fcf_mac);
94862306a36Sopenharmony_ci				return -EINVAL;
94962306a36Sopenharmony_ci			}
95062306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_MAC);
95162306a36Sopenharmony_ci			break;
95262306a36Sopenharmony_ci		case FIP_DT_NAME:
95362306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_wwn_desc))
95462306a36Sopenharmony_ci				goto len_err;
95562306a36Sopenharmony_ci			wwn = (struct fip_wwn_desc *)desc;
95662306a36Sopenharmony_ci			fcf->switch_name = get_unaligned_be64(&wwn->fd_wwn);
95762306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_NAME);
95862306a36Sopenharmony_ci			break;
95962306a36Sopenharmony_ci		case FIP_DT_FAB:
96062306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_fab_desc))
96162306a36Sopenharmony_ci				goto len_err;
96262306a36Sopenharmony_ci			fab = (struct fip_fab_desc *)desc;
96362306a36Sopenharmony_ci			fcf->fabric_name = get_unaligned_be64(&fab->fd_wwn);
96462306a36Sopenharmony_ci			fcf->vfid = ntohs(fab->fd_vfid);
96562306a36Sopenharmony_ci			fcf->fc_map = ntoh24(fab->fd_map);
96662306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_FAB);
96762306a36Sopenharmony_ci			break;
96862306a36Sopenharmony_ci		case FIP_DT_FKA:
96962306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_fka_desc))
97062306a36Sopenharmony_ci				goto len_err;
97162306a36Sopenharmony_ci			fka = (struct fip_fka_desc *)desc;
97262306a36Sopenharmony_ci			if (fka->fd_flags & FIP_FKA_ADV_D)
97362306a36Sopenharmony_ci				fcf->fd_flags = 1;
97462306a36Sopenharmony_ci			t = ntohl(fka->fd_fka_period);
97562306a36Sopenharmony_ci			if (t >= FCOE_CTLR_MIN_FKA)
97662306a36Sopenharmony_ci				fcf->fka_period = msecs_to_jiffies(t);
97762306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_FKA);
97862306a36Sopenharmony_ci			break;
97962306a36Sopenharmony_ci		case FIP_DT_MAP_OUI:
98062306a36Sopenharmony_ci		case FIP_DT_FCOE_SIZE:
98162306a36Sopenharmony_ci		case FIP_DT_FLOGI:
98262306a36Sopenharmony_ci		case FIP_DT_FDISC:
98362306a36Sopenharmony_ci		case FIP_DT_LOGO:
98462306a36Sopenharmony_ci		case FIP_DT_ELP:
98562306a36Sopenharmony_ci		default:
98662306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
98762306a36Sopenharmony_ci					"in FIP adv\n", desc->fip_dtype);
98862306a36Sopenharmony_ci			/* standard says ignore unknown descriptors >= 128 */
98962306a36Sopenharmony_ci			if (desc->fip_dtype < FIP_DT_NON_CRITICAL)
99062306a36Sopenharmony_ci				return -EINVAL;
99162306a36Sopenharmony_ci			break;
99262306a36Sopenharmony_ci		}
99362306a36Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
99462306a36Sopenharmony_ci		rlen -= dlen;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci	if (!fcf->fc_map || (fcf->fc_map & 0x10000))
99762306a36Sopenharmony_ci		return -EINVAL;
99862306a36Sopenharmony_ci	if (!fcf->switch_name)
99962306a36Sopenharmony_ci		return -EINVAL;
100062306a36Sopenharmony_ci	if (desc_mask) {
100162306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n",
100262306a36Sopenharmony_ci				desc_mask);
100362306a36Sopenharmony_ci		return -EINVAL;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci	return 0;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cilen_err:
100862306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
100962306a36Sopenharmony_ci			desc->fip_dtype, dlen);
101062306a36Sopenharmony_ci	return -EINVAL;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/**
101462306a36Sopenharmony_ci * fcoe_ctlr_recv_adv() - Handle an incoming advertisement
101562306a36Sopenharmony_ci * @fip: The FCoE controller receiving the advertisement
101662306a36Sopenharmony_ci * @skb: The received FIP packet
101762306a36Sopenharmony_ci */
101862306a36Sopenharmony_cistatic void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
102162306a36Sopenharmony_ci	struct fcoe_fcf new;
102262306a36Sopenharmony_ci	unsigned long sol_tov = msecs_to_jiffies(FCOE_CTLR_SOL_TOV);
102362306a36Sopenharmony_ci	int first = 0;
102462306a36Sopenharmony_ci	int mtu_valid;
102562306a36Sopenharmony_ci	int found = 0;
102662306a36Sopenharmony_ci	int rc = 0;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (fcoe_ctlr_parse_adv(fip, skb, &new))
102962306a36Sopenharmony_ci		return;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
103262306a36Sopenharmony_ci	first = list_empty(&fip->fcfs);
103362306a36Sopenharmony_ci	list_for_each_entry(fcf, &fip->fcfs, list) {
103462306a36Sopenharmony_ci		if (fcf->switch_name == new.switch_name &&
103562306a36Sopenharmony_ci		    fcf->fabric_name == new.fabric_name &&
103662306a36Sopenharmony_ci		    fcf->fc_map == new.fc_map &&
103762306a36Sopenharmony_ci		    ether_addr_equal(fcf->fcf_mac, new.fcf_mac)) {
103862306a36Sopenharmony_ci			found = 1;
103962306a36Sopenharmony_ci			break;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci	if (!found) {
104362306a36Sopenharmony_ci		if (fip->fcf_count >= FCOE_CTLR_FCF_LIMIT)
104462306a36Sopenharmony_ci			goto out;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci		fcf = kmalloc(sizeof(*fcf), GFP_ATOMIC);
104762306a36Sopenharmony_ci		if (!fcf)
104862306a36Sopenharmony_ci			goto out;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		memcpy(fcf, &new, sizeof(new));
105162306a36Sopenharmony_ci		fcf->fip = fip;
105262306a36Sopenharmony_ci		rc = fcoe_sysfs_fcf_add(fcf);
105362306a36Sopenharmony_ci		if (rc) {
105462306a36Sopenharmony_ci			printk(KERN_ERR "Failed to allocate sysfs instance "
105562306a36Sopenharmony_ci			       "for FCF, fab %16.16llx mac %pM\n",
105662306a36Sopenharmony_ci			       new.fabric_name, new.fcf_mac);
105762306a36Sopenharmony_ci			kfree(fcf);
105862306a36Sopenharmony_ci			goto out;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci	} else {
106162306a36Sopenharmony_ci		/*
106262306a36Sopenharmony_ci		 * Update the FCF's keep-alive descriptor flags.
106362306a36Sopenharmony_ci		 * Other flag changes from new advertisements are
106462306a36Sopenharmony_ci		 * ignored after a solicited advertisement is
106562306a36Sopenharmony_ci		 * received and the FCF is selectable (usable).
106662306a36Sopenharmony_ci		 */
106762306a36Sopenharmony_ci		fcf->fd_flags = new.fd_flags;
106862306a36Sopenharmony_ci		if (!fcoe_ctlr_fcf_usable(fcf))
106962306a36Sopenharmony_ci			fcf->flags = new.flags;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		if (fcf == fip->sel_fcf && !fcf->fd_flags) {
107262306a36Sopenharmony_ci			fip->ctlr_ka_time -= fcf->fka_period;
107362306a36Sopenharmony_ci			fip->ctlr_ka_time += new.fka_period;
107462306a36Sopenharmony_ci			if (time_before(fip->ctlr_ka_time, fip->timer.expires))
107562306a36Sopenharmony_ci				mod_timer(&fip->timer, fip->ctlr_ka_time);
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci		fcf->fka_period = new.fka_period;
107862306a36Sopenharmony_ci		memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN);
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	mtu_valid = fcoe_ctlr_mtu_valid(fcf);
108262306a36Sopenharmony_ci	fcf->time = jiffies;
108362306a36Sopenharmony_ci	if (!found)
108462306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
108562306a36Sopenharmony_ci				fcf->fabric_name, fcf->fcf_mac);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	/*
108862306a36Sopenharmony_ci	 * If this advertisement is not solicited and our max receive size
108962306a36Sopenharmony_ci	 * hasn't been verified, send a solicited advertisement.
109062306a36Sopenharmony_ci	 */
109162306a36Sopenharmony_ci	if (!mtu_valid)
109262306a36Sopenharmony_ci		fcoe_ctlr_solicit(fip, fcf);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	/*
109562306a36Sopenharmony_ci	 * If its been a while since we did a solicit, and this is
109662306a36Sopenharmony_ci	 * the first advertisement we've received, do a multicast
109762306a36Sopenharmony_ci	 * solicitation to gather as many advertisements as we can
109862306a36Sopenharmony_ci	 * before selection occurs.
109962306a36Sopenharmony_ci	 */
110062306a36Sopenharmony_ci	if (first && time_after(jiffies, fip->sol_time + sol_tov))
110162306a36Sopenharmony_ci		fcoe_ctlr_solicit(fip, NULL);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	/*
110462306a36Sopenharmony_ci	 * Put this FCF at the head of the list for priority among equals.
110562306a36Sopenharmony_ci	 * This helps in the case of an NPV switch which insists we use
110662306a36Sopenharmony_ci	 * the FCF that answers multicast solicitations, not the others that
110762306a36Sopenharmony_ci	 * are sending periodic multicast advertisements.
110862306a36Sopenharmony_ci	 */
110962306a36Sopenharmony_ci	if (mtu_valid)
111062306a36Sopenharmony_ci		list_move(&fcf->list, &fip->fcfs);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	/*
111362306a36Sopenharmony_ci	 * If this is the first validated FCF, note the time and
111462306a36Sopenharmony_ci	 * set a timer to trigger selection.
111562306a36Sopenharmony_ci	 */
111662306a36Sopenharmony_ci	if (mtu_valid && !fip->sel_fcf && !fip->sel_time &&
111762306a36Sopenharmony_ci	    fcoe_ctlr_fcf_usable(fcf)) {
111862306a36Sopenharmony_ci		fip->sel_time = jiffies +
111962306a36Sopenharmony_ci			msecs_to_jiffies(FCOE_CTLR_START_DELAY);
112062306a36Sopenharmony_ci		if (!timer_pending(&fip->timer) ||
112162306a36Sopenharmony_ci		    time_before(fip->sel_time, fip->timer.expires))
112262306a36Sopenharmony_ci			mod_timer(&fip->timer, fip->sel_time);
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ciout:
112662306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci/**
113062306a36Sopenharmony_ci * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame
113162306a36Sopenharmony_ci * @fip: The FCoE controller which received the packet
113262306a36Sopenharmony_ci * @skb: The received FIP packet
113362306a36Sopenharmony_ci */
113462306a36Sopenharmony_cistatic void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
113762306a36Sopenharmony_ci	struct fip_header *fiph;
113862306a36Sopenharmony_ci	struct fc_frame *fp = (struct fc_frame *)skb;
113962306a36Sopenharmony_ci	struct fc_frame_header *fh = NULL;
114062306a36Sopenharmony_ci	struct fip_desc *desc;
114162306a36Sopenharmony_ci	struct fip_encaps *els;
114262306a36Sopenharmony_ci	struct fcoe_fcf *sel;
114362306a36Sopenharmony_ci	enum fip_desc_type els_dtype = 0;
114462306a36Sopenharmony_ci	u8 els_op;
114562306a36Sopenharmony_ci	u8 sub;
114662306a36Sopenharmony_ci	u8 granted_mac[ETH_ALEN] = { 0 };
114762306a36Sopenharmony_ci	size_t els_len = 0;
114862306a36Sopenharmony_ci	size_t rlen;
114962306a36Sopenharmony_ci	size_t dlen;
115062306a36Sopenharmony_ci	u32 desc_mask = 0;
115162306a36Sopenharmony_ci	u32 desc_cnt = 0;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
115462306a36Sopenharmony_ci	sub = fiph->fip_subcode;
115562306a36Sopenharmony_ci	if (sub != FIP_SC_REQ && sub != FIP_SC_REP)
115662306a36Sopenharmony_ci		goto drop;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
115962306a36Sopenharmony_ci	if (rlen + sizeof(*fiph) > skb->len)
116062306a36Sopenharmony_ci		goto drop;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
116362306a36Sopenharmony_ci	while (rlen > 0) {
116462306a36Sopenharmony_ci		desc_cnt++;
116562306a36Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
116662306a36Sopenharmony_ci		if (dlen < sizeof(*desc) || dlen > rlen)
116762306a36Sopenharmony_ci			goto drop;
116862306a36Sopenharmony_ci		/* Drop ELS if there are duplicate critical descriptors */
116962306a36Sopenharmony_ci		if (desc->fip_dtype < 32) {
117062306a36Sopenharmony_ci			if ((desc->fip_dtype != FIP_DT_MAC) &&
117162306a36Sopenharmony_ci			    (desc_mask & 1U << desc->fip_dtype)) {
117262306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
117362306a36Sopenharmony_ci						"Descriptors in FIP ELS\n");
117462306a36Sopenharmony_ci				goto drop;
117562306a36Sopenharmony_ci			}
117662306a36Sopenharmony_ci			desc_mask |= (1 << desc->fip_dtype);
117762306a36Sopenharmony_ci		}
117862306a36Sopenharmony_ci		switch (desc->fip_dtype) {
117962306a36Sopenharmony_ci		case FIP_DT_MAC:
118062306a36Sopenharmony_ci			sel = fip->sel_fcf;
118162306a36Sopenharmony_ci			if (desc_cnt == 1) {
118262306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
118362306a36Sopenharmony_ci						"received out of order\n");
118462306a36Sopenharmony_ci				goto drop;
118562306a36Sopenharmony_ci			}
118662306a36Sopenharmony_ci			/*
118762306a36Sopenharmony_ci			 * Some switch implementations send two MAC descriptors,
118862306a36Sopenharmony_ci			 * with first MAC(granted_mac) being the FPMA, and the
118962306a36Sopenharmony_ci			 * second one(fcoe_mac) is used as destination address
119062306a36Sopenharmony_ci			 * for sending/receiving FCoE packets. FIP traffic is
119162306a36Sopenharmony_ci			 * sent using fip_mac. For regular switches, both
119262306a36Sopenharmony_ci			 * fip_mac and fcoe_mac would be the same.
119362306a36Sopenharmony_ci			 */
119462306a36Sopenharmony_ci			if (desc_cnt == 2)
119562306a36Sopenharmony_ci				memcpy(granted_mac,
119662306a36Sopenharmony_ci				       ((struct fip_mac_desc *)desc)->fd_mac,
119762306a36Sopenharmony_ci				       ETH_ALEN);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_mac_desc))
120062306a36Sopenharmony_ci				goto len_err;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci			if ((desc_cnt == 3) && (sel))
120362306a36Sopenharmony_ci				memcpy(sel->fcoe_mac,
120462306a36Sopenharmony_ci				       ((struct fip_mac_desc *)desc)->fd_mac,
120562306a36Sopenharmony_ci				       ETH_ALEN);
120662306a36Sopenharmony_ci			break;
120762306a36Sopenharmony_ci		case FIP_DT_FLOGI:
120862306a36Sopenharmony_ci		case FIP_DT_FDISC:
120962306a36Sopenharmony_ci		case FIP_DT_LOGO:
121062306a36Sopenharmony_ci		case FIP_DT_ELP:
121162306a36Sopenharmony_ci			if (desc_cnt != 1) {
121262306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
121362306a36Sopenharmony_ci						"received out of order\n");
121462306a36Sopenharmony_ci				goto drop;
121562306a36Sopenharmony_ci			}
121662306a36Sopenharmony_ci			if (fh)
121762306a36Sopenharmony_ci				goto drop;
121862306a36Sopenharmony_ci			if (dlen < sizeof(*els) + sizeof(*fh) + 1)
121962306a36Sopenharmony_ci				goto len_err;
122062306a36Sopenharmony_ci			els_len = dlen - sizeof(*els);
122162306a36Sopenharmony_ci			els = (struct fip_encaps *)desc;
122262306a36Sopenharmony_ci			fh = (struct fc_frame_header *)(els + 1);
122362306a36Sopenharmony_ci			els_dtype = desc->fip_dtype;
122462306a36Sopenharmony_ci			break;
122562306a36Sopenharmony_ci		default:
122662306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
122762306a36Sopenharmony_ci					"in FIP adv\n", desc->fip_dtype);
122862306a36Sopenharmony_ci			/* standard says ignore unknown descriptors >= 128 */
122962306a36Sopenharmony_ci			if (desc->fip_dtype < FIP_DT_NON_CRITICAL)
123062306a36Sopenharmony_ci				goto drop;
123162306a36Sopenharmony_ci			if (desc_cnt <= 2) {
123262306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
123362306a36Sopenharmony_ci						"received out of order\n");
123462306a36Sopenharmony_ci				goto drop;
123562306a36Sopenharmony_ci			}
123662306a36Sopenharmony_ci			break;
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
123962306a36Sopenharmony_ci		rlen -= dlen;
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (!fh)
124362306a36Sopenharmony_ci		goto drop;
124462306a36Sopenharmony_ci	els_op = *(u8 *)(fh + 1);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) &&
124762306a36Sopenharmony_ci	    sub == FIP_SC_REP && fip->mode != FIP_MODE_VN2VN) {
124862306a36Sopenharmony_ci		if (els_op == ELS_LS_ACC) {
124962306a36Sopenharmony_ci			if (!is_valid_ether_addr(granted_mac)) {
125062306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip,
125162306a36Sopenharmony_ci					"Invalid MAC address %pM in FIP ELS\n",
125262306a36Sopenharmony_ci					granted_mac);
125362306a36Sopenharmony_ci				goto drop;
125462306a36Sopenharmony_ci			}
125562306a36Sopenharmony_ci			memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci			if (fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
125862306a36Sopenharmony_ci				fip->flogi_oxid = FC_XID_UNKNOWN;
125962306a36Sopenharmony_ci				if (els_dtype == FIP_DT_FLOGI)
126062306a36Sopenharmony_ci					fcoe_ctlr_announce(fip);
126162306a36Sopenharmony_ci			}
126262306a36Sopenharmony_ci		} else if (els_dtype == FIP_DT_FLOGI &&
126362306a36Sopenharmony_ci			   !fcoe_ctlr_flogi_retry(fip))
126462306a36Sopenharmony_ci			goto drop;	/* retrying FLOGI so drop reject */
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) &&
126862306a36Sopenharmony_ci	    (!(1U << FIP_DT_MAC & desc_mask)))) {
126962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "Missing critical descriptors "
127062306a36Sopenharmony_ci				"in FIP ELS\n");
127162306a36Sopenharmony_ci		goto drop;
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	/*
127562306a36Sopenharmony_ci	 * Convert skb into an fc_frame containing only the ELS.
127662306a36Sopenharmony_ci	 */
127762306a36Sopenharmony_ci	skb_pull(skb, (u8 *)fh - skb->data);
127862306a36Sopenharmony_ci	skb_trim(skb, els_len);
127962306a36Sopenharmony_ci	fp = (struct fc_frame *)skb;
128062306a36Sopenharmony_ci	fc_frame_init(fp);
128162306a36Sopenharmony_ci	fr_sof(fp) = FC_SOF_I3;
128262306a36Sopenharmony_ci	fr_eof(fp) = FC_EOF_T;
128362306a36Sopenharmony_ci	fr_dev(fp) = lport;
128462306a36Sopenharmony_ci	fr_encaps(fp) = els_dtype;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	this_cpu_inc(lport->stats->RxFrames);
128762306a36Sopenharmony_ci	this_cpu_add(lport->stats->RxWords, skb->len / FIP_BPW);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	fc_exch_recv(lport, fp);
129062306a36Sopenharmony_ci	return;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cilen_err:
129362306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
129462306a36Sopenharmony_ci			desc->fip_dtype, dlen);
129562306a36Sopenharmony_cidrop:
129662306a36Sopenharmony_ci	kfree_skb(skb);
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci/**
130062306a36Sopenharmony_ci * fcoe_ctlr_recv_clr_vlink() - Handle an incoming link reset frame
130162306a36Sopenharmony_ci * @fip: The FCoE controller that received the frame
130262306a36Sopenharmony_ci * @skb: The received FIP packet
130362306a36Sopenharmony_ci *
130462306a36Sopenharmony_ci * There may be multiple VN_Port descriptors.
130562306a36Sopenharmony_ci * The overall length has already been checked.
130662306a36Sopenharmony_ci */
130762306a36Sopenharmony_cistatic void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
130862306a36Sopenharmony_ci				     struct sk_buff *skb)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	struct fip_desc *desc;
131162306a36Sopenharmony_ci	struct fip_mac_desc *mp;
131262306a36Sopenharmony_ci	struct fip_wwn_desc *wp;
131362306a36Sopenharmony_ci	struct fip_vn_desc *vp;
131462306a36Sopenharmony_ci	size_t rlen;
131562306a36Sopenharmony_ci	size_t dlen;
131662306a36Sopenharmony_ci	struct fcoe_fcf *fcf = fip->sel_fcf;
131762306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
131862306a36Sopenharmony_ci	struct fc_lport *vn_port = NULL;
131962306a36Sopenharmony_ci	u32 desc_mask;
132062306a36Sopenharmony_ci	int num_vlink_desc;
132162306a36Sopenharmony_ci	int reset_phys_port = 0;
132262306a36Sopenharmony_ci	struct fip_vn_desc **vlink_desc_arr = NULL;
132362306a36Sopenharmony_ci	struct fip_header *fh = (struct fip_header *)skb->data;
132462306a36Sopenharmony_ci	struct ethhdr *eh = eth_hdr(skb);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (!fcf) {
132962306a36Sopenharmony_ci		/*
133062306a36Sopenharmony_ci		 * We are yet to select best FCF, but we got CVL in the
133162306a36Sopenharmony_ci		 * meantime. reset the ctlr and let it rediscover the FCF
133262306a36Sopenharmony_ci		 */
133362306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "Resetting fcoe_ctlr as FCF has not been "
133462306a36Sopenharmony_ci		    "selected yet\n");
133562306a36Sopenharmony_ci		mutex_lock(&fip->ctlr_mutex);
133662306a36Sopenharmony_ci		fcoe_ctlr_reset(fip);
133762306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
133862306a36Sopenharmony_ci		return;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	/*
134262306a36Sopenharmony_ci	 * If we've selected an FCF check that the CVL is from there to avoid
134362306a36Sopenharmony_ci	 * processing CVLs from an unexpected source.  If it is from an
134462306a36Sopenharmony_ci	 * unexpected source drop it on the floor.
134562306a36Sopenharmony_ci	 */
134662306a36Sopenharmony_ci	if (!ether_addr_equal(eh->h_source, fcf->fcf_mac)) {
134762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "Dropping CVL due to source address "
134862306a36Sopenharmony_ci		    "mismatch with FCF src=%pM\n", eh->h_source);
134962306a36Sopenharmony_ci		return;
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/*
135362306a36Sopenharmony_ci	 * If we haven't logged into the fabric but receive a CVL we should
135462306a36Sopenharmony_ci	 * reset everything and go back to solicitation.
135562306a36Sopenharmony_ci	 */
135662306a36Sopenharmony_ci	if (!lport->port_id) {
135762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "lport not logged in, resoliciting\n");
135862306a36Sopenharmony_ci		mutex_lock(&fip->ctlr_mutex);
135962306a36Sopenharmony_ci		fcoe_ctlr_reset(fip);
136062306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
136162306a36Sopenharmony_ci		fc_lport_reset(fip->lp);
136262306a36Sopenharmony_ci		fcoe_ctlr_solicit(fip, NULL);
136362306a36Sopenharmony_ci		return;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/*
136762306a36Sopenharmony_ci	 * mask of required descriptors.  Validating each one clears its bit.
136862306a36Sopenharmony_ci	 */
136962306a36Sopenharmony_ci	desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	rlen = ntohs(fh->fip_dl_len) * FIP_BPW;
137262306a36Sopenharmony_ci	desc = (struct fip_desc *)(fh + 1);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	/*
137562306a36Sopenharmony_ci	 * Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen'
137662306a36Sopenharmony_ci	 * before determining max Vx_Port descriptor but a buggy FCF could have
137762306a36Sopenharmony_ci	 * omitted either or both MAC Address and Name Identifier descriptors
137862306a36Sopenharmony_ci	 */
137962306a36Sopenharmony_ci	num_vlink_desc = rlen / sizeof(*vp);
138062306a36Sopenharmony_ci	if (num_vlink_desc)
138162306a36Sopenharmony_ci		vlink_desc_arr = kmalloc_array(num_vlink_desc, sizeof(vp),
138262306a36Sopenharmony_ci					       GFP_ATOMIC);
138362306a36Sopenharmony_ci	if (!vlink_desc_arr)
138462306a36Sopenharmony_ci		return;
138562306a36Sopenharmony_ci	num_vlink_desc = 0;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	while (rlen >= sizeof(*desc)) {
138862306a36Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
138962306a36Sopenharmony_ci		if (dlen > rlen)
139062306a36Sopenharmony_ci			goto err;
139162306a36Sopenharmony_ci		/* Drop CVL if there are duplicate critical descriptors */
139262306a36Sopenharmony_ci		if ((desc->fip_dtype < 32) &&
139362306a36Sopenharmony_ci		    (desc->fip_dtype != FIP_DT_VN_ID) &&
139462306a36Sopenharmony_ci		    !(desc_mask & 1U << desc->fip_dtype)) {
139562306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
139662306a36Sopenharmony_ci					"Descriptors in FIP CVL\n");
139762306a36Sopenharmony_ci			goto err;
139862306a36Sopenharmony_ci		}
139962306a36Sopenharmony_ci		switch (desc->fip_dtype) {
140062306a36Sopenharmony_ci		case FIP_DT_MAC:
140162306a36Sopenharmony_ci			mp = (struct fip_mac_desc *)desc;
140262306a36Sopenharmony_ci			if (dlen < sizeof(*mp))
140362306a36Sopenharmony_ci				goto err;
140462306a36Sopenharmony_ci			if (!ether_addr_equal(mp->fd_mac, fcf->fcf_mac))
140562306a36Sopenharmony_ci				goto err;
140662306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_MAC);
140762306a36Sopenharmony_ci			break;
140862306a36Sopenharmony_ci		case FIP_DT_NAME:
140962306a36Sopenharmony_ci			wp = (struct fip_wwn_desc *)desc;
141062306a36Sopenharmony_ci			if (dlen < sizeof(*wp))
141162306a36Sopenharmony_ci				goto err;
141262306a36Sopenharmony_ci			if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name)
141362306a36Sopenharmony_ci				goto err;
141462306a36Sopenharmony_ci			desc_mask &= ~BIT(FIP_DT_NAME);
141562306a36Sopenharmony_ci			break;
141662306a36Sopenharmony_ci		case FIP_DT_VN_ID:
141762306a36Sopenharmony_ci			vp = (struct fip_vn_desc *)desc;
141862306a36Sopenharmony_ci			if (dlen < sizeof(*vp))
141962306a36Sopenharmony_ci				goto err;
142062306a36Sopenharmony_ci			vlink_desc_arr[num_vlink_desc++] = vp;
142162306a36Sopenharmony_ci			vn_port = fc_vport_id_lookup(lport,
142262306a36Sopenharmony_ci						      ntoh24(vp->fd_fc_id));
142362306a36Sopenharmony_ci			if (vn_port && (vn_port == lport)) {
142462306a36Sopenharmony_ci				mutex_lock(&fip->ctlr_mutex);
142562306a36Sopenharmony_ci				this_cpu_inc(lport->stats->VLinkFailureCount);
142662306a36Sopenharmony_ci				fcoe_ctlr_reset(fip);
142762306a36Sopenharmony_ci				mutex_unlock(&fip->ctlr_mutex);
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci			break;
143062306a36Sopenharmony_ci		default:
143162306a36Sopenharmony_ci			/* standard says ignore unknown descriptors >= 128 */
143262306a36Sopenharmony_ci			if (desc->fip_dtype < FIP_DT_NON_CRITICAL)
143362306a36Sopenharmony_ci				goto err;
143462306a36Sopenharmony_ci			break;
143562306a36Sopenharmony_ci		}
143662306a36Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
143762306a36Sopenharmony_ci		rlen -= dlen;
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	/*
144162306a36Sopenharmony_ci	 * reset only if all required descriptors were present and valid.
144262306a36Sopenharmony_ci	 */
144362306a36Sopenharmony_ci	if (desc_mask)
144462306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
144562306a36Sopenharmony_ci				desc_mask);
144662306a36Sopenharmony_ci	else if (!num_vlink_desc) {
144762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "CVL: no Vx_Port descriptor found\n");
144862306a36Sopenharmony_ci		/*
144962306a36Sopenharmony_ci		 * No Vx_Port description. Clear all NPIV ports,
145062306a36Sopenharmony_ci		 * followed by physical port
145162306a36Sopenharmony_ci		 */
145262306a36Sopenharmony_ci		mutex_lock(&fip->ctlr_mutex);
145362306a36Sopenharmony_ci		this_cpu_inc(lport->stats->VLinkFailureCount);
145462306a36Sopenharmony_ci		fcoe_ctlr_reset(fip);
145562306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		mutex_lock(&lport->lp_mutex);
145862306a36Sopenharmony_ci		list_for_each_entry(vn_port, &lport->vports, list)
145962306a36Sopenharmony_ci			fc_lport_reset(vn_port);
146062306a36Sopenharmony_ci		mutex_unlock(&lport->lp_mutex);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci		fc_lport_reset(fip->lp);
146362306a36Sopenharmony_ci		fcoe_ctlr_solicit(fip, NULL);
146462306a36Sopenharmony_ci	} else {
146562306a36Sopenharmony_ci		int i;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
146862306a36Sopenharmony_ci		for (i = 0; i < num_vlink_desc; i++) {
146962306a36Sopenharmony_ci			vp = vlink_desc_arr[i];
147062306a36Sopenharmony_ci			vn_port = fc_vport_id_lookup(lport,
147162306a36Sopenharmony_ci						     ntoh24(vp->fd_fc_id));
147262306a36Sopenharmony_ci			if (!vn_port)
147362306a36Sopenharmony_ci				continue;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci			/*
147662306a36Sopenharmony_ci			 * 'port_id' is already validated, check MAC address and
147762306a36Sopenharmony_ci			 * wwpn
147862306a36Sopenharmony_ci			 */
147962306a36Sopenharmony_ci			if (!ether_addr_equal(fip->get_src_addr(vn_port),
148062306a36Sopenharmony_ci					      vp->fd_mac) ||
148162306a36Sopenharmony_ci				get_unaligned_be64(&vp->fd_wwpn) !=
148262306a36Sopenharmony_ci							vn_port->wwpn)
148362306a36Sopenharmony_ci				continue;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci			if (vn_port == lport)
148662306a36Sopenharmony_ci				/*
148762306a36Sopenharmony_ci				 * Physical port, defer processing till all
148862306a36Sopenharmony_ci				 * listed NPIV ports are cleared
148962306a36Sopenharmony_ci				 */
149062306a36Sopenharmony_ci				reset_phys_port = 1;
149162306a36Sopenharmony_ci			else    /* NPIV port */
149262306a36Sopenharmony_ci				fc_lport_reset(vn_port);
149362306a36Sopenharmony_ci		}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci		if (reset_phys_port) {
149662306a36Sopenharmony_ci			fc_lport_reset(fip->lp);
149762306a36Sopenharmony_ci			fcoe_ctlr_solicit(fip, NULL);
149862306a36Sopenharmony_ci		}
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cierr:
150262306a36Sopenharmony_ci	kfree(vlink_desc_arr);
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci/**
150662306a36Sopenharmony_ci * fcoe_ctlr_recv() - Receive a FIP packet
150762306a36Sopenharmony_ci * @fip: The FCoE controller that received the packet
150862306a36Sopenharmony_ci * @skb: The received FIP packet
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * This may be called from either NET_RX_SOFTIRQ or IRQ.
151162306a36Sopenharmony_ci */
151262306a36Sopenharmony_civoid fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
151562306a36Sopenharmony_ci	if (!skb)
151662306a36Sopenharmony_ci		return;
151762306a36Sopenharmony_ci	skb_queue_tail(&fip->fip_recv_list, skb);
151862306a36Sopenharmony_ci	schedule_work(&fip->recv_work);
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_recv);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci/**
152362306a36Sopenharmony_ci * fcoe_ctlr_recv_handler() - Receive a FIP frame
152462306a36Sopenharmony_ci * @fip: The FCoE controller that received the frame
152562306a36Sopenharmony_ci * @skb: The received FIP frame
152662306a36Sopenharmony_ci *
152762306a36Sopenharmony_ci * Returns non-zero if the frame is dropped.
152862306a36Sopenharmony_ci */
152962306a36Sopenharmony_cistatic int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	struct fip_header *fiph;
153262306a36Sopenharmony_ci	struct ethhdr *eh;
153362306a36Sopenharmony_ci	enum fip_state state;
153462306a36Sopenharmony_ci	bool fip_vlan_resp = false;
153562306a36Sopenharmony_ci	u16 op;
153662306a36Sopenharmony_ci	u8 sub;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	if (skb_linearize(skb))
153962306a36Sopenharmony_ci		goto drop;
154062306a36Sopenharmony_ci	if (skb->len < sizeof(*fiph))
154162306a36Sopenharmony_ci		goto drop;
154262306a36Sopenharmony_ci	eh = eth_hdr(skb);
154362306a36Sopenharmony_ci	if (fip->mode == FIP_MODE_VN2VN) {
154462306a36Sopenharmony_ci		if (!ether_addr_equal(eh->h_dest, fip->ctl_src_addr) &&
154562306a36Sopenharmony_ci		    !ether_addr_equal(eh->h_dest, fcoe_all_vn2vn) &&
154662306a36Sopenharmony_ci		    !ether_addr_equal(eh->h_dest, fcoe_all_p2p))
154762306a36Sopenharmony_ci			goto drop;
154862306a36Sopenharmony_ci	} else if (!ether_addr_equal(eh->h_dest, fip->ctl_src_addr) &&
154962306a36Sopenharmony_ci		   !ether_addr_equal(eh->h_dest, fcoe_all_enode))
155062306a36Sopenharmony_ci		goto drop;
155162306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
155262306a36Sopenharmony_ci	op = ntohs(fiph->fip_op);
155362306a36Sopenharmony_ci	sub = fiph->fip_subcode;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER)
155662306a36Sopenharmony_ci		goto drop;
155762306a36Sopenharmony_ci	if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
155862306a36Sopenharmony_ci		goto drop;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
156162306a36Sopenharmony_ci	state = fip->state;
156262306a36Sopenharmony_ci	if (state == FIP_ST_AUTO) {
156362306a36Sopenharmony_ci		fip->map_dest = 0;
156462306a36Sopenharmony_ci		fcoe_ctlr_set_state(fip, FIP_ST_ENABLED);
156562306a36Sopenharmony_ci		state = FIP_ST_ENABLED;
156662306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci	fip_vlan_resp = fip->fip_resp;
156962306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
157262306a36Sopenharmony_ci		return fcoe_ctlr_vn_recv(fip, skb);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	if (fip_vlan_resp && op == FIP_OP_VLAN) {
157562306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "fip vlan discovery\n");
157662306a36Sopenharmony_ci		return fcoe_ctlr_vlan_recv(fip, skb);
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP &&
158062306a36Sopenharmony_ci	    state != FIP_ST_VNMP_CLAIM)
158162306a36Sopenharmony_ci		goto drop;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (op == FIP_OP_LS) {
158462306a36Sopenharmony_ci		fcoe_ctlr_recv_els(fip, skb);	/* consumes skb */
158562306a36Sopenharmony_ci		return 0;
158662306a36Sopenharmony_ci	}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	if (state != FIP_ST_ENABLED)
158962306a36Sopenharmony_ci		goto drop;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	if (op == FIP_OP_DISC && sub == FIP_SC_ADV)
159262306a36Sopenharmony_ci		fcoe_ctlr_recv_adv(fip, skb);
159362306a36Sopenharmony_ci	else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK)
159462306a36Sopenharmony_ci		fcoe_ctlr_recv_clr_vlink(fip, skb);
159562306a36Sopenharmony_ci	kfree_skb(skb);
159662306a36Sopenharmony_ci	return 0;
159762306a36Sopenharmony_cidrop:
159862306a36Sopenharmony_ci	kfree_skb(skb);
159962306a36Sopenharmony_ci	return -1;
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci/**
160362306a36Sopenharmony_ci * fcoe_ctlr_select() - Select the best FCF (if possible)
160462306a36Sopenharmony_ci * @fip: The FCoE controller
160562306a36Sopenharmony_ci *
160662306a36Sopenharmony_ci * Returns the selected FCF, or NULL if none are usable.
160762306a36Sopenharmony_ci *
160862306a36Sopenharmony_ci * If there are conflicting advertisements, no FCF can be chosen.
160962306a36Sopenharmony_ci *
161062306a36Sopenharmony_ci * If there is already a selected FCF, this will choose a better one or
161162306a36Sopenharmony_ci * an equivalent one that hasn't already been sent a FLOGI.
161262306a36Sopenharmony_ci *
161362306a36Sopenharmony_ci * Called with lock held.
161462306a36Sopenharmony_ci */
161562306a36Sopenharmony_cistatic struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
161862306a36Sopenharmony_ci	struct fcoe_fcf *best = fip->sel_fcf;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	list_for_each_entry(fcf, &fip->fcfs, list) {
162162306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "consider FCF fab %16.16llx "
162262306a36Sopenharmony_ci				"VFID %d mac %pM map %x val %d "
162362306a36Sopenharmony_ci				"sent %u pri %u\n",
162462306a36Sopenharmony_ci				fcf->fabric_name, fcf->vfid, fcf->fcf_mac,
162562306a36Sopenharmony_ci				fcf->fc_map, fcoe_ctlr_mtu_valid(fcf),
162662306a36Sopenharmony_ci				fcf->flogi_sent, fcf->pri);
162762306a36Sopenharmony_ci		if (!fcoe_ctlr_fcf_usable(fcf)) {
162862306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx "
162962306a36Sopenharmony_ci					"map %x %svalid %savailable\n",
163062306a36Sopenharmony_ci					fcf->fabric_name, fcf->fc_map,
163162306a36Sopenharmony_ci					(fcf->flags & FIP_FL_SOL) ? "" : "in",
163262306a36Sopenharmony_ci					(fcf->flags & FIP_FL_AVAIL) ?
163362306a36Sopenharmony_ci					"" : "un");
163462306a36Sopenharmony_ci			continue;
163562306a36Sopenharmony_ci		}
163662306a36Sopenharmony_ci		if (!best || fcf->pri < best->pri || best->flogi_sent)
163762306a36Sopenharmony_ci			best = fcf;
163862306a36Sopenharmony_ci		if (fcf->fabric_name != best->fabric_name ||
163962306a36Sopenharmony_ci		    fcf->vfid != best->vfid ||
164062306a36Sopenharmony_ci		    fcf->fc_map != best->fc_map) {
164162306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
164262306a36Sopenharmony_ci					"or FC-MAP\n");
164362306a36Sopenharmony_ci			return NULL;
164462306a36Sopenharmony_ci		}
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci	fip->sel_fcf = best;
164762306a36Sopenharmony_ci	if (best) {
164862306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "using FCF mac %pM\n", best->fcf_mac);
164962306a36Sopenharmony_ci		fip->port_ka_time = jiffies +
165062306a36Sopenharmony_ci			msecs_to_jiffies(FIP_VN_KA_PERIOD);
165162306a36Sopenharmony_ci		fip->ctlr_ka_time = jiffies + best->fka_period;
165262306a36Sopenharmony_ci		if (time_before(fip->ctlr_ka_time, fip->timer.expires))
165362306a36Sopenharmony_ci			mod_timer(&fip->timer, fip->ctlr_ka_time);
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci	return best;
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci/**
165962306a36Sopenharmony_ci * fcoe_ctlr_flogi_send_locked() - send FIP-encapsulated FLOGI to current FCF
166062306a36Sopenharmony_ci * @fip: The FCoE controller
166162306a36Sopenharmony_ci *
166262306a36Sopenharmony_ci * Returns non-zero error if it could not be sent.
166362306a36Sopenharmony_ci *
166462306a36Sopenharmony_ci * Called with ctlr_mutex and ctlr_lock held.
166562306a36Sopenharmony_ci * Caller must verify that fip->sel_fcf is not NULL.
166662306a36Sopenharmony_ci */
166762306a36Sopenharmony_cistatic int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	struct sk_buff *skb;
167062306a36Sopenharmony_ci	struct sk_buff *skb_orig;
167162306a36Sopenharmony_ci	struct fc_frame_header *fh;
167262306a36Sopenharmony_ci	int error;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	skb_orig = fip->flogi_req;
167562306a36Sopenharmony_ci	if (!skb_orig)
167662306a36Sopenharmony_ci		return -EINVAL;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	/*
167962306a36Sopenharmony_ci	 * Clone and send the FLOGI request.  If clone fails, use original.
168062306a36Sopenharmony_ci	 */
168162306a36Sopenharmony_ci	skb = skb_clone(skb_orig, GFP_ATOMIC);
168262306a36Sopenharmony_ci	if (!skb) {
168362306a36Sopenharmony_ci		skb = skb_orig;
168462306a36Sopenharmony_ci		fip->flogi_req = NULL;
168562306a36Sopenharmony_ci	}
168662306a36Sopenharmony_ci	fh = (struct fc_frame_header *)skb->data;
168762306a36Sopenharmony_ci	error = fcoe_ctlr_encaps(fip, fip->lp, FIP_DT_FLOGI, skb,
168862306a36Sopenharmony_ci				 ntoh24(fh->fh_d_id));
168962306a36Sopenharmony_ci	if (error) {
169062306a36Sopenharmony_ci		kfree_skb(skb);
169162306a36Sopenharmony_ci		return error;
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci	fip->send(fip, skb);
169462306a36Sopenharmony_ci	fip->sel_fcf->flogi_sent = 1;
169562306a36Sopenharmony_ci	return 0;
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci/**
169962306a36Sopenharmony_ci * fcoe_ctlr_flogi_retry() - resend FLOGI request to a new FCF if possible
170062306a36Sopenharmony_ci * @fip: The FCoE controller
170162306a36Sopenharmony_ci *
170262306a36Sopenharmony_ci * Returns non-zero error code if there's no FLOGI request to retry or
170362306a36Sopenharmony_ci * no alternate FCF available.
170462306a36Sopenharmony_ci */
170562306a36Sopenharmony_cistatic int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip)
170662306a36Sopenharmony_ci{
170762306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
170862306a36Sopenharmony_ci	int error;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
171162306a36Sopenharmony_ci	spin_lock_bh(&fip->ctlr_lock);
171262306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n");
171362306a36Sopenharmony_ci	fcf = fcoe_ctlr_select(fip);
171462306a36Sopenharmony_ci	if (!fcf || fcf->flogi_sent) {
171562306a36Sopenharmony_ci		kfree_skb(fip->flogi_req);
171662306a36Sopenharmony_ci		fip->flogi_req = NULL;
171762306a36Sopenharmony_ci		error = -ENOENT;
171862306a36Sopenharmony_ci	} else {
171962306a36Sopenharmony_ci		fcoe_ctlr_solicit(fip, NULL);
172062306a36Sopenharmony_ci		error = fcoe_ctlr_flogi_send_locked(fip);
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci	spin_unlock_bh(&fip->ctlr_lock);
172362306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
172462306a36Sopenharmony_ci	return error;
172562306a36Sopenharmony_ci}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci/**
172962306a36Sopenharmony_ci * fcoe_ctlr_flogi_send() - Handle sending of FIP FLOGI.
173062306a36Sopenharmony_ci * @fip: The FCoE controller that timed out
173162306a36Sopenharmony_ci *
173262306a36Sopenharmony_ci * Done here because fcoe_ctlr_els_send() can't get mutex.
173362306a36Sopenharmony_ci *
173462306a36Sopenharmony_ci * Called with ctlr_mutex held.  The caller must not hold ctlr_lock.
173562306a36Sopenharmony_ci */
173662306a36Sopenharmony_cistatic void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip)
173762306a36Sopenharmony_ci{
173862306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	spin_lock_bh(&fip->ctlr_lock);
174162306a36Sopenharmony_ci	fcf = fip->sel_fcf;
174262306a36Sopenharmony_ci	if (!fcf || !fip->flogi_req_send)
174362306a36Sopenharmony_ci		goto unlock;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "sending FLOGI\n");
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	/*
174862306a36Sopenharmony_ci	 * If this FLOGI is being sent due to a timeout retry
174962306a36Sopenharmony_ci	 * to the same FCF as before, select a different FCF if possible.
175062306a36Sopenharmony_ci	 */
175162306a36Sopenharmony_ci	if (fcf->flogi_sent) {
175262306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "sending FLOGI - reselect\n");
175362306a36Sopenharmony_ci		fcf = fcoe_ctlr_select(fip);
175462306a36Sopenharmony_ci		if (!fcf || fcf->flogi_sent) {
175562306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "sending FLOGI - clearing\n");
175662306a36Sopenharmony_ci			list_for_each_entry(fcf, &fip->fcfs, list)
175762306a36Sopenharmony_ci				fcf->flogi_sent = 0;
175862306a36Sopenharmony_ci			fcf = fcoe_ctlr_select(fip);
175962306a36Sopenharmony_ci		}
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci	if (fcf) {
176262306a36Sopenharmony_ci		fcoe_ctlr_flogi_send_locked(fip);
176362306a36Sopenharmony_ci		fip->flogi_req_send = 0;
176462306a36Sopenharmony_ci	} else /* XXX */
176562306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "No FCF selected - defer send\n");
176662306a36Sopenharmony_ciunlock:
176762306a36Sopenharmony_ci	spin_unlock_bh(&fip->ctlr_lock);
176862306a36Sopenharmony_ci}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci/**
177162306a36Sopenharmony_ci * fcoe_ctlr_timeout() - FIP timeout handler
177262306a36Sopenharmony_ci * @t: Timer context use to obtain the controller reference
177362306a36Sopenharmony_ci */
177462306a36Sopenharmony_cistatic void fcoe_ctlr_timeout(struct timer_list *t)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	struct fcoe_ctlr *fip = from_timer(fip, t, timer);
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	schedule_work(&fip->timer_work);
177962306a36Sopenharmony_ci}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci/**
178262306a36Sopenharmony_ci * fcoe_ctlr_timer_work() - Worker thread function for timer work
178362306a36Sopenharmony_ci * @work: Handle to a FCoE controller
178462306a36Sopenharmony_ci *
178562306a36Sopenharmony_ci * Ages FCFs.  Triggers FCF selection if possible.
178662306a36Sopenharmony_ci * Sends keep-alives and resets.
178762306a36Sopenharmony_ci */
178862306a36Sopenharmony_cistatic void fcoe_ctlr_timer_work(struct work_struct *work)
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	struct fcoe_ctlr *fip;
179162306a36Sopenharmony_ci	struct fc_lport *vport;
179262306a36Sopenharmony_ci	u8 *mac;
179362306a36Sopenharmony_ci	u8 reset = 0;
179462306a36Sopenharmony_ci	u8 send_ctlr_ka = 0;
179562306a36Sopenharmony_ci	u8 send_port_ka = 0;
179662306a36Sopenharmony_ci	struct fcoe_fcf *sel;
179762306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
179862306a36Sopenharmony_ci	unsigned long next_timer;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	fip = container_of(work, struct fcoe_ctlr, timer_work);
180162306a36Sopenharmony_ci	if (fip->mode == FIP_MODE_VN2VN)
180262306a36Sopenharmony_ci		return fcoe_ctlr_vn_timeout(fip);
180362306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
180462306a36Sopenharmony_ci	if (fip->state == FIP_ST_DISABLED) {
180562306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
180662306a36Sopenharmony_ci		return;
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	fcf = fip->sel_fcf;
181062306a36Sopenharmony_ci	next_timer = fcoe_ctlr_age_fcfs(fip);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	sel = fip->sel_fcf;
181362306a36Sopenharmony_ci	if (!sel && fip->sel_time) {
181462306a36Sopenharmony_ci		if (time_after_eq(jiffies, fip->sel_time)) {
181562306a36Sopenharmony_ci			sel = fcoe_ctlr_select(fip);
181662306a36Sopenharmony_ci			fip->sel_time = 0;
181762306a36Sopenharmony_ci		} else if (time_after(next_timer, fip->sel_time))
181862306a36Sopenharmony_ci			next_timer = fip->sel_time;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	if (sel && fip->flogi_req_send)
182262306a36Sopenharmony_ci		fcoe_ctlr_flogi_send(fip);
182362306a36Sopenharmony_ci	else if (!sel && fcf)
182462306a36Sopenharmony_ci		reset = 1;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	if (sel && !sel->fd_flags) {
182762306a36Sopenharmony_ci		if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
182862306a36Sopenharmony_ci			fip->ctlr_ka_time = jiffies + sel->fka_period;
182962306a36Sopenharmony_ci			send_ctlr_ka = 1;
183062306a36Sopenharmony_ci		}
183162306a36Sopenharmony_ci		if (time_after(next_timer, fip->ctlr_ka_time))
183262306a36Sopenharmony_ci			next_timer = fip->ctlr_ka_time;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci		if (time_after_eq(jiffies, fip->port_ka_time)) {
183562306a36Sopenharmony_ci			fip->port_ka_time = jiffies +
183662306a36Sopenharmony_ci				msecs_to_jiffies(FIP_VN_KA_PERIOD);
183762306a36Sopenharmony_ci			send_port_ka = 1;
183862306a36Sopenharmony_ci		}
183962306a36Sopenharmony_ci		if (time_after(next_timer, fip->port_ka_time))
184062306a36Sopenharmony_ci			next_timer = fip->port_ka_time;
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci	if (!list_empty(&fip->fcfs))
184362306a36Sopenharmony_ci		mod_timer(&fip->timer, next_timer);
184462306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	if (reset) {
184762306a36Sopenharmony_ci		fc_lport_reset(fip->lp);
184862306a36Sopenharmony_ci		/* restart things with a solicitation */
184962306a36Sopenharmony_ci		fcoe_ctlr_solicit(fip, NULL);
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (send_ctlr_ka)
185362306a36Sopenharmony_ci		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (send_port_ka) {
185662306a36Sopenharmony_ci		mutex_lock(&fip->lp->lp_mutex);
185762306a36Sopenharmony_ci		mac = fip->get_src_addr(fip->lp);
185862306a36Sopenharmony_ci		fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
185962306a36Sopenharmony_ci		list_for_each_entry(vport, &fip->lp->vports, list) {
186062306a36Sopenharmony_ci			mac = fip->get_src_addr(vport);
186162306a36Sopenharmony_ci			fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
186262306a36Sopenharmony_ci		}
186362306a36Sopenharmony_ci		mutex_unlock(&fip->lp->lp_mutex);
186462306a36Sopenharmony_ci	}
186562306a36Sopenharmony_ci}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci/**
186862306a36Sopenharmony_ci * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames
186962306a36Sopenharmony_ci * @recv_work: Handle to a FCoE controller
187062306a36Sopenharmony_ci */
187162306a36Sopenharmony_cistatic void fcoe_ctlr_recv_work(struct work_struct *recv_work)
187262306a36Sopenharmony_ci{
187362306a36Sopenharmony_ci	struct fcoe_ctlr *fip;
187462306a36Sopenharmony_ci	struct sk_buff *skb;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	fip = container_of(recv_work, struct fcoe_ctlr, recv_work);
187762306a36Sopenharmony_ci	while ((skb = skb_dequeue(&fip->fip_recv_list)))
187862306a36Sopenharmony_ci		fcoe_ctlr_recv_handler(fip, skb);
187962306a36Sopenharmony_ci}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci/**
188262306a36Sopenharmony_ci * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
188362306a36Sopenharmony_ci * @fip: The FCoE controller
188462306a36Sopenharmony_ci * @lport: The local port
188562306a36Sopenharmony_ci * @fp:	 The FC frame to snoop
188662306a36Sopenharmony_ci *
188762306a36Sopenharmony_ci * Snoop potential response to FLOGI or even incoming FLOGI.
188862306a36Sopenharmony_ci *
188962306a36Sopenharmony_ci * The caller has checked that we are waiting for login as indicated
189062306a36Sopenharmony_ci * by fip->flogi_oxid != FC_XID_UNKNOWN.
189162306a36Sopenharmony_ci *
189262306a36Sopenharmony_ci * The caller is responsible for freeing the frame.
189362306a36Sopenharmony_ci * Fill in the granted_mac address.
189462306a36Sopenharmony_ci *
189562306a36Sopenharmony_ci * Return non-zero if the frame should not be delivered to libfc.
189662306a36Sopenharmony_ci */
189762306a36Sopenharmony_ciint fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
189862306a36Sopenharmony_ci			 struct fc_frame *fp)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	struct fc_frame_header *fh;
190162306a36Sopenharmony_ci	u8 op;
190262306a36Sopenharmony_ci	u8 *sa;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	sa = eth_hdr(&fp->skb)->h_source;
190562306a36Sopenharmony_ci	fh = fc_frame_header_get(fp);
190662306a36Sopenharmony_ci	if (fh->fh_type != FC_TYPE_ELS)
190762306a36Sopenharmony_ci		return 0;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	op = fc_frame_payload_op(fp);
191062306a36Sopenharmony_ci	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
191162306a36Sopenharmony_ci	    fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci		mutex_lock(&fip->ctlr_mutex);
191462306a36Sopenharmony_ci		if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) {
191562306a36Sopenharmony_ci			mutex_unlock(&fip->ctlr_mutex);
191662306a36Sopenharmony_ci			return -EINVAL;
191762306a36Sopenharmony_ci		}
191862306a36Sopenharmony_ci		fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
191962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip,
192062306a36Sopenharmony_ci				"received FLOGI LS_ACC using non-FIP mode\n");
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci		/*
192362306a36Sopenharmony_ci		 * FLOGI accepted.
192462306a36Sopenharmony_ci		 * If the src mac addr is FC_OUI-based, then we mark the
192562306a36Sopenharmony_ci		 * address_mode flag to use FC_OUI-based Ethernet DA.
192662306a36Sopenharmony_ci		 * Otherwise we use the FCoE gateway addr
192762306a36Sopenharmony_ci		 */
192862306a36Sopenharmony_ci		if (ether_addr_equal(sa, (u8[6])FC_FCOE_FLOGI_MAC)) {
192962306a36Sopenharmony_ci			fcoe_ctlr_map_dest(fip);
193062306a36Sopenharmony_ci		} else {
193162306a36Sopenharmony_ci			memcpy(fip->dest_addr, sa, ETH_ALEN);
193262306a36Sopenharmony_ci			fip->map_dest = 0;
193362306a36Sopenharmony_ci		}
193462306a36Sopenharmony_ci		fip->flogi_oxid = FC_XID_UNKNOWN;
193562306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
193662306a36Sopenharmony_ci		fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
193762306a36Sopenharmony_ci	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
193862306a36Sopenharmony_ci		/*
193962306a36Sopenharmony_ci		 * Save source MAC for point-to-point responses.
194062306a36Sopenharmony_ci		 */
194162306a36Sopenharmony_ci		mutex_lock(&fip->ctlr_mutex);
194262306a36Sopenharmony_ci		if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) {
194362306a36Sopenharmony_ci			memcpy(fip->dest_addr, sa, ETH_ALEN);
194462306a36Sopenharmony_ci			fip->map_dest = 0;
194562306a36Sopenharmony_ci			if (fip->state == FIP_ST_AUTO)
194662306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. "
194762306a36Sopenharmony_ci						"Setting non-FIP mode\n");
194862306a36Sopenharmony_ci			fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
194962306a36Sopenharmony_ci		}
195062306a36Sopenharmony_ci		mutex_unlock(&fip->ctlr_mutex);
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci	return 0;
195362306a36Sopenharmony_ci}
195462306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci/**
195762306a36Sopenharmony_ci * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN
195862306a36Sopenharmony_ci * @mac:    The MAC address to convert
195962306a36Sopenharmony_ci * @scheme: The scheme to use when converting
196062306a36Sopenharmony_ci * @port:   The port indicator for converting
196162306a36Sopenharmony_ci *
196262306a36Sopenharmony_ci * Returns: u64 fc world wide name
196362306a36Sopenharmony_ci */
196462306a36Sopenharmony_ciu64 fcoe_wwn_from_mac(unsigned char mac[ETH_ALEN],
196562306a36Sopenharmony_ci		      unsigned int scheme, unsigned int port)
196662306a36Sopenharmony_ci{
196762306a36Sopenharmony_ci	u64 wwn;
196862306a36Sopenharmony_ci	u64 host_mac;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	/* The MAC is in NO, so flip only the low 48 bits */
197162306a36Sopenharmony_ci	host_mac = ((u64) mac[0] << 40) |
197262306a36Sopenharmony_ci		((u64) mac[1] << 32) |
197362306a36Sopenharmony_ci		((u64) mac[2] << 24) |
197462306a36Sopenharmony_ci		((u64) mac[3] << 16) |
197562306a36Sopenharmony_ci		((u64) mac[4] << 8) |
197662306a36Sopenharmony_ci		(u64) mac[5];
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	WARN_ON(host_mac >= (1ULL << 48));
197962306a36Sopenharmony_ci	wwn = host_mac | ((u64) scheme << 60);
198062306a36Sopenharmony_ci	switch (scheme) {
198162306a36Sopenharmony_ci	case 1:
198262306a36Sopenharmony_ci		WARN_ON(port != 0);
198362306a36Sopenharmony_ci		break;
198462306a36Sopenharmony_ci	case 2:
198562306a36Sopenharmony_ci		WARN_ON(port >= 0xfff);
198662306a36Sopenharmony_ci		wwn |= (u64) port << 48;
198762306a36Sopenharmony_ci		break;
198862306a36Sopenharmony_ci	default:
198962306a36Sopenharmony_ci		WARN_ON(1);
199062306a36Sopenharmony_ci		break;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	return wwn;
199462306a36Sopenharmony_ci}
199562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci/**
199862306a36Sopenharmony_ci * fcoe_ctlr_rport() - return the fcoe_rport for a given fc_rport_priv
199962306a36Sopenharmony_ci * @rdata: libfc remote port
200062306a36Sopenharmony_ci */
200162306a36Sopenharmony_cistatic inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata)
200262306a36Sopenharmony_ci{
200362306a36Sopenharmony_ci	return container_of(rdata, struct fcoe_rport, rdata);
200462306a36Sopenharmony_ci}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci/**
200762306a36Sopenharmony_ci * fcoe_ctlr_vn_send() - Send a FIP VN2VN Probe Request or Reply.
200862306a36Sopenharmony_ci * @fip: The FCoE controller
200962306a36Sopenharmony_ci * @sub: sub-opcode for probe request, reply, or advertisement.
201062306a36Sopenharmony_ci * @dest: The destination Ethernet MAC address
201162306a36Sopenharmony_ci * @min_len: minimum size of the Ethernet payload to be sent
201262306a36Sopenharmony_ci */
201362306a36Sopenharmony_cistatic void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
201462306a36Sopenharmony_ci			      enum fip_vn2vn_subcode sub,
201562306a36Sopenharmony_ci			      const u8 *dest, size_t min_len)
201662306a36Sopenharmony_ci{
201762306a36Sopenharmony_ci	struct sk_buff *skb;
201862306a36Sopenharmony_ci	struct fip_vn2vn_probe_frame {
201962306a36Sopenharmony_ci		struct ethhdr eth;
202062306a36Sopenharmony_ci		struct fip_header fip;
202162306a36Sopenharmony_ci		struct fip_mac_desc mac;
202262306a36Sopenharmony_ci		struct fip_wwn_desc wwnn;
202362306a36Sopenharmony_ci		struct fip_vn_desc vn;
202462306a36Sopenharmony_ci	} __packed * frame;
202562306a36Sopenharmony_ci	struct fip_fc4_feat *ff;
202662306a36Sopenharmony_ci	struct fip_size_desc *size;
202762306a36Sopenharmony_ci	u32 fcp_feat;
202862306a36Sopenharmony_ci	size_t len;
202962306a36Sopenharmony_ci	size_t dlen;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	len = sizeof(*frame);
203262306a36Sopenharmony_ci	dlen = 0;
203362306a36Sopenharmony_ci	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
203462306a36Sopenharmony_ci		dlen = sizeof(struct fip_fc4_feat) +
203562306a36Sopenharmony_ci		       sizeof(struct fip_size_desc);
203662306a36Sopenharmony_ci		len += dlen;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci	dlen += sizeof(frame->mac) + sizeof(frame->wwnn) + sizeof(frame->vn);
203962306a36Sopenharmony_ci	len = max(len, min_len + sizeof(struct ethhdr));
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	skb = dev_alloc_skb(len);
204262306a36Sopenharmony_ci	if (!skb)
204362306a36Sopenharmony_ci		return;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	frame = (struct fip_vn2vn_probe_frame *)skb->data;
204662306a36Sopenharmony_ci	memset(frame, 0, len);
204762306a36Sopenharmony_ci	memcpy(frame->eth.h_dest, dest, ETH_ALEN);
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	if (sub == FIP_SC_VN_BEACON) {
205062306a36Sopenharmony_ci		hton24(frame->eth.h_source, FIP_VN_FC_MAP);
205162306a36Sopenharmony_ci		hton24(frame->eth.h_source + 3, fip->port_id);
205262306a36Sopenharmony_ci	} else {
205362306a36Sopenharmony_ci		memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
205462306a36Sopenharmony_ci	}
205562306a36Sopenharmony_ci	frame->eth.h_proto = htons(ETH_P_FIP);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
205862306a36Sopenharmony_ci	frame->fip.fip_op = htons(FIP_OP_VN2VN);
205962306a36Sopenharmony_ci	frame->fip.fip_subcode = sub;
206062306a36Sopenharmony_ci	frame->fip.fip_dl_len = htons(dlen / FIP_BPW);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
206362306a36Sopenharmony_ci	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
206462306a36Sopenharmony_ci	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	frame->wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
206762306a36Sopenharmony_ci	frame->wwnn.fd_desc.fip_dlen = sizeof(frame->wwnn) / FIP_BPW;
206862306a36Sopenharmony_ci	put_unaligned_be64(fip->lp->wwnn, &frame->wwnn.fd_wwn);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	frame->vn.fd_desc.fip_dtype = FIP_DT_VN_ID;
207162306a36Sopenharmony_ci	frame->vn.fd_desc.fip_dlen = sizeof(frame->vn) / FIP_BPW;
207262306a36Sopenharmony_ci	hton24(frame->vn.fd_mac, FIP_VN_FC_MAP);
207362306a36Sopenharmony_ci	hton24(frame->vn.fd_mac + 3, fip->port_id);
207462306a36Sopenharmony_ci	hton24(frame->vn.fd_fc_id, fip->port_id);
207562306a36Sopenharmony_ci	put_unaligned_be64(fip->lp->wwpn, &frame->vn.fd_wwpn);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	/*
207862306a36Sopenharmony_ci	 * For claims, add FC-4 features.
207962306a36Sopenharmony_ci	 * TBD: Add interface to get fc-4 types and features from libfc.
208062306a36Sopenharmony_ci	 */
208162306a36Sopenharmony_ci	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
208262306a36Sopenharmony_ci		ff = (struct fip_fc4_feat *)(frame + 1);
208362306a36Sopenharmony_ci		ff->fd_desc.fip_dtype = FIP_DT_FC4F;
208462306a36Sopenharmony_ci		ff->fd_desc.fip_dlen = sizeof(*ff) / FIP_BPW;
208562306a36Sopenharmony_ci		ff->fd_fts = fip->lp->fcts;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci		fcp_feat = 0;
208862306a36Sopenharmony_ci		if (fip->lp->service_params & FCP_SPPF_INIT_FCN)
208962306a36Sopenharmony_ci			fcp_feat |= FCP_FEAT_INIT;
209062306a36Sopenharmony_ci		if (fip->lp->service_params & FCP_SPPF_TARG_FCN)
209162306a36Sopenharmony_ci			fcp_feat |= FCP_FEAT_TARG;
209262306a36Sopenharmony_ci		fcp_feat <<= (FC_TYPE_FCP * 4) % 32;
209362306a36Sopenharmony_ci		ff->fd_ff.fd_feat[FC_TYPE_FCP * 4 / 32] = htonl(fcp_feat);
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci		size = (struct fip_size_desc *)(ff + 1);
209662306a36Sopenharmony_ci		size->fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
209762306a36Sopenharmony_ci		size->fd_desc.fip_dlen = sizeof(*size) / FIP_BPW;
209862306a36Sopenharmony_ci		size->fd_size = htons(fcoe_ctlr_fcoe_size(fip));
209962306a36Sopenharmony_ci	}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	skb_put(skb, len);
210262306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
210362306a36Sopenharmony_ci	skb->priority = fip->priority;
210462306a36Sopenharmony_ci	skb_reset_mac_header(skb);
210562306a36Sopenharmony_ci	skb_reset_network_header(skb);
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	fip->send(fip, skb);
210862306a36Sopenharmony_ci}
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci/**
211162306a36Sopenharmony_ci * fcoe_ctlr_vn_rport_callback - Event handler for rport events.
211262306a36Sopenharmony_ci * @lport: The lport which is receiving the event
211362306a36Sopenharmony_ci * @rdata: remote port private data
211462306a36Sopenharmony_ci * @event: The event that occurred
211562306a36Sopenharmony_ci *
211662306a36Sopenharmony_ci * Locking Note:  The rport lock must not be held when calling this function.
211762306a36Sopenharmony_ci */
211862306a36Sopenharmony_cistatic void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
211962306a36Sopenharmony_ci					struct fc_rport_priv *rdata,
212062306a36Sopenharmony_ci					enum fc_rport_event event)
212162306a36Sopenharmony_ci{
212262306a36Sopenharmony_ci	struct fcoe_ctlr *fip = lport->disc.priv;
212362306a36Sopenharmony_ci	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n",
212662306a36Sopenharmony_ci			rdata->ids.port_id, event);
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
212962306a36Sopenharmony_ci	switch (event) {
213062306a36Sopenharmony_ci	case RPORT_EV_READY:
213162306a36Sopenharmony_ci		frport->login_count = 0;
213262306a36Sopenharmony_ci		break;
213362306a36Sopenharmony_ci	case RPORT_EV_LOGO:
213462306a36Sopenharmony_ci	case RPORT_EV_FAILED:
213562306a36Sopenharmony_ci	case RPORT_EV_STOP:
213662306a36Sopenharmony_ci		frport->login_count++;
213762306a36Sopenharmony_ci		if (frport->login_count > FCOE_CTLR_VN2VN_LOGIN_LIMIT) {
213862306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip,
213962306a36Sopenharmony_ci					"rport FLOGI limited port_id %6.6x\n",
214062306a36Sopenharmony_ci					rdata->ids.port_id);
214162306a36Sopenharmony_ci			fc_rport_logoff(rdata);
214262306a36Sopenharmony_ci		}
214362306a36Sopenharmony_ci		break;
214462306a36Sopenharmony_ci	default:
214562306a36Sopenharmony_ci		break;
214662306a36Sopenharmony_ci	}
214762306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
214862306a36Sopenharmony_ci}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_cistatic struct fc_rport_operations fcoe_ctlr_vn_rport_ops = {
215162306a36Sopenharmony_ci	.event_callback = fcoe_ctlr_vn_rport_callback,
215262306a36Sopenharmony_ci};
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci/**
215562306a36Sopenharmony_ci * fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode
215662306a36Sopenharmony_ci * @lport: The local port
215762306a36Sopenharmony_ci *
215862306a36Sopenharmony_ci * Called with ctlr_mutex held.
215962306a36Sopenharmony_ci */
216062306a36Sopenharmony_cistatic void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
216162306a36Sopenharmony_ci{
216262306a36Sopenharmony_ci	struct fc_rport_priv *rdata;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	mutex_lock(&lport->disc.disc_mutex);
216562306a36Sopenharmony_ci	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
216662306a36Sopenharmony_ci		if (kref_get_unless_zero(&rdata->kref)) {
216762306a36Sopenharmony_ci			fc_rport_logoff(rdata);
216862306a36Sopenharmony_ci			kref_put(&rdata->kref, fc_rport_destroy);
216962306a36Sopenharmony_ci		}
217062306a36Sopenharmony_ci	}
217162306a36Sopenharmony_ci	lport->disc.disc_callback = NULL;
217262306a36Sopenharmony_ci	mutex_unlock(&lport->disc.disc_mutex);
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci/**
217662306a36Sopenharmony_ci * fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode
217762306a36Sopenharmony_ci * @lport: The local port
217862306a36Sopenharmony_ci *
217962306a36Sopenharmony_ci * Called through the local port template for discovery.
218062306a36Sopenharmony_ci * Called without the ctlr_mutex held.
218162306a36Sopenharmony_ci */
218262306a36Sopenharmony_cistatic void fcoe_ctlr_disc_stop(struct fc_lport *lport)
218362306a36Sopenharmony_ci{
218462306a36Sopenharmony_ci	struct fcoe_ctlr *fip = lport->disc.priv;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
218762306a36Sopenharmony_ci	fcoe_ctlr_disc_stop_locked(lport);
218862306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
218962306a36Sopenharmony_ci}
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci/**
219262306a36Sopenharmony_ci * fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode
219362306a36Sopenharmony_ci * @lport: The local port
219462306a36Sopenharmony_ci *
219562306a36Sopenharmony_ci * Called through the local port template for discovery.
219662306a36Sopenharmony_ci * Called without the ctlr_mutex held.
219762306a36Sopenharmony_ci */
219862306a36Sopenharmony_cistatic void fcoe_ctlr_disc_stop_final(struct fc_lport *lport)
219962306a36Sopenharmony_ci{
220062306a36Sopenharmony_ci	fcoe_ctlr_disc_stop(lport);
220162306a36Sopenharmony_ci	fc_rport_flush_queue();
220262306a36Sopenharmony_ci	synchronize_rcu();
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci/**
220662306a36Sopenharmony_ci * fcoe_ctlr_vn_restart() - VN2VN probe restart with new port_id
220762306a36Sopenharmony_ci * @fip: The FCoE controller
220862306a36Sopenharmony_ci *
220962306a36Sopenharmony_ci * Called with fcoe_ctlr lock held.
221062306a36Sopenharmony_ci */
221162306a36Sopenharmony_cistatic void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
221262306a36Sopenharmony_ci{
221362306a36Sopenharmony_ci	unsigned long wait;
221462306a36Sopenharmony_ci	u32 port_id;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	fcoe_ctlr_disc_stop_locked(fip->lp);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	/*
221962306a36Sopenharmony_ci	 * Get proposed port ID.
222062306a36Sopenharmony_ci	 * If this is the first try after link up, use any previous port_id.
222162306a36Sopenharmony_ci	 * If there was none, use the low bits of the port_name.
222262306a36Sopenharmony_ci	 * On subsequent tries, get the next random one.
222362306a36Sopenharmony_ci	 * Don't use reserved IDs, use another non-zero value, just as random.
222462306a36Sopenharmony_ci	 */
222562306a36Sopenharmony_ci	port_id = fip->port_id;
222662306a36Sopenharmony_ci	if (fip->probe_tries)
222762306a36Sopenharmony_ci		port_id = prandom_u32_state(&fip->rnd_state) & 0xffff;
222862306a36Sopenharmony_ci	else if (!port_id)
222962306a36Sopenharmony_ci		port_id = fip->lp->wwpn & 0xffff;
223062306a36Sopenharmony_ci	if (!port_id || port_id == 0xffff)
223162306a36Sopenharmony_ci		port_id = 1;
223262306a36Sopenharmony_ci	fip->port_id = port_id;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	if (fip->probe_tries < FIP_VN_RLIM_COUNT) {
223562306a36Sopenharmony_ci		fip->probe_tries++;
223662306a36Sopenharmony_ci		wait = get_random_u32_below(FIP_VN_PROBE_WAIT);
223762306a36Sopenharmony_ci	} else
223862306a36Sopenharmony_ci		wait = FIP_VN_RLIM_INT;
223962306a36Sopenharmony_ci	mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait));
224062306a36Sopenharmony_ci	fcoe_ctlr_set_state(fip, FIP_ST_VNMP_START);
224162306a36Sopenharmony_ci}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci/**
224462306a36Sopenharmony_ci * fcoe_ctlr_vn_start() - Start in VN2VN mode
224562306a36Sopenharmony_ci * @fip: The FCoE controller
224662306a36Sopenharmony_ci *
224762306a36Sopenharmony_ci * Called with fcoe_ctlr lock held.
224862306a36Sopenharmony_ci */
224962306a36Sopenharmony_cistatic void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip)
225062306a36Sopenharmony_ci{
225162306a36Sopenharmony_ci	fip->probe_tries = 0;
225262306a36Sopenharmony_ci	prandom_seed_state(&fip->rnd_state, fip->lp->wwpn);
225362306a36Sopenharmony_ci	fcoe_ctlr_vn_restart(fip);
225462306a36Sopenharmony_ci}
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci/**
225762306a36Sopenharmony_ci * fcoe_ctlr_vn_parse - parse probe request or response
225862306a36Sopenharmony_ci * @fip: The FCoE controller
225962306a36Sopenharmony_ci * @skb: incoming packet
226062306a36Sopenharmony_ci * @frport: parsed FCoE rport from the probe request
226162306a36Sopenharmony_ci *
226262306a36Sopenharmony_ci * Returns non-zero error number on error.
226362306a36Sopenharmony_ci * Does not consume the packet.
226462306a36Sopenharmony_ci */
226562306a36Sopenharmony_cistatic int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
226662306a36Sopenharmony_ci			      struct sk_buff *skb,
226762306a36Sopenharmony_ci			      struct fcoe_rport *frport)
226862306a36Sopenharmony_ci{
226962306a36Sopenharmony_ci	struct fip_header *fiph;
227062306a36Sopenharmony_ci	struct fip_desc *desc = NULL;
227162306a36Sopenharmony_ci	struct fip_mac_desc *macd = NULL;
227262306a36Sopenharmony_ci	struct fip_wwn_desc *wwn = NULL;
227362306a36Sopenharmony_ci	struct fip_vn_desc *vn = NULL;
227462306a36Sopenharmony_ci	struct fip_size_desc *size = NULL;
227562306a36Sopenharmony_ci	size_t rlen;
227662306a36Sopenharmony_ci	size_t dlen;
227762306a36Sopenharmony_ci	u32 desc_mask = 0;
227862306a36Sopenharmony_ci	u32 dtype;
227962306a36Sopenharmony_ci	u8 sub;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
228262306a36Sopenharmony_ci	frport->flags = ntohs(fiph->fip_flags);
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	sub = fiph->fip_subcode;
228562306a36Sopenharmony_ci	switch (sub) {
228662306a36Sopenharmony_ci	case FIP_SC_VN_PROBE_REQ:
228762306a36Sopenharmony_ci	case FIP_SC_VN_PROBE_REP:
228862306a36Sopenharmony_ci	case FIP_SC_VN_BEACON:
228962306a36Sopenharmony_ci		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
229062306a36Sopenharmony_ci			    BIT(FIP_DT_VN_ID);
229162306a36Sopenharmony_ci		break;
229262306a36Sopenharmony_ci	case FIP_SC_VN_CLAIM_NOTIFY:
229362306a36Sopenharmony_ci	case FIP_SC_VN_CLAIM_REP:
229462306a36Sopenharmony_ci		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
229562306a36Sopenharmony_ci			    BIT(FIP_DT_VN_ID) | BIT(FIP_DT_FC4F) |
229662306a36Sopenharmony_ci			    BIT(FIP_DT_FCOE_SIZE);
229762306a36Sopenharmony_ci		break;
229862306a36Sopenharmony_ci	default:
229962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
230062306a36Sopenharmony_ci		return -EINVAL;
230162306a36Sopenharmony_ci	}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
230462306a36Sopenharmony_ci	if (rlen + sizeof(*fiph) > skb->len)
230562306a36Sopenharmony_ci		return -EINVAL;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
230862306a36Sopenharmony_ci	while (rlen > 0) {
230962306a36Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
231062306a36Sopenharmony_ci		if (dlen < sizeof(*desc) || dlen > rlen)
231162306a36Sopenharmony_ci			return -EINVAL;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci		dtype = desc->fip_dtype;
231462306a36Sopenharmony_ci		if (dtype < 32) {
231562306a36Sopenharmony_ci			if (!(desc_mask & BIT(dtype))) {
231662306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip,
231762306a36Sopenharmony_ci						"unexpected or duplicated desc "
231862306a36Sopenharmony_ci						"desc type %u in "
231962306a36Sopenharmony_ci						"FIP VN2VN subtype %u\n",
232062306a36Sopenharmony_ci						dtype, sub);
232162306a36Sopenharmony_ci				return -EINVAL;
232262306a36Sopenharmony_ci			}
232362306a36Sopenharmony_ci			desc_mask &= ~BIT(dtype);
232462306a36Sopenharmony_ci		}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci		switch (dtype) {
232762306a36Sopenharmony_ci		case FIP_DT_MAC:
232862306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_mac_desc))
232962306a36Sopenharmony_ci				goto len_err;
233062306a36Sopenharmony_ci			macd = (struct fip_mac_desc *)desc;
233162306a36Sopenharmony_ci			if (!is_valid_ether_addr(macd->fd_mac)) {
233262306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip,
233362306a36Sopenharmony_ci					"Invalid MAC addr %pM in FIP VN2VN\n",
233462306a36Sopenharmony_ci					 macd->fd_mac);
233562306a36Sopenharmony_ci				return -EINVAL;
233662306a36Sopenharmony_ci			}
233762306a36Sopenharmony_ci			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
233862306a36Sopenharmony_ci			break;
233962306a36Sopenharmony_ci		case FIP_DT_NAME:
234062306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_wwn_desc))
234162306a36Sopenharmony_ci				goto len_err;
234262306a36Sopenharmony_ci			wwn = (struct fip_wwn_desc *)desc;
234362306a36Sopenharmony_ci			frport->rdata.ids.node_name =
234462306a36Sopenharmony_ci				get_unaligned_be64(&wwn->fd_wwn);
234562306a36Sopenharmony_ci			break;
234662306a36Sopenharmony_ci		case FIP_DT_VN_ID:
234762306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_vn_desc))
234862306a36Sopenharmony_ci				goto len_err;
234962306a36Sopenharmony_ci			vn = (struct fip_vn_desc *)desc;
235062306a36Sopenharmony_ci			memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN);
235162306a36Sopenharmony_ci			frport->rdata.ids.port_id = ntoh24(vn->fd_fc_id);
235262306a36Sopenharmony_ci			frport->rdata.ids.port_name =
235362306a36Sopenharmony_ci				get_unaligned_be64(&vn->fd_wwpn);
235462306a36Sopenharmony_ci			break;
235562306a36Sopenharmony_ci		case FIP_DT_FC4F:
235662306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_fc4_feat))
235762306a36Sopenharmony_ci				goto len_err;
235862306a36Sopenharmony_ci			break;
235962306a36Sopenharmony_ci		case FIP_DT_FCOE_SIZE:
236062306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_size_desc))
236162306a36Sopenharmony_ci				goto len_err;
236262306a36Sopenharmony_ci			size = (struct fip_size_desc *)desc;
236362306a36Sopenharmony_ci			frport->fcoe_len = ntohs(size->fd_size);
236462306a36Sopenharmony_ci			break;
236562306a36Sopenharmony_ci		default:
236662306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
236762306a36Sopenharmony_ci					"in FIP probe\n", dtype);
236862306a36Sopenharmony_ci			/* standard says ignore unknown descriptors >= 128 */
236962306a36Sopenharmony_ci			if (dtype < FIP_DT_NON_CRITICAL)
237062306a36Sopenharmony_ci				return -EINVAL;
237162306a36Sopenharmony_ci			break;
237262306a36Sopenharmony_ci		}
237362306a36Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
237462306a36Sopenharmony_ci		rlen -= dlen;
237562306a36Sopenharmony_ci	}
237662306a36Sopenharmony_ci	return 0;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_cilen_err:
237962306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
238062306a36Sopenharmony_ci			dtype, dlen);
238162306a36Sopenharmony_ci	return -EINVAL;
238262306a36Sopenharmony_ci}
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci/**
238562306a36Sopenharmony_ci * fcoe_ctlr_vn_send_claim() - send multicast FIP VN2VN Claim Notification.
238662306a36Sopenharmony_ci * @fip: The FCoE controller
238762306a36Sopenharmony_ci *
238862306a36Sopenharmony_ci * Called with ctlr_mutex held.
238962306a36Sopenharmony_ci */
239062306a36Sopenharmony_cistatic void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip)
239162306a36Sopenharmony_ci{
239262306a36Sopenharmony_ci	fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_NOTIFY, fcoe_all_vn2vn, 0);
239362306a36Sopenharmony_ci	fip->sol_time = jiffies;
239462306a36Sopenharmony_ci}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci/**
239762306a36Sopenharmony_ci * fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request.
239862306a36Sopenharmony_ci * @fip: The FCoE controller
239962306a36Sopenharmony_ci * @frport: parsed FCoE rport from the probe request
240062306a36Sopenharmony_ci *
240162306a36Sopenharmony_ci * Called with ctlr_mutex held.
240262306a36Sopenharmony_ci */
240362306a36Sopenharmony_cistatic void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
240462306a36Sopenharmony_ci				   struct fcoe_rport *frport)
240562306a36Sopenharmony_ci{
240662306a36Sopenharmony_ci	if (frport->rdata.ids.port_id != fip->port_id)
240762306a36Sopenharmony_ci		return;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	switch (fip->state) {
241062306a36Sopenharmony_ci	case FIP_ST_VNMP_CLAIM:
241162306a36Sopenharmony_ci	case FIP_ST_VNMP_UP:
241262306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_probe_req: send reply, state %x\n",
241362306a36Sopenharmony_ci				fip->state);
241462306a36Sopenharmony_ci		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
241562306a36Sopenharmony_ci				  frport->enode_mac, 0);
241662306a36Sopenharmony_ci		break;
241762306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE1:
241862306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE2:
241962306a36Sopenharmony_ci		/*
242062306a36Sopenharmony_ci		 * Decide whether to reply to the Probe.
242162306a36Sopenharmony_ci		 * Our selected address is never a "recorded" one, so
242262306a36Sopenharmony_ci		 * only reply if our WWPN is greater and the
242362306a36Sopenharmony_ci		 * Probe's REC bit is not set.
242462306a36Sopenharmony_ci		 * If we don't reply, we will change our address.
242562306a36Sopenharmony_ci		 */
242662306a36Sopenharmony_ci		if (fip->lp->wwpn > frport->rdata.ids.port_name &&
242762306a36Sopenharmony_ci		    !(frport->flags & FIP_FL_REC_OR_P2P)) {
242862306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "vn_probe_req: "
242962306a36Sopenharmony_ci					"port_id collision\n");
243062306a36Sopenharmony_ci			fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
243162306a36Sopenharmony_ci					  frport->enode_mac, 0);
243262306a36Sopenharmony_ci			break;
243362306a36Sopenharmony_ci		}
243462306a36Sopenharmony_ci		fallthrough;
243562306a36Sopenharmony_ci	case FIP_ST_VNMP_START:
243662306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_probe_req: "
243762306a36Sopenharmony_ci				"restart VN2VN negotiation\n");
243862306a36Sopenharmony_ci		fcoe_ctlr_vn_restart(fip);
243962306a36Sopenharmony_ci		break;
244062306a36Sopenharmony_ci	default:
244162306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_probe_req: ignore state %x\n",
244262306a36Sopenharmony_ci				fip->state);
244362306a36Sopenharmony_ci		break;
244462306a36Sopenharmony_ci	}
244562306a36Sopenharmony_ci}
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci/**
244862306a36Sopenharmony_ci * fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply.
244962306a36Sopenharmony_ci * @fip: The FCoE controller
245062306a36Sopenharmony_ci * @frport: parsed FCoE rport from the probe request
245162306a36Sopenharmony_ci *
245262306a36Sopenharmony_ci * Called with ctlr_mutex held.
245362306a36Sopenharmony_ci */
245462306a36Sopenharmony_cistatic void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip,
245562306a36Sopenharmony_ci				     struct fcoe_rport *frport)
245662306a36Sopenharmony_ci{
245762306a36Sopenharmony_ci	if (frport->rdata.ids.port_id != fip->port_id)
245862306a36Sopenharmony_ci		return;
245962306a36Sopenharmony_ci	switch (fip->state) {
246062306a36Sopenharmony_ci	case FIP_ST_VNMP_START:
246162306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE1:
246262306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE2:
246362306a36Sopenharmony_ci	case FIP_ST_VNMP_CLAIM:
246462306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_probe_reply: restart state %x\n",
246562306a36Sopenharmony_ci				fip->state);
246662306a36Sopenharmony_ci		fcoe_ctlr_vn_restart(fip);
246762306a36Sopenharmony_ci		break;
246862306a36Sopenharmony_ci	case FIP_ST_VNMP_UP:
246962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_probe_reply: send claim notify\n");
247062306a36Sopenharmony_ci		fcoe_ctlr_vn_send_claim(fip);
247162306a36Sopenharmony_ci		break;
247262306a36Sopenharmony_ci	default:
247362306a36Sopenharmony_ci		break;
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci/**
247862306a36Sopenharmony_ci * fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply.
247962306a36Sopenharmony_ci * @fip: The FCoE controller
248062306a36Sopenharmony_ci * @new: newly-parsed FCoE rport as a template for new rdata
248162306a36Sopenharmony_ci *
248262306a36Sopenharmony_ci * Called with ctlr_mutex held.
248362306a36Sopenharmony_ci */
248462306a36Sopenharmony_cistatic void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fcoe_rport *new)
248562306a36Sopenharmony_ci{
248662306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
248762306a36Sopenharmony_ci	struct fc_rport_priv *rdata;
248862306a36Sopenharmony_ci	struct fc_rport_identifiers *ids;
248962306a36Sopenharmony_ci	struct fcoe_rport *frport;
249062306a36Sopenharmony_ci	u32 port_id;
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	port_id = new->rdata.ids.port_id;
249362306a36Sopenharmony_ci	if (port_id == fip->port_id)
249462306a36Sopenharmony_ci		return;
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci	mutex_lock(&lport->disc.disc_mutex);
249762306a36Sopenharmony_ci	rdata = fc_rport_create(lport, port_id);
249862306a36Sopenharmony_ci	if (!rdata) {
249962306a36Sopenharmony_ci		mutex_unlock(&lport->disc.disc_mutex);
250062306a36Sopenharmony_ci		return;
250162306a36Sopenharmony_ci	}
250262306a36Sopenharmony_ci	mutex_lock(&rdata->rp_mutex);
250362306a36Sopenharmony_ci	mutex_unlock(&lport->disc.disc_mutex);
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	rdata->ops = &fcoe_ctlr_vn_rport_ops;
250662306a36Sopenharmony_ci	rdata->disc_id = lport->disc.disc_id;
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	ids = &rdata->ids;
250962306a36Sopenharmony_ci	if ((ids->port_name != -1 &&
251062306a36Sopenharmony_ci	     ids->port_name != new->rdata.ids.port_name) ||
251162306a36Sopenharmony_ci	    (ids->node_name != -1 &&
251262306a36Sopenharmony_ci	     ids->node_name != new->rdata.ids.node_name)) {
251362306a36Sopenharmony_ci		mutex_unlock(&rdata->rp_mutex);
251462306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_add rport logoff %6.6x\n", port_id);
251562306a36Sopenharmony_ci		fc_rport_logoff(rdata);
251662306a36Sopenharmony_ci		mutex_lock(&rdata->rp_mutex);
251762306a36Sopenharmony_ci	}
251862306a36Sopenharmony_ci	ids->port_name = new->rdata.ids.port_name;
251962306a36Sopenharmony_ci	ids->node_name = new->rdata.ids.node_name;
252062306a36Sopenharmony_ci	mutex_unlock(&rdata->rp_mutex);
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	frport = fcoe_ctlr_rport(rdata);
252362306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s state %d\n",
252462306a36Sopenharmony_ci			port_id, frport->fcoe_len ? "old" : "new",
252562306a36Sopenharmony_ci			rdata->rp_state);
252662306a36Sopenharmony_ci	frport->fcoe_len = new->fcoe_len;
252762306a36Sopenharmony_ci	frport->flags = new->flags;
252862306a36Sopenharmony_ci	frport->login_count = new->login_count;
252962306a36Sopenharmony_ci	memcpy(frport->enode_mac, new->enode_mac, ETH_ALEN);
253062306a36Sopenharmony_ci	memcpy(frport->vn_mac, new->vn_mac, ETH_ALEN);
253162306a36Sopenharmony_ci	frport->time = 0;
253262306a36Sopenharmony_ci}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci/**
253562306a36Sopenharmony_ci * fcoe_ctlr_vn_lookup() - Find VN remote port's MAC address
253662306a36Sopenharmony_ci * @fip: The FCoE controller
253762306a36Sopenharmony_ci * @port_id:  The port_id of the remote VN_node
253862306a36Sopenharmony_ci * @mac: buffer which will hold the VN_NODE destination MAC address, if found.
253962306a36Sopenharmony_ci *
254062306a36Sopenharmony_ci * Returns non-zero error if no remote port found.
254162306a36Sopenharmony_ci */
254262306a36Sopenharmony_cistatic int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac)
254362306a36Sopenharmony_ci{
254462306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
254562306a36Sopenharmony_ci	struct fc_rport_priv *rdata;
254662306a36Sopenharmony_ci	struct fcoe_rport *frport;
254762306a36Sopenharmony_ci	int ret = -1;
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	rdata = fc_rport_lookup(lport, port_id);
255062306a36Sopenharmony_ci	if (rdata) {
255162306a36Sopenharmony_ci		frport = fcoe_ctlr_rport(rdata);
255262306a36Sopenharmony_ci		memcpy(mac, frport->enode_mac, ETH_ALEN);
255362306a36Sopenharmony_ci		ret = 0;
255462306a36Sopenharmony_ci		kref_put(&rdata->kref, fc_rport_destroy);
255562306a36Sopenharmony_ci	}
255662306a36Sopenharmony_ci	return ret;
255762306a36Sopenharmony_ci}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci/**
256062306a36Sopenharmony_ci * fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification
256162306a36Sopenharmony_ci * @fip: The FCoE controller
256262306a36Sopenharmony_ci * @new: newly-parsed FCoE rport as a template for new rdata
256362306a36Sopenharmony_ci *
256462306a36Sopenharmony_ci * Called with ctlr_mutex held.
256562306a36Sopenharmony_ci */
256662306a36Sopenharmony_cistatic void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
256762306a36Sopenharmony_ci				      struct fcoe_rport *new)
256862306a36Sopenharmony_ci{
256962306a36Sopenharmony_ci	if (new->flags & FIP_FL_REC_OR_P2P) {
257062306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "send probe req for P2P/REC\n");
257162306a36Sopenharmony_ci		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
257262306a36Sopenharmony_ci		return;
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci	switch (fip->state) {
257562306a36Sopenharmony_ci	case FIP_ST_VNMP_START:
257662306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE1:
257762306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE2:
257862306a36Sopenharmony_ci		if (new->rdata.ids.port_id == fip->port_id) {
257962306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
258062306a36Sopenharmony_ci					"restart, state %d\n",
258162306a36Sopenharmony_ci					fip->state);
258262306a36Sopenharmony_ci			fcoe_ctlr_vn_restart(fip);
258362306a36Sopenharmony_ci		}
258462306a36Sopenharmony_ci		break;
258562306a36Sopenharmony_ci	case FIP_ST_VNMP_CLAIM:
258662306a36Sopenharmony_ci	case FIP_ST_VNMP_UP:
258762306a36Sopenharmony_ci		if (new->rdata.ids.port_id == fip->port_id) {
258862306a36Sopenharmony_ci			if (new->rdata.ids.port_name > fip->lp->wwpn) {
258962306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
259062306a36Sopenharmony_ci						"restart, port_id collision\n");
259162306a36Sopenharmony_ci				fcoe_ctlr_vn_restart(fip);
259262306a36Sopenharmony_ci				break;
259362306a36Sopenharmony_ci			}
259462306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
259562306a36Sopenharmony_ci					"send claim notify\n");
259662306a36Sopenharmony_ci			fcoe_ctlr_vn_send_claim(fip);
259762306a36Sopenharmony_ci			break;
259862306a36Sopenharmony_ci		}
259962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_claim_notify: send reply to %x\n",
260062306a36Sopenharmony_ci				new->rdata.ids.port_id);
260162306a36Sopenharmony_ci		fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, new->enode_mac,
260262306a36Sopenharmony_ci				  min((u32)new->fcoe_len,
260362306a36Sopenharmony_ci				      fcoe_ctlr_fcoe_size(fip)));
260462306a36Sopenharmony_ci		fcoe_ctlr_vn_add(fip, new);
260562306a36Sopenharmony_ci		break;
260662306a36Sopenharmony_ci	default:
260762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
260862306a36Sopenharmony_ci				"ignoring claim from %x\n",
260962306a36Sopenharmony_ci				new->rdata.ids.port_id);
261062306a36Sopenharmony_ci		break;
261162306a36Sopenharmony_ci	}
261262306a36Sopenharmony_ci}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci/**
261562306a36Sopenharmony_ci * fcoe_ctlr_vn_claim_resp() - handle received Claim Response
261662306a36Sopenharmony_ci * @fip: The FCoE controller that received the frame
261762306a36Sopenharmony_ci * @new: newly-parsed FCoE rport from the Claim Response
261862306a36Sopenharmony_ci *
261962306a36Sopenharmony_ci * Called with ctlr_mutex held.
262062306a36Sopenharmony_ci */
262162306a36Sopenharmony_cistatic void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip,
262262306a36Sopenharmony_ci				    struct fcoe_rport *new)
262362306a36Sopenharmony_ci{
262462306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n",
262562306a36Sopenharmony_ci			new->rdata.ids.port_id, fcoe_ctlr_state(fip->state));
262662306a36Sopenharmony_ci	if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM)
262762306a36Sopenharmony_ci		fcoe_ctlr_vn_add(fip, new);
262862306a36Sopenharmony_ci}
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci/**
263162306a36Sopenharmony_ci * fcoe_ctlr_vn_beacon() - handle received beacon.
263262306a36Sopenharmony_ci * @fip: The FCoE controller that received the frame
263362306a36Sopenharmony_ci * @new: newly-parsed FCoE rport from the Beacon
263462306a36Sopenharmony_ci *
263562306a36Sopenharmony_ci * Called with ctlr_mutex held.
263662306a36Sopenharmony_ci */
263762306a36Sopenharmony_cistatic void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip,
263862306a36Sopenharmony_ci				struct fcoe_rport *new)
263962306a36Sopenharmony_ci{
264062306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
264162306a36Sopenharmony_ci	struct fc_rport_priv *rdata;
264262306a36Sopenharmony_ci	struct fcoe_rport *frport;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	if (new->flags & FIP_FL_REC_OR_P2P) {
264562306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "p2p beacon while in vn2vn mode\n");
264662306a36Sopenharmony_ci		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
264762306a36Sopenharmony_ci		return;
264862306a36Sopenharmony_ci	}
264962306a36Sopenharmony_ci	rdata = fc_rport_lookup(lport, new->rdata.ids.port_id);
265062306a36Sopenharmony_ci	if (rdata) {
265162306a36Sopenharmony_ci		if (rdata->ids.node_name == new->rdata.ids.node_name &&
265262306a36Sopenharmony_ci		    rdata->ids.port_name == new->rdata.ids.port_name) {
265362306a36Sopenharmony_ci			frport = fcoe_ctlr_rport(rdata);
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "beacon from rport %x\n",
265662306a36Sopenharmony_ci					rdata->ids.port_id);
265762306a36Sopenharmony_ci			if (!frport->time && fip->state == FIP_ST_VNMP_UP) {
265862306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip, "beacon expired "
265962306a36Sopenharmony_ci						"for rport %x\n",
266062306a36Sopenharmony_ci						rdata->ids.port_id);
266162306a36Sopenharmony_ci				fc_rport_login(rdata);
266262306a36Sopenharmony_ci			}
266362306a36Sopenharmony_ci			frport->time = jiffies;
266462306a36Sopenharmony_ci		}
266562306a36Sopenharmony_ci		kref_put(&rdata->kref, fc_rport_destroy);
266662306a36Sopenharmony_ci		return;
266762306a36Sopenharmony_ci	}
266862306a36Sopenharmony_ci	if (fip->state != FIP_ST_VNMP_UP)
266962306a36Sopenharmony_ci		return;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	/*
267262306a36Sopenharmony_ci	 * Beacon from a new neighbor.
267362306a36Sopenharmony_ci	 * Send a claim notify if one hasn't been sent recently.
267462306a36Sopenharmony_ci	 * Don't add the neighbor yet.
267562306a36Sopenharmony_ci	 */
267662306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n",
267762306a36Sopenharmony_ci			new->rdata.ids.port_id);
267862306a36Sopenharmony_ci	if (time_after(jiffies,
267962306a36Sopenharmony_ci		       fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT)))
268062306a36Sopenharmony_ci		fcoe_ctlr_vn_send_claim(fip);
268162306a36Sopenharmony_ci}
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci/**
268462306a36Sopenharmony_ci * fcoe_ctlr_vn_age() - Check for VN_ports without recent beacons
268562306a36Sopenharmony_ci * @fip: The FCoE controller
268662306a36Sopenharmony_ci *
268762306a36Sopenharmony_ci * Called with ctlr_mutex held.
268862306a36Sopenharmony_ci * Called only in state FIP_ST_VNMP_UP.
268962306a36Sopenharmony_ci * Returns the soonest time for next age-out or a time far in the future.
269062306a36Sopenharmony_ci */
269162306a36Sopenharmony_cistatic unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
269262306a36Sopenharmony_ci{
269362306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
269462306a36Sopenharmony_ci	struct fc_rport_priv *rdata;
269562306a36Sopenharmony_ci	struct fcoe_rport *frport;
269662306a36Sopenharmony_ci	unsigned long next_time;
269762306a36Sopenharmony_ci	unsigned long deadline;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
270062306a36Sopenharmony_ci	mutex_lock(&lport->disc.disc_mutex);
270162306a36Sopenharmony_ci	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
270262306a36Sopenharmony_ci		if (!kref_get_unless_zero(&rdata->kref))
270362306a36Sopenharmony_ci			continue;
270462306a36Sopenharmony_ci		frport = fcoe_ctlr_rport(rdata);
270562306a36Sopenharmony_ci		if (!frport->time) {
270662306a36Sopenharmony_ci			kref_put(&rdata->kref, fc_rport_destroy);
270762306a36Sopenharmony_ci			continue;
270862306a36Sopenharmony_ci		}
270962306a36Sopenharmony_ci		deadline = frport->time +
271062306a36Sopenharmony_ci			   msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
271162306a36Sopenharmony_ci		if (time_after_eq(jiffies, deadline)) {
271262306a36Sopenharmony_ci			frport->time = 0;
271362306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip,
271462306a36Sopenharmony_ci				"port %16.16llx fc_id %6.6x beacon expired\n",
271562306a36Sopenharmony_ci				rdata->ids.port_name, rdata->ids.port_id);
271662306a36Sopenharmony_ci			fc_rport_logoff(rdata);
271762306a36Sopenharmony_ci		} else if (time_before(deadline, next_time))
271862306a36Sopenharmony_ci			next_time = deadline;
271962306a36Sopenharmony_ci		kref_put(&rdata->kref, fc_rport_destroy);
272062306a36Sopenharmony_ci	}
272162306a36Sopenharmony_ci	mutex_unlock(&lport->disc.disc_mutex);
272262306a36Sopenharmony_ci	return next_time;
272362306a36Sopenharmony_ci}
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci/**
272662306a36Sopenharmony_ci * fcoe_ctlr_vn_recv() - Receive a FIP frame
272762306a36Sopenharmony_ci * @fip: The FCoE controller that received the frame
272862306a36Sopenharmony_ci * @skb: The received FIP frame
272962306a36Sopenharmony_ci *
273062306a36Sopenharmony_ci * Returns non-zero if the frame is dropped.
273162306a36Sopenharmony_ci * Always consumes the frame.
273262306a36Sopenharmony_ci */
273362306a36Sopenharmony_cistatic int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
273462306a36Sopenharmony_ci{
273562306a36Sopenharmony_ci	struct fip_header *fiph;
273662306a36Sopenharmony_ci	enum fip_vn2vn_subcode sub;
273762306a36Sopenharmony_ci	struct fcoe_rport frport = { };
273862306a36Sopenharmony_ci	int rc, vlan_id = 0;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
274162306a36Sopenharmony_ci	sub = fiph->fip_subcode;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	if (fip->lp->vlan)
274462306a36Sopenharmony_ci		vlan_id = skb_vlan_tag_get_id(skb);
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if (vlan_id && vlan_id != fip->lp->vlan) {
274762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_recv drop frame sub %x vlan %d\n",
274862306a36Sopenharmony_ci				sub, vlan_id);
274962306a36Sopenharmony_ci		rc = -EAGAIN;
275062306a36Sopenharmony_ci		goto drop;
275162306a36Sopenharmony_ci	}
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci	rc = fcoe_ctlr_vn_parse(fip, skb, &frport);
275462306a36Sopenharmony_ci	if (rc) {
275562306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc);
275662306a36Sopenharmony_ci		goto drop;
275762306a36Sopenharmony_ci	}
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
276062306a36Sopenharmony_ci	switch (sub) {
276162306a36Sopenharmony_ci	case FIP_SC_VN_PROBE_REQ:
276262306a36Sopenharmony_ci		fcoe_ctlr_vn_probe_req(fip, &frport);
276362306a36Sopenharmony_ci		break;
276462306a36Sopenharmony_ci	case FIP_SC_VN_PROBE_REP:
276562306a36Sopenharmony_ci		fcoe_ctlr_vn_probe_reply(fip, &frport);
276662306a36Sopenharmony_ci		break;
276762306a36Sopenharmony_ci	case FIP_SC_VN_CLAIM_NOTIFY:
276862306a36Sopenharmony_ci		fcoe_ctlr_vn_claim_notify(fip, &frport);
276962306a36Sopenharmony_ci		break;
277062306a36Sopenharmony_ci	case FIP_SC_VN_CLAIM_REP:
277162306a36Sopenharmony_ci		fcoe_ctlr_vn_claim_resp(fip, &frport);
277262306a36Sopenharmony_ci		break;
277362306a36Sopenharmony_ci	case FIP_SC_VN_BEACON:
277462306a36Sopenharmony_ci		fcoe_ctlr_vn_beacon(fip, &frport);
277562306a36Sopenharmony_ci		break;
277662306a36Sopenharmony_ci	default:
277762306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub);
277862306a36Sopenharmony_ci		rc = -1;
277962306a36Sopenharmony_ci		break;
278062306a36Sopenharmony_ci	}
278162306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
278262306a36Sopenharmony_cidrop:
278362306a36Sopenharmony_ci	kfree_skb(skb);
278462306a36Sopenharmony_ci	return rc;
278562306a36Sopenharmony_ci}
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci/**
278862306a36Sopenharmony_ci * fcoe_ctlr_vlan_parse - parse vlan discovery request or response
278962306a36Sopenharmony_ci * @fip: The FCoE controller
279062306a36Sopenharmony_ci * @skb: incoming packet
279162306a36Sopenharmony_ci * @frport: parsed FCoE rport from the probe request
279262306a36Sopenharmony_ci *
279362306a36Sopenharmony_ci * Returns non-zero error number on error.
279462306a36Sopenharmony_ci * Does not consume the packet.
279562306a36Sopenharmony_ci */
279662306a36Sopenharmony_cistatic int fcoe_ctlr_vlan_parse(struct fcoe_ctlr *fip,
279762306a36Sopenharmony_ci			      struct sk_buff *skb,
279862306a36Sopenharmony_ci			      struct fcoe_rport *frport)
279962306a36Sopenharmony_ci{
280062306a36Sopenharmony_ci	struct fip_header *fiph;
280162306a36Sopenharmony_ci	struct fip_desc *desc = NULL;
280262306a36Sopenharmony_ci	struct fip_mac_desc *macd = NULL;
280362306a36Sopenharmony_ci	struct fip_wwn_desc *wwn = NULL;
280462306a36Sopenharmony_ci	size_t rlen;
280562306a36Sopenharmony_ci	size_t dlen;
280662306a36Sopenharmony_ci	u32 desc_mask = 0;
280762306a36Sopenharmony_ci	u32 dtype;
280862306a36Sopenharmony_ci	u8 sub;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
281162306a36Sopenharmony_ci	frport->flags = ntohs(fiph->fip_flags);
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	sub = fiph->fip_subcode;
281462306a36Sopenharmony_ci	switch (sub) {
281562306a36Sopenharmony_ci	case FIP_SC_VL_REQ:
281662306a36Sopenharmony_ci		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME);
281762306a36Sopenharmony_ci		break;
281862306a36Sopenharmony_ci	default:
281962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
282062306a36Sopenharmony_ci		return -EINVAL;
282162306a36Sopenharmony_ci	}
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	rlen = ntohs(fiph->fip_dl_len) * 4;
282462306a36Sopenharmony_ci	if (rlen + sizeof(*fiph) > skb->len)
282562306a36Sopenharmony_ci		return -EINVAL;
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	desc = (struct fip_desc *)(fiph + 1);
282862306a36Sopenharmony_ci	while (rlen > 0) {
282962306a36Sopenharmony_ci		dlen = desc->fip_dlen * FIP_BPW;
283062306a36Sopenharmony_ci		if (dlen < sizeof(*desc) || dlen > rlen)
283162306a36Sopenharmony_ci			return -EINVAL;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci		dtype = desc->fip_dtype;
283462306a36Sopenharmony_ci		if (dtype < 32) {
283562306a36Sopenharmony_ci			if (!(desc_mask & BIT(dtype))) {
283662306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip,
283762306a36Sopenharmony_ci						"unexpected or duplicated desc "
283862306a36Sopenharmony_ci						"desc type %u in "
283962306a36Sopenharmony_ci						"FIP VN2VN subtype %u\n",
284062306a36Sopenharmony_ci						dtype, sub);
284162306a36Sopenharmony_ci				return -EINVAL;
284262306a36Sopenharmony_ci			}
284362306a36Sopenharmony_ci			desc_mask &= ~BIT(dtype);
284462306a36Sopenharmony_ci		}
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci		switch (dtype) {
284762306a36Sopenharmony_ci		case FIP_DT_MAC:
284862306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_mac_desc))
284962306a36Sopenharmony_ci				goto len_err;
285062306a36Sopenharmony_ci			macd = (struct fip_mac_desc *)desc;
285162306a36Sopenharmony_ci			if (!is_valid_ether_addr(macd->fd_mac)) {
285262306a36Sopenharmony_ci				LIBFCOE_FIP_DBG(fip,
285362306a36Sopenharmony_ci					"Invalid MAC addr %pM in FIP VN2VN\n",
285462306a36Sopenharmony_ci					 macd->fd_mac);
285562306a36Sopenharmony_ci				return -EINVAL;
285662306a36Sopenharmony_ci			}
285762306a36Sopenharmony_ci			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
285862306a36Sopenharmony_ci			break;
285962306a36Sopenharmony_ci		case FIP_DT_NAME:
286062306a36Sopenharmony_ci			if (dlen != sizeof(struct fip_wwn_desc))
286162306a36Sopenharmony_ci				goto len_err;
286262306a36Sopenharmony_ci			wwn = (struct fip_wwn_desc *)desc;
286362306a36Sopenharmony_ci			frport->rdata.ids.node_name =
286462306a36Sopenharmony_ci				get_unaligned_be64(&wwn->fd_wwn);
286562306a36Sopenharmony_ci			break;
286662306a36Sopenharmony_ci		default:
286762306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
286862306a36Sopenharmony_ci					"in FIP probe\n", dtype);
286962306a36Sopenharmony_ci			/* standard says ignore unknown descriptors >= 128 */
287062306a36Sopenharmony_ci			if (dtype < FIP_DT_NON_CRITICAL)
287162306a36Sopenharmony_ci				return -EINVAL;
287262306a36Sopenharmony_ci			break;
287362306a36Sopenharmony_ci		}
287462306a36Sopenharmony_ci		desc = (struct fip_desc *)((char *)desc + dlen);
287562306a36Sopenharmony_ci		rlen -= dlen;
287662306a36Sopenharmony_ci	}
287762306a36Sopenharmony_ci	return 0;
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_cilen_err:
288062306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
288162306a36Sopenharmony_ci			dtype, dlen);
288262306a36Sopenharmony_ci	return -EINVAL;
288362306a36Sopenharmony_ci}
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci/**
288662306a36Sopenharmony_ci * fcoe_ctlr_vlan_send() - Send a FIP VLAN Notification
288762306a36Sopenharmony_ci * @fip: The FCoE controller
288862306a36Sopenharmony_ci * @sub: sub-opcode for vlan notification or vn2vn vlan notification
288962306a36Sopenharmony_ci * @dest: The destination Ethernet MAC address
289062306a36Sopenharmony_ci */
289162306a36Sopenharmony_cistatic void fcoe_ctlr_vlan_send(struct fcoe_ctlr *fip,
289262306a36Sopenharmony_ci			      enum fip_vlan_subcode sub,
289362306a36Sopenharmony_ci			      const u8 *dest)
289462306a36Sopenharmony_ci{
289562306a36Sopenharmony_ci	struct sk_buff *skb;
289662306a36Sopenharmony_ci	struct fip_vlan_notify_frame {
289762306a36Sopenharmony_ci		struct ethhdr eth;
289862306a36Sopenharmony_ci		struct fip_header fip;
289962306a36Sopenharmony_ci		struct fip_mac_desc mac;
290062306a36Sopenharmony_ci		struct fip_vlan_desc vlan;
290162306a36Sopenharmony_ci	} __packed * frame;
290262306a36Sopenharmony_ci	size_t len;
290362306a36Sopenharmony_ci	size_t dlen;
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci	len = sizeof(*frame);
290662306a36Sopenharmony_ci	dlen = sizeof(frame->mac) + sizeof(frame->vlan);
290762306a36Sopenharmony_ci	len = max(len, sizeof(struct ethhdr));
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci	skb = dev_alloc_skb(len);
291062306a36Sopenharmony_ci	if (!skb)
291162306a36Sopenharmony_ci		return;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	LIBFCOE_FIP_DBG(fip, "fip %s vlan notification, vlan %d\n",
291462306a36Sopenharmony_ci			fip->mode == FIP_MODE_VN2VN ? "vn2vn" : "fcf",
291562306a36Sopenharmony_ci			fip->lp->vlan);
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	frame = (struct fip_vlan_notify_frame *)skb->data;
291862306a36Sopenharmony_ci	memset(frame, 0, len);
291962306a36Sopenharmony_ci	memcpy(frame->eth.h_dest, dest, ETH_ALEN);
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
292262306a36Sopenharmony_ci	frame->eth.h_proto = htons(ETH_P_FIP);
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
292562306a36Sopenharmony_ci	frame->fip.fip_op = htons(FIP_OP_VLAN);
292662306a36Sopenharmony_ci	frame->fip.fip_subcode = sub;
292762306a36Sopenharmony_ci	frame->fip.fip_dl_len = htons(dlen / FIP_BPW);
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
293062306a36Sopenharmony_ci	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
293162306a36Sopenharmony_ci	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	frame->vlan.fd_desc.fip_dtype = FIP_DT_VLAN;
293462306a36Sopenharmony_ci	frame->vlan.fd_desc.fip_dlen = sizeof(frame->vlan) / FIP_BPW;
293562306a36Sopenharmony_ci	put_unaligned_be16(fip->lp->vlan, &frame->vlan.fd_vlan);
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci	skb_put(skb, len);
293862306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_FIP);
293962306a36Sopenharmony_ci	skb->priority = fip->priority;
294062306a36Sopenharmony_ci	skb_reset_mac_header(skb);
294162306a36Sopenharmony_ci	skb_reset_network_header(skb);
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	fip->send(fip, skb);
294462306a36Sopenharmony_ci}
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci/**
294762306a36Sopenharmony_ci * fcoe_ctlr_vlan_disc_reply() - send FIP VLAN Discovery Notification.
294862306a36Sopenharmony_ci * @fip: The FCoE controller
294962306a36Sopenharmony_ci * @frport: The newly-parsed FCoE rport from the Discovery Request
295062306a36Sopenharmony_ci *
295162306a36Sopenharmony_ci * Called with ctlr_mutex held.
295262306a36Sopenharmony_ci */
295362306a36Sopenharmony_cistatic void fcoe_ctlr_vlan_disc_reply(struct fcoe_ctlr *fip,
295462306a36Sopenharmony_ci				      struct fcoe_rport *frport)
295562306a36Sopenharmony_ci{
295662306a36Sopenharmony_ci	enum fip_vlan_subcode sub = FIP_SC_VL_NOTE;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	if (fip->mode == FIP_MODE_VN2VN)
295962306a36Sopenharmony_ci		sub = FIP_SC_VL_VN2VN_NOTE;
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci	fcoe_ctlr_vlan_send(fip, sub, frport->enode_mac);
296262306a36Sopenharmony_ci}
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci/**
296562306a36Sopenharmony_ci * fcoe_ctlr_vlan_recv - vlan request receive handler for VN2VN mode.
296662306a36Sopenharmony_ci * @fip: The FCoE controller
296762306a36Sopenharmony_ci * @skb: The received FIP packet
296862306a36Sopenharmony_ci */
296962306a36Sopenharmony_cistatic int fcoe_ctlr_vlan_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
297062306a36Sopenharmony_ci{
297162306a36Sopenharmony_ci	struct fip_header *fiph;
297262306a36Sopenharmony_ci	enum fip_vlan_subcode sub;
297362306a36Sopenharmony_ci	struct fcoe_rport frport = { };
297462306a36Sopenharmony_ci	int rc;
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	fiph = (struct fip_header *)skb->data;
297762306a36Sopenharmony_ci	sub = fiph->fip_subcode;
297862306a36Sopenharmony_ci	rc = fcoe_ctlr_vlan_parse(fip, skb, &frport);
297962306a36Sopenharmony_ci	if (rc) {
298062306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vlan_recv vlan_parse error %d\n", rc);
298162306a36Sopenharmony_ci		goto drop;
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
298462306a36Sopenharmony_ci	if (sub == FIP_SC_VL_REQ)
298562306a36Sopenharmony_ci		fcoe_ctlr_vlan_disc_reply(fip, &frport);
298662306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_cidrop:
298962306a36Sopenharmony_ci	kfree_skb(skb);
299062306a36Sopenharmony_ci	return rc;
299162306a36Sopenharmony_ci}
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci/**
299462306a36Sopenharmony_ci * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
299562306a36Sopenharmony_ci * @lport: The local port
299662306a36Sopenharmony_ci * @fp: The received frame
299762306a36Sopenharmony_ci *
299862306a36Sopenharmony_ci * This should never be called since we don't see RSCNs or other
299962306a36Sopenharmony_ci * fabric-generated ELSes.
300062306a36Sopenharmony_ci */
300162306a36Sopenharmony_cistatic void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp)
300262306a36Sopenharmony_ci{
300362306a36Sopenharmony_ci	struct fc_seq_els_data rjt_data;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	rjt_data.reason = ELS_RJT_UNSUP;
300662306a36Sopenharmony_ci	rjt_data.explan = ELS_EXPL_NONE;
300762306a36Sopenharmony_ci	fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
300862306a36Sopenharmony_ci	fc_frame_free(fp);
300962306a36Sopenharmony_ci}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci/*
301262306a36Sopenharmony_ci * fcoe_ctlr_disc_start - start discovery for VN2VN mode.
301362306a36Sopenharmony_ci *
301462306a36Sopenharmony_ci * This sets a flag indicating that remote ports should be created
301562306a36Sopenharmony_ci * and started for the peers we discover.  We use the disc_callback
301662306a36Sopenharmony_ci * pointer as that flag.  Peers already discovered are created here.
301762306a36Sopenharmony_ci *
301862306a36Sopenharmony_ci * The lport lock is held during this call. The callback must be done
301962306a36Sopenharmony_ci * later, without holding either the lport or discovery locks.
302062306a36Sopenharmony_ci * The fcoe_ctlr lock may also be held during this call.
302162306a36Sopenharmony_ci */
302262306a36Sopenharmony_cistatic void fcoe_ctlr_disc_start(void (*callback)(struct fc_lport *,
302362306a36Sopenharmony_ci						  enum fc_disc_event),
302462306a36Sopenharmony_ci				 struct fc_lport *lport)
302562306a36Sopenharmony_ci{
302662306a36Sopenharmony_ci	struct fc_disc *disc = &lport->disc;
302762306a36Sopenharmony_ci	struct fcoe_ctlr *fip = disc->priv;
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	mutex_lock(&disc->disc_mutex);
303062306a36Sopenharmony_ci	disc->disc_callback = callback;
303162306a36Sopenharmony_ci	disc->disc_id = (disc->disc_id + 2) | 1;
303262306a36Sopenharmony_ci	disc->pending = 1;
303362306a36Sopenharmony_ci	schedule_work(&fip->timer_work);
303462306a36Sopenharmony_ci	mutex_unlock(&disc->disc_mutex);
303562306a36Sopenharmony_ci}
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci/**
303862306a36Sopenharmony_ci * fcoe_ctlr_vn_disc() - report FIP VN_port discovery results after claim state.
303962306a36Sopenharmony_ci * @fip: The FCoE controller
304062306a36Sopenharmony_ci *
304162306a36Sopenharmony_ci * Starts the FLOGI and PLOGI login process to each discovered rport for which
304262306a36Sopenharmony_ci * we've received at least one beacon.
304362306a36Sopenharmony_ci * Performs the discovery complete callback.
304462306a36Sopenharmony_ci */
304562306a36Sopenharmony_cistatic void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
304662306a36Sopenharmony_ci{
304762306a36Sopenharmony_ci	struct fc_lport *lport = fip->lp;
304862306a36Sopenharmony_ci	struct fc_disc *disc = &lport->disc;
304962306a36Sopenharmony_ci	struct fc_rport_priv *rdata;
305062306a36Sopenharmony_ci	struct fcoe_rport *frport;
305162306a36Sopenharmony_ci	void (*callback)(struct fc_lport *, enum fc_disc_event);
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	mutex_lock(&disc->disc_mutex);
305462306a36Sopenharmony_ci	callback = disc->pending ? disc->disc_callback : NULL;
305562306a36Sopenharmony_ci	disc->pending = 0;
305662306a36Sopenharmony_ci	list_for_each_entry_rcu(rdata, &disc->rports, peers) {
305762306a36Sopenharmony_ci		if (!kref_get_unless_zero(&rdata->kref))
305862306a36Sopenharmony_ci			continue;
305962306a36Sopenharmony_ci		frport = fcoe_ctlr_rport(rdata);
306062306a36Sopenharmony_ci		if (frport->time)
306162306a36Sopenharmony_ci			fc_rport_login(rdata);
306262306a36Sopenharmony_ci		kref_put(&rdata->kref, fc_rport_destroy);
306362306a36Sopenharmony_ci	}
306462306a36Sopenharmony_ci	mutex_unlock(&disc->disc_mutex);
306562306a36Sopenharmony_ci	if (callback)
306662306a36Sopenharmony_ci		callback(lport, DISC_EV_SUCCESS);
306762306a36Sopenharmony_ci}
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci/**
307062306a36Sopenharmony_ci * fcoe_ctlr_vn_timeout - timer work function for VN2VN mode.
307162306a36Sopenharmony_ci * @fip: The FCoE controller
307262306a36Sopenharmony_ci */
307362306a36Sopenharmony_cistatic void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
307462306a36Sopenharmony_ci{
307562306a36Sopenharmony_ci	unsigned long next_time;
307662306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
307762306a36Sopenharmony_ci	u32 new_port_id = 0;
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
308062306a36Sopenharmony_ci	switch (fip->state) {
308162306a36Sopenharmony_ci	case FIP_ST_VNMP_START:
308262306a36Sopenharmony_ci		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1);
308362306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_timeout: send 1st probe request\n");
308462306a36Sopenharmony_ci		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
308562306a36Sopenharmony_ci		next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT);
308662306a36Sopenharmony_ci		break;
308762306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE1:
308862306a36Sopenharmony_ci		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2);
308962306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_timeout: send 2nd probe request\n");
309062306a36Sopenharmony_ci		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
309162306a36Sopenharmony_ci		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
309262306a36Sopenharmony_ci		break;
309362306a36Sopenharmony_ci	case FIP_ST_VNMP_PROBE2:
309462306a36Sopenharmony_ci		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_CLAIM);
309562306a36Sopenharmony_ci		new_port_id = fip->port_id;
309662306a36Sopenharmony_ci		hton24(mac, FIP_VN_FC_MAP);
309762306a36Sopenharmony_ci		hton24(mac + 3, new_port_id);
309862306a36Sopenharmony_ci		fcoe_ctlr_map_dest(fip);
309962306a36Sopenharmony_ci		fip->update_mac(fip->lp, mac);
310062306a36Sopenharmony_ci		LIBFCOE_FIP_DBG(fip, "vn_timeout: send claim notify\n");
310162306a36Sopenharmony_ci		fcoe_ctlr_vn_send_claim(fip);
310262306a36Sopenharmony_ci		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
310362306a36Sopenharmony_ci		break;
310462306a36Sopenharmony_ci	case FIP_ST_VNMP_CLAIM:
310562306a36Sopenharmony_ci		/*
310662306a36Sopenharmony_ci		 * This may be invoked either by starting discovery so don't
310762306a36Sopenharmony_ci		 * go to the next state unless it's been long enough.
310862306a36Sopenharmony_ci		 */
310962306a36Sopenharmony_ci		next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT);
311062306a36Sopenharmony_ci		if (time_after_eq(jiffies, next_time)) {
311162306a36Sopenharmony_ci			fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP);
311262306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "vn_timeout: send vn2vn beacon\n");
311362306a36Sopenharmony_ci			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
311462306a36Sopenharmony_ci					  fcoe_all_vn2vn, 0);
311562306a36Sopenharmony_ci			next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
311662306a36Sopenharmony_ci			fip->port_ka_time = next_time;
311762306a36Sopenharmony_ci		}
311862306a36Sopenharmony_ci		fcoe_ctlr_vn_disc(fip);
311962306a36Sopenharmony_ci		break;
312062306a36Sopenharmony_ci	case FIP_ST_VNMP_UP:
312162306a36Sopenharmony_ci		next_time = fcoe_ctlr_vn_age(fip);
312262306a36Sopenharmony_ci		if (time_after_eq(jiffies, fip->port_ka_time)) {
312362306a36Sopenharmony_ci			LIBFCOE_FIP_DBG(fip, "vn_timeout: send vn2vn beacon\n");
312462306a36Sopenharmony_ci			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
312562306a36Sopenharmony_ci					  fcoe_all_vn2vn, 0);
312662306a36Sopenharmony_ci			fip->port_ka_time = jiffies +
312762306a36Sopenharmony_ci				 msecs_to_jiffies(FIP_VN_BEACON_INT +
312862306a36Sopenharmony_ci					get_random_u32_below(FIP_VN_BEACON_FUZZ));
312962306a36Sopenharmony_ci		}
313062306a36Sopenharmony_ci		if (time_before(fip->port_ka_time, next_time))
313162306a36Sopenharmony_ci			next_time = fip->port_ka_time;
313262306a36Sopenharmony_ci		break;
313362306a36Sopenharmony_ci	case FIP_ST_LINK_WAIT:
313462306a36Sopenharmony_ci		goto unlock;
313562306a36Sopenharmony_ci	default:
313662306a36Sopenharmony_ci		WARN(1, "unexpected state %d\n", fip->state);
313762306a36Sopenharmony_ci		goto unlock;
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci	mod_timer(&fip->timer, next_time);
314062306a36Sopenharmony_ciunlock:
314162306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	/* If port ID is new, notify local port after dropping ctlr_mutex */
314462306a36Sopenharmony_ci	if (new_port_id)
314562306a36Sopenharmony_ci		fc_lport_set_local_id(fip->lp, new_port_id);
314662306a36Sopenharmony_ci}
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci/**
314962306a36Sopenharmony_ci * fcoe_ctlr_mode_set() - Set or reset the ctlr's mode
315062306a36Sopenharmony_ci * @lport: The local port to be (re)configured
315162306a36Sopenharmony_ci * @fip:   The FCoE controller whose mode is changing
315262306a36Sopenharmony_ci * @fip_mode: The new fip mode
315362306a36Sopenharmony_ci *
315462306a36Sopenharmony_ci * Note that the we shouldn't be changing the libfc discovery settings
315562306a36Sopenharmony_ci * (fc_disc_config) while an lport is going through the libfc state
315662306a36Sopenharmony_ci * machine. The mode can only be changed when a fcoe_ctlr device is
315762306a36Sopenharmony_ci * disabled, so that should ensure that this routine is only called
315862306a36Sopenharmony_ci * when nothing is happening.
315962306a36Sopenharmony_ci */
316062306a36Sopenharmony_cistatic void fcoe_ctlr_mode_set(struct fc_lport *lport, struct fcoe_ctlr *fip,
316162306a36Sopenharmony_ci			       enum fip_mode fip_mode)
316262306a36Sopenharmony_ci{
316362306a36Sopenharmony_ci	void *priv;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci	WARN_ON(lport->state != LPORT_ST_RESET &&
316662306a36Sopenharmony_ci		lport->state != LPORT_ST_DISABLED);
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	if (fip_mode == FIP_MODE_VN2VN) {
316962306a36Sopenharmony_ci		lport->rport_priv_size = sizeof(struct fcoe_rport);
317062306a36Sopenharmony_ci		lport->point_to_multipoint = 1;
317162306a36Sopenharmony_ci		lport->tt.disc_recv_req = fcoe_ctlr_disc_recv;
317262306a36Sopenharmony_ci		lport->tt.disc_start = fcoe_ctlr_disc_start;
317362306a36Sopenharmony_ci		lport->tt.disc_stop = fcoe_ctlr_disc_stop;
317462306a36Sopenharmony_ci		lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final;
317562306a36Sopenharmony_ci		priv = fip;
317662306a36Sopenharmony_ci	} else {
317762306a36Sopenharmony_ci		lport->rport_priv_size = 0;
317862306a36Sopenharmony_ci		lport->point_to_multipoint = 0;
317962306a36Sopenharmony_ci		lport->tt.disc_recv_req = NULL;
318062306a36Sopenharmony_ci		lport->tt.disc_start = NULL;
318162306a36Sopenharmony_ci		lport->tt.disc_stop = NULL;
318262306a36Sopenharmony_ci		lport->tt.disc_stop_final = NULL;
318362306a36Sopenharmony_ci		priv = lport;
318462306a36Sopenharmony_ci	}
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	fc_disc_config(lport, priv);
318762306a36Sopenharmony_ci}
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci/**
319062306a36Sopenharmony_ci * fcoe_libfc_config() - Sets up libfc related properties for local port
319162306a36Sopenharmony_ci * @lport:    The local port to configure libfc for
319262306a36Sopenharmony_ci * @fip:      The FCoE controller in use by the local port
319362306a36Sopenharmony_ci * @tt:       The libfc function template
319462306a36Sopenharmony_ci * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
319562306a36Sopenharmony_ci *
319662306a36Sopenharmony_ci * Returns : 0 for success
319762306a36Sopenharmony_ci */
319862306a36Sopenharmony_ciint fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
319962306a36Sopenharmony_ci		      const struct libfc_function_template *tt, int init_fcp)
320062306a36Sopenharmony_ci{
320162306a36Sopenharmony_ci	/* Set the function pointers set by the LLDD */
320262306a36Sopenharmony_ci	memcpy(&lport->tt, tt, sizeof(*tt));
320362306a36Sopenharmony_ci	if (init_fcp && fc_fcp_init(lport))
320462306a36Sopenharmony_ci		return -ENOMEM;
320562306a36Sopenharmony_ci	fc_exch_init(lport);
320662306a36Sopenharmony_ci	fc_elsct_init(lport);
320762306a36Sopenharmony_ci	fc_lport_init(lport);
320862306a36Sopenharmony_ci	fc_disc_init(lport);
320962306a36Sopenharmony_ci	fcoe_ctlr_mode_set(lport, fip, fip->mode);
321062306a36Sopenharmony_ci	return 0;
321162306a36Sopenharmony_ci}
321262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fcoe_libfc_config);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_civoid fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev)
321562306a36Sopenharmony_ci{
321662306a36Sopenharmony_ci	struct fcoe_ctlr_device *ctlr_dev = fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
321762306a36Sopenharmony_ci	struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
321862306a36Sopenharmony_ci	struct fcoe_fcf *fcf;
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	mutex_lock(&fip->ctlr_mutex);
322162306a36Sopenharmony_ci	mutex_lock(&ctlr_dev->lock);
322262306a36Sopenharmony_ci
322362306a36Sopenharmony_ci	fcf = fcoe_fcf_device_priv(fcf_dev);
322462306a36Sopenharmony_ci	if (fcf)
322562306a36Sopenharmony_ci		fcf_dev->selected = (fcf == fip->sel_fcf) ? 1 : 0;
322662306a36Sopenharmony_ci	else
322762306a36Sopenharmony_ci		fcf_dev->selected = 0;
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci	mutex_unlock(&ctlr_dev->lock);
323062306a36Sopenharmony_ci	mutex_unlock(&fip->ctlr_mutex);
323162306a36Sopenharmony_ci}
323262306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_fcf_get_selected);
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_civoid fcoe_ctlr_set_fip_mode(struct fcoe_ctlr_device *ctlr_dev)
323562306a36Sopenharmony_ci{
323662306a36Sopenharmony_ci	struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
323762306a36Sopenharmony_ci	struct fc_lport *lport = ctlr->lp;
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	mutex_lock(&ctlr->ctlr_mutex);
324062306a36Sopenharmony_ci	switch (ctlr_dev->mode) {
324162306a36Sopenharmony_ci	case FIP_CONN_TYPE_VN2VN:
324262306a36Sopenharmony_ci		ctlr->mode = FIP_MODE_VN2VN;
324362306a36Sopenharmony_ci		break;
324462306a36Sopenharmony_ci	case FIP_CONN_TYPE_FABRIC:
324562306a36Sopenharmony_ci	default:
324662306a36Sopenharmony_ci		ctlr->mode = FIP_MODE_FABRIC;
324762306a36Sopenharmony_ci		break;
324862306a36Sopenharmony_ci	}
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci	mutex_unlock(&ctlr->ctlr_mutex);
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	fcoe_ctlr_mode_set(lport, ctlr, ctlr->mode);
325362306a36Sopenharmony_ci}
325462306a36Sopenharmony_ciEXPORT_SYMBOL(fcoe_ctlr_set_fip_mode);
3255