diff --git a/src/include/ipxe/fcoe.h b/src/include/ipxe/fcoe.h index 69120d3e..6ba5b406 100644 --- a/src/include/ipxe/fcoe.h +++ b/src/include/ipxe/fcoe.h @@ -86,4 +86,7 @@ enum fcoe_eof { FCOE_EOF_A = 0x50, /**< End of Frame Abort */ }; +/** FCoE VLAN priority */ +#define FCOE_VLAN_PRIORITY 3 + #endif /* _IPXE_FCOE_H */ diff --git a/src/net/fcoe.c b/src/net/fcoe.c index 47446799..70728771 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -103,6 +104,10 @@ enum fcoe_flags { FCOE_HAVE_FIP_FCF = 0x0004, /** FCoE forwarder supports server-provided MAC addresses */ FCOE_FCF_ALLOWS_SPMA = 0x0008, + /** An alternative VLAN has been found */ + FCOE_VLAN_FOUND = 0x0010, + /** VLAN discovery has timed out */ + FCOE_VLAN_TIMED_OUT = 0x0020, }; struct net_protocol fcoe_protocol __net_protocol; @@ -124,6 +129,15 @@ static uint8_t all_fcf_macs[ETH_ALEN] = static uint8_t default_fcf_mac[ETH_ALEN] = { 0x0e, 0xfc, 0x00, 0xff, 0xff, 0xfe }; +/** Maximum number of VLAN requests before giving up on VLAN discovery */ +#define FCOE_MAX_VLAN_REQUESTS 2 + +/** Delay between retrying VLAN requests */ +#define FCOE_VLAN_RETRY_DELAY ( TICKS_PER_SEC ) + +/** Delay between retrying polling VLAN requests */ +#define FCOE_VLAN_POLL_DELAY ( 30 * TICKS_PER_SEC ) + /** Maximum number of FIP solicitations before giving up on FIP */ #define FCOE_MAX_FIP_SOLICITATIONS 2 @@ -182,8 +196,9 @@ static void fcoe_reset ( struct fcoe_port *fcoe ) { netdev_link_ok ( fcoe->netdev ) ) { fcoe->flags |= FCOE_HAVE_NETWORK; start_timer_nodelay ( &fcoe->timer ); - DBGC ( fcoe, "FCoE %s starting FIP solicitation\n", - fcoe->netdev->name ); + DBGC ( fcoe, "FCoE %s starting %s\n", fcoe->netdev->name, + ( vlan_can_be_trunk ( fcoe->netdev ) ? + "VLAN discovery" : "FIP solicitation" ) ); } /* Send notification of window change */ @@ -533,6 +548,100 @@ static int fcoe_fip_parse ( struct fcoe_port *fcoe, struct fip_header *fiphdr, return 0; } +/** + * Send FIP VLAN request + * + * @v fcoe FCoE port + * @ret rc Return status code + */ +static int fcoe_fip_tx_vlan ( struct fcoe_port *fcoe ) { + struct io_buffer *iobuf; + struct { + struct fip_header hdr; + struct fip_mac_address mac_address; + } __attribute__ (( packed )) *request; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *request ) ); + if ( ! iobuf ) + return -ENOMEM; + iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + + /* Construct VLAN request */ + request = iob_put ( iobuf, sizeof ( *request ) ); + memset ( request, 0, sizeof ( *request ) ); + request->hdr.version = FIP_VERSION; + request->hdr.code = htons ( FIP_CODE_VLAN ); + request->hdr.subcode = FIP_VLAN_REQUEST; + request->hdr.len = htons ( ( sizeof ( *request ) - + sizeof ( request->hdr ) ) / 4 ); + request->mac_address.type = FIP_MAC_ADDRESS; + request->mac_address.len = + ( sizeof ( request->mac_address ) / 4 ); + memcpy ( request->mac_address.mac, fcoe->netdev->ll_addr, + sizeof ( request->mac_address.mac ) ); + + /* Send VLAN request */ + if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, + &fip_protocol, all_fcf_macs, + fcoe->netdev->ll_addr ) ) != 0 ) { + DBGC ( fcoe, "FCoE %s could not send VLAN request: " + "%s\n", fcoe->netdev->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle received FIP VLAN notification + * + * @v fcoe FCoE port + * @v descs Descriptor list + * @v flags Flags + * @ret rc Return status code + */ +static int fcoe_fip_rx_vlan ( struct fcoe_port *fcoe, + struct fip_descriptors *descs, + unsigned int flags __unused ) { + struct fip_mac_address *mac_address = fip_mac_address ( descs ); + struct fip_vlan *vlan = fip_vlan ( descs ); + unsigned int tag; + int rc; + + /* Sanity checks */ + if ( ! mac_address ) { + DBGC ( fcoe, "FCoE %s received VLAN notification missing MAC " + "address\n", fcoe->netdev->name ); + return -EINVAL; + } + if ( ! vlan ) { + DBGC ( fcoe, "FCoE %s received VLAN notification missing VLAN " + "tag\n", fcoe->netdev->name ); + return -EINVAL; + } + + /* Create VLAN */ + tag = ntohs ( vlan->vlan ); + DBGC ( fcoe, "FCoE %s creating VLAN %d for FCF %s\n", + fcoe->netdev->name, tag, eth_ntoa ( mac_address->mac ) ); + if ( ( rc = vlan_create ( fcoe->netdev, tag, + FCOE_VLAN_PRIORITY ) ) != 0 ) { + DBGC ( fcoe, "FCoE %s could not create VLAN %d: %s\n", + fcoe->netdev->name, tag, strerror ( rc ) ); + return rc; + } + + /* Record that a VLAN was found. This FCoE port will play no + * further active role; the real FCoE traffic will use the + * port automatically created for the new VLAN device. + */ + fcoe->flags |= FCOE_VLAN_FOUND; + + return 0; +} + /** * Send FIP discovery solicitation * @@ -797,6 +906,8 @@ struct fip_handler { /** FIP handlers */ static struct fip_handler fip_handlers[] = { + { FIP_CODE_VLAN, FIP_VLAN_NOTIFY, + fcoe_fip_rx_vlan }, { FIP_CODE_DISCOVERY, FIP_DISCOVERY_ADVERTISE, fcoe_fip_rx_advertisement }, { FIP_CODE_ELS, FIP_ELS_RESPONSE, @@ -891,7 +1002,42 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { /* Increment the timeout counter */ fcoe->timeouts++; - if ( ! ( fcoe->flags & FCOE_HAVE_FCF ) ) { + if ( vlan_can_be_trunk ( fcoe->netdev ) & + ! ( fcoe->flags & FCOE_VLAN_TIMED_OUT ) ) { + + /* If we have already found a VLAN, send infrequent + * VLAN requests, in case VLAN information changes. + */ + if ( fcoe->flags & FCOE_VLAN_FOUND ) { + fcoe->flags &= ~FCOE_VLAN_FOUND; + fcoe->timeouts = 0; + start_timer_fixed ( &fcoe->timer, + FCOE_VLAN_POLL_DELAY ); + fcoe_fip_tx_vlan ( fcoe ); + return; + } + + /* If we have not yet found a VLAN, and we have not + * yet timed out and given up on finding one, then + * send a VLAN request and wait. + */ + if ( fcoe->timeouts <= FCOE_MAX_VLAN_REQUESTS ) { + start_timer_fixed ( &fcoe->timer, + FCOE_VLAN_RETRY_DELAY ); + fcoe_fip_tx_vlan ( fcoe ); + return; + } + + /* We have timed out waiting for a VLAN; proceed to + * FIP discovery. + */ + fcoe->flags |= FCOE_VLAN_TIMED_OUT; + fcoe->timeouts = 0; + DBGC ( fcoe, "FCoE %s giving up on VLAN discovery\n", + fcoe->netdev->name ); + start_timer_nodelay ( &fcoe->timer ); + + } else if ( ! ( fcoe->flags & FCOE_HAVE_FCF ) ) { /* If we have not yet found a FIP-capable forwarder, * and we have not yet timed out and given up on