[SCSI] libfcoe, fcoe: libfcoe NPIV support

The FIP code in libfcoe needed several changes to support NPIV

1) dst_src_addr needs to be managed per-n_port-ID for FPMA fabrics with NPIV
   enabled.  Managing the MAC address is now handled in fcoe, with some slight
   changes to update_mac() and a new get_src_addr() function pointer.

2) The libfc elsct_send() hook is used to setup FCoE specific response
   handlers for FIP encapsulated ELS exchanges.  This lets the FCoE specific
   handling know which VN_Port the exchange is for, and doesn't require
   tracking OX_IDs.  It might be possible to roll back to the full FIP frame
   in these, but for now I've just stashed the contents of the MAC address
   descriptor in the skb context block for later use.  Also, because
   fcoe_elsct_send() just passes control on to fc_elsct_send(), all transmits
   still come through the normal frame_send() path.

3) The NPIV changes added a mutex hold in the keep alive sending, the lport
   mutex is protecting the vport list.  We can't take a mutex from a timer,
   so move the FIP keep alive logic to the link work struct.

Signed-off-by: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 8ca488d..a64c398 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -226,7 +226,8 @@
 }
 
 static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new);
+static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr);
+static u8 *fcoe_get_src_mac(struct fc_lport *lport);
 static void fcoe_destroy_work(struct work_struct *work);
 
 /**
@@ -254,6 +255,7 @@
 	fcoe_ctlr_init(&fcoe->ctlr);
 	fcoe->ctlr.send = fcoe_fip_send;
 	fcoe->ctlr.update_mac = fcoe_update_src_mac;
+	fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
 
 	fcoe_interface_setup(fcoe, netdev);
 
@@ -286,8 +288,6 @@
 	/* Delete secondary MAC addresses */
 	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
 	dev_unicast_delete(netdev, flogi_maddr);
-	if (!is_zero_ether_addr(fip->data_src_addr))
-		dev_unicast_delete(netdev, fip->data_src_addr);
 	if (fip->spma)
 		dev_unicast_delete(netdev, fip->ctl_src_addr);
 	dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
@@ -369,26 +369,38 @@
 
 /**
  * fcoe_update_src_mac() - Update Ethernet MAC filters.
- * @fip: FCoE controller.
- * @old: Unicast MAC address to delete if the MAC is non-zero.
- * @new: Unicast MAC address to add.
+ * @lport: libfc lport
+ * @addr: Unicast MAC address to add.
  *
  * Remove any previously-set unicast MAC filter.
  * Add secondary FCoE MAC address filter for our OUI.
  */
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
+static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
 {
-	struct fcoe_interface *fcoe;
+	struct fcoe_port *port = lport_priv(lport);
+	struct fcoe_interface *fcoe = port->fcoe;
 
-	fcoe = fcoe_from_ctlr(fip);
 	rtnl_lock();
-	if (!is_zero_ether_addr(old))
-		dev_unicast_delete(fcoe->netdev, old);
-	dev_unicast_add(fcoe->netdev, new);
+	if (!is_zero_ether_addr(port->data_src_addr))
+		dev_unicast_delete(fcoe->netdev, port->data_src_addr);
+	if (!is_zero_ether_addr(addr))
+		dev_unicast_add(fcoe->netdev, addr);
+	memcpy(port->data_src_addr, addr, ETH_ALEN);
 	rtnl_unlock();
 }
 
 /**
+ * fcoe_get_src_mac() - return the Ethernet source address for an lport
+ * @lport: libfc lport
+ */
+static u8 *fcoe_get_src_mac(struct fc_lport *lport)
+{
+	struct fcoe_port *port = lport_priv(lport);
+
+	return port->data_src_addr;
+}
+
+/**
  * fcoe_lport_config() - sets up the fc_lport
  * @lp: ptr to the fc_lport
  *
@@ -650,6 +662,11 @@
 	/* Free existing transmit skbs */
 	fcoe_clean_pending_queue(lport);
 
+	rtnl_lock();
+	if (!is_zero_ether_addr(port->data_src_addr))
+		dev_unicast_delete(netdev, port->data_src_addr);
+	rtnl_unlock();
+
 	/* receives may not be stopped until after this */
 	fcoe_interface_put(fcoe);
 
