]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[fcoe] Add support for FIP VLAN discovery
authorMichael Brown <mcb30@ipxe.org>
Fri, 26 Nov 2010 01:06:32 +0000 (01:06 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 26 Nov 2010 01:09:41 +0000 (01:09 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/fcoe.h
src/net/fcoe.c

index 69120d3e66d83592a8f8b28dad8946f12723719d..6ba5b406a89a674e7c6900913b92a78638ec7ef6 100644 (file)
@@ -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 */
index 474467995622284c94591b533f4935984aac9fa6..7072877181ae6c1ee37e554b7e4f19b43a62a363 100644 (file)
@@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/xfer.h>
 #include <ipxe/netdevice.h>
 #include <ipxe/ethernet.h>
+#include <ipxe/vlan.h>
 #include <ipxe/features.h>
 #include <ipxe/errortab.h>
 #include <ipxe/device.h>
@@ -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