| Vasu Dev | 9b34ecf | 2009-03-17 11:42:13 -0700 | [diff] [blame] | 1 | /* | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 2 |  * Copyright (c) 2008-2009 Cisco Systems, Inc.  All rights reserved. | 
 | 3 |  * Copyright (c) 2009 Intel Corporation.  All rights reserved. | 
| Vasu Dev | 9b34ecf | 2009-03-17 11:42:13 -0700 | [diff] [blame] | 4 |  * | 
 | 5 |  * This program is free software; you can redistribute it and/or modify it | 
 | 6 |  * under the terms and conditions of the GNU General Public License, | 
 | 7 |  * version 2, as published by the Free Software Foundation. | 
 | 8 |  * | 
 | 9 |  * This program is distributed in the hope it will be useful, but WITHOUT | 
 | 10 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 | 11 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
 | 12 |  * more details. | 
 | 13 |  * | 
 | 14 |  * You should have received a copy of the GNU General Public License along with | 
 | 15 |  * this program; if not, write to the Free Software Foundation, Inc., | 
 | 16 |  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 
 | 17 |  * | 
 | 18 |  * Maintained at www.Open-FCoE.org | 
 | 19 |  */ | 
 | 20 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 21 | #include <linux/types.h> | 
| Vasu Dev | 9b34ecf | 2009-03-17 11:42:13 -0700 | [diff] [blame] | 22 | #include <linux/module.h> | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 23 | #include <linux/kernel.h> | 
 | 24 | #include <linux/list.h> | 
 | 25 | #include <linux/spinlock.h> | 
 | 26 | #include <linux/timer.h> | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 27 | #include <linux/netdevice.h> | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 28 | #include <linux/etherdevice.h> | 
 | 29 | #include <linux/ethtool.h> | 
 | 30 | #include <linux/if_ether.h> | 
 | 31 | #include <linux/if_vlan.h> | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 32 | #include <linux/errno.h> | 
 | 33 | #include <linux/bitops.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 34 | #include <linux/slab.h> | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 35 | #include <net/rtnetlink.h> | 
 | 36 |  | 
 | 37 | #include <scsi/fc/fc_els.h> | 
 | 38 | #include <scsi/fc/fc_fs.h> | 
 | 39 | #include <scsi/fc/fc_fip.h> | 
 | 40 | #include <scsi/fc/fc_encaps.h> | 
 | 41 | #include <scsi/fc/fc_fcoe.h> | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 42 | #include <scsi/fc/fc_fcp.h> | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 43 |  | 
 | 44 | #include <scsi/libfc.h> | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 45 | #include <scsi/libfcoe.h> | 
| Vasu Dev | 9b34ecf | 2009-03-17 11:42:13 -0700 | [diff] [blame] | 46 |  | 
| Yi Zou | 21b7b2f | 2011-01-28 16:04:45 -0800 | [diff] [blame] | 47 | #include "libfcoe.h" | 
 | 48 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 49 | #define	FCOE_CTLR_MIN_FKA	500		/* min keep alive (mS) */ | 
 | 50 | #define	FCOE_CTLR_DEF_FKA	FIP_DEF_FKA	/* default keep alive (mS) */ | 
 | 51 |  | 
 | 52 | static void fcoe_ctlr_timeout(unsigned long); | 
| Joe Eykholt | 4291365 | 2010-03-12 16:08:23 -0800 | [diff] [blame] | 53 | static void fcoe_ctlr_timer_work(struct work_struct *); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 54 | static void fcoe_ctlr_recv_work(struct work_struct *); | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 55 | static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 56 |  | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 57 | static void fcoe_ctlr_vn_start(struct fcoe_ctlr *); | 
 | 58 | static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *); | 
 | 59 | static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *); | 
 | 60 | static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *); | 
 | 61 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 62 | static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS; | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 63 | static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS; | 
 | 64 | static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS; | 
 | 65 | static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 66 |  | 
| Yi Zou | 0095a92 | 2011-01-28 16:05:00 -0800 | [diff] [blame] | 67 | static const char * const fcoe_ctlr_states[] = { | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 68 | 	[FIP_ST_DISABLED] =	"DISABLED", | 
 | 69 | 	[FIP_ST_LINK_WAIT] =	"LINK_WAIT", | 
 | 70 | 	[FIP_ST_AUTO] =		"AUTO", | 
 | 71 | 	[FIP_ST_NON_FIP] =	"NON_FIP", | 
 | 72 | 	[FIP_ST_ENABLED] =	"ENABLED", | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 73 | 	[FIP_ST_VNMP_START] =	"VNMP_START", | 
 | 74 | 	[FIP_ST_VNMP_PROBE1] =	"VNMP_PROBE1", | 
 | 75 | 	[FIP_ST_VNMP_PROBE2] =	"VNMP_PROBE2", | 
 | 76 | 	[FIP_ST_VNMP_CLAIM] =	"VNMP_CLAIM", | 
 | 77 | 	[FIP_ST_VNMP_UP] =	"VNMP_UP", | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 78 | }; | 
 | 79 |  | 
 | 80 | static const char *fcoe_ctlr_state(enum fip_state state) | 
 | 81 | { | 
 | 82 | 	const char *cp = "unknown"; | 
 | 83 |  | 
 | 84 | 	if (state < ARRAY_SIZE(fcoe_ctlr_states)) | 
 | 85 | 		cp = fcoe_ctlr_states[state]; | 
 | 86 | 	if (!cp) | 
 | 87 | 		cp = "unknown"; | 
 | 88 | 	return cp; | 
 | 89 | } | 
 | 90 |  | 
 | 91 | /** | 
 | 92 |  * fcoe_ctlr_set_state() - Set and do debug printing for the new FIP state. | 
 | 93 |  * @fip: The FCoE controller | 
 | 94 |  * @state: The new state | 
 | 95 |  */ | 
 | 96 | static void fcoe_ctlr_set_state(struct fcoe_ctlr *fip, enum fip_state state) | 
 | 97 | { | 
 | 98 | 	if (state == fip->state) | 
 | 99 | 		return; | 
 | 100 | 	if (fip->lp) | 
 | 101 | 		LIBFCOE_FIP_DBG(fip, "state %s -> %s\n", | 
 | 102 | 			fcoe_ctlr_state(fip->state), fcoe_ctlr_state(state)); | 
 | 103 | 	fip->state = state; | 
 | 104 | } | 
 | 105 |  | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 106 | /** | 
 | 107 |  * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid | 
 | 108 |  * @fcf: The FCF to check | 
 | 109 |  * | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 110 |  * Return non-zero if FCF fcoe_size has been validated. | 
 | 111 |  */ | 
 | 112 | static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf) | 
 | 113 | { | 
 | 114 | 	return (fcf->flags & FIP_FL_SOL) != 0; | 
 | 115 | } | 
 | 116 |  | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 117 | /** | 
 | 118 |  * fcoe_ctlr_fcf_usable() - Check if a FCF is usable | 
 | 119 |  * @fcf: The FCF to check | 
 | 120 |  * | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 121 |  * Return non-zero if the FCF is usable. | 
 | 122 |  */ | 
 | 123 | static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) | 
 | 124 | { | 
 | 125 | 	u16 flags = FIP_FL_SOL | FIP_FL_AVAIL; | 
 | 126 |  | 
 | 127 | 	return (fcf->flags & flags) == flags; | 
 | 128 | } | 
 | 129 |  | 
 | 130 | /** | 
| Joe Eykholt | cd229e4 | 2010-07-20 15:20:40 -0700 | [diff] [blame] | 131 |  * fcoe_ctlr_map_dest() - Set flag and OUI for mapping destination addresses | 
 | 132 |  * @fip: The FCoE controller | 
 | 133 |  */ | 
 | 134 | static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip) | 
 | 135 | { | 
 | 136 | 	if (fip->mode == FIP_MODE_VN2VN) | 
 | 137 | 		hton24(fip->dest_addr, FIP_VN_FC_MAP); | 
 | 138 | 	else | 
 | 139 | 		hton24(fip->dest_addr, FIP_DEF_FC_MAP); | 
 | 140 | 	hton24(fip->dest_addr + 3, 0); | 
 | 141 | 	fip->map_dest = 1; | 
 | 142 | } | 
 | 143 |  | 
 | 144 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 145 |  * fcoe_ctlr_init() - Initialize the FCoE Controller instance | 
 | 146 |  * @fip: The FCoE controller to initialize | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 147 |  */ | 
| Joe Eykholt | 3d902ac | 2010-07-20 15:19:58 -0700 | [diff] [blame] | 148 | void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 149 | { | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 150 | 	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT); | 
| Joe Eykholt | 3d902ac | 2010-07-20 15:19:58 -0700 | [diff] [blame] | 151 | 	fip->mode = mode; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 152 | 	INIT_LIST_HEAD(&fip->fcfs); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 153 | 	mutex_init(&fip->ctlr_mutex); | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 154 | 	spin_lock_init(&fip->ctlr_lock); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 155 | 	fip->flogi_oxid = FC_XID_UNKNOWN; | 
 | 156 | 	setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip); | 
| Joe Eykholt | 4291365 | 2010-03-12 16:08:23 -0800 | [diff] [blame] | 157 | 	INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_work); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 158 | 	INIT_WORK(&fip->recv_work, fcoe_ctlr_recv_work); | 
 | 159 | 	skb_queue_head_init(&fip->fip_recv_list); | 
 | 160 | } | 
 | 161 | EXPORT_SYMBOL(fcoe_ctlr_init); | 
 | 162 |  | 
 | 163 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 164 |  * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller | 
 | 165 |  * @fip: The FCoE controller whose FCFs are to be reset | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 166 |  * | 
 | 167 |  * Called with &fcoe_ctlr lock held. | 
 | 168 |  */ | 
 | 169 | static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip) | 
 | 170 | { | 
 | 171 | 	struct fcoe_fcf *fcf; | 
 | 172 | 	struct fcoe_fcf *next; | 
 | 173 |  | 
 | 174 | 	fip->sel_fcf = NULL; | 
 | 175 | 	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { | 
 | 176 | 		list_del(&fcf->list); | 
 | 177 | 		kfree(fcf); | 
 | 178 | 	} | 
 | 179 | 	fip->fcf_count = 0; | 
 | 180 | 	fip->sel_time = 0; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 184 |  * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller | 
 | 185 |  * @fip: The FCoE controller to tear down | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 186 |  * | 
 | 187 |  * This is called by FCoE drivers before freeing the &fcoe_ctlr. | 
 | 188 |  * | 
 | 189 |  * The receive handler will have been deleted before this to guarantee | 
 | 190 |  * that no more recv_work will be scheduled. | 
 | 191 |  * | 
 | 192 |  * The timer routine will simply return once we set FIP_ST_DISABLED. | 
 | 193 |  * This guarantees that no further timeouts or work will be scheduled. | 
 | 194 |  */ | 
 | 195 | void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) | 
 | 196 | { | 
| Chris Leech | a4b7cfa | 2009-08-25 13:59:08 -0700 | [diff] [blame] | 197 | 	cancel_work_sync(&fip->recv_work); | 
| Joe Eykholt | 1f4aed8 | 2009-11-03 11:48:22 -0800 | [diff] [blame] | 198 | 	skb_queue_purge(&fip->fip_recv_list); | 
| Chris Leech | a4b7cfa | 2009-08-25 13:59:08 -0700 | [diff] [blame] | 199 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 200 | 	mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 201 | 	fcoe_ctlr_set_state(fip, FIP_ST_DISABLED); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 202 | 	fcoe_ctlr_reset_fcfs(fip); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 203 | 	mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 204 | 	del_timer_sync(&fip->timer); | 
| Joe Eykholt | 4291365 | 2010-03-12 16:08:23 -0800 | [diff] [blame] | 205 | 	cancel_work_sync(&fip->timer_work); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 206 | } | 
 | 207 | EXPORT_SYMBOL(fcoe_ctlr_destroy); | 
 | 208 |  | 
 | 209 | /** | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 210 |  * fcoe_ctlr_announce() - announce new FCF selection | 
| Joe Eykholt | 69316ee | 2010-11-30 16:19:40 -0800 | [diff] [blame] | 211 |  * @fip: The FCoE controller | 
 | 212 |  * | 
 | 213 |  * Also sets the destination MAC for FCoE and control packets | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 214 |  * | 
 | 215 |  * Called with neither ctlr_mutex nor ctlr_lock held. | 
| Joe Eykholt | 69316ee | 2010-11-30 16:19:40 -0800 | [diff] [blame] | 216 |  */ | 
 | 217 | static void fcoe_ctlr_announce(struct fcoe_ctlr *fip) | 
 | 218 | { | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 219 | 	struct fcoe_fcf *sel; | 
 | 220 | 	struct fcoe_fcf *fcf; | 
 | 221 |  | 
 | 222 | 	mutex_lock(&fip->ctlr_mutex); | 
 | 223 | 	spin_lock_bh(&fip->ctlr_lock); | 
 | 224 |  | 
 | 225 | 	kfree_skb(fip->flogi_req); | 
 | 226 | 	fip->flogi_req = NULL; | 
 | 227 | 	list_for_each_entry(fcf, &fip->fcfs, list) | 
 | 228 | 		fcf->flogi_sent = 0; | 
 | 229 |  | 
 | 230 | 	spin_unlock_bh(&fip->ctlr_lock); | 
 | 231 | 	sel = fip->sel_fcf; | 
| Joe Eykholt | 69316ee | 2010-11-30 16:19:40 -0800 | [diff] [blame] | 232 |  | 
 | 233 | 	if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr)) | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 234 | 		goto unlock; | 
| Joe Eykholt | 69316ee | 2010-11-30 16:19:40 -0800 | [diff] [blame] | 235 | 	if (!is_zero_ether_addr(fip->dest_addr)) { | 
 | 236 | 		printk(KERN_NOTICE "libfcoe: host%d: " | 
 | 237 | 		       "FIP Fibre-Channel Forwarder MAC %pM deselected\n", | 
 | 238 | 		       fip->lp->host->host_no, fip->dest_addr); | 
 | 239 | 		memset(fip->dest_addr, 0, ETH_ALEN); | 
 | 240 | 	} | 
 | 241 | 	if (sel) { | 
 | 242 | 		printk(KERN_INFO "libfcoe: host%d: FIP selected " | 
 | 243 | 		       "Fibre-Channel Forwarder MAC %pM\n", | 
 | 244 | 		       fip->lp->host->host_no, sel->fcf_mac); | 
 | 245 | 		memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN); | 
 | 246 | 		fip->map_dest = 0; | 
 | 247 | 	} | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 248 | unlock: | 
 | 249 | 	mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 69316ee | 2010-11-30 16:19:40 -0800 | [diff] [blame] | 250 | } | 
 | 251 |  | 
 | 252 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 253 |  * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port | 
 | 254 |  * @fip: The FCoE controller to get the maximum FCoE size from | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 255 |  * | 
 | 256 |  * Returns the maximum packet size including the FCoE header and trailer, | 
 | 257 |  * but not including any Ethernet or VLAN headers. | 
 | 258 |  */ | 
 | 259 | static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip) | 
 | 260 | { | 
 | 261 | 	/* | 
 | 262 | 	 * Determine the max FCoE frame size allowed, including | 
 | 263 | 	 * FCoE header and trailer. | 
 | 264 | 	 * Note:  lp->mfs is currently the payload size, not the frame size. | 
 | 265 | 	 */ | 
 | 266 | 	return fip->lp->mfs + sizeof(struct fc_frame_header) + | 
 | 267 | 		sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof); | 
 | 268 | } | 
 | 269 |  | 
 | 270 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 271 |  * fcoe_ctlr_solicit() - Send a FIP solicitation | 
 | 272 |  * @fip: The FCoE controller to send the solicitation on | 
 | 273 |  * @fcf: The destination FCF (if NULL, a multicast solicitation is sent) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 274 |  */ | 
 | 275 | static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf) | 
 | 276 | { | 
 | 277 | 	struct sk_buff *skb; | 
 | 278 | 	struct fip_sol { | 
 | 279 | 		struct ethhdr eth; | 
 | 280 | 		struct fip_header fip; | 
 | 281 | 		struct { | 
 | 282 | 			struct fip_mac_desc mac; | 
 | 283 | 			struct fip_wwn_desc wwnn; | 
 | 284 | 			struct fip_size_desc size; | 
| Yi Zou | 0095a92 | 2011-01-28 16:05:00 -0800 | [diff] [blame] | 285 | 		} __packed desc; | 
 | 286 | 	}  __packed * sol; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 287 | 	u32 fcoe_size; | 
 | 288 |  | 
 | 289 | 	skb = dev_alloc_skb(sizeof(*sol)); | 
 | 290 | 	if (!skb) | 
 | 291 | 		return; | 
 | 292 |  | 
 | 293 | 	sol = (struct fip_sol *)skb->data; | 
 | 294 |  | 
 | 295 | 	memset(sol, 0, sizeof(*sol)); | 
 | 296 | 	memcpy(sol->eth.h_dest, fcf ? fcf->fcf_mac : fcoe_all_fcfs, ETH_ALEN); | 
 | 297 | 	memcpy(sol->eth.h_source, fip->ctl_src_addr, ETH_ALEN); | 
 | 298 | 	sol->eth.h_proto = htons(ETH_P_FIP); | 
 | 299 |  | 
 | 300 | 	sol->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); | 
 | 301 | 	sol->fip.fip_op = htons(FIP_OP_DISC); | 
 | 302 | 	sol->fip.fip_subcode = FIP_SC_SOL; | 
 | 303 | 	sol->fip.fip_dl_len = htons(sizeof(sol->desc) / FIP_BPW); | 
 | 304 | 	sol->fip.fip_flags = htons(FIP_FL_FPMA); | 
| Vasu Dev | 184dd34 | 2009-05-17 12:33:28 +0000 | [diff] [blame] | 305 | 	if (fip->spma) | 
 | 306 | 		sol->fip.fip_flags |= htons(FIP_FL_SPMA); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 307 |  | 
 | 308 | 	sol->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC; | 
 | 309 | 	sol->desc.mac.fd_desc.fip_dlen = sizeof(sol->desc.mac) / FIP_BPW; | 
 | 310 | 	memcpy(sol->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); | 
 | 311 |  | 
 | 312 | 	sol->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME; | 
 | 313 | 	sol->desc.wwnn.fd_desc.fip_dlen = sizeof(sol->desc.wwnn) / FIP_BPW; | 
 | 314 | 	put_unaligned_be64(fip->lp->wwnn, &sol->desc.wwnn.fd_wwn); | 
 | 315 |  | 
 | 316 | 	fcoe_size = fcoe_ctlr_fcoe_size(fip); | 
 | 317 | 	sol->desc.size.fd_desc.fip_dtype = FIP_DT_FCOE_SIZE; | 
 | 318 | 	sol->desc.size.fd_desc.fip_dlen = sizeof(sol->desc.size) / FIP_BPW; | 
 | 319 | 	sol->desc.size.fd_size = htons(fcoe_size); | 
 | 320 |  | 
 | 321 | 	skb_put(skb, sizeof(*sol)); | 