@@ -706,10 +723,16 @@
 	return 0;
 }
 
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport,
+		u32 did, struct fc_frame *fp, unsigned int op,
+		void (*resp)(struct fc_seq *, struct fc_frame *, void *),
+		void *arg, u32 timeout);
+
 static struct libfc_function_template fcoe_libfc_fcn_templ = {
 	.frame_send = fcoe_xmit,
 	.ddp_setup = fcoe_ddp_setup,
 	.ddp_done = fcoe_ddp_done,
+	.elsct_send = fcoe_elsct_send,
 };
 
 /**
@@ -1226,7 +1249,7 @@
 	}
 
 	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
-	    fcoe_ctlr_els_send(&fcoe->ctlr, skb))
+	    fcoe_ctlr_els_send(&fcoe->ctlr, lp, skb))
 		return 0;
 
 	sof = fr_sof(fp);
@@ -1291,7 +1314,7 @@
 	if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
 		memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
 	else
-		memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN);
+		memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
 	hp = (struct fcoe_hdr *)(eh + 1);
 	memset(hp, 0, sizeof(*hp));
@@ -1464,11 +1487,6 @@
 			}
 			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
 		}
-		if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
-		    fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) {
-			fc_frame_free(fp);
-			continue;
-		}
 		fc_exch_recv(lp, fp);
 	}
 	return 0;
@@ -2061,3 +2079,94 @@
 	fcoe_if_exit();
 }
 module_exit(fcoe_exit);
+
+/**
+ * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler
+ * @seq: active sequence in the FLOGI or FDISC exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc FLOGI response handler.
+ */
+static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+	struct fcoe_ctlr *fip = arg;
+	struct fc_exch *exch = fc_seq_exch(seq);
+	struct fc_lport *lport = exch->lp;
+	u8 *mac;
+
+	if (IS_ERR(fp))
+		goto done;
+
+	mac = fr_cb(fp)->granted_mac;
+	if (is_zero_ether_addr(mac)) {
+		/* pre-FIP */
+		mac = eth_hdr(&fp->skb)->h_source;
+		if (fcoe_ctlr_recv_flogi(fip, lport, fp, mac)) {
+			fc_frame_free(fp);
+			return;
+		}
+	} else {
+		/* FIP, libfcoe has already seen it */
+		fip->update_mac(lport, fr_cb(fp)->granted_mac);
+	}
+done:
+	fc_lport_flogi_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_logo_resp() - FCoE specific LOGO response handler
+ * @seq: active sequence in the LOGO exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc LOGO response handler.
+ */
+static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+	struct fcoe_ctlr *fip = arg;
+	struct fc_exch *exch = fc_seq_exch(seq);
+	struct fc_lport *lport = exch->lp;
+	static u8 zero_mac[ETH_ALEN] = { 0 };
+
+	if (!IS_ERR(fp))
+		fip->update_mac(lport, zero_mac);
+	fc_lport_logo_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_elsct_send - FCoE specific ELS handler
+ *
+ * This does special case handling of FIP encapsualted ELS exchanges for FCoE,
+ * using FCoE specific response handlers and passing the FIP controller as
+ * the argument (the lport is still available from the exchange).
+ *
+ * Most of the work here is just handed off to the libfc routine.
+ */
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport,
+		u32 did, struct fc_frame *fp, unsigned int op,
+		void (*resp)(struct fc_seq *, struct fc_frame *, void *),
+		void *arg, u32 timeout)
+{
+	struct fcoe_port *port = lport_priv(lport);
+	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_ctlr *fip = &fcoe->ctlr;
+	struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+	switch (op) {
+	case ELS_FLOGI:
+	case ELS_FDISC:
+		return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp,
+				     fip, timeout);
+	case ELS_LOGO:
+		/* only hook onto fabric logouts, not port logouts */
+		if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
+			break;
+		return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp,
+				     fip, timeout);
+	}
+	return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
+}
+
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index a123552..99dfa7c 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -104,6 +104,7 @@
 	u8	fcoe_pending_queue_active;
 	struct timer_list timer;		/* queue timer */
 	struct work_struct destroy_work;	/* to prevent rtnl deadlocks */
+	u8 data_src_addr[ETH_ALEN];
 };
 
 #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index d8ea04a..6a93ba9 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -322,6 +322,7 @@
 /**
  * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF.
  * @fip:	FCoE controller.
+ * @lport:	libfc fc_lport to send from
  * @ports:	0 for controller keep-alive, 1 for port keep-alive.
  * @sa:		source MAC address.
  *
@@ -332,7 +333,9 @@
  * The source MAC is the assigned mapped source address.
  * The destination is the FCF's F-port.
  */
-static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
+static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
+				      struct fc_lport *lport,
+				      int ports, u8 *sa)
 {
 	struct sk_buff *skb;
 	struct fip_kal {
@@ -374,16 +377,14 @@
 	kal->mac.fd_desc.fip_dtype = FIP_DT_MAC;
 	kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW;
 	memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
-
 	if (ports) {
 		vn = (struct fip_vn_desc *)(kal + 1);
 		vn->fd_desc.fip_dtype = FIP_DT_VN_ID;
 		vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW;
-		memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN);
+		memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
 		hton24(vn->fd_fc_id, fc_host_port_id(lp->host));
 		put_unaligned_be64(lp->wwpn, &vn->fd_wwpn);
 	}
-
 	skb_put(skb, len);
 	skb->protocol = htons(ETH_P_FIP);
 	skb_reset_mac_header(skb);
@@ -394,6 +395,7 @@
 /**
  * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it.
  * @fip:	FCoE controller.
+ * @lport:	libfc fc_lport to use for the source address
  * @dtype:	FIP descriptor type for the frame.
  * @skb:	FCoE ELS frame including FC header but no FCoE headers.
  *
@@ -405,7 +407,7 @@
  * Headroom includes the FIP encapsulation description, FIP header, and
  * Ethernet header.  The tailroom is for the FIP MAC descriptor.
  */
-static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
+static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
 			    u8 dtype, struct sk_buff *skb)
 {
 	struct fip_encaps_head {
@@ -450,7 +452,7 @@
 	mac->fd_desc.fip_dtype = FIP_DT_MAC;
 	mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
 	if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC)
-		memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
+		memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
 	else if (fip->spma)
 		memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
 
@@ -463,6 +465,7 @@
 /**
  * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate.
  * @fip:	FCoE controller.
+ * @lport:	libfc fc_lport to send from
  * @skb:	FCoE ELS frame including FC header but no FCoE headers.
  *
  * Returns a non-zero error code if the frame should not be sent.
@@ -471,11 +474,13 @@
  * The caller must check that the length is a multiple of 4.
  * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
  */
-int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
+		       struct sk_buff *skb)
 {
 	struct fc_frame_header *fh;
 	u16 old_xid;
 	u8 op;
+	u8 mac[ETH_ALEN];
 
 	fh = (struct fc_frame_header *)skb->data;
 	op = *(u8 *)(fh + 1);
@@ -530,14 +535,15 @@
 		 * FLOGI.
 		 */
 		fip->flogi_oxid = FC_XID_UNKNOWN;
-		fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id);
+		fc_fcoe_set_mac(mac, fh->fh_d_id);
+		fip->update_mac(lport, mac);
 		return 0;
 	default:
 		if (fip->state != FIP_ST_ENABLED)
 			goto drop;
 		return 0;
 	}
-	if (fcoe_ctlr_encaps(fip, op, skb))
+	if (fcoe_ctlr_encaps(fip, lport, op, skb))
 		goto drop;
 	fip->send(fip, skb);
 	return -EINPROGRESS;
@@ -796,7 +802,7 @@
 {
 	struct fc_lport *lp = fip->lp;
 	struct fip_header *fiph;
-	struct fc_frame *fp;
+	struct fc_frame *fp = (struct fc_frame *)skb;
 	struct fc_frame_header *fh = NULL;
 	struct fip_desc *desc;
 	struct fip_encaps *els;
@@ -835,6 +841,7 @@
 						"in FIP ELS\n");
 				goto drop;
 			}
+			memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
 			break;
 		case FIP_DT_FLOGI:
 		case FIP_DT_FDISC:
@@ -865,13 +872,10 @@
 		goto drop;
 	els_op = *(u8 *)(fh + 1);
 
-	if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) &&
-	    sub == FIP_SC_REP && fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
-	    els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) {
+	if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP &&
+	    fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
+	    els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac))
 		fip->flogi_oxid = FC_XID_UNKNOWN;
