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