| Chris Leech | 0f49153 | 2009-05-06 10:52:18 -0700 | [diff] [blame] | 322 | 	skb->protocol = htons(ETH_P_FIP); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 323 | 	skb_reset_mac_header(skb); | 
 | 324 | 	skb_reset_network_header(skb); | 
 | 325 | 	fip->send(fip, skb); | 
 | 326 |  | 
 | 327 | 	if (!fcf) | 
 | 328 | 		fip->sol_time = jiffies; | 
 | 329 | } | 
 | 330 |  | 
 | 331 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 332 |  * fcoe_ctlr_link_up() - Start FCoE controller | 
 | 333 |  * @fip: The FCoE controller to start | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 334 |  * | 
 | 335 |  * Called from the LLD when the network link is ready. | 
 | 336 |  */ | 
 | 337 | void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) | 
 | 338 | { | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 339 | 	mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 340 | 	if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) { | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 341 | 		mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 342 | 		fc_linkup(fip->lp); | 
 | 343 | 	} else if (fip->state == FIP_ST_LINK_WAIT) { | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 344 | 		fcoe_ctlr_set_state(fip, fip->mode); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 345 | 		switch (fip->mode) { | 
 | 346 | 		default: | 
 | 347 | 			LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode); | 
 | 348 | 			/* fall-through */ | 
 | 349 | 		case FIP_MODE_AUTO: | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 350 | 			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 351 | 			/* fall-through */ | 
 | 352 | 		case FIP_MODE_FABRIC: | 
 | 353 | 		case FIP_MODE_NON_FIP: | 
 | 354 | 			mutex_unlock(&fip->ctlr_mutex); | 
 | 355 | 			fc_linkup(fip->lp); | 
 | 356 | 			fcoe_ctlr_solicit(fip, NULL); | 
 | 357 | 			break; | 
 | 358 | 		case FIP_MODE_VN2VN: | 
 | 359 | 			fcoe_ctlr_vn_start(fip); | 
 | 360 | 			mutex_unlock(&fip->ctlr_mutex); | 
 | 361 | 			fc_linkup(fip->lp); | 
 | 362 | 			break; | 
 | 363 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 364 | 	} else | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 365 | 		mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 366 | } | 
 | 367 | EXPORT_SYMBOL(fcoe_ctlr_link_up); | 
 | 368 |  | 
 | 369 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 370 |  * fcoe_ctlr_reset() - Reset a FCoE controller | 
 | 371 |  * @fip:       The FCoE controller to reset | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 372 |  */ | 
| Joe Eykholt | dd42dac | 2009-11-03 11:48:27 -0800 | [diff] [blame] | 373 | static void fcoe_ctlr_reset(struct fcoe_ctlr *fip) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 374 | { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 375 | 	fcoe_ctlr_reset_fcfs(fip); | 
 | 376 | 	del_timer(&fip->timer); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 377 | 	fip->ctlr_ka_time = 0; | 
 | 378 | 	fip->port_ka_time = 0; | 
 | 379 | 	fip->sol_time = 0; | 
 | 380 | 	fip->flogi_oxid = FC_XID_UNKNOWN; | 
| Joe Eykholt | cd229e4 | 2010-07-20 15:20:40 -0700 | [diff] [blame] | 381 | 	fcoe_ctlr_map_dest(fip); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 382 | } | 
 | 383 |  | 
 | 384 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 385 |  * fcoe_ctlr_link_down() - Stop a FCoE controller | 
 | 386 |  * @fip: The FCoE controller to be stopped | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 387 |  * | 
 | 388 |  * Returns non-zero if the link was up and now isn't. | 
 | 389 |  * | 
 | 390 |  * Called from the LLD when the network link is not ready. | 
 | 391 |  * There may be multiple calls while the link is down. | 
 | 392 |  */ | 
 | 393 | int fcoe_ctlr_link_down(struct fcoe_ctlr *fip) | 
 | 394 | { | 
| Joe Eykholt | dd42dac | 2009-11-03 11:48:27 -0800 | [diff] [blame] | 395 | 	int link_dropped; | 
 | 396 |  | 
 | 397 | 	LIBFCOE_FIP_DBG(fip, "link down.\n"); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 398 | 	mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | dd42dac | 2009-11-03 11:48:27 -0800 | [diff] [blame] | 399 | 	fcoe_ctlr_reset(fip); | 
| Joe Eykholt | 4291365 | 2010-03-12 16:08:23 -0800 | [diff] [blame] | 400 | 	link_dropped = fip->state != FIP_ST_LINK_WAIT; | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 401 | 	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 402 | 	mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | dd42dac | 2009-11-03 11:48:27 -0800 | [diff] [blame] | 403 |  | 
 | 404 | 	if (link_dropped) | 
 | 405 | 		fc_linkdown(fip->lp); | 
 | 406 | 	return link_dropped; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 407 | } | 
 | 408 | EXPORT_SYMBOL(fcoe_ctlr_link_down); | 
 | 409 |  | 
 | 410 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 411 |  * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF | 
 | 412 |  * @fip:   The FCoE controller to send the FKA on | 
 | 413 |  * @lport: libfc fc_lport to send from | 
 | 414 |  * @ports: 0 for controller keep-alive, 1 for port keep-alive | 
 | 415 |  * @sa:	   The source MAC address | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 416 |  * | 
 | 417 |  * A controller keep-alive is sent every fka_period (typically 8 seconds). | 
 | 418 |  * The source MAC is the native MAC address. | 
 | 419 |  * | 
 | 420 |  * A port keep-alive is sent every 90 seconds while logged in. | 
 | 421 |  * The source MAC is the assigned mapped source address. | 
 | 422 |  * The destination is the FCF's F-port. | 
 | 423 |  */ | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 424 | static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, | 
 | 425 | 				      struct fc_lport *lport, | 
 | 426 | 				      int ports, u8 *sa) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 427 | { | 
 | 428 | 	struct sk_buff *skb; | 
 | 429 | 	struct fip_kal { | 
 | 430 | 		struct ethhdr eth; | 
 | 431 | 		struct fip_header fip; | 
 | 432 | 		struct fip_mac_desc mac; | 
| Yi Zou | 0095a92 | 2011-01-28 16:05:00 -0800 | [diff] [blame] | 433 | 	} __packed * kal; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 434 | 	struct fip_vn_desc *vn; | 
 | 435 | 	u32 len; | 
 | 436 | 	struct fc_lport *lp; | 
 | 437 | 	struct fcoe_fcf *fcf; | 
 | 438 |  | 
 | 439 | 	fcf = fip->sel_fcf; | 
 | 440 | 	lp = fip->lp; | 
| Joe Eykholt | 281ae64 | 2010-06-11 16:43:33 -0700 | [diff] [blame] | 441 | 	if (!fcf || (ports && !lp->port_id)) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 442 | 		return; | 
 | 443 |  | 
| Yi Zou | be276cb | 2009-11-03 11:50:16 -0800 | [diff] [blame] | 444 | 	len = sizeof(*kal) + ports * sizeof(*vn); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 445 | 	skb = dev_alloc_skb(len); | 
 | 446 | 	if (!skb) | 
 | 447 | 		return; | 
 | 448 |  | 
 | 449 | 	kal = (struct fip_kal *)skb->data; | 
 | 450 | 	memset(kal, 0, len); | 
 | 451 | 	memcpy(kal->eth.h_dest, fcf->fcf_mac, ETH_ALEN); | 
 | 452 | 	memcpy(kal->eth.h_source, sa, ETH_ALEN); | 
 | 453 | 	kal->eth.h_proto = htons(ETH_P_FIP); | 
 | 454 |  | 
 | 455 | 	kal->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); | 
 | 456 | 	kal->fip.fip_op = htons(FIP_OP_CTRL); | 
 | 457 | 	kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE; | 
 | 458 | 	kal->fip.fip_dl_len = htons((sizeof(kal->mac) + | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 459 | 				     ports * sizeof(*vn)) / FIP_BPW); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 460 | 	kal->fip.fip_flags = htons(FIP_FL_FPMA); | 
| Vasu Dev | 184dd34 | 2009-05-17 12:33:28 +0000 | [diff] [blame] | 461 | 	if (fip->spma) | 
 | 462 | 		kal->fip.fip_flags |= htons(FIP_FL_SPMA); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 463 |  | 
 | 464 | 	kal->mac.fd_desc.fip_dtype = FIP_DT_MAC; | 
 | 465 | 	kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW; | 
 | 466 | 	memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 467 | 	if (ports) { | 
 | 468 | 		vn = (struct fip_vn_desc *)(kal + 1); | 
 | 469 | 		vn->fd_desc.fip_dtype = FIP_DT_VN_ID; | 
 | 470 | 		vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW; | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 471 | 		memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN); | 
| Kaladhar Musunuru | fb83153 | 2010-05-07 15:18:57 -0700 | [diff] [blame] | 472 | 		hton24(vn->fd_fc_id, lport->port_id); | 
 | 473 | 		put_unaligned_be64(lport->wwpn, &vn->fd_wwpn); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 474 | 	} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 475 | 	skb_put(skb, len); | 
| Chris Leech | 0f49153 | 2009-05-06 10:52:18 -0700 | [diff] [blame] | 476 | 	skb->protocol = htons(ETH_P_FIP); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 477 | 	skb_reset_mac_header(skb); | 
 | 478 | 	skb_reset_network_header(skb); | 
 | 479 | 	fip->send(fip, skb); | 
 | 480 | } | 
 | 481 |  | 
 | 482 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 483 |  * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it | 
 | 484 |  * @fip:   The FCoE controller for the ELS frame | 
 | 485 |  * @dtype: The FIP descriptor type for the frame | 
 | 486 |  * @skb:   The FCoE ELS frame including FC header but no FCoE headers | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 487 |  * @d_id:  The destination port ID. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 488 |  * | 
 | 489 |  * Returns non-zero error code on failure. | 
 | 490 |  * | 
 | 491 |  * The caller must check that the length is a multiple of 4. | 
 | 492 |  * | 
 | 493 |  * The @skb must have enough headroom (28 bytes) and tailroom (8 bytes). | 
 | 494 |  * Headroom includes the FIP encapsulation description, FIP header, and | 
 | 495 |  * Ethernet header.  The tailroom is for the FIP MAC descriptor. | 
 | 496 |  */ | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 497 | static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 498 | 			    u8 dtype, struct sk_buff *skb, u32 d_id) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 499 | { | 
 | 500 | 	struct fip_encaps_head { | 
 | 501 | 		struct ethhdr eth; | 
 | 502 | 		struct fip_header fip; | 
 | 503 | 		struct fip_encaps encaps; | 
| Yi Zou | 0095a92 | 2011-01-28 16:05:00 -0800 | [diff] [blame] | 504 | 	} __packed * cap; | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 505 | 	struct fc_frame_header *fh; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 506 | 	struct fip_mac_desc *mac; | 
 | 507 | 	struct fcoe_fcf *fcf; | 
 | 508 | 	size_t dlen; | 
| Yi Zou | 5a84bae | 2009-07-29 17:03:55 -0700 | [diff] [blame] | 509 | 	u16 fip_flags; | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 510 | 	u8 op; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 511 |  | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 512 | 	fh = (struct fc_frame_header *)skb->data; | 
 | 513 | 	op = *(u8 *)(fh + 1); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 514 | 	dlen = sizeof(struct fip_encaps) + skb->len;	/* len before push */ | 
 | 515 | 	cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap)); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 516 | 	memset(cap, 0, sizeof(*cap)); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 517 |  | 
 | 518 | 	if (lport->point_to_multipoint) { | 
 | 519 | 		if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest)) | 
 | 520 | 			return -ENODEV; | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 521 | 		fip_flags = 0; | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 522 | 	} else { | 
 | 523 | 		fcf = fip->sel_fcf; | 
 | 524 | 		if (!fcf) | 
 | 525 | 			return -ENODEV; | 
 | 526 | 		fip_flags = fcf->flags; | 
 | 527 | 		fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA : | 
 | 528 | 					 FIP_FL_FPMA; | 
 | 529 | 		if (!fip_flags) | 
 | 530 | 			return -ENODEV; | 
 | 531 | 		memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN); | 
 | 532 | 	} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 533 | 	memcpy(cap->eth.h_source, fip->ctl_src_addr, ETH_ALEN); | 
 | 534 | 	cap->eth.h_proto = htons(ETH_P_FIP); | 
 | 535 |  | 
 | 536 | 	cap->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); | 
 | 537 | 	cap->fip.fip_op = htons(FIP_OP_LS); | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 538 | 	if (op == ELS_LS_ACC || op == ELS_LS_RJT) | 
 | 539 | 		cap->fip.fip_subcode = FIP_SC_REP; | 
 | 540 | 	else | 
 | 541 | 		cap->fip.fip_subcode = FIP_SC_REQ; | 
| Yi Zou | 5a84bae | 2009-07-29 17:03:55 -0700 | [diff] [blame] | 542 | 	cap->fip.fip_flags = htons(fip_flags); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 543 |  | 
 | 544 | 	cap->encaps.fd_desc.fip_dtype = dtype; | 
 | 545 | 	cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW; | 
 | 546 |  | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 547 | 	if (op != ELS_LS_RJT) { | 
 | 548 | 		dlen += sizeof(*mac); | 
 | 549 | 		mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac)); | 
 | 550 | 		memset(mac, 0, sizeof(*mac)); | 
 | 551 | 		mac->fd_desc.fip_dtype = FIP_DT_MAC; | 
 | 552 | 		mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW; | 
 | 553 | 		if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) { | 
 | 554 | 			memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN); | 
 | 555 | 		} else if (fip->mode == FIP_MODE_VN2VN) { | 
 | 556 | 			hton24(mac->fd_mac, FIP_VN_FC_MAP); | 
 | 557 | 			hton24(mac->fd_mac + 3, fip->port_id); | 
 | 558 | 		} else if (fip_flags & FIP_FL_SPMA) { | 
 | 559 | 			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n"); | 
 | 560 | 			memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN); | 
 | 561 | 		} else { | 
 | 562 | 			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n"); | 
 | 563 | 			/* FPMA only FLOGI.  Must leave the MAC desc zeroed. */ | 
 | 564 | 		} | 
| Robert Love | 593abc0 | 2010-04-09 14:22:17 -0700 | [diff] [blame] | 565 | 	} | 
| Joe Eykholt | 5554345 | 2010-07-20 15:20:35 -0700 | [diff] [blame] | 566 | 	cap->fip.fip_dl_len = htons(dlen / FIP_BPW); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 567 |  | 
| Chris Leech | 0f49153 | 2009-05-06 10:52:18 -0700 | [diff] [blame] | 568 | 	skb->protocol = htons(ETH_P_FIP); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 569 | 	skb_reset_mac_header(skb); | 
 | 570 | 	skb_reset_network_header(skb); | 
 | 571 | 	return 0; | 
 | 572 | } | 
 | 573 |  | 
 | 574 | /** | 
 | 575 |  * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate. | 
 | 576 |  * @fip:	FCoE controller. | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 577 |  * @lport:	libfc fc_lport to send from | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 578 |  * @skb:	FCoE ELS frame including FC header but no FCoE headers. | 
 | 579 |  * | 
 | 580 |  * Returns a non-zero error code if the frame should not be sent. | 
 | 581 |  * Returns zero if the caller should send the frame with FCoE encapsulation. | 
 | 582 |  * | 
 | 583 |  * The caller must check that the length is a multiple of 4. | 
 | 584 |  * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes). | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 585 |  * The the skb must also be an fc_frame. | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 586 |  * | 
 | 587 |  * This is called from the lower-level driver with spinlocks held, | 
 | 588 |  * so we must not take a mutex here. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 589 |  */ | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 590 | int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, | 
 | 591 | 		       struct sk_buff *skb) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 592 | { | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 593 | 	struct fc_frame *fp; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 594 | 	struct fc_frame_header *fh; | 
 | 595 | 	u16 old_xid; | 
 | 596 | 	u8 op; | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 597 | 	u8 mac[ETH_ALEN]; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 598 |  | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 599 | 	fp = container_of(skb, struct fc_frame, skb); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 600 | 	fh = (struct fc_frame_header *)skb->data; | 
 | 601 | 	op = *(u8 *)(fh + 1); | 
 | 602 |  | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 603 | 	if (op == ELS_FLOGI && fip->mode != FIP_MODE_VN2VN) { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 604 | 		old_xid = fip->flogi_oxid; | 
 | 605 | 		fip->flogi_oxid = ntohs(fh->fh_ox_id); | 
 | 606 | 		if (fip->state == FIP_ST_AUTO) { | 
 | 607 | 			if (old_xid == FC_XID_UNKNOWN) | 
 | 608 | 				fip->flogi_count = 0; | 
 | 609 | 			fip->flogi_count++; | 
 | 610 | 			if (fip->flogi_count < 3) | 
 | 611 | 				goto drop; | 
| Joe Eykholt | cd229e4 | 2010-07-20 15:20:40 -0700 | [diff] [blame] | 612 | 			fcoe_ctlr_map_dest(fip); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 613 | 			return 0; | 
 | 614 | 		} | 
| Joe Eykholt | 5f48f70 | 2009-05-06 10:52:12 -0700 | [diff] [blame] | 615 | 		if (fip->state == FIP_ST_NON_FIP) | 
| Joe Eykholt | cd229e4 | 2010-07-20 15:20:40 -0700 | [diff] [blame] | 616 | 			fcoe_ctlr_map_dest(fip); | 
| Joe Eykholt | 5f48f70 | 2009-05-06 10:52:12 -0700 | [diff] [blame] | 617 | 	} | 
 | 618 |  | 
 | 619 | 	if (fip->state == FIP_ST_NON_FIP) | 
 | 620 | 		return 0; | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 621 | 	if (!fip->sel_fcf && fip->mode != FIP_MODE_VN2VN) | 
| Joe Eykholt | f31f2a1 | 2009-11-03 11:48:32 -0800 | [diff] [blame] | 622 | 		goto drop; | 
| Joe Eykholt | 5f48f70 | 2009-05-06 10:52:12 -0700 | [diff] [blame] | 623 | 	switch (op) { | 
 | 624 | 	case ELS_FLOGI: | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 625 | 		op = FIP_DT_FLOGI; | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 626 | 		if (fip->mode == FIP_MODE_VN2VN) | 
 | 627 | 			break; | 
 | 628 | 		spin_lock_bh(&fip->ctlr_lock); | 
 | 629 | 		kfree_skb(fip->flogi_req); | 
 | 630 | 		fip->flogi_req = skb; | 
 | 631 | 		fip->flogi_req_send = 1; | 
 | 632 | 		spin_unlock_bh(&fip->ctlr_lock); | 
 | 633 | 		schedule_work(&fip->timer_work); | 
 | 634 | 		return -EINPROGRESS; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 635 | 	case ELS_FDISC: | 
 | 636 | 		if (ntoh24(fh->fh_s_id)) | 
 | 637 | 			return 0; | 
 | 638 | 		op = FIP_DT_FDISC; | 
 | 639 | 		break; | 
 | 640 | 	case ELS_LOGO: | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 641 | 		if (fip->mode == FIP_MODE_VN2VN) { | 
 | 642 | 			if (fip->state != FIP_ST_VNMP_UP) | 
 | 643 | 				return -EINVAL; | 
 | 644 | 			if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI) | 
 | 645 | 				return -EINVAL; | 
 | 646 | 		} else { | 
 | 647 | 			if (fip->state != FIP_ST_ENABLED) | 
 | 648 | 				return 0; | 
 | 649 | 			if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) | 
 | 650 | 				return 0; | 
 | 651 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 652 | 		op = FIP_DT_LOGO; | 
 | 653 | 		break; | 
 | 654 | 	case ELS_LS_ACC: | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 655 | 		/* | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 656 | 		 * If non-FIP, we may have gotten an SID by accepting an FLOGI | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 657 | 		 * from a point-to-point connection.  Switch to using | 
 | 658 | 		 * the source mac based on the SID.  The destination | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 659 | 		 * MAC in this case would have been set by receiving the | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 660 | 		 * FLOGI. | 
 | 661 | 		 */ | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 662 | 		if (fip->state == FIP_ST_NON_FIP) { | 
 | 663 | 			if (fip->flogi_oxid == FC_XID_UNKNOWN) | 
 | 664 | 				return 0; | 
 | 665 | 			fip->flogi_oxid = FC_XID_UNKNOWN; | 
 | 666 | 			fc_fcoe_set_mac(mac, fh->fh_d_id); | 
 | 667 | 			fip->update_mac(lport, mac); | 
 | 668 | 		} | 
 | 669 | 		/* fall through */ | 
 | 670 | 	case ELS_LS_RJT: | 
 | 671 | 		op = fr_encaps(fp); | 
 | 672 | 		if (op) | 
 | 673 | 			break; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 674 | 		return 0; | 
 | 675 | 	default: | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 676 | 		if (fip->state != FIP_ST_ENABLED && | 
 | 677 | 		    fip->state != FIP_ST_VNMP_UP) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 678 | 			goto drop; | 
 | 679 | 		return 0; | 
 | 680 | 	} | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 681 | 	LIBFCOE_FIP_DBG(fip, "els_send op %u d_id %x\n", | 
 | 682 | 			op, ntoh24(fh->fh_d_id)); | 
 | 683 | 	if (fcoe_ctlr_encaps(fip, lport, op, skb, ntoh24(fh->fh_d_id))) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 684 | 		goto drop; | 
 | 685 | 	fip->send(fip, skb); | 
 | 686 | 	return -EINPROGRESS; | 
 | 687 | drop: | 
 | 688 | 	kfree_skb(skb); | 
 | 689 | 	return -EINVAL; | 
 | 690 | } | 
 | 691 | EXPORT_SYMBOL(fcoe_ctlr_els_send); | 
 | 692 |  | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 693 | /** | 
 | 694 |  * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller | 
 | 695 |  * @fip: The FCoE controller to free FCFs on | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 696 |  * | 
| Joe Eykholt | f018b73 | 2010-03-12 16:08:55 -0800 | [diff] [blame] | 697 |  * Called with lock held and preemption disabled. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 698 |  * | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 699 |  * An FCF is considered old if we have missed two advertisements. | 
 | 700 |  * That is, there have been no valid advertisement from it for 2.5 | 
 | 701 |  * times its keep-alive period. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 702 |  * | 
 | 703 |  * In addition, determine the time when an FCF selection can occur. | 
| Yi Zou | f3da80e | 2009-11-20 14:55:08 -0800 | [diff] [blame] | 704 |  * | 
 | 705 |  * Also, increment the MissDiscAdvCount when no advertisement is received | 
 | 706 |  * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB). | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 707 |  * | 
 | 708 |  * Returns the time in jiffies for the next call. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 709 |  */ | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 710 | static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 711 | { | 
 | 712 | 	struct fcoe_fcf *fcf; | 
 | 713 | 	struct fcoe_fcf *next; | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 714 | 	unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); | 
 | 715 | 	unsigned long deadline; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 716 | 	unsigned long sel_time = 0; | 
