]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[netdevice] Add generic concept of a network device configurator
authorMichael Brown <mcb30@ipxe.org>
Thu, 31 Oct 2013 15:37:52 +0000 (15:37 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 5 Nov 2013 17:30:15 +0000 (17:30 +0000)
iPXE supports multiple mechanisms for network device configuration:
DHCPv4 for IPv4, FIP for FCoE, and SLAAC for IPv6.  At present, DHCPv4
requires an explicit action (e.g. a "dhcp" command), FIP is initiated
implicitly upon opening a network device, and SLAAC takes place
whenever a RA happens to be received.

Add a generic concept of a network device configurator, which provides
a common interface to triggering configuration and to reporting the
result of the configuration process.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/netdevice.h
src/net/netdevice.c

index ae6e5585a0ba16deaf4feebeae662046e0aeeab8..2ea3c8bb19aa5e5a850f62671bbbd0dcc15651ec 100644 (file)
@@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/tables.h>
 #include <ipxe/refcnt.h>
 #include <ipxe/settings.h>
+#include <ipxe/interface.h>
 
 struct io_buffer;
 struct net_device;
@@ -292,6 +293,45 @@ struct net_device_stats {
        struct net_device_error errors[NETDEV_MAX_UNIQUE_ERRORS];
 };
 
+/** A network device configuration */
+struct net_device_configuration {
+       /** Network device */
+       struct net_device *netdev;
+       /** Network device configurator */
+       struct net_device_configurator *configurator;
+       /** Configuration status */
+       int rc;
+       /** Job control interface */
+       struct interface job;
+};
+
+/** A network device configurator */
+struct net_device_configurator {
+       /** Name */
+       const char *name;
+       /** Check applicability of configurator
+        *
+        * @v netdev            Network device
+        * @ret applies         Configurator applies to this network device
+        */
+       int ( * applies ) ( struct net_device *netdev );
+       /** Start configuring network device
+        *
+        * @v job               Job control interface
+        * @v netdev            Network device
+        * @ret rc              Return status code
+        */
+       int ( * start ) ( struct interface *job, struct net_device *netdev );
+};
+
+/** Network device configurator table */
+#define NET_DEVICE_CONFIGURATORS \
+       __table ( struct net_device_configurator, "net_device_configurators" )
+
+/** Declare a network device configurator */
+#define __net_device_configurator \
+       __table_entry ( NET_DEVICE_CONFIGURATORS, 01 )
+
 /** Maximum length of a network device name */
 #define NETDEV_NAME_LEN 12
 
@@ -374,6 +414,9 @@ struct net_device {
 
        /** Driver private data */
        void *priv;
+
+       /** Network device configurations (variable length) */
+       struct net_device_configuration configs[0];
 };
 
 /** Network device is open */
@@ -531,6 +574,35 @@ netdev_settings_init ( struct net_device *netdev ) {
        netdev->settings.settings.op = &netdev_settings_operations;
 }
 
+/**
+ * Get network device configuration
+ *
+ * @v netdev           Network device
+ * @v configurator     Network device configurator
+ * @ret config         Network device configuration
+ */
+static inline struct net_device_configuration *
+netdev_configuration ( struct net_device *netdev,
+                      struct net_device_configurator *configurator ) {
+
+       return &netdev->configs[ table_index ( NET_DEVICE_CONFIGURATORS,
+                                              configurator ) ];
+}
+
+/**
+ * Check if configurator applies to network device
+ *
+ * @v netdev           Network device
+ * @v configurator     Network device configurator
+ * @ret applies                Configurator applies to network device
+ */
+static inline int
+netdev_configurator_applies ( struct net_device *netdev,
+                             struct net_device_configurator *configurator ) {
+       return ( ( configurator->applies == NULL ) ||
+                configurator->applies ( netdev ) );
+}
+
 /**
  * Check link state of network device
  *
@@ -619,6 +691,13 @@ extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
                    uint16_t net_proto, const void *ll_dest,
                    const void *ll_source, unsigned int flags );
 extern void net_poll ( void );
+extern struct net_device_configurator *
+find_netdev_configurator ( const char *name );
+extern int netdev_configure ( struct net_device *netdev,
+                             struct net_device_configurator *configurator );
+extern int netdev_configure_all ( struct net_device *netdev );
+extern int netdev_configuration_in_progress ( struct net_device *netdev );
+extern int netdev_configuration_ok ( struct net_device *netdev );
 
 /**
  * Complete network transmission
index fcd1f7cf209fcdc9fb450b184b5803d5a868d233..9a2f811733e602e4867c65a388d479bda5c75991 100644 (file)
@@ -54,6 +54,16 @@ static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
 #define EINFO_EUNKNOWN_LINK_STATUS \
        __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" )
 
+/** Default not-yet-attempted-configuration status code */
+#define EUNUSED_CONFIG __einfo_error ( EINFO_EUNUSED_CONFIG )
+#define EINFO_EUNUSED_CONFIG \
+       __einfo_uniqify ( EINFO_EINPROGRESS, 0x02, "Unused" )
+
+/** Default configuration-in-progress status code */
+#define EINPROGRESS_CONFIG __einfo_error ( EINFO_EINPROGRESS_CONFIG )
+#define EINFO_EINPROGRESS_CONFIG \
+       __einfo_uniqify ( EINFO_EINPROGRESS, 0x03, "Incomplete" )
+
 /** Default link-down status code */
 #define ENOTCONN_LINK_DOWN __einfo_error ( EINFO_ENOTCONN_LINK_DOWN )
 #define EINFO_ENOTCONN_LINK_DOWN \
@@ -63,6 +73,8 @@ static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
 struct errortab netdev_errors[] __errortab = {
        __einfo_errortab ( EINFO_EUNKNOWN_LINK_STATUS ),
        __einfo_errortab ( EINFO_ENOTCONN_LINK_DOWN ),
+       __einfo_errortab ( EINFO_EUNUSED_CONFIG ),
+       __einfo_errortab ( EINFO_EINPROGRESS_CONFIG ),
 };
 
 /**
@@ -443,6 +455,41 @@ static void netdev_rx_flush ( struct net_device *netdev ) {
        }
 }
 
+/**
+ * Finish network device configuration
+ *
+ * @v config           Network device configuration
+ * @v rc               Reason for completion
+ */
+static void netdev_config_close ( struct net_device_configuration *config,
+                                 int rc ) {
+       struct net_device_configurator *configurator = config->configurator;
+       struct net_device *netdev = config->netdev;
+
+       /* Restart interface */
+       intf_restart ( &config->job, rc );
+
+       /* Record configuration result */
+       config->rc = rc;
+       if ( rc == 0 ) {
+               DBGC ( netdev, "NETDEV %s configured via %s\n",
+                      netdev->name, configurator->name );
+       } else {
+               DBGC ( netdev, "NETDEV %s configuration via %s failed: %s\n",
+                      netdev->name, configurator->name, strerror ( rc ) );
+       }
+}
+
+/** Network device configuration interface operations */
+static struct interface_operation netdev_config_ops[] = {
+       INTF_OP ( intf_close, struct net_device_configuration *,
+                 netdev_config_close ),
+};
+
+/** Network device configuration interface descriptor */
+static struct interface_descriptor netdev_config_desc =
+       INTF_DESC ( struct net_device_configuration, job, netdev_config_ops );
+
 /**
  * Free network device
  *
@@ -461,16 +508,22 @@ static void free_netdev ( struct refcnt *refcnt ) {
 /**
  * Allocate network device
  *
- * @v priv_size                Size of private data area (net_device::priv)
+ * @v priv_len         Length of private data area (net_device::priv)
  * @ret netdev         Network device, or NULL
  *
  * Allocates space for a network device and its private data area.
  */
-struct net_device * alloc_netdev ( size_t priv_size ) {
+struct net_device * alloc_netdev ( size_t priv_len ) {
        struct net_device *netdev;
+       struct net_device_configurator *configurator;
+       struct net_device_configuration *config;
+       unsigned int num_configs;
+       size_t confs_len;
        size_t total_len;
 
-       total_len = ( sizeof ( *netdev ) + priv_size );
+       num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS );
+       confs_len = ( num_configs * sizeof ( netdev->configs[0] ) );
+       total_len = ( sizeof ( *netdev ) + confs_len + priv_len );
        netdev = zalloc ( total_len );
        if ( netdev ) {
                ref_init ( &netdev->refcnt, free_netdev );
@@ -479,7 +532,17 @@ struct net_device * alloc_netdev ( size_t priv_size ) {
                INIT_LIST_HEAD ( &netdev->tx_deferred );
                INIT_LIST_HEAD ( &netdev->rx_queue );
                netdev_settings_init ( netdev );
-               netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
+               config = netdev->configs;
+               for_each_table_entry ( configurator, NET_DEVICE_CONFIGURATORS ){
+                       config->netdev = netdev;
+                       config->configurator = configurator;
+                       config->rc = -EUNUSED_CONFIG;
+                       intf_init ( &config->job, &netdev_config_desc,
+                                   &netdev->refcnt );
+                       config++;
+               }
+               netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) +
+                                confs_len );
        }
        return netdev;
 }
@@ -595,6 +658,8 @@ int netdev_open ( struct net_device *netdev ) {
  * @v netdev           Network device
  */
 void netdev_close ( struct net_device *netdev ) {
+       unsigned int num_configs;
+       unsigned int i;
 
        /* Do nothing if device is already closed */
        if ( ! ( netdev->state & NETDEV_OPEN ) )
@@ -602,6 +667,15 @@ void netdev_close ( struct net_device *netdev ) {
 
        DBGC ( netdev, "NETDEV %s closing\n", netdev->name );
 
+       /* Terminate any ongoing configurations.  Use intf_close()
+        * rather than intf_restart() to allow the cancellation to be
+        * reported back to us if a configuration is actually in
+        * progress.
+        */
+       num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS );
+       for ( i = 0 ; i < num_configs ; i++ )
+               intf_close ( &netdev->configs[i].job, -ECANCELED );
+
        /* Remove from open devices list */
        list_del ( &netdev->open_list );
 
@@ -643,9 +717,9 @@ void unregister_netdev ( struct net_device *netdev ) {
        unregister_settings ( netdev_settings ( netdev ) );
 
        /* Remove from device list */
+       DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name );
        list_del ( &netdev->list );
        netdev_put ( netdev );
-       DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name );
 }
 
 /** Enable or disable interrupts
@@ -931,3 +1005,127 @@ static unsigned int net_discard ( void ) {
 struct cache_discarder net_discarder __cache_discarder ( CACHE_NORMAL ) = {
        .discard = net_discard,
 };
+
+/**
+ * Find network device configurator
+ *
+ * @v name             Name
+ * @ret configurator   Network device configurator, or NULL
+ */
+struct net_device_configurator * find_netdev_configurator ( const char *name ) {
+       struct net_device_configurator *configurator;
+
+       for_each_table_entry ( configurator, NET_DEVICE_CONFIGURATORS ) {
+               if ( strcmp ( configurator->name, name ) == 0 )
+                       return configurator;
+       }
+       return NULL;
+}
+
+/**
+ * Start network device configuration
+ *
+ * @v netdev           Network device
+ * @v configurator     Network device configurator
+ * @ret rc             Return status code
+ */
+int netdev_configure ( struct net_device *netdev,
+                      struct net_device_configurator *configurator ) {
+       struct net_device_configuration *config =
+               netdev_configuration ( netdev, configurator );
+       int rc;
+
+       /* Check applicability of configurator */
+       if ( ! netdev_configurator_applies ( netdev, configurator ) ) {
+               DBGC ( netdev, "NETDEV %s does not support configuration via "
+                      "%s\n", netdev->name, configurator->name );
+               return -ENOTSUP;
+       }
+
+       /* Terminate any ongoing configuration */
+       intf_restart ( &config->job, -ECANCELED );
+
+       /* Mark configuration as being in progress */
+       config->rc = -EINPROGRESS_CONFIG;
+
+       DBGC ( netdev, "NETDEV %s starting configuration via %s\n",
+              netdev->name, configurator->name );
+
+       /* Start configuration */
+       if ( ( rc = configurator->start ( &config->job, netdev ) ) != 0 ) {
+               DBGC ( netdev, "NETDEV %s could not start configuration via "
+                      "%s: %s\n", netdev->name, configurator->name,
+                      strerror ( rc ) );
+               config->rc = rc;
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Start network device configuration via all supported configurators
+ *
+ * @v netdev           Network device
+ * @ret rc             Return status code
+ */
+int netdev_configure_all ( struct net_device *netdev ) {
+       struct net_device_configurator *configurator;
+       int rc;
+
+       /* Start configuration for each configurator */
+       for_each_table_entry ( configurator, NET_DEVICE_CONFIGURATORS ) {
+
+               /* Skip any inapplicable configurators */
+               if ( ! netdev_configurator_applies ( netdev, configurator ) )
+                       continue;
+
+               /* Start configuration */
+               if ( ( rc = netdev_configure ( netdev, configurator ) ) != 0 )
+                       return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Check if network device has a configuration with a specified status code
+ *
+ * @v netdev           Network device
+ * @v rc               Status code
+ * @ret has_rc         Network device has a configuration with this status code
+ */
+static int netdev_has_configuration_rc ( struct net_device *netdev, int rc ) {
+       unsigned int num_configs;
+       unsigned int i;
+
+       num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS );
+       for ( i = 0 ; i < num_configs ; i++ ) {
+               if ( netdev->configs[i].rc == rc )
+                       return 1;
+       }
+       return 0;
+}
+
+/**
+ * Check if network device configuration is in progress
+ *
+ * @v netdev           Network device
+ * @ret is_in_progress Network device configuration is in progress
+ */
+int netdev_configuration_in_progress ( struct net_device *netdev ) {
+
+       return netdev_has_configuration_rc ( netdev, -EINPROGRESS_CONFIG );
+}
+
+/**
+ * Check if network device has at least one successful configuration
+ *
+ * @v netdev           Network device
+ * @v configurator     Configurator
+ * @ret rc             Return status code
+ */
+int netdev_configuration_ok ( struct net_device *netdev ) {
+
+       return netdev_has_configuration_rc ( netdev, 0 );
+}