-		fip->update_mac(fip, fip->data_src_addr, granted_mac);
-		memcpy(fip->data_src_addr, granted_mac, ETH_ALEN);
-	}
 
 	/*
 	 * Convert skb into an fc_frame containing only the ELS.
@@ -958,7 +962,7 @@
 			if (dlen < sizeof(*vp))
 				return;
 			if (compare_ether_addr(vp->fd_mac,
-			    fip->data_src_addr) == 0 &&
+			    fip->get_src_addr(lp)) == 0 &&
 			    get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn &&
 			    ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host))
 				desc_mask &= ~BIT(FIP_DT_VN_ID);
@@ -1113,8 +1117,6 @@
 	struct fcoe_fcf *sel;
 	struct fcoe_fcf *fcf;
 	unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
-	u8 send_ctlr_ka;
-	u8 send_port_ka;
 
 	spin_lock_bh(&fip->lock);
 	if (fip->state == FIP_ST_DISABLED) {
@@ -1153,12 +1155,10 @@
 		schedule_work(&fip->link_work);
 	}
 
-	send_ctlr_ka = 0;
-	send_port_ka = 0;
 	if (sel) {
 		if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
 			fip->ctlr_ka_time = jiffies + sel->fka_period;
-			send_ctlr_ka = 1;
+			fip->send_ctlr_ka = 1;
 		}
 		if (time_after(next_timer, fip->ctlr_ka_time))
 			next_timer = fip->ctlr_ka_time;
@@ -1166,7 +1166,7 @@
 		if (time_after_eq(jiffies, fip->port_ka_time)) {
 			fip->port_ka_time += jiffies +
 					msecs_to_jiffies(FIP_VN_KA_PERIOD);
-			send_port_ka = 1;
+			fip->send_port_ka = 1;
 		}
 		if (time_after(next_timer, fip->port_ka_time))
 			next_timer = fip->port_ka_time;
@@ -1176,12 +1176,9 @@
 				msecs_to_jiffies(FCOE_CTLR_START_DELAY);
 		mod_timer(&fip->timer, next_timer);
 	}
+	if (fip->send_ctlr_ka || fip->send_port_ka)
+		schedule_work(&fip->link_work);
 	spin_unlock_bh(&fip->lock);
-
-	if (send_ctlr_ka)
-		fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr);
-	if (send_port_ka)
-		fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr);
 }
 
 /**
@@ -1196,6 +1193,8 @@
 static void fcoe_ctlr_link_work(struct work_struct *work)
 {
 	struct fcoe_ctlr *fip;
+	struct fc_lport *vport;
+	u8 *mac;
 	int link;
 	int last_link;
 
@@ -1212,6 +1211,22 @@
 		else
 			fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
 	}
+
+	if (fip->send_ctlr_ka) {
+		fip->send_ctlr_ka = 0;
+		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
+	}
+	if (fip->send_port_ka) {
+		fip->send_port_ka = 0;
+		mutex_lock(&fip->lp->lp_mutex);
+		mac = fip->get_src_addr(fip->lp);
+		fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
+		list_for_each_entry(vport, &fip->lp->vports, list) {
+			mac = fip->get_src_addr(vport);
+			fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
+		}
+		mutex_unlock(&fip->lp->lp_mutex);
+	}
 }
 
 /**
@@ -1236,6 +1251,7 @@
 /**
  * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request.
  * @fip:	FCoE controller.
+ * @lport:	libfc fc_lport instance received on
  * @fp:		FC frame.
  * @sa:		Ethernet source MAC address from received FCoE frame.
  *
@@ -1248,7 +1264,8 @@
  *
  * Return non-zero if the frame should not be delivered to libfc.
  */
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
+			 struct fc_frame *fp, u8 *sa)
 {
 	struct fc_frame_header *fh;
 	u8 op;
@@ -1283,11 +1300,9 @@
 			fip->map_dest = 0;
 		}
 		fip->flogi_oxid = FC_XID_UNKNOWN;
-		memcpy(mac, fip->data_src_addr, ETH_ALEN);
-		fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id);
+		fc_fcoe_set_mac(mac, fh->fh_d_id);
+		fip->update_mac(lport, mac);
 		spin_unlock_bh(&fip->lock);
-
-		fip->update_mac(fip, mac, fip->data_src_addr);
 	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
 		/*
 		 * Save source MAC for point-to-point responses.
@@ -1370,3 +1385,4 @@
 	return 0;
 }
 EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index 92984587..aae54fe 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -31,7 +31,7 @@
 /*
  * fc_elsct_send - sends ELS/CT frame
  */
-static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
+struct fc_seq *fc_elsct_send(struct fc_lport *lport,
 				    u32 did,
 				    struct fc_frame *fp,
 				    unsigned int op,
@@ -63,6 +63,7 @@
 
 	return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
 }
+EXPORT_SYMBOL(fc_elsct_send);
 
 int fc_elsct_init(struct fc_lport *lport)
 {
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index ccba67c..807f5b3 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1320,7 +1320,7 @@
  * held, but it will lock, call an _enter_* function or fc_lport_error
  * and then unlock the lport.
  */
-static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
 			       void *lp_arg)
 {
 	struct fc_lport *lport = lp_arg;
@@ -1357,6 +1357,7 @@
 err:
 	mutex_unlock(&lport->lp_mutex);
 }