| Joe Eykholt | f018b73 | 2010-03-12 16:08:55 -0800 | [diff] [blame] | 717 | 	struct fcoe_dev_stats *stats; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 718 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 719 | 	stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu()); | 
 | 720 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 721 | 	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 722 | 		deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2; | 
 | 723 | 		if (fip->sel_fcf == fcf) { | 
 | 724 | 			if (time_after(jiffies, deadline)) { | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 725 | 				stats->MissDiscAdvCount++; | 
 | 726 | 				printk(KERN_INFO "libfcoe: host%d: " | 
 | 727 | 				       "Missing Discovery Advertisement " | 
 | 728 | 				       "for fab %16.16llx count %lld\n", | 
 | 729 | 				       fip->lp->host->host_no, fcf->fabric_name, | 
 | 730 | 				       stats->MissDiscAdvCount); | 
 | 731 | 			} else if (time_after(next_timer, deadline)) | 
 | 732 | 				next_timer = deadline; | 
| Yi Zou | f3da80e | 2009-11-20 14:55:08 -0800 | [diff] [blame] | 733 | 		} | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 734 |  | 
 | 735 | 		deadline += fcf->fka_period; | 
| Joe Eykholt | d99ee45 | 2010-06-11 16:44:15 -0700 | [diff] [blame] | 736 | 		if (time_after_eq(jiffies, deadline)) { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 737 | 			if (fip->sel_fcf == fcf) | 
 | 738 | 				fip->sel_fcf = NULL; | 
 | 739 | 			list_del(&fcf->list); | 
 | 740 | 			WARN_ON(!fip->fcf_count); | 
 | 741 | 			fip->fcf_count--; | 
 | 742 | 			kfree(fcf); | 
| Joe Eykholt | f018b73 | 2010-03-12 16:08:55 -0800 | [diff] [blame] | 743 | 			stats->VLinkFailureCount++; | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 744 | 		} else { | 
 | 745 | 			if (time_after(next_timer, deadline)) | 
 | 746 | 				next_timer = deadline; | 
 | 747 | 			if (fcoe_ctlr_mtu_valid(fcf) && | 
 | 748 | 			    (!sel_time || time_before(sel_time, fcf->time))) | 
 | 749 | 				sel_time = fcf->time; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 750 | 		} | 
 | 751 | 	} | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 752 | 	put_cpu(); | 
| Joe Eykholt | d99ee45 | 2010-06-11 16:44:15 -0700 | [diff] [blame] | 753 | 	if (sel_time && !fip->sel_fcf && !fip->sel_time) { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 754 | 		sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY); | 
 | 755 | 		fip->sel_time = sel_time; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 756 | 	} | 
| Joe Eykholt | d99ee45 | 2010-06-11 16:44:15 -0700 | [diff] [blame] | 757 |  | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 758 | 	return next_timer; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 759 | } | 
 | 760 |  | 
 | 761 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 762 |  * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 763 |  * @fip: The FCoE controller receiving the advertisement | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 764 |  * @skb: The received FIP advertisement frame | 
 | 765 |  * @fcf: The resulting FCF entry | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 766 |  * | 
 | 767 |  * Returns zero on a valid parsed advertisement, | 
 | 768 |  * otherwise returns non zero value. | 
 | 769 |  */ | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 770 | static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, | 
 | 771 | 			       struct sk_buff *skb, struct fcoe_fcf *fcf) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 772 | { | 
 | 773 | 	struct fip_header *fiph; | 
 | 774 | 	struct fip_desc *desc = NULL; | 
 | 775 | 	struct fip_wwn_desc *wwn; | 
 | 776 | 	struct fip_fab_desc *fab; | 
 | 777 | 	struct fip_fka_desc *fka; | 
 | 778 | 	unsigned long t; | 
 | 779 | 	size_t rlen; | 
 | 780 | 	size_t dlen; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 781 | 	u32 desc_mask; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 782 |  | 
 | 783 | 	memset(fcf, 0, sizeof(*fcf)); | 
 | 784 | 	fcf->fka_period = msecs_to_jiffies(FCOE_CTLR_DEF_FKA); | 
 | 785 |  | 
 | 786 | 	fiph = (struct fip_header *)skb->data; | 
 | 787 | 	fcf->flags = ntohs(fiph->fip_flags); | 
 | 788 |  | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 789 | 	/* | 
 | 790 | 	 * mask of required descriptors. validating each one clears its bit. | 
 | 791 | 	 */ | 
 | 792 | 	desc_mask = BIT(FIP_DT_PRI) | BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | | 
 | 793 | 			BIT(FIP_DT_FAB) | BIT(FIP_DT_FKA); | 
 | 794 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 795 | 	rlen = ntohs(fiph->fip_dl_len) * 4; | 
 | 796 | 	if (rlen + sizeof(*fiph) > skb->len) | 
 | 797 | 		return -EINVAL; | 
 | 798 |  | 
 | 799 | 	desc = (struct fip_desc *)(fiph + 1); | 
 | 800 | 	while (rlen > 0) { | 
 | 801 | 		dlen = desc->fip_dlen * FIP_BPW; | 
 | 802 | 		if (dlen < sizeof(*desc) || dlen > rlen) | 
 | 803 | 			return -EINVAL; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 804 | 		/* Drop Adv if there are duplicate critical descriptors */ | 
 | 805 | 		if ((desc->fip_dtype < 32) && | 
 | 806 | 		    !(desc_mask & 1U << desc->fip_dtype)) { | 
 | 807 | 			LIBFCOE_FIP_DBG(fip, "Duplicate Critical " | 
 | 808 | 					"Descriptors in FIP adv\n"); | 
 | 809 | 			return -EINVAL; | 
 | 810 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 811 | 		switch (desc->fip_dtype) { | 
 | 812 | 		case FIP_DT_PRI: | 
 | 813 | 			if (dlen != sizeof(struct fip_pri_desc)) | 
 | 814 | 				goto len_err; | 
 | 815 | 			fcf->pri = ((struct fip_pri_desc *)desc)->fd_pri; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 816 | 			desc_mask &= ~BIT(FIP_DT_PRI); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 817 | 			break; | 
 | 818 | 		case FIP_DT_MAC: | 
 | 819 | 			if (dlen != sizeof(struct fip_mac_desc)) | 
 | 820 | 				goto len_err; | 
 | 821 | 			memcpy(fcf->fcf_mac, | 
 | 822 | 			       ((struct fip_mac_desc *)desc)->fd_mac, | 
 | 823 | 			       ETH_ALEN); | 
 | 824 | 			if (!is_valid_ether_addr(fcf->fcf_mac)) { | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 825 | 				LIBFCOE_FIP_DBG(fip, | 
 | 826 | 					"Invalid MAC addr %pM in FIP adv\n", | 
 | 827 | 					fcf->fcf_mac); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 828 | 				return -EINVAL; | 
 | 829 | 			} | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 830 | 			desc_mask &= ~BIT(FIP_DT_MAC); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 831 | 			break; | 
 | 832 | 		case FIP_DT_NAME: | 
 | 833 | 			if (dlen != sizeof(struct fip_wwn_desc)) | 
 | 834 | 				goto len_err; | 
 | 835 | 			wwn = (struct fip_wwn_desc *)desc; | 
 | 836 | 			fcf->switch_name = get_unaligned_be64(&wwn->fd_wwn); | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 837 | 			desc_mask &= ~BIT(FIP_DT_NAME); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 838 | 			break; | 
 | 839 | 		case FIP_DT_FAB: | 
 | 840 | 			if (dlen != sizeof(struct fip_fab_desc)) | 
 | 841 | 				goto len_err; | 
 | 842 | 			fab = (struct fip_fab_desc *)desc; | 
 | 843 | 			fcf->fabric_name = get_unaligned_be64(&fab->fd_wwn); | 
 | 844 | 			fcf->vfid = ntohs(fab->fd_vfid); | 
 | 845 | 			fcf->fc_map = ntoh24(fab->fd_map); | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 846 | 			desc_mask &= ~BIT(FIP_DT_FAB); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 847 | 			break; | 
 | 848 | 		case FIP_DT_FKA: | 
 | 849 | 			if (dlen != sizeof(struct fip_fka_desc)) | 
 | 850 | 				goto len_err; | 
 | 851 | 			fka = (struct fip_fka_desc *)desc; | 
| Yi Zou | 8cdffdc | 2009-11-20 14:54:57 -0800 | [diff] [blame] | 852 | 			if (fka->fd_flags & FIP_FKA_ADV_D) | 
 | 853 | 				fcf->fd_flags = 1; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 854 | 			t = ntohl(fka->fd_fka_period); | 
 | 855 | 			if (t >= FCOE_CTLR_MIN_FKA) | 
 | 856 | 				fcf->fka_period = msecs_to_jiffies(t); | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 857 | 			desc_mask &= ~BIT(FIP_DT_FKA); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 858 | 			break; | 
 | 859 | 		case FIP_DT_MAP_OUI: | 
 | 860 | 		case FIP_DT_FCOE_SIZE: | 
 | 861 | 		case FIP_DT_FLOGI: | 
 | 862 | 		case FIP_DT_FDISC: | 
 | 863 | 		case FIP_DT_LOGO: | 
 | 864 | 		case FIP_DT_ELP: | 
 | 865 | 		default: | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 866 | 			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " | 
| Robert Love | 650bd12 | 2009-06-10 15:31:05 -0700 | [diff] [blame] | 867 | 					"in FIP adv\n", desc->fip_dtype); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 868 | 			/* standard says ignore unknown descriptors >= 128 */ | 
 | 869 | 			if (desc->fip_dtype < FIP_DT_VENDOR_BASE) | 
 | 870 | 				return -EINVAL; | 
| Bhanu Prakash Gollapudi | 1508f3e | 2010-06-11 16:43:38 -0700 | [diff] [blame] | 871 | 			break; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 872 | 		} | 
 | 873 | 		desc = (struct fip_desc *)((char *)desc + dlen); | 
 | 874 | 		rlen -= dlen; | 
 | 875 | 	} | 
 | 876 | 	if (!fcf->fc_map || (fcf->fc_map & 0x10000)) | 
 | 877 | 		return -EINVAL; | 
| Vasu Dev | 240778e | 2010-07-20 15:21:33 -0700 | [diff] [blame] | 878 | 	if (!fcf->switch_name) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 879 | 		return -EINVAL; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 880 | 	if (desc_mask) { | 
 | 881 | 		LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n", | 
 | 882 | 				desc_mask); | 
 | 883 | 		return -EINVAL; | 
 | 884 | 	} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 885 | 	return 0; | 
 | 886 |  | 
 | 887 | len_err: | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 888 | 	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", | 
| Robert Love | 650bd12 | 2009-06-10 15:31:05 -0700 | [diff] [blame] | 889 | 			desc->fip_dtype, dlen); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 890 | 	return -EINVAL; | 
 | 891 | } | 
 | 892 |  | 
 | 893 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 894 |  * fcoe_ctlr_recv_adv() - Handle an incoming advertisement | 
 | 895 |  * @fip: The FCoE controller receiving the advertisement | 
 | 896 |  * @skb: The received FIP packet | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 897 |  */ | 
 | 898 | static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) | 
 | 899 | { | 
 | 900 | 	struct fcoe_fcf *fcf; | 
 | 901 | 	struct fcoe_fcf new; | 
 | 902 | 	struct fcoe_fcf *found; | 
 | 903 | 	unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV); | 
 | 904 | 	int first = 0; | 
 | 905 | 	int mtu_valid; | 
 | 906 |  | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 907 | 	if (fcoe_ctlr_parse_adv(fip, skb, &new)) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 908 | 		return; | 
 | 909 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 910 | 	mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 911 | 	first = list_empty(&fip->fcfs); | 
 | 912 | 	found = NULL; | 
 | 913 | 	list_for_each_entry(fcf, &fip->fcfs, list) { | 
 | 914 | 		if (fcf->switch_name == new.switch_name && | 
 | 915 | 		    fcf->fabric_name == new.fabric_name && | 
 | 916 | 		    fcf->fc_map == new.fc_map && | 
 | 917 | 		    compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) { | 
 | 918 | 			found = fcf; | 
 | 919 | 			break; | 
 | 920 | 		} | 
 | 921 | 	} | 
 | 922 | 	if (!found) { | 
 | 923 | 		if (fip->fcf_count >= FCOE_CTLR_FCF_LIMIT) | 
 | 924 | 			goto out; | 
 | 925 |  | 
 | 926 | 		fcf = kmalloc(sizeof(*fcf), GFP_ATOMIC); | 
 | 927 | 		if (!fcf) | 
 | 928 | 			goto out; | 
 | 929 |  | 
 | 930 | 		fip->fcf_count++; | 
 | 931 | 		memcpy(fcf, &new, sizeof(new)); | 
 | 932 | 		list_add(&fcf->list, &fip->fcfs); | 
 | 933 | 	} else { | 
 | 934 | 		/* | 
| Joe Eykholt | c600fea | 2010-06-11 16:44:20 -0700 | [diff] [blame] | 935 | 		 * Update the FCF's keep-alive descriptor flags. | 
 | 936 | 		 * Other flag changes from new advertisements are | 
 | 937 | 		 * ignored after a solicited advertisement is | 
 | 938 | 		 * received and the FCF is selectable (usable). | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 939 | 		 */ | 
| Joe Eykholt | c600fea | 2010-06-11 16:44:20 -0700 | [diff] [blame] | 940 | 		fcf->fd_flags = new.fd_flags; | 
 | 941 | 		if (!fcoe_ctlr_fcf_usable(fcf)) | 
 | 942 | 			fcf->flags = new.flags; | 
 | 943 |  | 
| Joe Eykholt | d99ee45 | 2010-06-11 16:44:15 -0700 | [diff] [blame] | 944 | 		if (fcf == fip->sel_fcf && !fcf->fd_flags) { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 945 | 			fip->ctlr_ka_time -= fcf->fka_period; | 
 | 946 | 			fip->ctlr_ka_time += new.fka_period; | 
 | 947 | 			if (time_before(fip->ctlr_ka_time, fip->timer.expires)) | 
 | 948 | 				mod_timer(&fip->timer, fip->ctlr_ka_time); | 
| Joe Eykholt | c600fea | 2010-06-11 16:44:20 -0700 | [diff] [blame] | 949 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 950 | 		fcf->fka_period = new.fka_period; | 
 | 951 | 		memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN); | 
 | 952 | 	} | 
 | 953 | 	mtu_valid = fcoe_ctlr_mtu_valid(fcf); | 
 | 954 | 	fcf->time = jiffies; | 
| Joe Eykholt | 9069f5c | 2010-11-30 16:20:02 -0800 | [diff] [blame] | 955 | 	if (!found) | 
 | 956 | 		LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n", | 
 | 957 | 				fcf->fabric_name, fcf->fcf_mac); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 958 |  | 
 | 959 | 	/* | 
 | 960 | 	 * If this advertisement is not solicited and our max receive size | 
 | 961 | 	 * hasn't been verified, send a solicited advertisement. | 
 | 962 | 	 */ | 
 | 963 | 	if (!mtu_valid) | 
 | 964 | 		fcoe_ctlr_solicit(fip, fcf); | 
 | 965 |  | 
 | 966 | 	/* | 
 | 967 | 	 * If its been a while since we did a solicit, and this is | 
 | 968 | 	 * the first advertisement we've received, do a multicast | 
 | 969 | 	 * solicitation to gather as many advertisements as we can | 
 | 970 | 	 * before selection occurs. | 
 | 971 | 	 */ | 
 | 972 | 	if (first && time_after(jiffies, fip->sol_time + sol_tov)) | 
 | 973 | 		fcoe_ctlr_solicit(fip, NULL); | 
 | 974 |  | 
 | 975 | 	/* | 
| Joe Eykholt | 981c115 | 2010-11-30 16:20:07 -0800 | [diff] [blame] | 976 | 	 * Put this FCF at the head of the list for priority among equals. | 
 | 977 | 	 * This helps in the case of an NPV switch which insists we use | 
 | 978 | 	 * the FCF that answers multicast solicitations, not the others that | 
 | 979 | 	 * are sending periodic multicast advertisements. | 
 | 980 | 	 */ | 
| Kirill A. Shutemov | 63ce249 | 2011-04-01 16:06:09 -0700 | [diff] [blame] | 981 | 	if (mtu_valid) | 
 | 982 | 		list_move(&fcf->list, &fip->fcfs); | 
| Joe Eykholt | 981c115 | 2010-11-30 16:20:07 -0800 | [diff] [blame] | 983 |  | 
 | 984 | 	/* | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 985 | 	 * If this is the first validated FCF, note the time and | 
 | 986 | 	 * set a timer to trigger selection. | 
 | 987 | 	 */ | 
