mirror of https://github.com/ipxe/ipxe.git
[netdevice] Add generic concept of a network device configurator
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>pull/17/head
parent
55e85ad1ee
commit
f2bc138391
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue