132 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			132 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | # $Id$ | ||
|  | 
 | ||
|  | Adding netmap support to network device drivers | ||
|  | ------------------------------------------------ | ||
|  | 
 | ||
|  | Netmap requires some small modifications to device drivers | ||
|  | to support the new API. You will need to add small patches | ||
|  | in 3-4 places in the original source, and implement typically | ||
|  | 5 new functions. | ||
|  | 
 | ||
|  | Device driver patches | ||
|  | ------------------------ | ||
|  | + in the initial part of the source, after the device-specific | ||
|  |   headers and prototypes have been declared, add the following | ||
|  |     <pre> | ||
|  | 	+#if defined(DEV_NETMAP) || defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) | ||
|  | 	+#include <dev/netmap/if_re_netmap.h> | ||
|  | 	+#endif /* !DEV_NETMAP */ | ||
|  |     </pre> | ||
|  |     The place is typically ... in FreeBSD, and | ||
|  |     ... on Linux. | ||
|  | 
 | ||
|  |     The header really contains the new functions that implement | ||
|  |     the netmap API. Including them inline simplifies the building | ||
|  |     as it does not require to insert additional dependencies in the | ||
|  |     build system. | ||
|  | 
 | ||
|  |     On FreeBSD DEV_NETMAP is sufficient to detect whether netmap extensions | ||
|  |     should be compiled in, whereas CONFIG_NETMAP and CONFIG_NETMAP_MODULE | ||
|  |     are the Linux equivalent. | ||
|  | 
 | ||
|  |     If a driver is made of multiple source files, you will need to include | ||
|  |     the additional header in all the (few) patched files, preferably using | ||
|  |     a macro such as NETMAP_FOO_MAIN to indicate the file where the | ||
|  |     new functions should be compiled in. | ||
|  | 
 | ||
|  | + near the end of the attach routine, once the ifnet/net_device structure | ||
|  |   has been filled and initialized, add | ||
|  |     <pre> | ||
|  | 	+#ifdef DEV_NETMAP | ||
|  | 	+	foo_netmap_attach(adapter); | ||
|  | 	+#endif /* DEV_NETMAP */ | ||
|  |     </pre> | ||
|  |   The argument is either the ifnet or the private device descriptor.   | ||
|  |   This is in foo_attach() on FreeBSD, and somewhere in the path of | ||
|  |   XXX foo_open() in Linux | ||
|  | 
 | ||
|  | + near the code called on device removal, add | ||
|  |     <pre> | ||
|  | 	+#ifdef DEV_NETMAP | ||
|  | 	+	netmap_detach(ifp); | ||
|  | 	+#endif /* DEV_NETMAP */ | ||
|  |     </pre> | ||
|  | 
 | ||
|  | + after the tx/rx rings have been initialized, add a patch like this: | ||
|  |     <pre> | ||
|  | 	+#ifdef DEV_NETMAP | ||
|  | 	+	foo_netmap_config(priv); | ||
|  | 	+#endif /* DEV_NETMAP */ | ||
|  |     </pre> | ||
|  |     The argument is typically the private device descriptor, or even | ||
|  |     the struct ifnet/net_device. | ||
|  | 
 | ||
|  | + in the interrupt dispatch routines, something like | ||
|  |     <pre> | ||
|  | 	+#ifdef DEV_NETMAP | ||
|  | 	+       int dummy; | ||
|  | 	+       if (netmap_rx_irq(adapter->netdev, rx_ring->queue_index, &dummy)) | ||
|  | 	+               return true; | ||
|  | 	+#endif /* DEV_NETMAP */ | ||
|  | 	... | ||
|  | 	+#ifdef DEV_NETMAP | ||
|  | 	+       if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index)) | ||
|  | 	+               return true; /* seems to be ignored */ | ||
|  | 	+#endif /* DEV_NETMAP */ | ||
|  |      </pre> | ||
|  |      to skip the normal processing and instead wake up the process in | ||
|  |      charge of doing I/O | ||
|  | 
 | ||
|  | New functions | ||
|  | ---------------- | ||
|  | The new functions serve to register the netmap-enabled device driver, | ||
|  | support the enable/disable of netmap mode, attach netmap buffers to the | ||
|  | NIC rings, and finally implement the handlers (*_txsync(), *_rxsync()) | ||
|  | called by the system calls. | ||
|  | 
 | ||
|  | * foo_netmap_attach() | ||
|  |     This is a relatively mechanical function. The purpose is to fetch from | ||
|  |     the device descriptor information on the number of rings and buffers, | ||
|  |     the way locks are used, and invoke netmap_attach(). | ||
|  | 
 | ||
|  | * foo_netmap_config() | ||
|  |     This function is in charge of (over)writing the NIC rings with | ||
|  |     pointers to the netmap buffers. Although this is device dependent, | ||
|  |     we can often ignore the locking issue and expect that the locking is | ||
|  |     already taken care of by the caller. | ||
|  | 
 | ||
|  |     foo_netmap_config() only needs to run if the card is in netmap mode. | ||
|  |     A quick way to check is to call netmap_ring_init() on one of the rings, | ||
|  |     if the function returns NULL we can immediately exit. | ||
|  |     Otherwise, we should run a couple of nested loops (on the rings, | ||
|  |     and then on the buffers) to fill the NIC descriptors with the | ||
|  |     addresses of the (preallocated) netmap buffers. | ||
|  | 
 | ||
|  |     For the TX rings this can even be a no-op because these rings are | ||
|  |     typically uninitialized, and the pointers can be overridden in the | ||
|  |     txsync() routine. | ||
|  | 
 | ||
|  |     For the receive ring, the operation is more critical because the | ||
|  |     buffers should be available by the time the NIC is enabled. | ||
|  | 
 | ||
|  |     Note that the device driver typically maintains head and tail pointers | ||
|  |     to indicate which buffers are used. It might be convenient to retain | ||
|  |     these indexes because may of the support routines, watchdogs etc. | ||
|  |     depends on their values. | ||
|  | 
 | ||
|  |     We should note that, especially on the receive ring, there might be | ||
|  |     an offset between the indexes used in the netmap ring and those used | ||
|  |     in the NIC ring (which might even be non-contiguous). | ||
|  | 
 | ||
|  | * foo_netmap_reg() | ||
|  |     support entering/exiting of netmap mode. Typically, lock, stop the device, | ||
|  |     set/clear the netmap flag, and restart the device. | ||
|  |     An unfortunate side effect of stopping and restarting the device is that | ||
|  |     in many drivers the link is reinitialized, causing long delays for the | ||
|  |     speed negotiations and spanning tree setup. | ||
|  | 
 | ||
|  | 
 | ||
|  | * foo_netmap_txsync() | ||
|  | 
 | ||
|  | * foo_netmap_rxsync() |