| Joe Eykholt | d99ee45 | 2010-06-11 16:44:15 -0700 | [diff] [blame] | 988 | 	if (mtu_valid && !fip->sel_fcf && fcoe_ctlr_fcf_usable(fcf)) { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 989 | 		fip->sel_time = jiffies + | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 990 | 			msecs_to_jiffies(FCOE_CTLR_START_DELAY); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 991 | 		if (!timer_pending(&fip->timer) || | 
 | 992 | 		    time_before(fip->sel_time, fip->timer.expires)) | 
 | 993 | 			mod_timer(&fip->timer, fip->sel_time); | 
 | 994 | 	} | 
 | 995 | out: | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 996 | 	mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 997 | } | 
 | 998 |  | 
 | 999 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1000 |  * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame | 
 | 1001 |  * @fip: The FCoE controller which received the packet | 
 | 1002 |  * @skb: The received FIP packet | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1003 |  */ | 
 | 1004 | static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) | 
 | 1005 | { | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1006 | 	struct fc_lport *lport = fip->lp; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1007 | 	struct fip_header *fiph; | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 1008 | 	struct fc_frame *fp = (struct fc_frame *)skb; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1009 | 	struct fc_frame_header *fh = NULL; | 
 | 1010 | 	struct fip_desc *desc; | 
 | 1011 | 	struct fip_encaps *els; | 
 | 1012 | 	struct fcoe_dev_stats *stats; | 
 | 1013 | 	enum fip_desc_type els_dtype = 0; | 
 | 1014 | 	u8 els_op; | 
 | 1015 | 	u8 sub; | 
 | 1016 | 	u8 granted_mac[ETH_ALEN] = { 0 }; | 
 | 1017 | 	size_t els_len = 0; | 
 | 1018 | 	size_t rlen; | 
 | 1019 | 	size_t dlen; | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1020 | 	u32 desc_mask = 0; | 
 | 1021 | 	u32 desc_cnt = 0; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1022 |  | 
 | 1023 | 	fiph = (struct fip_header *)skb->data; | 
 | 1024 | 	sub = fiph->fip_subcode; | 
 | 1025 | 	if (sub != FIP_SC_REQ && sub != FIP_SC_REP) | 
 | 1026 | 		goto drop; | 
 | 1027 |  | 
 | 1028 | 	rlen = ntohs(fiph->fip_dl_len) * 4; | 
 | 1029 | 	if (rlen + sizeof(*fiph) > skb->len) | 
 | 1030 | 		goto drop; | 
 | 1031 |  | 
 | 1032 | 	desc = (struct fip_desc *)(fiph + 1); | 
 | 1033 | 	while (rlen > 0) { | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1034 | 		desc_cnt++; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1035 | 		dlen = desc->fip_dlen * FIP_BPW; | 
 | 1036 | 		if (dlen < sizeof(*desc) || dlen > rlen) | 
 | 1037 | 			goto drop; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 1038 | 		/* Drop ELS if there are duplicate critical descriptors */ | 
 | 1039 | 		if (desc->fip_dtype < 32) { | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1040 | 			if (desc_mask & 1U << desc->fip_dtype) { | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 1041 | 				LIBFCOE_FIP_DBG(fip, "Duplicate Critical " | 
 | 1042 | 						"Descriptors in FIP ELS\n"); | 
 | 1043 | 				goto drop; | 
 | 1044 | 			} | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1045 | 			desc_mask |= (1 << desc->fip_dtype); | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 1046 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1047 | 		switch (desc->fip_dtype) { | 
 | 1048 | 		case FIP_DT_MAC: | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1049 | 			if (desc_cnt == 1) { | 
 | 1050 | 				LIBFCOE_FIP_DBG(fip, "FIP descriptors " | 
 | 1051 | 						"received out of order\n"); | 
 | 1052 | 				goto drop; | 
 | 1053 | 			} | 
 | 1054 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1055 | 			if (dlen != sizeof(struct fip_mac_desc)) | 
 | 1056 | 				goto len_err; | 
 | 1057 | 			memcpy(granted_mac, | 
 | 1058 | 			       ((struct fip_mac_desc *)desc)->fd_mac, | 
 | 1059 | 			       ETH_ALEN); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1060 | 			break; | 
 | 1061 | 		case FIP_DT_FLOGI: | 
 | 1062 | 		case FIP_DT_FDISC: | 
 | 1063 | 		case FIP_DT_LOGO: | 
 | 1064 | 		case FIP_DT_ELP: | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1065 | 			if (desc_cnt != 1) { | 
 | 1066 | 				LIBFCOE_FIP_DBG(fip, "FIP descriptors " | 
 | 1067 | 						"received out of order\n"); | 
 | 1068 | 				goto drop; | 
 | 1069 | 			} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1070 | 			if (fh) | 
 | 1071 | 				goto drop; | 
 | 1072 | 			if (dlen < sizeof(*els) + sizeof(*fh) + 1) | 
 | 1073 | 				goto len_err; | 
 | 1074 | 			els_len = dlen - sizeof(*els); | 
 | 1075 | 			els = (struct fip_encaps *)desc; | 
 | 1076 | 			fh = (struct fc_frame_header *)(els + 1); | 
 | 1077 | 			els_dtype = desc->fip_dtype; | 
 | 1078 | 			break; | 
 | 1079 | 		default: | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 1080 | 			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " | 
| Robert Love | 650bd12 | 2009-06-10 15:31:05 -0700 | [diff] [blame] | 1081 | 					"in FIP adv\n", desc->fip_dtype); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1082 | 			/* standard says ignore unknown descriptors >= 128 */ | 
 | 1083 | 			if (desc->fip_dtype < FIP_DT_VENDOR_BASE) | 
 | 1084 | 				goto drop; | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1085 | 			if (desc_cnt <= 2) { | 
 | 1086 | 				LIBFCOE_FIP_DBG(fip, "FIP descriptors " | 
 | 1087 | 						"received out of order\n"); | 
 | 1088 | 				goto drop; | 
 | 1089 | 			} | 
| Bhanu Prakash Gollapudi | 1508f3e | 2010-06-11 16:43:38 -0700 | [diff] [blame] | 1090 | 			break; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1091 | 		} | 
 | 1092 | 		desc = (struct fip_desc *)((char *)desc + dlen); | 
 | 1093 | 		rlen -= dlen; | 
 | 1094 | 	} | 
 | 1095 |  | 
 | 1096 | 	if (!fh) | 
 | 1097 | 		goto drop; | 
 | 1098 | 	els_op = *(u8 *)(fh + 1); | 
 | 1099 |  | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1100 | 	if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) && | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1101 | 	    sub == FIP_SC_REP && fip->mode != FIP_MODE_VN2VN) { | 
 | 1102 | 		if (els_op == ELS_LS_ACC) { | 
 | 1103 | 			if (!is_valid_ether_addr(granted_mac)) { | 
 | 1104 | 				LIBFCOE_FIP_DBG(fip, | 
 | 1105 | 					"Invalid MAC address %pM in FIP ELS\n", | 
 | 1106 | 					granted_mac); | 
 | 1107 | 				goto drop; | 
 | 1108 | 			} | 
 | 1109 | 			memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1110 |  | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1111 | 			if (fip->flogi_oxid == ntohs(fh->fh_ox_id)) { | 
 | 1112 | 				fip->flogi_oxid = FC_XID_UNKNOWN; | 
 | 1113 | 				if (els_dtype == FIP_DT_FLOGI) | 
 | 1114 | 					fcoe_ctlr_announce(fip); | 
 | 1115 | 			} | 
 | 1116 | 		} else if (els_dtype == FIP_DT_FLOGI && | 
 | 1117 | 			   !fcoe_ctlr_flogi_retry(fip)) | 
 | 1118 | 			goto drop;	/* retrying FLOGI so drop reject */ | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1119 | 	} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1120 |  | 
| Bhanu Prakash Gollapudi | be61331 | 2010-06-11 16:44:36 -0700 | [diff] [blame] | 1121 | 	if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) && | 
 | 1122 | 	    (!(1U << FIP_DT_MAC & desc_mask)))) { | 
 | 1123 | 		LIBFCOE_FIP_DBG(fip, "Missing critical descriptors " | 
 | 1124 | 				"in FIP ELS\n"); | 
 | 1125 | 		goto drop; | 
 | 1126 | 	} | 
 | 1127 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1128 | 	/* | 
 | 1129 | 	 * Convert skb into an fc_frame containing only the ELS. | 
 | 1130 | 	 */ | 
 | 1131 | 	skb_pull(skb, (u8 *)fh - skb->data); | 
 | 1132 | 	skb_trim(skb, els_len); | 
 | 1133 | 	fp = (struct fc_frame *)skb; | 
 | 1134 | 	fc_frame_init(fp); | 
 | 1135 | 	fr_sof(fp) = FC_SOF_I3; | 
 | 1136 | 	fr_eof(fp) = FC_EOF_T; | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1137 | 	fr_dev(fp) = lport; | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1138 | 	fr_encaps(fp) = els_dtype; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1139 |  | 
| Joe Eykholt | f018b73 | 2010-03-12 16:08:55 -0800 | [diff] [blame] | 1140 | 	stats = per_cpu_ptr(lport->dev_stats, get_cpu()); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1141 | 	stats->RxFrames++; | 
 | 1142 | 	stats->RxWords += skb->len / FIP_BPW; | 
| Joe Eykholt | f018b73 | 2010-03-12 16:08:55 -0800 | [diff] [blame] | 1143 | 	put_cpu(); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1144 |  | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1145 | 	fc_exch_recv(lport, fp); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1146 | 	return; | 
 | 1147 |  | 
 | 1148 | len_err: | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 1149 | 	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", | 
| Robert Love | 650bd12 | 2009-06-10 15:31:05 -0700 | [diff] [blame] | 1150 | 			desc->fip_dtype, dlen); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1151 | drop: | 
 | 1152 | 	kfree_skb(skb); | 
 | 1153 | } | 
 | 1154 |  | 
 | 1155 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1156 |  * fcoe_ctlr_recv_els() - Handle an incoming link reset frame | 
 | 1157 |  * @fip: The FCoE controller that received the frame | 
 | 1158 |  * @fh:	 The received FIP header | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1159 |  * | 
 | 1160 |  * There may be multiple VN_Port descriptors. | 
 | 1161 |  * The overall length has already been checked. | 
 | 1162 |  */ | 
 | 1163 | static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1164 | 				     struct fip_header *fh) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1165 | { | 
 | 1166 | 	struct fip_desc *desc; | 
 | 1167 | 	struct fip_mac_desc *mp; | 
 | 1168 | 	struct fip_wwn_desc *wp; | 
 | 1169 | 	struct fip_vn_desc *vp; | 
 | 1170 | 	size_t rlen; | 
 | 1171 | 	size_t dlen; | 
 | 1172 | 	struct fcoe_fcf *fcf = fip->sel_fcf; | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1173 | 	struct fc_lport *lport = fip->lp; | 
| Bhanu Prakash Gollapudi | 5550fda | 2010-06-11 16:44:31 -0700 | [diff] [blame] | 1174 | 	struct fc_lport *vn_port = NULL; | 
 | 1175 | 	u32 desc_mask; | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1176 | 	int num_vlink_desc; | 
 | 1177 | 	int reset_phys_port = 0; | 
 | 1178 | 	struct fip_vn_desc **vlink_desc_arr = NULL; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1179 |  | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 1180 | 	LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); | 
| Robert Love | d29510a | 2010-05-07 15:18:30 -0700 | [diff] [blame] | 1181 |  | 
| Robert Love | 7b2787e | 2010-05-07 15:18:41 -0700 | [diff] [blame] | 1182 | 	if (!fcf || !lport->port_id) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1183 | 		return; | 
 | 1184 |  | 
 | 1185 | 	/* | 
 | 1186 | 	 * mask of required descriptors.  Validating each one clears its bit. | 
 | 1187 | 	 */ | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1188 | 	desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1189 |  | 
 | 1190 | 	rlen = ntohs(fh->fip_dl_len) * FIP_BPW; | 
 | 1191 | 	desc = (struct fip_desc *)(fh + 1); | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1192 |  | 
 | 1193 | 	/* | 
 | 1194 | 	 * Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen' | 
 | 1195 | 	 * before determining max Vx_Port descriptor but a buggy FCF could have | 
 | 1196 | 	 * omited either or both MAC Address and Name Identifier descriptors | 
 | 1197 | 	 */ | 
 | 1198 | 	num_vlink_desc = rlen / sizeof(*vp); | 
 | 1199 | 	if (num_vlink_desc) | 
 | 1200 | 		vlink_desc_arr = kmalloc(sizeof(vp) * num_vlink_desc, | 
 | 1201 | 					 GFP_ATOMIC); | 
 | 1202 | 	if (!vlink_desc_arr) | 
 | 1203 | 		return; | 
 | 1204 | 	num_vlink_desc = 0; | 
 | 1205 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1206 | 	while (rlen >= sizeof(*desc)) { | 
 | 1207 | 		dlen = desc->fip_dlen * FIP_BPW; | 
 | 1208 | 		if (dlen > rlen) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1209 | 			goto err; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 1210 | 		/* Drop CVL if there are duplicate critical descriptors */ | 
 | 1211 | 		if ((desc->fip_dtype < 32) && | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1212 | 		    (desc->fip_dtype != FIP_DT_VN_ID) && | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 1213 | 		    !(desc_mask & 1U << desc->fip_dtype)) { | 
 | 1214 | 			LIBFCOE_FIP_DBG(fip, "Duplicate Critical " | 
 | 1215 | 					"Descriptors in FIP CVL\n"); | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1216 | 			goto err; | 
| Bhanu Prakash Gollapudi | 0a9c5d3 | 2010-06-11 16:44:25 -0700 | [diff] [blame] | 1217 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1218 | 		switch (desc->fip_dtype) { | 
 | 1219 | 		case FIP_DT_MAC: | 
 | 1220 | 			mp = (struct fip_mac_desc *)desc; | 
 | 1221 | 			if (dlen < sizeof(*mp)) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1222 | 				goto err; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1223 | 			if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac)) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1224 | 				goto err; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1225 | 			desc_mask &= ~BIT(FIP_DT_MAC); | 
 | 1226 | 			break; | 
 | 1227 | 		case FIP_DT_NAME: | 
 | 1228 | 			wp = (struct fip_wwn_desc *)desc; | 
 | 1229 | 			if (dlen < sizeof(*wp)) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1230 | 				goto err; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1231 | 			if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1232 | 				goto err; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1233 | 			desc_mask &= ~BIT(FIP_DT_NAME); | 
 | 1234 | 			break; | 
 | 1235 | 		case FIP_DT_VN_ID: | 
 | 1236 | 			vp = (struct fip_vn_desc *)desc; | 
 | 1237 | 			if (dlen < sizeof(*vp)) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1238 | 				goto err; | 
 | 1239 | 			vlink_desc_arr[num_vlink_desc++] = vp; | 
 | 1240 | 			vn_port = fc_vport_id_lookup(lport, | 
 | 1241 | 						      ntoh24(vp->fd_fc_id)); | 
 | 1242 | 			if (vn_port && (vn_port == lport)) { | 
 | 1243 | 				mutex_lock(&fip->ctlr_mutex); | 
 | 1244 | 				per_cpu_ptr(lport->dev_stats, | 
 | 1245 | 					    get_cpu())->VLinkFailureCount++; | 
 | 1246 | 				put_cpu(); | 
 | 1247 | 				fcoe_ctlr_reset(fip); | 
 | 1248 | 				mutex_unlock(&fip->ctlr_mutex); | 
| Bhanu Prakash Gollapudi | 5550fda | 2010-06-11 16:44:31 -0700 | [diff] [blame] | 1249 | 			} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1250 | 			break; | 
 | 1251 | 		default: | 
 | 1252 | 			/* standard says ignore unknown descriptors >= 128 */ | 
 | 1253 | 			if (desc->fip_dtype < FIP_DT_VENDOR_BASE) | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1254 | 				goto err; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1255 | 			break; | 
 | 1256 | 		} | 
 | 1257 | 		desc = (struct fip_desc *)((char *)desc + dlen); | 
 | 1258 | 		rlen -= dlen; | 
 | 1259 | 	} | 
 | 1260 |  | 
 | 1261 | 	/* | 
 | 1262 | 	 * reset only if all required descriptors were present and valid. | 
 | 1263 | 	 */ | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1264 | 	if (desc_mask) | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 1265 | 		LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n", | 
 | 1266 | 				desc_mask); | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1267 | 	else if (!num_vlink_desc) { | 
 | 1268 | 		LIBFCOE_FIP_DBG(fip, "CVL: no Vx_Port descriptor found\n"); | 
 | 1269 | 		/* | 
 | 1270 | 		 * No Vx_Port description. Clear all NPIV ports, | 
 | 1271 | 		 * followed by physical port | 
 | 1272 | 		 */ | 
 | 1273 | 		mutex_lock(&lport->lp_mutex); | 
 | 1274 | 		list_for_each_entry(vn_port, &lport->vports, list) | 
| Bhanu Prakash Gollapudi | 5550fda | 2010-06-11 16:44:31 -0700 | [diff] [blame] | 1275 | 			fc_lport_reset(vn_port); | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1276 | 		mutex_unlock(&lport->lp_mutex); | 
| Joe Eykholt | dd42dac | 2009-11-03 11:48:27 -0800 | [diff] [blame] | 1277 |  | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1278 | 		mutex_lock(&fip->ctlr_mutex); | 
 | 1279 | 		per_cpu_ptr(lport->dev_stats, | 
 | 1280 | 			    get_cpu())->VLinkFailureCount++; | 
 | 1281 | 		put_cpu(); | 
 | 1282 | 		fcoe_ctlr_reset(fip); | 
 | 1283 | 		mutex_unlock(&fip->ctlr_mutex); | 
 | 1284 |  | 
 | 1285 | 		fc_lport_reset(fip->lp); | 
 | 1286 | 		fcoe_ctlr_solicit(fip, NULL); | 
 | 1287 | 	} else { | 
 | 1288 | 		int i; | 
 | 1289 |  | 
 | 1290 | 		LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); | 
 | 1291 | 		for (i = 0; i < num_vlink_desc; i++) { | 
 | 1292 | 			vp = vlink_desc_arr[i]; | 
 | 1293 | 			vn_port = fc_vport_id_lookup(lport, | 
 | 1294 | 						     ntoh24(vp->fd_fc_id)); | 
 | 1295 | 			if (!vn_port) | 
 | 1296 | 				continue; | 
 | 1297 |  | 
 | 1298 | 			/* | 
 | 1299 | 			 * 'port_id' is already validated, check MAC address and | 
 | 1300 | 			 * wwpn | 
 | 1301 | 			 */ | 
 | 1302 | 			if (compare_ether_addr(fip->get_src_addr(vn_port), | 
 | 1303 | 						vp->fd_mac) != 0 || | 
 | 1304 | 				get_unaligned_be64(&vp->fd_wwpn) != | 
 | 1305 | 							vn_port->wwpn) | 
 | 1306 | 				continue; | 
 | 1307 |  | 
 | 1308 | 			if (vn_port == lport) | 
 | 1309 | 				/* | 
 | 1310 | 				 * Physical port, defer processing till all | 
 | 1311 | 				 * listed NPIV ports are cleared | 
 | 1312 | 				 */ | 
 | 1313 | 				reset_phys_port = 1; | 
 | 1314 | 			else    /* NPIV port */ | 
 | 1315 | 				fc_lport_reset(vn_port); | 
 | 1316 | 		} | 
 | 1317 |  | 
 | 1318 | 		if (reset_phys_port) { | 
| Bhanu Prakash Gollapudi | 5550fda | 2010-06-11 16:44:31 -0700 | [diff] [blame] | 1319 | 			fc_lport_reset(fip->lp); | 
 | 1320 | 			fcoe_ctlr_solicit(fip, NULL); | 
 | 1321 | 		} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1322 | 	} | 
| Bhanu Prakash Gollapudi | c051ad2 | 2011-05-16 16:45:24 -0700 | [diff] [blame] | 1323 |  | 
 | 1324 | err: | 
 | 1325 | 	kfree(vlink_desc_arr); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1326 | } | 
 | 1327 |  | 
 | 1328 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1329 |  * fcoe_ctlr_recv() - Receive a FIP packet | 
 | 1330 |  * @fip: The FCoE controller that received the packet | 
 | 1331 |  * @skb: The received FIP packet | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1332 |  * | 
| Joe Eykholt | 1f4aed8 | 2009-11-03 11:48:22 -0800 | [diff] [blame] | 1333 |  * This may be called from either NET_RX_SOFTIRQ or IRQ. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1334 |  */ | 
 | 1335 | void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb) | 
 | 1336 | { | 
| Joe Eykholt | 1f4aed8 | 2009-11-03 11:48:22 -0800 | [diff] [blame] | 1337 | 	skb_queue_tail(&fip->fip_recv_list, skb); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1338 | 	schedule_work(&fip->recv_work); | 
 | 1339 | } | 
 | 1340 | EXPORT_SYMBOL(fcoe_ctlr_recv); | 
 | 1341 |  | 
 | 1342 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1343 |  * fcoe_ctlr_recv_handler() - Receive a FIP frame | 
 | 1344 |  * @fip: The FCoE controller that received the frame | 
 | 1345 |  * @skb: The received FIP frame | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1346 |  * | 
 | 1347 |  * Returns non-zero if the frame is dropped. | 
 | 1348 |  */ | 
 | 1349 | static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) | 
 | 1350 | { | 
 | 1351 | 	struct fip_header *fiph; | 
 | 1352 | 	struct ethhdr *eh; | 
 | 1353 | 	enum fip_state state; | 
 | 1354 | 	u16 op; | 
 | 1355 | 	u8 sub; | 
 | 1356 |  | 
 | 1357 | 	if (skb_linearize(skb)) | 
 | 1358 | 		goto drop; | 
 | 1359 | 	if (skb->len < sizeof(*fiph)) | 
 | 1360 | 		goto drop; | 
 | 1361 | 	eh = eth_hdr(skb); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1362 | 	if (fip->mode == FIP_MODE_VN2VN) { | 
 | 1363 | 		if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) && | 
 | 1364 | 		    compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) && | 
 | 1365 | 		    compare_ether_addr(eh->h_dest, fcoe_all_p2p)) | 
 | 1366 | 			goto drop; | 
 | 1367 | 	} else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) && | 
 | 1368 | 		   compare_ether_addr(eh->h_dest, fcoe_all_enode)) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1369 | 		goto drop; | 
 | 1370 | 	fiph = (struct fip_header *)skb->data; | 
 | 1371 | 	op = ntohs(fiph->fip_op); | 
 | 1372 | 	sub = fiph->fip_subcode; | 
 | 1373 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1374 | 	if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER) | 
 | 1375 | 		goto drop; | 
 | 1376 | 	if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len) | 
 | 1377 | 		goto drop; | 
 | 1378 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1379 | 	mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1380 | 	state = fip->state; | 
 | 1381 | 	if (state == FIP_ST_AUTO) { | 
 | 1382 | 		fip->map_dest = 0; | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 1383 | 		fcoe_ctlr_set_state(fip, FIP_ST_ENABLED); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1384 | 		state = FIP_ST_ENABLED; | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 1385 | 		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n"); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1386 | 	} | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1387 | 	mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1388 |  | 
 | 1389 | 	if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN) | 
 | 1390 | 		return fcoe_ctlr_vn_recv(fip, skb); | 
 | 1391 |  | 
 | 1392 | 	if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP && | 
 | 1393 | 	    state != FIP_ST_VNMP_CLAIM) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1394 | 		goto drop; | 
 | 1395 |  | 
 | 1396 | 	if (op == FIP_OP_LS) { | 
 | 1397 | 		fcoe_ctlr_recv_els(fip, skb);	/* consumes skb */ | 
 | 1398 | 		return 0; | 
 | 1399 | 	} | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1400 |  | 
 | 1401 | 	if (state != FIP_ST_ENABLED) | 
 | 1402 | 		goto drop; | 
 | 1403 |  | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1404 | 	if (op == FIP_OP_DISC && sub == FIP_SC_ADV) | 
 | 1405 | 		fcoe_ctlr_recv_adv(fip, skb); | 
 | 1406 | 	else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) | 
 | 1407 | 		fcoe_ctlr_recv_clr_vlink(fip, fiph); | 
 | 1408 | 	kfree_skb(skb); | 
 | 1409 | 	return 0; | 
 | 1410 | drop: | 
 | 1411 | 	kfree_skb(skb); | 
 | 1412 | 	return -1; | 
 | 1413 | } | 
 | 1414 |  | 
 | 1415 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1416 |  * fcoe_ctlr_select() - Select the best FCF (if possible) | 
 | 1417 |  * @fip: The FCoE controller | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1418 |  * | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1419 |  * Returns the selected FCF, or NULL if none are usable. | 
 | 1420 |  * | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1421 |  * If there are conflicting advertisements, no FCF can be chosen. | 
 | 1422 |  * | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1423 |  * If there is already a selected FCF, this will choose a better one or | 
 | 1424 |  * an equivalent one that hasn't already been sent a FLOGI. | 
 | 1425 |  * | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1426 |  * Called with lock held. | 
 | 1427 |  */ | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1428 | static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1429 | { | 
 | 1430 | 	struct fcoe_fcf *fcf; | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1431 | 	struct fcoe_fcf *best = fip->sel_fcf; | 
| Joe Eykholt | b69ae0a | 2010-11-30 16:19:51 -0800 | [diff] [blame] | 1432 | 	struct fcoe_fcf *first; | 
 | 1433 |  | 
 | 1434 | 	first = list_first_entry(&fip->fcfs, struct fcoe_fcf, list); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1435 |  | 
 | 1436 | 	list_for_each_entry(fcf, &fip->fcfs, list) { | 
| Joe Eykholt | 9069f5c | 2010-11-30 16:20:02 -0800 | [diff] [blame] | 1437 | 		LIBFCOE_FIP_DBG(fip, "consider FCF fab %16.16llx " | 
 | 1438 | 				"VFID %d mac %pM map %x val %d " | 
 | 1439 | 				"sent %u pri %u\n", | 
 | 1440 | 				fcf->fabric_name, fcf->vfid, fcf->fcf_mac, | 
 | 1441 | 				fcf->fc_map, fcoe_ctlr_mtu_valid(fcf), | 
 | 1442 | 				fcf->flogi_sent, fcf->pri); | 
| Joe Eykholt | b69ae0a | 2010-11-30 16:19:51 -0800 | [diff] [blame] | 1443 | 		if (fcf->fabric_name != first->fabric_name || | 
 | 1444 | 		    fcf->vfid != first->vfid || | 
 | 1445 | 		    fcf->fc_map != first->fc_map) { | 
 | 1446 | 			LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, " | 
 | 1447 | 					"or FC-MAP\n"); | 
 | 1448 | 			return NULL; | 
 | 1449 | 		} | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1450 | 		if (fcf->flogi_sent) | 
 | 1451 | 			continue; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1452 | 		if (!fcoe_ctlr_fcf_usable(fcf)) { | 
| Chris Leech | 9f8f3aa | 2010-04-09 14:23:16 -0700 | [diff] [blame] | 1453 | 			LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx " | 
 | 1454 | 					"map %x %svalid %savailable\n", | 
 | 1455 | 					fcf->fabric_name, fcf->fc_map, | 
 | 1456 | 					(fcf->flags & FIP_FL_SOL) ? "" : "in", | 
 | 1457 | 					(fcf->flags & FIP_FL_AVAIL) ? | 
 | 1458 | 					"" : "un"); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1459 | 			continue; | 
 | 1460 | 		} | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1461 | 		if (!best || fcf->pri < best->pri || best->flogi_sent) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1462 | 			best = fcf; | 
 | 1463 | 	} | 
 | 1464 | 	fip->sel_fcf = best; | 
| Joe Eykholt | c47036a | 2010-11-30 16:19:46 -0800 | [diff] [blame] | 1465 | 	if (best) { | 
| Joe Eykholt | 9069f5c | 2010-11-30 16:20:02 -0800 | [diff] [blame] | 1466 | 		LIBFCOE_FIP_DBG(fip, "using FCF mac %pM\n", best->fcf_mac); | 
| Joe Eykholt | c47036a | 2010-11-30 16:19:46 -0800 | [diff] [blame] | 1467 | 		fip->port_ka_time = jiffies + | 
 | 1468 | 			msecs_to_jiffies(FIP_VN_KA_PERIOD); | 
 | 1469 | 		fip->ctlr_ka_time = jiffies + best->fka_period; | 
 | 1470 | 		if (time_before(fip->ctlr_ka_time, fip->timer.expires)) | 
 | 1471 | 			mod_timer(&fip->timer, fip->ctlr_ka_time); | 
 | 1472 | 	} | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1473 | 	return best; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1474 | } | 
 | 1475 |  | 
 | 1476 | /** | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1477 |  * fcoe_ctlr_flogi_send_locked() - send FIP-encapsulated FLOGI to current FCF | 
 | 1478 |  * @fip: The FCoE controller | 
 | 1479 |  * | 
 | 1480 |  * Returns non-zero error if it could not be sent. | 
 | 1481 |  * | 
 | 1482 |  * Called with ctlr_mutex and ctlr_lock held. | 
 | 1483 |  * Caller must verify that fip->sel_fcf is not NULL. | 
 | 1484 |  */ | 
 | 1485 | static int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip) | 
 | 1486 | { | 
 | 1487 | 	struct sk_buff *skb; | 
 | 1488 | 	struct sk_buff *skb_orig; | 
 | 1489 | 	struct fc_frame_header *fh; | 
 | 1490 | 	int error; | 
 | 1491 |  | 
 | 1492 | 	skb_orig = fip->flogi_req; | 
 | 1493 | 	if (!skb_orig) | 
 | 1494 | 		return -EINVAL; | 
 | 1495 |  | 
 | 1496 | 	/* | 
 | 1497 | 	 * Clone and send the FLOGI request.  If clone fails, use original. | 
 | 1498 | 	 */ | 
 | 1499 | 	skb = skb_clone(skb_orig, GFP_ATOMIC); | 
 | 1500 | 	if (!skb) { | 
 | 1501 | 		skb = skb_orig; | 
 | 1502 | 		fip->flogi_req = NULL; | 
 | 1503 | 	} | 
 | 1504 | 	fh = (struct fc_frame_header *)skb->data; | 
 | 1505 | 	error = fcoe_ctlr_encaps(fip, fip->lp, FIP_DT_FLOGI, skb, | 
 | 1506 | 				 ntoh24(fh->fh_d_id)); | 
 | 1507 | 	if (error) { | 
 | 1508 | 		kfree_skb(skb); | 
 | 1509 | 		return error; | 
 | 1510 | 	} | 
 | 1511 | 	fip->send(fip, skb); | 
 | 1512 | 	fip->sel_fcf->flogi_sent = 1; | 
 | 1513 | 	return 0; | 
 | 1514 | } | 
 | 1515 |  | 
 | 1516 | /** | 
 | 1517 |  * fcoe_ctlr_flogi_retry() - resend FLOGI request to a new FCF if possible | 
 | 1518 |  * @fip: The FCoE controller | 
 | 1519 |  * | 
 | 1520 |  * Returns non-zero error code if there's no FLOGI request to retry or | 
 | 1521 |  * no alternate FCF available. | 
 | 1522 |  */ | 
 | 1523 | static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip) | 
 | 1524 | { | 
 | 1525 | 	struct fcoe_fcf *fcf; | 
 | 1526 | 	int error; | 
 | 1527 |  | 
 | 1528 | 	mutex_lock(&fip->ctlr_mutex); | 
 | 1529 | 	spin_lock_bh(&fip->ctlr_lock); | 
 | 1530 | 	LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n"); | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1531 | 	fcf = fcoe_ctlr_select(fip); | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1532 | 	if (!fcf || fcf->flogi_sent) { | 
 | 1533 | 		kfree_skb(fip->flogi_req); | 
 | 1534 | 		fip->flogi_req = NULL; | 
 | 1535 | 		error = -ENOENT; | 
 | 1536 | 	} else { | 
 | 1537 | 		fcoe_ctlr_solicit(fip, NULL); | 
 | 1538 | 		error = fcoe_ctlr_flogi_send_locked(fip); | 
 | 1539 | 	} | 
 | 1540 | 	spin_unlock_bh(&fip->ctlr_lock); | 
 | 1541 | 	mutex_unlock(&fip->ctlr_mutex); | 
 | 1542 | 	return error; | 
 | 1543 | } | 
 | 1544 |  | 
 | 1545 |  | 
 | 1546 | /** | 
 | 1547 |  * fcoe_ctlr_flogi_send() - Handle sending of FIP FLOGI. | 
 | 1548 |  * @fip: The FCoE controller that timed out | 
 | 1549 |  * | 
 | 1550 |  * Done here because fcoe_ctlr_els_send() can't get mutex. | 
 | 1551 |  * | 
 | 1552 |  * Called with ctlr_mutex held.  The caller must not hold ctlr_lock. | 
 | 1553 |  */ | 
 | 1554 | static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip) | 
 | 1555 | { | 
 | 1556 | 	struct fcoe_fcf *fcf; | 
 | 1557 |  | 
 | 1558 | 	spin_lock_bh(&fip->ctlr_lock); | 
 | 1559 | 	fcf = fip->sel_fcf; | 
 | 1560 | 	if (!fcf || !fip->flogi_req_send) | 
 | 1561 | 		goto unlock; | 
 | 1562 |  | 
 | 1563 | 	LIBFCOE_FIP_DBG(fip, "sending FLOGI\n"); | 
 | 1564 |  | 
 | 1565 | 	/* | 
 | 1566 | 	 * If this FLOGI is being sent due to a timeout retry | 
 | 1567 | 	 * to the same FCF as before, select a different FCF if possible. | 
 | 1568 | 	 */ | 
 | 1569 | 	if (fcf->flogi_sent) { | 
 | 1570 | 		LIBFCOE_FIP_DBG(fip, "sending FLOGI - reselect\n"); | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1571 | 		fcf = fcoe_ctlr_select(fip); | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1572 | 		if (!fcf || fcf->flogi_sent) { | 
 | 1573 | 			LIBFCOE_FIP_DBG(fip, "sending FLOGI - clearing\n"); | 
 | 1574 | 			list_for_each_entry(fcf, &fip->fcfs, list) | 
 | 1575 | 				fcf->flogi_sent = 0; | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1576 | 			fcf = fcoe_ctlr_select(fip); | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1577 | 		} | 
 | 1578 | 	} | 
 | 1579 | 	if (fcf) { | 
 | 1580 | 		fcoe_ctlr_flogi_send_locked(fip); | 
 | 1581 | 		fip->flogi_req_send = 0; | 
 | 1582 | 	} else /* XXX */ | 
 | 1583 | 		LIBFCOE_FIP_DBG(fip, "No FCF selected - defer send\n"); | 
 | 1584 | unlock: | 
 | 1585 | 	spin_unlock_bh(&fip->ctlr_lock); | 
 | 1586 | } | 
 | 1587 |  | 
 | 1588 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1589 |  * fcoe_ctlr_timeout() - FIP timeout handler | 
 | 1590 |  * @arg: The FCoE controller that timed out | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1591 |  */ | 
 | 1592 | static void fcoe_ctlr_timeout(unsigned long arg) | 
 | 1593 | { | 
 | 1594 | 	struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg; | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1595 |  | 
 | 1596 | 	schedule_work(&fip->timer_work); | 
 | 1597 | } | 
 | 1598 |  | 
 | 1599 | /** | 
 | 1600 |  * fcoe_ctlr_timer_work() - Worker thread function for timer work | 
 | 1601 |  * @work: Handle to a FCoE controller | 
 | 1602 |  * | 
 | 1603 |  * Ages FCFs.  Triggers FCF selection if possible. | 
 | 1604 |  * Sends keep-alives and resets. | 
 | 1605 |  */ | 
 | 1606 | static void fcoe_ctlr_timer_work(struct work_struct *work) | 
 | 1607 | { | 
 | 1608 | 	struct fcoe_ctlr *fip; | 
 | 1609 | 	struct fc_lport *vport; | 
 | 1610 | 	u8 *mac; | 
 | 1611 | 	u8 reset = 0; | 
 | 1612 | 	u8 send_ctlr_ka = 0; | 
 | 1613 | 	u8 send_port_ka = 0; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1614 | 	struct fcoe_fcf *sel; | 
 | 1615 | 	struct fcoe_fcf *fcf; | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 1616 | 	unsigned long next_timer; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1617 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1618 | 	fip = container_of(work, struct fcoe_ctlr, timer_work); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1619 | 	if (fip->mode == FIP_MODE_VN2VN) | 
 | 1620 | 		return fcoe_ctlr_vn_timeout(fip); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1621 | 	mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1622 | 	if (fip->state == FIP_ST_DISABLED) { | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1623 | 		mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1624 | 		return; | 
 | 1625 | 	} | 
 | 1626 |  | 
 | 1627 | 	fcf = fip->sel_fcf; | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 1628 | 	next_timer = fcoe_ctlr_age_fcfs(fip); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1629 |  | 
 | 1630 | 	sel = fip->sel_fcf; | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 1631 | 	if (!sel && fip->sel_time) { | 
 | 1632 | 		if (time_after_eq(jiffies, fip->sel_time)) { | 
| Joe Eykholt | ba9cd5d | 2010-11-30 16:20:12 -0800 | [diff] [blame] | 1633 | 			sel = fcoe_ctlr_select(fip); | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 1634 | 			fip->sel_time = 0; | 
 | 1635 | 		} else if (time_after(next_timer, fip->sel_time)) | 
 | 1636 | 			next_timer = fip->sel_time; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1637 | 	} | 
 | 1638 |  | 
| Joe Eykholt | 794d98e | 2010-11-30 16:19:56 -0800 | [diff] [blame] | 1639 | 	if (sel && fip->flogi_req_send) | 
 | 1640 | 		fcoe_ctlr_flogi_send(fip); | 
 | 1641 | 	else if (!sel && fcf) | 
 | 1642 | 		reset = 1; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1643 |  | 
| Yi Zou | 8cdffdc | 2009-11-20 14:54:57 -0800 | [diff] [blame] | 1644 | 	if (sel && !sel->fd_flags) { | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1645 | 		if (time_after_eq(jiffies, fip->ctlr_ka_time)) { | 
 | 1646 | 			fip->ctlr_ka_time = jiffies + sel->fka_period; | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1647 | 			send_ctlr_ka = 1; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1648 | 		} | 
 | 1649 | 		if (time_after(next_timer, fip->ctlr_ka_time)) | 
 | 1650 | 			next_timer = fip->ctlr_ka_time; | 
 | 1651 |  | 
 | 1652 | 		if (time_after_eq(jiffies, fip->port_ka_time)) { | 
| Bhanu Prakash Gollapudi | f47dd85 | 2010-01-21 10:16:00 -0800 | [diff] [blame] | 1653 | 			fip->port_ka_time = jiffies + | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1654 | 				msecs_to_jiffies(FIP_VN_KA_PERIOD); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1655 | 			send_port_ka = 1; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1656 | 		} | 
 | 1657 | 		if (time_after(next_timer, fip->port_ka_time)) | 
 | 1658 | 			next_timer = fip->port_ka_time; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1659 | 	} | 
| Joe Eykholt | 8690cb8 | 2010-06-11 16:44:10 -0700 | [diff] [blame] | 1660 | 	if (!list_empty(&fip->fcfs)) | 
 | 1661 | 		mod_timer(&fip->timer, next_timer); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1662 | 	mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1663 |  | 
| Bhanu Prakash Gollapudi | 516a648 | 2010-06-11 16:43:44 -0700 | [diff] [blame] | 1664 | 	if (reset) { | 
| Joe Eykholt | dd42dac | 2009-11-03 11:48:27 -0800 | [diff] [blame] | 1665 | 		fc_lport_reset(fip->lp); | 
| Bhanu Prakash Gollapudi | 516a648 | 2010-06-11 16:43:44 -0700 | [diff] [blame] | 1666 | 		/* restart things with a solicitation */ | 
 | 1667 | 		fcoe_ctlr_solicit(fip, NULL); | 
 | 1668 | 	} | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 1669 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1670 | 	if (send_ctlr_ka) | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 1671 | 		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr); | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1672 |  | 
 | 1673 | 	if (send_port_ka) { | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 1674 | 		mutex_lock(&fip->lp->lp_mutex); | 
 | 1675 | 		mac = fip->get_src_addr(fip->lp); | 
 | 1676 | 		fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac); | 
 | 1677 | 		list_for_each_entry(vport, &fip->lp->vports, list) { | 
 | 1678 | 			mac = fip->get_src_addr(vport); | 
 | 1679 | 			fcoe_ctlr_send_keep_alive(fip, vport, 1, mac); | 
 | 1680 | 		} | 
 | 1681 | 		mutex_unlock(&fip->lp->lp_mutex); | 
 | 1682 | 	} | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1683 | } | 
 | 1684 |  | 
 | 1685 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1686 |  * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames | 
 | 1687 |  * @recv_work: Handle to a FCoE controller | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1688 |  */ | 
 | 1689 | static void fcoe_ctlr_recv_work(struct work_struct *recv_work) | 
 | 1690 | { | 
 | 1691 | 	struct fcoe_ctlr *fip; | 
 | 1692 | 	struct sk_buff *skb; | 
 | 1693 |  | 
 | 1694 | 	fip = container_of(recv_work, struct fcoe_ctlr, recv_work); | 
| Joe Eykholt | 1f4aed8 | 2009-11-03 11:48:22 -0800 | [diff] [blame] | 1695 | 	while ((skb = skb_dequeue(&fip->fip_recv_list))) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1696 | 		fcoe_ctlr_recv_handler(fip, skb); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1697 | } | 
 | 1698 |  | 
 | 1699 | /** | 
| Joe Eykholt | 386309c | 2009-11-03 11:49:16 -0800 | [diff] [blame] | 1700 |  * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1701 |  * @fip: The FCoE controller | 
 | 1702 |  * @fp:	 The FC frame to snoop | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1703 |  * | 
 | 1704 |  * Snoop potential response to FLOGI or even incoming FLOGI. | 
 | 1705 |  * | 
 | 1706 |  * The caller has checked that we are waiting for login as indicated | 
 | 1707 |  * by fip->flogi_oxid != FC_XID_UNKNOWN. | 
 | 1708 |  * | 
 | 1709 |  * The caller is responsible for freeing the frame. | 
| Joe Eykholt | 386309c | 2009-11-03 11:49:16 -0800 | [diff] [blame] | 1710 |  * Fill in the granted_mac address. | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1711 |  * | 
 | 1712 |  * Return non-zero if the frame should not be delivered to libfc. | 
 | 1713 |  */ | 
