Subject: Re: [net-next PATCH 1/2] igbvf: add new driver to support 82576 virtual functions

From: Andrew Morton <[email-masked]>
Date: Tue, 10 Mar 2009 22:21:54 -0700

On Tue, 10 Mar 2009 19:09:28 -0700 Jeff Kirsher <[email-masked]> wrote:

> From: Alexander Duyck <[email-masked]>
> 
> This adds an igbvf driver to handle virtual functions provided
> by the igb driver.

The drive-by reader is now wondering what a "virtual function" is.
 
>
> ...
>
> +#define IGBVF_STAT(m, b) sizeof(((struct igbvf_adapter *)0)->m), \
> +		offsetof(struct igbvf_adapter, m), \
> +		offsetof(struct igbvf_adapter, b)
>
> +static const struct igbvf_stats igbvf_gstrings_stats[] = {
> +	{ "rx_packets", IGBVF_STAT(stats.gprc, stats.base_gprc) },
> +	{ "tx_packets", IGBVF_STAT(stats.gptc, stats.base_gptc) },
> +	{ "rx_bytes", IGBVF_STAT(stats.gorc, stats.base_gorc) },
> +	{ "tx_bytes", IGBVF_STAT(stats.gotc, stats.base_gotc) },
> +	{ "multicast", IGBVF_STAT(stats.mprc, stats.base_mprc) },
> +	{ "lbrx_bytes", IGBVF_STAT(stats.gorlbc, stats.base_gorlbc) },
> +	{ "lbrx_packets", IGBVF_STAT(stats.gprlbc, stats.base_gprlbc) },
> +	{ "tx_restart_queue", IGBVF_STAT(restart_queue, zero_base) },
> +	{ "rx_long_byte_count", IGBVF_STAT(stats.gorc, stats.base_gorc) },
> +	{ "rx_csum_offload_good", IGBVF_STAT(hw_csum_good, zero_base) },
> +	{ "rx_csum_offload_errors", IGBVF_STAT(hw_csum_err, zero_base) },
> +	{ "rx_header_split", IGBVF_STAT(rx_hdr_split, zero_base) },
> +	{ "alloc_rx_buff_failed", IGBVF_STAT(alloc_rx_buff_failed, zero_base) },
> +};

<stares at it for a while>

It would be clearer if `m' and `b' were (much) more meaningful identifiers.

> +#define IGBVF_GLOBAL_STATS_LEN	\
> +	(sizeof(igbvf_gstrings_stats) / sizeof(struct igbvf_stats))

This is ARRAY_SIZE().

> +#define IGBVF_STATS_LEN (IGBVF_GLOBAL_STATS_LEN)

Why does this need to exist?