+EXPORT_SYMBOL(fc_lport_logo_resp);
 
 /**
  * fc_rport_enter_logo() - Logout of the fabric
@@ -1397,7 +1398,7 @@
  * held, but it will lock, call an _enter_* function or fc_lport_error
  * and then unlock the lport.
  */
-static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 				void *lp_arg)
 {
 	struct fc_lport *lport = lp_arg;
@@ -1480,6 +1481,7 @@
 err:
 	mutex_unlock(&lport->lp_mutex);
 }
+EXPORT_SYMBOL(fc_lport_flogi_resp);
 
 /**
  * fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
index 148126d..ab2f8d4 100644
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -28,6 +28,8 @@
 #include <scsi/fc/fc_fcp.h>
 #include <scsi/fc/fc_encaps.h>
 
+#include <linux/if_ether.h>
+
 /*
  * The fc_frame interface is used to pass frame data between functions.
  * The frame includes the data buffer, length, and SOF / EOF delimiter types.
@@ -67,6 +69,7 @@
 	enum fc_sof	fr_sof;		/* start of frame delimiter */
 	enum fc_eof	fr_eof;		/* end of frame delimiter */
 	u8		fr_flags;	/* flags - see below */
+	u8		granted_mac[ETH_ALEN]; /* FCoE MAC address */
 };
 
 
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index dfeb1ee..dad66ce 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -900,6 +900,16 @@
  * Initializes ELS/CT interface
  */
 int fc_elsct_init(struct fc_lport *lp);
+struct fc_seq *fc_elsct_send(struct fc_lport *lport,
+				    u32 did,
+				    struct fc_frame *fp,
+				    unsigned int op,
+				    void (*resp)(struct fc_seq *,
+						 struct fc_frame *fp,
+						 void *arg),
+				    void *arg, u32 timer_msec);
+void fc_lport_flogi_resp(struct fc_seq *, struct fc_frame *, void *);
+void fc_lport_logo_resp(struct fc_seq *, struct fc_frame *, void *);
 
 
 /*
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index b241060..8ef5e20 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -74,11 +74,13 @@
  * @last_link:	last link state reported to libfc.
  * @map_dest:	use the FC_MAP mode for destination MAC addresses.
  * @spma:	supports SPMA server-provided MACs mode
+ * @send_ctlr_ka: need to send controller keep alive
+ * @send_port_ka: need to send port keep alives
  * @dest_addr:	MAC address of the selected FC forwarder.
  * @ctl_src_addr: the native MAC address of our local port.
- * @data_src_addr: the assigned MAC address for the local port after FLOGI.
  * @send:	LLD-supplied function to handle sending of FIP Ethernet frames.
  * @update_mac: LLD-supplied function to handle changes to MAC addresses.
+ * @get_src_addr: LLD-supplied function to supply a source MAC address.
  * @lock:	lock protecting this structure.
  *
  * This structure is used by all FCoE drivers.  It contains information
@@ -106,12 +108,14 @@
 	u8 last_link;
 	u8 map_dest;
 	u8 spma;
+	u8 send_ctlr_ka;
+	u8 send_port_ka;
 	u8 dest_addr[ETH_ALEN];
 	u8 ctl_src_addr[ETH_ALEN];
-	u8 data_src_addr[ETH_ALEN];
 
 	void (*send)(struct fcoe_ctlr *, struct sk_buff *);
-	void (*update_mac)(struct fcoe_ctlr *, u8 *old, u8 *new);
+	void (*update_mac)(struct fc_lport *, u8 *addr);
+	u8 * (*get_src_addr)(struct fc_lport *);
 	spinlock_t lock;
 };
 
@@ -155,9 +159,10 @@
 void fcoe_ctlr_destroy(struct fcoe_ctlr *);
 void fcoe_ctlr_link_up(struct fcoe_ctlr *);
 int fcoe_ctlr_link_down(struct fcoe_ctlr *);
-int fcoe_ctlr_els_send(struct fcoe_ctlr *, struct sk_buff *);
+int fcoe_ctlr_els_send(struct fcoe_ctlr *, struct fc_lport *, struct sk_buff *);
 void fcoe_ctlr_recv(struct fcoe_ctlr *, struct sk_buff *);
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_frame *fp, u8 *sa);
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *lport,
+			 struct fc_frame *fp, u8 *sa);
 
 /* libfcoe funcs */
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);