| Chris Leech | 11b5618 | 2009-11-03 11:46:29 -0800 | [diff] [blame] | 1714 | int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, | 
| Joe Eykholt | 386309c | 2009-11-03 11:49:16 -0800 | [diff] [blame] | 1715 | 			 struct fc_frame *fp) | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1716 | { | 
 | 1717 | 	struct fc_frame_header *fh; | 
 | 1718 | 	u8 op; | 
| Joe Eykholt | 386309c | 2009-11-03 11:49:16 -0800 | [diff] [blame] | 1719 | 	u8 *sa; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1720 |  | 
| Joe Eykholt | 386309c | 2009-11-03 11:49:16 -0800 | [diff] [blame] | 1721 | 	sa = eth_hdr(&fp->skb)->h_source; | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1722 | 	fh = fc_frame_header_get(fp); | 
 | 1723 | 	if (fh->fh_type != FC_TYPE_ELS) | 
 | 1724 | 		return 0; | 
 | 1725 |  | 
 | 1726 | 	op = fc_frame_payload_op(fp); | 
 | 1727 | 	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP && | 
 | 1728 | 	    fip->flogi_oxid == ntohs(fh->fh_ox_id)) { | 
 | 1729 |  | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1730 | 		mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1731 | 		if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) { | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1732 | 			mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1733 | 			return -EINVAL; | 
 | 1734 | 		} | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 1735 | 		fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP); | 
| Joe Eykholt | 0f51c2e | 2009-11-03 11:48:16 -0800 | [diff] [blame] | 1736 | 		LIBFCOE_FIP_DBG(fip, | 
 | 1737 | 				"received FLOGI LS_ACC using non-FIP mode\n"); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1738 |  | 
 | 1739 | 		/* | 
 | 1740 | 		 * FLOGI accepted. | 
 | 1741 | 		 * If the src mac addr is FC_OUI-based, then we mark the | 
 | 1742 | 		 * address_mode flag to use FC_OUI-based Ethernet DA. | 
 | 1743 | 		 * Otherwise we use the FCoE gateway addr | 
 | 1744 | 		 */ | 
 | 1745 | 		if (!compare_ether_addr(sa, (u8[6])FC_FCOE_FLOGI_MAC)) { | 
| Joe Eykholt | cd229e4 | 2010-07-20 15:20:40 -0700 | [diff] [blame] | 1746 | 			fcoe_ctlr_map_dest(fip); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1747 | 		} else { | 
 | 1748 | 			memcpy(fip->dest_addr, sa, ETH_ALEN); | 
 | 1749 | 			fip->map_dest = 0; | 
 | 1750 | 		} | 
 | 1751 | 		fip->flogi_oxid = FC_XID_UNKNOWN; | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1752 | 		mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 386309c | 2009-11-03 11:49:16 -0800 | [diff] [blame] | 1753 | 		fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1754 | 	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) { | 
 | 1755 | 		/* | 
 | 1756 | 		 * Save source MAC for point-to-point responses. | 
 | 1757 | 		 */ | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1758 | 		mutex_lock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1759 | 		if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) { | 
 | 1760 | 			memcpy(fip->dest_addr, sa, ETH_ALEN); | 
 | 1761 | 			fip->map_dest = 0; | 
| Joe Eykholt | e49bf61 | 2010-03-12 16:07:57 -0800 | [diff] [blame] | 1762 | 			if (fip->state == FIP_ST_AUTO) | 
 | 1763 | 				LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. " | 
 | 1764 | 						"Setting non-FIP mode\n"); | 