>
> ...
>
> +static int igbvf_set_ringparam(struct net_device *netdev,
> +                               struct ethtool_ringparam *ring)
> +{
> +	struct igbvf_adapter *adapter = netdev_priv(netdev);
> +	struct igbvf_ring *tx_ring, *tx_old;
> +	struct igbvf_ring *rx_ring, *rx_old;
> +	int err;
> +
> +	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
> +		return -EINVAL;
> +
> +	while (test_and_set_bit(__IGBVF_RESETTING, &adapter->state))
> +		msleep(1);

No timeout needed here?  Interrupts might not be working, for example..

> +	if (netif_running(adapter->netdev))
> +		igbvf_down(adapter);
> +
> +	tx_old = adapter->tx_ring;
> +	rx_old = adapter->rx_ring;
> +
> +	err = -ENOMEM;
> +	tx_ring = kzalloc(sizeof(struct igbvf_ring), GFP_KERNEL);
> +	if (!tx_ring)
> +		goto err_alloc_tx;
> +	/*
> +	 * use a memcpy to save any previously configured
> +	 * items like napi structs from having to be
> +	 * reinitialized
> +	 */
> +	memcpy(tx_ring, tx_old, sizeof(struct igbvf_ring));
> +
> +	rx_ring = kzalloc(sizeof(struct igbvf_ring), GFP_KERNEL);
> +	if (!rx_ring)
> +		goto err_alloc_rx;
> +	memcpy(rx_ring, rx_old, sizeof(struct igbvf_ring));
> +
> +	adapter->tx_ring = tx_ring;
> +	adapter->rx_ring = rx_ring;
> +
> +	rx_ring->count = max(ring->rx_pending, (u32)IGBVF_MIN_RXD);
> +	rx_ring->count = min(rx_ring->count, (u32)(IGBVF_MAX_RXD));
> +	rx_ring->count = ALIGN(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE);
> +
> +	tx_ring->count = max(ring->tx_pending, (u32)IGBVF_MIN_TXD);
> +	tx_ring->count = min(tx_ring->count, (u32)(IGBVF_MAX_TXD));
> +	tx_ring->count = ALIGN(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE);
> +
> +	if (netif_running(adapter->netdev)) {
> +		/* Try to get new resources before deleting old */
> +		err = igbvf_setup_rx_resources(adapter);
> +		if (err)
> +			goto err_setup_rx;
> +		err = igbvf_setup_tx_resources(adapter);
> +		if (err)
> +			goto err_setup_tx;
> +
> +		/*
> +		 * restore the old in order to free it,
> +		 * then add in the new
> +		 */
> +		adapter->rx_ring = rx_old;
> +		adapter->tx_ring = tx_old;
> +		igbvf_free_rx_resources(adapter);
> +		igbvf_free_tx_resources(adapter);
> +		kfree(tx_old);
> +		kfree(rx_old);

That's odd-looking.  Why take a copy of rx_old and tx_old when we're
about to free them?

> +		adapter->rx_ring = rx_ring;
> +		adapter->tx_ring = tx_ring;
> +		err = igbvf_up(adapter);
> +		if (err)
> +			goto err_setup;
> +	}
> +
> +	clear_bit(__IGBVF_RESETTING, &adapter->state);
> +	return 0;
> +err_setup_tx:
> +	igbvf_free_rx_resources(adapter);
> +err_setup_rx:
> +	adapter->rx_ring = rx_old;
> +	adapter->tx_ring = tx_old;
> +	kfree(rx_ring);
> +err_alloc_rx:
> +	kfree(tx_ring);
> +err_alloc_tx:
> +	igbvf_up(adapter);
> +err_setup:
> +	clear_bit(__IGBVF_RESETTING, &adapter->state);
> +	return err;
> +}
> +
>
> ...
>
> +static void igbvf_diag_test(struct net_device *netdev,
> +                            struct ethtool_test *eth_test, u64 *data)
> +{
> +	struct igbvf_adapter *adapter = netdev_priv(netdev);
> +
> +	set_bit(__IGBVF_TESTING, &adapter->state);
> +
> +	/*
> +	 * Link test performed before hardware reset so autoneg doesn't
> +	 * interfere with test result
> +	 */
> +	if (igbvf_link_test(adapter, &data[0]))
> +		eth_test->flags |= ETH_TEST_FL_FAILED;
> +
> +	clear_bit(__IGBVF_TESTING, &adapter->state);
> +	msleep_interruptible(4 * 1000);

If this process has signal_pending(), msleep_interruptible() becomes a
no-op.  I expect the driver breaks?

> +}
> +
>
> ...
>
> +static void igbvf_get_ethtool_stats(struct net_device *netdev,
> +                                    struct ethtool_stats *stats,
> +                                    u64 *data)
> +{
> +	struct igbvf_adapter *adapter = netdev_priv(netdev);
> +	int i;
> +
> +	igbvf_update_stats(adapter);
> +	for (i = 0; i < IGBVF_GLOBAL_STATS_LEN; i++) {
> +		char *p = (char *)adapter +
> +		          igbvf_gstrings_stats[i].stat_offset;
> +		char *b = (char *)adapter +
> +		          igbvf_gstrings_stats[i].base_stat_offset;
> +		data[i] = ((igbvf_gstrings_stats[i].sizeof_stat ==
> +		            sizeof(u64)) ? *(u64 *)p : *(u32 *)p) -
> +		          ((igbvf_gstrings_stats[i].sizeof_stat ==
> +		            sizeof(u64)) ? *(u64 *)b : *(u32 *)b);

yitch.

Are we using the C type system as well as possible here?

> +	}
> +
> +}
> +
>
> ...
>
> +#define IGBVF_DESC_UNUSED(R) \
> +	((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
> +	(R)->next_to_clean - (R)->next_to_use - 1)

my eyes.

This macro evaluates its argument multiple times and will misbehave (or
be slow) if passed an expreesion with side-effects.

Why not just write a plain old C function for this?

> +#define IGBVF_RX_DESC_ADV(R, i)     \
> +	(&(((union e1000_adv_rx_desc *)((R).desc))[i]))
> +#define IGBVF_TX_DESC_ADV(R, i)     \
> +	(&(((union e1000_adv_tx_desc *)((R).desc))[i]))
> +#define IGBVF_TX_CTXTDESC_ADV(R, i) \
> +	(&(((struct e1000_adv_tx_context_desc *)((R).desc))[i]))

Maybe igbvf_ring.desc should have had type

	union {
		union e1000_adv_rx_desc;
		union e1000_adv_tx_desc;
		union e1000_adv_tx_context_desc;
	} *

Or something.

>
> ...
>
> +/**
> + * igbvf_alloc_rx_buffers - Replace used receive buffers; packet split
> + * @rx_ring: address of ring structure to repopulate
> + * @cleaned_count: number of buffers to repopulate
> + **/
> +static void igbvf_alloc_rx_buffers(struct igbvf_ring *rx_ring,
> +                                   int cleaned_count)
> +{
> +	struct igbvf_adapter *adapter = rx_ring->adapter;
> +	struct net_device *netdev = adapter->netdev;
> +	struct pci_dev *pdev = adapter->pdev;
> +	union e1000_adv_rx_desc *rx_desc;
> +	struct igbvf_buffer *buffer_info;
> +	struct sk_buff *skb;
> +	unsigned int i;
> +	int bufsz;
> +
> +	i = rx_ring->next_to_use;
> +	buffer_info = &rx_ring->buffer_info[i];
> +
> +	if (adapter->rx_ps_hdr_size)
> +		bufsz = adapter->rx_ps_hdr_size;
> +	else
> +		bufsz = adapter->rx_buffer_len;
> +	bufsz += NET_IP_ALIGN;
> +
> +	while (cleaned_count--) {
> +		rx_desc = IGBVF_RX_DESC_ADV(*rx_ring, i);
> +
> +		if (adapter->rx_ps_hdr_size && !buffer_info->page_dma) {
> +			if (!buffer_info->page) {
> +				buffer_info->page = alloc_page(GFP_ATOMIC);
> +				if (!buffer_info->page) {
> +					adapter->alloc_rx_buff_failed++;
> +					goto no_buffers;
> +				}
> +				buffer_info->page_offset = 0;
> +			} else {
> +				buffer_info->page_offset ^= PAGE_SIZE / 2;
> +			}
> +			buffer_info->page_dma =
> +				pci_map_page(pdev, buffer_info->page,
> +				             buffer_info->page_offset,
> +				             PAGE_SIZE / 2,
> +				             PCI_DMA_FROMDEVICE);
> +		}
> +
> +		if (!buffer_info->skb) {
> +			skb = netdev_alloc_skb(netdev, bufsz);
> +			if (!skb) {
> +				adapter->alloc_rx_buff_failed++;
> +				goto no_buffers;
> +			}
> +
> +			/* Make buffer alignment 2 beyond a 16 byte boundary
> +			 * this will result in a 16 byte aligned IP header after
> +			 * the 14 byte MAC header is removed
> +			 */
> +			skb_reserve(skb, NET_IP_ALIGN);
> +
> +			buffer_info->skb = skb;
> +			buffer_info->dma = pci_map_single(pdev, skb->data,
> +			                                  bufsz,
> +			                                  PCI_DMA_FROMDEVICE);
> +		}
> +		/* Refresh the desc even if buffer_addrs didn't change because
> +		 * each write-back erases this info. */
> +		if (adapter->rx_ps_hdr_size) {
> +			rx_desc->read.pkt_addr =
> +			     cpu_to_le64(buffer_info->page_dma);
> +			rx_desc->read.hdr_addr = cpu_to_le64(buffer_info->dma);
> +		} else {
> +			rx_desc->read.pkt_addr =
> +			     cpu_to_le64(buffer_info->dma);
> +			rx_desc->read.hdr_addr = 0;
> +		}
> +
> +		i++;
> +		if (i == rx_ring->count)
> +			i = 0;
> +		buffer_info = &rx_ring->buffer_info[i];
> +	}
> +
> +no_buffers:
> +	if (rx_ring->next_to_use != i) {
> +		rx_ring->next_to_use = i;
> +		if (i == 0)
> +			i = (rx_ring->count - 1);
> +		else
> +			i--;
> +
> +		/* Force memory writes to complete before letting h/w
> +		 * know there are new descriptors to fetch.  (Only
> +		 * applicable for weak-ordered memory model archs,
> +		 * such as IA-64). */
> +		wmb();
> +		writel(i, adapter->hw.hw_addr + rx_ring->tail);

Does wmb() work reliably for masters other than CPUs?  I lose track...

> +	}
> +}
> +
>
> ...
>
> +/**
> + * igbvf_setup_tx_resources - allocate Tx resources (Descriptors)
> + * @adapter: board private structure
> + *
> + * Return 0 on success, negative on failure
> + **/
> +int igbvf_setup_tx_resources(struct igbvf_adapter *adapter)
> +{
> +	struct igbvf_ring *tx_ring = adapter->tx_ring;
> +	int err = -ENOMEM, size;
> +
> +	size = sizeof(struct igbvf_buffer) * tx_ring->count;
> +	tx_ring->buffer_info = vmalloc(size);

How large is this likely to be?

> +	if (!tx_ring->buffer_info)
> +		goto err;
> +	memset(tx_ring->buffer_info, 0, size);
> +
> +	/* round up to nearest 4K */
> +	tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc);
> +	tx_ring->size = ALIGN(tx_ring->size, 4096);
> +
> +	err = igbvf_alloc_ring_dma(adapter, tx_ring);
> +	if (err)
> +		goto err;
> +
> +	tx_ring->next_to_use = 0;
> +	tx_ring->next_to_clean = 0;
> +	spin_lock_init(&adapter->tx_queue_lock);
> +
> +	return 0;
> +err:
> +	vfree(tx_ring->buffer_info);
> +	dev_err(&adapter->pdev->dev,
> +	        "Unable to allocate memory for the transmit descriptor ring\n");
> +	return err;
> +}
> +
>
> ...
>
> +void igbvf_set_interrupt_capability(struct igbvf_adapter *adapter)
> +{
> +	int err = -ENOMEM;
> +	int i;
> +
> +	/* we allocate 3 vectors, 1 for tx, 1 for rx, one for pf messages */
> +	adapter->msix_entries = kcalloc(3, sizeof(struct msix_entry),
> +	                                GFP_KERNEL);
> +	if (adapter->msix_entries) {
> +		for (i = 0; i < 3; i++)
> +			adapter->msix_entries[i].entry = i;
> +
> +		err = pci_enable_msix(adapter->pdev,
> +		                      adapter->msix_entries, 3);
> +	}
> +
> +	if (err) {
> +		/* MSI-X failed */
> +		dev_err(&adapter->pdev->dev,
> +		        "Failed to initialize MSI-X interrupts.\n");
> +		igbvf_reset_interrupt_capability(adapter);

This can run pci_disable_msix() even if pci_enable_msix() falied.  Seems odd.

> +	}
> +}
> +
>
> ...
>
> +static int __devinit igbvf_alloc_queues(struct igbvf_adapter *adapter)
> +{
> +	struct net_device *netdev = adapter->netdev;
> +
> +	adapter->tx_ring = kzalloc(sizeof(struct igbvf_ring), GFP_KERNEL);
> +	if (!adapter->tx_ring)
> +		goto tx_err;
> +	adapter->tx_ring->adapter = adapter;
> +
> +	adapter->rx_ring = kzalloc(sizeof(struct igbvf_ring), GFP_KERNEL);
> +	if (!adapter->rx_ring)
> +		goto err;
> +	adapter->rx_ring->adapter = adapter;
> +
> +	netif_napi_add(netdev, &adapter->rx_ring->napi, igbvf_poll, 64);
> +
> +	return 0;
> +err:
> +	dev_err(&adapter->pdev->dev, "Unable to allocate memory for queues\n");
> +	kfree(adapter->rx_ring);
> +tx_err:
> +	kfree(adapter->tx_ring);
> +	return -ENOMEM;
> +}

