Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a6cf881 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Martin K. Petersen
Browse files

fcoe: implement FIP VLAN responder



When running in VN2VN mode there is no central instance which would send
out any FIP VLAN discovery notifications. So this patch adds a new sysfs
attribute 'fip_vlan_responder' which will activate a FIP VLAN discovery
responder.

Signed-off-by: default avatarHannes Reinecke <hare@suse.com>
Acked-by: default avatarJohannes Thumshirn <jth@kernel.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent b3d30f4a
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -673,6 +673,12 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
	fcoe = port->priv;
	ctlr = fcoe_to_ctlr(fcoe);

	/* Figure out the VLAN ID, if any */
	if (netdev->priv_flags & IFF_802_1Q_VLAN)
		lport->vlan = vlan_dev_vlan_id(netdev);
	else
		lport->vlan = 0;

	/*
	 * Determine max frame size based on underlying device and optional
	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+224 −0
Original line number Diff line number Diff line
@@ -59,6 +59,8 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *);
static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *);
static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *);

static int fcoe_ctlr_vlan_recv(struct fcoe_ctlr *, struct sk_buff *);

static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
@@ -149,6 +151,7 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
{
	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
	fip->mode = mode;
	fip->fip_resp = false;
	INIT_LIST_HEAD(&fip->fcfs);
	mutex_init(&fip->ctlr_mutex);
	spin_lock_init(&fip->ctlr_lock);
@@ -1513,6 +1516,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
	struct fip_header *fiph;
	struct ethhdr *eh;
	enum fip_state state;
	bool fip_vlan_resp = false;
	u16 op;
	u8 sub;

@@ -1546,11 +1550,17 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
		state = FIP_ST_ENABLED;
		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
	}
	fip_vlan_resp = fip->fip_resp;
	mutex_unlock(&fip->ctlr_mutex);

	if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
		return fcoe_ctlr_vn_recv(fip, skb);

	if (fip_vlan_resp && op == FIP_OP_VLAN) {
		LIBFCOE_FIP_DBG(fip, "fip vlan discovery\n");
		return fcoe_ctlr_vlan_recv(fip, skb);
	}

	if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP &&
	    state != FIP_ST_VNMP_CLAIM)
		goto drop;
@@ -2703,6 +2713,220 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
	return rc;
}

/**
 * fcoe_ctlr_vlan_parse - parse vlan discovery request or response
 * @fip: The FCoE controller
 * @skb: incoming packet
 * @rdata: buffer for resulting parsed VLAN entry plus fcoe_rport
 *
 * Returns non-zero error number on error.
 * Does not consume the packet.
 */
static int fcoe_ctlr_vlan_parse(struct fcoe_ctlr *fip,
			      struct sk_buff *skb,
			      struct fc_rport_priv *rdata)
{
	struct fip_header *fiph;
	struct fip_desc *desc = NULL;
	struct fip_mac_desc *macd = NULL;
	struct fip_wwn_desc *wwn = NULL;
	struct fcoe_rport *frport;
	size_t rlen;
	size_t dlen;
	u32 desc_mask = 0;
	u32 dtype;
	u8 sub;

	memset(rdata, 0, sizeof(*rdata) + sizeof(*frport));
	frport = fcoe_ctlr_rport(rdata);

	fiph = (struct fip_header *)skb->data;
	frport->flags = ntohs(fiph->fip_flags);