| Joe Eykholt | 9b651da | 2010-07-20 15:20:24 -0700 | [diff] [blame] | 1765 | 			fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1766 | 		} | 
| Joe Eykholt | fdb068c | 2010-07-20 15:19:47 -0700 | [diff] [blame] | 1767 | 		mutex_unlock(&fip->ctlr_mutex); | 
| Joe Eykholt | 97c8389 | 2009-03-17 11:42:40 -0700 | [diff] [blame] | 1768 | 	} | 
 | 1769 | 	return 0; | 
 | 1770 | } | 
 | 1771 | EXPORT_SYMBOL(fcoe_ctlr_recv_flogi); | 
 | 1772 |  | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 1773 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 1774 |  * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN | 
 | 1775 |  * @mac:    The MAC address to convert | 
 | 1776 |  * @scheme: The scheme to use when converting | 
 | 1777 |  * @port:   The port indicator for converting | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 1778 |  * | 
 | 1779 |  * Returns: u64 fc world wide name | 
 | 1780 |  */ | 
 | 1781 | u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], | 
 | 1782 | 		      unsigned int scheme, unsigned int port) | 
 | 1783 | { | 
 | 1784 | 	u64 wwn; | 
 | 1785 | 	u64 host_mac; | 
 | 1786 |  | 
 | 1787 | 	/* The MAC is in NO, so flip only the low 48 bits */ | 
 | 1788 | 	host_mac = ((u64) mac[0] << 40) | | 
 | 1789 | 		((u64) mac[1] << 32) | | 
 | 1790 | 		((u64) mac[2] << 24) | | 
 | 1791 | 		((u64) mac[3] << 16) | | 
 | 1792 | 		((u64) mac[4] << 8) | | 
 | 1793 | 		(u64) mac[5]; | 
 | 1794 |  | 
 | 1795 | 	WARN_ON(host_mac >= (1ULL << 48)); | 
 | 1796 | 	wwn = host_mac | ((u64) scheme << 60); | 
 | 1797 | 	switch (scheme) { | 
 | 1798 | 	case 1: | 
 | 1799 | 		WARN_ON(port != 0); | 
 | 1800 | 		break; | 
 | 1801 | 	case 2: | 
 | 1802 | 		WARN_ON(port >= 0xfff); | 
 | 1803 | 		wwn |= (u64) port << 48; | 
 | 1804 | 		break; | 
 | 1805 | 	default: | 
 | 1806 | 		WARN_ON(1); | 
 | 1807 | 		break; | 
 | 1808 | 	} | 
 | 1809 |  | 
 | 1810 | 	return wwn; | 
 | 1811 | } | 
 | 1812 | EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac); | 
 | 1813 |  | 
 | 1814 | /** | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1815 |  * fcoe_ctlr_rport() - return the fcoe_rport for a given fc_rport_priv | 
 | 1816 |  * @rdata: libfc remote port | 
 | 1817 |  */ | 
 | 1818 | static inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata) | 
 | 1819 | { | 
 | 1820 | 	return (struct fcoe_rport *)(rdata + 1); | 
 | 1821 | } | 
 | 1822 |  | 
 | 1823 | /** | 
 | 1824 |  * fcoe_ctlr_vn_send() - Send a FIP VN2VN Probe Request or Reply. | 
 | 1825 |  * @fip: The FCoE controller | 
 | 1826 |  * @sub: sub-opcode for probe request, reply, or advertisement. | 
 | 1827 |  * @dest: The destination Ethernet MAC address | 
 | 1828 |  * @min_len: minimum size of the Ethernet payload to be sent | 
 | 1829 |  */ | 
 | 1830 | static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip, | 
 | 1831 | 			      enum fip_vn2vn_subcode sub, | 
 | 1832 | 			      const u8 *dest, size_t min_len) | 
 | 1833 | { | 
 | 1834 | 	struct sk_buff *skb; | 
 | 1835 | 	struct fip_frame { | 
 | 1836 | 		struct ethhdr eth; | 
 | 1837 | 		struct fip_header fip; | 
 | 1838 | 		struct fip_mac_desc mac; | 
 | 1839 | 		struct fip_wwn_desc wwnn; | 
 | 1840 | 		struct fip_vn_desc vn; | 
| Yi Zou | 0095a92 | 2011-01-28 16:05:00 -0800 | [diff] [blame] | 1841 | 	} __packed * frame; | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1842 | 	struct fip_fc4_feat *ff; | 
 | 1843 | 	struct fip_size_desc *size; | 
 | 1844 | 	u32 fcp_feat; | 
 | 1845 | 	size_t len; | 
 | 1846 | 	size_t dlen; | 
 | 1847 |  | 
 | 1848 | 	len = sizeof(*frame); | 
 | 1849 | 	dlen = 0; | 
 | 1850 | 	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) { | 
 | 1851 | 		dlen = sizeof(struct fip_fc4_feat) + | 
 | 1852 | 		       sizeof(struct fip_size_desc); | 
 | 1853 | 		len += dlen; | 
 | 1854 | 	} | 
 | 1855 | 	dlen += sizeof(frame->mac) + sizeof(frame->wwnn) + sizeof(frame->vn); | 
 | 1856 | 	len = max(len, min_len + sizeof(struct ethhdr)); | 
 | 1857 |  | 
 | 1858 | 	skb = dev_alloc_skb(len); | 
 | 1859 | 	if (!skb) | 
 | 1860 | 		return; | 
 | 1861 |  | 
 | 1862 | 	frame = (struct fip_frame *)skb->data; | 
 | 1863 | 	memset(frame, 0, len); | 
 | 1864 | 	memcpy(frame->eth.h_dest, dest, ETH_ALEN); | 
 | 1865 | 	memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN); | 
 | 1866 | 	frame->eth.h_proto = htons(ETH_P_FIP); | 
 | 1867 |  | 
 | 1868 | 	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); | 
 | 1869 | 	frame->fip.fip_op = htons(FIP_OP_VN2VN); | 
 | 1870 | 	frame->fip.fip_subcode = sub; | 
 | 1871 | 	frame->fip.fip_dl_len = htons(dlen / FIP_BPW); | 
 | 1872 |  | 
 | 1873 | 	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC; | 
 | 1874 | 	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW; | 
 | 1875 | 	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); | 
 | 1876 |  | 
 | 1877 | 	frame->wwnn.fd_desc.fip_dtype = FIP_DT_NAME; | 
 | 1878 | 	frame->wwnn.fd_desc.fip_dlen = sizeof(frame->wwnn) / FIP_BPW; | 
 | 1879 | 	put_unaligned_be64(fip->lp->wwnn, &frame->wwnn.fd_wwn); | 
 | 1880 |  | 
 | 1881 | 	frame->vn.fd_desc.fip_dtype = FIP_DT_VN_ID; | 
 | 1882 | 	frame->vn.fd_desc.fip_dlen = sizeof(frame->vn) / FIP_BPW; | 
 | 1883 | 	hton24(frame->vn.fd_mac, FIP_VN_FC_MAP); | 
 | 1884 | 	hton24(frame->vn.fd_mac + 3, fip->port_id); | 
 | 1885 | 	hton24(frame->vn.fd_fc_id, fip->port_id); | 
 | 1886 | 	put_unaligned_be64(fip->lp->wwpn, &frame->vn.fd_wwpn); | 
 | 1887 |  | 
 | 1888 | 	/* | 
 | 1889 | 	 * For claims, add FC-4 features. | 
 | 1890 | 	 * TBD: Add interface to get fc-4 types and features from libfc. | 
 | 1891 | 	 */ | 
 | 1892 | 	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) { | 
 | 1893 | 		ff = (struct fip_fc4_feat *)(frame + 1); | 
 | 1894 | 		ff->fd_desc.fip_dtype = FIP_DT_FC4F; | 
 | 1895 | 		ff->fd_desc.fip_dlen = sizeof(*ff) / FIP_BPW; | 
 | 1896 | 		ff->fd_fts = fip->lp->fcts; | 
 | 1897 |  | 
 | 1898 | 		fcp_feat = 0; | 
 | 1899 | 		if (fip->lp->service_params & FCP_SPPF_INIT_FCN) | 
 | 1900 | 			fcp_feat |= FCP_FEAT_INIT; | 
 | 1901 | 		if (fip->lp->service_params & FCP_SPPF_TARG_FCN) | 
 | 1902 | 			fcp_feat |= FCP_FEAT_TARG; | 
 | 1903 | 		fcp_feat <<= (FC_TYPE_FCP * 4) % 32; | 
 | 1904 | 		ff->fd_ff.fd_feat[FC_TYPE_FCP * 4 / 32] = htonl(fcp_feat); | 
 | 1905 |  | 
 | 1906 | 		size = (struct fip_size_desc *)(ff + 1); | 
 | 1907 | 		size->fd_desc.fip_dtype = FIP_DT_FCOE_SIZE; | 
 | 1908 | 		size->fd_desc.fip_dlen = sizeof(*size) / FIP_BPW; | 
 | 1909 | 		size->fd_size = htons(fcoe_ctlr_fcoe_size(fip)); | 
 | 1910 | 	} | 
 | 1911 |  | 
 | 1912 | 	skb_put(skb, len); | 
 | 1913 | 	skb->protocol = htons(ETH_P_FIP); | 
 | 1914 | 	skb_reset_mac_header(skb); | 
 | 1915 | 	skb_reset_network_header(skb); | 
 | 1916 |  | 
 | 1917 | 	fip->send(fip, skb); | 
 | 1918 | } | 
 | 1919 |  | 
 | 1920 | /** | 
 | 1921 |  * fcoe_ctlr_vn_rport_callback - Event handler for rport events. | 
 | 1922 |  * @lport: The lport which is receiving the event | 
 | 1923 |  * @rdata: remote port private data | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 1924 |  * @event: The event that occurred | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 1925 |  * | 
 | 1926 |  * Locking Note:  The rport lock must not be held when calling this function. | 
 | 1927 |  */ | 
 | 1928 | static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport, | 
 | 1929 | 					struct fc_rport_priv *rdata, | 
 | 1930 | 					enum fc_rport_event event) | 
 | 1931 | { | 
 | 1932 | 	struct fcoe_ctlr *fip = lport->disc.priv; | 
 | 1933 | 	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata); | 
 | 1934 |  | 
 | 1935 | 	LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n", | 
 | 1936 | 			rdata->ids.port_id, event); | 
 | 1937 |  | 
 | 1938 | 	mutex_lock(&fip->ctlr_mutex); | 
 | 1939 | 	switch (event) { | 
 | 1940 | 	case RPORT_EV_READY: | 
 | 1941 | 		frport->login_count = 0; | 
 | 1942 | 		break; | 
 | 1943 | 	case RPORT_EV_LOGO: | 
 | 1944 | 	case RPORT_EV_FAILED: | 
 | 1945 | 	case RPORT_EV_STOP: | 
 | 1946 | 		frport->login_count++; | 
 | 1947 | 		if (frport->login_count > FCOE_CTLR_VN2VN_LOGIN_LIMIT) { | 
 | 1948 | 			LIBFCOE_FIP_DBG(fip, | 
 | 1949 | 					"rport FLOGI limited port_id %6.6x\n", | 
 | 1950 | 					rdata->ids.port_id); | 
 | 1951 | 			lport->tt.rport_logoff(rdata); | 
 | 1952 | 		} | 
 | 1953 | 		break; | 
 | 1954 | 	default: | 
 | 1955 | 		break; | 
 | 1956 | 	} | 
 | 1957 | 	mutex_unlock(&fip->ctlr_mutex); | 
 | 1958 | } | 
 | 1959 |  | 
 | 1960 | static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = { | 
 | 1961 | 	.event_callback = fcoe_ctlr_vn_rport_callback, | 
 | 1962 | }; | 
 | 1963 |  | 
 | 1964 | /** | 
 | 1965 |  * fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode | 
 | 1966 |  * @fip: The FCoE controller | 
 | 1967 |  * | 
 | 1968 |  * Called with ctlr_mutex held. | 
 | 1969 |  */ | 
 | 1970 | static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport) | 
 | 1971 | { | 
 | 1972 | 	mutex_lock(&lport->disc.disc_mutex); | 
 | 1973 | 	lport->disc.disc_callback = NULL; | 
 | 1974 | 	mutex_unlock(&lport->disc.disc_mutex); | 
 | 1975 | } | 
 | 1976 |  | 
 | 1977 | /** | 
 | 1978 |  * fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode | 
 | 1979 |  * @fip: The FCoE controller | 
 | 1980 |  * | 
 | 1981 |  * Called through the local port template for discovery. | 
 | 1982 |  * Called without the ctlr_mutex held. | 
 | 1983 |  */ | 
 | 1984 | static void fcoe_ctlr_disc_stop(struct fc_lport *lport) | 
 | 1985 | { | 
 | 1986 | 	struct fcoe_ctlr *fip = lport->disc.priv; | 
 | 1987 |  | 
 | 1988 | 	mutex_lock(&fip->ctlr_mutex); | 
 | 1989 | 	fcoe_ctlr_disc_stop_locked(lport); | 
 | 1990 | 	mutex_unlock(&fip->ctlr_mutex); | 
 | 1991 | } | 
 | 1992 |  | 
 | 1993 | /** | 
 | 1994 |  * fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode | 
 | 1995 |  * @fip: The FCoE controller | 
 | 1996 |  * | 
 | 1997 |  * Called through the local port template for discovery. | 
 | 1998 |  * Called without the ctlr_mutex held. | 
 | 1999 |  */ | 
 | 2000 | static void fcoe_ctlr_disc_stop_final(struct fc_lport *lport) | 
 | 2001 | { | 
 | 2002 | 	fcoe_ctlr_disc_stop(lport); | 
 | 2003 | 	lport->tt.rport_flush_queue(); | 
 | 2004 | 	synchronize_rcu(); | 
 | 2005 | } | 
 | 2006 |  | 
 | 2007 | /** | 
 | 2008 |  * fcoe_ctlr_vn_restart() - VN2VN probe restart with new port_id | 
 | 2009 |  * @fip: The FCoE controller | 
 | 2010 |  * | 
 | 2011 |  * Called with fcoe_ctlr lock held. | 
 | 2012 |  */ | 
 | 2013 | static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip) | 
 | 2014 | { | 
 | 2015 | 	unsigned long wait; | 
 | 2016 | 	u32 port_id; | 
 | 2017 |  | 
 | 2018 | 	fcoe_ctlr_disc_stop_locked(fip->lp); | 
 | 2019 |  | 
 | 2020 | 	/* | 
 | 2021 | 	 * Get proposed port ID. | 
 | 2022 | 	 * If this is the first try after link up, use any previous port_id. | 
 | 2023 | 	 * If there was none, use the low bits of the port_name. | 
 | 2024 | 	 * On subsequent tries, get the next random one. | 
 | 2025 | 	 * Don't use reserved IDs, use another non-zero value, just as random. | 
 | 2026 | 	 */ | 
 | 2027 | 	port_id = fip->port_id; | 
 | 2028 | 	if (fip->probe_tries) | 
 | 2029 | 		port_id = prandom32(&fip->rnd_state) & 0xffff; | 
 | 2030 | 	else if (!port_id) | 
 | 2031 | 		port_id = fip->lp->wwpn & 0xffff; | 
 | 2032 | 	if (!port_id || port_id == 0xffff) | 
 | 2033 | 		port_id = 1; | 
 | 2034 | 	fip->port_id = port_id; | 
 | 2035 |  | 
 | 2036 | 	if (fip->probe_tries < FIP_VN_RLIM_COUNT) { | 
 | 2037 | 		fip->probe_tries++; | 
 | 2038 | 		wait = random32() % FIP_VN_PROBE_WAIT; | 
 | 2039 | 	} else | 
 | 2040 | 		wait = FIP_VN_RLIM_INT; | 
 | 2041 | 	mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait)); | 
 | 2042 | 	fcoe_ctlr_set_state(fip, FIP_ST_VNMP_START); | 
 | 2043 | } | 
 | 2044 |  | 
 | 2045 | /** | 
 | 2046 |  * fcoe_ctlr_vn_start() - Start in VN2VN mode | 
 | 2047 |  * @fip: The FCoE controller | 
 | 2048 |  * | 
 | 2049 |  * Called with fcoe_ctlr lock held. | 
 | 2050 |  */ | 
 | 2051 | static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip) | 
 | 2052 | { | 
 | 2053 | 	fip->probe_tries = 0; | 
 | 2054 | 	prandom32_seed(&fip->rnd_state, fip->lp->wwpn); | 
 | 2055 | 	fcoe_ctlr_vn_restart(fip); | 
 | 2056 | } | 
 | 2057 |  | 
 | 2058 | /** | 
 | 2059 |  * fcoe_ctlr_vn_parse - parse probe request or response | 
 | 2060 |  * @fip: The FCoE controller | 
 | 2061 |  * @skb: incoming packet | 
 | 2062 |  * @rdata: buffer for resulting parsed VN entry plus fcoe_rport | 
 | 2063 |  * | 
 | 2064 |  * Returns non-zero error number on error. | 
 | 2065 |  * Does not consume the packet. | 
 | 2066 |  */ | 
 | 2067 | static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip, | 
 | 2068 | 			      struct sk_buff *skb, | 
 | 2069 | 			      struct fc_rport_priv *rdata) | 
 | 2070 | { | 
 | 2071 | 	struct fip_header *fiph; | 
 | 2072 | 	struct fip_desc *desc = NULL; | 
 | 2073 | 	struct fip_mac_desc *macd = NULL; | 
 | 2074 | 	struct fip_wwn_desc *wwn = NULL; | 
 | 2075 | 	struct fip_vn_desc *vn = NULL; | 
 | 2076 | 	struct fip_size_desc *size = NULL; | 
 | 2077 | 	struct fcoe_rport *frport; | 
 | 2078 | 	size_t rlen; | 
 | 2079 | 	size_t dlen; | 
 | 2080 | 	u32 desc_mask = 0; | 
 | 2081 | 	u32 dtype; | 
 | 2082 | 	u8 sub; | 
 | 2083 |  | 
 | 2084 | 	memset(rdata, 0, sizeof(*rdata) + sizeof(*frport)); | 
 | 2085 | 	frport = fcoe_ctlr_rport(rdata); | 
 | 2086 |  | 
 | 2087 | 	fiph = (struct fip_header *)skb->data; | 
 | 2088 | 	frport->flags = ntohs(fiph->fip_flags); | 
 | 2089 |  | 
 | 2090 | 	sub = fiph->fip_subcode; | 
 | 2091 | 	switch (sub) { | 
 | 2092 | 	case FIP_SC_VN_PROBE_REQ: | 
 | 2093 | 	case FIP_SC_VN_PROBE_REP: | 
 | 2094 | 	case FIP_SC_VN_BEACON: | 
 | 2095 | 		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | | 
 | 2096 | 			    BIT(FIP_DT_VN_ID); | 
 | 2097 | 		break; | 
 | 2098 | 	case FIP_SC_VN_CLAIM_NOTIFY: | 
 | 2099 | 	case FIP_SC_VN_CLAIM_REP: | 
 | 2100 | 		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | | 
 | 2101 | 			    BIT(FIP_DT_VN_ID) | BIT(FIP_DT_FC4F) | | 
 | 2102 | 			    BIT(FIP_DT_FCOE_SIZE); | 
 | 2103 | 		break; | 
 | 2104 | 	default: | 
 | 2105 | 		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub); | 
 | 2106 | 		return -EINVAL; | 
 | 2107 | 	} | 
 | 2108 |  | 
 | 2109 | 	rlen = ntohs(fiph->fip_dl_len) * 4; | 
 | 2110 | 	if (rlen + sizeof(*fiph) > skb->len) | 
 | 2111 | 		return -EINVAL; | 
 | 2112 |  | 
 | 2113 | 	desc = (struct fip_desc *)(fiph + 1); | 
 | 2114 | 	while (rlen > 0) { | 
 | 2115 | 		dlen = desc->fip_dlen * FIP_BPW; | 
 | 2116 | 		if (dlen < sizeof(*desc) || dlen > rlen) | 
 | 2117 | 			return -EINVAL; | 
 | 2118 |  | 
 | 2119 | 		dtype = desc->fip_dtype; | 
 | 2120 | 		if (dtype < 32) { | 
 | 2121 | 			if (!(desc_mask & BIT(dtype))) { | 
 | 2122 | 				LIBFCOE_FIP_DBG(fip, | 
 | 2123 | 						"unexpected or duplicated desc " | 
 | 2124 | 						"desc type %u in " | 
 | 2125 | 						"FIP VN2VN subtype %u\n", | 
 | 2126 | 						dtype, sub); | 
 | 2127 | 				return -EINVAL; | 
 | 2128 | 			} | 
 | 2129 | 			desc_mask &= ~BIT(dtype); | 
 | 2130 | 		} | 
 | 2131 |  | 
 | 2132 | 		switch (dtype) { | 
 | 2133 | 		case FIP_DT_MAC: | 
 | 2134 | 			if (dlen != sizeof(struct fip_mac_desc)) | 
 | 2135 | 				goto len_err; | 
 | 2136 | 			macd = (struct fip_mac_desc *)desc; | 
 | 2137 | 			if (!is_valid_ether_addr(macd->fd_mac)) { | 
 | 2138 | 				LIBFCOE_FIP_DBG(fip, | 
 | 2139 | 					"Invalid MAC addr %pM in FIP VN2VN\n", | 
 | 2140 | 					 macd->fd_mac); | 
 | 2141 | 				return -EINVAL; | 
 | 2142 | 			} | 
 | 2143 | 			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN); | 
 | 2144 | 			break; | 
 | 2145 | 		case FIP_DT_NAME: | 
 | 2146 | 			if (dlen != sizeof(struct fip_wwn_desc)) | 
 | 2147 | 				goto len_err; | 
 | 2148 | 			wwn = (struct fip_wwn_desc *)desc; | 
 | 2149 | 			rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn); | 
 | 2150 | 			break; | 
 | 2151 | 		case FIP_DT_VN_ID: | 
 | 2152 | 			if (dlen != sizeof(struct fip_vn_desc)) | 
 | 2153 | 				goto len_err; | 
 | 2154 | 			vn = (struct fip_vn_desc *)desc; | 
 | 2155 | 			memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN); | 
 | 2156 | 			rdata->ids.port_id = ntoh24(vn->fd_fc_id); | 
 | 2157 | 			rdata->ids.port_name = get_unaligned_be64(&vn->fd_wwpn); | 
 | 2158 | 			break; | 
 | 2159 | 		case FIP_DT_FC4F: | 
 | 2160 | 			if (dlen != sizeof(struct fip_fc4_feat)) | 
 | 2161 | 				goto len_err; | 
 | 2162 | 			break; | 
 | 2163 | 		case FIP_DT_FCOE_SIZE: | 
 | 2164 | 			if (dlen != sizeof(struct fip_size_desc)) | 
 | 2165 | 				goto len_err; | 
 | 2166 | 			size = (struct fip_size_desc *)desc; | 
 | 2167 | 			frport->fcoe_len = ntohs(size->fd_size); | 
 | 2168 | 			break; | 
 | 2169 | 		default: | 
 | 2170 | 			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " | 
 | 2171 | 					"in FIP probe\n", dtype); | 
 | 2172 | 			/* standard says ignore unknown descriptors >= 128 */ | 
 | 2173 | 			if (dtype < FIP_DT_VENDOR_BASE) | 
 | 2174 | 				return -EINVAL; | 
 | 2175 | 			break; | 
 | 2176 | 		} | 
 | 2177 | 		desc = (struct fip_desc *)((char *)desc + dlen); | 
 | 2178 | 		rlen -= dlen; | 
 | 2179 | 	} | 
 | 2180 | 	return 0; | 
 | 2181 |  | 
 | 2182 | len_err: | 
 | 2183 | 	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", | 
 | 2184 | 			dtype, dlen); | 
 | 2185 | 	return -EINVAL; | 
 | 2186 | } | 
 | 2187 |  | 
 | 2188 | /** | 
 | 2189 |  * fcoe_ctlr_vn_send_claim() - send multicast FIP VN2VN Claim Notification. | 
 | 2190 |  * @fip: The FCoE controller | 
 | 2191 |  * | 
 | 2192 |  * Called with ctlr_mutex held. | 
 | 2193 |  */ | 
 | 2194 | static void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip) | 
 | 2195 | { | 
 | 2196 | 	fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_NOTIFY, fcoe_all_vn2vn, 0); | 
 | 2197 | 	fip->sol_time = jiffies; | 
 | 2198 | } | 
 | 2199 |  | 
 | 2200 | /** | 
 | 2201 |  * fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request. | 
 | 2202 |  * @fip: The FCoE controller | 
 | 2203 |  * @rdata: parsed remote port with frport from the probe request | 
 | 2204 |  * | 
 | 2205 |  * Called with ctlr_mutex held. | 
 | 2206 |  */ | 
 | 2207 | static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip, | 
 | 2208 | 				   struct fc_rport_priv *rdata) | 
 | 2209 | { | 
 | 2210 | 	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata); | 
 | 2211 |  | 
 | 2212 | 	if (rdata->ids.port_id != fip->port_id) | 
 | 2213 | 		return; | 
 | 2214 |  | 
 | 2215 | 	switch (fip->state) { | 
 | 2216 | 	case FIP_ST_VNMP_CLAIM: | 
 | 2217 | 	case FIP_ST_VNMP_UP: | 
 | 2218 | 		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP, | 
 | 2219 | 				  frport->enode_mac, 0); | 
 | 2220 | 		break; | 
 | 2221 | 	case FIP_ST_VNMP_PROBE1: | 
 | 2222 | 	case FIP_ST_VNMP_PROBE2: | 
 | 2223 | 		/* | 
 | 2224 | 		 * Decide whether to reply to the Probe. | 
 | 2225 | 		 * Our selected address is never a "recorded" one, so | 
 | 2226 | 		 * only reply if our WWPN is greater and the | 
 | 2227 | 		 * Probe's REC bit is not set. | 
 | 2228 | 		 * If we don't reply, we will change our address. | 
 | 2229 | 		 */ | 
 | 2230 | 		if (fip->lp->wwpn > rdata->ids.port_name && | 
 | 2231 | 		    !(frport->flags & FIP_FL_REC_OR_P2P)) { | 
 | 2232 | 			fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP, | 
 | 2233 | 					  frport->enode_mac, 0); | 
 | 2234 | 			break; | 
 | 2235 | 		} | 
 | 2236 | 		/* fall through */ | 
 | 2237 | 	case FIP_ST_VNMP_START: | 
 | 2238 | 		fcoe_ctlr_vn_restart(fip); | 
 | 2239 | 		break; | 
 | 2240 | 	default: | 
 | 2241 | 		break; | 
 | 2242 | 	} | 
 | 2243 | } | 
 | 2244 |  | 
 | 2245 | /** | 
 | 2246 |  * fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply. | 
 | 2247 |  * @fip: The FCoE controller | 
 | 2248 |  * @rdata: parsed remote port with frport from the probe request | 
 | 2249 |  * | 
 | 2250 |  * Called with ctlr_mutex held. | 
 | 2251 |  */ | 
 | 2252 | static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip, | 
 | 2253 | 				   struct fc_rport_priv *rdata) | 
 | 2254 | { | 
 | 2255 | 	if (rdata->ids.port_id != fip->port_id) | 
 | 2256 | 		return; | 
 | 2257 | 	switch (fip->state) { | 
 | 2258 | 	case FIP_ST_VNMP_START: | 
 | 2259 | 	case FIP_ST_VNMP_PROBE1: | 
 | 2260 | 	case FIP_ST_VNMP_PROBE2: | 
 | 2261 | 	case FIP_ST_VNMP_CLAIM: | 
 | 2262 | 		fcoe_ctlr_vn_restart(fip); | 
 | 2263 | 		break; | 
 | 2264 | 	case FIP_ST_VNMP_UP: | 
 | 2265 | 		fcoe_ctlr_vn_send_claim(fip); | 
 | 2266 | 		break; | 
 | 2267 | 	default: | 
 | 2268 | 		break; | 
 | 2269 | 	} | 
 | 2270 | } | 
 | 2271 |  | 
 | 2272 | /** | 
 | 2273 |  * fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply. | 
 | 2274 |  * @fip: The FCoE controller | 
 | 2275 |  * @new: newly-parsed remote port with frport as a template for new rdata | 
 | 2276 |  * | 
 | 2277 |  * Called with ctlr_mutex held. | 
 | 2278 |  */ | 
 | 2279 | static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new) | 
 | 2280 | { | 
 | 2281 | 	struct fc_lport *lport = fip->lp; | 
 | 2282 | 	struct fc_rport_priv *rdata; | 
 | 2283 | 	struct fc_rport_identifiers *ids; | 
 | 2284 | 	struct fcoe_rport *frport; | 
 | 2285 | 	u32 port_id; | 
 | 2286 |  | 
 | 2287 | 	port_id = new->ids.port_id; | 
 | 2288 | 	if (port_id == fip->port_id) | 
 | 2289 | 		return; | 
 | 2290 |  | 
 | 2291 | 	mutex_lock(&lport->disc.disc_mutex); | 
 | 2292 | 	rdata = lport->tt.rport_create(lport, port_id); | 
 | 2293 | 	if (!rdata) { | 
 | 2294 | 		mutex_unlock(&lport->disc.disc_mutex); | 
 | 2295 | 		return; | 
 | 2296 | 	} | 
 | 2297 |  | 
 | 2298 | 	rdata->ops = &fcoe_ctlr_vn_rport_ops; | 
 | 2299 | 	rdata->disc_id = lport->disc.disc_id; | 
 | 2300 |  | 
 | 2301 | 	ids = &rdata->ids; | 
 | 2302 | 	if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) || | 
 | 2303 | 	    (ids->node_name != -1 && ids->node_name != new->ids.node_name)) | 
 | 2304 | 		lport->tt.rport_logoff(rdata); | 
 | 2305 | 	ids->port_name = new->ids.port_name; | 
 | 2306 | 	ids->node_name = new->ids.node_name; | 
 | 2307 | 	mutex_unlock(&lport->disc.disc_mutex); | 
 | 2308 |  | 
 | 2309 | 	frport = fcoe_ctlr_rport(rdata); | 
 | 2310 | 	LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n", | 
 | 2311 | 			port_id, frport->fcoe_len ? "old" : "new"); | 
 | 2312 | 	*frport = *fcoe_ctlr_rport(new); | 
 | 2313 | 	frport->time = 0; | 
 | 2314 | } | 
 | 2315 |  | 
 | 2316 | /** | 
 | 2317 |  * fcoe_ctlr_vn_lookup() - Find VN remote port's MAC address | 
 | 2318 |  * @fip: The FCoE controller | 
 | 2319 |  * @port_id:  The port_id of the remote VN_node | 
 | 2320 |  * @mac: buffer which will hold the VN_NODE destination MAC address, if found. | 
 | 2321 |  * | 
 | 2322 |  * Returns non-zero error if no remote port found. | 
 | 2323 |  */ | 
 | 2324 | static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac) | 
 | 2325 | { | 
 | 2326 | 	struct fc_lport *lport = fip->lp; | 
 | 2327 | 	struct fc_rport_priv *rdata; | 
 | 2328 | 	struct fcoe_rport *frport; | 
 | 2329 | 	int ret = -1; | 
 | 2330 |  | 
 | 2331 | 	rcu_read_lock(); | 
 | 2332 | 	rdata = lport->tt.rport_lookup(lport, port_id); | 
 | 2333 | 	if (rdata) { | 
 | 2334 | 		frport = fcoe_ctlr_rport(rdata); | 
 | 2335 | 		memcpy(mac, frport->enode_mac, ETH_ALEN); | 
 | 2336 | 		ret = 0; | 
 | 2337 | 	} | 
 | 2338 | 	rcu_read_unlock(); | 
 | 2339 | 	return ret; | 
 | 2340 | } | 
 | 2341 |  | 
 | 2342 | /** | 
 | 2343 |  * fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification | 
 | 2344 |  * @fip: The FCoE controller | 
 | 2345 |  * @new: newly-parsed remote port with frport as a template for new rdata | 
 | 2346 |  * | 
 | 2347 |  * Called with ctlr_mutex held. | 
 | 2348 |  */ | 
 | 2349 | static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip, | 
 | 2350 | 				      struct fc_rport_priv *new) | 
 | 2351 | { | 
 | 2352 | 	struct fcoe_rport *frport = fcoe_ctlr_rport(new); | 
 | 2353 |  | 
 | 2354 | 	if (frport->flags & FIP_FL_REC_OR_P2P) { | 
 | 2355 | 		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); | 
 | 2356 | 		return; | 
 | 2357 | 	} | 
 | 2358 | 	switch (fip->state) { | 
 | 2359 | 	case FIP_ST_VNMP_START: | 
 | 2360 | 	case FIP_ST_VNMP_PROBE1: | 
 | 2361 | 	case FIP_ST_VNMP_PROBE2: | 
 | 2362 | 		if (new->ids.port_id == fip->port_id) | 
 | 2363 | 			fcoe_ctlr_vn_restart(fip); | 
 | 2364 | 		break; | 
 | 2365 | 	case FIP_ST_VNMP_CLAIM: | 
 | 2366 | 	case FIP_ST_VNMP_UP: | 
 | 2367 | 		if (new->ids.port_id == fip->port_id) { | 
 | 2368 | 			if (new->ids.port_name > fip->lp->wwpn) { | 
 | 2369 | 				fcoe_ctlr_vn_restart(fip); | 
 | 2370 | 				break; | 
 | 2371 | 			} | 
 | 2372 | 			fcoe_ctlr_vn_send_claim(fip); | 
 | 2373 | 			break; | 
 | 2374 | 		} | 
 | 2375 | 		fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, frport->enode_mac, | 
 | 2376 | 				  min((u32)frport->fcoe_len, | 
 | 2377 | 				      fcoe_ctlr_fcoe_size(fip))); | 
 | 2378 | 		fcoe_ctlr_vn_add(fip, new); | 
 | 2379 | 		break; | 
 | 2380 | 	default: | 
 | 2381 | 		break; | 
 | 2382 | 	} | 
 | 2383 | } | 
 | 2384 |  | 
 | 2385 | /** | 
 | 2386 |  * fcoe_ctlr_vn_claim_resp() - handle received Claim Response | 
 | 2387 |  * @fip: The FCoE controller that received the frame | 
 | 2388 |  * @new: newly-parsed remote port with frport from the Claim Response | 
 | 2389 |  * | 
 | 2390 |  * Called with ctlr_mutex held. | 
 | 2391 |  */ | 
 | 2392 | static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip, | 
 | 2393 | 				    struct fc_rport_priv *new) | 
 | 2394 | { | 
 | 2395 | 	LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n", | 
 | 2396 | 			new->ids.port_id, fcoe_ctlr_state(fip->state)); | 
 | 2397 | 	if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM) | 
 | 2398 | 		fcoe_ctlr_vn_add(fip, new); | 
 | 2399 | } | 
 | 2400 |  | 
 | 2401 | /** | 
 | 2402 |  * fcoe_ctlr_vn_beacon() - handle received beacon. | 
 | 2403 |  * @fip: The FCoE controller that received the frame | 
 | 2404 |  * @new: newly-parsed remote port with frport from the Beacon | 
 | 2405 |  * | 
 | 2406 |  * Called with ctlr_mutex held. | 
 | 2407 |  */ | 
 | 2408 | static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip, | 
 | 2409 | 				struct fc_rport_priv *new) | 
 | 2410 | { | 
 | 2411 | 	struct fc_lport *lport = fip->lp; | 
 | 2412 | 	struct fc_rport_priv *rdata; | 
 | 2413 | 	struct fcoe_rport *frport; | 
 | 2414 |  | 
 | 2415 | 	frport = fcoe_ctlr_rport(new); | 
 | 2416 | 	if (frport->flags & FIP_FL_REC_OR_P2P) { | 
 | 2417 | 		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); | 
 | 2418 | 		return; | 
 | 2419 | 	} | 
 | 2420 | 	mutex_lock(&lport->disc.disc_mutex); | 
 | 2421 | 	rdata = lport->tt.rport_lookup(lport, new->ids.port_id); | 
 | 2422 | 	if (rdata) | 
 | 2423 | 		kref_get(&rdata->kref); | 
 | 2424 | 	mutex_unlock(&lport->disc.disc_mutex); | 
 | 2425 | 	if (rdata) { | 
 | 2426 | 		if (rdata->ids.node_name == new->ids.node_name && | 
 | 2427 | 		    rdata->ids.port_name == new->ids.port_name) { | 
 | 2428 | 			frport = fcoe_ctlr_rport(rdata); | 
 | 2429 | 			if (!frport->time && fip->state == FIP_ST_VNMP_UP) | 
 | 2430 | 				lport->tt.rport_login(rdata); | 
 | 2431 | 			frport->time = jiffies; | 
 | 2432 | 		} | 
 | 2433 | 		kref_put(&rdata->kref, lport->tt.rport_destroy); | 
 | 2434 | 		return; | 
 | 2435 | 	} | 
 | 2436 | 	if (fip->state != FIP_ST_VNMP_UP) | 
 | 2437 | 		return; | 
 | 2438 |  | 
 | 2439 | 	/* | 
 | 2440 | 	 * Beacon from a new neighbor. | 
 | 2441 | 	 * Send a claim notify if one hasn't been sent recently. | 
 | 2442 | 	 * Don't add the neighbor yet. | 
 | 2443 | 	 */ | 
 | 2444 | 	LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n", | 
 | 2445 | 			new->ids.port_id); | 
 | 2446 | 	if (time_after(jiffies, | 
 | 2447 | 		       fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT))) | 
 | 2448 | 		fcoe_ctlr_vn_send_claim(fip); | 
 | 2449 | } | 
 | 2450 |  | 
 | 2451 | /** | 
 | 2452 |  * fcoe_ctlr_vn_age() - Check for VN_ports without recent beacons | 
 | 2453 |  * @fip: The FCoE controller | 
 | 2454 |  * | 
 | 2455 |  * Called with ctlr_mutex held. | 
 | 2456 |  * Called only in state FIP_ST_VNMP_UP. | 
 | 2457 |  * Returns the soonest time for next age-out or a time far in the future. | 
 | 2458 |  */ | 
 | 2459 | static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip) | 
 | 2460 | { | 
 | 2461 | 	struct fc_lport *lport = fip->lp; | 
 | 2462 | 	struct fc_rport_priv *rdata; | 
 | 2463 | 	struct fcoe_rport *frport; | 
 | 2464 | 	unsigned long next_time; | 
 | 2465 | 	unsigned long deadline; | 
 | 2466 |  | 
 | 2467 | 	next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10); | 
 | 2468 | 	mutex_lock(&lport->disc.disc_mutex); | 
 | 2469 | 	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) { | 
 | 2470 | 		frport = fcoe_ctlr_rport(rdata); | 
 | 2471 | 		if (!frport->time) | 
 | 2472 | 			continue; | 
 | 2473 | 		deadline = frport->time + | 
 | 2474 | 			   msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10); | 
 | 2475 | 		if (time_after_eq(jiffies, deadline)) { | 
 | 2476 | 			frport->time = 0; | 
 | 2477 | 			LIBFCOE_FIP_DBG(fip, | 
 | 2478 | 				"port %16.16llx fc_id %6.6x beacon expired\n", | 
 | 2479 | 				rdata->ids.port_name, rdata->ids.port_id); | 
 | 2480 | 			lport->tt.rport_logoff(rdata); | 
 | 2481 | 		} else if (time_before(deadline, next_time)) | 
 | 2482 | 			next_time = deadline; | 
 | 2483 | 	} | 
 | 2484 | 	mutex_unlock(&lport->disc.disc_mutex); | 
 | 2485 | 	return next_time; | 
 | 2486 | } | 
 | 2487 |  | 
 | 2488 | /** | 
 | 2489 |  * fcoe_ctlr_vn_recv() - Receive a FIP frame | 
 | 2490 |  * @fip: The FCoE controller that received the frame | 
 | 2491 |  * @skb: The received FIP frame | 
 | 2492 |  * | 
 | 2493 |  * Returns non-zero if the frame is dropped. | 
 | 2494 |  * Always consumes the frame. | 
 | 2495 |  */ | 
 | 2496 | static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb) | 
 | 2497 | { | 
 | 2498 | 	struct fip_header *fiph; | 
 | 2499 | 	enum fip_vn2vn_subcode sub; | 
| Kiran Patil | 2dc02ee | 2010-10-08 17:12:41 -0700 | [diff] [blame] | 2500 | 	struct { | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2501 | 		struct fc_rport_priv rdata; | 
 | 2502 | 		struct fcoe_rport frport; | 
 | 2503 | 	} buf; | 
 | 2504 | 	int rc; | 
 | 2505 |  | 
 | 2506 | 	fiph = (struct fip_header *)skb->data; | 
 | 2507 | 	sub = fiph->fip_subcode; | 
 | 2508 |  | 
 | 2509 | 	rc = fcoe_ctlr_vn_parse(fip, skb, &buf.rdata); | 
 | 2510 | 	if (rc) { | 
 | 2511 | 		LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc); | 
 | 2512 | 		goto drop; | 
 | 2513 | 	} | 
 | 2514 |  | 
 | 2515 | 	mutex_lock(&fip->ctlr_mutex); | 
 | 2516 | 	switch (sub) { | 
 | 2517 | 	case FIP_SC_VN_PROBE_REQ: | 
 | 2518 | 		fcoe_ctlr_vn_probe_req(fip, &buf.rdata); | 
 | 2519 | 		break; | 
 | 2520 | 	case FIP_SC_VN_PROBE_REP: | 
 | 2521 | 		fcoe_ctlr_vn_probe_reply(fip, &buf.rdata); | 
 | 2522 | 		break; | 
 | 2523 | 	case FIP_SC_VN_CLAIM_NOTIFY: | 
 | 2524 | 		fcoe_ctlr_vn_claim_notify(fip, &buf.rdata); | 
 | 2525 | 		break; | 
 | 2526 | 	case FIP_SC_VN_CLAIM_REP: | 
 | 2527 | 		fcoe_ctlr_vn_claim_resp(fip, &buf.rdata); | 
 | 2528 | 		break; | 
 | 2529 | 	case FIP_SC_VN_BEACON: | 
 | 2530 | 		fcoe_ctlr_vn_beacon(fip, &buf.rdata); | 
 | 2531 | 		break; | 
 | 2532 | 	default: | 
 | 2533 | 		LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub); | 
 | 2534 | 		rc = -1; | 
 | 2535 | 		break; | 
 | 2536 | 	} | 
 | 2537 | 	mutex_unlock(&fip->ctlr_mutex); | 
 | 2538 | drop: | 
 | 2539 | 	kfree_skb(skb); | 
 | 2540 | 	return rc; | 
 | 2541 | } | 
 | 2542 |  | 
 | 2543 | /** | 
 | 2544 |  * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode. | 
| Joe Eykholt | 9226115 | 2010-07-20 15:21:12 -0700 | [diff] [blame] | 2545 |  * @lport: The local port | 
 | 2546 |  * @fp: The received frame | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2547 |  * | 
 | 2548 |  * This should never be called since we don't see RSCNs or other | 
 | 2549 |  * fabric-generated ELSes. | 
 | 2550 |  */ | 
