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