The error handling here is all mucked up.

>
> ...
>
> +/**
> + * igbvf_set_multi - Multicast and Promiscuous mode set
> + * @netdev: network interface device structure
> + *
> + * The set_multi entry point is called whenever the multicast address
> + * list or the network interface flags are updated.  This routine is
> + * responsible for configuring the hardware for proper multicast,
> + * promiscuous mode, and all-multi behavior.
> + **/

It is conventional to terminate a kerneldoc comemnt with

    */

> +static void igbvf_set_multi(struct net_device *netdev)
> +{
> +	struct igbvf_adapter *adapter = netdev_priv(netdev);
> +	struct e1000_hw *hw = &adapter->hw;
> +	struct dev_mc_list *mc_ptr;
> +	u8  *mta_list;
> +	int i;
> +
> +	if (netdev->mc_count) {
> +		mta_list = kmalloc(netdev->mc_count * 6, GFP_ATOMIC);
> +		if (!mta_list)
> +			return;

Shouldn't this be reported to someone?

> +		/* prepare a packed array of only addresses. */
> +		mc_ptr = netdev->mc_list;
> +
> +		for (i = 0; i < netdev->mc_count; i++) {
> +			if (!mc_ptr)
> +				break;
> +			memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr,
> +			       ETH_ALEN);
> +			mc_ptr = mc_ptr->next;
> +		}
> +
> +		hw->mac.ops.update_mc_addr_list(hw, mta_list, i, 0, 0);
> +		kfree(mta_list);
> +	} else {
> +		/*
> +		 * if we're called from probe, we might not have
> +		 * anything to do here, so clear out the list
> +		 */
> +		hw->mac.ops.update_mc_addr_list(hw, NULL, 0, 0, 0);
> +	}
> +}
> +
>
> ...
>
> +void igbvf_down(struct igbvf_adapter *adapter)
> +{
> +	struct net_device *netdev = adapter->netdev;
> +
> +	/*
> +	 * signal that we're down so the interrupt handler does not
> +	 * reschedule our watchdog timer
> +	 */
> +	set_bit(__IGBVF_DOWN, &adapter->state);
> +
> +	netif_stop_queue(netdev);
> +
> +	/* FIX ME!!!! */
> +	/* need to disable local queue transmit and receive */
> +
> +	msleep(10);

Random mysterious msleep() deserves a code commeent.

> +	napi_disable(&adapter->rx_ring->napi);
> +
> +	igbvf_irq_disable(adapter);
> +
> +	del_timer_sync(&adapter->watchdog_timer);
> +
> +	netdev->tx_queue_len = adapter->tx_queue_len;
> +	netif_carrier_off(netdev);
> +	adapter->link_speed = 0;
> +	adapter->link_duplex = 0;
> +
> +	igbvf_reset(adapter);
> +	igbvf_clean_tx_ring(adapter);
> +	igbvf_clean_rx_ring(adapter);
> +}
> +
> +void igbvf_reinit_locked(struct igbvf_adapter *adapter)
> +{
> +	might_sleep();
> +	while (test_and_set_bit(__IGBVF_RESETTING, &adapter->state))
> +		msleep(1);

timeout?

> +	igbvf_down(adapter);
> +	igbvf_up(adapter);
> +	clear_bit(__IGBVF_RESETTING, &adapter->state);
> +}
> +
>
> ...
>
> +static int __devinit igbvf_probe(struct pci_dev *pdev,
> +                                 const struct pci_device_id *ent)
> +{
> +	struct net_device *netdev;
> +	struct igbvf_adapter *adapter;
> +	struct e1000_hw *hw;
> +	const struct igbvf_info *ei = igbvf_info_tbl[ent->driver_data];
> +
> +	static int cards_found;
> +	int err, pci_using_dac;
> +
> +	err = pci_enable_device_mem(pdev);
> +	if (err)
> +		return err;
> +
> +	pci_using_dac = 0;
> +	err = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
> +	if (!err) {
> +		err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
> +		if (!err)
> +			pci_using_dac = 1;
> +	} else {
> +		err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
> +		if (err) {
> +			err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
> +			if (err) {
> +				dev_err(&pdev->dev, "No usable DMA "
> +				        "configuration, aborting\n");
> +				goto err_dma;
> +			}
> +		}
> +	}
> +
> +	err = pci_request_regions(pdev, igbvf_driver_name);
> +	if (err)
> +		goto err_pci_reg;
> +
> +	pci_set_master(pdev);
> +
> +	err = -ENOMEM;
> +	netdev = alloc_etherdev(sizeof(struct igbvf_adapter));
> +	if (!netdev)
> +		goto err_alloc_etherdev;
> +
> +	SET_NETDEV_DEV(netdev, &pdev->dev);
> +
> +	pci_set_drvdata(pdev, netdev);
> +	adapter = netdev_priv(netdev);
> +	hw = &adapter->hw;
> +	adapter->netdev = netdev;
> +	adapter->pdev = pdev;
> +	adapter->ei = ei;
> +	adapter->pba = ei->pba;
> +	adapter->flags = ei->flags;
> +	adapter->hw.back = adapter;
> +	adapter->hw.mac.type = ei->mac;
> +	adapter->msg_enable = (1 << NETIF_MSG_DRV | NETIF_MSG_PROBE) - 1;
> +
> +	/* PCI config space info */
> +
> +	hw->vendor_id = pdev->vendor;
> +	hw->device_id = pdev->device;
> +	hw->subsystem_vendor_id = pdev->subsystem_vendor;
> +	hw->subsystem_device_id = pdev->subsystem_device;
> +
> +	pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id);
> +
> +	err = -EIO;
> +	adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, 0),
> +	                              pci_resource_len(pdev, 0));
> +
> +	if (!adapter->hw.hw_addr)
> +		goto err_ioremap;
> +
> +	if (ei->get_variants) {
> +		err = ei->get_variants(adapter);
> +		if (err)
> +			goto err_hw_init;
> +	}
> +
> +	/* setup adapter struct */
> +	err = igbvf_sw_init(adapter);
> +	if (err)
> +		goto err_sw_init;
> +
> +	/* construct the net_device struct */
> +	netdev->netdev_ops = &igbvf_netdev_ops;
> +
> +	igbvf_set_ethtool_ops(netdev);
> +	netdev->watchdog_timeo = 5 * HZ;
> +	strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
> +
> +	adapter->bd_number = cards_found++;
> +
> +	netdev->features = NETIF_F_SG |
> +	                   NETIF_F_IP_CSUM |
> +	                   NETIF_F_HW_VLAN_TX |
> +	                   NETIF_F_HW_VLAN_RX |
> +	                   NETIF_F_HW_VLAN_FILTER;
> +
> +	netdev->features |= NETIF_F_IPV6_CSUM;
> +	netdev->features |= NETIF_F_TSO;
> +	netdev->features |= NETIF_F_TSO6;
> +
> +	if (pci_using_dac)
> +		netdev->features |= NETIF_F_HIGHDMA;
> +
> +	netdev->vlan_features |= NETIF_F_TSO;
> +	netdev->vlan_features |= NETIF_F_TSO6;
> +	netdev->vlan_features |= NETIF_F_IP_CSUM;
> +	netdev->vlan_features |= NETIF_F_IPV6_CSUM;
> +	netdev->vlan_features |= NETIF_F_SG;
> +
> +	/*reset the controller to put the device in a known good state */
> +	err = hw->mac.ops.reset_hw(hw);
> +	if (err) {
> +		dev_info(&pdev->dev,
> +		         "PF still in reset state, assigning new address\n");
> +		random_ether_addr(hw->mac.addr);
> +	} else {
> +		err = hw->mac.ops.read_mac_addr(hw);
> +		if (err) {
> +			dev_err(&pdev->dev, "Error reading MAC address\n");
> +			goto err_hw_init;
> +		}
> +	}
> +
> +	memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len);
> +	memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len);
> +
> +	if (!is_valid_ether_addr(netdev->perm_addr)) {
> +		dev_err(&pdev->dev, "Invalid MAC Address: "
> +		        "%02x:%02x:%02x:%02x:%02x:%02x\n",
> +		        netdev->dev_addr[0], netdev->dev_addr[1],
> +		        netdev->dev_addr[2], netdev->dev_addr[3],
> +		        netdev->dev_addr[4], netdev->dev_addr[5]);
> +		err = -EIO;
> +		goto err_hw_init;
> +	}
> +
> +	init_timer(&adapter->watchdog_timer);
> +	adapter->watchdog_timer.function = &igbvf_watchdog;
> +	adapter->watchdog_timer.data = (unsigned long) adapter;