| Joe Eykholt | 9226115 | 2010-07-20 15:21:12 -0700 | [diff] [blame] | 2551 | static void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp) | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2552 | { | 
 | 2553 | 	struct fc_seq_els_data rjt_data; | 
 | 2554 |  | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2555 | 	rjt_data.reason = ELS_RJT_UNSUP; | 
 | 2556 | 	rjt_data.explan = ELS_EXPL_NONE; | 
| Joe Eykholt | 9226115 | 2010-07-20 15:21:12 -0700 | [diff] [blame] | 2557 | 	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2558 | 	fc_frame_free(fp); | 
 | 2559 | } | 
 | 2560 |  | 
 | 2561 | /** | 
 | 2562 |  * fcoe_ctlr_disc_recv - start discovery for VN2VN mode. | 
 | 2563 |  * @fip: The FCoE controller | 
 | 2564 |  * | 
 | 2565 |  * This sets a flag indicating that remote ports should be created | 
 | 2566 |  * and started for the peers we discover.  We use the disc_callback | 
 | 2567 |  * pointer as that flag.  Peers already discovered are created here. | 
 | 2568 |  * | 
 | 2569 |  * The lport lock is held during this call. The callback must be done | 
 | 2570 |  * later, without holding either the lport or discovery locks. | 
 | 2571 |  * The fcoe_ctlr lock may also be held during this call. | 
 | 2572 |  */ | 
 | 2573 | static void fcoe_ctlr_disc_start(void (*callback)(struct fc_lport *, | 
 | 2574 | 						  enum fc_disc_event), | 
 | 2575 | 				 struct fc_lport *lport) | 
 | 2576 | { | 
 | 2577 | 	struct fc_disc *disc = &lport->disc; | 
 | 2578 | 	struct fcoe_ctlr *fip = disc->priv; | 
 | 2579 |  | 
 | 2580 | 	mutex_lock(&disc->disc_mutex); | 
 | 2581 | 	disc->disc_callback = callback; | 
 | 2582 | 	disc->disc_id = (disc->disc_id + 2) | 1; | 
 | 2583 | 	disc->pending = 1; | 
 | 2584 | 	schedule_work(&fip->timer_work); | 
 | 2585 | 	mutex_unlock(&disc->disc_mutex); | 
 | 2586 | } | 
 | 2587 |  | 
 | 2588 | /** | 
 | 2589 |  * fcoe_ctlr_vn_disc() - report FIP VN_port discovery results after claim state. | 
 | 2590 |  * @fip: The FCoE controller | 
 | 2591 |  * | 
 | 2592 |  * Starts the FLOGI and PLOGI login process to each discovered rport for which | 
 | 2593 |  * we've received at least one beacon. | 
 | 2594 |  * Performs the discovery complete callback. | 
 | 2595 |  */ | 
 | 2596 | static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip) | 
 | 2597 | { | 
 | 2598 | 	struct fc_lport *lport = fip->lp; | 
 | 2599 | 	struct fc_disc *disc = &lport->disc; | 
 | 2600 | 	struct fc_rport_priv *rdata; | 
 | 2601 | 	struct fcoe_rport *frport; | 
 | 2602 | 	void (*callback)(struct fc_lport *, enum fc_disc_event); | 
 | 2603 |  | 
 | 2604 | 	mutex_lock(&disc->disc_mutex); | 
 | 2605 | 	callback = disc->pending ? disc->disc_callback : NULL; | 
 | 2606 | 	disc->pending = 0; | 
 | 2607 | 	list_for_each_entry_rcu(rdata, &disc->rports, peers) { | 
 | 2608 | 		frport = fcoe_ctlr_rport(rdata); | 
 | 2609 | 		if (frport->time) | 
 | 2610 | 			lport->tt.rport_login(rdata); | 
 | 2611 | 	} | 
 | 2612 | 	mutex_unlock(&disc->disc_mutex); | 
 | 2613 | 	if (callback) | 
 | 2614 | 		callback(lport, DISC_EV_SUCCESS); | 
 | 2615 | } | 
 | 2616 |  | 
 | 2617 | /** | 
 | 2618 |  * fcoe_ctlr_vn_timeout - timer work function for VN2VN mode. | 
 | 2619 |  * @fip: The FCoE controller | 
 | 2620 |  */ | 
 | 2621 | static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip) | 
 | 2622 | { | 
 | 2623 | 	unsigned long next_time; | 
 | 2624 | 	u8 mac[ETH_ALEN]; | 
 | 2625 | 	u32 new_port_id = 0; | 
 | 2626 |  | 
 | 2627 | 	mutex_lock(&fip->ctlr_mutex); | 
 | 2628 | 	switch (fip->state) { | 
 | 2629 | 	case FIP_ST_VNMP_START: | 
 | 2630 | 		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1); | 
 | 2631 | 		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); | 
 | 2632 | 		next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT); | 
 | 2633 | 		break; | 
 | 2634 | 	case FIP_ST_VNMP_PROBE1: | 
 | 2635 | 		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2); | 
 | 2636 | 		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); | 
 | 2637 | 		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); | 
 | 2638 | 		break; | 
 | 2639 | 	case FIP_ST_VNMP_PROBE2: | 
 | 2640 | 		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_CLAIM); | 
 | 2641 | 		new_port_id = fip->port_id; | 
 | 2642 | 		hton24(mac, FIP_VN_FC_MAP); | 
 | 2643 | 		hton24(mac + 3, new_port_id); | 
| Joe Eykholt | cd229e4 | 2010-07-20 15:20:40 -0700 | [diff] [blame] | 2644 | 		fcoe_ctlr_map_dest(fip); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2645 | 		fip->update_mac(fip->lp, mac); | 
 | 2646 | 		fcoe_ctlr_vn_send_claim(fip); | 
 | 2647 | 		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); | 
 | 2648 | 		break; | 
 | 2649 | 	case FIP_ST_VNMP_CLAIM: | 
 | 2650 | 		/* | 
 | 2651 | 		 * This may be invoked either by starting discovery so don't | 
 | 2652 | 		 * go to the next state unless it's been long enough. | 
 | 2653 | 		 */ | 
 | 2654 | 		next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT); | 
 | 2655 | 		if (time_after_eq(jiffies, next_time)) { | 
 | 2656 | 			fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP); | 
 | 2657 | 			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON, | 
 | 2658 | 					  fcoe_all_vn2vn, 0); | 
 | 2659 | 			next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); | 
 | 2660 | 			fip->port_ka_time = next_time; | 
 | 2661 | 		} | 
 | 2662 | 		fcoe_ctlr_vn_disc(fip); | 
 | 2663 | 		break; | 
 | 2664 | 	case FIP_ST_VNMP_UP: | 
 | 2665 | 		next_time = fcoe_ctlr_vn_age(fip); | 
 | 2666 | 		if (time_after_eq(jiffies, fip->port_ka_time)) { | 
 | 2667 | 			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON, | 
 | 2668 | 					  fcoe_all_vn2vn, 0); | 
 | 2669 | 			fip->port_ka_time = jiffies + | 
 | 2670 | 				 msecs_to_jiffies(FIP_VN_BEACON_INT + | 
 | 2671 | 					(random32() % FIP_VN_BEACON_FUZZ)); | 
 | 2672 | 		} | 
 | 2673 | 		if (time_before(fip->port_ka_time, next_time)) | 
 | 2674 | 			next_time = fip->port_ka_time; | 
 | 2675 | 		break; | 
 | 2676 | 	case FIP_ST_LINK_WAIT: | 
 | 2677 | 		goto unlock; | 
 | 2678 | 	default: | 
| Joe Perches | 11aa990 | 2010-11-30 16:19:09 -0800 | [diff] [blame] | 2679 | 		WARN(1, "unexpected state %d\n", fip->state); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2680 | 		goto unlock; | 
 | 2681 | 	} | 
 | 2682 | 	mod_timer(&fip->timer, next_time); | 
 | 2683 | unlock: | 
 | 2684 | 	mutex_unlock(&fip->ctlr_mutex); | 
 | 2685 |  | 
 | 2686 | 	/* If port ID is new, notify local port after dropping ctlr_mutex */ | 
 | 2687 | 	if (new_port_id) | 
 | 2688 | 		fc_lport_set_local_id(fip->lp, new_port_id); | 
 | 2689 | } | 
 | 2690 |  | 
 | 2691 | /** | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 2692 |  * fcoe_libfc_config() - Sets up libfc related properties for local port | 
 | 2693 |  * @lp: The local port to configure libfc for | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2694 |  * @fip: The FCoE controller in use by the local port | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 2695 |  * @tt: The libfc function template | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2696 |  * @init_fcp: If non-zero, the FCP portion of libfc should be initialized | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 2697 |  * | 
 | 2698 |  * Returns : 0 for success | 
 | 2699 |  */ | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2700 | int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip, | 
 | 2701 | 		      const struct libfc_function_template *tt, int init_fcp) | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 2702 | { | 
 | 2703 | 	/* Set the function pointers set by the LLDD */ | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 2704 | 	memcpy(&lport->tt, tt, sizeof(*tt)); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2705 | 	if (init_fcp && fc_fcp_init(lport)) | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 2706 | 		return -ENOMEM; | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 2707 | 	fc_exch_init(lport); | 
 | 2708 | 	fc_elsct_init(lport); | 
 | 2709 | 	fc_lport_init(lport); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2710 | 	if (fip->mode == FIP_MODE_VN2VN) | 
 | 2711 | 		lport->rport_priv_size = sizeof(struct fcoe_rport); | 
| Robert Love | 70b51aa | 2009-11-03 11:47:45 -0800 | [diff] [blame] | 2712 | 	fc_rport_init(lport); | 
| Joe Eykholt | e10f8c6 | 2010-07-20 15:20:30 -0700 | [diff] [blame] | 2713 | 	if (fip->mode == FIP_MODE_VN2VN) { | 
 | 2714 | 		lport->point_to_multipoint = 1; | 
 | 2715 | 		lport->tt.disc_recv_req = fcoe_ctlr_disc_recv; | 
 | 2716 | 		lport->tt.disc_start = fcoe_ctlr_disc_start; | 
 | 2717 | 		lport->tt.disc_stop = fcoe_ctlr_disc_stop; | 
 | 2718 | 		lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final; | 
 | 2719 | 		mutex_init(&lport->disc.disc_mutex); | 
 | 2720 | 		INIT_LIST_HEAD(&lport->disc.rports); | 
 | 2721 | 		lport->disc.priv = fip; | 
 | 2722 | 	} else { | 
 | 2723 | 		fc_disc_init(lport); | 
 | 2724 | 	} | 
| Vasu Dev | 5e80f7f | 2009-03-17 11:42:18 -0700 | [diff] [blame] | 2725 | 	return 0; | 
 | 2726 | } | 
 | 2727 | EXPORT_SYMBOL_GPL(fcoe_libfc_config); |