162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IBM Power Virtual Ethernet Device Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2003, 2010
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors: Dave Larson <larson1@us.ibm.com>
862306a36Sopenharmony_ci *	    Santiago Leon <santil@linux.vnet.ibm.com>
962306a36Sopenharmony_ci *	    Brian King <brking@linux.vnet.ibm.com>
1062306a36Sopenharmony_ci *	    Robert Jennings <rcj@linux.vnet.ibm.com>
1162306a36Sopenharmony_ci *	    Anton Blanchard <anton@au.ibm.com>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/netdevice.h>
2062306a36Sopenharmony_ci#include <linux/etherdevice.h>
2162306a36Sopenharmony_ci#include <linux/skbuff.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/mm.h>
2562306a36Sopenharmony_ci#include <linux/pm.h>
2662306a36Sopenharmony_ci#include <linux/ethtool.h>
2762306a36Sopenharmony_ci#include <linux/in.h>
2862306a36Sopenharmony_ci#include <linux/ip.h>
2962306a36Sopenharmony_ci#include <linux/ipv6.h>
3062306a36Sopenharmony_ci#include <linux/slab.h>
3162306a36Sopenharmony_ci#include <asm/hvcall.h>
3262306a36Sopenharmony_ci#include <linux/atomic.h>
3362306a36Sopenharmony_ci#include <asm/vio.h>
3462306a36Sopenharmony_ci#include <asm/iommu.h>
3562306a36Sopenharmony_ci#include <asm/firmware.h>
3662306a36Sopenharmony_ci#include <net/tcp.h>
3762306a36Sopenharmony_ci#include <net/ip6_checksum.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include "ibmveth.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic irqreturn_t ibmveth_interrupt(int irq, void *dev_instance);
4262306a36Sopenharmony_cistatic void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter);
4362306a36Sopenharmony_cistatic unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct kobj_type ktype_veth_pool;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic const char ibmveth_driver_name[] = "ibmveth";
4962306a36Sopenharmony_cistatic const char ibmveth_driver_string[] = "IBM Power Virtual Ethernet Driver";
5062306a36Sopenharmony_ci#define ibmveth_driver_version "1.06"
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciMODULE_AUTHOR("Santiago Leon <santil@linux.vnet.ibm.com>");
5362306a36Sopenharmony_ciMODULE_DESCRIPTION("IBM Power Virtual Ethernet Driver");
5462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5562306a36Sopenharmony_ciMODULE_VERSION(ibmveth_driver_version);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic unsigned int tx_copybreak __read_mostly = 128;
5862306a36Sopenharmony_cimodule_param(tx_copybreak, uint, 0644);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(tx_copybreak,
6062306a36Sopenharmony_ci	"Maximum size of packet that is copied to a new buffer on transmit");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic unsigned int rx_copybreak __read_mostly = 128;
6362306a36Sopenharmony_cimodule_param(rx_copybreak, uint, 0644);
6462306a36Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak,
6562306a36Sopenharmony_ci	"Maximum size of packet that is copied to a new buffer on receive");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic unsigned int rx_flush __read_mostly = 0;
6862306a36Sopenharmony_cimodule_param(rx_flush, uint, 0644);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(rx_flush, "Flush receive buffers before use");
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic bool old_large_send __read_mostly;
7262306a36Sopenharmony_cimodule_param(old_large_send, bool, 0444);
7362306a36Sopenharmony_ciMODULE_PARM_DESC(old_large_send,
7462306a36Sopenharmony_ci	"Use old large send method on firmware that supports the new method");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct ibmveth_stat {
7762306a36Sopenharmony_ci	char name[ETH_GSTRING_LEN];
7862306a36Sopenharmony_ci	int offset;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define IBMVETH_STAT_OFF(stat) offsetof(struct ibmveth_adapter, stat)
8262306a36Sopenharmony_ci#define IBMVETH_GET_STAT(a, off) *((u64 *)(((unsigned long)(a)) + off))
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic struct ibmveth_stat ibmveth_stats[] = {
8562306a36Sopenharmony_ci	{ "replenish_task_cycles", IBMVETH_STAT_OFF(replenish_task_cycles) },
8662306a36Sopenharmony_ci	{ "replenish_no_mem", IBMVETH_STAT_OFF(replenish_no_mem) },
8762306a36Sopenharmony_ci	{ "replenish_add_buff_failure",
8862306a36Sopenharmony_ci			IBMVETH_STAT_OFF(replenish_add_buff_failure) },
8962306a36Sopenharmony_ci	{ "replenish_add_buff_success",
9062306a36Sopenharmony_ci			IBMVETH_STAT_OFF(replenish_add_buff_success) },
9162306a36Sopenharmony_ci	{ "rx_invalid_buffer", IBMVETH_STAT_OFF(rx_invalid_buffer) },
9262306a36Sopenharmony_ci	{ "rx_no_buffer", IBMVETH_STAT_OFF(rx_no_buffer) },
9362306a36Sopenharmony_ci	{ "tx_map_failed", IBMVETH_STAT_OFF(tx_map_failed) },
9462306a36Sopenharmony_ci	{ "tx_send_failed", IBMVETH_STAT_OFF(tx_send_failed) },
9562306a36Sopenharmony_ci	{ "fw_enabled_ipv4_csum", IBMVETH_STAT_OFF(fw_ipv4_csum_support) },
9662306a36Sopenharmony_ci	{ "fw_enabled_ipv6_csum", IBMVETH_STAT_OFF(fw_ipv6_csum_support) },
9762306a36Sopenharmony_ci	{ "tx_large_packets", IBMVETH_STAT_OFF(tx_large_packets) },
9862306a36Sopenharmony_ci	{ "rx_large_packets", IBMVETH_STAT_OFF(rx_large_packets) },
9962306a36Sopenharmony_ci	{ "fw_enabled_large_send", IBMVETH_STAT_OFF(fw_large_send_support) }
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* simple methods of getting data from the current rxq entry */
10362306a36Sopenharmony_cistatic inline u32 ibmveth_rxq_flags(struct ibmveth_adapter *adapter)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	return be32_to_cpu(adapter->rx_queue.queue_addr[adapter->rx_queue.index].flags_off);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic inline int ibmveth_rxq_toggle(struct ibmveth_adapter *adapter)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	return (ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_TOGGLE) >>
11162306a36Sopenharmony_ci			IBMVETH_RXQ_TOGGLE_SHIFT;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic inline int ibmveth_rxq_pending_buffer(struct ibmveth_adapter *adapter)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	return ibmveth_rxq_toggle(adapter) == adapter->rx_queue.toggle;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic inline int ibmveth_rxq_buffer_valid(struct ibmveth_adapter *adapter)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_VALID;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_OFF_MASK;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline int ibmveth_rxq_large_packet(struct ibmveth_adapter *adapter)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_LRG_PKT;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return be32_to_cpu(adapter->rx_queue.queue_addr[adapter->rx_queue.index].length);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic inline int ibmveth_rxq_csum_good(struct ibmveth_adapter *adapter)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_CSUM_GOOD;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic unsigned int ibmveth_real_max_tx_queues(void)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	unsigned int n_cpu = num_online_cpus();
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return min(n_cpu, IBMVETH_MAX_QUEUES);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* setup the initial settings for a buffer pool */
15262306a36Sopenharmony_cistatic void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool,
15362306a36Sopenharmony_ci				     u32 pool_index, u32 pool_size,
15462306a36Sopenharmony_ci				     u32 buff_size, u32 pool_active)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	pool->size = pool_size;
15762306a36Sopenharmony_ci	pool->index = pool_index;
15862306a36Sopenharmony_ci	pool->buff_size = buff_size;
15962306a36Sopenharmony_ci	pool->threshold = pool_size * 7 / 8;
16062306a36Sopenharmony_ci	pool->active = pool_active;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* allocate and setup an buffer pool - called during open */
16462306a36Sopenharmony_cistatic int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	int i;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	pool->free_map = kmalloc_array(pool->size, sizeof(u16), GFP_KERNEL);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!pool->free_map)
17162306a36Sopenharmony_ci		return -1;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	pool->dma_addr = kcalloc(pool->size, sizeof(dma_addr_t), GFP_KERNEL);
17462306a36Sopenharmony_ci	if (!pool->dma_addr) {
17562306a36Sopenharmony_ci		kfree(pool->free_map);
17662306a36Sopenharmony_ci		pool->free_map = NULL;
17762306a36Sopenharmony_ci		return -1;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	pool->skbuff = kcalloc(pool->size, sizeof(void *), GFP_KERNEL);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!pool->skbuff) {
18362306a36Sopenharmony_ci		kfree(pool->dma_addr);
18462306a36Sopenharmony_ci		pool->dma_addr = NULL;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		kfree(pool->free_map);
18762306a36Sopenharmony_ci		pool->free_map = NULL;
18862306a36Sopenharmony_ci		return -1;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	for (i = 0; i < pool->size; ++i)
19262306a36Sopenharmony_ci		pool->free_map[i] = i;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	atomic_set(&pool->available, 0);
19562306a36Sopenharmony_ci	pool->producer_index = 0;
19662306a36Sopenharmony_ci	pool->consumer_index = 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic inline void ibmveth_flush_buffer(void *addr, unsigned long length)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	unsigned long offset;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	for (offset = 0; offset < length; offset += SMP_CACHE_BYTES)
20662306a36Sopenharmony_ci		asm("dcbf %0,%1,1" :: "b" (addr), "r" (offset));
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* replenish the buffers for a pool.  note that we don't need to
21062306a36Sopenharmony_ci * skb_reserve these since they are used for incoming...
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_cistatic void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter,
21362306a36Sopenharmony_ci					  struct ibmveth_buff_pool *pool)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	u32 i;
21662306a36Sopenharmony_ci	u32 count = pool->size - atomic_read(&pool->available);
21762306a36Sopenharmony_ci	u32 buffers_added = 0;
21862306a36Sopenharmony_ci	struct sk_buff *skb;
21962306a36Sopenharmony_ci	unsigned int free_index, index;
22062306a36Sopenharmony_ci	u64 correlator;
22162306a36Sopenharmony_ci	unsigned long lpar_rc;
22262306a36Sopenharmony_ci	dma_addr_t dma_addr;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	mb();
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
22762306a36Sopenharmony_ci		union ibmveth_buf_desc desc;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		skb = netdev_alloc_skb(adapter->netdev, pool->buff_size);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		if (!skb) {
23262306a36Sopenharmony_ci			netdev_dbg(adapter->netdev,
23362306a36Sopenharmony_ci				   "replenish: unable to allocate skb\n");
23462306a36Sopenharmony_ci			adapter->replenish_no_mem++;
23562306a36Sopenharmony_ci			break;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		free_index = pool->consumer_index;
23962306a36Sopenharmony_ci		pool->consumer_index++;
24062306a36Sopenharmony_ci		if (pool->consumer_index >= pool->size)
24162306a36Sopenharmony_ci			pool->consumer_index = 0;
24262306a36Sopenharmony_ci		index = pool->free_map[free_index];
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		BUG_ON(index == IBM_VETH_INVALID_MAP);
24562306a36Sopenharmony_ci		BUG_ON(pool->skbuff[index] != NULL);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		dma_addr = dma_map_single(&adapter->vdev->dev, skb->data,
24862306a36Sopenharmony_ci				pool->buff_size, DMA_FROM_DEVICE);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		if (dma_mapping_error(&adapter->vdev->dev, dma_addr))
25162306a36Sopenharmony_ci			goto failure;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		pool->free_map[free_index] = IBM_VETH_INVALID_MAP;
25462306a36Sopenharmony_ci		pool->dma_addr[index] = dma_addr;
25562306a36Sopenharmony_ci		pool->skbuff[index] = skb;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		correlator = ((u64)pool->index << 32) | index;
25862306a36Sopenharmony_ci		*(u64 *)skb->data = correlator;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		desc.fields.flags_len = IBMVETH_BUF_VALID | pool->buff_size;
26162306a36Sopenharmony_ci		desc.fields.address = dma_addr;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (rx_flush) {
26462306a36Sopenharmony_ci			unsigned int len = min(pool->buff_size,
26562306a36Sopenharmony_ci						adapter->netdev->mtu +
26662306a36Sopenharmony_ci						IBMVETH_BUFF_OH);
26762306a36Sopenharmony_ci			ibmveth_flush_buffer(skb->data, len);
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci		lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address,
27062306a36Sopenharmony_ci						   desc.desc);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		if (lpar_rc != H_SUCCESS) {
27362306a36Sopenharmony_ci			goto failure;
27462306a36Sopenharmony_ci		} else {
27562306a36Sopenharmony_ci			buffers_added++;
27662306a36Sopenharmony_ci			adapter->replenish_add_buff_success++;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	mb();
28162306a36Sopenharmony_ci	atomic_add(buffers_added, &(pool->available));
28262306a36Sopenharmony_ci	return;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cifailure:
28562306a36Sopenharmony_ci	pool->free_map[free_index] = index;
28662306a36Sopenharmony_ci	pool->skbuff[index] = NULL;
28762306a36Sopenharmony_ci	if (pool->consumer_index == 0)
28862306a36Sopenharmony_ci		pool->consumer_index = pool->size - 1;
28962306a36Sopenharmony_ci	else
29062306a36Sopenharmony_ci		pool->consumer_index--;
29162306a36Sopenharmony_ci	if (!dma_mapping_error(&adapter->vdev->dev, dma_addr))
29262306a36Sopenharmony_ci		dma_unmap_single(&adapter->vdev->dev,
29362306a36Sopenharmony_ci		                 pool->dma_addr[index], pool->buff_size,
29462306a36Sopenharmony_ci		                 DMA_FROM_DEVICE);
29562306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
29662306a36Sopenharmony_ci	adapter->replenish_add_buff_failure++;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	mb();
29962306a36Sopenharmony_ci	atomic_add(buffers_added, &(pool->available));
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/*
30362306a36Sopenharmony_ci * The final 8 bytes of the buffer list is a counter of frames dropped
30462306a36Sopenharmony_ci * because there was not a buffer in the buffer list capable of holding
30562306a36Sopenharmony_ci * the frame.
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_cistatic void ibmveth_update_rx_no_buffer(struct ibmveth_adapter *adapter)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	__be64 *p = adapter->buffer_list_addr + 4096 - 8;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	adapter->rx_no_buffer = be64_to_cpup(p);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/* replenish routine */
31562306a36Sopenharmony_cistatic void ibmveth_replenish_task(struct ibmveth_adapter *adapter)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	int i;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	adapter->replenish_task_cycles++;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	for (i = (IBMVETH_NUM_BUFF_POOLS - 1); i >= 0; i--) {
32262306a36Sopenharmony_ci		struct ibmveth_buff_pool *pool = &adapter->rx_buff_pool[i];
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		if (pool->active &&
32562306a36Sopenharmony_ci		    (atomic_read(&pool->available) < pool->threshold))
32662306a36Sopenharmony_ci			ibmveth_replenish_buffer_pool(adapter, pool);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ibmveth_update_rx_no_buffer(adapter);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/* empty and free ana buffer pool - also used to do cleanup in error paths */
33362306a36Sopenharmony_cistatic void ibmveth_free_buffer_pool(struct ibmveth_adapter *adapter,
33462306a36Sopenharmony_ci				     struct ibmveth_buff_pool *pool)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int i;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	kfree(pool->free_map);
33962306a36Sopenharmony_ci	pool->free_map = NULL;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (pool->skbuff && pool->dma_addr) {
34262306a36Sopenharmony_ci		for (i = 0; i < pool->size; ++i) {
34362306a36Sopenharmony_ci			struct sk_buff *skb = pool->skbuff[i];
34462306a36Sopenharmony_ci			if (skb) {
34562306a36Sopenharmony_ci				dma_unmap_single(&adapter->vdev->dev,
34662306a36Sopenharmony_ci						 pool->dma_addr[i],
34762306a36Sopenharmony_ci						 pool->buff_size,
34862306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
34962306a36Sopenharmony_ci				dev_kfree_skb_any(skb);
35062306a36Sopenharmony_ci				pool->skbuff[i] = NULL;
35162306a36Sopenharmony_ci			}
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (pool->dma_addr) {
35662306a36Sopenharmony_ci		kfree(pool->dma_addr);
35762306a36Sopenharmony_ci		pool->dma_addr = NULL;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (pool->skbuff) {
36162306a36Sopenharmony_ci		kfree(pool->skbuff);
36262306a36Sopenharmony_ci		pool->skbuff = NULL;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/* remove a buffer from a pool */
36762306a36Sopenharmony_cistatic void ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter,
36862306a36Sopenharmony_ci					    u64 correlator)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	unsigned int pool  = correlator >> 32;
37162306a36Sopenharmony_ci	unsigned int index = correlator & 0xffffffffUL;
37262306a36Sopenharmony_ci	unsigned int free_index;
37362306a36Sopenharmony_ci	struct sk_buff *skb;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	BUG_ON(pool >= IBMVETH_NUM_BUFF_POOLS);
37662306a36Sopenharmony_ci	BUG_ON(index >= adapter->rx_buff_pool[pool].size);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	skb = adapter->rx_buff_pool[pool].skbuff[index];
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	BUG_ON(skb == NULL);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	adapter->rx_buff_pool[pool].skbuff[index] = NULL;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	dma_unmap_single(&adapter->vdev->dev,
38562306a36Sopenharmony_ci			 adapter->rx_buff_pool[pool].dma_addr[index],
38662306a36Sopenharmony_ci			 adapter->rx_buff_pool[pool].buff_size,
38762306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	free_index = adapter->rx_buff_pool[pool].producer_index;
39062306a36Sopenharmony_ci	adapter->rx_buff_pool[pool].producer_index++;
39162306a36Sopenharmony_ci	if (adapter->rx_buff_pool[pool].producer_index >=
39262306a36Sopenharmony_ci	    adapter->rx_buff_pool[pool].size)
39362306a36Sopenharmony_ci		adapter->rx_buff_pool[pool].producer_index = 0;
39462306a36Sopenharmony_ci	adapter->rx_buff_pool[pool].free_map[free_index] = index;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	mb();
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	atomic_dec(&(adapter->rx_buff_pool[pool].available));
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/* get the current buffer on the rx queue */
40262306a36Sopenharmony_cistatic inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *adapter)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	u64 correlator = adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator;
40562306a36Sopenharmony_ci	unsigned int pool = correlator >> 32;
40662306a36Sopenharmony_ci	unsigned int index = correlator & 0xffffffffUL;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	BUG_ON(pool >= IBMVETH_NUM_BUFF_POOLS);
40962306a36Sopenharmony_ci	BUG_ON(index >= adapter->rx_buff_pool[pool].size);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return adapter->rx_buff_pool[pool].skbuff[index];
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/* recycle the current buffer on the rx queue */
41562306a36Sopenharmony_cistatic int ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	u32 q_index = adapter->rx_queue.index;
41862306a36Sopenharmony_ci	u64 correlator = adapter->rx_queue.queue_addr[q_index].correlator;
41962306a36Sopenharmony_ci	unsigned int pool = correlator >> 32;
42062306a36Sopenharmony_ci	unsigned int index = correlator & 0xffffffffUL;
42162306a36Sopenharmony_ci	union ibmveth_buf_desc desc;
42262306a36Sopenharmony_ci	unsigned long lpar_rc;
42362306a36Sopenharmony_ci	int ret = 1;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	BUG_ON(pool >= IBMVETH_NUM_BUFF_POOLS);
42662306a36Sopenharmony_ci	BUG_ON(index >= adapter->rx_buff_pool[pool].size);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!adapter->rx_buff_pool[pool].active) {
42962306a36Sopenharmony_ci		ibmveth_rxq_harvest_buffer(adapter);
43062306a36Sopenharmony_ci		ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[pool]);
43162306a36Sopenharmony_ci		goto out;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	desc.fields.flags_len = IBMVETH_BUF_VALID |
43562306a36Sopenharmony_ci		adapter->rx_buff_pool[pool].buff_size;
43662306a36Sopenharmony_ci	desc.fields.address = adapter->rx_buff_pool[pool].dma_addr[index];
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (lpar_rc != H_SUCCESS) {
44162306a36Sopenharmony_ci		netdev_dbg(adapter->netdev, "h_add_logical_lan_buffer failed "
44262306a36Sopenharmony_ci			   "during recycle rc=%ld", lpar_rc);
44362306a36Sopenharmony_ci		ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
44462306a36Sopenharmony_ci		ret = 0;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (++adapter->rx_queue.index == adapter->rx_queue.num_slots) {
44862306a36Sopenharmony_ci		adapter->rx_queue.index = 0;
44962306a36Sopenharmony_ci		adapter->rx_queue.toggle = !adapter->rx_queue.toggle;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ciout:
45362306a36Sopenharmony_ci	return ret;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (++adapter->rx_queue.index == adapter->rx_queue.num_slots) {
46162306a36Sopenharmony_ci		adapter->rx_queue.index = 0;
46262306a36Sopenharmony_ci		adapter->rx_queue.toggle = !adapter->rx_queue.toggle;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic void ibmveth_free_tx_ltb(struct ibmveth_adapter *adapter, int idx)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	dma_unmap_single(&adapter->vdev->dev, adapter->tx_ltb_dma[idx],
46962306a36Sopenharmony_ci			 adapter->tx_ltb_size, DMA_TO_DEVICE);
47062306a36Sopenharmony_ci	kfree(adapter->tx_ltb_ptr[idx]);
47162306a36Sopenharmony_ci	adapter->tx_ltb_ptr[idx] = NULL;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int ibmveth_allocate_tx_ltb(struct ibmveth_adapter *adapter, int idx)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	adapter->tx_ltb_ptr[idx] = kzalloc(adapter->tx_ltb_size,
47762306a36Sopenharmony_ci					   GFP_KERNEL);
47862306a36Sopenharmony_ci	if (!adapter->tx_ltb_ptr[idx]) {
47962306a36Sopenharmony_ci		netdev_err(adapter->netdev,
48062306a36Sopenharmony_ci			   "unable to allocate tx long term buffer\n");
48162306a36Sopenharmony_ci		return -ENOMEM;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci	adapter->tx_ltb_dma[idx] = dma_map_single(&adapter->vdev->dev,
48462306a36Sopenharmony_ci						  adapter->tx_ltb_ptr[idx],
48562306a36Sopenharmony_ci						  adapter->tx_ltb_size,
48662306a36Sopenharmony_ci						  DMA_TO_DEVICE);
48762306a36Sopenharmony_ci	if (dma_mapping_error(&adapter->vdev->dev, adapter->tx_ltb_dma[idx])) {
48862306a36Sopenharmony_ci		netdev_err(adapter->netdev,
48962306a36Sopenharmony_ci			   "unable to DMA map tx long term buffer\n");
49062306a36Sopenharmony_ci		kfree(adapter->tx_ltb_ptr[idx]);
49162306a36Sopenharmony_ci		adapter->tx_ltb_ptr[idx] = NULL;
49262306a36Sopenharmony_ci		return -ENOMEM;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
49962306a36Sopenharmony_ci        union ibmveth_buf_desc rxq_desc, u64 mac_address)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	int rc, try_again = 1;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/*
50462306a36Sopenharmony_ci	 * After a kexec the adapter will still be open, so our attempt to
50562306a36Sopenharmony_ci	 * open it will fail. So if we get a failure we free the adapter and
50662306a36Sopenharmony_ci	 * try again, but only once.
50762306a36Sopenharmony_ci	 */
50862306a36Sopenharmony_ciretry:
50962306a36Sopenharmony_ci	rc = h_register_logical_lan(adapter->vdev->unit_address,
51062306a36Sopenharmony_ci				    adapter->buffer_list_dma, rxq_desc.desc,
51162306a36Sopenharmony_ci				    adapter->filter_list_dma, mac_address);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (rc != H_SUCCESS && try_again) {
51462306a36Sopenharmony_ci		do {
51562306a36Sopenharmony_ci			rc = h_free_logical_lan(adapter->vdev->unit_address);
51662306a36Sopenharmony_ci		} while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY));
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		try_again = 0;
51962306a36Sopenharmony_ci		goto retry;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return rc;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic int ibmveth_open(struct net_device *netdev)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
52862306a36Sopenharmony_ci	u64 mac_address;
52962306a36Sopenharmony_ci	int rxq_entries = 1;
53062306a36Sopenharmony_ci	unsigned long lpar_rc;
53162306a36Sopenharmony_ci	int rc;
53262306a36Sopenharmony_ci	union ibmveth_buf_desc rxq_desc;
53362306a36Sopenharmony_ci	int i;
53462306a36Sopenharmony_ci	struct device *dev;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	netdev_dbg(netdev, "open starting\n");
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	napi_enable(&adapter->napi);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	for(i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
54162306a36Sopenharmony_ci		rxq_entries += adapter->rx_buff_pool[i].size;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	rc = -ENOMEM;
54462306a36Sopenharmony_ci	adapter->buffer_list_addr = (void*) get_zeroed_page(GFP_KERNEL);
54562306a36Sopenharmony_ci	if (!adapter->buffer_list_addr) {
54662306a36Sopenharmony_ci		netdev_err(netdev, "unable to allocate list pages\n");
54762306a36Sopenharmony_ci		goto out;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	adapter->filter_list_addr = (void*) get_zeroed_page(GFP_KERNEL);
55162306a36Sopenharmony_ci	if (!adapter->filter_list_addr) {
55262306a36Sopenharmony_ci		netdev_err(netdev, "unable to allocate filter pages\n");
55362306a36Sopenharmony_ci		goto out_free_buffer_list;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	dev = &adapter->vdev->dev;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) *
55962306a36Sopenharmony_ci						rxq_entries;
56062306a36Sopenharmony_ci	adapter->rx_queue.queue_addr =
56162306a36Sopenharmony_ci		dma_alloc_coherent(dev, adapter->rx_queue.queue_len,
56262306a36Sopenharmony_ci				   &adapter->rx_queue.queue_dma, GFP_KERNEL);
56362306a36Sopenharmony_ci	if (!adapter->rx_queue.queue_addr)
56462306a36Sopenharmony_ci		goto out_free_filter_list;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	adapter->buffer_list_dma = dma_map_single(dev,
56762306a36Sopenharmony_ci			adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL);
56862306a36Sopenharmony_ci	if (dma_mapping_error(dev, adapter->buffer_list_dma)) {
56962306a36Sopenharmony_ci		netdev_err(netdev, "unable to map buffer list pages\n");
57062306a36Sopenharmony_ci		goto out_free_queue_mem;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	adapter->filter_list_dma = dma_map_single(dev,
57462306a36Sopenharmony_ci			adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL);
57562306a36Sopenharmony_ci	if (dma_mapping_error(dev, adapter->filter_list_dma)) {
57662306a36Sopenharmony_ci		netdev_err(netdev, "unable to map filter list pages\n");
57762306a36Sopenharmony_ci		goto out_unmap_buffer_list;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	for (i = 0; i < netdev->real_num_tx_queues; i++) {
58162306a36Sopenharmony_ci		if (ibmveth_allocate_tx_ltb(adapter, i))
58262306a36Sopenharmony_ci			goto out_free_tx_ltb;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	adapter->rx_queue.index = 0;
58662306a36Sopenharmony_ci	adapter->rx_queue.num_slots = rxq_entries;
58762306a36Sopenharmony_ci	adapter->rx_queue.toggle = 1;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	mac_address = ether_addr_to_u64(netdev->dev_addr);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	rxq_desc.fields.flags_len = IBMVETH_BUF_VALID |
59262306a36Sopenharmony_ci					adapter->rx_queue.queue_len;
59362306a36Sopenharmony_ci	rxq_desc.fields.address = adapter->rx_queue.queue_dma;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	netdev_dbg(netdev, "buffer list @ 0x%p\n", adapter->buffer_list_addr);
59662306a36Sopenharmony_ci	netdev_dbg(netdev, "filter list @ 0x%p\n", adapter->filter_list_addr);
59762306a36Sopenharmony_ci	netdev_dbg(netdev, "receive q   @ 0x%p\n", adapter->rx_queue.queue_addr);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	lpar_rc = ibmveth_register_logical_lan(adapter, rxq_desc, mac_address);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (lpar_rc != H_SUCCESS) {
60462306a36Sopenharmony_ci		netdev_err(netdev, "h_register_logical_lan failed with %ld\n",
60562306a36Sopenharmony_ci			   lpar_rc);
60662306a36Sopenharmony_ci		netdev_err(netdev, "buffer TCE:0x%llx filter TCE:0x%llx rxq "
60762306a36Sopenharmony_ci			   "desc:0x%llx MAC:0x%llx\n",
60862306a36Sopenharmony_ci				     adapter->buffer_list_dma,
60962306a36Sopenharmony_ci				     adapter->filter_list_dma,
61062306a36Sopenharmony_ci				     rxq_desc.desc,
61162306a36Sopenharmony_ci				     mac_address);
61262306a36Sopenharmony_ci		rc = -ENONET;
61362306a36Sopenharmony_ci		goto out_unmap_filter_list;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
61762306a36Sopenharmony_ci		if (!adapter->rx_buff_pool[i].active)
61862306a36Sopenharmony_ci			continue;
61962306a36Sopenharmony_ci		if (ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[i])) {
62062306a36Sopenharmony_ci			netdev_err(netdev, "unable to alloc pool\n");
62162306a36Sopenharmony_ci			adapter->rx_buff_pool[i].active = 0;
62262306a36Sopenharmony_ci			rc = -ENOMEM;
62362306a36Sopenharmony_ci			goto out_free_buffer_pools;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	netdev_dbg(netdev, "registering irq 0x%x\n", netdev->irq);
62862306a36Sopenharmony_ci	rc = request_irq(netdev->irq, ibmveth_interrupt, 0, netdev->name,
62962306a36Sopenharmony_ci			 netdev);
63062306a36Sopenharmony_ci	if (rc != 0) {
63162306a36Sopenharmony_ci		netdev_err(netdev, "unable to request irq 0x%x, rc %d\n",
63262306a36Sopenharmony_ci			   netdev->irq, rc);
63362306a36Sopenharmony_ci		do {
63462306a36Sopenharmony_ci			lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
63562306a36Sopenharmony_ci		} while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		goto out_free_buffer_pools;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	rc = -ENOMEM;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	netdev_dbg(netdev, "initial replenish cycle\n");
64362306a36Sopenharmony_ci	ibmveth_interrupt(netdev->irq, netdev);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	netif_tx_start_all_queues(netdev);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	netdev_dbg(netdev, "open complete\n");
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return 0;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ciout_free_buffer_pools:
65262306a36Sopenharmony_ci	while (--i >= 0) {
65362306a36Sopenharmony_ci		if (adapter->rx_buff_pool[i].active)
65462306a36Sopenharmony_ci			ibmveth_free_buffer_pool(adapter,
65562306a36Sopenharmony_ci						 &adapter->rx_buff_pool[i]);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ciout_unmap_filter_list:
65862306a36Sopenharmony_ci	dma_unmap_single(dev, adapter->filter_list_dma, 4096,
65962306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ciout_free_tx_ltb:
66262306a36Sopenharmony_ci	while (--i >= 0) {
66362306a36Sopenharmony_ci		ibmveth_free_tx_ltb(adapter, i);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ciout_unmap_buffer_list:
66762306a36Sopenharmony_ci	dma_unmap_single(dev, adapter->buffer_list_dma, 4096,
66862306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
66962306a36Sopenharmony_ciout_free_queue_mem:
67062306a36Sopenharmony_ci	dma_free_coherent(dev, adapter->rx_queue.queue_len,
67162306a36Sopenharmony_ci			  adapter->rx_queue.queue_addr,
67262306a36Sopenharmony_ci			  adapter->rx_queue.queue_dma);
67362306a36Sopenharmony_ciout_free_filter_list:
67462306a36Sopenharmony_ci	free_page((unsigned long)adapter->filter_list_addr);
67562306a36Sopenharmony_ciout_free_buffer_list:
67662306a36Sopenharmony_ci	free_page((unsigned long)adapter->buffer_list_addr);
67762306a36Sopenharmony_ciout:
67862306a36Sopenharmony_ci	napi_disable(&adapter->napi);
67962306a36Sopenharmony_ci	return rc;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int ibmveth_close(struct net_device *netdev)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
68562306a36Sopenharmony_ci	struct device *dev = &adapter->vdev->dev;
68662306a36Sopenharmony_ci	long lpar_rc;
68762306a36Sopenharmony_ci	int i;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	netdev_dbg(netdev, "close starting\n");
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	napi_disable(&adapter->napi);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	do {
69862306a36Sopenharmony_ci		lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
69962306a36Sopenharmony_ci	} while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (lpar_rc != H_SUCCESS) {
70262306a36Sopenharmony_ci		netdev_err(netdev, "h_free_logical_lan failed with %lx, "
70362306a36Sopenharmony_ci			   "continuing with close\n", lpar_rc);
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	free_irq(netdev->irq, netdev);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	ibmveth_update_rx_no_buffer(adapter);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	dma_unmap_single(dev, adapter->buffer_list_dma, 4096,
71162306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
71262306a36Sopenharmony_ci	free_page((unsigned long)adapter->buffer_list_addr);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	dma_unmap_single(dev, adapter->filter_list_dma, 4096,
71562306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
71662306a36Sopenharmony_ci	free_page((unsigned long)adapter->filter_list_addr);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	dma_free_coherent(dev, adapter->rx_queue.queue_len,
71962306a36Sopenharmony_ci			  adapter->rx_queue.queue_addr,
72062306a36Sopenharmony_ci			  adapter->rx_queue.queue_dma);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
72362306a36Sopenharmony_ci		if (adapter->rx_buff_pool[i].active)
72462306a36Sopenharmony_ci			ibmveth_free_buffer_pool(adapter,
72562306a36Sopenharmony_ci						 &adapter->rx_buff_pool[i]);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	for (i = 0; i < netdev->real_num_tx_queues; i++)
72862306a36Sopenharmony_ci		ibmveth_free_tx_ltb(adapter, i);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	netdev_dbg(netdev, "close complete\n");
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic int ibmveth_set_link_ksettings(struct net_device *dev,
73662306a36Sopenharmony_ci				      const struct ethtool_link_ksettings *cmd)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return ethtool_virtdev_set_link_ksettings(dev, cmd,
74162306a36Sopenharmony_ci						  &adapter->speed,
74262306a36Sopenharmony_ci						  &adapter->duplex);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic int ibmveth_get_link_ksettings(struct net_device *dev,
74662306a36Sopenharmony_ci				      struct ethtool_link_ksettings *cmd)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	cmd->base.speed = adapter->speed;
75162306a36Sopenharmony_ci	cmd->base.duplex = adapter->duplex;
75262306a36Sopenharmony_ci	cmd->base.port = PORT_OTHER;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void ibmveth_init_link_settings(struct net_device *dev)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	adapter->speed = SPEED_1000;
76262306a36Sopenharmony_ci	adapter->duplex = DUPLEX_FULL;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev,
76662306a36Sopenharmony_ci			       struct ethtool_drvinfo *info)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	strscpy(info->driver, ibmveth_driver_name, sizeof(info->driver));
76962306a36Sopenharmony_ci	strscpy(info->version, ibmveth_driver_version, sizeof(info->version));
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic netdev_features_t ibmveth_fix_features(struct net_device *dev,
77362306a36Sopenharmony_ci	netdev_features_t features)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	/*
77662306a36Sopenharmony_ci	 * Since the ibmveth firmware interface does not have the
77762306a36Sopenharmony_ci	 * concept of separate tx/rx checksum offload enable, if rx
77862306a36Sopenharmony_ci	 * checksum is disabled we also have to disable tx checksum
77962306a36Sopenharmony_ci	 * offload. Once we disable rx checksum offload, we are no
78062306a36Sopenharmony_ci	 * longer allowed to send tx buffers that are not properly
78162306a36Sopenharmony_ci	 * checksummed.
78262306a36Sopenharmony_ci	 */
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (!(features & NETIF_F_RXCSUM))
78562306a36Sopenharmony_ci		features &= ~NETIF_F_CSUM_MASK;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return features;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int ibmveth_set_csum_offload(struct net_device *dev, u32 data)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
79362306a36Sopenharmony_ci	unsigned long set_attr, clr_attr, ret_attr;
79462306a36Sopenharmony_ci	unsigned long set_attr6, clr_attr6;
79562306a36Sopenharmony_ci	long ret, ret4, ret6;
79662306a36Sopenharmony_ci	int rc1 = 0, rc2 = 0;
79762306a36Sopenharmony_ci	int restart = 0;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (netif_running(dev)) {
80062306a36Sopenharmony_ci		restart = 1;
80162306a36Sopenharmony_ci		ibmveth_close(dev);
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	set_attr = 0;
80562306a36Sopenharmony_ci	clr_attr = 0;
80662306a36Sopenharmony_ci	set_attr6 = 0;
80762306a36Sopenharmony_ci	clr_attr6 = 0;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (data) {
81062306a36Sopenharmony_ci		set_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM;
81162306a36Sopenharmony_ci		set_attr6 = IBMVETH_ILLAN_IPV6_TCP_CSUM;
81262306a36Sopenharmony_ci	} else {
81362306a36Sopenharmony_ci		clr_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM;
81462306a36Sopenharmony_ci		clr_attr6 = IBMVETH_ILLAN_IPV6_TCP_CSUM;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ret = h_illan_attributes(adapter->vdev->unit_address, 0, 0, &ret_attr);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (ret == H_SUCCESS &&
82062306a36Sopenharmony_ci	    (ret_attr & IBMVETH_ILLAN_PADDED_PKT_CSUM)) {
82162306a36Sopenharmony_ci		ret4 = h_illan_attributes(adapter->vdev->unit_address, clr_attr,
82262306a36Sopenharmony_ci					 set_attr, &ret_attr);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		if (ret4 != H_SUCCESS) {
82562306a36Sopenharmony_ci			netdev_err(dev, "unable to change IPv4 checksum "
82662306a36Sopenharmony_ci					"offload settings. %d rc=%ld\n",
82762306a36Sopenharmony_ci					data, ret4);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci			h_illan_attributes(adapter->vdev->unit_address,
83062306a36Sopenharmony_ci					   set_attr, clr_attr, &ret_attr);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci			if (data == 1)
83362306a36Sopenharmony_ci				dev->features &= ~NETIF_F_IP_CSUM;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		} else {
83662306a36Sopenharmony_ci			adapter->fw_ipv4_csum_support = data;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		ret6 = h_illan_attributes(adapter->vdev->unit_address,
84062306a36Sopenharmony_ci					 clr_attr6, set_attr6, &ret_attr);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		if (ret6 != H_SUCCESS) {
84362306a36Sopenharmony_ci			netdev_err(dev, "unable to change IPv6 checksum "
84462306a36Sopenharmony_ci					"offload settings. %d rc=%ld\n",
84562306a36Sopenharmony_ci					data, ret6);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			h_illan_attributes(adapter->vdev->unit_address,
84862306a36Sopenharmony_ci					   set_attr6, clr_attr6, &ret_attr);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci			if (data == 1)
85162306a36Sopenharmony_ci				dev->features &= ~NETIF_F_IPV6_CSUM;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		} else
85462306a36Sopenharmony_ci			adapter->fw_ipv6_csum_support = data;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		if (ret4 == H_SUCCESS || ret6 == H_SUCCESS)
85762306a36Sopenharmony_ci			adapter->rx_csum = data;
85862306a36Sopenharmony_ci		else
85962306a36Sopenharmony_ci			rc1 = -EIO;
86062306a36Sopenharmony_ci	} else {
86162306a36Sopenharmony_ci		rc1 = -EIO;
86262306a36Sopenharmony_ci		netdev_err(dev, "unable to change checksum offload settings."
86362306a36Sopenharmony_ci				     " %d rc=%ld ret_attr=%lx\n", data, ret,
86462306a36Sopenharmony_ci				     ret_attr);
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (restart)
86862306a36Sopenharmony_ci		rc2 = ibmveth_open(dev);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return rc1 ? rc1 : rc2;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int ibmveth_set_tso(struct net_device *dev, u32 data)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
87662306a36Sopenharmony_ci	unsigned long set_attr, clr_attr, ret_attr;
87762306a36Sopenharmony_ci	long ret1, ret2;
87862306a36Sopenharmony_ci	int rc1 = 0, rc2 = 0;
87962306a36Sopenharmony_ci	int restart = 0;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (netif_running(dev)) {
88262306a36Sopenharmony_ci		restart = 1;
88362306a36Sopenharmony_ci		ibmveth_close(dev);
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	set_attr = 0;
88762306a36Sopenharmony_ci	clr_attr = 0;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if (data)
89062306a36Sopenharmony_ci		set_attr = IBMVETH_ILLAN_LRG_SR_ENABLED;
89162306a36Sopenharmony_ci	else
89262306a36Sopenharmony_ci		clr_attr = IBMVETH_ILLAN_LRG_SR_ENABLED;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	ret1 = h_illan_attributes(adapter->vdev->unit_address, 0, 0, &ret_attr);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	if (ret1 == H_SUCCESS && (ret_attr & IBMVETH_ILLAN_LRG_SND_SUPPORT) &&
89762306a36Sopenharmony_ci	    !old_large_send) {
89862306a36Sopenharmony_ci		ret2 = h_illan_attributes(adapter->vdev->unit_address, clr_attr,
89962306a36Sopenharmony_ci					  set_attr, &ret_attr);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		if (ret2 != H_SUCCESS) {
90262306a36Sopenharmony_ci			netdev_err(dev, "unable to change tso settings. %d rc=%ld\n",
90362306a36Sopenharmony_ci				   data, ret2);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci			h_illan_attributes(adapter->vdev->unit_address,
90662306a36Sopenharmony_ci					   set_attr, clr_attr, &ret_attr);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci			if (data == 1)
90962306a36Sopenharmony_ci				dev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
91062306a36Sopenharmony_ci			rc1 = -EIO;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		} else {
91362306a36Sopenharmony_ci			adapter->fw_large_send_support = data;
91462306a36Sopenharmony_ci			adapter->large_send = data;
91562306a36Sopenharmony_ci		}
91662306a36Sopenharmony_ci	} else {
91762306a36Sopenharmony_ci		/* Older firmware version of large send offload does not
91862306a36Sopenharmony_ci		 * support tcp6/ipv6
91962306a36Sopenharmony_ci		 */
92062306a36Sopenharmony_ci		if (data == 1) {
92162306a36Sopenharmony_ci			dev->features &= ~NETIF_F_TSO6;
92262306a36Sopenharmony_ci			netdev_info(dev, "TSO feature requires all partitions to have updated driver");
92362306a36Sopenharmony_ci		}
92462306a36Sopenharmony_ci		adapter->large_send = data;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (restart)
92862306a36Sopenharmony_ci		rc2 = ibmveth_open(dev);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return rc1 ? rc1 : rc2;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic int ibmveth_set_features(struct net_device *dev,
93462306a36Sopenharmony_ci	netdev_features_t features)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
93762306a36Sopenharmony_ci	int rx_csum = !!(features & NETIF_F_RXCSUM);
93862306a36Sopenharmony_ci	int large_send = !!(features & (NETIF_F_TSO | NETIF_F_TSO6));
93962306a36Sopenharmony_ci	int rc1 = 0, rc2 = 0;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (rx_csum != adapter->rx_csum) {
94262306a36Sopenharmony_ci		rc1 = ibmveth_set_csum_offload(dev, rx_csum);
94362306a36Sopenharmony_ci		if (rc1 && !adapter->rx_csum)
94462306a36Sopenharmony_ci			dev->features =
94562306a36Sopenharmony_ci				features & ~(NETIF_F_CSUM_MASK |
94662306a36Sopenharmony_ci					     NETIF_F_RXCSUM);
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (large_send != adapter->large_send) {
95062306a36Sopenharmony_ci		rc2 = ibmveth_set_tso(dev, large_send);
95162306a36Sopenharmony_ci		if (rc2 && !adapter->large_send)
95262306a36Sopenharmony_ci			dev->features =
95362306a36Sopenharmony_ci				features & ~(NETIF_F_TSO | NETIF_F_TSO6);
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return rc1 ? rc1 : rc2;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic void ibmveth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	int i;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (stringset != ETH_SS_STATS)
96462306a36Sopenharmony_ci		return;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++, data += ETH_GSTRING_LEN)
96762306a36Sopenharmony_ci		memcpy(data, ibmveth_stats[i].name, ETH_GSTRING_LEN);
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_cistatic int ibmveth_get_sset_count(struct net_device *dev, int sset)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	switch (sset) {
97362306a36Sopenharmony_ci	case ETH_SS_STATS:
97462306a36Sopenharmony_ci		return ARRAY_SIZE(ibmveth_stats);
97562306a36Sopenharmony_ci	default:
97662306a36Sopenharmony_ci		return -EOPNOTSUPP;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void ibmveth_get_ethtool_stats(struct net_device *dev,
98162306a36Sopenharmony_ci				      struct ethtool_stats *stats, u64 *data)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	int i;
98462306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++)
98762306a36Sopenharmony_ci		data[i] = IBMVETH_GET_STAT(adapter, ibmveth_stats[i].offset);
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic void ibmveth_get_channels(struct net_device *netdev,
99162306a36Sopenharmony_ci				 struct ethtool_channels *channels)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	channels->max_tx = ibmveth_real_max_tx_queues();
99462306a36Sopenharmony_ci	channels->tx_count = netdev->real_num_tx_queues;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	channels->max_rx = netdev->real_num_rx_queues;
99762306a36Sopenharmony_ci	channels->rx_count = netdev->real_num_rx_queues;
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic int ibmveth_set_channels(struct net_device *netdev,
100162306a36Sopenharmony_ci				struct ethtool_channels *channels)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
100462306a36Sopenharmony_ci	unsigned int old = netdev->real_num_tx_queues,
100562306a36Sopenharmony_ci		     goal = channels->tx_count;
100662306a36Sopenharmony_ci	int rc, i;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/* If ndo_open has not been called yet then don't allocate, just set
100962306a36Sopenharmony_ci	 * desired netdev_queue's and return
101062306a36Sopenharmony_ci	 */
101162306a36Sopenharmony_ci	if (!(netdev->flags & IFF_UP))
101262306a36Sopenharmony_ci		return netif_set_real_num_tx_queues(netdev, goal);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* We have IBMVETH_MAX_QUEUES netdev_queue's allocated
101562306a36Sopenharmony_ci	 * but we may need to alloc/free the ltb's.
101662306a36Sopenharmony_ci	 */
101762306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* Allocate any queue that we need */
102062306a36Sopenharmony_ci	for (i = old; i < goal; i++) {
102162306a36Sopenharmony_ci		if (adapter->tx_ltb_ptr[i])
102262306a36Sopenharmony_ci			continue;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		rc = ibmveth_allocate_tx_ltb(adapter, i);
102562306a36Sopenharmony_ci		if (!rc)
102662306a36Sopenharmony_ci			continue;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		/* if something goes wrong, free everything we just allocated */
102962306a36Sopenharmony_ci		netdev_err(netdev, "Failed to allocate more tx queues, returning to %d queues\n",
103062306a36Sopenharmony_ci			   old);
103162306a36Sopenharmony_ci		goal = old;
103262306a36Sopenharmony_ci		old = i;
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	rc = netif_set_real_num_tx_queues(netdev, goal);
103662306a36Sopenharmony_ci	if (rc) {
103762306a36Sopenharmony_ci		netdev_err(netdev, "Failed to set real tx queues, returning to %d queues\n",
103862306a36Sopenharmony_ci			   old);
103962306a36Sopenharmony_ci		goal = old;
104062306a36Sopenharmony_ci		old = i;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci	/* Free any that are no longer needed */
104362306a36Sopenharmony_ci	for (i = old; i > goal; i--) {
104462306a36Sopenharmony_ci		if (adapter->tx_ltb_ptr[i - 1])
104562306a36Sopenharmony_ci			ibmveth_free_tx_ltb(adapter, i - 1);
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	netif_tx_wake_all_queues(netdev);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return rc;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
105462306a36Sopenharmony_ci	.get_drvinfo		         = netdev_get_drvinfo,
105562306a36Sopenharmony_ci	.get_link		         = ethtool_op_get_link,
105662306a36Sopenharmony_ci	.get_strings		         = ibmveth_get_strings,
105762306a36Sopenharmony_ci	.get_sset_count		         = ibmveth_get_sset_count,
105862306a36Sopenharmony_ci	.get_ethtool_stats	         = ibmveth_get_ethtool_stats,
105962306a36Sopenharmony_ci	.get_link_ksettings	         = ibmveth_get_link_ksettings,
106062306a36Sopenharmony_ci	.set_link_ksettings              = ibmveth_set_link_ksettings,
106162306a36Sopenharmony_ci	.get_channels			 = ibmveth_get_channels,
106262306a36Sopenharmony_ci	.set_channels			 = ibmveth_set_channels
106362306a36Sopenharmony_ci};
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	return -EOPNOTSUPP;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic int ibmveth_send(struct ibmveth_adapter *adapter,
107162306a36Sopenharmony_ci			unsigned long desc, unsigned long mss)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	unsigned long correlator;
107462306a36Sopenharmony_ci	unsigned int retry_count;
107562306a36Sopenharmony_ci	unsigned long ret;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/*
107862306a36Sopenharmony_ci	 * The retry count sets a maximum for the number of broadcast and
107962306a36Sopenharmony_ci	 * multicast destinations within the system.
108062306a36Sopenharmony_ci	 */
108162306a36Sopenharmony_ci	retry_count = 1024;
108262306a36Sopenharmony_ci	correlator = 0;
108362306a36Sopenharmony_ci	do {
108462306a36Sopenharmony_ci		ret = h_send_logical_lan(adapter->vdev->unit_address, desc,
108562306a36Sopenharmony_ci					 correlator, &correlator, mss,
108662306a36Sopenharmony_ci					 adapter->fw_large_send_support);
108762306a36Sopenharmony_ci	} while ((ret == H_BUSY) && (retry_count--));
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (ret != H_SUCCESS && ret != H_DROPPED) {
109062306a36Sopenharmony_ci		netdev_err(adapter->netdev, "tx: h_send_logical_lan failed "
109162306a36Sopenharmony_ci			   "with rc=%ld\n", ret);
109262306a36Sopenharmony_ci		return 1;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic int ibmveth_is_packet_unsupported(struct sk_buff *skb,
109962306a36Sopenharmony_ci					 struct net_device *netdev)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct ethhdr *ether_header;
110262306a36Sopenharmony_ci	int ret = 0;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	ether_header = eth_hdr(skb);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (ether_addr_equal(ether_header->h_dest, netdev->dev_addr)) {
110762306a36Sopenharmony_ci		netdev_dbg(netdev, "veth doesn't support loopback packets, dropping packet.\n");
110862306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
110962306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	return ret;
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
111662306a36Sopenharmony_ci				      struct net_device *netdev)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
111962306a36Sopenharmony_ci	unsigned int desc_flags, total_bytes;
112062306a36Sopenharmony_ci	union ibmveth_buf_desc desc;
112162306a36Sopenharmony_ci	int i, queue_num = skb_get_queue_mapping(skb);
112262306a36Sopenharmony_ci	unsigned long mss = 0;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (ibmveth_is_packet_unsupported(skb, netdev))
112562306a36Sopenharmony_ci		goto out;
112662306a36Sopenharmony_ci	/* veth can't checksum offload UDP */
112762306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL &&
112862306a36Sopenharmony_ci	    ((skb->protocol == htons(ETH_P_IP) &&
112962306a36Sopenharmony_ci	      ip_hdr(skb)->protocol != IPPROTO_TCP) ||
113062306a36Sopenharmony_ci	     (skb->protocol == htons(ETH_P_IPV6) &&
113162306a36Sopenharmony_ci	      ipv6_hdr(skb)->nexthdr != IPPROTO_TCP)) &&
113262306a36Sopenharmony_ci	    skb_checksum_help(skb)) {
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		netdev_err(netdev, "tx: failed to checksum packet\n");
113562306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
113662306a36Sopenharmony_ci		goto out;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	desc_flags = IBMVETH_BUF_VALID;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
114262306a36Sopenharmony_ci		unsigned char *buf = skb_transport_header(skb) +
114362306a36Sopenharmony_ci						skb->csum_offset;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci		desc_flags |= (IBMVETH_BUF_NO_CSUM | IBMVETH_BUF_CSUM_GOOD);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		/* Need to zero out the checksum */
114862306a36Sopenharmony_ci		buf[0] = 0;
114962306a36Sopenharmony_ci		buf[1] = 0;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		if (skb_is_gso(skb) && adapter->fw_large_send_support)
115262306a36Sopenharmony_ci			desc_flags |= IBMVETH_BUF_LRG_SND;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL && skb_is_gso(skb)) {
115662306a36Sopenharmony_ci		if (adapter->fw_large_send_support) {
115762306a36Sopenharmony_ci			mss = (unsigned long)skb_shinfo(skb)->gso_size;
115862306a36Sopenharmony_ci			adapter->tx_large_packets++;
115962306a36Sopenharmony_ci		} else if (!skb_is_gso_v6(skb)) {
116062306a36Sopenharmony_ci			/* Put -1 in the IP checksum to tell phyp it
116162306a36Sopenharmony_ci			 * is a largesend packet. Put the mss in
116262306a36Sopenharmony_ci			 * the TCP checksum.
116362306a36Sopenharmony_ci			 */
116462306a36Sopenharmony_ci			ip_hdr(skb)->check = 0xffff;
116562306a36Sopenharmony_ci			tcp_hdr(skb)->check =
116662306a36Sopenharmony_ci				cpu_to_be16(skb_shinfo(skb)->gso_size);
116762306a36Sopenharmony_ci			adapter->tx_large_packets++;
116862306a36Sopenharmony_ci		}
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* Copy header into mapped buffer */
117262306a36Sopenharmony_ci	if (unlikely(skb->len > adapter->tx_ltb_size)) {
117362306a36Sopenharmony_ci		netdev_err(adapter->netdev, "tx: packet size (%u) exceeds ltb (%u)\n",
117462306a36Sopenharmony_ci			   skb->len, adapter->tx_ltb_size);
117562306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
117662306a36Sopenharmony_ci		goto out;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci	memcpy(adapter->tx_ltb_ptr[queue_num], skb->data, skb_headlen(skb));
117962306a36Sopenharmony_ci	total_bytes = skb_headlen(skb);
118062306a36Sopenharmony_ci	/* Copy frags into mapped buffers */
118162306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
118262306a36Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		memcpy(adapter->tx_ltb_ptr[queue_num] + total_bytes,
118562306a36Sopenharmony_ci		       skb_frag_address_safe(frag), skb_frag_size(frag));
118662306a36Sopenharmony_ci		total_bytes += skb_frag_size(frag);
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (unlikely(total_bytes != skb->len)) {
119062306a36Sopenharmony_ci		netdev_err(adapter->netdev, "tx: incorrect packet len copied into ltb (%u != %u)\n",
119162306a36Sopenharmony_ci			   skb->len, total_bytes);
119262306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
119362306a36Sopenharmony_ci		goto out;
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci	desc.fields.flags_len = desc_flags | skb->len;
119662306a36Sopenharmony_ci	desc.fields.address = adapter->tx_ltb_dma[queue_num];
119762306a36Sopenharmony_ci	/* finish writing to long_term_buff before VIOS accessing it */
119862306a36Sopenharmony_ci	dma_wmb();
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	if (ibmveth_send(adapter, desc.desc, mss)) {
120162306a36Sopenharmony_ci		adapter->tx_send_failed++;
120262306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
120362306a36Sopenharmony_ci	} else {
120462306a36Sopenharmony_ci		netdev->stats.tx_packets++;
120562306a36Sopenharmony_ci		netdev->stats.tx_bytes += skb->len;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ciout:
120962306a36Sopenharmony_ci	dev_consume_skb_any(skb);
121062306a36Sopenharmony_ci	return NETDEV_TX_OK;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic void ibmveth_rx_mss_helper(struct sk_buff *skb, u16 mss, int lrg_pkt)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct tcphdr *tcph;
121862306a36Sopenharmony_ci	int offset = 0;
121962306a36Sopenharmony_ci	int hdr_len;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	/* only TCP packets will be aggregated */
122262306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP)) {
122362306a36Sopenharmony_ci		struct iphdr *iph = (struct iphdr *)skb->data;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci		if (iph->protocol == IPPROTO_TCP) {
122662306a36Sopenharmony_ci			offset = iph->ihl * 4;
122762306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
122862306a36Sopenharmony_ci		} else {
122962306a36Sopenharmony_ci			return;
123062306a36Sopenharmony_ci		}
123162306a36Sopenharmony_ci	} else if (skb->protocol == htons(ETH_P_IPV6)) {
123262306a36Sopenharmony_ci		struct ipv6hdr *iph6 = (struct ipv6hdr *)skb->data;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci		if (iph6->nexthdr == IPPROTO_TCP) {
123562306a36Sopenharmony_ci			offset = sizeof(struct ipv6hdr);
123662306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
123762306a36Sopenharmony_ci		} else {
123862306a36Sopenharmony_ci			return;
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci	} else {
124162306a36Sopenharmony_ci		return;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci	/* if mss is not set through Large Packet bit/mss in rx buffer,
124462306a36Sopenharmony_ci	 * expect that the mss will be written to the tcp header checksum.
124562306a36Sopenharmony_ci	 */
124662306a36Sopenharmony_ci	tcph = (struct tcphdr *)(skb->data + offset);
124762306a36Sopenharmony_ci	if (lrg_pkt) {
124862306a36Sopenharmony_ci		skb_shinfo(skb)->gso_size = mss;
124962306a36Sopenharmony_ci	} else if (offset) {
125062306a36Sopenharmony_ci		skb_shinfo(skb)->gso_size = ntohs(tcph->check);
125162306a36Sopenharmony_ci		tcph->check = 0;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (skb_shinfo(skb)->gso_size) {
125562306a36Sopenharmony_ci		hdr_len = offset + tcph->doff * 4;
125662306a36Sopenharmony_ci		skb_shinfo(skb)->gso_segs =
125762306a36Sopenharmony_ci				DIV_ROUND_UP(skb->len - hdr_len,
125862306a36Sopenharmony_ci					     skb_shinfo(skb)->gso_size);
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic void ibmveth_rx_csum_helper(struct sk_buff *skb,
126362306a36Sopenharmony_ci				   struct ibmveth_adapter *adapter)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct iphdr *iph = NULL;
126662306a36Sopenharmony_ci	struct ipv6hdr *iph6 = NULL;
126762306a36Sopenharmony_ci	__be16 skb_proto = 0;
126862306a36Sopenharmony_ci	u16 iphlen = 0;
126962306a36Sopenharmony_ci	u16 iph_proto = 0;
127062306a36Sopenharmony_ci	u16 tcphdrlen = 0;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	skb_proto = be16_to_cpu(skb->protocol);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (skb_proto == ETH_P_IP) {
127562306a36Sopenharmony_ci		iph = (struct iphdr *)skb->data;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci		/* If the IP checksum is not offloaded and if the packet
127862306a36Sopenharmony_ci		 *  is large send, the checksum must be rebuilt.
127962306a36Sopenharmony_ci		 */
128062306a36Sopenharmony_ci		if (iph->check == 0xffff) {
128162306a36Sopenharmony_ci			iph->check = 0;
128262306a36Sopenharmony_ci			iph->check = ip_fast_csum((unsigned char *)iph,
128362306a36Sopenharmony_ci						  iph->ihl);
128462306a36Sopenharmony_ci		}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		iphlen = iph->ihl * 4;
128762306a36Sopenharmony_ci		iph_proto = iph->protocol;
128862306a36Sopenharmony_ci	} else if (skb_proto == ETH_P_IPV6) {
128962306a36Sopenharmony_ci		iph6 = (struct ipv6hdr *)skb->data;
129062306a36Sopenharmony_ci		iphlen = sizeof(struct ipv6hdr);
129162306a36Sopenharmony_ci		iph_proto = iph6->nexthdr;
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/* When CSO is enabled the TCP checksum may have be set to NULL by
129562306a36Sopenharmony_ci	 * the sender given that we zeroed out TCP checksum field in
129662306a36Sopenharmony_ci	 * transmit path (refer ibmveth_start_xmit routine). In this case set
129762306a36Sopenharmony_ci	 * up CHECKSUM_PARTIAL. If the packet is forwarded, the checksum will
129862306a36Sopenharmony_ci	 * then be recalculated by the destination NIC (CSO must be enabled
129962306a36Sopenharmony_ci	 * on the destination NIC).
130062306a36Sopenharmony_ci	 *
130162306a36Sopenharmony_ci	 * In an OVS environment, when a flow is not cached, specifically for a
130262306a36Sopenharmony_ci	 * new TCP connection, the first packet information is passed up to
130362306a36Sopenharmony_ci	 * the user space for finding a flow. During this process, OVS computes
130462306a36Sopenharmony_ci	 * checksum on the first packet when CHECKSUM_PARTIAL flag is set.
130562306a36Sopenharmony_ci	 *
130662306a36Sopenharmony_ci	 * So, re-compute TCP pseudo header checksum.
130762306a36Sopenharmony_ci	 */
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (iph_proto == IPPROTO_TCP) {
131062306a36Sopenharmony_ci		struct tcphdr *tcph = (struct tcphdr *)(skb->data + iphlen);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci		if (tcph->check == 0x0000) {
131362306a36Sopenharmony_ci			/* Recompute TCP pseudo header checksum  */
131462306a36Sopenharmony_ci			tcphdrlen = skb->len - iphlen;
131562306a36Sopenharmony_ci			if (skb_proto == ETH_P_IP)
131662306a36Sopenharmony_ci				tcph->check =
131762306a36Sopenharmony_ci				 ~csum_tcpudp_magic(iph->saddr,
131862306a36Sopenharmony_ci				iph->daddr, tcphdrlen, iph_proto, 0);
131962306a36Sopenharmony_ci			else if (skb_proto == ETH_P_IPV6)
132062306a36Sopenharmony_ci				tcph->check =
132162306a36Sopenharmony_ci				 ~csum_ipv6_magic(&iph6->saddr,
132262306a36Sopenharmony_ci				&iph6->daddr, tcphdrlen, iph_proto, 0);
132362306a36Sopenharmony_ci			/* Setup SKB fields for checksum offload */
132462306a36Sopenharmony_ci			skb_partial_csum_set(skb, iphlen,
132562306a36Sopenharmony_ci					     offsetof(struct tcphdr, check));
132662306a36Sopenharmony_ci			skb_reset_network_header(skb);
132762306a36Sopenharmony_ci		}
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int ibmveth_poll(struct napi_struct *napi, int budget)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	struct ibmveth_adapter *adapter =
133462306a36Sopenharmony_ci			container_of(napi, struct ibmveth_adapter, napi);
133562306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
133662306a36Sopenharmony_ci	int frames_processed = 0;
133762306a36Sopenharmony_ci	unsigned long lpar_rc;
133862306a36Sopenharmony_ci	u16 mss = 0;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	while (frames_processed < budget) {
134162306a36Sopenharmony_ci		if (!ibmveth_rxq_pending_buffer(adapter))
134262306a36Sopenharmony_ci			break;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		smp_rmb();
134562306a36Sopenharmony_ci		if (!ibmveth_rxq_buffer_valid(adapter)) {
134662306a36Sopenharmony_ci			wmb(); /* suggested by larson1 */
134762306a36Sopenharmony_ci			adapter->rx_invalid_buffer++;
134862306a36Sopenharmony_ci			netdev_dbg(netdev, "recycling invalid buffer\n");
134962306a36Sopenharmony_ci			ibmveth_rxq_recycle_buffer(adapter);
135062306a36Sopenharmony_ci		} else {
135162306a36Sopenharmony_ci			struct sk_buff *skb, *new_skb;
135262306a36Sopenharmony_ci			int length = ibmveth_rxq_frame_length(adapter);
135362306a36Sopenharmony_ci			int offset = ibmveth_rxq_frame_offset(adapter);
135462306a36Sopenharmony_ci			int csum_good = ibmveth_rxq_csum_good(adapter);
135562306a36Sopenharmony_ci			int lrg_pkt = ibmveth_rxq_large_packet(adapter);
135662306a36Sopenharmony_ci			__sum16 iph_check = 0;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci			skb = ibmveth_rxq_get_buffer(adapter);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci			/* if the large packet bit is set in the rx queue
136162306a36Sopenharmony_ci			 * descriptor, the mss will be written by PHYP eight
136262306a36Sopenharmony_ci			 * bytes from the start of the rx buffer, which is
136362306a36Sopenharmony_ci			 * skb->data at this stage
136462306a36Sopenharmony_ci			 */
136562306a36Sopenharmony_ci			if (lrg_pkt) {
136662306a36Sopenharmony_ci				__be64 *rxmss = (__be64 *)(skb->data + 8);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci				mss = (u16)be64_to_cpu(*rxmss);
136962306a36Sopenharmony_ci			}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci			new_skb = NULL;
137262306a36Sopenharmony_ci			if (length < rx_copybreak)
137362306a36Sopenharmony_ci				new_skb = netdev_alloc_skb(netdev, length);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci			if (new_skb) {
137662306a36Sopenharmony_ci				skb_copy_to_linear_data(new_skb,
137762306a36Sopenharmony_ci							skb->data + offset,
137862306a36Sopenharmony_ci							length);
137962306a36Sopenharmony_ci				if (rx_flush)
138062306a36Sopenharmony_ci					ibmveth_flush_buffer(skb->data,
138162306a36Sopenharmony_ci						length + offset);
138262306a36Sopenharmony_ci				if (!ibmveth_rxq_recycle_buffer(adapter))
138362306a36Sopenharmony_ci					kfree_skb(skb);
138462306a36Sopenharmony_ci				skb = new_skb;
138562306a36Sopenharmony_ci			} else {
138662306a36Sopenharmony_ci				ibmveth_rxq_harvest_buffer(adapter);
138762306a36Sopenharmony_ci				skb_reserve(skb, offset);
138862306a36Sopenharmony_ci			}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci			skb_put(skb, length);
139162306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, netdev);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci			/* PHYP without PLSO support places a -1 in the ip
139462306a36Sopenharmony_ci			 * checksum for large send frames.
139562306a36Sopenharmony_ci			 */
139662306a36Sopenharmony_ci			if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
139762306a36Sopenharmony_ci				struct iphdr *iph = (struct iphdr *)skb->data;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci				iph_check = iph->check;
140062306a36Sopenharmony_ci			}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci			if ((length > netdev->mtu + ETH_HLEN) ||
140362306a36Sopenharmony_ci			    lrg_pkt || iph_check == 0xffff) {
140462306a36Sopenharmony_ci				ibmveth_rx_mss_helper(skb, mss, lrg_pkt);
140562306a36Sopenharmony_ci				adapter->rx_large_packets++;
140662306a36Sopenharmony_ci			}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci			if (csum_good) {
140962306a36Sopenharmony_ci				skb->ip_summed = CHECKSUM_UNNECESSARY;
141062306a36Sopenharmony_ci				ibmveth_rx_csum_helper(skb, adapter);
141162306a36Sopenharmony_ci			}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci			napi_gro_receive(napi, skb);	/* send it up */
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci			netdev->stats.rx_packets++;
141662306a36Sopenharmony_ci			netdev->stats.rx_bytes += length;
141762306a36Sopenharmony_ci			frames_processed++;
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	ibmveth_replenish_task(adapter);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	if (frames_processed < budget) {
142462306a36Sopenharmony_ci		napi_complete_done(napi, frames_processed);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci		/* We think we are done - reenable interrupts,
142762306a36Sopenharmony_ci		 * then check once more to make sure we are done.
142862306a36Sopenharmony_ci		 */
142962306a36Sopenharmony_ci		lpar_rc = h_vio_signal(adapter->vdev->unit_address,
143062306a36Sopenharmony_ci				       VIO_IRQ_ENABLE);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		BUG_ON(lpar_rc != H_SUCCESS);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		if (ibmveth_rxq_pending_buffer(adapter) &&
143562306a36Sopenharmony_ci		    napi_reschedule(napi)) {
143662306a36Sopenharmony_ci			lpar_rc = h_vio_signal(adapter->vdev->unit_address,
143762306a36Sopenharmony_ci					       VIO_IRQ_DISABLE);
143862306a36Sopenharmony_ci		}
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	return frames_processed;
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic irqreturn_t ibmveth_interrupt(int irq, void *dev_instance)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct net_device *netdev = dev_instance;
144762306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
144862306a36Sopenharmony_ci	unsigned long lpar_rc;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (napi_schedule_prep(&adapter->napi)) {
145162306a36Sopenharmony_ci		lpar_rc = h_vio_signal(adapter->vdev->unit_address,
145262306a36Sopenharmony_ci				       VIO_IRQ_DISABLE);
145362306a36Sopenharmony_ci		BUG_ON(lpar_rc != H_SUCCESS);
145462306a36Sopenharmony_ci		__napi_schedule(&adapter->napi);
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci	return IRQ_HANDLED;
145762306a36Sopenharmony_ci}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic void ibmveth_set_multicast_list(struct net_device *netdev)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
146262306a36Sopenharmony_ci	unsigned long lpar_rc;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	if ((netdev->flags & IFF_PROMISC) ||
146562306a36Sopenharmony_ci	    (netdev_mc_count(netdev) > adapter->mcastFilterSize)) {
146662306a36Sopenharmony_ci		lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
146762306a36Sopenharmony_ci					   IbmVethMcastEnableRecv |
146862306a36Sopenharmony_ci					   IbmVethMcastDisableFiltering,
146962306a36Sopenharmony_ci					   0);
147062306a36Sopenharmony_ci		if (lpar_rc != H_SUCCESS) {
147162306a36Sopenharmony_ci			netdev_err(netdev, "h_multicast_ctrl rc=%ld when "
147262306a36Sopenharmony_ci				   "entering promisc mode\n", lpar_rc);
147362306a36Sopenharmony_ci		}
147462306a36Sopenharmony_ci	} else {
147562306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
147662306a36Sopenharmony_ci		/* clear the filter table & disable filtering */
147762306a36Sopenharmony_ci		lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
147862306a36Sopenharmony_ci					   IbmVethMcastEnableRecv |
147962306a36Sopenharmony_ci					   IbmVethMcastDisableFiltering |
148062306a36Sopenharmony_ci					   IbmVethMcastClearFilterTable,
148162306a36Sopenharmony_ci					   0);
148262306a36Sopenharmony_ci		if (lpar_rc != H_SUCCESS) {
148362306a36Sopenharmony_ci			netdev_err(netdev, "h_multicast_ctrl rc=%ld when "
148462306a36Sopenharmony_ci				   "attempting to clear filter table\n",
148562306a36Sopenharmony_ci				   lpar_rc);
148662306a36Sopenharmony_ci		}
148762306a36Sopenharmony_ci		/* add the addresses to the filter table */
148862306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, netdev) {
148962306a36Sopenharmony_ci			/* add the multicast address to the filter table */
149062306a36Sopenharmony_ci			u64 mcast_addr;
149162306a36Sopenharmony_ci			mcast_addr = ether_addr_to_u64(ha->addr);
149262306a36Sopenharmony_ci			lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
149362306a36Sopenharmony_ci						   IbmVethMcastAddFilter,
149462306a36Sopenharmony_ci						   mcast_addr);
149562306a36Sopenharmony_ci			if (lpar_rc != H_SUCCESS) {
149662306a36Sopenharmony_ci				netdev_err(netdev, "h_multicast_ctrl rc=%ld "
149762306a36Sopenharmony_ci					   "when adding an entry to the filter "
149862306a36Sopenharmony_ci					   "table\n", lpar_rc);
149962306a36Sopenharmony_ci			}
150062306a36Sopenharmony_ci		}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci		/* re-enable filtering */
150362306a36Sopenharmony_ci		lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
150462306a36Sopenharmony_ci					   IbmVethMcastEnableFiltering,
150562306a36Sopenharmony_ci					   0);
150662306a36Sopenharmony_ci		if (lpar_rc != H_SUCCESS) {
150762306a36Sopenharmony_ci			netdev_err(netdev, "h_multicast_ctrl rc=%ld when "
150862306a36Sopenharmony_ci				   "enabling filtering\n", lpar_rc);
150962306a36Sopenharmony_ci		}
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
151662306a36Sopenharmony_ci	struct vio_dev *viodev = adapter->vdev;
151762306a36Sopenharmony_ci	int new_mtu_oh = new_mtu + IBMVETH_BUFF_OH;
151862306a36Sopenharmony_ci	int i, rc;
151962306a36Sopenharmony_ci	int need_restart = 0;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
152262306a36Sopenharmony_ci		if (new_mtu_oh <= adapter->rx_buff_pool[i].buff_size)
152362306a36Sopenharmony_ci			break;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	if (i == IBMVETH_NUM_BUFF_POOLS)
152662306a36Sopenharmony_ci		return -EINVAL;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	/* Deactivate all the buffer pools so that the next loop can activate
152962306a36Sopenharmony_ci	   only the buffer pools necessary to hold the new MTU */
153062306a36Sopenharmony_ci	if (netif_running(adapter->netdev)) {
153162306a36Sopenharmony_ci		need_restart = 1;
153262306a36Sopenharmony_ci		ibmveth_close(adapter->netdev);
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/* Look for an active buffer pool that can hold the new MTU */
153662306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
153762306a36Sopenharmony_ci		adapter->rx_buff_pool[i].active = 1;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		if (new_mtu_oh <= adapter->rx_buff_pool[i].buff_size) {
154062306a36Sopenharmony_ci			dev->mtu = new_mtu;
154162306a36Sopenharmony_ci			vio_cmo_set_dev_desired(viodev,
154262306a36Sopenharmony_ci						ibmveth_get_desired_dma
154362306a36Sopenharmony_ci						(viodev));
154462306a36Sopenharmony_ci			if (need_restart) {
154562306a36Sopenharmony_ci				return ibmveth_open(adapter->netdev);
154662306a36Sopenharmony_ci			}
154762306a36Sopenharmony_ci			return 0;
154862306a36Sopenharmony_ci		}
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (need_restart && (rc = ibmveth_open(adapter->netdev)))
155262306a36Sopenharmony_ci		return rc;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	return -EINVAL;
155562306a36Sopenharmony_ci}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
155862306a36Sopenharmony_cistatic void ibmveth_poll_controller(struct net_device *dev)
155962306a36Sopenharmony_ci{
156062306a36Sopenharmony_ci	ibmveth_replenish_task(netdev_priv(dev));
156162306a36Sopenharmony_ci	ibmveth_interrupt(dev->irq, dev);
156262306a36Sopenharmony_ci}
156362306a36Sopenharmony_ci#endif
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci/**
156662306a36Sopenharmony_ci * ibmveth_get_desired_dma - Calculate IO memory desired by the driver
156762306a36Sopenharmony_ci *
156862306a36Sopenharmony_ci * @vdev: struct vio_dev for the device whose desired IO mem is to be returned
156962306a36Sopenharmony_ci *
157062306a36Sopenharmony_ci * Return value:
157162306a36Sopenharmony_ci *	Number of bytes of IO data the driver will need to perform well.
157262306a36Sopenharmony_ci */
157362306a36Sopenharmony_cistatic unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(&vdev->dev);
157662306a36Sopenharmony_ci	struct ibmveth_adapter *adapter;
157762306a36Sopenharmony_ci	struct iommu_table *tbl;
157862306a36Sopenharmony_ci	unsigned long ret;
157962306a36Sopenharmony_ci	int i;
158062306a36Sopenharmony_ci	int rxqentries = 1;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	tbl = get_iommu_table_base(&vdev->dev);
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	/* netdev inits at probe time along with the structures we need below*/
158562306a36Sopenharmony_ci	if (netdev == NULL)
158662306a36Sopenharmony_ci		return IOMMU_PAGE_ALIGN(IBMVETH_IO_ENTITLEMENT_DEFAULT, tbl);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	adapter = netdev_priv(netdev);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	ret = IBMVETH_BUFF_LIST_SIZE + IBMVETH_FILT_LIST_SIZE;
159162306a36Sopenharmony_ci	ret += IOMMU_PAGE_ALIGN(netdev->mtu, tbl);
159262306a36Sopenharmony_ci	/* add size of mapped tx buffers */
159362306a36Sopenharmony_ci	ret += IOMMU_PAGE_ALIGN(IBMVETH_MAX_TX_BUF_SIZE, tbl);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
159662306a36Sopenharmony_ci		/* add the size of the active receive buffers */
159762306a36Sopenharmony_ci		if (adapter->rx_buff_pool[i].active)
159862306a36Sopenharmony_ci			ret +=
159962306a36Sopenharmony_ci			    adapter->rx_buff_pool[i].size *
160062306a36Sopenharmony_ci			    IOMMU_PAGE_ALIGN(adapter->rx_buff_pool[i].
160162306a36Sopenharmony_ci					     buff_size, tbl);
160262306a36Sopenharmony_ci		rxqentries += adapter->rx_buff_pool[i].size;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci	/* add the size of the receive queue entries */
160562306a36Sopenharmony_ci	ret += IOMMU_PAGE_ALIGN(
160662306a36Sopenharmony_ci		rxqentries * sizeof(struct ibmveth_rx_q_entry), tbl);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	return ret;
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic int ibmveth_set_mac_addr(struct net_device *dev, void *p)
161262306a36Sopenharmony_ci{
161362306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(dev);
161462306a36Sopenharmony_ci	struct sockaddr *addr = p;
161562306a36Sopenharmony_ci	u64 mac_address;
161662306a36Sopenharmony_ci	int rc;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
161962306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	mac_address = ether_addr_to_u64(addr->sa_data);
162262306a36Sopenharmony_ci	rc = h_change_logical_lan_mac(adapter->vdev->unit_address, mac_address);
162362306a36Sopenharmony_ci	if (rc) {
162462306a36Sopenharmony_ci		netdev_err(adapter->netdev, "h_change_logical_lan_mac failed with rc=%d\n", rc);
162562306a36Sopenharmony_ci		return rc;
162662306a36Sopenharmony_ci	}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	return 0;
163162306a36Sopenharmony_ci}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_cistatic const struct net_device_ops ibmveth_netdev_ops = {
163462306a36Sopenharmony_ci	.ndo_open		= ibmveth_open,
163562306a36Sopenharmony_ci	.ndo_stop		= ibmveth_close,
163662306a36Sopenharmony_ci	.ndo_start_xmit		= ibmveth_start_xmit,
163762306a36Sopenharmony_ci	.ndo_set_rx_mode	= ibmveth_set_multicast_list,
163862306a36Sopenharmony_ci	.ndo_eth_ioctl		= ibmveth_ioctl,
163962306a36Sopenharmony_ci	.ndo_change_mtu		= ibmveth_change_mtu,
164062306a36Sopenharmony_ci	.ndo_fix_features	= ibmveth_fix_features,
164162306a36Sopenharmony_ci	.ndo_set_features	= ibmveth_set_features,
164262306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
164362306a36Sopenharmony_ci	.ndo_set_mac_address    = ibmveth_set_mac_addr,
164462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
164562306a36Sopenharmony_ci	.ndo_poll_controller	= ibmveth_poll_controller,
164662306a36Sopenharmony_ci#endif
164762306a36Sopenharmony_ci};
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_cistatic int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	int rc, i, mac_len;
165262306a36Sopenharmony_ci	struct net_device *netdev;
165362306a36Sopenharmony_ci	struct ibmveth_adapter *adapter;
165462306a36Sopenharmony_ci	unsigned char *mac_addr_p;
165562306a36Sopenharmony_ci	__be32 *mcastFilterSize_p;
165662306a36Sopenharmony_ci	long ret;
165762306a36Sopenharmony_ci	unsigned long ret_attr;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	dev_dbg(&dev->dev, "entering ibmveth_probe for UA 0x%x\n",
166062306a36Sopenharmony_ci		dev->unit_address);
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	mac_addr_p = (unsigned char *)vio_get_attribute(dev, VETH_MAC_ADDR,
166362306a36Sopenharmony_ci							&mac_len);
166462306a36Sopenharmony_ci	if (!mac_addr_p) {
166562306a36Sopenharmony_ci		dev_err(&dev->dev, "Can't find VETH_MAC_ADDR attribute\n");
166662306a36Sopenharmony_ci		return -EINVAL;
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci	/* Workaround for old/broken pHyp */
166962306a36Sopenharmony_ci	if (mac_len == 8)
167062306a36Sopenharmony_ci		mac_addr_p += 2;
167162306a36Sopenharmony_ci	else if (mac_len != 6) {
167262306a36Sopenharmony_ci		dev_err(&dev->dev, "VETH_MAC_ADDR attribute wrong len %d\n",
167362306a36Sopenharmony_ci			mac_len);
167462306a36Sopenharmony_ci		return -EINVAL;
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	mcastFilterSize_p = (__be32 *)vio_get_attribute(dev,
167862306a36Sopenharmony_ci							VETH_MCAST_FILTER_SIZE,
167962306a36Sopenharmony_ci							NULL);
168062306a36Sopenharmony_ci	if (!mcastFilterSize_p) {
168162306a36Sopenharmony_ci		dev_err(&dev->dev, "Can't find VETH_MCAST_FILTER_SIZE "
168262306a36Sopenharmony_ci			"attribute\n");
168362306a36Sopenharmony_ci		return -EINVAL;
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	netdev = alloc_etherdev_mqs(sizeof(struct ibmveth_adapter), IBMVETH_MAX_QUEUES, 1);
168762306a36Sopenharmony_ci	if (!netdev)
168862306a36Sopenharmony_ci		return -ENOMEM;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	adapter = netdev_priv(netdev);
169162306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, netdev);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	adapter->vdev = dev;
169462306a36Sopenharmony_ci	adapter->netdev = netdev;
169562306a36Sopenharmony_ci	adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p);
169662306a36Sopenharmony_ci	ibmveth_init_link_settings(netdev);
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	netif_napi_add_weight(netdev, &adapter->napi, ibmveth_poll, 16);
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	netdev->irq = dev->irq;
170162306a36Sopenharmony_ci	netdev->netdev_ops = &ibmveth_netdev_ops;
170262306a36Sopenharmony_ci	netdev->ethtool_ops = &netdev_ethtool_ops;
170362306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &dev->dev);
170462306a36Sopenharmony_ci	netdev->hw_features = NETIF_F_SG;
170562306a36Sopenharmony_ci	if (vio_get_attribute(dev, "ibm,illan-options", NULL) != NULL) {
170662306a36Sopenharmony_ci		netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
170762306a36Sopenharmony_ci				       NETIF_F_RXCSUM;
170862306a36Sopenharmony_ci	}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	netdev->features |= netdev->hw_features;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	ret = h_illan_attributes(adapter->vdev->unit_address, 0, 0, &ret_attr);
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	/* If running older firmware, TSO should not be enabled by default */
171562306a36Sopenharmony_ci	if (ret == H_SUCCESS && (ret_attr & IBMVETH_ILLAN_LRG_SND_SUPPORT) &&
171662306a36Sopenharmony_ci	    !old_large_send) {
171762306a36Sopenharmony_ci		netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
171862306a36Sopenharmony_ci		netdev->features |= netdev->hw_features;
171962306a36Sopenharmony_ci	} else {
172062306a36Sopenharmony_ci		netdev->hw_features |= NETIF_F_TSO;
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	adapter->is_active_trunk = false;
172462306a36Sopenharmony_ci	if (ret == H_SUCCESS && (ret_attr & IBMVETH_ILLAN_ACTIVE_TRUNK)) {
172562306a36Sopenharmony_ci		adapter->is_active_trunk = true;
172662306a36Sopenharmony_ci		netdev->hw_features |= NETIF_F_FRAGLIST;
172762306a36Sopenharmony_ci		netdev->features |= NETIF_F_FRAGLIST;
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	netdev->min_mtu = IBMVETH_MIN_MTU;
173162306a36Sopenharmony_ci	netdev->max_mtu = ETH_MAX_MTU - IBMVETH_BUFF_OH;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	eth_hw_addr_set(netdev, mac_addr_p);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_CMO))
173662306a36Sopenharmony_ci		memcpy(pool_count, pool_count_cmo, sizeof(pool_count));
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
173962306a36Sopenharmony_ci		struct kobject *kobj = &adapter->rx_buff_pool[i].kobj;
174062306a36Sopenharmony_ci		int error;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci		ibmveth_init_buffer_pool(&adapter->rx_buff_pool[i], i,
174362306a36Sopenharmony_ci					 pool_count[i], pool_size[i],
174462306a36Sopenharmony_ci					 pool_active[i]);
174562306a36Sopenharmony_ci		error = kobject_init_and_add(kobj, &ktype_veth_pool,
174662306a36Sopenharmony_ci					     &dev->dev.kobj, "pool%d", i);
174762306a36Sopenharmony_ci		if (!error)
174862306a36Sopenharmony_ci			kobject_uevent(kobj, KOBJ_ADD);
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	rc = netif_set_real_num_tx_queues(netdev, min(num_online_cpus(),
175262306a36Sopenharmony_ci						      IBMVETH_DEFAULT_QUEUES));
175362306a36Sopenharmony_ci	if (rc) {
175462306a36Sopenharmony_ci		netdev_dbg(netdev, "failed to set number of tx queues rc=%d\n",
175562306a36Sopenharmony_ci			   rc);
175662306a36Sopenharmony_ci		free_netdev(netdev);
175762306a36Sopenharmony_ci		return rc;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci	adapter->tx_ltb_size = PAGE_ALIGN(IBMVETH_MAX_TX_BUF_SIZE);
176062306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_MAX_QUEUES; i++)
176162306a36Sopenharmony_ci		adapter->tx_ltb_ptr[i] = NULL;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	netdev_dbg(netdev, "adapter @ 0x%p\n", adapter);
176462306a36Sopenharmony_ci	netdev_dbg(netdev, "registering netdev...\n");
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	ibmveth_set_features(netdev, netdev->features);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	rc = register_netdev(netdev);
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	if (rc) {
177162306a36Sopenharmony_ci		netdev_dbg(netdev, "failed to register netdev rc=%d\n", rc);
177262306a36Sopenharmony_ci		free_netdev(netdev);
177362306a36Sopenharmony_ci		return rc;
177462306a36Sopenharmony_ci	}
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	netdev_dbg(netdev, "registered\n");
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	return 0;
177962306a36Sopenharmony_ci}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_cistatic void ibmveth_remove(struct vio_dev *dev)
178262306a36Sopenharmony_ci{
178362306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(&dev->dev);
178462306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
178562306a36Sopenharmony_ci	int i;
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
178862306a36Sopenharmony_ci		kobject_put(&adapter->rx_buff_pool[i].kobj);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	unregister_netdev(netdev);
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	free_netdev(netdev);
179362306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
179462306a36Sopenharmony_ci}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_cistatic struct attribute veth_active_attr;
179762306a36Sopenharmony_cistatic struct attribute veth_num_attr;
179862306a36Sopenharmony_cistatic struct attribute veth_size_attr;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic ssize_t veth_pool_show(struct kobject *kobj,
180162306a36Sopenharmony_ci			      struct attribute *attr, char *buf)
180262306a36Sopenharmony_ci{
180362306a36Sopenharmony_ci	struct ibmveth_buff_pool *pool = container_of(kobj,
180462306a36Sopenharmony_ci						      struct ibmveth_buff_pool,
180562306a36Sopenharmony_ci						      kobj);
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	if (attr == &veth_active_attr)
180862306a36Sopenharmony_ci		return sprintf(buf, "%d\n", pool->active);
180962306a36Sopenharmony_ci	else if (attr == &veth_num_attr)
181062306a36Sopenharmony_ci		return sprintf(buf, "%d\n", pool->size);
181162306a36Sopenharmony_ci	else if (attr == &veth_size_attr)
181262306a36Sopenharmony_ci		return sprintf(buf, "%d\n", pool->buff_size);
181362306a36Sopenharmony_ci	return 0;
181462306a36Sopenharmony_ci}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_cistatic ssize_t veth_pool_store(struct kobject *kobj, struct attribute *attr,
181762306a36Sopenharmony_ci			       const char *buf, size_t count)
181862306a36Sopenharmony_ci{
181962306a36Sopenharmony_ci	struct ibmveth_buff_pool *pool = container_of(kobj,
182062306a36Sopenharmony_ci						      struct ibmveth_buff_pool,
182162306a36Sopenharmony_ci						      kobj);
182262306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(kobj_to_dev(kobj->parent));
182362306a36Sopenharmony_ci	struct ibmveth_adapter *adapter = netdev_priv(netdev);
182462306a36Sopenharmony_ci	long value = simple_strtol(buf, NULL, 10);
182562306a36Sopenharmony_ci	long rc;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (attr == &veth_active_attr) {
182862306a36Sopenharmony_ci		if (value && !pool->active) {
182962306a36Sopenharmony_ci			if (netif_running(netdev)) {
183062306a36Sopenharmony_ci				if (ibmveth_alloc_buffer_pool(pool)) {
183162306a36Sopenharmony_ci					netdev_err(netdev,
183262306a36Sopenharmony_ci						   "unable to alloc pool\n");
183362306a36Sopenharmony_ci					return -ENOMEM;
183462306a36Sopenharmony_ci				}
183562306a36Sopenharmony_ci				pool->active = 1;
183662306a36Sopenharmony_ci				ibmveth_close(netdev);
183762306a36Sopenharmony_ci				if ((rc = ibmveth_open(netdev)))
183862306a36Sopenharmony_ci					return rc;
183962306a36Sopenharmony_ci			} else {
184062306a36Sopenharmony_ci				pool->active = 1;
184162306a36Sopenharmony_ci			}
184262306a36Sopenharmony_ci		} else if (!value && pool->active) {
184362306a36Sopenharmony_ci			int mtu = netdev->mtu + IBMVETH_BUFF_OH;
184462306a36Sopenharmony_ci			int i;
184562306a36Sopenharmony_ci			/* Make sure there is a buffer pool with buffers that
184662306a36Sopenharmony_ci			   can hold a packet of the size of the MTU */
184762306a36Sopenharmony_ci			for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
184862306a36Sopenharmony_ci				if (pool == &adapter->rx_buff_pool[i])
184962306a36Sopenharmony_ci					continue;
185062306a36Sopenharmony_ci				if (!adapter->rx_buff_pool[i].active)
185162306a36Sopenharmony_ci					continue;
185262306a36Sopenharmony_ci				if (mtu <= adapter->rx_buff_pool[i].buff_size)
185362306a36Sopenharmony_ci					break;
185462306a36Sopenharmony_ci			}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci			if (i == IBMVETH_NUM_BUFF_POOLS) {
185762306a36Sopenharmony_ci				netdev_err(netdev, "no active pool >= MTU\n");
185862306a36Sopenharmony_ci				return -EPERM;
185962306a36Sopenharmony_ci			}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci			if (netif_running(netdev)) {
186262306a36Sopenharmony_ci				ibmveth_close(netdev);
186362306a36Sopenharmony_ci				pool->active = 0;
186462306a36Sopenharmony_ci				if ((rc = ibmveth_open(netdev)))
186562306a36Sopenharmony_ci					return rc;
186662306a36Sopenharmony_ci			}
186762306a36Sopenharmony_ci			pool->active = 0;
186862306a36Sopenharmony_ci		}
186962306a36Sopenharmony_ci	} else if (attr == &veth_num_attr) {
187062306a36Sopenharmony_ci		if (value <= 0 || value > IBMVETH_MAX_POOL_COUNT) {
187162306a36Sopenharmony_ci			return -EINVAL;
187262306a36Sopenharmony_ci		} else {
187362306a36Sopenharmony_ci			if (netif_running(netdev)) {
187462306a36Sopenharmony_ci				ibmveth_close(netdev);
187562306a36Sopenharmony_ci				pool->size = value;
187662306a36Sopenharmony_ci				if ((rc = ibmveth_open(netdev)))
187762306a36Sopenharmony_ci					return rc;
187862306a36Sopenharmony_ci			} else {
187962306a36Sopenharmony_ci				pool->size = value;
188062306a36Sopenharmony_ci			}
188162306a36Sopenharmony_ci		}
188262306a36Sopenharmony_ci	} else if (attr == &veth_size_attr) {
188362306a36Sopenharmony_ci		if (value <= IBMVETH_BUFF_OH || value > IBMVETH_MAX_BUF_SIZE) {
188462306a36Sopenharmony_ci			return -EINVAL;
188562306a36Sopenharmony_ci		} else {
188662306a36Sopenharmony_ci			if (netif_running(netdev)) {
188762306a36Sopenharmony_ci				ibmveth_close(netdev);
188862306a36Sopenharmony_ci				pool->buff_size = value;
188962306a36Sopenharmony_ci				if ((rc = ibmveth_open(netdev)))
189062306a36Sopenharmony_ci					return rc;
189162306a36Sopenharmony_ci			} else {
189262306a36Sopenharmony_ci				pool->buff_size = value;
189362306a36Sopenharmony_ci			}
189462306a36Sopenharmony_ci		}
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/* kick the interrupt handler to allocate/deallocate pools */
189862306a36Sopenharmony_ci	ibmveth_interrupt(netdev->irq, netdev);
189962306a36Sopenharmony_ci	return count;
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci#define ATTR(_name, _mode)				\
190462306a36Sopenharmony_ci	struct attribute veth_##_name##_attr = {	\
190562306a36Sopenharmony_ci	.name = __stringify(_name), .mode = _mode,	\
190662306a36Sopenharmony_ci	};
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_cistatic ATTR(active, 0644);
190962306a36Sopenharmony_cistatic ATTR(num, 0644);
191062306a36Sopenharmony_cistatic ATTR(size, 0644);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_cistatic struct attribute *veth_pool_attrs[] = {
191362306a36Sopenharmony_ci	&veth_active_attr,
191462306a36Sopenharmony_ci	&veth_num_attr,
191562306a36Sopenharmony_ci	&veth_size_attr,
191662306a36Sopenharmony_ci	NULL,
191762306a36Sopenharmony_ci};
191862306a36Sopenharmony_ciATTRIBUTE_GROUPS(veth_pool);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_cistatic const struct sysfs_ops veth_pool_ops = {
192162306a36Sopenharmony_ci	.show   = veth_pool_show,
192262306a36Sopenharmony_ci	.store  = veth_pool_store,
192362306a36Sopenharmony_ci};
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_cistatic struct kobj_type ktype_veth_pool = {
192662306a36Sopenharmony_ci	.release        = NULL,
192762306a36Sopenharmony_ci	.sysfs_ops      = &veth_pool_ops,
192862306a36Sopenharmony_ci	.default_groups = veth_pool_groups,
192962306a36Sopenharmony_ci};
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic int ibmveth_resume(struct device *dev)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(dev);
193462306a36Sopenharmony_ci	ibmveth_interrupt(netdev->irq, netdev);
193562306a36Sopenharmony_ci	return 0;
193662306a36Sopenharmony_ci}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_cistatic const struct vio_device_id ibmveth_device_table[] = {
193962306a36Sopenharmony_ci	{ "network", "IBM,l-lan"},
194062306a36Sopenharmony_ci	{ "", "" }
194162306a36Sopenharmony_ci};
194262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vio, ibmveth_device_table);
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_cistatic const struct dev_pm_ops ibmveth_pm_ops = {
194562306a36Sopenharmony_ci	.resume = ibmveth_resume
194662306a36Sopenharmony_ci};
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_cistatic struct vio_driver ibmveth_driver = {
194962306a36Sopenharmony_ci	.id_table	= ibmveth_device_table,
195062306a36Sopenharmony_ci	.probe		= ibmveth_probe,
195162306a36Sopenharmony_ci	.remove		= ibmveth_remove,
195262306a36Sopenharmony_ci	.get_desired_dma = ibmveth_get_desired_dma,
195362306a36Sopenharmony_ci	.name		= ibmveth_driver_name,
195462306a36Sopenharmony_ci	.pm		= &ibmveth_pm_ops,
195562306a36Sopenharmony_ci};
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_cistatic int __init ibmveth_module_init(void)
195862306a36Sopenharmony_ci{
195962306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: %s %s\n", ibmveth_driver_name,
196062306a36Sopenharmony_ci	       ibmveth_driver_string, ibmveth_driver_version);
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	return vio_register_driver(&ibmveth_driver);
196362306a36Sopenharmony_ci}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_cistatic void __exit ibmveth_module_exit(void)
196662306a36Sopenharmony_ci{
196762306a36Sopenharmony_ci	vio_unregister_driver(&ibmveth_driver);
196862306a36Sopenharmony_ci}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_cimodule_init(ibmveth_module_init);
197162306a36Sopenharmony_cimodule_exit(ibmveth_module_exit);
1972