Could use setup_timer() here.

> +	INIT_WORK(&adapter->reset_task, igbvf_reset_task);
> +	INIT_WORK(&adapter->watchdog_task, igbvf_watchdog_task);
> +
> +	/* ring size defaults */
> +	adapter->rx_ring->count = 1024;
> +	adapter->tx_ring->count = 1024;
> +
> +	/* reset the hardware with the new settings */
> +	igbvf_reset(adapter);
> +
> +	/* tell the stack to leave us alone until igbvf_open() is called */
> +	netif_carrier_off(netdev);
> +	netif_stop_queue(netdev);
> +
> +	strcpy(netdev->name, "eth%d");
> +	err = register_netdev(netdev);
> +	if (err)
> +		goto err_hw_init;
> +
> +	igbvf_print_device_info(adapter);
> +
> +	igbvf_initialize_last_counter_stats(adapter);
> +
> +	return 0;
> +
> +err_hw_init:
> +	kfree(adapter->tx_ring);
> +	kfree(adapter->rx_ring);
> +	igbvf_reset_interrupt_capability(adapter);
> +err_sw_init:
> +	iounmap(adapter->hw.hw_addr);
> +err_ioremap:
> +	free_netdev(netdev);
> +err_alloc_etherdev:
> +	pci_release_regions(pdev);
> +err_pci_reg:
> +err_dma:
> +	pci_disable_device(pdev);
> +	return err;
> +}
> +
> +/**
> + * igbvf_remove - Device Removal Routine
> + * @pdev: PCI device information struct
> + *
> + * igbvf_remove is called by the PCI subsystem to alert the driver
> + * that it should release a PCI device.  The could be caused by a
> + * Hot-Plug event, or because the driver is going to be removed from
> + * memory.
> + **/
> +static void __devexit igbvf_remove(struct pci_dev *pdev)
> +{
> +	struct net_device *netdev = pci_get_drvdata(pdev);
> +	struct igbvf_adapter *adapter = netdev_priv(netdev);
> +	struct e1000_hw *hw = &adapter->hw;
> +
> +	/*
> +	 * flush_scheduled work may reschedule our watchdog task, so
> +	 * explicitly disable watchdog tasks from being rescheduled
> +	 */
> +	set_bit(__IGBVF_DOWN, &adapter->state);
> +	del_timer_sync(&adapter->watchdog_timer);
> +
> +	flush_scheduled_work();

flush_work() is a bit more modern - it will flush a specific work item
rather than waiting for the kernel-wide queue to drain.

> +	unregister_netdev(netdev);
> +
> +	igbvf_reset_interrupt_capability(adapter);
> +	kfree(adapter->tx_ring);
> +	kfree(adapter->rx_ring);
> +
> +	iounmap(hw->hw_addr);
> +	if (hw->flash_address)
> +		iounmap(hw->flash_address);
> +	pci_release_regions(pdev);
> +
> +	free_netdev(netdev);
> +
> +	pci_disable_device(pdev);
> +}
> +
>
> ...
>

What a lot of code.