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: Hannes Reinecke <hare@suse.com>
Acked-by: Johannes Thumshirn <jth@kernel.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Hannes Reinecke 2016-07-19 13:49:40 +02:00 committed by Martin K. Petersen
parent b3d30f4a24
commit 9a6cf881df
5 changed files with 274 additions and 1 deletions

View File

@ -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()

View File

@ -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 @@ drop:
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

View File

@ -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,

View File

@ -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;

View File

@ -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;