	sub = fiph->fip_subcode;
	switch (sub) {
	case FIP_SC_VL_REQ:
		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME);
		break;
	default:
		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
		return -EINVAL;
	}

	rlen = ntohs(fiph->fip_dl_len) * 4;
	if (rlen + sizeof(*fiph) > skb->len)
		return -EINVAL;

	desc = (struct fip_desc *)(fiph + 1);
	while (rlen > 0) {
		dlen = desc->fip_dlen * FIP_BPW;
		if (dlen < sizeof(*desc) || dlen > rlen)
			return -EINVAL;

		dtype = desc->fip_dtype;
		if (dtype < 32) {
			if (!(desc_mask & BIT(dtype))) {
				LIBFCOE_FIP_DBG(fip,
						"unexpected or duplicated desc "
						"desc type %u in "
						"FIP VN2VN subtype %u\n",
						dtype, sub);
				return -EINVAL;
			}
			desc_mask &= ~BIT(dtype);
		}

		switch (dtype) {
		case FIP_DT_MAC:
			if (dlen != sizeof(struct fip_mac_desc))
				goto len_err;
			macd = (struct fip_mac_desc *)desc;
			if (!is_valid_ether_addr(macd->fd_mac)) {
				LIBFCOE_FIP_DBG(fip,
					"Invalid MAC addr %pM in FIP VN2VN\n",
					 macd->fd_mac);
				return -EINVAL;
			}
			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
			break;
		case FIP_DT_NAME:
			if (dlen != sizeof(struct fip_wwn_desc))
				goto len_err;
			wwn = (struct fip_wwn_desc *)desc;
			rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn);
			break;
		default:
			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
					"in FIP probe\n", dtype);
			/* standard says ignore unknown descriptors >= 128 */
			if (dtype < FIP_DT_NON_CRITICAL)
				return -EINVAL;
			break;
		}
		desc = (struct fip_desc *)((char *)desc + dlen);
		rlen -= dlen;
	}
	return 0;

len_err:
	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
			dtype, dlen);
	return -EINVAL;
}

/**
 * fcoe_ctlr_vlan_send() - Send a FIP VLAN Notification
 * @fip: The FCoE controller
 * @sub: sub-opcode for vlan notification or vn2vn vlan notification
 * @dest: The destination Ethernet MAC address
 * @min_len: minimum size of the Ethernet payload to be sent
 */
static void fcoe_ctlr_vlan_send(struct fcoe_ctlr *fip,
			      enum fip_vlan_subcode sub,
			      const u8 *dest)
{
	struct sk_buff *skb;
	struct fip_vlan_notify_frame {
		struct ethhdr eth;
		struct fip_header fip;
		struct fip_mac_desc mac;
		struct fip_vlan_desc vlan;
	} __packed * frame;
	size_t len;
	size_t dlen;

	len = sizeof(*frame);
	dlen = sizeof(frame->mac) + sizeof(frame->vlan);
	len = max(len, sizeof(struct ethhdr));

	skb = dev_alloc_skb(len);
	if (!skb)
		return;

	LIBFCOE_FIP_DBG(fip, "fip %s vlan notification, vlan %d\n",
			fip->mode == FIP_MODE_VN2VN ? "vn2vn" : "fcf",
			fip->lp->vlan);

	frame = (struct fip_vlan_notify_frame *)skb->data;
	memset(frame, 0, len);
	memcpy(frame->eth.h_dest, dest, ETH_ALEN);

	memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
	frame->eth.h_proto = htons(ETH_P_FIP);

	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
	frame->fip.fip_op = htons(FIP_OP_VLAN);
	frame->fip.fip_subcode = sub;
	frame->fip.fip_dl_len = htons(dlen / FIP_BPW);

	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);

	frame->vlan.fd_desc.fip_dtype = FIP_DT_VLAN;
	frame->vlan.fd_desc.fip_dlen = sizeof(frame->vlan) / FIP_BPW;
	put_unaligned_be16(fip->lp->vlan, &frame->vlan.fd_vlan);

	skb_put(skb, len);
	skb->protocol = htons(ETH_P_FIP);
	skb->priority = fip->priority;
	skb_reset_mac_header(skb);
	skb_reset_network_header(skb);

	fip->send(fip, skb);
}

/**
 * fcoe_ctlr_vlan_disk_reply() - send FIP VLAN Discovery Notification.
 * @fip: The FCoE controller
 *
 * Called with ctlr_mutex held.
 */
static void fcoe_ctlr_vlan_disc_reply(struct fcoe_ctlr *fip,
				      struct fc_rport_priv *rdata)
{
	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
	enum fip_vlan_subcode sub = FIP_SC_VL_NOTE;

	if (fip->mode == FIP_MODE_VN2VN)
		sub = FIP_SC_VL_VN2VN_NOTE;

	fcoe_ctlr_vlan_send(fip, sub, frport->enode_mac);
}

/**
 * fcoe_ctlr_vlan_recv - vlan request receive handler for VN2VN mode.
 * @lport: The local port
 * @fp: The received frame
 *
 */
static int fcoe_ctlr_vlan_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
	struct fip_header *fiph;
	enum fip_vlan_subcode sub;
	struct {
		struct fc_rport_priv rdata;
		struct fcoe_rport frport;
	} buf;
	int rc;

	fiph = (struct fip_header *)skb->data;
	sub = fiph->fip_subcode;
	rc = fcoe_ctlr_vlan_parse(fip, skb, &buf.rdata);
	if (rc) {
		LIBFCOE_FIP_DBG(fip, "vlan_recv vlan_parse error %d\n", rc);
		goto drop;
	}
	mutex_lock(&fip->ctlr_mutex);
	if (sub == FIP_SC_VL_REQ)
		fcoe_ctlr_vlan_disc_reply(fip, &buf.rdata);
	mutex_unlock(&fip->ctlr_mutex);

drop:
	kfree(skb);
	return rc;
}

/**
 * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
 * @lport: The local port
+39 −0
Original line number Diff line number Diff line
@@ -385,6 +385,44 @@ static FCOE_DEVICE_ATTR(ctlr, enabled, S_IRUGO | S_IWUSR,
			show_ctlr_enabled_state,
			store_ctlr_enabled);

static ssize_t store_ctlr_fip_resp(struct device *dev,
			      struct device_attribute *attr,
			      const char *buf, size_t count)
{
	struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
	struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr);

	mutex_lock(&fip->ctlr_mutex);
	if ((buf[1] == '\0') || ((buf[1] == '\n') && (buf[2] == '\0'))) {
		if (buf[0] == '1') {
			fip->fip_resp = 1;
			mutex_unlock(&fip->ctlr_mutex);
			return count;
		}
		if (buf[0] == '0') {
			fip->fip_resp = 0;
			mutex_unlock(&fip->ctlr_mutex);
			return count;
		}
	}
	mutex_unlock(&fip->ctlr_mutex);
	return -EINVAL;
}

static ssize_t show_ctlr_fip_resp(struct device *dev,
				  struct device_attribute *attr,
				  char *buf)
{
	struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
	struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr);

	return sprintf(buf, "%d\n", fip->fip_resp ? 1 : 0);
}

static FCOE_DEVICE_ATTR(ctlr, fip_vlan_responder, S_IRUGO | S_IWUSR,
			show_ctlr_fip_resp,
			store_ctlr_fip_resp);

static ssize_t
store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev,
					 struct device_attribute *attr,
@@ -467,6 +505,7 @@ static struct attribute_group fcoe_ctlr_lesb_attr_group = {
};

static struct attribute *fcoe_ctlr_attrs[] = {
	&device_attr_fcoe_ctlr_fip_vlan_responder.attr,
	&device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr,
	&device_attr_fcoe_ctlr_enabled.attr,
	&device_attr_fcoe_ctlr_mode.attr,
+1 −0
Original line number Diff line number Diff line
@@ -878,6 +878,7 @@ struct fc_lport {
	struct libfc_function_template tt;
	u8			       link_up;
	u8			       qfull;
	u16			       vlan;
	enum fc_lport_state	       state;
	unsigned long		       boot_time;
	struct fc_host_statistics      host_stats;
+4 −1
Original line number Diff line number Diff line
@@ -110,8 +110,10 @@ enum fip_mode {
 * @flogi_req_send: send of FLOGI requested
 * @flogi_count:   number of FLOGI attempts in AUTO mode.
 * @map_dest:	   use the FC_MAP mode for destination MAC addresses.
 * @fip_resp:	   start FIP VLAN discovery responder
 * @spma:	   supports SPMA server-provided MACs mode
 * @probe_tries:   number of FC_IDs probed
 * @priority:      DCBx FCoE APP priority
 * @dest_addr:	   MAC address of the selected FC forwarder.
 * @ctl_src_addr:  the native MAC address of our local port.
 * @send:	   LLD-supplied function to handle sending FIP Ethernet frames
@@ -149,7 +151,8 @@ struct fcoe_ctlr {
	u16 flogi_oxid;
	u8 flogi_req_send;
	u8 flogi_count;
	u8 map_dest;
	bool map_dest;
	bool fip_resp;
	u8 spma;
	u8 probe_tries;
	u8 priority;