|  | @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			| 31 | 31 |  #include <ipxe/tables.h>
 | 
		
	
		
			
			| 32 | 32 |  #include <ipxe/process.h>
 | 
		
	
		
			
			| 33 | 33 |  #include <ipxe/init.h>
 | 
		
	
		
			
			|  | 34 | +#include <ipxe/malloc.h>
 | 
		
	
		
			
			| 34 | 35 |  #include <ipxe/device.h>
 | 
		
	
		
			
			| 35 | 36 |  #include <ipxe/errortab.h>
 | 
		
	
		
			
			| 36 | 37 |  #include <ipxe/vlan.h>
 | 
		
	
	
		
			
			|  | @@ -212,6 +213,43 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) {
 | 
		
	
		
			
			| 212 | 213 |  	return rc;
 | 
		
	
		
			
			| 213 | 214 |  }
 | 
		
	
		
			
			| 214 | 215 |  
 | 
		
	
		
			
			|  | 216 | +/**
 | 
		
	
		
			
			|  | 217 | + * Defer transmitted packet
 | 
		
	
		
			
			|  | 218 | + *
 | 
		
	
		
			
			|  | 219 | + * @v netdev		Network device
 | 
		
	
		
			
			|  | 220 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 221 | + *
 | 
		
	
		
			
			|  | 222 | + * Drivers may call netdev_tx_defer() if there is insufficient space
 | 
		
	
		
			
			|  | 223 | + * in the transmit descriptor ring.  Any packets deferred in this way
 | 
		
	
		
			
			|  | 224 | + * will be automatically retransmitted as soon as space becomes
 | 
		
	
		
			
			|  | 225 | + * available (i.e. as soon as the driver calls netdev_tx_complete()).
 | 
		
	
		
			
			|  | 226 | + *
 | 
		
	
		
			
			|  | 227 | + * The packet must currently be in the network device's TX queue.
 | 
		
	
		
			
			|  | 228 | + *
 | 
		
	
		
			
			|  | 229 | + * Drivers utilising netdev_tx_defer() must ensure that space in the
 | 
		
	
		
			
			|  | 230 | + * transmit descriptor ring is freed up @b before calling
 | 
		
	
		
			
			|  | 231 | + * netdev_tx_complete().  For example, if the ring is modelled using a
 | 
		
	
		
			
			|  | 232 | + * producer counter and a consumer counter, then the consumer counter
 | 
		
	
		
			
			|  | 233 | + * must be incremented before the call to netdev_tx_complete().
 | 
		
	
		
			
			|  | 234 | + * Failure to do this will cause the retransmitted packet to be
 | 
		
	
		
			
			|  | 235 | + * immediately redeferred (which will result in out-of-order
 | 
		
	
		
			
			|  | 236 | + * transmissions and other nastiness).
 | 
		
	
		
			
			|  | 237 | + */
 | 
		
	
		
			
			|  | 238 | +void netdev_tx_defer ( struct net_device *netdev, struct io_buffer *iobuf ) {
 | 
		
	
		
			
			|  | 239 | +
 | 
		
	
		
			
			|  | 240 | +	/* Catch data corruption as early as possible */
 | 
		
	
		
			
			|  | 241 | +	list_check_contains_entry ( iobuf, &netdev->tx_queue, list );
 | 
		
	
		
			
			|  | 242 | +
 | 
		
	
		
			
			|  | 243 | +	/* Remove from transmit queue */
 | 
		
	
		
			
			|  | 244 | +	list_del ( &iobuf->list );
 | 
		
	
		
			
			|  | 245 | +
 | 
		
	
		
			
			|  | 246 | +	/* Add to deferred transmit queue */
 | 
		
	
		
			
			|  | 247 | +	list_add_tail ( &iobuf->list, &netdev->tx_deferred );
 | 
		
	
		
			
			|  | 248 | +
 | 
		
	
		
			
			|  | 249 | +	/* Record "out of space" statistic */
 | 
		
	
		
			
			|  | 250 | +	netdev_tx_err ( netdev, NULL, -ENOBUFS );
 | 
		
	
		
			
			|  | 251 | +}
 | 
		
	
		
			
			|  | 252 | +
 | 
		
	
		
			
			| 215 | 253 |  /**
 | 
		
	
		
			
			| 216 | 254 |   * Discard transmitted packet
 | 
		
	
		
			
			| 217 | 255 |   *
 | 
		
	
	
		
			
			|  | @@ -257,6 +295,13 @@ void netdev_tx_complete_err ( struct net_device *netdev,
 | 
		
	
		
			
			| 257 | 295 |  	/* Dequeue and free I/O buffer */
 | 
		
	
		
			
			| 258 | 296 |  	list_del ( &iobuf->list );
 | 
		
	
		
			
			| 259 | 297 |  	netdev_tx_err ( netdev, iobuf, rc );
 | 
		
	
		
			
			|  | 298 | +
 | 
		
	
		
			
			|  | 299 | +	/* Transmit first pending packet, if any */
 | 
		
	
		
			
			|  | 300 | +	if ( ( iobuf = list_first_entry ( &netdev->tx_deferred,
 | 
		
	
		
			
			|  | 301 | +					  struct io_buffer, list ) ) != NULL ) {
 | 
		
	
		
			
			|  | 302 | +		list_del ( &iobuf->list );
 | 
		
	
		
			
			|  | 303 | +		netdev_tx ( netdev, iobuf );
 | 
		
	
		
			
			|  | 304 | +	}
 | 
		
	
		
			
			| 260 | 305 |  }
 | 
		
	
		
			
			| 261 | 306 |  
 | 
		
	
		
			
			| 262 | 307 |  /**
 | 
		
	
	
		
			
			|  | @@ -270,9 +315,9 @@ void netdev_tx_complete_err ( struct net_device *netdev,
 | 
		
	
		
			
			| 270 | 315 |  void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) {
 | 
		
	
		
			
			| 271 | 316 |  	struct io_buffer *iobuf;
 | 
		
	
		
			
			| 272 | 317 |  
 | 
		
	
		
			
			| 273 |  | -	list_for_each_entry ( iobuf, &netdev->tx_queue, list ) {
 | 
		
	
		
			
			|  | 318 | +	if ( ( iobuf = list_first_entry ( &netdev->tx_queue, struct io_buffer,
 | 
		
	
		
			
			|  | 319 | +					  list ) ) != NULL ) {
 | 
		
	
		
			
			| 274 | 320 |  		netdev_tx_complete_err ( netdev, iobuf, rc );
 | 
		
	
		
			
			| 275 |  | -		return;
 | 
		
	
		
			
			| 276 | 321 |  	}
 | 
		
	
		
			
			| 277 | 322 |  }
 | 
		
	
		
			
			| 278 | 323 |  
 | 
		
	
	
		
			
			|  | @@ -283,10 +328,15 @@ void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) {
 | 
		
	
		
			
			| 283 | 328 |   */
 | 
		
	
		
			
			| 284 | 329 |  static void netdev_tx_flush ( struct net_device *netdev ) {
 | 
		
	
		
			
			| 285 | 330 |  
 | 
		
	
		
			
			| 286 |  | -	/* Discard any packets in the TX queue */
 | 
		
	
		
			
			|  | 331 | +	/* Discard any packets in the TX queue.  This will also cause
 | 
		
	
		
			
			|  | 332 | +	 * any packets in the deferred TX queue to be discarded
 | 
		
	
		
			
			|  | 333 | +	 * automatically.
 | 
		
	
		
			
			|  | 334 | +	 */
 | 
		
	
		
			
			| 287 | 335 |  	while ( ! list_empty ( &netdev->tx_queue ) ) {
 | 
		
	
		
			
			| 288 | 336 |  		netdev_tx_complete_next_err ( netdev, -ECANCELED );
 | 
		
	
		
			
			| 289 | 337 |  	}
 | 
		
	
		
			
			|  | 338 | +	assert ( list_empty ( &netdev->tx_queue ) );
 | 
		
	
		
			
			|  | 339 | +	assert ( list_empty ( &netdev->tx_deferred ) );
 | 
		
	
		
			
			| 290 | 340 |  }
 | 
		
	
		
			
			| 291 | 341 |  
 | 
		
	
		
			
			| 292 | 342 |  /**
 | 
		
	
	
		
			
			|  | @@ -424,6 +474,7 @@ struct net_device * alloc_netdev ( size_t priv_size ) {
 | 
		
	
		
			
			| 424 | 474 |  		ref_init ( &netdev->refcnt, free_netdev );
 | 
		
	
		
			
			| 425 | 475 |  		netdev->link_rc = -EUNKNOWN_LINK_STATUS;
 | 
		
	
		
			
			| 426 | 476 |  		INIT_LIST_HEAD ( &netdev->tx_queue );
 | 
		
	
		
			
			|  | 477 | +		INIT_LIST_HEAD ( &netdev->tx_deferred );
 | 
		
	
		
			
			| 427 | 478 |  		INIT_LIST_HEAD ( &netdev->rx_queue );
 | 
		
	
		
			
			| 428 | 479 |  		netdev_settings_init ( netdev );
 | 
		
	
		
			
			| 429 | 480 |  		netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
 | 
		
	
	
		
			
			|  | @@ -817,3 +868,36 @@ __weak struct net_device * vlan_find ( struct net_device *trunk __unused,
 | 
		
	
		
			
			| 817 | 868 |  
 | 
		
	
		
			
			| 818 | 869 |  /** Networking stack process */
 | 
		
	
		
			
			| 819 | 870 |  PERMANENT_PROCESS ( net_process, net_step );
 | 
		
	
		
			
			|  | 871 | +
 | 
		
	
		
			
			|  | 872 | +/**
 | 
		
	
		
			
			|  | 873 | + * Discard some cached network device data
 | 
		
	
		
			
			|  | 874 | + *
 | 
		
	
		
			
			|  | 875 | + * @ret discarded	Number of cached items discarded
 | 
		
	
		
			
			|  | 876 | + */
 | 
		
	
		
			
			|  | 877 | +static unsigned int net_discard ( void ) {
 | 
		
	
		
			
			|  | 878 | +	struct net_device *netdev;
 | 
		
	
		
			
			|  | 879 | +	struct io_buffer *iobuf;
 | 
		
	
		
			
			|  | 880 | +	unsigned int discarded = 0;
 | 
		
	
		
			
			|  | 881 | +
 | 
		
	
		
			
			|  | 882 | +	/* Try to drop one deferred TX packet from each network device */
 | 
		
	
		
			
			|  | 883 | +	for_each_netdev ( netdev ) {
 | 
		
	
		
			
			|  | 884 | +		if ( ( iobuf = list_first_entry ( &netdev->tx_deferred,
 | 
		
	
		
			
			|  | 885 | +						  struct io_buffer,
 | 
		
	
		
			
			|  | 886 | +						  list ) ) != NULL ) {
 | 
		
	
		
			
			|  | 887 | +
 | 
		
	
		
			
			|  | 888 | +			/* Discard first deferred packet */
 | 
		
	
		
			
			|  | 889 | +			list_del ( &iobuf->list );
 | 
		
	
		
			
			|  | 890 | +			free ( iobuf );
 | 
		
	
		
			
			|  | 891 | +
 | 
		
	
		
			
			|  | 892 | +			/* Report discard */
 | 
		
	
		
			
			|  | 893 | +			discarded++;
 | 
		
	
		
			
			|  | 894 | +		}
 | 
		
	
		
			
			|  | 895 | +	}
 | 
		
	
		
			
			|  | 896 | +
 | 
		
	
		
			
			|  | 897 | +	return discarded;
 | 
		
	
		
			
			|  | 898 | +}
 | 
		
	
		
			
			|  | 899 | +
 | 
		
	
		
			
			|  | 900 | +/** Network device cache discarder */
 | 
		
	
		
			
			|  | 901 | +struct cache_discarder net_discarder __cache_discarder ( CACHE_NORMAL ) = {
 | 
		
	
		
			
			|  | 902 | +	.discard = net_discard,
 | 
		
	
		
			
			|  | 903 | +};
 |