[netdevice] Allocate private data for each network upper-layer driver

Allow network upper-layer drivers (such as LLDP, which attaches to
each network device in order to provide a corresponding LLDP settings
block) to specify a size for private data, which will be allocated as
part of the network device structure (as with the existing private
data allocated for the underlying device driver).

This will allow network upper-layer drivers to be simplified by
omitting memory allocation and freeing code.  If the upper-layer
driver requires a reference counter (e.g. for interface
initialisation), then it may use the network device's existing
reference counter, since this is now the reference counter for the
containing block of memory.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/arch/x86/interface/pxe/pxe_call.c b/src/arch/x86/interface/pxe/pxe_call.c
index 6711829..0e8d5c5 100644
--- a/src/arch/x86/interface/pxe/pxe_call.c
+++ b/src/arch/x86/interface/pxe/pxe_call.c
@@ -375,9 +375,10 @@
  * Notify BIOS of existence of network device
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int pxe_notify ( struct net_device *netdev ) {
+static int pxe_notify ( struct net_device *netdev, void *priv __unused ) {
 
 	/* Do nothing if we already have a network device */
 	if ( pxe_netdev )
diff --git a/src/arch/x86/interface/vmware/guestinfo.c b/src/arch/x86/interface/vmware/guestinfo.c
index a0530c8..b52c2e8 100644
--- a/src/arch/x86/interface/vmware/guestinfo.c
+++ b/src/arch/x86/interface/vmware/guestinfo.c
@@ -207,9 +207,11 @@
  * Create per-netdevice GuestInfo settings
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int guestinfo_net_probe ( struct net_device *netdev ) {
+static int guestinfo_net_probe ( struct net_device *netdev,
+				 void *priv __unused ) {
 	struct settings *settings;
 	int rc;
 
@@ -247,8 +249,10 @@
  * Remove per-netdevice GuestInfo settings
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void guestinfo_net_remove ( struct net_device *netdev ) {
+static void guestinfo_net_remove ( struct net_device *netdev,
+				   void *priv __unused ) {
 	struct settings *parent = netdev_settings ( netdev );
 	struct settings *settings;
 
diff --git a/src/core/cachedhcp.c b/src/core/cachedhcp.c
index 60213f0..57226e1 100644
--- a/src/core/cachedhcp.c
+++ b/src/core/cachedhcp.c
@@ -295,9 +295,10 @@
  * Apply cached DHCPACK to network device, if applicable
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int cachedhcp_probe ( struct net_device *netdev ) {
+static int cachedhcp_probe ( struct net_device *netdev, void *priv __unused ) {
 
 	/* Apply cached DHCPACK to network device, if applicable */
 	return cachedhcp_apply ( &cached_dhcpack, netdev );
diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c
index 90930a5..12713c5 100644
--- a/src/drivers/net/netfront.c
+++ b/src/drivers/net/netfront.c
@@ -1056,9 +1056,11 @@
  * Inhibit emulated PCI devices
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int netfront_net_probe ( struct net_device *netdev ) {
+static int netfront_net_probe ( struct net_device *netdev,
+				void *priv __unused ) {
 	struct netfront_nic *netfront;
 
 	/* Inhibit emulated PCI devices matching an existing netfront device */
diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h
index a65dbfd..16db413 100644
--- a/src/include/ipxe/netdevice.h
+++ b/src/include/ipxe/netdevice.h
@@ -473,22 +473,27 @@
 struct net_driver {
 	/** Name */
 	const char *name;
+	/** Size of private data */
+	size_t priv_len;
 	/** Probe device
 	 *
 	 * @v netdev		Network device
+	 * @v priv		Private data
 	 * @ret rc		Return status code
 	 */
-	int ( * probe ) ( struct net_device *netdev );
+	int ( * probe ) ( struct net_device *netdev, void *priv );
 	/** Notify of device or link state change
 	 *
 	 * @v netdev		Network device
+	 * @v priv		Private data
 	 */
-	void ( * notify ) ( struct net_device *netdev );
+	void ( * notify ) ( struct net_device *netdev, void *priv );
 	/** Remove device
 	 *
 	 * @v netdev		Network device
+	 * @v priv		Private data
 	 */
-	void ( * remove ) ( struct net_device *netdev );
+	void ( * remove ) ( struct net_device *netdev, void *priv );
 };
 
 /** Network driver table */
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c
index c4f7d4e..8443be9 100644
--- a/src/interface/efi/efi_snp.c
+++ b/src/interface/efi/efi_snp.c
@@ -1777,9 +1777,10 @@
  * Create SNP device
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int efi_snp_probe ( struct net_device *netdev ) {
+static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) {
 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 	struct efi_device *efidev;
 	struct efi_snp_device *snpdev;
@@ -2017,8 +2018,9 @@
  * Handle SNP device or link state change
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void efi_snp_notify ( struct net_device *netdev ) {
+static void efi_snp_notify ( struct net_device *netdev, void *priv __unused ) {
 	struct efi_snp_device *snpdev;
 
 	/* Locate SNP device */
@@ -2042,8 +2044,9 @@
  * Destroy SNP device
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void efi_snp_remove ( struct net_device *netdev ) {
+static void efi_snp_remove ( struct net_device *netdev, void *priv __unused ) {
 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 	struct efi_snp_device *snpdev;
 	int leak = efi_shutdown_in_progress;
diff --git a/src/net/fcoe.c b/src/net/fcoe.c
index f910eee..70804dd 100644
--- a/src/net/fcoe.c
+++ b/src/net/fcoe.c
@@ -1110,9 +1110,10 @@
  * Create FCoE port
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int fcoe_probe ( struct net_device *netdev ) {
+static int fcoe_probe ( struct net_device *netdev, void *priv __unused ) {
 	struct ll_protocol *ll_protocol = netdev->ll_protocol;
 	struct fcoe_port *fcoe;
 	int rc;
@@ -1162,8 +1163,9 @@
  * Handle FCoE port device or link state change
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void fcoe_notify ( struct net_device *netdev ) {
+static void fcoe_notify ( struct net_device *netdev, void *priv __unused ) {
 	struct fcoe_port *fcoe;
 
 	/* Sanity check */
@@ -1185,8 +1187,9 @@
  * Destroy FCoE port
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void fcoe_remove ( struct net_device *netdev ) {
+static void fcoe_remove ( struct net_device *netdev, void *priv __unused ) {
 	struct fcoe_port *fcoe;
 
 	/* Sanity check */
diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c
index 4f5c618..5e805fa 100644
--- a/src/net/infiniband/xsigo.c
+++ b/src/net/infiniband/xsigo.c
@@ -1829,8 +1829,10 @@
  * Handle device or link status change
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void xsigo_net_notify ( struct net_device *netdev ) {
+static void xsigo_net_notify ( struct net_device *netdev,
+			       void *priv __unused ) {
 	struct xsigo_device *xdev;
 	struct ib_device *ibdev;
 	struct xsigo_manager *xcm;
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index ef5e51d..a0173df 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -1224,9 +1224,11 @@
  * Register IPv6 link-local address settings
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int ipv6_register_settings ( struct net_device *netdev ) {
+static int ipv6_register_settings ( struct net_device *netdev,
+				    void *priv __unused ) {
 	struct settings *parent = netdev_settings ( netdev );
 	struct ipv6_settings *ipv6set;
 	int rc;
diff --git a/src/net/lldp.c b/src/net/lldp.c
index 72e3ecd..2ef32cb 100644
--- a/src/net/lldp.c
+++ b/src/net/lldp.c
@@ -296,9 +296,10 @@
  * Create LLDP settings block
  *
  * @v netdev		Network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int lldp_probe ( struct net_device *netdev ) {
+static int lldp_probe ( struct net_device *netdev, void *priv __unused ) {
 	struct lldp_settings *lldpset;
 	int rc;
 
diff --git a/src/net/neighbour.c b/src/net/neighbour.c
index 7f66d99..13a8bc3 100644
--- a/src/net/neighbour.c
+++ b/src/net/neighbour.c
@@ -383,8 +383,9 @@
  * Update neighbour cache on network device state change or removal
  *
  * @v netdev		Network device
+ * @v priv		Private data
  */
-static void neighbour_flush ( struct net_device *netdev ) {
+static void neighbour_flush ( struct net_device *netdev, void *priv __unused ) {
 	struct neighbour *neighbour;
 	struct neighbour *tmp;
 
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index 9151782..ff08071 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -110,16 +110,64 @@
 }
 
 /**
+ * Get offset of network device driver private data
+ *
+ * @v driver		Upper-layer driver, or NULL for device driver
+ * @ret offset		Offset of driver private data
+ */
+static size_t netdev_priv_offset ( struct net_driver *driver ) {
+	struct net_device *netdev;
+	unsigned int num_configs;
+	size_t offset;
+
+	/* Allow space for network device */
+	offset = sizeof ( *netdev );
+
+	/* Allow space for configurations */
+	num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS );
+	offset += ( num_configs * sizeof ( netdev->configs[0] ) );
+
+	/* Place variable-length device driver private data at end */
+	if ( ! driver )
+		driver = table_end ( NET_DRIVERS );
+
+	/* Allow space for preceding upper-layer drivers' private data */
+	for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) {
+		offset += driver->priv_len;
+	}
+
+	/* Sanity check */
+	assert ( ( offset & ( sizeof ( void * ) - 1 ) ) == 0 );
+
+	return offset;
+}
+
+/**
+ * Get network device driver private data
+ *
+ * @v netdev		Network device
+ * @v driver		Upper-layer driver, or NULL for device driver
+ * @ret priv		Driver private data
+ */
+static void * netdev_priv ( struct net_device *netdev,
+			    struct net_driver *driver ) {
+
+	return ( ( ( void * ) netdev ) + netdev_priv_offset ( driver ) );
+}
+
+/**
  * Notify drivers of network device or link state change
  *
  * @v netdev		Network device
  */
 static void netdev_notify ( struct net_device *netdev ) {
 	struct net_driver *driver;
+	void *priv;
 
 	for_each_table_entry ( driver, NET_DRIVERS ) {
+		priv = netdev_priv ( netdev, driver );
 		if ( driver->notify )
-			driver->notify ( netdev );
+			driver->notify ( netdev, priv );
 	}
 }
 
@@ -675,14 +723,8 @@
 	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;
 
-	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 );
+	netdev = zalloc ( netdev_priv_offset ( NULL ) + priv_len );
 	if ( netdev ) {
 		ref_init ( &netdev->refcnt, free_netdev );
 		netdev->link_rc = -EUNKNOWN_LINK_STATUS;
@@ -701,8 +743,7 @@
 				    &netdev->refcnt );
 			config++;
 		}
-		netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) +
-				 confs_len );
+		netdev->priv = netdev_priv ( netdev, NULL );
 	}
 	return netdev;
 }
@@ -722,6 +763,7 @@
 	struct net_device *duplicate;
 	unsigned int i;
 	uint32_t seed;
+	void *priv;
 	int rc;
 
 	/* Set initial link-layer address, if not already set */
@@ -784,7 +826,9 @@
 
 	/* Probe device */
 	for_each_table_entry ( driver, NET_DRIVERS ) {
-		if ( driver->probe && ( rc = driver->probe ( netdev ) ) != 0 ) {
+		priv = netdev_priv ( netdev, driver );
+		if ( driver->probe &&
+		     ( rc = driver->probe ( netdev, priv ) ) != 0 ) {
 			DBGC ( netdev, "NETDEV %s could not add %s device: "
 			       "%s\n", netdev->name, driver->name,
 			       strerror ( rc ) );
@@ -796,8 +840,9 @@
 
  err_probe:
 	for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) {
+		priv = netdev_priv ( netdev, driver );
 		if ( driver->remove )
-			driver->remove ( netdev );
+			driver->remove ( netdev, priv );
 	}
 	clear_settings ( netdev_settings ( netdev ) );
 	unregister_settings ( netdev_settings ( netdev ) );
@@ -896,14 +941,16 @@
  */
 void unregister_netdev ( struct net_device *netdev ) {
 	struct net_driver *driver;
+	void *priv;
 
 	/* Ensure device is closed */
 	netdev_close ( netdev );
 
 	/* Remove device */
 	for_each_table_entry_reverse ( driver, NET_DRIVERS ) {
+		priv = netdev_priv ( netdev, driver );
 		if ( driver->remove )
-			driver->remove ( netdev );
+			driver->remove ( netdev, priv );
 	}
 
 	/* Unregister per-netdev configuration settings */
diff --git a/src/net/vlan.c b/src/net/vlan.c
index d73a957..c61bb85 100644
--- a/src/net/vlan.c
+++ b/src/net/vlan.c
@@ -470,9 +470,10 @@
  * Create automatic VLAN device
  *
  * @v trunk		Trunk network device
+ * @v priv		Private data
  * @ret rc		Return status code
  */
-static int vlan_probe ( struct net_device *trunk ) {
+static int vlan_probe ( struct net_device *trunk, void *priv __unused ) {
 	int rc;
 
 	/* Do nothing unless an automatic VLAN exists */
@@ -498,8 +499,9 @@
  * Handle trunk network device link state change
  *
  * @v trunk		Trunk network device
+ * @v priv		Private data
  */
-static void vlan_notify ( struct net_device *trunk ) {
+static void vlan_notify ( struct net_device *trunk, void *priv __unused ) {
 	struct net_device *netdev;
 	struct vlan_device *vlan;
 
@@ -538,8 +540,9 @@
  * Destroy all VLAN devices for a given trunk
  *
  * @v trunk		Trunk network device
+ * @v priv		Private data
  */
-static void vlan_remove ( struct net_device *trunk ) {
+static void vlan_remove ( struct net_device *trunk, void *priv __unused ) {
 
 	/* Remove all VLAN devices attached to this trunk, safe
 	 * against arbitrary net device removal.