162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/**************************************************************************/ 362306a36Sopenharmony_ci/* */ 462306a36Sopenharmony_ci/* IBM System i and System p Virtual NIC Device Driver */ 562306a36Sopenharmony_ci/* Copyright (C) 2014 IBM Corp. */ 662306a36Sopenharmony_ci/* Santiago Leon (santi_leon@yahoo.com) */ 762306a36Sopenharmony_ci/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */ 862306a36Sopenharmony_ci/* John Allen (jallen@linux.vnet.ibm.com) */ 962306a36Sopenharmony_ci/* */ 1062306a36Sopenharmony_ci/* */ 1162306a36Sopenharmony_ci/* This module contains the implementation of a virtual ethernet device */ 1262306a36Sopenharmony_ci/* for use with IBM i/p Series LPAR Linux. It utilizes the logical LAN */ 1362306a36Sopenharmony_ci/* option of the RS/6000 Platform Architecture to interface with virtual */ 1462306a36Sopenharmony_ci/* ethernet NICs that are presented to the partition by the hypervisor. */ 1562306a36Sopenharmony_ci/* */ 1662306a36Sopenharmony_ci/* Messages are passed between the VNIC driver and the VNIC server using */ 1762306a36Sopenharmony_ci/* Command/Response Queues (CRQs) and sub CRQs (sCRQs). CRQs are used to */ 1862306a36Sopenharmony_ci/* issue and receive commands that initiate communication with the server */ 1962306a36Sopenharmony_ci/* on driver initialization. Sub CRQs (sCRQs) are similar to CRQs, but */ 2062306a36Sopenharmony_ci/* are used by the driver to notify the server that a packet is */ 2162306a36Sopenharmony_ci/* ready for transmission or that a buffer has been added to receive a */ 2262306a36Sopenharmony_ci/* packet. Subsequently, sCRQs are used by the server to notify the */ 2362306a36Sopenharmony_ci/* driver that a packet transmission has been completed or that a packet */ 2462306a36Sopenharmony_ci/* has been received and placed in a waiting buffer. */ 2562306a36Sopenharmony_ci/* */ 2662306a36Sopenharmony_ci/* In lieu of a more conventional "on-the-fly" DMA mapping strategy in */ 2762306a36Sopenharmony_ci/* which skbs are DMA mapped and immediately unmapped when the transmit */ 2862306a36Sopenharmony_ci/* or receive has been completed, the VNIC driver is required to use */ 2962306a36Sopenharmony_ci/* "long term mapping". This entails that large, continuous DMA mapped */ 3062306a36Sopenharmony_ci/* buffers are allocated on driver initialization and these buffers are */ 3162306a36Sopenharmony_ci/* then continuously reused to pass skbs to and from the VNIC server. */ 3262306a36Sopenharmony_ci/* */ 3362306a36Sopenharmony_ci/**************************************************************************/ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/moduleparam.h> 3762306a36Sopenharmony_ci#include <linux/types.h> 3862306a36Sopenharmony_ci#include <linux/errno.h> 3962306a36Sopenharmony_ci#include <linux/completion.h> 4062306a36Sopenharmony_ci#include <linux/ioport.h> 4162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 4262306a36Sopenharmony_ci#include <linux/kernel.h> 4362306a36Sopenharmony_ci#include <linux/netdevice.h> 4462306a36Sopenharmony_ci#include <linux/etherdevice.h> 4562306a36Sopenharmony_ci#include <linux/skbuff.h> 4662306a36Sopenharmony_ci#include <linux/init.h> 4762306a36Sopenharmony_ci#include <linux/delay.h> 4862306a36Sopenharmony_ci#include <linux/mm.h> 4962306a36Sopenharmony_ci#include <linux/ethtool.h> 5062306a36Sopenharmony_ci#include <linux/proc_fs.h> 5162306a36Sopenharmony_ci#include <linux/if_arp.h> 5262306a36Sopenharmony_ci#include <linux/in.h> 5362306a36Sopenharmony_ci#include <linux/ip.h> 5462306a36Sopenharmony_ci#include <linux/ipv6.h> 5562306a36Sopenharmony_ci#include <linux/irq.h> 5662306a36Sopenharmony_ci#include <linux/irqdomain.h> 5762306a36Sopenharmony_ci#include <linux/kthread.h> 5862306a36Sopenharmony_ci#include <linux/seq_file.h> 5962306a36Sopenharmony_ci#include <linux/interrupt.h> 6062306a36Sopenharmony_ci#include <net/net_namespace.h> 6162306a36Sopenharmony_ci#include <asm/hvcall.h> 6262306a36Sopenharmony_ci#include <linux/atomic.h> 6362306a36Sopenharmony_ci#include <asm/vio.h> 6462306a36Sopenharmony_ci#include <asm/xive.h> 6562306a36Sopenharmony_ci#include <asm/iommu.h> 6662306a36Sopenharmony_ci#include <linux/uaccess.h> 6762306a36Sopenharmony_ci#include <asm/firmware.h> 6862306a36Sopenharmony_ci#include <linux/workqueue.h> 6962306a36Sopenharmony_ci#include <linux/if_vlan.h> 7062306a36Sopenharmony_ci#include <linux/utsname.h> 7162306a36Sopenharmony_ci#include <linux/cpu.h> 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#include "ibmvnic.h" 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const char ibmvnic_driver_name[] = "ibmvnic"; 7662306a36Sopenharmony_cistatic const char ibmvnic_driver_string[] = "IBM System i/p Virtual NIC Driver"; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciMODULE_AUTHOR("Santiago Leon"); 7962306a36Sopenharmony_ciMODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver"); 8062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8162306a36Sopenharmony_ciMODULE_VERSION(IBMVNIC_DRIVER_VERSION); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int ibmvnic_version = IBMVNIC_INITIAL_VERSION; 8462306a36Sopenharmony_cistatic void release_sub_crqs(struct ibmvnic_adapter *, bool); 8562306a36Sopenharmony_cistatic int ibmvnic_reset_crq(struct ibmvnic_adapter *); 8662306a36Sopenharmony_cistatic int ibmvnic_send_crq_init(struct ibmvnic_adapter *); 8762306a36Sopenharmony_cistatic int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *); 8862306a36Sopenharmony_cistatic int ibmvnic_send_crq(struct ibmvnic_adapter *, union ibmvnic_crq *); 8962306a36Sopenharmony_cistatic int send_subcrq_indirect(struct ibmvnic_adapter *, u64, u64, u64); 9062306a36Sopenharmony_cistatic irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance); 9162306a36Sopenharmony_cistatic int enable_scrq_irq(struct ibmvnic_adapter *, 9262306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *); 9362306a36Sopenharmony_cistatic int disable_scrq_irq(struct ibmvnic_adapter *, 9462306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *); 9562306a36Sopenharmony_cistatic int pending_scrq(struct ibmvnic_adapter *, 9662306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *); 9762306a36Sopenharmony_cistatic union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *, 9862306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *); 9962306a36Sopenharmony_cistatic int ibmvnic_poll(struct napi_struct *napi, int data); 10062306a36Sopenharmony_cistatic int reset_sub_crq_queues(struct ibmvnic_adapter *adapter); 10162306a36Sopenharmony_cistatic inline void reinit_init_done(struct ibmvnic_adapter *adapter); 10262306a36Sopenharmony_cistatic void send_query_map(struct ibmvnic_adapter *adapter); 10362306a36Sopenharmony_cistatic int send_request_map(struct ibmvnic_adapter *, dma_addr_t, u32, u8); 10462306a36Sopenharmony_cistatic int send_request_unmap(struct ibmvnic_adapter *, u8); 10562306a36Sopenharmony_cistatic int send_login(struct ibmvnic_adapter *adapter); 10662306a36Sopenharmony_cistatic void send_query_cap(struct ibmvnic_adapter *adapter); 10762306a36Sopenharmony_cistatic int init_sub_crqs(struct ibmvnic_adapter *); 10862306a36Sopenharmony_cistatic int init_sub_crq_irqs(struct ibmvnic_adapter *adapter); 10962306a36Sopenharmony_cistatic int ibmvnic_reset_init(struct ibmvnic_adapter *, bool reset); 11062306a36Sopenharmony_cistatic void release_crq_queue(struct ibmvnic_adapter *); 11162306a36Sopenharmony_cistatic int __ibmvnic_set_mac(struct net_device *, u8 *); 11262306a36Sopenharmony_cistatic int init_crq_queue(struct ibmvnic_adapter *adapter); 11362306a36Sopenharmony_cistatic int send_query_phys_parms(struct ibmvnic_adapter *adapter); 11462306a36Sopenharmony_cistatic void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter, 11562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *tx_scrq); 11662306a36Sopenharmony_cistatic void free_long_term_buff(struct ibmvnic_adapter *adapter, 11762306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb); 11862306a36Sopenharmony_cistatic void ibmvnic_disable_irqs(struct ibmvnic_adapter *adapter); 11962306a36Sopenharmony_cistatic void flush_reset_queue(struct ibmvnic_adapter *adapter); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistruct ibmvnic_stat { 12262306a36Sopenharmony_ci char name[ETH_GSTRING_LEN]; 12362306a36Sopenharmony_ci int offset; 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define IBMVNIC_STAT_OFF(stat) (offsetof(struct ibmvnic_adapter, stats) + \ 12762306a36Sopenharmony_ci offsetof(struct ibmvnic_statistics, stat)) 12862306a36Sopenharmony_ci#define IBMVNIC_GET_STAT(a, off) (*((u64 *)(((unsigned long)(a)) + (off)))) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct ibmvnic_stat ibmvnic_stats[] = { 13162306a36Sopenharmony_ci {"rx_packets", IBMVNIC_STAT_OFF(rx_packets)}, 13262306a36Sopenharmony_ci {"rx_bytes", IBMVNIC_STAT_OFF(rx_bytes)}, 13362306a36Sopenharmony_ci {"tx_packets", IBMVNIC_STAT_OFF(tx_packets)}, 13462306a36Sopenharmony_ci {"tx_bytes", IBMVNIC_STAT_OFF(tx_bytes)}, 13562306a36Sopenharmony_ci {"ucast_tx_packets", IBMVNIC_STAT_OFF(ucast_tx_packets)}, 13662306a36Sopenharmony_ci {"ucast_rx_packets", IBMVNIC_STAT_OFF(ucast_rx_packets)}, 13762306a36Sopenharmony_ci {"mcast_tx_packets", IBMVNIC_STAT_OFF(mcast_tx_packets)}, 13862306a36Sopenharmony_ci {"mcast_rx_packets", IBMVNIC_STAT_OFF(mcast_rx_packets)}, 13962306a36Sopenharmony_ci {"bcast_tx_packets", IBMVNIC_STAT_OFF(bcast_tx_packets)}, 14062306a36Sopenharmony_ci {"bcast_rx_packets", IBMVNIC_STAT_OFF(bcast_rx_packets)}, 14162306a36Sopenharmony_ci {"align_errors", IBMVNIC_STAT_OFF(align_errors)}, 14262306a36Sopenharmony_ci {"fcs_errors", IBMVNIC_STAT_OFF(fcs_errors)}, 14362306a36Sopenharmony_ci {"single_collision_frames", IBMVNIC_STAT_OFF(single_collision_frames)}, 14462306a36Sopenharmony_ci {"multi_collision_frames", IBMVNIC_STAT_OFF(multi_collision_frames)}, 14562306a36Sopenharmony_ci {"sqe_test_errors", IBMVNIC_STAT_OFF(sqe_test_errors)}, 14662306a36Sopenharmony_ci {"deferred_tx", IBMVNIC_STAT_OFF(deferred_tx)}, 14762306a36Sopenharmony_ci {"late_collisions", IBMVNIC_STAT_OFF(late_collisions)}, 14862306a36Sopenharmony_ci {"excess_collisions", IBMVNIC_STAT_OFF(excess_collisions)}, 14962306a36Sopenharmony_ci {"internal_mac_tx_errors", IBMVNIC_STAT_OFF(internal_mac_tx_errors)}, 15062306a36Sopenharmony_ci {"carrier_sense", IBMVNIC_STAT_OFF(carrier_sense)}, 15162306a36Sopenharmony_ci {"too_long_frames", IBMVNIC_STAT_OFF(too_long_frames)}, 15262306a36Sopenharmony_ci {"internal_mac_rx_errors", IBMVNIC_STAT_OFF(internal_mac_rx_errors)}, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int send_crq_init_complete(struct ibmvnic_adapter *adapter) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci union ibmvnic_crq crq; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 16062306a36Sopenharmony_ci crq.generic.first = IBMVNIC_CRQ_INIT_CMD; 16162306a36Sopenharmony_ci crq.generic.cmd = IBMVNIC_CRQ_INIT_COMPLETE; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return ibmvnic_send_crq(adapter, &crq); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int send_version_xchg(struct ibmvnic_adapter *adapter) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci union ibmvnic_crq crq; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 17162306a36Sopenharmony_ci crq.version_exchange.first = IBMVNIC_CRQ_CMD; 17262306a36Sopenharmony_ci crq.version_exchange.cmd = VERSION_EXCHANGE; 17362306a36Sopenharmony_ci crq.version_exchange.version = cpu_to_be16(ibmvnic_version); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return ibmvnic_send_crq(adapter, &crq); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void ibmvnic_clean_queue_affinity(struct ibmvnic_adapter *adapter, 17962306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *queue) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (!(queue && queue->irq)) 18262306a36Sopenharmony_ci return; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci cpumask_clear(queue->affinity_mask); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (irq_set_affinity_and_hint(queue->irq, NULL)) 18762306a36Sopenharmony_ci netdev_warn(adapter->netdev, 18862306a36Sopenharmony_ci "%s: Clear affinity failed, queue addr = %p, IRQ = %d\n", 18962306a36Sopenharmony_ci __func__, queue, queue->irq); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void ibmvnic_clean_affinity(struct ibmvnic_adapter *adapter) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue **rxqs; 19562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue **txqs; 19662306a36Sopenharmony_ci int num_rxqs, num_txqs; 19762306a36Sopenharmony_ci int i; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci rxqs = adapter->rx_scrq; 20062306a36Sopenharmony_ci txqs = adapter->tx_scrq; 20162306a36Sopenharmony_ci num_txqs = adapter->num_active_tx_scrqs; 20262306a36Sopenharmony_ci num_rxqs = adapter->num_active_rx_scrqs; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "%s: Cleaning irq affinity hints", __func__); 20562306a36Sopenharmony_ci if (txqs) { 20662306a36Sopenharmony_ci for (i = 0; i < num_txqs; i++) 20762306a36Sopenharmony_ci ibmvnic_clean_queue_affinity(adapter, txqs[i]); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci if (rxqs) { 21062306a36Sopenharmony_ci for (i = 0; i < num_rxqs; i++) 21162306a36Sopenharmony_ci ibmvnic_clean_queue_affinity(adapter, rxqs[i]); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int ibmvnic_set_queue_affinity(struct ibmvnic_sub_crq_queue *queue, 21662306a36Sopenharmony_ci unsigned int *cpu, int *stragglers, 21762306a36Sopenharmony_ci int stride) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci cpumask_var_t mask; 22062306a36Sopenharmony_ci int i; 22162306a36Sopenharmony_ci int rc = 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!(queue && queue->irq)) 22462306a36Sopenharmony_ci return rc; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* cpumask_var_t is either a pointer or array, allocation works here */ 22762306a36Sopenharmony_ci if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* while we have extra cpu give one extra to this irq */ 23162306a36Sopenharmony_ci if (*stragglers) { 23262306a36Sopenharmony_ci stride++; 23362306a36Sopenharmony_ci (*stragglers)--; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci /* atomic write is safer than writing bit by bit directly */ 23662306a36Sopenharmony_ci for (i = 0; i < stride; i++) { 23762306a36Sopenharmony_ci cpumask_set_cpu(*cpu, mask); 23862306a36Sopenharmony_ci *cpu = cpumask_next_wrap(*cpu, cpu_online_mask, 23962306a36Sopenharmony_ci nr_cpu_ids, false); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci /* set queue affinity mask */ 24262306a36Sopenharmony_ci cpumask_copy(queue->affinity_mask, mask); 24362306a36Sopenharmony_ci rc = irq_set_affinity_and_hint(queue->irq, queue->affinity_mask); 24462306a36Sopenharmony_ci free_cpumask_var(mask); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return rc; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* assumes cpu read lock is held */ 25062306a36Sopenharmony_cistatic void ibmvnic_set_affinity(struct ibmvnic_adapter *adapter) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue **rxqs = adapter->rx_scrq; 25362306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue **txqs = adapter->tx_scrq; 25462306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *queue; 25562306a36Sopenharmony_ci int num_rxqs = adapter->num_active_rx_scrqs, i_rxqs = 0; 25662306a36Sopenharmony_ci int num_txqs = adapter->num_active_tx_scrqs, i_txqs = 0; 25762306a36Sopenharmony_ci int total_queues, stride, stragglers, i; 25862306a36Sopenharmony_ci unsigned int num_cpu, cpu; 25962306a36Sopenharmony_ci bool is_rx_queue; 26062306a36Sopenharmony_ci int rc = 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "%s: Setting irq affinity hints", __func__); 26362306a36Sopenharmony_ci if (!(adapter->rx_scrq && adapter->tx_scrq)) { 26462306a36Sopenharmony_ci netdev_warn(adapter->netdev, 26562306a36Sopenharmony_ci "%s: Set affinity failed, queues not allocated\n", 26662306a36Sopenharmony_ci __func__); 26762306a36Sopenharmony_ci return; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci total_queues = num_rxqs + num_txqs; 27162306a36Sopenharmony_ci num_cpu = num_online_cpus(); 27262306a36Sopenharmony_ci /* number of cpu's assigned per irq */ 27362306a36Sopenharmony_ci stride = max_t(int, num_cpu / total_queues, 1); 27462306a36Sopenharmony_ci /* number of leftover cpu's */ 27562306a36Sopenharmony_ci stragglers = num_cpu >= total_queues ? num_cpu % total_queues : 0; 27662306a36Sopenharmony_ci /* next available cpu to assign irq to */ 27762306a36Sopenharmony_ci cpu = cpumask_next(-1, cpu_online_mask); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci for (i = 0; i < total_queues; i++) { 28062306a36Sopenharmony_ci is_rx_queue = false; 28162306a36Sopenharmony_ci /* balance core load by alternating rx and tx assignments 28262306a36Sopenharmony_ci * ex: TX0 -> RX0 -> TX1 -> RX1 etc. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if ((i % 2 == 1 && i_rxqs < num_rxqs) || i_txqs == num_txqs) { 28562306a36Sopenharmony_ci queue = rxqs[i_rxqs++]; 28662306a36Sopenharmony_ci is_rx_queue = true; 28762306a36Sopenharmony_ci } else { 28862306a36Sopenharmony_ci queue = txqs[i_txqs++]; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci rc = ibmvnic_set_queue_affinity(queue, &cpu, &stragglers, 29262306a36Sopenharmony_ci stride); 29362306a36Sopenharmony_ci if (rc) 29462306a36Sopenharmony_ci goto out; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (!queue || is_rx_queue) 29762306a36Sopenharmony_ci continue; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci rc = __netif_set_xps_queue(adapter->netdev, 30062306a36Sopenharmony_ci cpumask_bits(queue->affinity_mask), 30162306a36Sopenharmony_ci i_txqs - 1, XPS_CPUS); 30262306a36Sopenharmony_ci if (rc) 30362306a36Sopenharmony_ci netdev_warn(adapter->netdev, "%s: Set XPS on queue %d failed, rc = %d.\n", 30462306a36Sopenharmony_ci __func__, i_txqs - 1, rc); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciout: 30862306a36Sopenharmony_ci if (rc) { 30962306a36Sopenharmony_ci netdev_warn(adapter->netdev, 31062306a36Sopenharmony_ci "%s: Set affinity failed, queue addr = %p, IRQ = %d, rc = %d.\n", 31162306a36Sopenharmony_ci __func__, queue, queue->irq, rc); 31262306a36Sopenharmony_ci ibmvnic_clean_affinity(adapter); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int ibmvnic_cpu_online(unsigned int cpu, struct hlist_node *node) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node); 32162306a36Sopenharmony_ci ibmvnic_set_affinity(adapter); 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int ibmvnic_cpu_dead(unsigned int cpu, struct hlist_node *node) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node_dead); 33062306a36Sopenharmony_ci ibmvnic_set_affinity(adapter); 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int ibmvnic_cpu_down_prep(unsigned int cpu, struct hlist_node *node) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node); 33962306a36Sopenharmony_ci ibmvnic_clean_affinity(adapter); 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic enum cpuhp_state ibmvnic_online; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int ibmvnic_cpu_notif_add(struct ibmvnic_adapter *adapter) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(ibmvnic_online, &adapter->node); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(CPUHP_IBMVNIC_DEAD, 35362306a36Sopenharmony_ci &adapter->node_dead); 35462306a36Sopenharmony_ci if (!ret) 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(ibmvnic_online, &adapter->node); 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void ibmvnic_cpu_notif_remove(struct ibmvnic_adapter *adapter) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(ibmvnic_online, &adapter->node); 36362306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(CPUHP_IBMVNIC_DEAD, 36462306a36Sopenharmony_ci &adapter->node_dead); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic long h_reg_sub_crq(unsigned long unit_address, unsigned long token, 36862306a36Sopenharmony_ci unsigned long length, unsigned long *number, 36962306a36Sopenharmony_ci unsigned long *irq) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; 37262306a36Sopenharmony_ci long rc; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, token, length); 37562306a36Sopenharmony_ci *number = retbuf[0]; 37662306a36Sopenharmony_ci *irq = retbuf[1]; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return rc; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/** 38262306a36Sopenharmony_ci * ibmvnic_wait_for_completion - Check device state and wait for completion 38362306a36Sopenharmony_ci * @adapter: private device data 38462306a36Sopenharmony_ci * @comp_done: completion structure to wait for 38562306a36Sopenharmony_ci * @timeout: time to wait in milliseconds 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Wait for a completion signal or until the timeout limit is reached 38862306a36Sopenharmony_ci * while checking that the device is still active. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic int ibmvnic_wait_for_completion(struct ibmvnic_adapter *adapter, 39162306a36Sopenharmony_ci struct completion *comp_done, 39262306a36Sopenharmony_ci unsigned long timeout) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct net_device *netdev; 39562306a36Sopenharmony_ci unsigned long div_timeout; 39662306a36Sopenharmony_ci u8 retry; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci netdev = adapter->netdev; 39962306a36Sopenharmony_ci retry = 5; 40062306a36Sopenharmony_ci div_timeout = msecs_to_jiffies(timeout / retry); 40162306a36Sopenharmony_ci while (true) { 40262306a36Sopenharmony_ci if (!adapter->crq.active) { 40362306a36Sopenharmony_ci netdev_err(netdev, "Device down!\n"); 40462306a36Sopenharmony_ci return -ENODEV; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci if (!retry--) 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci if (wait_for_completion_timeout(comp_done, div_timeout)) 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci netdev_err(netdev, "Operation timed out.\n"); 41262306a36Sopenharmony_ci return -ETIMEDOUT; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/** 41662306a36Sopenharmony_ci * reuse_ltb() - Check if a long term buffer can be reused 41762306a36Sopenharmony_ci * @ltb: The long term buffer to be checked 41862306a36Sopenharmony_ci * @size: The size of the long term buffer. 41962306a36Sopenharmony_ci * 42062306a36Sopenharmony_ci * An LTB can be reused unless its size has changed. 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Return: Return true if the LTB can be reused, false otherwise. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic bool reuse_ltb(struct ibmvnic_long_term_buff *ltb, int size) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci return (ltb->buff && ltb->size == size); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * alloc_long_term_buff() - Allocate a long term buffer (LTB) 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * @adapter: ibmvnic adapter associated to the LTB 43362306a36Sopenharmony_ci * @ltb: container object for the LTB 43462306a36Sopenharmony_ci * @size: size of the LTB 43562306a36Sopenharmony_ci * 43662306a36Sopenharmony_ci * Allocate an LTB of the specified size and notify VIOS. 43762306a36Sopenharmony_ci * 43862306a36Sopenharmony_ci * If the given @ltb already has the correct size, reuse it. Otherwise if 43962306a36Sopenharmony_ci * its non-NULL, free it. Then allocate a new one of the correct size. 44062306a36Sopenharmony_ci * Notify the VIOS either way since we may now be working with a new VIOS. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Allocating larger chunks of memory during resets, specially LPM or under 44362306a36Sopenharmony_ci * low memory situations can cause resets to fail/timeout and for LPAR to 44462306a36Sopenharmony_ci * lose connectivity. So hold onto the LTB even if we fail to communicate 44562306a36Sopenharmony_ci * with the VIOS and reuse it on next open. Free LTB when adapter is closed. 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Return: 0 if we were able to allocate the LTB and notify the VIOS and 44862306a36Sopenharmony_ci * a negative value otherwise. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_cistatic int alloc_long_term_buff(struct ibmvnic_adapter *adapter, 45162306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb, int size) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 45462306a36Sopenharmony_ci u64 prev = 0; 45562306a36Sopenharmony_ci int rc; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!reuse_ltb(ltb, size)) { 45862306a36Sopenharmony_ci dev_dbg(dev, 45962306a36Sopenharmony_ci "LTB size changed from 0x%llx to 0x%x, reallocating\n", 46062306a36Sopenharmony_ci ltb->size, size); 46162306a36Sopenharmony_ci prev = ltb->size; 46262306a36Sopenharmony_ci free_long_term_buff(adapter, ltb); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (ltb->buff) { 46662306a36Sopenharmony_ci dev_dbg(dev, "Reusing LTB [map %d, size 0x%llx]\n", 46762306a36Sopenharmony_ci ltb->map_id, ltb->size); 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci ltb->buff = dma_alloc_coherent(dev, size, <b->addr, 47062306a36Sopenharmony_ci GFP_KERNEL); 47162306a36Sopenharmony_ci if (!ltb->buff) { 47262306a36Sopenharmony_ci dev_err(dev, "Couldn't alloc long term buffer\n"); 47362306a36Sopenharmony_ci return -ENOMEM; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci ltb->size = size; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci ltb->map_id = find_first_zero_bit(adapter->map_ids, 47862306a36Sopenharmony_ci MAX_MAP_ID); 47962306a36Sopenharmony_ci bitmap_set(adapter->map_ids, ltb->map_id, 1); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci dev_dbg(dev, 48262306a36Sopenharmony_ci "Allocated new LTB [map %d, size 0x%llx was 0x%llx]\n", 48362306a36Sopenharmony_ci ltb->map_id, ltb->size, prev); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Ensure ltb is zeroed - specially when reusing it. */ 48762306a36Sopenharmony_ci memset(ltb->buff, 0, ltb->size); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci mutex_lock(&adapter->fw_lock); 49062306a36Sopenharmony_ci adapter->fw_done_rc = 0; 49162306a36Sopenharmony_ci reinit_completion(&adapter->fw_done); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci rc = send_request_map(adapter, ltb->addr, ltb->size, ltb->map_id); 49462306a36Sopenharmony_ci if (rc) { 49562306a36Sopenharmony_ci dev_err(dev, "send_request_map failed, rc = %d\n", rc); 49662306a36Sopenharmony_ci goto out; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); 50062306a36Sopenharmony_ci if (rc) { 50162306a36Sopenharmony_ci dev_err(dev, "LTB map request aborted or timed out, rc = %d\n", 50262306a36Sopenharmony_ci rc); 50362306a36Sopenharmony_ci goto out; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (adapter->fw_done_rc) { 50762306a36Sopenharmony_ci dev_err(dev, "Couldn't map LTB, rc = %d\n", 50862306a36Sopenharmony_ci adapter->fw_done_rc); 50962306a36Sopenharmony_ci rc = -EIO; 51062306a36Sopenharmony_ci goto out; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci rc = 0; 51362306a36Sopenharmony_ciout: 51462306a36Sopenharmony_ci /* don't free LTB on communication error - see function header */ 51562306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 51662306a36Sopenharmony_ci return rc; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void free_long_term_buff(struct ibmvnic_adapter *adapter, 52062306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!ltb->buff) 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* VIOS automatically unmaps the long term buffer at remote 52862306a36Sopenharmony_ci * end for the following resets: 52962306a36Sopenharmony_ci * FAILOVER, MOBILITY, TIMEOUT. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci if (adapter->reset_reason != VNIC_RESET_FAILOVER && 53262306a36Sopenharmony_ci adapter->reset_reason != VNIC_RESET_MOBILITY && 53362306a36Sopenharmony_ci adapter->reset_reason != VNIC_RESET_TIMEOUT) 53462306a36Sopenharmony_ci send_request_unmap(adapter, ltb->map_id); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ltb->buff = NULL; 53962306a36Sopenharmony_ci /* mark this map_id free */ 54062306a36Sopenharmony_ci bitmap_clear(adapter->map_ids, ltb->map_id, 1); 54162306a36Sopenharmony_ci ltb->map_id = 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/** 54562306a36Sopenharmony_ci * free_ltb_set - free the given set of long term buffers (LTBS) 54662306a36Sopenharmony_ci * @adapter: The ibmvnic adapter containing this ltb set 54762306a36Sopenharmony_ci * @ltb_set: The ltb_set to be freed 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * Free the set of LTBs in the given set. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void free_ltb_set(struct ibmvnic_adapter *adapter, 55362306a36Sopenharmony_ci struct ibmvnic_ltb_set *ltb_set) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci int i; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci for (i = 0; i < ltb_set->num_ltbs; i++) 55862306a36Sopenharmony_ci free_long_term_buff(adapter, <b_set->ltbs[i]); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci kfree(ltb_set->ltbs); 56162306a36Sopenharmony_ci ltb_set->ltbs = NULL; 56262306a36Sopenharmony_ci ltb_set->num_ltbs = 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/** 56662306a36Sopenharmony_ci * alloc_ltb_set() - Allocate a set of long term buffers (LTBs) 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * @adapter: ibmvnic adapter associated to the LTB 56962306a36Sopenharmony_ci * @ltb_set: container object for the set of LTBs 57062306a36Sopenharmony_ci * @num_buffs: Number of buffers in the LTB 57162306a36Sopenharmony_ci * @buff_size: Size of each buffer in the LTB 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * Allocate a set of LTBs to accommodate @num_buffs buffers of @buff_size 57462306a36Sopenharmony_ci * each. We currently cap size each LTB to IBMVNIC_ONE_LTB_SIZE. If the 57562306a36Sopenharmony_ci * new set of LTBs have fewer LTBs than the old set, free the excess LTBs. 57662306a36Sopenharmony_ci * If new set needs more than in old set, allocate the remaining ones. 57762306a36Sopenharmony_ci * Try and reuse as many LTBs as possible and avoid reallocation. 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * Any changes to this allocation strategy must be reflected in 58062306a36Sopenharmony_ci * map_rxpool_buff_to_ltb() and map_txpool_buff_to_ltb(). 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic int alloc_ltb_set(struct ibmvnic_adapter *adapter, 58362306a36Sopenharmony_ci struct ibmvnic_ltb_set *ltb_set, int num_buffs, 58462306a36Sopenharmony_ci int buff_size) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 58762306a36Sopenharmony_ci struct ibmvnic_ltb_set old_set; 58862306a36Sopenharmony_ci struct ibmvnic_ltb_set new_set; 58962306a36Sopenharmony_ci int rem_size; 59062306a36Sopenharmony_ci int tot_size; /* size of all ltbs */ 59162306a36Sopenharmony_ci int ltb_size; /* size of one ltb */ 59262306a36Sopenharmony_ci int nltbs; 59362306a36Sopenharmony_ci int rc; 59462306a36Sopenharmony_ci int n; 59562306a36Sopenharmony_ci int i; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci dev_dbg(dev, "%s() num_buffs %d, buff_size %d\n", __func__, num_buffs, 59862306a36Sopenharmony_ci buff_size); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci ltb_size = rounddown(IBMVNIC_ONE_LTB_SIZE, buff_size); 60162306a36Sopenharmony_ci tot_size = num_buffs * buff_size; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (ltb_size > tot_size) 60462306a36Sopenharmony_ci ltb_size = tot_size; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci nltbs = tot_size / ltb_size; 60762306a36Sopenharmony_ci if (tot_size % ltb_size) 60862306a36Sopenharmony_ci nltbs++; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci old_set = *ltb_set; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (old_set.num_ltbs == nltbs) { 61362306a36Sopenharmony_ci new_set = old_set; 61462306a36Sopenharmony_ci } else { 61562306a36Sopenharmony_ci int tmp = nltbs * sizeof(struct ibmvnic_long_term_buff); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci new_set.ltbs = kzalloc(tmp, GFP_KERNEL); 61862306a36Sopenharmony_ci if (!new_set.ltbs) 61962306a36Sopenharmony_ci return -ENOMEM; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci new_set.num_ltbs = nltbs; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Free any excess ltbs in old set */ 62462306a36Sopenharmony_ci for (i = new_set.num_ltbs; i < old_set.num_ltbs; i++) 62562306a36Sopenharmony_ci free_long_term_buff(adapter, &old_set.ltbs[i]); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Copy remaining ltbs to new set. All LTBs except the 62862306a36Sopenharmony_ci * last one are of the same size. alloc_long_term_buff() 62962306a36Sopenharmony_ci * will realloc if the size changes. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci n = min(old_set.num_ltbs, new_set.num_ltbs); 63262306a36Sopenharmony_ci for (i = 0; i < n; i++) 63362306a36Sopenharmony_ci new_set.ltbs[i] = old_set.ltbs[i]; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Any additional ltbs in new set will have NULL ltbs for 63662306a36Sopenharmony_ci * now and will be allocated in alloc_long_term_buff(). 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* We no longer need the old_set so free it. Note that we 64062306a36Sopenharmony_ci * may have reused some ltbs from old set and freed excess 64162306a36Sopenharmony_ci * ltbs above. So we only need to free the container now 64262306a36Sopenharmony_ci * not the LTBs themselves. (i.e. dont free_ltb_set()!) 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci kfree(old_set.ltbs); 64562306a36Sopenharmony_ci old_set.ltbs = NULL; 64662306a36Sopenharmony_ci old_set.num_ltbs = 0; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Install the new set. If allocations fail below, we will 64962306a36Sopenharmony_ci * retry later and know what size LTBs we need. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci *ltb_set = new_set; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci i = 0; 65562306a36Sopenharmony_ci rem_size = tot_size; 65662306a36Sopenharmony_ci while (rem_size) { 65762306a36Sopenharmony_ci if (ltb_size > rem_size) 65862306a36Sopenharmony_ci ltb_size = rem_size; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci rem_size -= ltb_size; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci rc = alloc_long_term_buff(adapter, &new_set.ltbs[i], ltb_size); 66362306a36Sopenharmony_ci if (rc) 66462306a36Sopenharmony_ci goto out; 66562306a36Sopenharmony_ci i++; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci WARN_ON(i != new_set.num_ltbs); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ciout: 67262306a36Sopenharmony_ci /* We may have allocated one/more LTBs before failing and we 67362306a36Sopenharmony_ci * want to try and reuse on next reset. So don't free ltb set. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci return rc; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/** 67962306a36Sopenharmony_ci * map_rxpool_buf_to_ltb - Map given rxpool buffer to offset in an LTB. 68062306a36Sopenharmony_ci * @rxpool: The receive buffer pool containing buffer 68162306a36Sopenharmony_ci * @bufidx: Index of buffer in rxpool 68262306a36Sopenharmony_ci * @ltbp: (Output) pointer to the long term buffer containing the buffer 68362306a36Sopenharmony_ci * @offset: (Output) offset of buffer in the LTB from @ltbp 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * Map the given buffer identified by [rxpool, bufidx] to an LTB in the 68662306a36Sopenharmony_ci * pool and its corresponding offset. Assume for now that each LTB is of 68762306a36Sopenharmony_ci * different size but could possibly be optimized based on the allocation 68862306a36Sopenharmony_ci * strategy in alloc_ltb_set(). 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistatic void map_rxpool_buf_to_ltb(struct ibmvnic_rx_pool *rxpool, 69162306a36Sopenharmony_ci unsigned int bufidx, 69262306a36Sopenharmony_ci struct ibmvnic_long_term_buff **ltbp, 69362306a36Sopenharmony_ci unsigned int *offset) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb; 69662306a36Sopenharmony_ci int nbufs; /* # of buffers in one ltb */ 69762306a36Sopenharmony_ci int i; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci WARN_ON(bufidx >= rxpool->size); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci for (i = 0; i < rxpool->ltb_set.num_ltbs; i++) { 70262306a36Sopenharmony_ci ltb = &rxpool->ltb_set.ltbs[i]; 70362306a36Sopenharmony_ci nbufs = ltb->size / rxpool->buff_size; 70462306a36Sopenharmony_ci if (bufidx < nbufs) 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci bufidx -= nbufs; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci *ltbp = ltb; 71062306a36Sopenharmony_ci *offset = bufidx * rxpool->buff_size; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/** 71462306a36Sopenharmony_ci * map_txpool_buf_to_ltb - Map given txpool buffer to offset in an LTB. 71562306a36Sopenharmony_ci * @txpool: The transmit buffer pool containing buffer 71662306a36Sopenharmony_ci * @bufidx: Index of buffer in txpool 71762306a36Sopenharmony_ci * @ltbp: (Output) pointer to the long term buffer (LTB) containing the buffer 71862306a36Sopenharmony_ci * @offset: (Output) offset of buffer in the LTB from @ltbp 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * Map the given buffer identified by [txpool, bufidx] to an LTB in the 72162306a36Sopenharmony_ci * pool and its corresponding offset. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_cistatic void map_txpool_buf_to_ltb(struct ibmvnic_tx_pool *txpool, 72462306a36Sopenharmony_ci unsigned int bufidx, 72562306a36Sopenharmony_ci struct ibmvnic_long_term_buff **ltbp, 72662306a36Sopenharmony_ci unsigned int *offset) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb; 72962306a36Sopenharmony_ci int nbufs; /* # of buffers in one ltb */ 73062306a36Sopenharmony_ci int i; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci WARN_ON_ONCE(bufidx >= txpool->num_buffers); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci for (i = 0; i < txpool->ltb_set.num_ltbs; i++) { 73562306a36Sopenharmony_ci ltb = &txpool->ltb_set.ltbs[i]; 73662306a36Sopenharmony_ci nbufs = ltb->size / txpool->buf_size; 73762306a36Sopenharmony_ci if (bufidx < nbufs) 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci bufidx -= nbufs; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci *ltbp = ltb; 74362306a36Sopenharmony_ci *offset = bufidx * txpool->buf_size; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void deactivate_rx_pools(struct ibmvnic_adapter *adapter) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci int i; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_rx_pools; i++) 75162306a36Sopenharmony_ci adapter->rx_pool[i].active = 0; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void replenish_rx_pool(struct ibmvnic_adapter *adapter, 75562306a36Sopenharmony_ci struct ibmvnic_rx_pool *pool) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int count = pool->size - atomic_read(&pool->available); 75862306a36Sopenharmony_ci u64 handle = adapter->rx_scrq[pool->index]->handle; 75962306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 76062306a36Sopenharmony_ci struct ibmvnic_ind_xmit_queue *ind_bufp; 76162306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *rx_scrq; 76262306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb; 76362306a36Sopenharmony_ci union sub_crq *sub_crq; 76462306a36Sopenharmony_ci int buffers_added = 0; 76562306a36Sopenharmony_ci unsigned long lpar_rc; 76662306a36Sopenharmony_ci struct sk_buff *skb; 76762306a36Sopenharmony_ci unsigned int offset; 76862306a36Sopenharmony_ci dma_addr_t dma_addr; 76962306a36Sopenharmony_ci unsigned char *dst; 77062306a36Sopenharmony_ci int shift = 0; 77162306a36Sopenharmony_ci int bufidx; 77262306a36Sopenharmony_ci int i; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!pool->active) 77562306a36Sopenharmony_ci return; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci rx_scrq = adapter->rx_scrq[pool->index]; 77862306a36Sopenharmony_ci ind_bufp = &rx_scrq->ind_buf; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* netdev_skb_alloc() could have failed after we saved a few skbs 78162306a36Sopenharmony_ci * in the indir_buf and we would not have sent them to VIOS yet. 78262306a36Sopenharmony_ci * To account for them, start the loop at ind_bufp->index rather 78362306a36Sopenharmony_ci * than 0. If we pushed all the skbs to VIOS, ind_bufp->index will 78462306a36Sopenharmony_ci * be 0. 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ci for (i = ind_bufp->index; i < count; ++i) { 78762306a36Sopenharmony_ci bufidx = pool->free_map[pool->next_free]; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* We maybe reusing the skb from earlier resets. Allocate 79062306a36Sopenharmony_ci * only if necessary. But since the LTB may have changed 79162306a36Sopenharmony_ci * during reset (see init_rx_pools()), update LTB below 79262306a36Sopenharmony_ci * even if reusing skb. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci skb = pool->rx_buff[bufidx].skb; 79562306a36Sopenharmony_ci if (!skb) { 79662306a36Sopenharmony_ci skb = netdev_alloc_skb(adapter->netdev, 79762306a36Sopenharmony_ci pool->buff_size); 79862306a36Sopenharmony_ci if (!skb) { 79962306a36Sopenharmony_ci dev_err(dev, "Couldn't replenish rx buff\n"); 80062306a36Sopenharmony_ci adapter->replenish_no_mem++; 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP; 80662306a36Sopenharmony_ci pool->next_free = (pool->next_free + 1) % pool->size; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Copy the skb to the long term mapped DMA buffer */ 80962306a36Sopenharmony_ci map_rxpool_buf_to_ltb(pool, bufidx, <b, &offset); 81062306a36Sopenharmony_ci dst = ltb->buff + offset; 81162306a36Sopenharmony_ci memset(dst, 0, pool->buff_size); 81262306a36Sopenharmony_ci dma_addr = ltb->addr + offset; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* add the skb to an rx_buff in the pool */ 81562306a36Sopenharmony_ci pool->rx_buff[bufidx].data = dst; 81662306a36Sopenharmony_ci pool->rx_buff[bufidx].dma = dma_addr; 81762306a36Sopenharmony_ci pool->rx_buff[bufidx].skb = skb; 81862306a36Sopenharmony_ci pool->rx_buff[bufidx].pool_index = pool->index; 81962306a36Sopenharmony_ci pool->rx_buff[bufidx].size = pool->buff_size; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* queue the rx_buff for the next send_subcrq_indirect */ 82262306a36Sopenharmony_ci sub_crq = &ind_bufp->indir_arr[ind_bufp->index++]; 82362306a36Sopenharmony_ci memset(sub_crq, 0, sizeof(*sub_crq)); 82462306a36Sopenharmony_ci sub_crq->rx_add.first = IBMVNIC_CRQ_CMD; 82562306a36Sopenharmony_ci sub_crq->rx_add.correlator = 82662306a36Sopenharmony_ci cpu_to_be64((u64)&pool->rx_buff[bufidx]); 82762306a36Sopenharmony_ci sub_crq->rx_add.ioba = cpu_to_be32(dma_addr); 82862306a36Sopenharmony_ci sub_crq->rx_add.map_id = ltb->map_id; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* The length field of the sCRQ is defined to be 24 bits so the 83162306a36Sopenharmony_ci * buffer size needs to be left shifted by a byte before it is 83262306a36Sopenharmony_ci * converted to big endian to prevent the last byte from being 83362306a36Sopenharmony_ci * truncated. 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 83662306a36Sopenharmony_ci shift = 8; 83762306a36Sopenharmony_ci#endif 83862306a36Sopenharmony_ci sub_crq->rx_add.len = cpu_to_be32(pool->buff_size << shift); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* if send_subcrq_indirect queue is full, flush to VIOS */ 84162306a36Sopenharmony_ci if (ind_bufp->index == IBMVNIC_MAX_IND_DESCS || 84262306a36Sopenharmony_ci i == count - 1) { 84362306a36Sopenharmony_ci lpar_rc = 84462306a36Sopenharmony_ci send_subcrq_indirect(adapter, handle, 84562306a36Sopenharmony_ci (u64)ind_bufp->indir_dma, 84662306a36Sopenharmony_ci (u64)ind_bufp->index); 84762306a36Sopenharmony_ci if (lpar_rc != H_SUCCESS) 84862306a36Sopenharmony_ci goto failure; 84962306a36Sopenharmony_ci buffers_added += ind_bufp->index; 85062306a36Sopenharmony_ci adapter->replenish_add_buff_success += ind_bufp->index; 85162306a36Sopenharmony_ci ind_bufp->index = 0; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci atomic_add(buffers_added, &pool->available); 85562306a36Sopenharmony_ci return; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cifailure: 85862306a36Sopenharmony_ci if (lpar_rc != H_PARAMETER && lpar_rc != H_CLOSED) 85962306a36Sopenharmony_ci dev_err_ratelimited(dev, "rx: replenish packet buffer failed\n"); 86062306a36Sopenharmony_ci for (i = ind_bufp->index - 1; i >= 0; --i) { 86162306a36Sopenharmony_ci struct ibmvnic_rx_buff *rx_buff; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci pool->next_free = pool->next_free == 0 ? 86462306a36Sopenharmony_ci pool->size - 1 : pool->next_free - 1; 86562306a36Sopenharmony_ci sub_crq = &ind_bufp->indir_arr[i]; 86662306a36Sopenharmony_ci rx_buff = (struct ibmvnic_rx_buff *) 86762306a36Sopenharmony_ci be64_to_cpu(sub_crq->rx_add.correlator); 86862306a36Sopenharmony_ci bufidx = (int)(rx_buff - pool->rx_buff); 86962306a36Sopenharmony_ci pool->free_map[pool->next_free] = bufidx; 87062306a36Sopenharmony_ci dev_kfree_skb_any(pool->rx_buff[bufidx].skb); 87162306a36Sopenharmony_ci pool->rx_buff[bufidx].skb = NULL; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci adapter->replenish_add_buff_failure += ind_bufp->index; 87462306a36Sopenharmony_ci atomic_add(buffers_added, &pool->available); 87562306a36Sopenharmony_ci ind_bufp->index = 0; 87662306a36Sopenharmony_ci if (lpar_rc == H_CLOSED || adapter->failover_pending) { 87762306a36Sopenharmony_ci /* Disable buffer pool replenishment and report carrier off if 87862306a36Sopenharmony_ci * queue is closed or pending failover. 87962306a36Sopenharmony_ci * Firmware guarantees that a signal will be sent to the 88062306a36Sopenharmony_ci * driver, triggering a reset. 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_ci deactivate_rx_pools(adapter); 88362306a36Sopenharmony_ci netif_carrier_off(adapter->netdev); 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void replenish_pools(struct ibmvnic_adapter *adapter) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci int i; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci adapter->replenish_task_cycles++; 89262306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_rx_pools; i++) { 89362306a36Sopenharmony_ci if (adapter->rx_pool[i].active) 89462306a36Sopenharmony_ci replenish_rx_pool(adapter, &adapter->rx_pool[i]); 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Replenished %d pools\n", i); 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic void release_stats_buffers(struct ibmvnic_adapter *adapter) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci kfree(adapter->tx_stats_buffers); 90362306a36Sopenharmony_ci kfree(adapter->rx_stats_buffers); 90462306a36Sopenharmony_ci adapter->tx_stats_buffers = NULL; 90562306a36Sopenharmony_ci adapter->rx_stats_buffers = NULL; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int init_stats_buffers(struct ibmvnic_adapter *adapter) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci adapter->tx_stats_buffers = 91162306a36Sopenharmony_ci kcalloc(IBMVNIC_MAX_QUEUES, 91262306a36Sopenharmony_ci sizeof(struct ibmvnic_tx_queue_stats), 91362306a36Sopenharmony_ci GFP_KERNEL); 91462306a36Sopenharmony_ci if (!adapter->tx_stats_buffers) 91562306a36Sopenharmony_ci return -ENOMEM; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci adapter->rx_stats_buffers = 91862306a36Sopenharmony_ci kcalloc(IBMVNIC_MAX_QUEUES, 91962306a36Sopenharmony_ci sizeof(struct ibmvnic_rx_queue_stats), 92062306a36Sopenharmony_ci GFP_KERNEL); 92162306a36Sopenharmony_ci if (!adapter->rx_stats_buffers) 92262306a36Sopenharmony_ci return -ENOMEM; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic void release_stats_token(struct ibmvnic_adapter *adapter) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (!adapter->stats_token) 93262306a36Sopenharmony_ci return; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci dma_unmap_single(dev, adapter->stats_token, 93562306a36Sopenharmony_ci sizeof(struct ibmvnic_statistics), 93662306a36Sopenharmony_ci DMA_FROM_DEVICE); 93762306a36Sopenharmony_ci adapter->stats_token = 0; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic int init_stats_token(struct ibmvnic_adapter *adapter) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 94362306a36Sopenharmony_ci dma_addr_t stok; 94462306a36Sopenharmony_ci int rc; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci stok = dma_map_single(dev, &adapter->stats, 94762306a36Sopenharmony_ci sizeof(struct ibmvnic_statistics), 94862306a36Sopenharmony_ci DMA_FROM_DEVICE); 94962306a36Sopenharmony_ci rc = dma_mapping_error(dev, stok); 95062306a36Sopenharmony_ci if (rc) { 95162306a36Sopenharmony_ci dev_err(dev, "Couldn't map stats buffer, rc = %d\n", rc); 95262306a36Sopenharmony_ci return rc; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci adapter->stats_token = stok; 95662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Stats token initialized (%llx)\n", stok); 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci/** 96162306a36Sopenharmony_ci * release_rx_pools() - Release any rx pools attached to @adapter. 96262306a36Sopenharmony_ci * @adapter: ibmvnic adapter 96362306a36Sopenharmony_ci * 96462306a36Sopenharmony_ci * Safe to call this multiple times - even if no pools are attached. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_cistatic void release_rx_pools(struct ibmvnic_adapter *adapter) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct ibmvnic_rx_pool *rx_pool; 96962306a36Sopenharmony_ci int i, j; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!adapter->rx_pool) 97262306a36Sopenharmony_ci return; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_rx_pools; i++) { 97562306a36Sopenharmony_ci rx_pool = &adapter->rx_pool[i]; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Releasing rx_pool[%d]\n", i); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci kfree(rx_pool->free_map); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci free_ltb_set(adapter, &rx_pool->ltb_set); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (!rx_pool->rx_buff) 98462306a36Sopenharmony_ci continue; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci for (j = 0; j < rx_pool->size; j++) { 98762306a36Sopenharmony_ci if (rx_pool->rx_buff[j].skb) { 98862306a36Sopenharmony_ci dev_kfree_skb_any(rx_pool->rx_buff[j].skb); 98962306a36Sopenharmony_ci rx_pool->rx_buff[j].skb = NULL; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci kfree(rx_pool->rx_buff); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci kfree(adapter->rx_pool); 99762306a36Sopenharmony_ci adapter->rx_pool = NULL; 99862306a36Sopenharmony_ci adapter->num_active_rx_pools = 0; 99962306a36Sopenharmony_ci adapter->prev_rx_pool_size = 0; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci/** 100362306a36Sopenharmony_ci * reuse_rx_pools() - Check if the existing rx pools can be reused. 100462306a36Sopenharmony_ci * @adapter: ibmvnic adapter 100562306a36Sopenharmony_ci * 100662306a36Sopenharmony_ci * Check if the existing rx pools in the adapter can be reused. The 100762306a36Sopenharmony_ci * pools can be reused if the pool parameters (number of pools, 100862306a36Sopenharmony_ci * number of buffers in the pool and size of each buffer) have not 100962306a36Sopenharmony_ci * changed. 101062306a36Sopenharmony_ci * 101162306a36Sopenharmony_ci * NOTE: This assumes that all pools have the same number of buffers 101262306a36Sopenharmony_ci * which is the case currently. If that changes, we must fix this. 101362306a36Sopenharmony_ci * 101462306a36Sopenharmony_ci * Return: true if the rx pools can be reused, false otherwise. 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_cistatic bool reuse_rx_pools(struct ibmvnic_adapter *adapter) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci u64 old_num_pools, new_num_pools; 101962306a36Sopenharmony_ci u64 old_pool_size, new_pool_size; 102062306a36Sopenharmony_ci u64 old_buff_size, new_buff_size; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (!adapter->rx_pool) 102362306a36Sopenharmony_ci return false; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci old_num_pools = adapter->num_active_rx_pools; 102662306a36Sopenharmony_ci new_num_pools = adapter->req_rx_queues; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci old_pool_size = adapter->prev_rx_pool_size; 102962306a36Sopenharmony_ci new_pool_size = adapter->req_rx_add_entries_per_subcrq; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci old_buff_size = adapter->prev_rx_buf_sz; 103262306a36Sopenharmony_ci new_buff_size = adapter->cur_rx_buf_sz; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (old_buff_size != new_buff_size || 103562306a36Sopenharmony_ci old_num_pools != new_num_pools || 103662306a36Sopenharmony_ci old_pool_size != new_pool_size) 103762306a36Sopenharmony_ci return false; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return true; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci/** 104362306a36Sopenharmony_ci * init_rx_pools(): Initialize the set of receiver pools in the adapter. 104462306a36Sopenharmony_ci * @netdev: net device associated with the vnic interface 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * Initialize the set of receiver pools in the ibmvnic adapter associated 104762306a36Sopenharmony_ci * with the net_device @netdev. If possible, reuse the existing rx pools. 104862306a36Sopenharmony_ci * Otherwise free any existing pools and allocate a new set of pools 104962306a36Sopenharmony_ci * before initializing them. 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * Return: 0 on success and negative value on error. 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_cistatic int init_rx_pools(struct net_device *netdev) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 105662306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 105762306a36Sopenharmony_ci struct ibmvnic_rx_pool *rx_pool; 105862306a36Sopenharmony_ci u64 num_pools; 105962306a36Sopenharmony_ci u64 pool_size; /* # of buffers in one pool */ 106062306a36Sopenharmony_ci u64 buff_size; 106162306a36Sopenharmony_ci int i, j, rc; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci pool_size = adapter->req_rx_add_entries_per_subcrq; 106462306a36Sopenharmony_ci num_pools = adapter->req_rx_queues; 106562306a36Sopenharmony_ci buff_size = adapter->cur_rx_buf_sz; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (reuse_rx_pools(adapter)) { 106862306a36Sopenharmony_ci dev_dbg(dev, "Reusing rx pools\n"); 106962306a36Sopenharmony_ci goto update_ltb; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* Allocate/populate the pools. */ 107362306a36Sopenharmony_ci release_rx_pools(adapter); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci adapter->rx_pool = kcalloc(num_pools, 107662306a36Sopenharmony_ci sizeof(struct ibmvnic_rx_pool), 107762306a36Sopenharmony_ci GFP_KERNEL); 107862306a36Sopenharmony_ci if (!adapter->rx_pool) { 107962306a36Sopenharmony_ci dev_err(dev, "Failed to allocate rx pools\n"); 108062306a36Sopenharmony_ci return -ENOMEM; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* Set num_active_rx_pools early. If we fail below after partial 108462306a36Sopenharmony_ci * allocation, release_rx_pools() will know how many to look for. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci adapter->num_active_rx_pools = num_pools; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci for (i = 0; i < num_pools; i++) { 108962306a36Sopenharmony_ci rx_pool = &adapter->rx_pool[i]; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 109262306a36Sopenharmony_ci "Initializing rx_pool[%d], %lld buffs, %lld bytes each\n", 109362306a36Sopenharmony_ci i, pool_size, buff_size); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci rx_pool->size = pool_size; 109662306a36Sopenharmony_ci rx_pool->index = i; 109762306a36Sopenharmony_ci rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci rx_pool->free_map = kcalloc(rx_pool->size, sizeof(int), 110062306a36Sopenharmony_ci GFP_KERNEL); 110162306a36Sopenharmony_ci if (!rx_pool->free_map) { 110262306a36Sopenharmony_ci dev_err(dev, "Couldn't alloc free_map %d\n", i); 110362306a36Sopenharmony_ci rc = -ENOMEM; 110462306a36Sopenharmony_ci goto out_release; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci rx_pool->rx_buff = kcalloc(rx_pool->size, 110862306a36Sopenharmony_ci sizeof(struct ibmvnic_rx_buff), 110962306a36Sopenharmony_ci GFP_KERNEL); 111062306a36Sopenharmony_ci if (!rx_pool->rx_buff) { 111162306a36Sopenharmony_ci dev_err(dev, "Couldn't alloc rx buffers\n"); 111262306a36Sopenharmony_ci rc = -ENOMEM; 111362306a36Sopenharmony_ci goto out_release; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci adapter->prev_rx_pool_size = pool_size; 111862306a36Sopenharmony_ci adapter->prev_rx_buf_sz = adapter->cur_rx_buf_sz; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ciupdate_ltb: 112162306a36Sopenharmony_ci for (i = 0; i < num_pools; i++) { 112262306a36Sopenharmony_ci rx_pool = &adapter->rx_pool[i]; 112362306a36Sopenharmony_ci dev_dbg(dev, "Updating LTB for rx pool %d [%d, %d]\n", 112462306a36Sopenharmony_ci i, rx_pool->size, rx_pool->buff_size); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci rc = alloc_ltb_set(adapter, &rx_pool->ltb_set, 112762306a36Sopenharmony_ci rx_pool->size, rx_pool->buff_size); 112862306a36Sopenharmony_ci if (rc) 112962306a36Sopenharmony_ci goto out; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci for (j = 0; j < rx_pool->size; ++j) { 113262306a36Sopenharmony_ci struct ibmvnic_rx_buff *rx_buff; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci rx_pool->free_map[j] = j; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* NOTE: Don't clear rx_buff->skb here - will leak 113762306a36Sopenharmony_ci * memory! replenish_rx_pool() will reuse skbs or 113862306a36Sopenharmony_ci * allocate as necessary. 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_ci rx_buff = &rx_pool->rx_buff[j]; 114162306a36Sopenharmony_ci rx_buff->dma = 0; 114262306a36Sopenharmony_ci rx_buff->data = 0; 114362306a36Sopenharmony_ci rx_buff->size = 0; 114462306a36Sopenharmony_ci rx_buff->pool_index = 0; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* Mark pool "empty" so replenish_rx_pools() will 114862306a36Sopenharmony_ci * update the LTB info for each buffer 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci atomic_set(&rx_pool->available, 0); 115162306a36Sopenharmony_ci rx_pool->next_alloc = 0; 115262306a36Sopenharmony_ci rx_pool->next_free = 0; 115362306a36Sopenharmony_ci /* replenish_rx_pool() may have called deactivate_rx_pools() 115462306a36Sopenharmony_ci * on failover. Ensure pool is active now. 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_ci rx_pool->active = 1; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ciout_release: 116062306a36Sopenharmony_ci release_rx_pools(adapter); 116162306a36Sopenharmony_ciout: 116262306a36Sopenharmony_ci /* We failed to allocate one or more LTBs or map them on the VIOS. 116362306a36Sopenharmony_ci * Hold onto the pools and any LTBs that we did allocate/map. 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ci return rc; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic void release_vpd_data(struct ibmvnic_adapter *adapter) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci if (!adapter->vpd) 117162306a36Sopenharmony_ci return; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci kfree(adapter->vpd->buff); 117462306a36Sopenharmony_ci kfree(adapter->vpd); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci adapter->vpd = NULL; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic void release_one_tx_pool(struct ibmvnic_adapter *adapter, 118062306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci kfree(tx_pool->tx_buff); 118362306a36Sopenharmony_ci kfree(tx_pool->free_map); 118462306a36Sopenharmony_ci free_ltb_set(adapter, &tx_pool->ltb_set); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci/** 118862306a36Sopenharmony_ci * release_tx_pools() - Release any tx pools attached to @adapter. 118962306a36Sopenharmony_ci * @adapter: ibmvnic adapter 119062306a36Sopenharmony_ci * 119162306a36Sopenharmony_ci * Safe to call this multiple times - even if no pools are attached. 119262306a36Sopenharmony_ci */ 119362306a36Sopenharmony_cistatic void release_tx_pools(struct ibmvnic_adapter *adapter) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci int i; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* init_tx_pools() ensures that ->tx_pool and ->tso_pool are 119862306a36Sopenharmony_ci * both NULL or both non-NULL. So we only need to check one. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci if (!adapter->tx_pool) 120162306a36Sopenharmony_ci return; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_tx_pools; i++) { 120462306a36Sopenharmony_ci release_one_tx_pool(adapter, &adapter->tx_pool[i]); 120562306a36Sopenharmony_ci release_one_tx_pool(adapter, &adapter->tso_pool[i]); 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci kfree(adapter->tx_pool); 120962306a36Sopenharmony_ci adapter->tx_pool = NULL; 121062306a36Sopenharmony_ci kfree(adapter->tso_pool); 121162306a36Sopenharmony_ci adapter->tso_pool = NULL; 121262306a36Sopenharmony_ci adapter->num_active_tx_pools = 0; 121362306a36Sopenharmony_ci adapter->prev_tx_pool_size = 0; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int init_one_tx_pool(struct net_device *netdev, 121762306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool, 121862306a36Sopenharmony_ci int pool_size, int buf_size) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci int i; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci tx_pool->tx_buff = kcalloc(pool_size, 122362306a36Sopenharmony_ci sizeof(struct ibmvnic_tx_buff), 122462306a36Sopenharmony_ci GFP_KERNEL); 122562306a36Sopenharmony_ci if (!tx_pool->tx_buff) 122662306a36Sopenharmony_ci return -ENOMEM; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci tx_pool->free_map = kcalloc(pool_size, sizeof(int), GFP_KERNEL); 122962306a36Sopenharmony_ci if (!tx_pool->free_map) { 123062306a36Sopenharmony_ci kfree(tx_pool->tx_buff); 123162306a36Sopenharmony_ci tx_pool->tx_buff = NULL; 123262306a36Sopenharmony_ci return -ENOMEM; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci for (i = 0; i < pool_size; i++) 123662306a36Sopenharmony_ci tx_pool->free_map[i] = i; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci tx_pool->consumer_index = 0; 123962306a36Sopenharmony_ci tx_pool->producer_index = 0; 124062306a36Sopenharmony_ci tx_pool->num_buffers = pool_size; 124162306a36Sopenharmony_ci tx_pool->buf_size = buf_size; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return 0; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/** 124762306a36Sopenharmony_ci * reuse_tx_pools() - Check if the existing tx pools can be reused. 124862306a36Sopenharmony_ci * @adapter: ibmvnic adapter 124962306a36Sopenharmony_ci * 125062306a36Sopenharmony_ci * Check if the existing tx pools in the adapter can be reused. The 125162306a36Sopenharmony_ci * pools can be reused if the pool parameters (number of pools, 125262306a36Sopenharmony_ci * number of buffers in the pool and mtu) have not changed. 125362306a36Sopenharmony_ci * 125462306a36Sopenharmony_ci * NOTE: This assumes that all pools have the same number of buffers 125562306a36Sopenharmony_ci * which is the case currently. If that changes, we must fix this. 125662306a36Sopenharmony_ci * 125762306a36Sopenharmony_ci * Return: true if the tx pools can be reused, false otherwise. 125862306a36Sopenharmony_ci */ 125962306a36Sopenharmony_cistatic bool reuse_tx_pools(struct ibmvnic_adapter *adapter) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci u64 old_num_pools, new_num_pools; 126262306a36Sopenharmony_ci u64 old_pool_size, new_pool_size; 126362306a36Sopenharmony_ci u64 old_mtu, new_mtu; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (!adapter->tx_pool) 126662306a36Sopenharmony_ci return false; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci old_num_pools = adapter->num_active_tx_pools; 126962306a36Sopenharmony_ci new_num_pools = adapter->num_active_tx_scrqs; 127062306a36Sopenharmony_ci old_pool_size = adapter->prev_tx_pool_size; 127162306a36Sopenharmony_ci new_pool_size = adapter->req_tx_entries_per_subcrq; 127262306a36Sopenharmony_ci old_mtu = adapter->prev_mtu; 127362306a36Sopenharmony_ci new_mtu = adapter->req_mtu; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if (old_mtu != new_mtu || 127662306a36Sopenharmony_ci old_num_pools != new_num_pools || 127762306a36Sopenharmony_ci old_pool_size != new_pool_size) 127862306a36Sopenharmony_ci return false; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return true; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci/** 128462306a36Sopenharmony_ci * init_tx_pools(): Initialize the set of transmit pools in the adapter. 128562306a36Sopenharmony_ci * @netdev: net device associated with the vnic interface 128662306a36Sopenharmony_ci * 128762306a36Sopenharmony_ci * Initialize the set of transmit pools in the ibmvnic adapter associated 128862306a36Sopenharmony_ci * with the net_device @netdev. If possible, reuse the existing tx pools. 128962306a36Sopenharmony_ci * Otherwise free any existing pools and allocate a new set of pools 129062306a36Sopenharmony_ci * before initializing them. 129162306a36Sopenharmony_ci * 129262306a36Sopenharmony_ci * Return: 0 on success and negative value on error. 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_cistatic int init_tx_pools(struct net_device *netdev) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 129762306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 129862306a36Sopenharmony_ci int num_pools; 129962306a36Sopenharmony_ci u64 pool_size; /* # of buffers in pool */ 130062306a36Sopenharmony_ci u64 buff_size; 130162306a36Sopenharmony_ci int i, j, rc; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci num_pools = adapter->req_tx_queues; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* We must notify the VIOS about the LTB on all resets - but we only 130662306a36Sopenharmony_ci * need to alloc/populate pools if either the number of buffers or 130762306a36Sopenharmony_ci * size of each buffer in the pool has changed. 130862306a36Sopenharmony_ci */ 130962306a36Sopenharmony_ci if (reuse_tx_pools(adapter)) { 131062306a36Sopenharmony_ci netdev_dbg(netdev, "Reusing tx pools\n"); 131162306a36Sopenharmony_ci goto update_ltb; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* Allocate/populate the pools. */ 131562306a36Sopenharmony_ci release_tx_pools(adapter); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci pool_size = adapter->req_tx_entries_per_subcrq; 131862306a36Sopenharmony_ci num_pools = adapter->num_active_tx_scrqs; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci adapter->tx_pool = kcalloc(num_pools, 132162306a36Sopenharmony_ci sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); 132262306a36Sopenharmony_ci if (!adapter->tx_pool) 132362306a36Sopenharmony_ci return -ENOMEM; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci adapter->tso_pool = kcalloc(num_pools, 132662306a36Sopenharmony_ci sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); 132762306a36Sopenharmony_ci /* To simplify release_tx_pools() ensure that ->tx_pool and 132862306a36Sopenharmony_ci * ->tso_pool are either both NULL or both non-NULL. 132962306a36Sopenharmony_ci */ 133062306a36Sopenharmony_ci if (!adapter->tso_pool) { 133162306a36Sopenharmony_ci kfree(adapter->tx_pool); 133262306a36Sopenharmony_ci adapter->tx_pool = NULL; 133362306a36Sopenharmony_ci return -ENOMEM; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci /* Set num_active_tx_pools early. If we fail below after partial 133762306a36Sopenharmony_ci * allocation, release_tx_pools() will know how many to look for. 133862306a36Sopenharmony_ci */ 133962306a36Sopenharmony_ci adapter->num_active_tx_pools = num_pools; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci buff_size = adapter->req_mtu + VLAN_HLEN; 134262306a36Sopenharmony_ci buff_size = ALIGN(buff_size, L1_CACHE_BYTES); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci for (i = 0; i < num_pools; i++) { 134562306a36Sopenharmony_ci dev_dbg(dev, "Init tx pool %d [%llu, %llu]\n", 134662306a36Sopenharmony_ci i, adapter->req_tx_entries_per_subcrq, buff_size); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci rc = init_one_tx_pool(netdev, &adapter->tx_pool[i], 134962306a36Sopenharmony_ci pool_size, buff_size); 135062306a36Sopenharmony_ci if (rc) 135162306a36Sopenharmony_ci goto out_release; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci rc = init_one_tx_pool(netdev, &adapter->tso_pool[i], 135462306a36Sopenharmony_ci IBMVNIC_TSO_BUFS, 135562306a36Sopenharmony_ci IBMVNIC_TSO_BUF_SZ); 135662306a36Sopenharmony_ci if (rc) 135762306a36Sopenharmony_ci goto out_release; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci adapter->prev_tx_pool_size = pool_size; 136162306a36Sopenharmony_ci adapter->prev_mtu = adapter->req_mtu; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ciupdate_ltb: 136462306a36Sopenharmony_ci /* NOTE: All tx_pools have the same number of buffers (which is 136562306a36Sopenharmony_ci * same as pool_size). All tso_pools have IBMVNIC_TSO_BUFS 136662306a36Sopenharmony_ci * buffers (see calls init_one_tx_pool() for these). 136762306a36Sopenharmony_ci * For consistency, we use tx_pool->num_buffers and 136862306a36Sopenharmony_ci * tso_pool->num_buffers below. 136962306a36Sopenharmony_ci */ 137062306a36Sopenharmony_ci rc = -1; 137162306a36Sopenharmony_ci for (i = 0; i < num_pools; i++) { 137262306a36Sopenharmony_ci struct ibmvnic_tx_pool *tso_pool; 137362306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci tx_pool = &adapter->tx_pool[i]; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci dev_dbg(dev, "Updating LTB for tx pool %d [%d, %d]\n", 137862306a36Sopenharmony_ci i, tx_pool->num_buffers, tx_pool->buf_size); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci rc = alloc_ltb_set(adapter, &tx_pool->ltb_set, 138162306a36Sopenharmony_ci tx_pool->num_buffers, tx_pool->buf_size); 138262306a36Sopenharmony_ci if (rc) 138362306a36Sopenharmony_ci goto out; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci tx_pool->consumer_index = 0; 138662306a36Sopenharmony_ci tx_pool->producer_index = 0; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci for (j = 0; j < tx_pool->num_buffers; j++) 138962306a36Sopenharmony_ci tx_pool->free_map[j] = j; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci tso_pool = &adapter->tso_pool[i]; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci dev_dbg(dev, "Updating LTB for tso pool %d [%d, %d]\n", 139462306a36Sopenharmony_ci i, tso_pool->num_buffers, tso_pool->buf_size); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci rc = alloc_ltb_set(adapter, &tso_pool->ltb_set, 139762306a36Sopenharmony_ci tso_pool->num_buffers, tso_pool->buf_size); 139862306a36Sopenharmony_ci if (rc) 139962306a36Sopenharmony_ci goto out; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci tso_pool->consumer_index = 0; 140262306a36Sopenharmony_ci tso_pool->producer_index = 0; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci for (j = 0; j < tso_pool->num_buffers; j++) 140562306a36Sopenharmony_ci tso_pool->free_map[j] = j; 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_ciout_release: 141062306a36Sopenharmony_ci release_tx_pools(adapter); 141162306a36Sopenharmony_ciout: 141262306a36Sopenharmony_ci /* We failed to allocate one or more LTBs or map them on the VIOS. 141362306a36Sopenharmony_ci * Hold onto the pools and any LTBs that we did allocate/map. 141462306a36Sopenharmony_ci */ 141562306a36Sopenharmony_ci return rc; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic void ibmvnic_napi_enable(struct ibmvnic_adapter *adapter) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci int i; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (adapter->napi_enabled) 142362306a36Sopenharmony_ci return; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) 142662306a36Sopenharmony_ci napi_enable(&adapter->napi[i]); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci adapter->napi_enabled = true; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic void ibmvnic_napi_disable(struct ibmvnic_adapter *adapter) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci int i; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci if (!adapter->napi_enabled) 143662306a36Sopenharmony_ci return; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 143962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Disabling napi[%d]\n", i); 144062306a36Sopenharmony_ci napi_disable(&adapter->napi[i]); 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci adapter->napi_enabled = false; 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic int init_napi(struct ibmvnic_adapter *adapter) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci int i; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci adapter->napi = kcalloc(adapter->req_rx_queues, 145162306a36Sopenharmony_ci sizeof(struct napi_struct), GFP_KERNEL); 145262306a36Sopenharmony_ci if (!adapter->napi) 145362306a36Sopenharmony_ci return -ENOMEM; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 145662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Adding napi[%d]\n", i); 145762306a36Sopenharmony_ci netif_napi_add(adapter->netdev, &adapter->napi[i], 145862306a36Sopenharmony_ci ibmvnic_poll); 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci adapter->num_active_rx_napi = adapter->req_rx_queues; 146262306a36Sopenharmony_ci return 0; 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic void release_napi(struct ibmvnic_adapter *adapter) 146662306a36Sopenharmony_ci{ 146762306a36Sopenharmony_ci int i; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (!adapter->napi) 147062306a36Sopenharmony_ci return; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_rx_napi; i++) { 147362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Releasing napi[%d]\n", i); 147462306a36Sopenharmony_ci netif_napi_del(&adapter->napi[i]); 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci kfree(adapter->napi); 147862306a36Sopenharmony_ci adapter->napi = NULL; 147962306a36Sopenharmony_ci adapter->num_active_rx_napi = 0; 148062306a36Sopenharmony_ci adapter->napi_enabled = false; 148162306a36Sopenharmony_ci} 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_cistatic const char *adapter_state_to_string(enum vnic_state state) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci switch (state) { 148662306a36Sopenharmony_ci case VNIC_PROBING: 148762306a36Sopenharmony_ci return "PROBING"; 148862306a36Sopenharmony_ci case VNIC_PROBED: 148962306a36Sopenharmony_ci return "PROBED"; 149062306a36Sopenharmony_ci case VNIC_OPENING: 149162306a36Sopenharmony_ci return "OPENING"; 149262306a36Sopenharmony_ci case VNIC_OPEN: 149362306a36Sopenharmony_ci return "OPEN"; 149462306a36Sopenharmony_ci case VNIC_CLOSING: 149562306a36Sopenharmony_ci return "CLOSING"; 149662306a36Sopenharmony_ci case VNIC_CLOSED: 149762306a36Sopenharmony_ci return "CLOSED"; 149862306a36Sopenharmony_ci case VNIC_REMOVING: 149962306a36Sopenharmony_ci return "REMOVING"; 150062306a36Sopenharmony_ci case VNIC_REMOVED: 150162306a36Sopenharmony_ci return "REMOVED"; 150262306a36Sopenharmony_ci case VNIC_DOWN: 150362306a36Sopenharmony_ci return "DOWN"; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci return "UNKNOWN"; 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_cistatic int ibmvnic_login(struct net_device *netdev) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci unsigned long flags, timeout = msecs_to_jiffies(20000); 151162306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 151262306a36Sopenharmony_ci int retry_count = 0; 151362306a36Sopenharmony_ci int retries = 10; 151462306a36Sopenharmony_ci bool retry; 151562306a36Sopenharmony_ci int rc; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci do { 151862306a36Sopenharmony_ci retry = false; 151962306a36Sopenharmony_ci if (retry_count > retries) { 152062306a36Sopenharmony_ci netdev_warn(netdev, "Login attempts exceeded\n"); 152162306a36Sopenharmony_ci return -EACCES; 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci adapter->init_done_rc = 0; 152562306a36Sopenharmony_ci reinit_completion(&adapter->init_done); 152662306a36Sopenharmony_ci rc = send_login(adapter); 152762306a36Sopenharmony_ci if (rc) 152862306a36Sopenharmony_ci return rc; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (!wait_for_completion_timeout(&adapter->init_done, 153162306a36Sopenharmony_ci timeout)) { 153262306a36Sopenharmony_ci netdev_warn(netdev, "Login timed out\n"); 153362306a36Sopenharmony_ci adapter->login_pending = false; 153462306a36Sopenharmony_ci goto partial_reset; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (adapter->init_done_rc == ABORTED) { 153862306a36Sopenharmony_ci netdev_warn(netdev, "Login aborted, retrying...\n"); 153962306a36Sopenharmony_ci retry = true; 154062306a36Sopenharmony_ci adapter->init_done_rc = 0; 154162306a36Sopenharmony_ci retry_count++; 154262306a36Sopenharmony_ci /* FW or device may be busy, so 154362306a36Sopenharmony_ci * wait a bit before retrying login 154462306a36Sopenharmony_ci */ 154562306a36Sopenharmony_ci msleep(500); 154662306a36Sopenharmony_ci } else if (adapter->init_done_rc == PARTIALSUCCESS) { 154762306a36Sopenharmony_ci retry_count++; 154862306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci retry = true; 155162306a36Sopenharmony_ci netdev_dbg(netdev, 155262306a36Sopenharmony_ci "Received partial success, retrying...\n"); 155362306a36Sopenharmony_ci adapter->init_done_rc = 0; 155462306a36Sopenharmony_ci reinit_completion(&adapter->init_done); 155562306a36Sopenharmony_ci send_query_cap(adapter); 155662306a36Sopenharmony_ci if (!wait_for_completion_timeout(&adapter->init_done, 155762306a36Sopenharmony_ci timeout)) { 155862306a36Sopenharmony_ci netdev_warn(netdev, 155962306a36Sopenharmony_ci "Capabilities query timed out\n"); 156062306a36Sopenharmony_ci return -ETIMEDOUT; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci rc = init_sub_crqs(adapter); 156462306a36Sopenharmony_ci if (rc) { 156562306a36Sopenharmony_ci netdev_warn(netdev, 156662306a36Sopenharmony_ci "SCRQ initialization failed\n"); 156762306a36Sopenharmony_ci return rc; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci rc = init_sub_crq_irqs(adapter); 157162306a36Sopenharmony_ci if (rc) { 157262306a36Sopenharmony_ci netdev_warn(netdev, 157362306a36Sopenharmony_ci "SCRQ irq initialization failed\n"); 157462306a36Sopenharmony_ci return rc; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci /* Default/timeout error handling, reset and start fresh */ 157762306a36Sopenharmony_ci } else if (adapter->init_done_rc) { 157862306a36Sopenharmony_ci netdev_warn(netdev, "Adapter login failed, init_done_rc = %d\n", 157962306a36Sopenharmony_ci adapter->init_done_rc); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cipartial_reset: 158262306a36Sopenharmony_ci /* adapter login failed, so free any CRQs or sub-CRQs 158362306a36Sopenharmony_ci * and register again before attempting to login again. 158462306a36Sopenharmony_ci * If we don't do this then the VIOS may think that 158562306a36Sopenharmony_ci * we are already logged in and reject any subsequent 158662306a36Sopenharmony_ci * attempts 158762306a36Sopenharmony_ci */ 158862306a36Sopenharmony_ci netdev_warn(netdev, 158962306a36Sopenharmony_ci "Freeing and re-registering CRQs before attempting to login again\n"); 159062306a36Sopenharmony_ci retry = true; 159162306a36Sopenharmony_ci adapter->init_done_rc = 0; 159262306a36Sopenharmony_ci release_sub_crqs(adapter, true); 159362306a36Sopenharmony_ci /* Much of this is similar logic as ibmvnic_probe(), 159462306a36Sopenharmony_ci * we are essentially re-initializing communication 159562306a36Sopenharmony_ci * with the server. We really should not run any 159662306a36Sopenharmony_ci * resets/failovers here because this is already a form 159762306a36Sopenharmony_ci * of reset and we do not want parallel resets occurring 159862306a36Sopenharmony_ci */ 159962306a36Sopenharmony_ci do { 160062306a36Sopenharmony_ci reinit_init_done(adapter); 160162306a36Sopenharmony_ci /* Clear any failovers we got in the previous 160262306a36Sopenharmony_ci * pass since we are re-initializing the CRQ 160362306a36Sopenharmony_ci */ 160462306a36Sopenharmony_ci adapter->failover_pending = false; 160562306a36Sopenharmony_ci release_crq_queue(adapter); 160662306a36Sopenharmony_ci /* If we don't sleep here then we risk an 160762306a36Sopenharmony_ci * unnecessary failover event from the VIOS. 160862306a36Sopenharmony_ci * This is a known VIOS issue caused by a vnic 160962306a36Sopenharmony_ci * device freeing and registering a CRQ too 161062306a36Sopenharmony_ci * quickly. 161162306a36Sopenharmony_ci */ 161262306a36Sopenharmony_ci msleep(1500); 161362306a36Sopenharmony_ci /* Avoid any resets, since we are currently 161462306a36Sopenharmony_ci * resetting. 161562306a36Sopenharmony_ci */ 161662306a36Sopenharmony_ci spin_lock_irqsave(&adapter->rwi_lock, flags); 161762306a36Sopenharmony_ci flush_reset_queue(adapter); 161862306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->rwi_lock, 161962306a36Sopenharmony_ci flags); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci rc = init_crq_queue(adapter); 162262306a36Sopenharmony_ci if (rc) { 162362306a36Sopenharmony_ci netdev_err(netdev, "login recovery: init CRQ failed %d\n", 162462306a36Sopenharmony_ci rc); 162562306a36Sopenharmony_ci return -EIO; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci rc = ibmvnic_reset_init(adapter, false); 162962306a36Sopenharmony_ci if (rc) 163062306a36Sopenharmony_ci netdev_err(netdev, "login recovery: Reset init failed %d\n", 163162306a36Sopenharmony_ci rc); 163262306a36Sopenharmony_ci /* IBMVNIC_CRQ_INIT will return EAGAIN if it 163362306a36Sopenharmony_ci * fails, since ibmvnic_reset_init will free 163462306a36Sopenharmony_ci * irq's in failure, we won't be able to receive 163562306a36Sopenharmony_ci * new CRQs so we need to keep trying. probe() 163662306a36Sopenharmony_ci * handles this similarly. 163762306a36Sopenharmony_ci */ 163862306a36Sopenharmony_ci } while (rc == -EAGAIN && retry_count++ < retries); 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci } while (retry); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci __ibmvnic_set_mac(netdev, adapter->mac_addr); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci netdev_dbg(netdev, "[S:%s] Login succeeded\n", adapter_state_to_string(adapter->state)); 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic void release_login_buffer(struct ibmvnic_adapter *adapter) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci if (!adapter->login_buf) 165162306a36Sopenharmony_ci return; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci dma_unmap_single(&adapter->vdev->dev, adapter->login_buf_token, 165462306a36Sopenharmony_ci adapter->login_buf_sz, DMA_TO_DEVICE); 165562306a36Sopenharmony_ci kfree(adapter->login_buf); 165662306a36Sopenharmony_ci adapter->login_buf = NULL; 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic void release_login_rsp_buffer(struct ibmvnic_adapter *adapter) 166062306a36Sopenharmony_ci{ 166162306a36Sopenharmony_ci if (!adapter->login_rsp_buf) 166262306a36Sopenharmony_ci return; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci dma_unmap_single(&adapter->vdev->dev, adapter->login_rsp_buf_token, 166562306a36Sopenharmony_ci adapter->login_rsp_buf_sz, DMA_FROM_DEVICE); 166662306a36Sopenharmony_ci kfree(adapter->login_rsp_buf); 166762306a36Sopenharmony_ci adapter->login_rsp_buf = NULL; 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cistatic void release_resources(struct ibmvnic_adapter *adapter) 167162306a36Sopenharmony_ci{ 167262306a36Sopenharmony_ci release_vpd_data(adapter); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci release_napi(adapter); 167562306a36Sopenharmony_ci release_login_buffer(adapter); 167662306a36Sopenharmony_ci release_login_rsp_buffer(adapter); 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 168262306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(20000); 168362306a36Sopenharmony_ci union ibmvnic_crq crq; 168462306a36Sopenharmony_ci bool resend; 168562306a36Sopenharmony_ci int rc; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci netdev_dbg(netdev, "setting link state %d\n", link_state); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 169062306a36Sopenharmony_ci crq.logical_link_state.first = IBMVNIC_CRQ_CMD; 169162306a36Sopenharmony_ci crq.logical_link_state.cmd = LOGICAL_LINK_STATE; 169262306a36Sopenharmony_ci crq.logical_link_state.link_state = link_state; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci do { 169562306a36Sopenharmony_ci resend = false; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci reinit_completion(&adapter->init_done); 169862306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 169962306a36Sopenharmony_ci if (rc) { 170062306a36Sopenharmony_ci netdev_err(netdev, "Failed to set link state\n"); 170162306a36Sopenharmony_ci return rc; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci if (!wait_for_completion_timeout(&adapter->init_done, 170562306a36Sopenharmony_ci timeout)) { 170662306a36Sopenharmony_ci netdev_err(netdev, "timeout setting link state\n"); 170762306a36Sopenharmony_ci return -ETIMEDOUT; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (adapter->init_done_rc == PARTIALSUCCESS) { 171162306a36Sopenharmony_ci /* Partuial success, delay and re-send */ 171262306a36Sopenharmony_ci mdelay(1000); 171362306a36Sopenharmony_ci resend = true; 171462306a36Sopenharmony_ci } else if (adapter->init_done_rc) { 171562306a36Sopenharmony_ci netdev_warn(netdev, "Unable to set link state, rc=%d\n", 171662306a36Sopenharmony_ci adapter->init_done_rc); 171762306a36Sopenharmony_ci return adapter->init_done_rc; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci } while (resend); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci return 0; 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic int set_real_num_queues(struct net_device *netdev) 172562306a36Sopenharmony_ci{ 172662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 172762306a36Sopenharmony_ci int rc; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci netdev_dbg(netdev, "Setting real tx/rx queues (%llx/%llx)\n", 173062306a36Sopenharmony_ci adapter->req_tx_queues, adapter->req_rx_queues); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); 173362306a36Sopenharmony_ci if (rc) { 173462306a36Sopenharmony_ci netdev_err(netdev, "failed to set the number of tx queues\n"); 173562306a36Sopenharmony_ci return rc; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci rc = netif_set_real_num_rx_queues(netdev, adapter->req_rx_queues); 173962306a36Sopenharmony_ci if (rc) 174062306a36Sopenharmony_ci netdev_err(netdev, "failed to set the number of rx queues\n"); 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci return rc; 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistatic int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 174862306a36Sopenharmony_ci union ibmvnic_crq crq; 174962306a36Sopenharmony_ci int len = 0; 175062306a36Sopenharmony_ci int rc; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci if (adapter->vpd->buff) 175362306a36Sopenharmony_ci len = adapter->vpd->len; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci mutex_lock(&adapter->fw_lock); 175662306a36Sopenharmony_ci adapter->fw_done_rc = 0; 175762306a36Sopenharmony_ci reinit_completion(&adapter->fw_done); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci crq.get_vpd_size.first = IBMVNIC_CRQ_CMD; 176062306a36Sopenharmony_ci crq.get_vpd_size.cmd = GET_VPD_SIZE; 176162306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 176262306a36Sopenharmony_ci if (rc) { 176362306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 176462306a36Sopenharmony_ci return rc; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); 176862306a36Sopenharmony_ci if (rc) { 176962306a36Sopenharmony_ci dev_err(dev, "Could not retrieve VPD size, rc = %d\n", rc); 177062306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 177162306a36Sopenharmony_ci return rc; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (!adapter->vpd->len) 177662306a36Sopenharmony_ci return -ENODATA; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci if (!adapter->vpd->buff) 177962306a36Sopenharmony_ci adapter->vpd->buff = kzalloc(adapter->vpd->len, GFP_KERNEL); 178062306a36Sopenharmony_ci else if (adapter->vpd->len != len) 178162306a36Sopenharmony_ci adapter->vpd->buff = 178262306a36Sopenharmony_ci krealloc(adapter->vpd->buff, 178362306a36Sopenharmony_ci adapter->vpd->len, GFP_KERNEL); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci if (!adapter->vpd->buff) { 178662306a36Sopenharmony_ci dev_err(dev, "Could allocate VPD buffer\n"); 178762306a36Sopenharmony_ci return -ENOMEM; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci adapter->vpd->dma_addr = 179162306a36Sopenharmony_ci dma_map_single(dev, adapter->vpd->buff, adapter->vpd->len, 179262306a36Sopenharmony_ci DMA_FROM_DEVICE); 179362306a36Sopenharmony_ci if (dma_mapping_error(dev, adapter->vpd->dma_addr)) { 179462306a36Sopenharmony_ci dev_err(dev, "Could not map VPD buffer\n"); 179562306a36Sopenharmony_ci kfree(adapter->vpd->buff); 179662306a36Sopenharmony_ci adapter->vpd->buff = NULL; 179762306a36Sopenharmony_ci return -ENOMEM; 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci mutex_lock(&adapter->fw_lock); 180162306a36Sopenharmony_ci adapter->fw_done_rc = 0; 180262306a36Sopenharmony_ci reinit_completion(&adapter->fw_done); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci crq.get_vpd.first = IBMVNIC_CRQ_CMD; 180562306a36Sopenharmony_ci crq.get_vpd.cmd = GET_VPD; 180662306a36Sopenharmony_ci crq.get_vpd.ioba = cpu_to_be32(adapter->vpd->dma_addr); 180762306a36Sopenharmony_ci crq.get_vpd.len = cpu_to_be32((u32)adapter->vpd->len); 180862306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 180962306a36Sopenharmony_ci if (rc) { 181062306a36Sopenharmony_ci kfree(adapter->vpd->buff); 181162306a36Sopenharmony_ci adapter->vpd->buff = NULL; 181262306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 181362306a36Sopenharmony_ci return rc; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); 181762306a36Sopenharmony_ci if (rc) { 181862306a36Sopenharmony_ci dev_err(dev, "Unable to retrieve VPD, rc = %d\n", rc); 181962306a36Sopenharmony_ci kfree(adapter->vpd->buff); 182062306a36Sopenharmony_ci adapter->vpd->buff = NULL; 182162306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 182262306a36Sopenharmony_ci return rc; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 182662306a36Sopenharmony_ci return 0; 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic int init_resources(struct ibmvnic_adapter *adapter) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 183262306a36Sopenharmony_ci int rc; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci rc = set_real_num_queues(netdev); 183562306a36Sopenharmony_ci if (rc) 183662306a36Sopenharmony_ci return rc; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci adapter->vpd = kzalloc(sizeof(*adapter->vpd), GFP_KERNEL); 183962306a36Sopenharmony_ci if (!adapter->vpd) 184062306a36Sopenharmony_ci return -ENOMEM; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci /* Vital Product Data (VPD) */ 184362306a36Sopenharmony_ci rc = ibmvnic_get_vpd(adapter); 184462306a36Sopenharmony_ci if (rc) { 184562306a36Sopenharmony_ci netdev_err(netdev, "failed to initialize Vital Product Data (VPD)\n"); 184662306a36Sopenharmony_ci return rc; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci rc = init_napi(adapter); 185062306a36Sopenharmony_ci if (rc) 185162306a36Sopenharmony_ci return rc; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci send_query_map(adapter); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci rc = init_rx_pools(netdev); 185662306a36Sopenharmony_ci if (rc) 185762306a36Sopenharmony_ci return rc; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci rc = init_tx_pools(netdev); 186062306a36Sopenharmony_ci return rc; 186162306a36Sopenharmony_ci} 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_cistatic int __ibmvnic_open(struct net_device *netdev) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 186662306a36Sopenharmony_ci enum vnic_state prev_state = adapter->state; 186762306a36Sopenharmony_ci int i, rc; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci adapter->state = VNIC_OPENING; 187062306a36Sopenharmony_ci replenish_pools(adapter); 187162306a36Sopenharmony_ci ibmvnic_napi_enable(adapter); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci /* We're ready to receive frames, enable the sub-crq interrupts and 187462306a36Sopenharmony_ci * set the logical link state to up 187562306a36Sopenharmony_ci */ 187662306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 187762306a36Sopenharmony_ci netdev_dbg(netdev, "Enabling rx_scrq[%d] irq\n", i); 187862306a36Sopenharmony_ci if (prev_state == VNIC_CLOSED) 187962306a36Sopenharmony_ci enable_irq(adapter->rx_scrq[i]->irq); 188062306a36Sopenharmony_ci enable_scrq_irq(adapter, adapter->rx_scrq[i]); 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) { 188462306a36Sopenharmony_ci netdev_dbg(netdev, "Enabling tx_scrq[%d] irq\n", i); 188562306a36Sopenharmony_ci if (prev_state == VNIC_CLOSED) 188662306a36Sopenharmony_ci enable_irq(adapter->tx_scrq[i]->irq); 188762306a36Sopenharmony_ci enable_scrq_irq(adapter, adapter->tx_scrq[i]); 188862306a36Sopenharmony_ci /* netdev_tx_reset_queue will reset dql stats. During NON_FATAL 188962306a36Sopenharmony_ci * resets, don't reset the stats because there could be batched 189062306a36Sopenharmony_ci * skb's waiting to be sent. If we reset dql stats, we risk 189162306a36Sopenharmony_ci * num_completed being greater than num_queued. This will cause 189262306a36Sopenharmony_ci * a BUG_ON in dql_completed(). 189362306a36Sopenharmony_ci */ 189462306a36Sopenharmony_ci if (adapter->reset_reason != VNIC_RESET_NON_FATAL) 189562306a36Sopenharmony_ci netdev_tx_reset_queue(netdev_get_tx_queue(netdev, i)); 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_UP); 189962306a36Sopenharmony_ci if (rc) { 190062306a36Sopenharmony_ci ibmvnic_napi_disable(adapter); 190162306a36Sopenharmony_ci ibmvnic_disable_irqs(adapter); 190262306a36Sopenharmony_ci return rc; 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci adapter->tx_queues_active = true; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci /* Since queues were stopped until now, there shouldn't be any 190862306a36Sopenharmony_ci * one in ibmvnic_complete_tx() or ibmvnic_xmit() so maybe we 190962306a36Sopenharmony_ci * don't need the synchronize_rcu()? Leaving it for consistency 191062306a36Sopenharmony_ci * with setting ->tx_queues_active = false. 191162306a36Sopenharmony_ci */ 191262306a36Sopenharmony_ci synchronize_rcu(); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci netif_tx_start_all_queues(netdev); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci if (prev_state == VNIC_CLOSED) { 191762306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) 191862306a36Sopenharmony_ci napi_schedule(&adapter->napi[i]); 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci adapter->state = VNIC_OPEN; 192262306a36Sopenharmony_ci return rc; 192362306a36Sopenharmony_ci} 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_cistatic int ibmvnic_open(struct net_device *netdev) 192662306a36Sopenharmony_ci{ 192762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 192862306a36Sopenharmony_ci int rc; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci ASSERT_RTNL(); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci /* If device failover is pending or we are about to reset, just set 193362306a36Sopenharmony_ci * device state and return. Device operation will be handled by reset 193462306a36Sopenharmony_ci * routine. 193562306a36Sopenharmony_ci * 193662306a36Sopenharmony_ci * It should be safe to overwrite the adapter->state here. Since 193762306a36Sopenharmony_ci * we hold the rtnl, either the reset has not actually started or 193862306a36Sopenharmony_ci * the rtnl got dropped during the set_link_state() in do_reset(). 193962306a36Sopenharmony_ci * In the former case, no one else is changing the state (again we 194062306a36Sopenharmony_ci * have the rtnl) and in the latter case, do_reset() will detect and 194162306a36Sopenharmony_ci * honor our setting below. 194262306a36Sopenharmony_ci */ 194362306a36Sopenharmony_ci if (adapter->failover_pending || (test_bit(0, &adapter->resetting))) { 194462306a36Sopenharmony_ci netdev_dbg(netdev, "[S:%s FOP:%d] Resetting, deferring open\n", 194562306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 194662306a36Sopenharmony_ci adapter->failover_pending); 194762306a36Sopenharmony_ci adapter->state = VNIC_OPEN; 194862306a36Sopenharmony_ci rc = 0; 194962306a36Sopenharmony_ci goto out; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci if (adapter->state != VNIC_CLOSED) { 195362306a36Sopenharmony_ci rc = ibmvnic_login(netdev); 195462306a36Sopenharmony_ci if (rc) 195562306a36Sopenharmony_ci goto out; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci rc = init_resources(adapter); 195862306a36Sopenharmony_ci if (rc) { 195962306a36Sopenharmony_ci netdev_err(netdev, "failed to initialize resources\n"); 196062306a36Sopenharmony_ci goto out; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci rc = __ibmvnic_open(netdev); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ciout: 196762306a36Sopenharmony_ci /* If open failed and there is a pending failover or in-progress reset, 196862306a36Sopenharmony_ci * set device state and return. Device operation will be handled by 196962306a36Sopenharmony_ci * reset routine. See also comments above regarding rtnl. 197062306a36Sopenharmony_ci */ 197162306a36Sopenharmony_ci if (rc && 197262306a36Sopenharmony_ci (adapter->failover_pending || (test_bit(0, &adapter->resetting)))) { 197362306a36Sopenharmony_ci adapter->state = VNIC_OPEN; 197462306a36Sopenharmony_ci rc = 0; 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci if (rc) { 197862306a36Sopenharmony_ci release_resources(adapter); 197962306a36Sopenharmony_ci release_rx_pools(adapter); 198062306a36Sopenharmony_ci release_tx_pools(adapter); 198162306a36Sopenharmony_ci } 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci return rc; 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_cistatic void clean_rx_pools(struct ibmvnic_adapter *adapter) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci struct ibmvnic_rx_pool *rx_pool; 198962306a36Sopenharmony_ci struct ibmvnic_rx_buff *rx_buff; 199062306a36Sopenharmony_ci u64 rx_entries; 199162306a36Sopenharmony_ci int rx_scrqs; 199262306a36Sopenharmony_ci int i, j; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci if (!adapter->rx_pool) 199562306a36Sopenharmony_ci return; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci rx_scrqs = adapter->num_active_rx_pools; 199862306a36Sopenharmony_ci rx_entries = adapter->req_rx_add_entries_per_subcrq; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci /* Free any remaining skbs in the rx buffer pools */ 200162306a36Sopenharmony_ci for (i = 0; i < rx_scrqs; i++) { 200262306a36Sopenharmony_ci rx_pool = &adapter->rx_pool[i]; 200362306a36Sopenharmony_ci if (!rx_pool || !rx_pool->rx_buff) 200462306a36Sopenharmony_ci continue; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Cleaning rx_pool[%d]\n", i); 200762306a36Sopenharmony_ci for (j = 0; j < rx_entries; j++) { 200862306a36Sopenharmony_ci rx_buff = &rx_pool->rx_buff[j]; 200962306a36Sopenharmony_ci if (rx_buff && rx_buff->skb) { 201062306a36Sopenharmony_ci dev_kfree_skb_any(rx_buff->skb); 201162306a36Sopenharmony_ci rx_buff->skb = NULL; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic void clean_one_tx_pool(struct ibmvnic_adapter *adapter, 201862306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool) 201962306a36Sopenharmony_ci{ 202062306a36Sopenharmony_ci struct ibmvnic_tx_buff *tx_buff; 202162306a36Sopenharmony_ci u64 tx_entries; 202262306a36Sopenharmony_ci int i; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci if (!tx_pool || !tx_pool->tx_buff) 202562306a36Sopenharmony_ci return; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci tx_entries = tx_pool->num_buffers; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci for (i = 0; i < tx_entries; i++) { 203062306a36Sopenharmony_ci tx_buff = &tx_pool->tx_buff[i]; 203162306a36Sopenharmony_ci if (tx_buff && tx_buff->skb) { 203262306a36Sopenharmony_ci dev_kfree_skb_any(tx_buff->skb); 203362306a36Sopenharmony_ci tx_buff->skb = NULL; 203462306a36Sopenharmony_ci } 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cistatic void clean_tx_pools(struct ibmvnic_adapter *adapter) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci int tx_scrqs; 204162306a36Sopenharmony_ci int i; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci if (!adapter->tx_pool || !adapter->tso_pool) 204462306a36Sopenharmony_ci return; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci tx_scrqs = adapter->num_active_tx_pools; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci /* Free any remaining skbs in the tx buffer pools */ 204962306a36Sopenharmony_ci for (i = 0; i < tx_scrqs; i++) { 205062306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i); 205162306a36Sopenharmony_ci clean_one_tx_pool(adapter, &adapter->tx_pool[i]); 205262306a36Sopenharmony_ci clean_one_tx_pool(adapter, &adapter->tso_pool[i]); 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci} 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_cistatic void ibmvnic_disable_irqs(struct ibmvnic_adapter *adapter) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 205962306a36Sopenharmony_ci int i; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci if (adapter->tx_scrq) { 206262306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) 206362306a36Sopenharmony_ci if (adapter->tx_scrq[i]->irq) { 206462306a36Sopenharmony_ci netdev_dbg(netdev, 206562306a36Sopenharmony_ci "Disabling tx_scrq[%d] irq\n", i); 206662306a36Sopenharmony_ci disable_scrq_irq(adapter, adapter->tx_scrq[i]); 206762306a36Sopenharmony_ci disable_irq(adapter->tx_scrq[i]->irq); 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci if (adapter->rx_scrq) { 207262306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 207362306a36Sopenharmony_ci if (adapter->rx_scrq[i]->irq) { 207462306a36Sopenharmony_ci netdev_dbg(netdev, 207562306a36Sopenharmony_ci "Disabling rx_scrq[%d] irq\n", i); 207662306a36Sopenharmony_ci disable_scrq_irq(adapter, adapter->rx_scrq[i]); 207762306a36Sopenharmony_ci disable_irq(adapter->rx_scrq[i]->irq); 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci} 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_cistatic void ibmvnic_cleanup(struct net_device *netdev) 208462306a36Sopenharmony_ci{ 208562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* ensure that transmissions are stopped if called by do_reset */ 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci adapter->tx_queues_active = false; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci /* Ensure complete_tx() and ibmvnic_xmit() see ->tx_queues_active 209262306a36Sopenharmony_ci * update so they don't restart a queue after we stop it below. 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_ci synchronize_rcu(); 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci if (test_bit(0, &adapter->resetting)) 209762306a36Sopenharmony_ci netif_tx_disable(netdev); 209862306a36Sopenharmony_ci else 209962306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci ibmvnic_napi_disable(adapter); 210262306a36Sopenharmony_ci ibmvnic_disable_irqs(adapter); 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic int __ibmvnic_close(struct net_device *netdev) 210662306a36Sopenharmony_ci{ 210762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 210862306a36Sopenharmony_ci int rc = 0; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci adapter->state = VNIC_CLOSING; 211162306a36Sopenharmony_ci rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); 211262306a36Sopenharmony_ci adapter->state = VNIC_CLOSED; 211362306a36Sopenharmony_ci return rc; 211462306a36Sopenharmony_ci} 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_cistatic int ibmvnic_close(struct net_device *netdev) 211762306a36Sopenharmony_ci{ 211862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 211962306a36Sopenharmony_ci int rc; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci netdev_dbg(netdev, "[S:%s FOP:%d FRR:%d] Closing\n", 212262306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 212362306a36Sopenharmony_ci adapter->failover_pending, 212462306a36Sopenharmony_ci adapter->force_reset_recovery); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* If device failover is pending, just set device state and return. 212762306a36Sopenharmony_ci * Device operation will be handled by reset routine. 212862306a36Sopenharmony_ci */ 212962306a36Sopenharmony_ci if (adapter->failover_pending) { 213062306a36Sopenharmony_ci adapter->state = VNIC_CLOSED; 213162306a36Sopenharmony_ci return 0; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci rc = __ibmvnic_close(netdev); 213562306a36Sopenharmony_ci ibmvnic_cleanup(netdev); 213662306a36Sopenharmony_ci clean_rx_pools(adapter); 213762306a36Sopenharmony_ci clean_tx_pools(adapter); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci return rc; 214062306a36Sopenharmony_ci} 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci/** 214362306a36Sopenharmony_ci * build_hdr_data - creates L2/L3/L4 header data buffer 214462306a36Sopenharmony_ci * @hdr_field: bitfield determining needed headers 214562306a36Sopenharmony_ci * @skb: socket buffer 214662306a36Sopenharmony_ci * @hdr_len: array of header lengths 214762306a36Sopenharmony_ci * @hdr_data: buffer to write the header to 214862306a36Sopenharmony_ci * 214962306a36Sopenharmony_ci * Reads hdr_field to determine which headers are needed by firmware. 215062306a36Sopenharmony_ci * Builds a buffer containing these headers. Saves individual header 215162306a36Sopenharmony_ci * lengths and total buffer length to be used to build descriptors. 215262306a36Sopenharmony_ci */ 215362306a36Sopenharmony_cistatic int build_hdr_data(u8 hdr_field, struct sk_buff *skb, 215462306a36Sopenharmony_ci int *hdr_len, u8 *hdr_data) 215562306a36Sopenharmony_ci{ 215662306a36Sopenharmony_ci int len = 0; 215762306a36Sopenharmony_ci u8 *hdr; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci if (skb_vlan_tagged(skb) && !skb_vlan_tag_present(skb)) 216062306a36Sopenharmony_ci hdr_len[0] = sizeof(struct vlan_ethhdr); 216162306a36Sopenharmony_ci else 216262306a36Sopenharmony_ci hdr_len[0] = sizeof(struct ethhdr); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 216562306a36Sopenharmony_ci hdr_len[1] = ip_hdr(skb)->ihl * 4; 216662306a36Sopenharmony_ci if (ip_hdr(skb)->protocol == IPPROTO_TCP) 216762306a36Sopenharmony_ci hdr_len[2] = tcp_hdrlen(skb); 216862306a36Sopenharmony_ci else if (ip_hdr(skb)->protocol == IPPROTO_UDP) 216962306a36Sopenharmony_ci hdr_len[2] = sizeof(struct udphdr); 217062306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6)) { 217162306a36Sopenharmony_ci hdr_len[1] = sizeof(struct ipv6hdr); 217262306a36Sopenharmony_ci if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) 217362306a36Sopenharmony_ci hdr_len[2] = tcp_hdrlen(skb); 217462306a36Sopenharmony_ci else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) 217562306a36Sopenharmony_ci hdr_len[2] = sizeof(struct udphdr); 217662306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_ARP)) { 217762306a36Sopenharmony_ci hdr_len[1] = arp_hdr_len(skb->dev); 217862306a36Sopenharmony_ci hdr_len[2] = 0; 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci memset(hdr_data, 0, 120); 218262306a36Sopenharmony_ci if ((hdr_field >> 6) & 1) { 218362306a36Sopenharmony_ci hdr = skb_mac_header(skb); 218462306a36Sopenharmony_ci memcpy(hdr_data, hdr, hdr_len[0]); 218562306a36Sopenharmony_ci len += hdr_len[0]; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci if ((hdr_field >> 5) & 1) { 218962306a36Sopenharmony_ci hdr = skb_network_header(skb); 219062306a36Sopenharmony_ci memcpy(hdr_data + len, hdr, hdr_len[1]); 219162306a36Sopenharmony_ci len += hdr_len[1]; 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci if ((hdr_field >> 4) & 1) { 219562306a36Sopenharmony_ci hdr = skb_transport_header(skb); 219662306a36Sopenharmony_ci memcpy(hdr_data + len, hdr, hdr_len[2]); 219762306a36Sopenharmony_ci len += hdr_len[2]; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci return len; 220062306a36Sopenharmony_ci} 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci/** 220362306a36Sopenharmony_ci * create_hdr_descs - create header and header extension descriptors 220462306a36Sopenharmony_ci * @hdr_field: bitfield determining needed headers 220562306a36Sopenharmony_ci * @hdr_data: buffer containing header data 220662306a36Sopenharmony_ci * @len: length of data buffer 220762306a36Sopenharmony_ci * @hdr_len: array of individual header lengths 220862306a36Sopenharmony_ci * @scrq_arr: descriptor array 220962306a36Sopenharmony_ci * 221062306a36Sopenharmony_ci * Creates header and, if needed, header extension descriptors and 221162306a36Sopenharmony_ci * places them in a descriptor array, scrq_arr 221262306a36Sopenharmony_ci */ 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic int create_hdr_descs(u8 hdr_field, u8 *hdr_data, int len, int *hdr_len, 221562306a36Sopenharmony_ci union sub_crq *scrq_arr) 221662306a36Sopenharmony_ci{ 221762306a36Sopenharmony_ci union sub_crq hdr_desc; 221862306a36Sopenharmony_ci int tmp_len = len; 221962306a36Sopenharmony_ci int num_descs = 0; 222062306a36Sopenharmony_ci u8 *data, *cur; 222162306a36Sopenharmony_ci int tmp; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci while (tmp_len > 0) { 222462306a36Sopenharmony_ci cur = hdr_data + len - tmp_len; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci memset(&hdr_desc, 0, sizeof(hdr_desc)); 222762306a36Sopenharmony_ci if (cur != hdr_data) { 222862306a36Sopenharmony_ci data = hdr_desc.hdr_ext.data; 222962306a36Sopenharmony_ci tmp = tmp_len > 29 ? 29 : tmp_len; 223062306a36Sopenharmony_ci hdr_desc.hdr_ext.first = IBMVNIC_CRQ_CMD; 223162306a36Sopenharmony_ci hdr_desc.hdr_ext.type = IBMVNIC_HDR_EXT_DESC; 223262306a36Sopenharmony_ci hdr_desc.hdr_ext.len = tmp; 223362306a36Sopenharmony_ci } else { 223462306a36Sopenharmony_ci data = hdr_desc.hdr.data; 223562306a36Sopenharmony_ci tmp = tmp_len > 24 ? 24 : tmp_len; 223662306a36Sopenharmony_ci hdr_desc.hdr.first = IBMVNIC_CRQ_CMD; 223762306a36Sopenharmony_ci hdr_desc.hdr.type = IBMVNIC_HDR_DESC; 223862306a36Sopenharmony_ci hdr_desc.hdr.len = tmp; 223962306a36Sopenharmony_ci hdr_desc.hdr.l2_len = (u8)hdr_len[0]; 224062306a36Sopenharmony_ci hdr_desc.hdr.l3_len = cpu_to_be16((u16)hdr_len[1]); 224162306a36Sopenharmony_ci hdr_desc.hdr.l4_len = (u8)hdr_len[2]; 224262306a36Sopenharmony_ci hdr_desc.hdr.flag = hdr_field << 1; 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci memcpy(data, cur, tmp); 224562306a36Sopenharmony_ci tmp_len -= tmp; 224662306a36Sopenharmony_ci *scrq_arr = hdr_desc; 224762306a36Sopenharmony_ci scrq_arr++; 224862306a36Sopenharmony_ci num_descs++; 224962306a36Sopenharmony_ci } 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci return num_descs; 225262306a36Sopenharmony_ci} 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci/** 225562306a36Sopenharmony_ci * build_hdr_descs_arr - build a header descriptor array 225662306a36Sopenharmony_ci * @skb: tx socket buffer 225762306a36Sopenharmony_ci * @indir_arr: indirect array 225862306a36Sopenharmony_ci * @num_entries: number of descriptors to be sent 225962306a36Sopenharmony_ci * @hdr_field: bit field determining which headers will be sent 226062306a36Sopenharmony_ci * 226162306a36Sopenharmony_ci * This function will build a TX descriptor array with applicable 226262306a36Sopenharmony_ci * L2/L3/L4 packet header descriptors to be sent by send_subcrq_indirect. 226362306a36Sopenharmony_ci */ 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_cistatic void build_hdr_descs_arr(struct sk_buff *skb, 226662306a36Sopenharmony_ci union sub_crq *indir_arr, 226762306a36Sopenharmony_ci int *num_entries, u8 hdr_field) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci int hdr_len[3] = {0, 0, 0}; 227062306a36Sopenharmony_ci u8 hdr_data[140] = {0}; 227162306a36Sopenharmony_ci int tot_len; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci tot_len = build_hdr_data(hdr_field, skb, hdr_len, 227462306a36Sopenharmony_ci hdr_data); 227562306a36Sopenharmony_ci *num_entries += create_hdr_descs(hdr_field, hdr_data, tot_len, hdr_len, 227662306a36Sopenharmony_ci indir_arr + 1); 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_cistatic int ibmvnic_xmit_workarounds(struct sk_buff *skb, 228062306a36Sopenharmony_ci struct net_device *netdev) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci /* For some backing devices, mishandling of small packets 228362306a36Sopenharmony_ci * can result in a loss of connection or TX stall. Device 228462306a36Sopenharmony_ci * architects recommend that no packet should be smaller 228562306a36Sopenharmony_ci * than the minimum MTU value provided to the driver, so 228662306a36Sopenharmony_ci * pad any packets to that length 228762306a36Sopenharmony_ci */ 228862306a36Sopenharmony_ci if (skb->len < netdev->min_mtu) 228962306a36Sopenharmony_ci return skb_put_padto(skb, netdev->min_mtu); 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci return 0; 229262306a36Sopenharmony_ci} 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_cistatic void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter, 229562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *tx_scrq) 229662306a36Sopenharmony_ci{ 229762306a36Sopenharmony_ci struct ibmvnic_ind_xmit_queue *ind_bufp; 229862306a36Sopenharmony_ci struct ibmvnic_tx_buff *tx_buff; 229962306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool; 230062306a36Sopenharmony_ci union sub_crq tx_scrq_entry; 230162306a36Sopenharmony_ci int queue_num; 230262306a36Sopenharmony_ci int entries; 230362306a36Sopenharmony_ci int index; 230462306a36Sopenharmony_ci int i; 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci ind_bufp = &tx_scrq->ind_buf; 230762306a36Sopenharmony_ci entries = (u64)ind_bufp->index; 230862306a36Sopenharmony_ci queue_num = tx_scrq->pool_index; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci for (i = entries - 1; i >= 0; --i) { 231162306a36Sopenharmony_ci tx_scrq_entry = ind_bufp->indir_arr[i]; 231262306a36Sopenharmony_ci if (tx_scrq_entry.v1.type != IBMVNIC_TX_DESC) 231362306a36Sopenharmony_ci continue; 231462306a36Sopenharmony_ci index = be32_to_cpu(tx_scrq_entry.v1.correlator); 231562306a36Sopenharmony_ci if (index & IBMVNIC_TSO_POOL_MASK) { 231662306a36Sopenharmony_ci tx_pool = &adapter->tso_pool[queue_num]; 231762306a36Sopenharmony_ci index &= ~IBMVNIC_TSO_POOL_MASK; 231862306a36Sopenharmony_ci } else { 231962306a36Sopenharmony_ci tx_pool = &adapter->tx_pool[queue_num]; 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci tx_pool->free_map[tx_pool->consumer_index] = index; 232262306a36Sopenharmony_ci tx_pool->consumer_index = tx_pool->consumer_index == 0 ? 232362306a36Sopenharmony_ci tx_pool->num_buffers - 1 : 232462306a36Sopenharmony_ci tx_pool->consumer_index - 1; 232562306a36Sopenharmony_ci tx_buff = &tx_pool->tx_buff[index]; 232662306a36Sopenharmony_ci adapter->netdev->stats.tx_packets--; 232762306a36Sopenharmony_ci adapter->netdev->stats.tx_bytes -= tx_buff->skb->len; 232862306a36Sopenharmony_ci adapter->tx_stats_buffers[queue_num].packets--; 232962306a36Sopenharmony_ci adapter->tx_stats_buffers[queue_num].bytes -= 233062306a36Sopenharmony_ci tx_buff->skb->len; 233162306a36Sopenharmony_ci dev_kfree_skb_any(tx_buff->skb); 233262306a36Sopenharmony_ci tx_buff->skb = NULL; 233362306a36Sopenharmony_ci adapter->netdev->stats.tx_dropped++; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci ind_bufp->index = 0; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci if (atomic_sub_return(entries, &tx_scrq->used) <= 233962306a36Sopenharmony_ci (adapter->req_tx_entries_per_subcrq / 2) && 234062306a36Sopenharmony_ci __netif_subqueue_stopped(adapter->netdev, queue_num)) { 234162306a36Sopenharmony_ci rcu_read_lock(); 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci if (adapter->tx_queues_active) { 234462306a36Sopenharmony_ci netif_wake_subqueue(adapter->netdev, queue_num); 234562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Started queue %d\n", 234662306a36Sopenharmony_ci queue_num); 234762306a36Sopenharmony_ci } 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci rcu_read_unlock(); 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci} 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_cistatic int ibmvnic_tx_scrq_flush(struct ibmvnic_adapter *adapter, 235462306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *tx_scrq) 235562306a36Sopenharmony_ci{ 235662306a36Sopenharmony_ci struct ibmvnic_ind_xmit_queue *ind_bufp; 235762306a36Sopenharmony_ci u64 dma_addr; 235862306a36Sopenharmony_ci u64 entries; 235962306a36Sopenharmony_ci u64 handle; 236062306a36Sopenharmony_ci int rc; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci ind_bufp = &tx_scrq->ind_buf; 236362306a36Sopenharmony_ci dma_addr = (u64)ind_bufp->indir_dma; 236462306a36Sopenharmony_ci entries = (u64)ind_bufp->index; 236562306a36Sopenharmony_ci handle = tx_scrq->handle; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci if (!entries) 236862306a36Sopenharmony_ci return 0; 236962306a36Sopenharmony_ci rc = send_subcrq_indirect(adapter, handle, dma_addr, entries); 237062306a36Sopenharmony_ci if (rc) 237162306a36Sopenharmony_ci ibmvnic_tx_scrq_clean_buffer(adapter, tx_scrq); 237262306a36Sopenharmony_ci else 237362306a36Sopenharmony_ci ind_bufp->index = 0; 237462306a36Sopenharmony_ci return 0; 237562306a36Sopenharmony_ci} 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_cistatic netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) 237862306a36Sopenharmony_ci{ 237962306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 238062306a36Sopenharmony_ci int queue_num = skb_get_queue_mapping(skb); 238162306a36Sopenharmony_ci u8 *hdrs = (u8 *)&adapter->tx_rx_desc_req; 238262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 238362306a36Sopenharmony_ci struct ibmvnic_ind_xmit_queue *ind_bufp; 238462306a36Sopenharmony_ci struct ibmvnic_tx_buff *tx_buff = NULL; 238562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *tx_scrq; 238662306a36Sopenharmony_ci struct ibmvnic_long_term_buff *ltb; 238762306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool; 238862306a36Sopenharmony_ci unsigned int tx_send_failed = 0; 238962306a36Sopenharmony_ci netdev_tx_t ret = NETDEV_TX_OK; 239062306a36Sopenharmony_ci unsigned int tx_map_failed = 0; 239162306a36Sopenharmony_ci union sub_crq indir_arr[16]; 239262306a36Sopenharmony_ci unsigned int tx_dropped = 0; 239362306a36Sopenharmony_ci unsigned int tx_packets = 0; 239462306a36Sopenharmony_ci unsigned int tx_bytes = 0; 239562306a36Sopenharmony_ci dma_addr_t data_dma_addr; 239662306a36Sopenharmony_ci struct netdev_queue *txq; 239762306a36Sopenharmony_ci unsigned long lpar_rc; 239862306a36Sopenharmony_ci union sub_crq tx_crq; 239962306a36Sopenharmony_ci unsigned int offset; 240062306a36Sopenharmony_ci int num_entries = 1; 240162306a36Sopenharmony_ci unsigned char *dst; 240262306a36Sopenharmony_ci int bufidx = 0; 240362306a36Sopenharmony_ci u8 proto = 0; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci /* If a reset is in progress, drop the packet since 240662306a36Sopenharmony_ci * the scrqs may get torn down. Otherwise use the 240762306a36Sopenharmony_ci * rcu to ensure reset waits for us to complete. 240862306a36Sopenharmony_ci */ 240962306a36Sopenharmony_ci rcu_read_lock(); 241062306a36Sopenharmony_ci if (!adapter->tx_queues_active) { 241162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci tx_send_failed++; 241462306a36Sopenharmony_ci tx_dropped++; 241562306a36Sopenharmony_ci ret = NETDEV_TX_OK; 241662306a36Sopenharmony_ci goto out; 241762306a36Sopenharmony_ci } 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci tx_scrq = adapter->tx_scrq[queue_num]; 242062306a36Sopenharmony_ci txq = netdev_get_tx_queue(netdev, queue_num); 242162306a36Sopenharmony_ci ind_bufp = &tx_scrq->ind_buf; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci if (ibmvnic_xmit_workarounds(skb, netdev)) { 242462306a36Sopenharmony_ci tx_dropped++; 242562306a36Sopenharmony_ci tx_send_failed++; 242662306a36Sopenharmony_ci ret = NETDEV_TX_OK; 242762306a36Sopenharmony_ci ibmvnic_tx_scrq_flush(adapter, tx_scrq); 242862306a36Sopenharmony_ci goto out; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci if (skb_is_gso(skb)) 243262306a36Sopenharmony_ci tx_pool = &adapter->tso_pool[queue_num]; 243362306a36Sopenharmony_ci else 243462306a36Sopenharmony_ci tx_pool = &adapter->tx_pool[queue_num]; 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci bufidx = tx_pool->free_map[tx_pool->consumer_index]; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci if (bufidx == IBMVNIC_INVALID_MAP) { 243962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 244062306a36Sopenharmony_ci tx_send_failed++; 244162306a36Sopenharmony_ci tx_dropped++; 244262306a36Sopenharmony_ci ibmvnic_tx_scrq_flush(adapter, tx_scrq); 244362306a36Sopenharmony_ci ret = NETDEV_TX_OK; 244462306a36Sopenharmony_ci goto out; 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci tx_pool->free_map[tx_pool->consumer_index] = IBMVNIC_INVALID_MAP; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci map_txpool_buf_to_ltb(tx_pool, bufidx, <b, &offset); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci dst = ltb->buff + offset; 245262306a36Sopenharmony_ci memset(dst, 0, tx_pool->buf_size); 245362306a36Sopenharmony_ci data_dma_addr = ltb->addr + offset; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci if (skb_shinfo(skb)->nr_frags) { 245662306a36Sopenharmony_ci int cur, i; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci /* Copy the head */ 245962306a36Sopenharmony_ci skb_copy_from_linear_data(skb, dst, skb_headlen(skb)); 246062306a36Sopenharmony_ci cur = skb_headlen(skb); 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci /* Copy the frags */ 246362306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 246462306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci memcpy(dst + cur, skb_frag_address(frag), 246762306a36Sopenharmony_ci skb_frag_size(frag)); 246862306a36Sopenharmony_ci cur += skb_frag_size(frag); 246962306a36Sopenharmony_ci } 247062306a36Sopenharmony_ci } else { 247162306a36Sopenharmony_ci skb_copy_from_linear_data(skb, dst, skb->len); 247262306a36Sopenharmony_ci } 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci /* post changes to long_term_buff *dst before VIOS accessing it */ 247562306a36Sopenharmony_ci dma_wmb(); 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci tx_pool->consumer_index = 247862306a36Sopenharmony_ci (tx_pool->consumer_index + 1) % tx_pool->num_buffers; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci tx_buff = &tx_pool->tx_buff[bufidx]; 248162306a36Sopenharmony_ci tx_buff->skb = skb; 248262306a36Sopenharmony_ci tx_buff->index = bufidx; 248362306a36Sopenharmony_ci tx_buff->pool_index = queue_num; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci memset(&tx_crq, 0, sizeof(tx_crq)); 248662306a36Sopenharmony_ci tx_crq.v1.first = IBMVNIC_CRQ_CMD; 248762306a36Sopenharmony_ci tx_crq.v1.type = IBMVNIC_TX_DESC; 248862306a36Sopenharmony_ci tx_crq.v1.n_crq_elem = 1; 248962306a36Sopenharmony_ci tx_crq.v1.n_sge = 1; 249062306a36Sopenharmony_ci tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci if (skb_is_gso(skb)) 249362306a36Sopenharmony_ci tx_crq.v1.correlator = 249462306a36Sopenharmony_ci cpu_to_be32(bufidx | IBMVNIC_TSO_POOL_MASK); 249562306a36Sopenharmony_ci else 249662306a36Sopenharmony_ci tx_crq.v1.correlator = cpu_to_be32(bufidx); 249762306a36Sopenharmony_ci tx_crq.v1.dma_reg = cpu_to_be16(ltb->map_id); 249862306a36Sopenharmony_ci tx_crq.v1.sge_len = cpu_to_be32(skb->len); 249962306a36Sopenharmony_ci tx_crq.v1.ioba = cpu_to_be64(data_dma_addr); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci if (adapter->vlan_header_insertion && skb_vlan_tag_present(skb)) { 250262306a36Sopenharmony_ci tx_crq.v1.flags2 |= IBMVNIC_TX_VLAN_INSERT; 250362306a36Sopenharmony_ci tx_crq.v1.vlan_id = cpu_to_be16(skb->vlan_tci); 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 250762306a36Sopenharmony_ci tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV4; 250862306a36Sopenharmony_ci proto = ip_hdr(skb)->protocol; 250962306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6)) { 251062306a36Sopenharmony_ci tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV6; 251162306a36Sopenharmony_ci proto = ipv6_hdr(skb)->nexthdr; 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci if (proto == IPPROTO_TCP) 251562306a36Sopenharmony_ci tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_TCP; 251662306a36Sopenharmony_ci else if (proto == IPPROTO_UDP) 251762306a36Sopenharmony_ci tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_UDP; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 252062306a36Sopenharmony_ci tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD; 252162306a36Sopenharmony_ci hdrs += 2; 252262306a36Sopenharmony_ci } 252362306a36Sopenharmony_ci if (skb_is_gso(skb)) { 252462306a36Sopenharmony_ci tx_crq.v1.flags1 |= IBMVNIC_TX_LSO; 252562306a36Sopenharmony_ci tx_crq.v1.mss = cpu_to_be16(skb_shinfo(skb)->gso_size); 252662306a36Sopenharmony_ci hdrs += 2; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci if ((*hdrs >> 7) & 1) 253062306a36Sopenharmony_ci build_hdr_descs_arr(skb, indir_arr, &num_entries, *hdrs); 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci tx_crq.v1.n_crq_elem = num_entries; 253362306a36Sopenharmony_ci tx_buff->num_entries = num_entries; 253462306a36Sopenharmony_ci /* flush buffer if current entry can not fit */ 253562306a36Sopenharmony_ci if (num_entries + ind_bufp->index > IBMVNIC_MAX_IND_DESCS) { 253662306a36Sopenharmony_ci lpar_rc = ibmvnic_tx_scrq_flush(adapter, tx_scrq); 253762306a36Sopenharmony_ci if (lpar_rc != H_SUCCESS) 253862306a36Sopenharmony_ci goto tx_flush_err; 253962306a36Sopenharmony_ci } 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci indir_arr[0] = tx_crq; 254262306a36Sopenharmony_ci memcpy(&ind_bufp->indir_arr[ind_bufp->index], &indir_arr[0], 254362306a36Sopenharmony_ci num_entries * sizeof(struct ibmvnic_generic_scrq)); 254462306a36Sopenharmony_ci ind_bufp->index += num_entries; 254562306a36Sopenharmony_ci if (__netdev_tx_sent_queue(txq, skb->len, 254662306a36Sopenharmony_ci netdev_xmit_more() && 254762306a36Sopenharmony_ci ind_bufp->index < IBMVNIC_MAX_IND_DESCS)) { 254862306a36Sopenharmony_ci lpar_rc = ibmvnic_tx_scrq_flush(adapter, tx_scrq); 254962306a36Sopenharmony_ci if (lpar_rc != H_SUCCESS) 255062306a36Sopenharmony_ci goto tx_err; 255162306a36Sopenharmony_ci } 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci if (atomic_add_return(num_entries, &tx_scrq->used) 255462306a36Sopenharmony_ci >= adapter->req_tx_entries_per_subcrq) { 255562306a36Sopenharmony_ci netdev_dbg(netdev, "Stopping queue %d\n", queue_num); 255662306a36Sopenharmony_ci netif_stop_subqueue(netdev, queue_num); 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci tx_packets++; 256062306a36Sopenharmony_ci tx_bytes += skb->len; 256162306a36Sopenharmony_ci txq_trans_cond_update(txq); 256262306a36Sopenharmony_ci ret = NETDEV_TX_OK; 256362306a36Sopenharmony_ci goto out; 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_citx_flush_err: 256662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 256762306a36Sopenharmony_ci tx_buff->skb = NULL; 256862306a36Sopenharmony_ci tx_pool->consumer_index = tx_pool->consumer_index == 0 ? 256962306a36Sopenharmony_ci tx_pool->num_buffers - 1 : 257062306a36Sopenharmony_ci tx_pool->consumer_index - 1; 257162306a36Sopenharmony_ci tx_dropped++; 257262306a36Sopenharmony_citx_err: 257362306a36Sopenharmony_ci if (lpar_rc != H_CLOSED && lpar_rc != H_PARAMETER) 257462306a36Sopenharmony_ci dev_err_ratelimited(dev, "tx: send failed\n"); 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci if (lpar_rc == H_CLOSED || adapter->failover_pending) { 257762306a36Sopenharmony_ci /* Disable TX and report carrier off if queue is closed 257862306a36Sopenharmony_ci * or pending failover. 257962306a36Sopenharmony_ci * Firmware guarantees that a signal will be sent to the 258062306a36Sopenharmony_ci * driver, triggering a reset or some other action. 258162306a36Sopenharmony_ci */ 258262306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 258362306a36Sopenharmony_ci netif_carrier_off(netdev); 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ciout: 258662306a36Sopenharmony_ci rcu_read_unlock(); 258762306a36Sopenharmony_ci netdev->stats.tx_dropped += tx_dropped; 258862306a36Sopenharmony_ci netdev->stats.tx_bytes += tx_bytes; 258962306a36Sopenharmony_ci netdev->stats.tx_packets += tx_packets; 259062306a36Sopenharmony_ci adapter->tx_send_failed += tx_send_failed; 259162306a36Sopenharmony_ci adapter->tx_map_failed += tx_map_failed; 259262306a36Sopenharmony_ci adapter->tx_stats_buffers[queue_num].packets += tx_packets; 259362306a36Sopenharmony_ci adapter->tx_stats_buffers[queue_num].bytes += tx_bytes; 259462306a36Sopenharmony_ci adapter->tx_stats_buffers[queue_num].dropped_packets += tx_dropped; 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci return ret; 259762306a36Sopenharmony_ci} 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_cistatic void ibmvnic_set_multi(struct net_device *netdev) 260062306a36Sopenharmony_ci{ 260162306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 260262306a36Sopenharmony_ci struct netdev_hw_addr *ha; 260362306a36Sopenharmony_ci union ibmvnic_crq crq; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 260662306a36Sopenharmony_ci crq.request_capability.first = IBMVNIC_CRQ_CMD; 260762306a36Sopenharmony_ci crq.request_capability.cmd = REQUEST_CAPABILITY; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 261062306a36Sopenharmony_ci if (!adapter->promisc_supported) 261162306a36Sopenharmony_ci return; 261262306a36Sopenharmony_ci } else { 261362306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) { 261462306a36Sopenharmony_ci /* Accept all multicast */ 261562306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 261662306a36Sopenharmony_ci crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; 261762306a36Sopenharmony_ci crq.multicast_ctrl.cmd = MULTICAST_CTRL; 261862306a36Sopenharmony_ci crq.multicast_ctrl.flags = IBMVNIC_ENABLE_ALL; 261962306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 262062306a36Sopenharmony_ci } else if (netdev_mc_empty(netdev)) { 262162306a36Sopenharmony_ci /* Reject all multicast */ 262262306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 262362306a36Sopenharmony_ci crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; 262462306a36Sopenharmony_ci crq.multicast_ctrl.cmd = MULTICAST_CTRL; 262562306a36Sopenharmony_ci crq.multicast_ctrl.flags = IBMVNIC_DISABLE_ALL; 262662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 262762306a36Sopenharmony_ci } else { 262862306a36Sopenharmony_ci /* Accept one or more multicast(s) */ 262962306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 263062306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 263162306a36Sopenharmony_ci crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; 263262306a36Sopenharmony_ci crq.multicast_ctrl.cmd = MULTICAST_CTRL; 263362306a36Sopenharmony_ci crq.multicast_ctrl.flags = IBMVNIC_ENABLE_MC; 263462306a36Sopenharmony_ci ether_addr_copy(&crq.multicast_ctrl.mac_addr[0], 263562306a36Sopenharmony_ci ha->addr); 263662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 263762306a36Sopenharmony_ci } 263862306a36Sopenharmony_ci } 263962306a36Sopenharmony_ci } 264062306a36Sopenharmony_ci} 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_cistatic int __ibmvnic_set_mac(struct net_device *netdev, u8 *dev_addr) 264362306a36Sopenharmony_ci{ 264462306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 264562306a36Sopenharmony_ci union ibmvnic_crq crq; 264662306a36Sopenharmony_ci int rc; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci if (!is_valid_ether_addr(dev_addr)) { 264962306a36Sopenharmony_ci rc = -EADDRNOTAVAIL; 265062306a36Sopenharmony_ci goto err; 265162306a36Sopenharmony_ci } 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 265462306a36Sopenharmony_ci crq.change_mac_addr.first = IBMVNIC_CRQ_CMD; 265562306a36Sopenharmony_ci crq.change_mac_addr.cmd = CHANGE_MAC_ADDR; 265662306a36Sopenharmony_ci ether_addr_copy(&crq.change_mac_addr.mac_addr[0], dev_addr); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci mutex_lock(&adapter->fw_lock); 265962306a36Sopenharmony_ci adapter->fw_done_rc = 0; 266062306a36Sopenharmony_ci reinit_completion(&adapter->fw_done); 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 266362306a36Sopenharmony_ci if (rc) { 266462306a36Sopenharmony_ci rc = -EIO; 266562306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 266662306a36Sopenharmony_ci goto err; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); 267062306a36Sopenharmony_ci /* netdev->dev_addr is changed in handle_change_mac_rsp function */ 267162306a36Sopenharmony_ci if (rc || adapter->fw_done_rc) { 267262306a36Sopenharmony_ci rc = -EIO; 267362306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 267462306a36Sopenharmony_ci goto err; 267562306a36Sopenharmony_ci } 267662306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 267762306a36Sopenharmony_ci return 0; 267862306a36Sopenharmony_cierr: 267962306a36Sopenharmony_ci ether_addr_copy(adapter->mac_addr, netdev->dev_addr); 268062306a36Sopenharmony_ci return rc; 268162306a36Sopenharmony_ci} 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_cistatic int ibmvnic_set_mac(struct net_device *netdev, void *p) 268462306a36Sopenharmony_ci{ 268562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 268662306a36Sopenharmony_ci struct sockaddr *addr = p; 268762306a36Sopenharmony_ci int rc; 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci rc = 0; 269062306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 269162306a36Sopenharmony_ci return -EADDRNOTAVAIL; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci ether_addr_copy(adapter->mac_addr, addr->sa_data); 269462306a36Sopenharmony_ci if (adapter->state != VNIC_PROBED) 269562306a36Sopenharmony_ci rc = __ibmvnic_set_mac(netdev, addr->sa_data); 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci return rc; 269862306a36Sopenharmony_ci} 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_cistatic const char *reset_reason_to_string(enum ibmvnic_reset_reason reason) 270162306a36Sopenharmony_ci{ 270262306a36Sopenharmony_ci switch (reason) { 270362306a36Sopenharmony_ci case VNIC_RESET_FAILOVER: 270462306a36Sopenharmony_ci return "FAILOVER"; 270562306a36Sopenharmony_ci case VNIC_RESET_MOBILITY: 270662306a36Sopenharmony_ci return "MOBILITY"; 270762306a36Sopenharmony_ci case VNIC_RESET_FATAL: 270862306a36Sopenharmony_ci return "FATAL"; 270962306a36Sopenharmony_ci case VNIC_RESET_NON_FATAL: 271062306a36Sopenharmony_ci return "NON_FATAL"; 271162306a36Sopenharmony_ci case VNIC_RESET_TIMEOUT: 271262306a36Sopenharmony_ci return "TIMEOUT"; 271362306a36Sopenharmony_ci case VNIC_RESET_CHANGE_PARAM: 271462306a36Sopenharmony_ci return "CHANGE_PARAM"; 271562306a36Sopenharmony_ci case VNIC_RESET_PASSIVE_INIT: 271662306a36Sopenharmony_ci return "PASSIVE_INIT"; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci return "UNKNOWN"; 271962306a36Sopenharmony_ci} 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci/* 272262306a36Sopenharmony_ci * Initialize the init_done completion and return code values. We 272362306a36Sopenharmony_ci * can get a transport event just after registering the CRQ and the 272462306a36Sopenharmony_ci * tasklet will use this to communicate the transport event. To ensure 272562306a36Sopenharmony_ci * we don't miss the notification/error, initialize these _before_ 272662306a36Sopenharmony_ci * regisering the CRQ. 272762306a36Sopenharmony_ci */ 272862306a36Sopenharmony_cistatic inline void reinit_init_done(struct ibmvnic_adapter *adapter) 272962306a36Sopenharmony_ci{ 273062306a36Sopenharmony_ci reinit_completion(&adapter->init_done); 273162306a36Sopenharmony_ci adapter->init_done_rc = 0; 273262306a36Sopenharmony_ci} 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci/* 273562306a36Sopenharmony_ci * do_reset returns zero if we are able to keep processing reset events, or 273662306a36Sopenharmony_ci * non-zero if we hit a fatal error and must halt. 273762306a36Sopenharmony_ci */ 273862306a36Sopenharmony_cistatic int do_reset(struct ibmvnic_adapter *adapter, 273962306a36Sopenharmony_ci struct ibmvnic_rwi *rwi, u32 reset_state) 274062306a36Sopenharmony_ci{ 274162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 274262306a36Sopenharmony_ci u64 old_num_rx_queues, old_num_tx_queues; 274362306a36Sopenharmony_ci u64 old_num_rx_slots, old_num_tx_slots; 274462306a36Sopenharmony_ci int rc; 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 274762306a36Sopenharmony_ci "[S:%s FOP:%d] Reset reason: %s, reset_state: %s\n", 274862306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 274962306a36Sopenharmony_ci adapter->failover_pending, 275062306a36Sopenharmony_ci reset_reason_to_string(rwi->reset_reason), 275162306a36Sopenharmony_ci adapter_state_to_string(reset_state)); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci adapter->reset_reason = rwi->reset_reason; 275462306a36Sopenharmony_ci /* requestor of VNIC_RESET_CHANGE_PARAM already has the rtnl lock */ 275562306a36Sopenharmony_ci if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM)) 275662306a36Sopenharmony_ci rtnl_lock(); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci /* Now that we have the rtnl lock, clear any pending failover. 275962306a36Sopenharmony_ci * This will ensure ibmvnic_open() has either completed or will 276062306a36Sopenharmony_ci * block until failover is complete. 276162306a36Sopenharmony_ci */ 276262306a36Sopenharmony_ci if (rwi->reset_reason == VNIC_RESET_FAILOVER) 276362306a36Sopenharmony_ci adapter->failover_pending = false; 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci /* read the state and check (again) after getting rtnl */ 276662306a36Sopenharmony_ci reset_state = adapter->state; 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci if (reset_state == VNIC_REMOVING || reset_state == VNIC_REMOVED) { 276962306a36Sopenharmony_ci rc = -EBUSY; 277062306a36Sopenharmony_ci goto out; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci netif_carrier_off(netdev); 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci old_num_rx_queues = adapter->req_rx_queues; 277662306a36Sopenharmony_ci old_num_tx_queues = adapter->req_tx_queues; 277762306a36Sopenharmony_ci old_num_rx_slots = adapter->req_rx_add_entries_per_subcrq; 277862306a36Sopenharmony_ci old_num_tx_slots = adapter->req_tx_entries_per_subcrq; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci ibmvnic_cleanup(netdev); 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci if (reset_state == VNIC_OPEN && 278362306a36Sopenharmony_ci adapter->reset_reason != VNIC_RESET_MOBILITY && 278462306a36Sopenharmony_ci adapter->reset_reason != VNIC_RESET_FAILOVER) { 278562306a36Sopenharmony_ci if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { 278662306a36Sopenharmony_ci rc = __ibmvnic_close(netdev); 278762306a36Sopenharmony_ci if (rc) 278862306a36Sopenharmony_ci goto out; 278962306a36Sopenharmony_ci } else { 279062306a36Sopenharmony_ci adapter->state = VNIC_CLOSING; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci /* Release the RTNL lock before link state change and 279362306a36Sopenharmony_ci * re-acquire after the link state change to allow 279462306a36Sopenharmony_ci * linkwatch_event to grab the RTNL lock and run during 279562306a36Sopenharmony_ci * a reset. 279662306a36Sopenharmony_ci */ 279762306a36Sopenharmony_ci rtnl_unlock(); 279862306a36Sopenharmony_ci rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); 279962306a36Sopenharmony_ci rtnl_lock(); 280062306a36Sopenharmony_ci if (rc) 280162306a36Sopenharmony_ci goto out; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci if (adapter->state == VNIC_OPEN) { 280462306a36Sopenharmony_ci /* When we dropped rtnl, ibmvnic_open() got 280562306a36Sopenharmony_ci * it and noticed that we are resetting and 280662306a36Sopenharmony_ci * set the adapter state to OPEN. Update our 280762306a36Sopenharmony_ci * new "target" state, and resume the reset 280862306a36Sopenharmony_ci * from VNIC_CLOSING state. 280962306a36Sopenharmony_ci */ 281062306a36Sopenharmony_ci netdev_dbg(netdev, 281162306a36Sopenharmony_ci "Open changed state from %s, updating.\n", 281262306a36Sopenharmony_ci adapter_state_to_string(reset_state)); 281362306a36Sopenharmony_ci reset_state = VNIC_OPEN; 281462306a36Sopenharmony_ci adapter->state = VNIC_CLOSING; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci if (adapter->state != VNIC_CLOSING) { 281862306a36Sopenharmony_ci /* If someone else changed the adapter state 281962306a36Sopenharmony_ci * when we dropped the rtnl, fail the reset 282062306a36Sopenharmony_ci */ 282162306a36Sopenharmony_ci rc = -EAGAIN; 282262306a36Sopenharmony_ci goto out; 282362306a36Sopenharmony_ci } 282462306a36Sopenharmony_ci adapter->state = VNIC_CLOSED; 282562306a36Sopenharmony_ci } 282662306a36Sopenharmony_ci } 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { 282962306a36Sopenharmony_ci release_resources(adapter); 283062306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 283162306a36Sopenharmony_ci release_crq_queue(adapter); 283262306a36Sopenharmony_ci } 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci if (adapter->reset_reason != VNIC_RESET_NON_FATAL) { 283562306a36Sopenharmony_ci /* remove the closed state so when we call open it appears 283662306a36Sopenharmony_ci * we are coming from the probed state. 283762306a36Sopenharmony_ci */ 283862306a36Sopenharmony_ci adapter->state = VNIC_PROBED; 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci reinit_init_done(adapter); 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { 284362306a36Sopenharmony_ci rc = init_crq_queue(adapter); 284462306a36Sopenharmony_ci } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) { 284562306a36Sopenharmony_ci rc = ibmvnic_reenable_crq_queue(adapter); 284662306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 284762306a36Sopenharmony_ci } else { 284862306a36Sopenharmony_ci rc = ibmvnic_reset_crq(adapter); 284962306a36Sopenharmony_ci if (rc == H_CLOSED || rc == H_SUCCESS) { 285062306a36Sopenharmony_ci rc = vio_enable_interrupts(adapter->vdev); 285162306a36Sopenharmony_ci if (rc) 285262306a36Sopenharmony_ci netdev_err(adapter->netdev, 285362306a36Sopenharmony_ci "Reset failed to enable interrupts. rc=%d\n", 285462306a36Sopenharmony_ci rc); 285562306a36Sopenharmony_ci } 285662306a36Sopenharmony_ci } 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci if (rc) { 285962306a36Sopenharmony_ci netdev_err(adapter->netdev, 286062306a36Sopenharmony_ci "Reset couldn't initialize crq. rc=%d\n", rc); 286162306a36Sopenharmony_ci goto out; 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci rc = ibmvnic_reset_init(adapter, true); 286562306a36Sopenharmony_ci if (rc) 286662306a36Sopenharmony_ci goto out; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci /* If the adapter was in PROBE or DOWN state prior to the reset, 286962306a36Sopenharmony_ci * exit here. 287062306a36Sopenharmony_ci */ 287162306a36Sopenharmony_ci if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN) { 287262306a36Sopenharmony_ci rc = 0; 287362306a36Sopenharmony_ci goto out; 287462306a36Sopenharmony_ci } 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci rc = ibmvnic_login(netdev); 287762306a36Sopenharmony_ci if (rc) 287862306a36Sopenharmony_ci goto out; 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { 288162306a36Sopenharmony_ci rc = init_resources(adapter); 288262306a36Sopenharmony_ci if (rc) 288362306a36Sopenharmony_ci goto out; 288462306a36Sopenharmony_ci } else if (adapter->req_rx_queues != old_num_rx_queues || 288562306a36Sopenharmony_ci adapter->req_tx_queues != old_num_tx_queues || 288662306a36Sopenharmony_ci adapter->req_rx_add_entries_per_subcrq != 288762306a36Sopenharmony_ci old_num_rx_slots || 288862306a36Sopenharmony_ci adapter->req_tx_entries_per_subcrq != 288962306a36Sopenharmony_ci old_num_tx_slots || 289062306a36Sopenharmony_ci !adapter->rx_pool || 289162306a36Sopenharmony_ci !adapter->tso_pool || 289262306a36Sopenharmony_ci !adapter->tx_pool) { 289362306a36Sopenharmony_ci release_napi(adapter); 289462306a36Sopenharmony_ci release_vpd_data(adapter); 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci rc = init_resources(adapter); 289762306a36Sopenharmony_ci if (rc) 289862306a36Sopenharmony_ci goto out; 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci } else { 290162306a36Sopenharmony_ci rc = init_tx_pools(netdev); 290262306a36Sopenharmony_ci if (rc) { 290362306a36Sopenharmony_ci netdev_dbg(netdev, 290462306a36Sopenharmony_ci "init tx pools failed (%d)\n", 290562306a36Sopenharmony_ci rc); 290662306a36Sopenharmony_ci goto out; 290762306a36Sopenharmony_ci } 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci rc = init_rx_pools(netdev); 291062306a36Sopenharmony_ci if (rc) { 291162306a36Sopenharmony_ci netdev_dbg(netdev, 291262306a36Sopenharmony_ci "init rx pools failed (%d)\n", 291362306a36Sopenharmony_ci rc); 291462306a36Sopenharmony_ci goto out; 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci } 291762306a36Sopenharmony_ci ibmvnic_disable_irqs(adapter); 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci adapter->state = VNIC_CLOSED; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci if (reset_state == VNIC_CLOSED) { 292262306a36Sopenharmony_ci rc = 0; 292362306a36Sopenharmony_ci goto out; 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci rc = __ibmvnic_open(netdev); 292762306a36Sopenharmony_ci if (rc) { 292862306a36Sopenharmony_ci rc = IBMVNIC_OPEN_FAILED; 292962306a36Sopenharmony_ci goto out; 293062306a36Sopenharmony_ci } 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci /* refresh device's multicast list */ 293362306a36Sopenharmony_ci ibmvnic_set_multi(netdev); 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci if (adapter->reset_reason == VNIC_RESET_FAILOVER || 293662306a36Sopenharmony_ci adapter->reset_reason == VNIC_RESET_MOBILITY) 293762306a36Sopenharmony_ci __netdev_notify_peers(netdev); 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci rc = 0; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ciout: 294262306a36Sopenharmony_ci /* restore the adapter state if reset failed */ 294362306a36Sopenharmony_ci if (rc) 294462306a36Sopenharmony_ci adapter->state = reset_state; 294562306a36Sopenharmony_ci /* requestor of VNIC_RESET_CHANGE_PARAM should still hold the rtnl lock */ 294662306a36Sopenharmony_ci if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM)) 294762306a36Sopenharmony_ci rtnl_unlock(); 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "[S:%s FOP:%d] Reset done, rc %d\n", 295062306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 295162306a36Sopenharmony_ci adapter->failover_pending, rc); 295262306a36Sopenharmony_ci return rc; 295362306a36Sopenharmony_ci} 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_cistatic int do_hard_reset(struct ibmvnic_adapter *adapter, 295662306a36Sopenharmony_ci struct ibmvnic_rwi *rwi, u32 reset_state) 295762306a36Sopenharmony_ci{ 295862306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 295962306a36Sopenharmony_ci int rc; 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Hard resetting driver (%s)\n", 296262306a36Sopenharmony_ci reset_reason_to_string(rwi->reset_reason)); 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci /* read the state and check (again) after getting rtnl */ 296562306a36Sopenharmony_ci reset_state = adapter->state; 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci if (reset_state == VNIC_REMOVING || reset_state == VNIC_REMOVED) { 296862306a36Sopenharmony_ci rc = -EBUSY; 296962306a36Sopenharmony_ci goto out; 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci netif_carrier_off(netdev); 297362306a36Sopenharmony_ci adapter->reset_reason = rwi->reset_reason; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci ibmvnic_cleanup(netdev); 297662306a36Sopenharmony_ci release_resources(adapter); 297762306a36Sopenharmony_ci release_sub_crqs(adapter, 0); 297862306a36Sopenharmony_ci release_crq_queue(adapter); 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci /* remove the closed state so when we call open it appears 298162306a36Sopenharmony_ci * we are coming from the probed state. 298262306a36Sopenharmony_ci */ 298362306a36Sopenharmony_ci adapter->state = VNIC_PROBED; 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci reinit_init_done(adapter); 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci rc = init_crq_queue(adapter); 298862306a36Sopenharmony_ci if (rc) { 298962306a36Sopenharmony_ci netdev_err(adapter->netdev, 299062306a36Sopenharmony_ci "Couldn't initialize crq. rc=%d\n", rc); 299162306a36Sopenharmony_ci goto out; 299262306a36Sopenharmony_ci } 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci rc = ibmvnic_reset_init(adapter, false); 299562306a36Sopenharmony_ci if (rc) 299662306a36Sopenharmony_ci goto out; 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci /* If the adapter was in PROBE or DOWN state prior to the reset, 299962306a36Sopenharmony_ci * exit here. 300062306a36Sopenharmony_ci */ 300162306a36Sopenharmony_ci if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN) 300262306a36Sopenharmony_ci goto out; 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci rc = ibmvnic_login(netdev); 300562306a36Sopenharmony_ci if (rc) 300662306a36Sopenharmony_ci goto out; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci rc = init_resources(adapter); 300962306a36Sopenharmony_ci if (rc) 301062306a36Sopenharmony_ci goto out; 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci ibmvnic_disable_irqs(adapter); 301362306a36Sopenharmony_ci adapter->state = VNIC_CLOSED; 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci if (reset_state == VNIC_CLOSED) 301662306a36Sopenharmony_ci goto out; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci rc = __ibmvnic_open(netdev); 301962306a36Sopenharmony_ci if (rc) { 302062306a36Sopenharmony_ci rc = IBMVNIC_OPEN_FAILED; 302162306a36Sopenharmony_ci goto out; 302262306a36Sopenharmony_ci } 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci __netdev_notify_peers(netdev); 302562306a36Sopenharmony_ciout: 302662306a36Sopenharmony_ci /* restore adapter state if reset failed */ 302762306a36Sopenharmony_ci if (rc) 302862306a36Sopenharmony_ci adapter->state = reset_state; 302962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "[S:%s FOP:%d] Hard reset done, rc %d\n", 303062306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 303162306a36Sopenharmony_ci adapter->failover_pending, rc); 303262306a36Sopenharmony_ci return rc; 303362306a36Sopenharmony_ci} 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_cistatic struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter) 303662306a36Sopenharmony_ci{ 303762306a36Sopenharmony_ci struct ibmvnic_rwi *rwi; 303862306a36Sopenharmony_ci unsigned long flags; 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci spin_lock_irqsave(&adapter->rwi_lock, flags); 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci if (!list_empty(&adapter->rwi_list)) { 304362306a36Sopenharmony_ci rwi = list_first_entry(&adapter->rwi_list, struct ibmvnic_rwi, 304462306a36Sopenharmony_ci list); 304562306a36Sopenharmony_ci list_del(&rwi->list); 304662306a36Sopenharmony_ci } else { 304762306a36Sopenharmony_ci rwi = NULL; 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->rwi_lock, flags); 305162306a36Sopenharmony_ci return rwi; 305262306a36Sopenharmony_ci} 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci/** 305562306a36Sopenharmony_ci * do_passive_init - complete probing when partner device is detected. 305662306a36Sopenharmony_ci * @adapter: ibmvnic_adapter struct 305762306a36Sopenharmony_ci * 305862306a36Sopenharmony_ci * If the ibmvnic device does not have a partner device to communicate with at boot 305962306a36Sopenharmony_ci * and that partner device comes online at a later time, this function is called 306062306a36Sopenharmony_ci * to complete the initialization process of ibmvnic device. 306162306a36Sopenharmony_ci * Caller is expected to hold rtnl_lock(). 306262306a36Sopenharmony_ci * 306362306a36Sopenharmony_ci * Returns non-zero if sub-CRQs are not initialized properly leaving the device 306462306a36Sopenharmony_ci * in the down state. 306562306a36Sopenharmony_ci * Returns 0 upon success and the device is in PROBED state. 306662306a36Sopenharmony_ci */ 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_cistatic int do_passive_init(struct ibmvnic_adapter *adapter) 306962306a36Sopenharmony_ci{ 307062306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(30000); 307162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 307262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 307362306a36Sopenharmony_ci int rc; 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci netdev_dbg(netdev, "Partner device found, probing.\n"); 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci adapter->state = VNIC_PROBING; 307862306a36Sopenharmony_ci reinit_completion(&adapter->init_done); 307962306a36Sopenharmony_ci adapter->init_done_rc = 0; 308062306a36Sopenharmony_ci adapter->crq.active = true; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci rc = send_crq_init_complete(adapter); 308362306a36Sopenharmony_ci if (rc) 308462306a36Sopenharmony_ci goto out; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci rc = send_version_xchg(adapter); 308762306a36Sopenharmony_ci if (rc) 308862306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "send_version_xchg failed, rc=%d\n", rc); 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { 309162306a36Sopenharmony_ci dev_err(dev, "Initialization sequence timed out\n"); 309262306a36Sopenharmony_ci rc = -ETIMEDOUT; 309362306a36Sopenharmony_ci goto out; 309462306a36Sopenharmony_ci } 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci rc = init_sub_crqs(adapter); 309762306a36Sopenharmony_ci if (rc) { 309862306a36Sopenharmony_ci dev_err(dev, "Initialization of sub crqs failed, rc=%d\n", rc); 309962306a36Sopenharmony_ci goto out; 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci rc = init_sub_crq_irqs(adapter); 310362306a36Sopenharmony_ci if (rc) { 310462306a36Sopenharmony_ci dev_err(dev, "Failed to initialize sub crq irqs\n, rc=%d", rc); 310562306a36Sopenharmony_ci goto init_failed; 310662306a36Sopenharmony_ci } 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci netdev->mtu = adapter->req_mtu - ETH_HLEN; 310962306a36Sopenharmony_ci netdev->min_mtu = adapter->min_mtu - ETH_HLEN; 311062306a36Sopenharmony_ci netdev->max_mtu = adapter->max_mtu - ETH_HLEN; 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci adapter->state = VNIC_PROBED; 311362306a36Sopenharmony_ci netdev_dbg(netdev, "Probed successfully. Waiting for signal from partner device.\n"); 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci return 0; 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ciinit_failed: 311862306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 311962306a36Sopenharmony_ciout: 312062306a36Sopenharmony_ci adapter->state = VNIC_DOWN; 312162306a36Sopenharmony_ci return rc; 312262306a36Sopenharmony_ci} 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_cistatic void __ibmvnic_reset(struct work_struct *work) 312562306a36Sopenharmony_ci{ 312662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 312762306a36Sopenharmony_ci unsigned int timeout = 5000; 312862306a36Sopenharmony_ci struct ibmvnic_rwi *tmprwi; 312962306a36Sopenharmony_ci bool saved_state = false; 313062306a36Sopenharmony_ci struct ibmvnic_rwi *rwi; 313162306a36Sopenharmony_ci unsigned long flags; 313262306a36Sopenharmony_ci struct device *dev; 313362306a36Sopenharmony_ci bool need_reset; 313462306a36Sopenharmony_ci int num_fails = 0; 313562306a36Sopenharmony_ci u32 reset_state; 313662306a36Sopenharmony_ci int rc = 0; 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); 313962306a36Sopenharmony_ci dev = &adapter->vdev->dev; 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_ci /* Wait for ibmvnic_probe() to complete. If probe is taking too long 314262306a36Sopenharmony_ci * or if another reset is in progress, defer work for now. If probe 314362306a36Sopenharmony_ci * eventually fails it will flush and terminate our work. 314462306a36Sopenharmony_ci * 314562306a36Sopenharmony_ci * Three possibilities here: 314662306a36Sopenharmony_ci * 1. Adpater being removed - just return 314762306a36Sopenharmony_ci * 2. Timed out on probe or another reset in progress - delay the work 314862306a36Sopenharmony_ci * 3. Completed probe - perform any resets in queue 314962306a36Sopenharmony_ci */ 315062306a36Sopenharmony_ci if (adapter->state == VNIC_PROBING && 315162306a36Sopenharmony_ci !wait_for_completion_timeout(&adapter->probe_done, timeout)) { 315262306a36Sopenharmony_ci dev_err(dev, "Reset thread timed out on probe"); 315362306a36Sopenharmony_ci queue_delayed_work(system_long_wq, 315462306a36Sopenharmony_ci &adapter->ibmvnic_delayed_reset, 315562306a36Sopenharmony_ci IBMVNIC_RESET_DELAY); 315662306a36Sopenharmony_ci return; 315762306a36Sopenharmony_ci } 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci /* adapter is done with probe (i.e state is never VNIC_PROBING now) */ 316062306a36Sopenharmony_ci if (adapter->state == VNIC_REMOVING) 316162306a36Sopenharmony_ci return; 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci /* ->rwi_list is stable now (no one else is removing entries) */ 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci /* ibmvnic_probe() may have purged the reset queue after we were 316662306a36Sopenharmony_ci * scheduled to process a reset so there maybe no resets to process. 316762306a36Sopenharmony_ci * Before setting the ->resetting bit though, we have to make sure 316862306a36Sopenharmony_ci * that there is infact a reset to process. Otherwise we may race 316962306a36Sopenharmony_ci * with ibmvnic_open() and end up leaving the vnic down: 317062306a36Sopenharmony_ci * 317162306a36Sopenharmony_ci * __ibmvnic_reset() ibmvnic_open() 317262306a36Sopenharmony_ci * ----------------- -------------- 317362306a36Sopenharmony_ci * 317462306a36Sopenharmony_ci * set ->resetting bit 317562306a36Sopenharmony_ci * find ->resetting bit is set 317662306a36Sopenharmony_ci * set ->state to IBMVNIC_OPEN (i.e 317762306a36Sopenharmony_ci * assume reset will open device) 317862306a36Sopenharmony_ci * return 317962306a36Sopenharmony_ci * find reset queue empty 318062306a36Sopenharmony_ci * return 318162306a36Sopenharmony_ci * 318262306a36Sopenharmony_ci * Neither performed vnic login/open and vnic stays down 318362306a36Sopenharmony_ci * 318462306a36Sopenharmony_ci * If we hold the lock and conditionally set the bit, either we 318562306a36Sopenharmony_ci * or ibmvnic_open() will complete the open. 318662306a36Sopenharmony_ci */ 318762306a36Sopenharmony_ci need_reset = false; 318862306a36Sopenharmony_ci spin_lock(&adapter->rwi_lock); 318962306a36Sopenharmony_ci if (!list_empty(&adapter->rwi_list)) { 319062306a36Sopenharmony_ci if (test_and_set_bit_lock(0, &adapter->resetting)) { 319162306a36Sopenharmony_ci queue_delayed_work(system_long_wq, 319262306a36Sopenharmony_ci &adapter->ibmvnic_delayed_reset, 319362306a36Sopenharmony_ci IBMVNIC_RESET_DELAY); 319462306a36Sopenharmony_ci } else { 319562306a36Sopenharmony_ci need_reset = true; 319662306a36Sopenharmony_ci } 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci spin_unlock(&adapter->rwi_lock); 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci if (!need_reset) 320162306a36Sopenharmony_ci return; 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci rwi = get_next_rwi(adapter); 320462306a36Sopenharmony_ci while (rwi) { 320562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->state_lock, flags); 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci if (adapter->state == VNIC_REMOVING || 320862306a36Sopenharmony_ci adapter->state == VNIC_REMOVED) { 320962306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->state_lock, flags); 321062306a36Sopenharmony_ci kfree(rwi); 321162306a36Sopenharmony_ci rc = EBUSY; 321262306a36Sopenharmony_ci break; 321362306a36Sopenharmony_ci } 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci if (!saved_state) { 321662306a36Sopenharmony_ci reset_state = adapter->state; 321762306a36Sopenharmony_ci saved_state = true; 321862306a36Sopenharmony_ci } 321962306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->state_lock, flags); 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci if (rwi->reset_reason == VNIC_RESET_PASSIVE_INIT) { 322262306a36Sopenharmony_ci rtnl_lock(); 322362306a36Sopenharmony_ci rc = do_passive_init(adapter); 322462306a36Sopenharmony_ci rtnl_unlock(); 322562306a36Sopenharmony_ci if (!rc) 322662306a36Sopenharmony_ci netif_carrier_on(adapter->netdev); 322762306a36Sopenharmony_ci } else if (adapter->force_reset_recovery) { 322862306a36Sopenharmony_ci /* Since we are doing a hard reset now, clear the 322962306a36Sopenharmony_ci * failover_pending flag so we don't ignore any 323062306a36Sopenharmony_ci * future MOBILITY or other resets. 323162306a36Sopenharmony_ci */ 323262306a36Sopenharmony_ci adapter->failover_pending = false; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci /* Transport event occurred during previous reset */ 323562306a36Sopenharmony_ci if (adapter->wait_for_reset) { 323662306a36Sopenharmony_ci /* Previous was CHANGE_PARAM; caller locked */ 323762306a36Sopenharmony_ci adapter->force_reset_recovery = false; 323862306a36Sopenharmony_ci rc = do_hard_reset(adapter, rwi, reset_state); 323962306a36Sopenharmony_ci } else { 324062306a36Sopenharmony_ci rtnl_lock(); 324162306a36Sopenharmony_ci adapter->force_reset_recovery = false; 324262306a36Sopenharmony_ci rc = do_hard_reset(adapter, rwi, reset_state); 324362306a36Sopenharmony_ci rtnl_unlock(); 324462306a36Sopenharmony_ci } 324562306a36Sopenharmony_ci if (rc) 324662306a36Sopenharmony_ci num_fails++; 324762306a36Sopenharmony_ci else 324862306a36Sopenharmony_ci num_fails = 0; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci /* If auto-priority-failover is enabled we can get 325162306a36Sopenharmony_ci * back to back failovers during resets, resulting 325262306a36Sopenharmony_ci * in at least two failed resets (from high-priority 325362306a36Sopenharmony_ci * backing device to low-priority one and then back) 325462306a36Sopenharmony_ci * If resets continue to fail beyond that, give the 325562306a36Sopenharmony_ci * adapter some time to settle down before retrying. 325662306a36Sopenharmony_ci */ 325762306a36Sopenharmony_ci if (num_fails >= 3) { 325862306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 325962306a36Sopenharmony_ci "[S:%s] Hard reset failed %d times, waiting 60 secs\n", 326062306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 326162306a36Sopenharmony_ci num_fails); 326262306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 326362306a36Sopenharmony_ci schedule_timeout(60 * HZ); 326462306a36Sopenharmony_ci } 326562306a36Sopenharmony_ci } else { 326662306a36Sopenharmony_ci rc = do_reset(adapter, rwi, reset_state); 326762306a36Sopenharmony_ci } 326862306a36Sopenharmony_ci tmprwi = rwi; 326962306a36Sopenharmony_ci adapter->last_reset_time = jiffies; 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci if (rc) 327262306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Reset failed, rc=%d\n", rc); 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci rwi = get_next_rwi(adapter); 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci /* 327762306a36Sopenharmony_ci * If there are no resets queued and the previous reset failed, 327862306a36Sopenharmony_ci * the adapter would be in an undefined state. So retry the 327962306a36Sopenharmony_ci * previous reset as a hard reset. 328062306a36Sopenharmony_ci * 328162306a36Sopenharmony_ci * Else, free the previous rwi and, if there is another reset 328262306a36Sopenharmony_ci * queued, process the new reset even if previous reset failed 328362306a36Sopenharmony_ci * (the previous reset could have failed because of a fail 328462306a36Sopenharmony_ci * over for instance, so process the fail over). 328562306a36Sopenharmony_ci */ 328662306a36Sopenharmony_ci if (!rwi && rc) 328762306a36Sopenharmony_ci rwi = tmprwi; 328862306a36Sopenharmony_ci else 328962306a36Sopenharmony_ci kfree(tmprwi); 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (rwi && (rwi->reset_reason == VNIC_RESET_FAILOVER || 329262306a36Sopenharmony_ci rwi->reset_reason == VNIC_RESET_MOBILITY || rc)) 329362306a36Sopenharmony_ci adapter->force_reset_recovery = true; 329462306a36Sopenharmony_ci } 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci if (adapter->wait_for_reset) { 329762306a36Sopenharmony_ci adapter->reset_done_rc = rc; 329862306a36Sopenharmony_ci complete(&adapter->reset_done); 329962306a36Sopenharmony_ci } 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci clear_bit_unlock(0, &adapter->resetting); 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 330462306a36Sopenharmony_ci "[S:%s FRR:%d WFR:%d] Done processing resets\n", 330562306a36Sopenharmony_ci adapter_state_to_string(adapter->state), 330662306a36Sopenharmony_ci adapter->force_reset_recovery, 330762306a36Sopenharmony_ci adapter->wait_for_reset); 330862306a36Sopenharmony_ci} 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_cistatic void __ibmvnic_delayed_reset(struct work_struct *work) 331162306a36Sopenharmony_ci{ 331262306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci adapter = container_of(work, struct ibmvnic_adapter, 331562306a36Sopenharmony_ci ibmvnic_delayed_reset.work); 331662306a36Sopenharmony_ci __ibmvnic_reset(&adapter->ibmvnic_reset); 331762306a36Sopenharmony_ci} 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_cistatic void flush_reset_queue(struct ibmvnic_adapter *adapter) 332062306a36Sopenharmony_ci{ 332162306a36Sopenharmony_ci struct list_head *entry, *tmp_entry; 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci if (!list_empty(&adapter->rwi_list)) { 332462306a36Sopenharmony_ci list_for_each_safe(entry, tmp_entry, &adapter->rwi_list) { 332562306a36Sopenharmony_ci list_del(entry); 332662306a36Sopenharmony_ci kfree(list_entry(entry, struct ibmvnic_rwi, list)); 332762306a36Sopenharmony_ci } 332862306a36Sopenharmony_ci } 332962306a36Sopenharmony_ci} 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_cistatic int ibmvnic_reset(struct ibmvnic_adapter *adapter, 333262306a36Sopenharmony_ci enum ibmvnic_reset_reason reason) 333362306a36Sopenharmony_ci{ 333462306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 333562306a36Sopenharmony_ci struct ibmvnic_rwi *rwi, *tmp; 333662306a36Sopenharmony_ci unsigned long flags; 333762306a36Sopenharmony_ci int ret; 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci spin_lock_irqsave(&adapter->rwi_lock, flags); 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci /* If failover is pending don't schedule any other reset. 334262306a36Sopenharmony_ci * Instead let the failover complete. If there is already a 334362306a36Sopenharmony_ci * a failover reset scheduled, we will detect and drop the 334462306a36Sopenharmony_ci * duplicate reset when walking the ->rwi_list below. 334562306a36Sopenharmony_ci */ 334662306a36Sopenharmony_ci if (adapter->state == VNIC_REMOVING || 334762306a36Sopenharmony_ci adapter->state == VNIC_REMOVED || 334862306a36Sopenharmony_ci (adapter->failover_pending && reason != VNIC_RESET_FAILOVER)) { 334962306a36Sopenharmony_ci ret = EBUSY; 335062306a36Sopenharmony_ci netdev_dbg(netdev, "Adapter removing or pending failover, skipping reset\n"); 335162306a36Sopenharmony_ci goto err; 335262306a36Sopenharmony_ci } 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci list_for_each_entry(tmp, &adapter->rwi_list, list) { 335562306a36Sopenharmony_ci if (tmp->reset_reason == reason) { 335662306a36Sopenharmony_ci netdev_dbg(netdev, "Skipping matching reset, reason=%s\n", 335762306a36Sopenharmony_ci reset_reason_to_string(reason)); 335862306a36Sopenharmony_ci ret = EBUSY; 335962306a36Sopenharmony_ci goto err; 336062306a36Sopenharmony_ci } 336162306a36Sopenharmony_ci } 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci rwi = kzalloc(sizeof(*rwi), GFP_ATOMIC); 336462306a36Sopenharmony_ci if (!rwi) { 336562306a36Sopenharmony_ci ret = ENOMEM; 336662306a36Sopenharmony_ci goto err; 336762306a36Sopenharmony_ci } 336862306a36Sopenharmony_ci /* if we just received a transport event, 336962306a36Sopenharmony_ci * flush reset queue and process this reset 337062306a36Sopenharmony_ci */ 337162306a36Sopenharmony_ci if (adapter->force_reset_recovery) 337262306a36Sopenharmony_ci flush_reset_queue(adapter); 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci rwi->reset_reason = reason; 337562306a36Sopenharmony_ci list_add_tail(&rwi->list, &adapter->rwi_list); 337662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Scheduling reset (reason %s)\n", 337762306a36Sopenharmony_ci reset_reason_to_string(reason)); 337862306a36Sopenharmony_ci queue_work(system_long_wq, &adapter->ibmvnic_reset); 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci ret = 0; 338162306a36Sopenharmony_cierr: 338262306a36Sopenharmony_ci /* ibmvnic_close() below can block, so drop the lock first */ 338362306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->rwi_lock, flags); 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci if (ret == ENOMEM) 338662306a36Sopenharmony_ci ibmvnic_close(netdev); 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci return -ret; 338962306a36Sopenharmony_ci} 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_cistatic void ibmvnic_tx_timeout(struct net_device *dev, unsigned int txqueue) 339262306a36Sopenharmony_ci{ 339362306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(dev); 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci if (test_bit(0, &adapter->resetting)) { 339662306a36Sopenharmony_ci netdev_err(adapter->netdev, 339762306a36Sopenharmony_ci "Adapter is resetting, skip timeout reset\n"); 339862306a36Sopenharmony_ci return; 339962306a36Sopenharmony_ci } 340062306a36Sopenharmony_ci /* No queuing up reset until at least 5 seconds (default watchdog val) 340162306a36Sopenharmony_ci * after last reset 340262306a36Sopenharmony_ci */ 340362306a36Sopenharmony_ci if (time_before(jiffies, (adapter->last_reset_time + dev->watchdog_timeo))) { 340462306a36Sopenharmony_ci netdev_dbg(dev, "Not yet time to tx timeout.\n"); 340562306a36Sopenharmony_ci return; 340662306a36Sopenharmony_ci } 340762306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_TIMEOUT); 340862306a36Sopenharmony_ci} 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_cistatic void remove_buff_from_pool(struct ibmvnic_adapter *adapter, 341162306a36Sopenharmony_ci struct ibmvnic_rx_buff *rx_buff) 341262306a36Sopenharmony_ci{ 341362306a36Sopenharmony_ci struct ibmvnic_rx_pool *pool = &adapter->rx_pool[rx_buff->pool_index]; 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci rx_buff->skb = NULL; 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci pool->free_map[pool->next_alloc] = (int)(rx_buff - pool->rx_buff); 341862306a36Sopenharmony_ci pool->next_alloc = (pool->next_alloc + 1) % pool->size; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci atomic_dec(&pool->available); 342162306a36Sopenharmony_ci} 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_cistatic int ibmvnic_poll(struct napi_struct *napi, int budget) 342462306a36Sopenharmony_ci{ 342562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *rx_scrq; 342662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 342762306a36Sopenharmony_ci struct net_device *netdev; 342862306a36Sopenharmony_ci int frames_processed; 342962306a36Sopenharmony_ci int scrq_num; 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci netdev = napi->dev; 343262306a36Sopenharmony_ci adapter = netdev_priv(netdev); 343362306a36Sopenharmony_ci scrq_num = (int)(napi - adapter->napi); 343462306a36Sopenharmony_ci frames_processed = 0; 343562306a36Sopenharmony_ci rx_scrq = adapter->rx_scrq[scrq_num]; 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_cirestart_poll: 343862306a36Sopenharmony_ci while (frames_processed < budget) { 343962306a36Sopenharmony_ci struct sk_buff *skb; 344062306a36Sopenharmony_ci struct ibmvnic_rx_buff *rx_buff; 344162306a36Sopenharmony_ci union sub_crq *next; 344262306a36Sopenharmony_ci u32 length; 344362306a36Sopenharmony_ci u16 offset; 344462306a36Sopenharmony_ci u8 flags = 0; 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci if (unlikely(test_bit(0, &adapter->resetting) && 344762306a36Sopenharmony_ci adapter->reset_reason != VNIC_RESET_NON_FATAL)) { 344862306a36Sopenharmony_ci enable_scrq_irq(adapter, rx_scrq); 344962306a36Sopenharmony_ci napi_complete_done(napi, frames_processed); 345062306a36Sopenharmony_ci return frames_processed; 345162306a36Sopenharmony_ci } 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci if (!pending_scrq(adapter, rx_scrq)) 345462306a36Sopenharmony_ci break; 345562306a36Sopenharmony_ci next = ibmvnic_next_scrq(adapter, rx_scrq); 345662306a36Sopenharmony_ci rx_buff = (struct ibmvnic_rx_buff *) 345762306a36Sopenharmony_ci be64_to_cpu(next->rx_comp.correlator); 345862306a36Sopenharmony_ci /* do error checking */ 345962306a36Sopenharmony_ci if (next->rx_comp.rc) { 346062306a36Sopenharmony_ci netdev_dbg(netdev, "rx buffer returned with rc %x\n", 346162306a36Sopenharmony_ci be16_to_cpu(next->rx_comp.rc)); 346262306a36Sopenharmony_ci /* free the entry */ 346362306a36Sopenharmony_ci next->rx_comp.first = 0; 346462306a36Sopenharmony_ci dev_kfree_skb_any(rx_buff->skb); 346562306a36Sopenharmony_ci remove_buff_from_pool(adapter, rx_buff); 346662306a36Sopenharmony_ci continue; 346762306a36Sopenharmony_ci } else if (!rx_buff->skb) { 346862306a36Sopenharmony_ci /* free the entry */ 346962306a36Sopenharmony_ci next->rx_comp.first = 0; 347062306a36Sopenharmony_ci remove_buff_from_pool(adapter, rx_buff); 347162306a36Sopenharmony_ci continue; 347262306a36Sopenharmony_ci } 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci length = be32_to_cpu(next->rx_comp.len); 347562306a36Sopenharmony_ci offset = be16_to_cpu(next->rx_comp.off_frame_data); 347662306a36Sopenharmony_ci flags = next->rx_comp.flags; 347762306a36Sopenharmony_ci skb = rx_buff->skb; 347862306a36Sopenharmony_ci /* load long_term_buff before copying to skb */ 347962306a36Sopenharmony_ci dma_rmb(); 348062306a36Sopenharmony_ci skb_copy_to_linear_data(skb, rx_buff->data + offset, 348162306a36Sopenharmony_ci length); 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci /* VLAN Header has been stripped by the system firmware and 348462306a36Sopenharmony_ci * needs to be inserted by the driver 348562306a36Sopenharmony_ci */ 348662306a36Sopenharmony_ci if (adapter->rx_vlan_header_insertion && 348762306a36Sopenharmony_ci (flags & IBMVNIC_VLAN_STRIPPED)) 348862306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 348962306a36Sopenharmony_ci ntohs(next->rx_comp.vlan_tci)); 349062306a36Sopenharmony_ci 349162306a36Sopenharmony_ci /* free the entry */ 349262306a36Sopenharmony_ci next->rx_comp.first = 0; 349362306a36Sopenharmony_ci remove_buff_from_pool(adapter, rx_buff); 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci skb_put(skb, length); 349662306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 349762306a36Sopenharmony_ci skb_record_rx_queue(skb, scrq_num); 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci if (flags & IBMVNIC_IP_CHKSUM_GOOD && 350062306a36Sopenharmony_ci flags & IBMVNIC_TCP_UDP_CHKSUM_GOOD) { 350162306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 350262306a36Sopenharmony_ci } 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci length = skb->len; 350562306a36Sopenharmony_ci napi_gro_receive(napi, skb); /* send it up */ 350662306a36Sopenharmony_ci netdev->stats.rx_packets++; 350762306a36Sopenharmony_ci netdev->stats.rx_bytes += length; 350862306a36Sopenharmony_ci adapter->rx_stats_buffers[scrq_num].packets++; 350962306a36Sopenharmony_ci adapter->rx_stats_buffers[scrq_num].bytes += length; 351062306a36Sopenharmony_ci frames_processed++; 351162306a36Sopenharmony_ci } 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci if (adapter->state != VNIC_CLOSING && 351462306a36Sopenharmony_ci ((atomic_read(&adapter->rx_pool[scrq_num].available) < 351562306a36Sopenharmony_ci adapter->req_rx_add_entries_per_subcrq / 2) || 351662306a36Sopenharmony_ci frames_processed < budget)) 351762306a36Sopenharmony_ci replenish_rx_pool(adapter, &adapter->rx_pool[scrq_num]); 351862306a36Sopenharmony_ci if (frames_processed < budget) { 351962306a36Sopenharmony_ci if (napi_complete_done(napi, frames_processed)) { 352062306a36Sopenharmony_ci enable_scrq_irq(adapter, rx_scrq); 352162306a36Sopenharmony_ci if (pending_scrq(adapter, rx_scrq)) { 352262306a36Sopenharmony_ci if (napi_reschedule(napi)) { 352362306a36Sopenharmony_ci disable_scrq_irq(adapter, rx_scrq); 352462306a36Sopenharmony_ci goto restart_poll; 352562306a36Sopenharmony_ci } 352662306a36Sopenharmony_ci } 352762306a36Sopenharmony_ci } 352862306a36Sopenharmony_ci } 352962306a36Sopenharmony_ci return frames_processed; 353062306a36Sopenharmony_ci} 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_cistatic int wait_for_reset(struct ibmvnic_adapter *adapter) 353362306a36Sopenharmony_ci{ 353462306a36Sopenharmony_ci int rc, ret; 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_ci adapter->fallback.mtu = adapter->req_mtu; 353762306a36Sopenharmony_ci adapter->fallback.rx_queues = adapter->req_rx_queues; 353862306a36Sopenharmony_ci adapter->fallback.tx_queues = adapter->req_tx_queues; 353962306a36Sopenharmony_ci adapter->fallback.rx_entries = adapter->req_rx_add_entries_per_subcrq; 354062306a36Sopenharmony_ci adapter->fallback.tx_entries = adapter->req_tx_entries_per_subcrq; 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci reinit_completion(&adapter->reset_done); 354362306a36Sopenharmony_ci adapter->wait_for_reset = true; 354462306a36Sopenharmony_ci rc = ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM); 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci if (rc) { 354762306a36Sopenharmony_ci ret = rc; 354862306a36Sopenharmony_ci goto out; 354962306a36Sopenharmony_ci } 355062306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->reset_done, 60000); 355162306a36Sopenharmony_ci if (rc) { 355262306a36Sopenharmony_ci ret = -ENODEV; 355362306a36Sopenharmony_ci goto out; 355462306a36Sopenharmony_ci } 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci ret = 0; 355762306a36Sopenharmony_ci if (adapter->reset_done_rc) { 355862306a36Sopenharmony_ci ret = -EIO; 355962306a36Sopenharmony_ci adapter->desired.mtu = adapter->fallback.mtu; 356062306a36Sopenharmony_ci adapter->desired.rx_queues = adapter->fallback.rx_queues; 356162306a36Sopenharmony_ci adapter->desired.tx_queues = adapter->fallback.tx_queues; 356262306a36Sopenharmony_ci adapter->desired.rx_entries = adapter->fallback.rx_entries; 356362306a36Sopenharmony_ci adapter->desired.tx_entries = adapter->fallback.tx_entries; 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_ci reinit_completion(&adapter->reset_done); 356662306a36Sopenharmony_ci adapter->wait_for_reset = true; 356762306a36Sopenharmony_ci rc = ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM); 356862306a36Sopenharmony_ci if (rc) { 356962306a36Sopenharmony_ci ret = rc; 357062306a36Sopenharmony_ci goto out; 357162306a36Sopenharmony_ci } 357262306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->reset_done, 357362306a36Sopenharmony_ci 60000); 357462306a36Sopenharmony_ci if (rc) { 357562306a36Sopenharmony_ci ret = -ENODEV; 357662306a36Sopenharmony_ci goto out; 357762306a36Sopenharmony_ci } 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ciout: 358062306a36Sopenharmony_ci adapter->wait_for_reset = false; 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_ci return ret; 358362306a36Sopenharmony_ci} 358462306a36Sopenharmony_ci 358562306a36Sopenharmony_cistatic int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu) 358662306a36Sopenharmony_ci{ 358762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci adapter->desired.mtu = new_mtu + ETH_HLEN; 359062306a36Sopenharmony_ci 359162306a36Sopenharmony_ci return wait_for_reset(adapter); 359262306a36Sopenharmony_ci} 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_cistatic netdev_features_t ibmvnic_features_check(struct sk_buff *skb, 359562306a36Sopenharmony_ci struct net_device *dev, 359662306a36Sopenharmony_ci netdev_features_t features) 359762306a36Sopenharmony_ci{ 359862306a36Sopenharmony_ci /* Some backing hardware adapters can not 359962306a36Sopenharmony_ci * handle packets with a MSS less than 224 360062306a36Sopenharmony_ci * or with only one segment. 360162306a36Sopenharmony_ci */ 360262306a36Sopenharmony_ci if (skb_is_gso(skb)) { 360362306a36Sopenharmony_ci if (skb_shinfo(skb)->gso_size < 224 || 360462306a36Sopenharmony_ci skb_shinfo(skb)->gso_segs == 1) 360562306a36Sopenharmony_ci features &= ~NETIF_F_GSO_MASK; 360662306a36Sopenharmony_ci } 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci return features; 360962306a36Sopenharmony_ci} 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_cistatic const struct net_device_ops ibmvnic_netdev_ops = { 361262306a36Sopenharmony_ci .ndo_open = ibmvnic_open, 361362306a36Sopenharmony_ci .ndo_stop = ibmvnic_close, 361462306a36Sopenharmony_ci .ndo_start_xmit = ibmvnic_xmit, 361562306a36Sopenharmony_ci .ndo_set_rx_mode = ibmvnic_set_multi, 361662306a36Sopenharmony_ci .ndo_set_mac_address = ibmvnic_set_mac, 361762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 361862306a36Sopenharmony_ci .ndo_tx_timeout = ibmvnic_tx_timeout, 361962306a36Sopenharmony_ci .ndo_change_mtu = ibmvnic_change_mtu, 362062306a36Sopenharmony_ci .ndo_features_check = ibmvnic_features_check, 362162306a36Sopenharmony_ci}; 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci/* ethtool functions */ 362462306a36Sopenharmony_ci 362562306a36Sopenharmony_cistatic int ibmvnic_get_link_ksettings(struct net_device *netdev, 362662306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 362762306a36Sopenharmony_ci{ 362862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 362962306a36Sopenharmony_ci int rc; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci rc = send_query_phys_parms(adapter); 363262306a36Sopenharmony_ci if (rc) { 363362306a36Sopenharmony_ci adapter->speed = SPEED_UNKNOWN; 363462306a36Sopenharmony_ci adapter->duplex = DUPLEX_UNKNOWN; 363562306a36Sopenharmony_ci } 363662306a36Sopenharmony_ci cmd->base.speed = adapter->speed; 363762306a36Sopenharmony_ci cmd->base.duplex = adapter->duplex; 363862306a36Sopenharmony_ci cmd->base.port = PORT_FIBRE; 363962306a36Sopenharmony_ci cmd->base.phy_address = 0; 364062306a36Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci return 0; 364362306a36Sopenharmony_ci} 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_cistatic void ibmvnic_get_drvinfo(struct net_device *netdev, 364662306a36Sopenharmony_ci struct ethtool_drvinfo *info) 364762306a36Sopenharmony_ci{ 364862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci strscpy(info->driver, ibmvnic_driver_name, sizeof(info->driver)); 365162306a36Sopenharmony_ci strscpy(info->version, IBMVNIC_DRIVER_VERSION, sizeof(info->version)); 365262306a36Sopenharmony_ci strscpy(info->fw_version, adapter->fw_version, 365362306a36Sopenharmony_ci sizeof(info->fw_version)); 365462306a36Sopenharmony_ci} 365562306a36Sopenharmony_ci 365662306a36Sopenharmony_cistatic u32 ibmvnic_get_msglevel(struct net_device *netdev) 365762306a36Sopenharmony_ci{ 365862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_ci return adapter->msg_enable; 366162306a36Sopenharmony_ci} 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_cistatic void ibmvnic_set_msglevel(struct net_device *netdev, u32 data) 366462306a36Sopenharmony_ci{ 366562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci adapter->msg_enable = data; 366862306a36Sopenharmony_ci} 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_cistatic u32 ibmvnic_get_link(struct net_device *netdev) 367162306a36Sopenharmony_ci{ 367262306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci /* Don't need to send a query because we request a logical link up at 367562306a36Sopenharmony_ci * init and then we wait for link state indications 367662306a36Sopenharmony_ci */ 367762306a36Sopenharmony_ci return adapter->logical_link_state; 367862306a36Sopenharmony_ci} 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_cistatic void ibmvnic_get_ringparam(struct net_device *netdev, 368162306a36Sopenharmony_ci struct ethtool_ringparam *ring, 368262306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 368362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 368462306a36Sopenharmony_ci{ 368562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 368662306a36Sopenharmony_ci 368762306a36Sopenharmony_ci ring->rx_max_pending = adapter->max_rx_add_entries_per_subcrq; 368862306a36Sopenharmony_ci ring->tx_max_pending = adapter->max_tx_entries_per_subcrq; 368962306a36Sopenharmony_ci ring->rx_mini_max_pending = 0; 369062306a36Sopenharmony_ci ring->rx_jumbo_max_pending = 0; 369162306a36Sopenharmony_ci ring->rx_pending = adapter->req_rx_add_entries_per_subcrq; 369262306a36Sopenharmony_ci ring->tx_pending = adapter->req_tx_entries_per_subcrq; 369362306a36Sopenharmony_ci ring->rx_mini_pending = 0; 369462306a36Sopenharmony_ci ring->rx_jumbo_pending = 0; 369562306a36Sopenharmony_ci} 369662306a36Sopenharmony_ci 369762306a36Sopenharmony_cistatic int ibmvnic_set_ringparam(struct net_device *netdev, 369862306a36Sopenharmony_ci struct ethtool_ringparam *ring, 369962306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 370062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 370162306a36Sopenharmony_ci{ 370262306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 370362306a36Sopenharmony_ci 370462306a36Sopenharmony_ci if (ring->rx_pending > adapter->max_rx_add_entries_per_subcrq || 370562306a36Sopenharmony_ci ring->tx_pending > adapter->max_tx_entries_per_subcrq) { 370662306a36Sopenharmony_ci netdev_err(netdev, "Invalid request.\n"); 370762306a36Sopenharmony_ci netdev_err(netdev, "Max tx buffers = %llu\n", 370862306a36Sopenharmony_ci adapter->max_rx_add_entries_per_subcrq); 370962306a36Sopenharmony_ci netdev_err(netdev, "Max rx buffers = %llu\n", 371062306a36Sopenharmony_ci adapter->max_tx_entries_per_subcrq); 371162306a36Sopenharmony_ci return -EINVAL; 371262306a36Sopenharmony_ci } 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci adapter->desired.rx_entries = ring->rx_pending; 371562306a36Sopenharmony_ci adapter->desired.tx_entries = ring->tx_pending; 371662306a36Sopenharmony_ci 371762306a36Sopenharmony_ci return wait_for_reset(adapter); 371862306a36Sopenharmony_ci} 371962306a36Sopenharmony_ci 372062306a36Sopenharmony_cistatic void ibmvnic_get_channels(struct net_device *netdev, 372162306a36Sopenharmony_ci struct ethtool_channels *channels) 372262306a36Sopenharmony_ci{ 372362306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci channels->max_rx = adapter->max_rx_queues; 372662306a36Sopenharmony_ci channels->max_tx = adapter->max_tx_queues; 372762306a36Sopenharmony_ci channels->max_other = 0; 372862306a36Sopenharmony_ci channels->max_combined = 0; 372962306a36Sopenharmony_ci channels->rx_count = adapter->req_rx_queues; 373062306a36Sopenharmony_ci channels->tx_count = adapter->req_tx_queues; 373162306a36Sopenharmony_ci channels->other_count = 0; 373262306a36Sopenharmony_ci channels->combined_count = 0; 373362306a36Sopenharmony_ci} 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_cistatic int ibmvnic_set_channels(struct net_device *netdev, 373662306a36Sopenharmony_ci struct ethtool_channels *channels) 373762306a36Sopenharmony_ci{ 373862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 373962306a36Sopenharmony_ci 374062306a36Sopenharmony_ci adapter->desired.rx_queues = channels->rx_count; 374162306a36Sopenharmony_ci adapter->desired.tx_queues = channels->tx_count; 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci return wait_for_reset(adapter); 374462306a36Sopenharmony_ci} 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_cistatic void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) 374762306a36Sopenharmony_ci{ 374862306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(dev); 374962306a36Sopenharmony_ci int i; 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 375262306a36Sopenharmony_ci return; 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++, data += ETH_GSTRING_LEN) 375562306a36Sopenharmony_ci memcpy(data, ibmvnic_stats[i].name, ETH_GSTRING_LEN); 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) { 375862306a36Sopenharmony_ci snprintf(data, ETH_GSTRING_LEN, "tx%d_packets", i); 375962306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci snprintf(data, ETH_GSTRING_LEN, "tx%d_bytes", i); 376262306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci snprintf(data, ETH_GSTRING_LEN, "tx%d_dropped_packets", i); 376562306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 376662306a36Sopenharmony_ci } 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 376962306a36Sopenharmony_ci snprintf(data, ETH_GSTRING_LEN, "rx%d_packets", i); 377062306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 377162306a36Sopenharmony_ci 377262306a36Sopenharmony_ci snprintf(data, ETH_GSTRING_LEN, "rx%d_bytes", i); 377362306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 377462306a36Sopenharmony_ci 377562306a36Sopenharmony_ci snprintf(data, ETH_GSTRING_LEN, "rx%d_interrupts", i); 377662306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 377762306a36Sopenharmony_ci } 377862306a36Sopenharmony_ci} 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_cistatic int ibmvnic_get_sset_count(struct net_device *dev, int sset) 378162306a36Sopenharmony_ci{ 378262306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(dev); 378362306a36Sopenharmony_ci 378462306a36Sopenharmony_ci switch (sset) { 378562306a36Sopenharmony_ci case ETH_SS_STATS: 378662306a36Sopenharmony_ci return ARRAY_SIZE(ibmvnic_stats) + 378762306a36Sopenharmony_ci adapter->req_tx_queues * NUM_TX_STATS + 378862306a36Sopenharmony_ci adapter->req_rx_queues * NUM_RX_STATS; 378962306a36Sopenharmony_ci default: 379062306a36Sopenharmony_ci return -EOPNOTSUPP; 379162306a36Sopenharmony_ci } 379262306a36Sopenharmony_ci} 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_cistatic void ibmvnic_get_ethtool_stats(struct net_device *dev, 379562306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 379662306a36Sopenharmony_ci{ 379762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(dev); 379862306a36Sopenharmony_ci union ibmvnic_crq crq; 379962306a36Sopenharmony_ci int i, j; 380062306a36Sopenharmony_ci int rc; 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 380362306a36Sopenharmony_ci crq.request_statistics.first = IBMVNIC_CRQ_CMD; 380462306a36Sopenharmony_ci crq.request_statistics.cmd = REQUEST_STATISTICS; 380562306a36Sopenharmony_ci crq.request_statistics.ioba = cpu_to_be32(adapter->stats_token); 380662306a36Sopenharmony_ci crq.request_statistics.len = 380762306a36Sopenharmony_ci cpu_to_be32(sizeof(struct ibmvnic_statistics)); 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci /* Wait for data to be written */ 381062306a36Sopenharmony_ci reinit_completion(&adapter->stats_done); 381162306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 381262306a36Sopenharmony_ci if (rc) 381362306a36Sopenharmony_ci return; 381462306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->stats_done, 10000); 381562306a36Sopenharmony_ci if (rc) 381662306a36Sopenharmony_ci return; 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++) 381962306a36Sopenharmony_ci data[i] = be64_to_cpu(IBMVNIC_GET_STAT 382062306a36Sopenharmony_ci (adapter, ibmvnic_stats[i].offset)); 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci for (j = 0; j < adapter->req_tx_queues; j++) { 382362306a36Sopenharmony_ci data[i] = adapter->tx_stats_buffers[j].packets; 382462306a36Sopenharmony_ci i++; 382562306a36Sopenharmony_ci data[i] = adapter->tx_stats_buffers[j].bytes; 382662306a36Sopenharmony_ci i++; 382762306a36Sopenharmony_ci data[i] = adapter->tx_stats_buffers[j].dropped_packets; 382862306a36Sopenharmony_ci i++; 382962306a36Sopenharmony_ci } 383062306a36Sopenharmony_ci 383162306a36Sopenharmony_ci for (j = 0; j < adapter->req_rx_queues; j++) { 383262306a36Sopenharmony_ci data[i] = adapter->rx_stats_buffers[j].packets; 383362306a36Sopenharmony_ci i++; 383462306a36Sopenharmony_ci data[i] = adapter->rx_stats_buffers[j].bytes; 383562306a36Sopenharmony_ci i++; 383662306a36Sopenharmony_ci data[i] = adapter->rx_stats_buffers[j].interrupts; 383762306a36Sopenharmony_ci i++; 383862306a36Sopenharmony_ci } 383962306a36Sopenharmony_ci} 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_cistatic const struct ethtool_ops ibmvnic_ethtool_ops = { 384262306a36Sopenharmony_ci .get_drvinfo = ibmvnic_get_drvinfo, 384362306a36Sopenharmony_ci .get_msglevel = ibmvnic_get_msglevel, 384462306a36Sopenharmony_ci .set_msglevel = ibmvnic_set_msglevel, 384562306a36Sopenharmony_ci .get_link = ibmvnic_get_link, 384662306a36Sopenharmony_ci .get_ringparam = ibmvnic_get_ringparam, 384762306a36Sopenharmony_ci .set_ringparam = ibmvnic_set_ringparam, 384862306a36Sopenharmony_ci .get_channels = ibmvnic_get_channels, 384962306a36Sopenharmony_ci .set_channels = ibmvnic_set_channels, 385062306a36Sopenharmony_ci .get_strings = ibmvnic_get_strings, 385162306a36Sopenharmony_ci .get_sset_count = ibmvnic_get_sset_count, 385262306a36Sopenharmony_ci .get_ethtool_stats = ibmvnic_get_ethtool_stats, 385362306a36Sopenharmony_ci .get_link_ksettings = ibmvnic_get_link_ksettings, 385462306a36Sopenharmony_ci}; 385562306a36Sopenharmony_ci 385662306a36Sopenharmony_ci/* Routines for managing CRQs/sCRQs */ 385762306a36Sopenharmony_ci 385862306a36Sopenharmony_cistatic int reset_one_sub_crq_queue(struct ibmvnic_adapter *adapter, 385962306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 386062306a36Sopenharmony_ci{ 386162306a36Sopenharmony_ci int rc; 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ci if (!scrq) { 386462306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Invalid scrq reset.\n"); 386562306a36Sopenharmony_ci return -EINVAL; 386662306a36Sopenharmony_ci } 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci if (scrq->irq) { 386962306a36Sopenharmony_ci free_irq(scrq->irq, scrq); 387062306a36Sopenharmony_ci irq_dispose_mapping(scrq->irq); 387162306a36Sopenharmony_ci scrq->irq = 0; 387262306a36Sopenharmony_ci } 387362306a36Sopenharmony_ci 387462306a36Sopenharmony_ci if (scrq->msgs) { 387562306a36Sopenharmony_ci memset(scrq->msgs, 0, 4 * PAGE_SIZE); 387662306a36Sopenharmony_ci atomic_set(&scrq->used, 0); 387762306a36Sopenharmony_ci scrq->cur = 0; 387862306a36Sopenharmony_ci scrq->ind_buf.index = 0; 387962306a36Sopenharmony_ci } else { 388062306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Invalid scrq reset\n"); 388162306a36Sopenharmony_ci return -EINVAL; 388262306a36Sopenharmony_ci } 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token, 388562306a36Sopenharmony_ci 4 * PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq); 388662306a36Sopenharmony_ci return rc; 388762306a36Sopenharmony_ci} 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_cistatic int reset_sub_crq_queues(struct ibmvnic_adapter *adapter) 389062306a36Sopenharmony_ci{ 389162306a36Sopenharmony_ci int i, rc; 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci if (!adapter->tx_scrq || !adapter->rx_scrq) 389462306a36Sopenharmony_ci return -EINVAL; 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_ci ibmvnic_clean_affinity(adapter); 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) { 389962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Re-setting tx_scrq[%d]\n", i); 390062306a36Sopenharmony_ci rc = reset_one_sub_crq_queue(adapter, adapter->tx_scrq[i]); 390162306a36Sopenharmony_ci if (rc) 390262306a36Sopenharmony_ci return rc; 390362306a36Sopenharmony_ci } 390462306a36Sopenharmony_ci 390562306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 390662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Re-setting rx_scrq[%d]\n", i); 390762306a36Sopenharmony_ci rc = reset_one_sub_crq_queue(adapter, adapter->rx_scrq[i]); 390862306a36Sopenharmony_ci if (rc) 390962306a36Sopenharmony_ci return rc; 391062306a36Sopenharmony_ci } 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci return rc; 391362306a36Sopenharmony_ci} 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_cistatic void release_sub_crq_queue(struct ibmvnic_adapter *adapter, 391662306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq, 391762306a36Sopenharmony_ci bool do_h_free) 391862306a36Sopenharmony_ci{ 391962306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 392062306a36Sopenharmony_ci long rc; 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Releasing sub-CRQ\n"); 392362306a36Sopenharmony_ci 392462306a36Sopenharmony_ci if (do_h_free) { 392562306a36Sopenharmony_ci /* Close the sub-crqs */ 392662306a36Sopenharmony_ci do { 392762306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_SUB_CRQ, 392862306a36Sopenharmony_ci adapter->vdev->unit_address, 392962306a36Sopenharmony_ci scrq->crq_num); 393062306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci if (rc) { 393362306a36Sopenharmony_ci netdev_err(adapter->netdev, 393462306a36Sopenharmony_ci "Failed to release sub-CRQ %16lx, rc = %ld\n", 393562306a36Sopenharmony_ci scrq->crq_num, rc); 393662306a36Sopenharmony_ci } 393762306a36Sopenharmony_ci } 393862306a36Sopenharmony_ci 393962306a36Sopenharmony_ci dma_free_coherent(dev, 394062306a36Sopenharmony_ci IBMVNIC_IND_ARR_SZ, 394162306a36Sopenharmony_ci scrq->ind_buf.indir_arr, 394262306a36Sopenharmony_ci scrq->ind_buf.indir_dma); 394362306a36Sopenharmony_ci 394462306a36Sopenharmony_ci dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, 394562306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 394662306a36Sopenharmony_ci free_pages((unsigned long)scrq->msgs, 2); 394762306a36Sopenharmony_ci free_cpumask_var(scrq->affinity_mask); 394862306a36Sopenharmony_ci kfree(scrq); 394962306a36Sopenharmony_ci} 395062306a36Sopenharmony_ci 395162306a36Sopenharmony_cistatic struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter 395262306a36Sopenharmony_ci *adapter) 395362306a36Sopenharmony_ci{ 395462306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 395562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq; 395662306a36Sopenharmony_ci int rc; 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci scrq = kzalloc(sizeof(*scrq), GFP_KERNEL); 395962306a36Sopenharmony_ci if (!scrq) 396062306a36Sopenharmony_ci return NULL; 396162306a36Sopenharmony_ci 396262306a36Sopenharmony_ci scrq->msgs = 396362306a36Sopenharmony_ci (union sub_crq *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); 396462306a36Sopenharmony_ci if (!scrq->msgs) { 396562306a36Sopenharmony_ci dev_warn(dev, "Couldn't allocate crq queue messages page\n"); 396662306a36Sopenharmony_ci goto zero_page_failed; 396762306a36Sopenharmony_ci } 396862306a36Sopenharmony_ci if (!zalloc_cpumask_var(&scrq->affinity_mask, GFP_KERNEL)) 396962306a36Sopenharmony_ci goto cpumask_alloc_failed; 397062306a36Sopenharmony_ci 397162306a36Sopenharmony_ci scrq->msg_token = dma_map_single(dev, scrq->msgs, 4 * PAGE_SIZE, 397262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 397362306a36Sopenharmony_ci if (dma_mapping_error(dev, scrq->msg_token)) { 397462306a36Sopenharmony_ci dev_warn(dev, "Couldn't map crq queue messages page\n"); 397562306a36Sopenharmony_ci goto map_failed; 397662306a36Sopenharmony_ci } 397762306a36Sopenharmony_ci 397862306a36Sopenharmony_ci rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token, 397962306a36Sopenharmony_ci 4 * PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq); 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci if (rc == H_RESOURCE) 398262306a36Sopenharmony_ci rc = ibmvnic_reset_crq(adapter); 398362306a36Sopenharmony_ci 398462306a36Sopenharmony_ci if (rc == H_CLOSED) { 398562306a36Sopenharmony_ci dev_warn(dev, "Partner adapter not ready, waiting.\n"); 398662306a36Sopenharmony_ci } else if (rc) { 398762306a36Sopenharmony_ci dev_warn(dev, "Error %d registering sub-crq\n", rc); 398862306a36Sopenharmony_ci goto reg_failed; 398962306a36Sopenharmony_ci } 399062306a36Sopenharmony_ci 399162306a36Sopenharmony_ci scrq->adapter = adapter; 399262306a36Sopenharmony_ci scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs); 399362306a36Sopenharmony_ci scrq->ind_buf.index = 0; 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_ci scrq->ind_buf.indir_arr = 399662306a36Sopenharmony_ci dma_alloc_coherent(dev, 399762306a36Sopenharmony_ci IBMVNIC_IND_ARR_SZ, 399862306a36Sopenharmony_ci &scrq->ind_buf.indir_dma, 399962306a36Sopenharmony_ci GFP_KERNEL); 400062306a36Sopenharmony_ci 400162306a36Sopenharmony_ci if (!scrq->ind_buf.indir_arr) 400262306a36Sopenharmony_ci goto indir_failed; 400362306a36Sopenharmony_ci 400462306a36Sopenharmony_ci spin_lock_init(&scrq->lock); 400562306a36Sopenharmony_ci 400662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 400762306a36Sopenharmony_ci "sub-crq initialized, num %lx, hw_irq=%lx, irq=%x\n", 400862306a36Sopenharmony_ci scrq->crq_num, scrq->hw_irq, scrq->irq); 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci return scrq; 401162306a36Sopenharmony_ci 401262306a36Sopenharmony_ciindir_failed: 401362306a36Sopenharmony_ci do { 401462306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_SUB_CRQ, 401562306a36Sopenharmony_ci adapter->vdev->unit_address, 401662306a36Sopenharmony_ci scrq->crq_num); 401762306a36Sopenharmony_ci } while (rc == H_BUSY || rc == H_IS_LONG_BUSY(rc)); 401862306a36Sopenharmony_cireg_failed: 401962306a36Sopenharmony_ci dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, 402062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 402162306a36Sopenharmony_cimap_failed: 402262306a36Sopenharmony_ci free_cpumask_var(scrq->affinity_mask); 402362306a36Sopenharmony_cicpumask_alloc_failed: 402462306a36Sopenharmony_ci free_pages((unsigned long)scrq->msgs, 2); 402562306a36Sopenharmony_cizero_page_failed: 402662306a36Sopenharmony_ci kfree(scrq); 402762306a36Sopenharmony_ci 402862306a36Sopenharmony_ci return NULL; 402962306a36Sopenharmony_ci} 403062306a36Sopenharmony_ci 403162306a36Sopenharmony_cistatic void release_sub_crqs(struct ibmvnic_adapter *adapter, bool do_h_free) 403262306a36Sopenharmony_ci{ 403362306a36Sopenharmony_ci int i; 403462306a36Sopenharmony_ci 403562306a36Sopenharmony_ci ibmvnic_clean_affinity(adapter); 403662306a36Sopenharmony_ci if (adapter->tx_scrq) { 403762306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_tx_scrqs; i++) { 403862306a36Sopenharmony_ci if (!adapter->tx_scrq[i]) 403962306a36Sopenharmony_ci continue; 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Releasing tx_scrq[%d]\n", 404262306a36Sopenharmony_ci i); 404362306a36Sopenharmony_ci ibmvnic_tx_scrq_clean_buffer(adapter, adapter->tx_scrq[i]); 404462306a36Sopenharmony_ci if (adapter->tx_scrq[i]->irq) { 404562306a36Sopenharmony_ci free_irq(adapter->tx_scrq[i]->irq, 404662306a36Sopenharmony_ci adapter->tx_scrq[i]); 404762306a36Sopenharmony_ci irq_dispose_mapping(adapter->tx_scrq[i]->irq); 404862306a36Sopenharmony_ci adapter->tx_scrq[i]->irq = 0; 404962306a36Sopenharmony_ci } 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci release_sub_crq_queue(adapter, adapter->tx_scrq[i], 405262306a36Sopenharmony_ci do_h_free); 405362306a36Sopenharmony_ci } 405462306a36Sopenharmony_ci 405562306a36Sopenharmony_ci kfree(adapter->tx_scrq); 405662306a36Sopenharmony_ci adapter->tx_scrq = NULL; 405762306a36Sopenharmony_ci adapter->num_active_tx_scrqs = 0; 405862306a36Sopenharmony_ci } 405962306a36Sopenharmony_ci 406062306a36Sopenharmony_ci if (adapter->rx_scrq) { 406162306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_rx_scrqs; i++) { 406262306a36Sopenharmony_ci if (!adapter->rx_scrq[i]) 406362306a36Sopenharmony_ci continue; 406462306a36Sopenharmony_ci 406562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Releasing rx_scrq[%d]\n", 406662306a36Sopenharmony_ci i); 406762306a36Sopenharmony_ci if (adapter->rx_scrq[i]->irq) { 406862306a36Sopenharmony_ci free_irq(adapter->rx_scrq[i]->irq, 406962306a36Sopenharmony_ci adapter->rx_scrq[i]); 407062306a36Sopenharmony_ci irq_dispose_mapping(adapter->rx_scrq[i]->irq); 407162306a36Sopenharmony_ci adapter->rx_scrq[i]->irq = 0; 407262306a36Sopenharmony_ci } 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci release_sub_crq_queue(adapter, adapter->rx_scrq[i], 407562306a36Sopenharmony_ci do_h_free); 407662306a36Sopenharmony_ci } 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci kfree(adapter->rx_scrq); 407962306a36Sopenharmony_ci adapter->rx_scrq = NULL; 408062306a36Sopenharmony_ci adapter->num_active_rx_scrqs = 0; 408162306a36Sopenharmony_ci } 408262306a36Sopenharmony_ci} 408362306a36Sopenharmony_ci 408462306a36Sopenharmony_cistatic int disable_scrq_irq(struct ibmvnic_adapter *adapter, 408562306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 408662306a36Sopenharmony_ci{ 408762306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 408862306a36Sopenharmony_ci unsigned long rc; 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address, 409162306a36Sopenharmony_ci H_DISABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0); 409262306a36Sopenharmony_ci if (rc) 409362306a36Sopenharmony_ci dev_err(dev, "Couldn't disable scrq irq 0x%lx. rc=%ld\n", 409462306a36Sopenharmony_ci scrq->hw_irq, rc); 409562306a36Sopenharmony_ci return rc; 409662306a36Sopenharmony_ci} 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci/* We can not use the IRQ chip EOI handler because that has the 409962306a36Sopenharmony_ci * unintended effect of changing the interrupt priority. 410062306a36Sopenharmony_ci */ 410162306a36Sopenharmony_cistatic void ibmvnic_xics_eoi(struct device *dev, struct ibmvnic_sub_crq_queue *scrq) 410262306a36Sopenharmony_ci{ 410362306a36Sopenharmony_ci u64 val = 0xff000000 | scrq->hw_irq; 410462306a36Sopenharmony_ci unsigned long rc; 410562306a36Sopenharmony_ci 410662306a36Sopenharmony_ci rc = plpar_hcall_norets(H_EOI, val); 410762306a36Sopenharmony_ci if (rc) 410862306a36Sopenharmony_ci dev_err(dev, "H_EOI FAILED irq 0x%llx. rc=%ld\n", val, rc); 410962306a36Sopenharmony_ci} 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_ci/* Due to a firmware bug, the hypervisor can send an interrupt to a 411262306a36Sopenharmony_ci * transmit or receive queue just prior to a partition migration. 411362306a36Sopenharmony_ci * Force an EOI after migration. 411462306a36Sopenharmony_ci */ 411562306a36Sopenharmony_cistatic void ibmvnic_clear_pending_interrupt(struct device *dev, 411662306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 411762306a36Sopenharmony_ci{ 411862306a36Sopenharmony_ci if (!xive_enabled()) 411962306a36Sopenharmony_ci ibmvnic_xics_eoi(dev, scrq); 412062306a36Sopenharmony_ci} 412162306a36Sopenharmony_ci 412262306a36Sopenharmony_cistatic int enable_scrq_irq(struct ibmvnic_adapter *adapter, 412362306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 412462306a36Sopenharmony_ci{ 412562306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 412662306a36Sopenharmony_ci unsigned long rc; 412762306a36Sopenharmony_ci 412862306a36Sopenharmony_ci if (scrq->hw_irq > 0x100000000ULL) { 412962306a36Sopenharmony_ci dev_err(dev, "bad hw_irq = %lx\n", scrq->hw_irq); 413062306a36Sopenharmony_ci return 1; 413162306a36Sopenharmony_ci } 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_ci if (test_bit(0, &adapter->resetting) && 413462306a36Sopenharmony_ci adapter->reset_reason == VNIC_RESET_MOBILITY) { 413562306a36Sopenharmony_ci ibmvnic_clear_pending_interrupt(dev, scrq); 413662306a36Sopenharmony_ci } 413762306a36Sopenharmony_ci 413862306a36Sopenharmony_ci rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address, 413962306a36Sopenharmony_ci H_ENABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0); 414062306a36Sopenharmony_ci if (rc) 414162306a36Sopenharmony_ci dev_err(dev, "Couldn't enable scrq irq 0x%lx. rc=%ld\n", 414262306a36Sopenharmony_ci scrq->hw_irq, rc); 414362306a36Sopenharmony_ci return rc; 414462306a36Sopenharmony_ci} 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_cistatic int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter, 414762306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 414862306a36Sopenharmony_ci{ 414962306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 415062306a36Sopenharmony_ci struct ibmvnic_tx_pool *tx_pool; 415162306a36Sopenharmony_ci struct ibmvnic_tx_buff *txbuff; 415262306a36Sopenharmony_ci struct netdev_queue *txq; 415362306a36Sopenharmony_ci union sub_crq *next; 415462306a36Sopenharmony_ci int index; 415562306a36Sopenharmony_ci int i; 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_cirestart_loop: 415862306a36Sopenharmony_ci while (pending_scrq(adapter, scrq)) { 415962306a36Sopenharmony_ci unsigned int pool = scrq->pool_index; 416062306a36Sopenharmony_ci int num_entries = 0; 416162306a36Sopenharmony_ci int total_bytes = 0; 416262306a36Sopenharmony_ci int num_packets = 0; 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci next = ibmvnic_next_scrq(adapter, scrq); 416562306a36Sopenharmony_ci for (i = 0; i < next->tx_comp.num_comps; i++) { 416662306a36Sopenharmony_ci index = be32_to_cpu(next->tx_comp.correlators[i]); 416762306a36Sopenharmony_ci if (index & IBMVNIC_TSO_POOL_MASK) { 416862306a36Sopenharmony_ci tx_pool = &adapter->tso_pool[pool]; 416962306a36Sopenharmony_ci index &= ~IBMVNIC_TSO_POOL_MASK; 417062306a36Sopenharmony_ci } else { 417162306a36Sopenharmony_ci tx_pool = &adapter->tx_pool[pool]; 417262306a36Sopenharmony_ci } 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci txbuff = &tx_pool->tx_buff[index]; 417562306a36Sopenharmony_ci num_packets++; 417662306a36Sopenharmony_ci num_entries += txbuff->num_entries; 417762306a36Sopenharmony_ci if (txbuff->skb) { 417862306a36Sopenharmony_ci total_bytes += txbuff->skb->len; 417962306a36Sopenharmony_ci if (next->tx_comp.rcs[i]) { 418062306a36Sopenharmony_ci dev_err(dev, "tx error %x\n", 418162306a36Sopenharmony_ci next->tx_comp.rcs[i]); 418262306a36Sopenharmony_ci dev_kfree_skb_irq(txbuff->skb); 418362306a36Sopenharmony_ci } else { 418462306a36Sopenharmony_ci dev_consume_skb_irq(txbuff->skb); 418562306a36Sopenharmony_ci } 418662306a36Sopenharmony_ci txbuff->skb = NULL; 418762306a36Sopenharmony_ci } else { 418862306a36Sopenharmony_ci netdev_warn(adapter->netdev, 418962306a36Sopenharmony_ci "TX completion received with NULL socket buffer\n"); 419062306a36Sopenharmony_ci } 419162306a36Sopenharmony_ci tx_pool->free_map[tx_pool->producer_index] = index; 419262306a36Sopenharmony_ci tx_pool->producer_index = 419362306a36Sopenharmony_ci (tx_pool->producer_index + 1) % 419462306a36Sopenharmony_ci tx_pool->num_buffers; 419562306a36Sopenharmony_ci } 419662306a36Sopenharmony_ci /* remove tx_comp scrq*/ 419762306a36Sopenharmony_ci next->tx_comp.first = 0; 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci txq = netdev_get_tx_queue(adapter->netdev, scrq->pool_index); 420062306a36Sopenharmony_ci netdev_tx_completed_queue(txq, num_packets, total_bytes); 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci if (atomic_sub_return(num_entries, &scrq->used) <= 420362306a36Sopenharmony_ci (adapter->req_tx_entries_per_subcrq / 2) && 420462306a36Sopenharmony_ci __netif_subqueue_stopped(adapter->netdev, 420562306a36Sopenharmony_ci scrq->pool_index)) { 420662306a36Sopenharmony_ci rcu_read_lock(); 420762306a36Sopenharmony_ci if (adapter->tx_queues_active) { 420862306a36Sopenharmony_ci netif_wake_subqueue(adapter->netdev, 420962306a36Sopenharmony_ci scrq->pool_index); 421062306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 421162306a36Sopenharmony_ci "Started queue %d\n", 421262306a36Sopenharmony_ci scrq->pool_index); 421362306a36Sopenharmony_ci } 421462306a36Sopenharmony_ci rcu_read_unlock(); 421562306a36Sopenharmony_ci } 421662306a36Sopenharmony_ci } 421762306a36Sopenharmony_ci 421862306a36Sopenharmony_ci enable_scrq_irq(adapter, scrq); 421962306a36Sopenharmony_ci 422062306a36Sopenharmony_ci if (pending_scrq(adapter, scrq)) { 422162306a36Sopenharmony_ci disable_scrq_irq(adapter, scrq); 422262306a36Sopenharmony_ci goto restart_loop; 422362306a36Sopenharmony_ci } 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci return 0; 422662306a36Sopenharmony_ci} 422762306a36Sopenharmony_ci 422862306a36Sopenharmony_cistatic irqreturn_t ibmvnic_interrupt_tx(int irq, void *instance) 422962306a36Sopenharmony_ci{ 423062306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq = instance; 423162306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = scrq->adapter; 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci disable_scrq_irq(adapter, scrq); 423462306a36Sopenharmony_ci ibmvnic_complete_tx(adapter, scrq); 423562306a36Sopenharmony_ci 423662306a36Sopenharmony_ci return IRQ_HANDLED; 423762306a36Sopenharmony_ci} 423862306a36Sopenharmony_ci 423962306a36Sopenharmony_cistatic irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance) 424062306a36Sopenharmony_ci{ 424162306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq = instance; 424262306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = scrq->adapter; 424362306a36Sopenharmony_ci 424462306a36Sopenharmony_ci /* When booting a kdump kernel we can hit pending interrupts 424562306a36Sopenharmony_ci * prior to completing driver initialization. 424662306a36Sopenharmony_ci */ 424762306a36Sopenharmony_ci if (unlikely(adapter->state != VNIC_OPEN)) 424862306a36Sopenharmony_ci return IRQ_NONE; 424962306a36Sopenharmony_ci 425062306a36Sopenharmony_ci adapter->rx_stats_buffers[scrq->scrq_num].interrupts++; 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) { 425362306a36Sopenharmony_ci disable_scrq_irq(adapter, scrq); 425462306a36Sopenharmony_ci __napi_schedule(&adapter->napi[scrq->scrq_num]); 425562306a36Sopenharmony_ci } 425662306a36Sopenharmony_ci 425762306a36Sopenharmony_ci return IRQ_HANDLED; 425862306a36Sopenharmony_ci} 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_cistatic int init_sub_crq_irqs(struct ibmvnic_adapter *adapter) 426162306a36Sopenharmony_ci{ 426262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 426362306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq; 426462306a36Sopenharmony_ci int i = 0, j = 0; 426562306a36Sopenharmony_ci int rc = 0; 426662306a36Sopenharmony_ci 426762306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) { 426862306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Initializing tx_scrq[%d] irq\n", 426962306a36Sopenharmony_ci i); 427062306a36Sopenharmony_ci scrq = adapter->tx_scrq[i]; 427162306a36Sopenharmony_ci scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); 427262306a36Sopenharmony_ci 427362306a36Sopenharmony_ci if (!scrq->irq) { 427462306a36Sopenharmony_ci rc = -EINVAL; 427562306a36Sopenharmony_ci dev_err(dev, "Error mapping irq\n"); 427662306a36Sopenharmony_ci goto req_tx_irq_failed; 427762306a36Sopenharmony_ci } 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci snprintf(scrq->name, sizeof(scrq->name), "ibmvnic-%x-tx%d", 428062306a36Sopenharmony_ci adapter->vdev->unit_address, i); 428162306a36Sopenharmony_ci rc = request_irq(scrq->irq, ibmvnic_interrupt_tx, 428262306a36Sopenharmony_ci 0, scrq->name, scrq); 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci if (rc) { 428562306a36Sopenharmony_ci dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n", 428662306a36Sopenharmony_ci scrq->irq, rc); 428762306a36Sopenharmony_ci irq_dispose_mapping(scrq->irq); 428862306a36Sopenharmony_ci goto req_tx_irq_failed; 428962306a36Sopenharmony_ci } 429062306a36Sopenharmony_ci } 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 429362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Initializing rx_scrq[%d] irq\n", 429462306a36Sopenharmony_ci i); 429562306a36Sopenharmony_ci scrq = adapter->rx_scrq[i]; 429662306a36Sopenharmony_ci scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); 429762306a36Sopenharmony_ci if (!scrq->irq) { 429862306a36Sopenharmony_ci rc = -EINVAL; 429962306a36Sopenharmony_ci dev_err(dev, "Error mapping irq\n"); 430062306a36Sopenharmony_ci goto req_rx_irq_failed; 430162306a36Sopenharmony_ci } 430262306a36Sopenharmony_ci snprintf(scrq->name, sizeof(scrq->name), "ibmvnic-%x-rx%d", 430362306a36Sopenharmony_ci adapter->vdev->unit_address, i); 430462306a36Sopenharmony_ci rc = request_irq(scrq->irq, ibmvnic_interrupt_rx, 430562306a36Sopenharmony_ci 0, scrq->name, scrq); 430662306a36Sopenharmony_ci if (rc) { 430762306a36Sopenharmony_ci dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n", 430862306a36Sopenharmony_ci scrq->irq, rc); 430962306a36Sopenharmony_ci irq_dispose_mapping(scrq->irq); 431062306a36Sopenharmony_ci goto req_rx_irq_failed; 431162306a36Sopenharmony_ci } 431262306a36Sopenharmony_ci } 431362306a36Sopenharmony_ci 431462306a36Sopenharmony_ci cpus_read_lock(); 431562306a36Sopenharmony_ci ibmvnic_set_affinity(adapter); 431662306a36Sopenharmony_ci cpus_read_unlock(); 431762306a36Sopenharmony_ci 431862306a36Sopenharmony_ci return rc; 431962306a36Sopenharmony_ci 432062306a36Sopenharmony_cireq_rx_irq_failed: 432162306a36Sopenharmony_ci for (j = 0; j < i; j++) { 432262306a36Sopenharmony_ci free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]); 432362306a36Sopenharmony_ci irq_dispose_mapping(adapter->rx_scrq[j]->irq); 432462306a36Sopenharmony_ci } 432562306a36Sopenharmony_ci i = adapter->req_tx_queues; 432662306a36Sopenharmony_cireq_tx_irq_failed: 432762306a36Sopenharmony_ci for (j = 0; j < i; j++) { 432862306a36Sopenharmony_ci free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]); 432962306a36Sopenharmony_ci irq_dispose_mapping(adapter->tx_scrq[j]->irq); 433062306a36Sopenharmony_ci } 433162306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 433262306a36Sopenharmony_ci return rc; 433362306a36Sopenharmony_ci} 433462306a36Sopenharmony_ci 433562306a36Sopenharmony_cistatic int init_sub_crqs(struct ibmvnic_adapter *adapter) 433662306a36Sopenharmony_ci{ 433762306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 433862306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue **allqueues; 433962306a36Sopenharmony_ci int registered_queues = 0; 434062306a36Sopenharmony_ci int total_queues; 434162306a36Sopenharmony_ci int more = 0; 434262306a36Sopenharmony_ci int i; 434362306a36Sopenharmony_ci 434462306a36Sopenharmony_ci total_queues = adapter->req_tx_queues + adapter->req_rx_queues; 434562306a36Sopenharmony_ci 434662306a36Sopenharmony_ci allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_KERNEL); 434762306a36Sopenharmony_ci if (!allqueues) 434862306a36Sopenharmony_ci return -ENOMEM; 434962306a36Sopenharmony_ci 435062306a36Sopenharmony_ci for (i = 0; i < total_queues; i++) { 435162306a36Sopenharmony_ci allqueues[i] = init_sub_crq_queue(adapter); 435262306a36Sopenharmony_ci if (!allqueues[i]) { 435362306a36Sopenharmony_ci dev_warn(dev, "Couldn't allocate all sub-crqs\n"); 435462306a36Sopenharmony_ci break; 435562306a36Sopenharmony_ci } 435662306a36Sopenharmony_ci registered_queues++; 435762306a36Sopenharmony_ci } 435862306a36Sopenharmony_ci 435962306a36Sopenharmony_ci /* Make sure we were able to register the minimum number of queues */ 436062306a36Sopenharmony_ci if (registered_queues < 436162306a36Sopenharmony_ci adapter->min_tx_queues + adapter->min_rx_queues) { 436262306a36Sopenharmony_ci dev_err(dev, "Fatal: Couldn't init min number of sub-crqs\n"); 436362306a36Sopenharmony_ci goto tx_failed; 436462306a36Sopenharmony_ci } 436562306a36Sopenharmony_ci 436662306a36Sopenharmony_ci /* Distribute the failed allocated queues*/ 436762306a36Sopenharmony_ci for (i = 0; i < total_queues - registered_queues + more ; i++) { 436862306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Reducing number of queues\n"); 436962306a36Sopenharmony_ci switch (i % 3) { 437062306a36Sopenharmony_ci case 0: 437162306a36Sopenharmony_ci if (adapter->req_rx_queues > adapter->min_rx_queues) 437262306a36Sopenharmony_ci adapter->req_rx_queues--; 437362306a36Sopenharmony_ci else 437462306a36Sopenharmony_ci more++; 437562306a36Sopenharmony_ci break; 437662306a36Sopenharmony_ci case 1: 437762306a36Sopenharmony_ci if (adapter->req_tx_queues > adapter->min_tx_queues) 437862306a36Sopenharmony_ci adapter->req_tx_queues--; 437962306a36Sopenharmony_ci else 438062306a36Sopenharmony_ci more++; 438162306a36Sopenharmony_ci break; 438262306a36Sopenharmony_ci } 438362306a36Sopenharmony_ci } 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci adapter->tx_scrq = kcalloc(adapter->req_tx_queues, 438662306a36Sopenharmony_ci sizeof(*adapter->tx_scrq), GFP_KERNEL); 438762306a36Sopenharmony_ci if (!adapter->tx_scrq) 438862306a36Sopenharmony_ci goto tx_failed; 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) { 439162306a36Sopenharmony_ci adapter->tx_scrq[i] = allqueues[i]; 439262306a36Sopenharmony_ci adapter->tx_scrq[i]->pool_index = i; 439362306a36Sopenharmony_ci adapter->num_active_tx_scrqs++; 439462306a36Sopenharmony_ci } 439562306a36Sopenharmony_ci 439662306a36Sopenharmony_ci adapter->rx_scrq = kcalloc(adapter->req_rx_queues, 439762306a36Sopenharmony_ci sizeof(*adapter->rx_scrq), GFP_KERNEL); 439862306a36Sopenharmony_ci if (!adapter->rx_scrq) 439962306a36Sopenharmony_ci goto rx_failed; 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 440262306a36Sopenharmony_ci adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues]; 440362306a36Sopenharmony_ci adapter->rx_scrq[i]->scrq_num = i; 440462306a36Sopenharmony_ci adapter->num_active_rx_scrqs++; 440562306a36Sopenharmony_ci } 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_ci kfree(allqueues); 440862306a36Sopenharmony_ci return 0; 440962306a36Sopenharmony_ci 441062306a36Sopenharmony_cirx_failed: 441162306a36Sopenharmony_ci kfree(adapter->tx_scrq); 441262306a36Sopenharmony_ci adapter->tx_scrq = NULL; 441362306a36Sopenharmony_citx_failed: 441462306a36Sopenharmony_ci for (i = 0; i < registered_queues; i++) 441562306a36Sopenharmony_ci release_sub_crq_queue(adapter, allqueues[i], 1); 441662306a36Sopenharmony_ci kfree(allqueues); 441762306a36Sopenharmony_ci return -ENOMEM; 441862306a36Sopenharmony_ci} 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_cistatic void send_request_cap(struct ibmvnic_adapter *adapter, int retry) 442162306a36Sopenharmony_ci{ 442262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 442362306a36Sopenharmony_ci union ibmvnic_crq crq; 442462306a36Sopenharmony_ci int max_entries; 442562306a36Sopenharmony_ci int cap_reqs; 442662306a36Sopenharmony_ci 442762306a36Sopenharmony_ci /* We send out 6 or 7 REQUEST_CAPABILITY CRQs below (depending on 442862306a36Sopenharmony_ci * the PROMISC flag). Initialize this count upfront. When the tasklet 442962306a36Sopenharmony_ci * receives a response to all of these, it will send the next protocol 443062306a36Sopenharmony_ci * message (QUERY_IP_OFFLOAD). 443162306a36Sopenharmony_ci */ 443262306a36Sopenharmony_ci if (!(adapter->netdev->flags & IFF_PROMISC) || 443362306a36Sopenharmony_ci adapter->promisc_supported) 443462306a36Sopenharmony_ci cap_reqs = 7; 443562306a36Sopenharmony_ci else 443662306a36Sopenharmony_ci cap_reqs = 6; 443762306a36Sopenharmony_ci 443862306a36Sopenharmony_ci if (!retry) { 443962306a36Sopenharmony_ci /* Sub-CRQ entries are 32 byte long */ 444062306a36Sopenharmony_ci int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4); 444162306a36Sopenharmony_ci 444262306a36Sopenharmony_ci atomic_set(&adapter->running_cap_crqs, cap_reqs); 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci if (adapter->min_tx_entries_per_subcrq > entries_page || 444562306a36Sopenharmony_ci adapter->min_rx_add_entries_per_subcrq > entries_page) { 444662306a36Sopenharmony_ci dev_err(dev, "Fatal, invalid entries per sub-crq\n"); 444762306a36Sopenharmony_ci return; 444862306a36Sopenharmony_ci } 444962306a36Sopenharmony_ci 445062306a36Sopenharmony_ci if (adapter->desired.mtu) 445162306a36Sopenharmony_ci adapter->req_mtu = adapter->desired.mtu; 445262306a36Sopenharmony_ci else 445362306a36Sopenharmony_ci adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN; 445462306a36Sopenharmony_ci 445562306a36Sopenharmony_ci if (!adapter->desired.tx_entries) 445662306a36Sopenharmony_ci adapter->desired.tx_entries = 445762306a36Sopenharmony_ci adapter->max_tx_entries_per_subcrq; 445862306a36Sopenharmony_ci if (!adapter->desired.rx_entries) 445962306a36Sopenharmony_ci adapter->desired.rx_entries = 446062306a36Sopenharmony_ci adapter->max_rx_add_entries_per_subcrq; 446162306a36Sopenharmony_ci 446262306a36Sopenharmony_ci max_entries = IBMVNIC_LTB_SET_SIZE / 446362306a36Sopenharmony_ci (adapter->req_mtu + IBMVNIC_BUFFER_HLEN); 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci if ((adapter->req_mtu + IBMVNIC_BUFFER_HLEN) * 446662306a36Sopenharmony_ci adapter->desired.tx_entries > IBMVNIC_LTB_SET_SIZE) { 446762306a36Sopenharmony_ci adapter->desired.tx_entries = max_entries; 446862306a36Sopenharmony_ci } 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_ci if ((adapter->req_mtu + IBMVNIC_BUFFER_HLEN) * 447162306a36Sopenharmony_ci adapter->desired.rx_entries > IBMVNIC_LTB_SET_SIZE) { 447262306a36Sopenharmony_ci adapter->desired.rx_entries = max_entries; 447362306a36Sopenharmony_ci } 447462306a36Sopenharmony_ci 447562306a36Sopenharmony_ci if (adapter->desired.tx_entries) 447662306a36Sopenharmony_ci adapter->req_tx_entries_per_subcrq = 447762306a36Sopenharmony_ci adapter->desired.tx_entries; 447862306a36Sopenharmony_ci else 447962306a36Sopenharmony_ci adapter->req_tx_entries_per_subcrq = 448062306a36Sopenharmony_ci adapter->max_tx_entries_per_subcrq; 448162306a36Sopenharmony_ci 448262306a36Sopenharmony_ci if (adapter->desired.rx_entries) 448362306a36Sopenharmony_ci adapter->req_rx_add_entries_per_subcrq = 448462306a36Sopenharmony_ci adapter->desired.rx_entries; 448562306a36Sopenharmony_ci else 448662306a36Sopenharmony_ci adapter->req_rx_add_entries_per_subcrq = 448762306a36Sopenharmony_ci adapter->max_rx_add_entries_per_subcrq; 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci if (adapter->desired.tx_queues) 449062306a36Sopenharmony_ci adapter->req_tx_queues = 449162306a36Sopenharmony_ci adapter->desired.tx_queues; 449262306a36Sopenharmony_ci else 449362306a36Sopenharmony_ci adapter->req_tx_queues = 449462306a36Sopenharmony_ci adapter->opt_tx_comp_sub_queues; 449562306a36Sopenharmony_ci 449662306a36Sopenharmony_ci if (adapter->desired.rx_queues) 449762306a36Sopenharmony_ci adapter->req_rx_queues = 449862306a36Sopenharmony_ci adapter->desired.rx_queues; 449962306a36Sopenharmony_ci else 450062306a36Sopenharmony_ci adapter->req_rx_queues = 450162306a36Sopenharmony_ci adapter->opt_rx_comp_queues; 450262306a36Sopenharmony_ci 450362306a36Sopenharmony_ci adapter->req_rx_add_queues = adapter->max_rx_add_queues; 450462306a36Sopenharmony_ci } else { 450562306a36Sopenharmony_ci atomic_add(cap_reqs, &adapter->running_cap_crqs); 450662306a36Sopenharmony_ci } 450762306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 450862306a36Sopenharmony_ci crq.request_capability.first = IBMVNIC_CRQ_CMD; 450962306a36Sopenharmony_ci crq.request_capability.cmd = REQUEST_CAPABILITY; 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci crq.request_capability.capability = cpu_to_be16(REQ_TX_QUEUES); 451262306a36Sopenharmony_ci crq.request_capability.number = cpu_to_be64(adapter->req_tx_queues); 451362306a36Sopenharmony_ci cap_reqs--; 451462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 451562306a36Sopenharmony_ci 451662306a36Sopenharmony_ci crq.request_capability.capability = cpu_to_be16(REQ_RX_QUEUES); 451762306a36Sopenharmony_ci crq.request_capability.number = cpu_to_be64(adapter->req_rx_queues); 451862306a36Sopenharmony_ci cap_reqs--; 451962306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci crq.request_capability.capability = cpu_to_be16(REQ_RX_ADD_QUEUES); 452262306a36Sopenharmony_ci crq.request_capability.number = cpu_to_be64(adapter->req_rx_add_queues); 452362306a36Sopenharmony_ci cap_reqs--; 452462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 452562306a36Sopenharmony_ci 452662306a36Sopenharmony_ci crq.request_capability.capability = 452762306a36Sopenharmony_ci cpu_to_be16(REQ_TX_ENTRIES_PER_SUBCRQ); 452862306a36Sopenharmony_ci crq.request_capability.number = 452962306a36Sopenharmony_ci cpu_to_be64(adapter->req_tx_entries_per_subcrq); 453062306a36Sopenharmony_ci cap_reqs--; 453162306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 453262306a36Sopenharmony_ci 453362306a36Sopenharmony_ci crq.request_capability.capability = 453462306a36Sopenharmony_ci cpu_to_be16(REQ_RX_ADD_ENTRIES_PER_SUBCRQ); 453562306a36Sopenharmony_ci crq.request_capability.number = 453662306a36Sopenharmony_ci cpu_to_be64(adapter->req_rx_add_entries_per_subcrq); 453762306a36Sopenharmony_ci cap_reqs--; 453862306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 453962306a36Sopenharmony_ci 454062306a36Sopenharmony_ci crq.request_capability.capability = cpu_to_be16(REQ_MTU); 454162306a36Sopenharmony_ci crq.request_capability.number = cpu_to_be64(adapter->req_mtu); 454262306a36Sopenharmony_ci cap_reqs--; 454362306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 454462306a36Sopenharmony_ci 454562306a36Sopenharmony_ci if (adapter->netdev->flags & IFF_PROMISC) { 454662306a36Sopenharmony_ci if (adapter->promisc_supported) { 454762306a36Sopenharmony_ci crq.request_capability.capability = 454862306a36Sopenharmony_ci cpu_to_be16(PROMISC_REQUESTED); 454962306a36Sopenharmony_ci crq.request_capability.number = cpu_to_be64(1); 455062306a36Sopenharmony_ci cap_reqs--; 455162306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 455262306a36Sopenharmony_ci } 455362306a36Sopenharmony_ci } else { 455462306a36Sopenharmony_ci crq.request_capability.capability = 455562306a36Sopenharmony_ci cpu_to_be16(PROMISC_REQUESTED); 455662306a36Sopenharmony_ci crq.request_capability.number = cpu_to_be64(0); 455762306a36Sopenharmony_ci cap_reqs--; 455862306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 455962306a36Sopenharmony_ci } 456062306a36Sopenharmony_ci 456162306a36Sopenharmony_ci /* Keep at end to catch any discrepancy between expected and actual 456262306a36Sopenharmony_ci * CRQs sent. 456362306a36Sopenharmony_ci */ 456462306a36Sopenharmony_ci WARN_ON(cap_reqs != 0); 456562306a36Sopenharmony_ci} 456662306a36Sopenharmony_ci 456762306a36Sopenharmony_cistatic int pending_scrq(struct ibmvnic_adapter *adapter, 456862306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 456962306a36Sopenharmony_ci{ 457062306a36Sopenharmony_ci union sub_crq *entry = &scrq->msgs[scrq->cur]; 457162306a36Sopenharmony_ci int rc; 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ci rc = !!(entry->generic.first & IBMVNIC_CRQ_CMD_RSP); 457462306a36Sopenharmony_ci 457562306a36Sopenharmony_ci /* Ensure that the SCRQ valid flag is loaded prior to loading the 457662306a36Sopenharmony_ci * contents of the SCRQ descriptor 457762306a36Sopenharmony_ci */ 457862306a36Sopenharmony_ci dma_rmb(); 457962306a36Sopenharmony_ci 458062306a36Sopenharmony_ci return rc; 458162306a36Sopenharmony_ci} 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_cistatic union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *adapter, 458462306a36Sopenharmony_ci struct ibmvnic_sub_crq_queue *scrq) 458562306a36Sopenharmony_ci{ 458662306a36Sopenharmony_ci union sub_crq *entry; 458762306a36Sopenharmony_ci unsigned long flags; 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_ci spin_lock_irqsave(&scrq->lock, flags); 459062306a36Sopenharmony_ci entry = &scrq->msgs[scrq->cur]; 459162306a36Sopenharmony_ci if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP) { 459262306a36Sopenharmony_ci if (++scrq->cur == scrq->size) 459362306a36Sopenharmony_ci scrq->cur = 0; 459462306a36Sopenharmony_ci } else { 459562306a36Sopenharmony_ci entry = NULL; 459662306a36Sopenharmony_ci } 459762306a36Sopenharmony_ci spin_unlock_irqrestore(&scrq->lock, flags); 459862306a36Sopenharmony_ci 459962306a36Sopenharmony_ci /* Ensure that the SCRQ valid flag is loaded prior to loading the 460062306a36Sopenharmony_ci * contents of the SCRQ descriptor 460162306a36Sopenharmony_ci */ 460262306a36Sopenharmony_ci dma_rmb(); 460362306a36Sopenharmony_ci 460462306a36Sopenharmony_ci return entry; 460562306a36Sopenharmony_ci} 460662306a36Sopenharmony_ci 460762306a36Sopenharmony_cistatic union ibmvnic_crq *ibmvnic_next_crq(struct ibmvnic_adapter *adapter) 460862306a36Sopenharmony_ci{ 460962306a36Sopenharmony_ci struct ibmvnic_crq_queue *queue = &adapter->crq; 461062306a36Sopenharmony_ci union ibmvnic_crq *crq; 461162306a36Sopenharmony_ci 461262306a36Sopenharmony_ci crq = &queue->msgs[queue->cur]; 461362306a36Sopenharmony_ci if (crq->generic.first & IBMVNIC_CRQ_CMD_RSP) { 461462306a36Sopenharmony_ci if (++queue->cur == queue->size) 461562306a36Sopenharmony_ci queue->cur = 0; 461662306a36Sopenharmony_ci } else { 461762306a36Sopenharmony_ci crq = NULL; 461862306a36Sopenharmony_ci } 461962306a36Sopenharmony_ci 462062306a36Sopenharmony_ci return crq; 462162306a36Sopenharmony_ci} 462262306a36Sopenharmony_ci 462362306a36Sopenharmony_cistatic void print_subcrq_error(struct device *dev, int rc, const char *func) 462462306a36Sopenharmony_ci{ 462562306a36Sopenharmony_ci switch (rc) { 462662306a36Sopenharmony_ci case H_PARAMETER: 462762306a36Sopenharmony_ci dev_warn_ratelimited(dev, 462862306a36Sopenharmony_ci "%s failed: Send request is malformed or adapter failover pending. (rc=%d)\n", 462962306a36Sopenharmony_ci func, rc); 463062306a36Sopenharmony_ci break; 463162306a36Sopenharmony_ci case H_CLOSED: 463262306a36Sopenharmony_ci dev_warn_ratelimited(dev, 463362306a36Sopenharmony_ci "%s failed: Backing queue closed. Adapter is down or failover pending. (rc=%d)\n", 463462306a36Sopenharmony_ci func, rc); 463562306a36Sopenharmony_ci break; 463662306a36Sopenharmony_ci default: 463762306a36Sopenharmony_ci dev_err_ratelimited(dev, "%s failed: (rc=%d)\n", func, rc); 463862306a36Sopenharmony_ci break; 463962306a36Sopenharmony_ci } 464062306a36Sopenharmony_ci} 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_cistatic int send_subcrq_indirect(struct ibmvnic_adapter *adapter, 464362306a36Sopenharmony_ci u64 remote_handle, u64 ioba, u64 num_entries) 464462306a36Sopenharmony_ci{ 464562306a36Sopenharmony_ci unsigned int ua = adapter->vdev->unit_address; 464662306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 464762306a36Sopenharmony_ci int rc; 464862306a36Sopenharmony_ci 464962306a36Sopenharmony_ci /* Make sure the hypervisor sees the complete request */ 465062306a36Sopenharmony_ci dma_wmb(); 465162306a36Sopenharmony_ci rc = plpar_hcall_norets(H_SEND_SUB_CRQ_INDIRECT, ua, 465262306a36Sopenharmony_ci cpu_to_be64(remote_handle), 465362306a36Sopenharmony_ci ioba, num_entries); 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_ci if (rc) 465662306a36Sopenharmony_ci print_subcrq_error(dev, rc, __func__); 465762306a36Sopenharmony_ci 465862306a36Sopenharmony_ci return rc; 465962306a36Sopenharmony_ci} 466062306a36Sopenharmony_ci 466162306a36Sopenharmony_cistatic int ibmvnic_send_crq(struct ibmvnic_adapter *adapter, 466262306a36Sopenharmony_ci union ibmvnic_crq *crq) 466362306a36Sopenharmony_ci{ 466462306a36Sopenharmony_ci unsigned int ua = adapter->vdev->unit_address; 466562306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 466662306a36Sopenharmony_ci u64 *u64_crq = (u64 *)crq; 466762306a36Sopenharmony_ci int rc; 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Sending CRQ: %016lx %016lx\n", 467062306a36Sopenharmony_ci (unsigned long)cpu_to_be64(u64_crq[0]), 467162306a36Sopenharmony_ci (unsigned long)cpu_to_be64(u64_crq[1])); 467262306a36Sopenharmony_ci 467362306a36Sopenharmony_ci if (!adapter->crq.active && 467462306a36Sopenharmony_ci crq->generic.first != IBMVNIC_CRQ_INIT_CMD) { 467562306a36Sopenharmony_ci dev_warn(dev, "Invalid request detected while CRQ is inactive, possible device state change during reset\n"); 467662306a36Sopenharmony_ci return -EINVAL; 467762306a36Sopenharmony_ci } 467862306a36Sopenharmony_ci 467962306a36Sopenharmony_ci /* Make sure the hypervisor sees the complete request */ 468062306a36Sopenharmony_ci dma_wmb(); 468162306a36Sopenharmony_ci 468262306a36Sopenharmony_ci rc = plpar_hcall_norets(H_SEND_CRQ, ua, 468362306a36Sopenharmony_ci cpu_to_be64(u64_crq[0]), 468462306a36Sopenharmony_ci cpu_to_be64(u64_crq[1])); 468562306a36Sopenharmony_ci 468662306a36Sopenharmony_ci if (rc) { 468762306a36Sopenharmony_ci if (rc == H_CLOSED) { 468862306a36Sopenharmony_ci dev_warn(dev, "CRQ Queue closed\n"); 468962306a36Sopenharmony_ci /* do not reset, report the fail, wait for passive init from server */ 469062306a36Sopenharmony_ci } 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_ci dev_warn(dev, "Send error (rc=%d)\n", rc); 469362306a36Sopenharmony_ci } 469462306a36Sopenharmony_ci 469562306a36Sopenharmony_ci return rc; 469662306a36Sopenharmony_ci} 469762306a36Sopenharmony_ci 469862306a36Sopenharmony_cistatic int ibmvnic_send_crq_init(struct ibmvnic_adapter *adapter) 469962306a36Sopenharmony_ci{ 470062306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 470162306a36Sopenharmony_ci union ibmvnic_crq crq; 470262306a36Sopenharmony_ci int retries = 100; 470362306a36Sopenharmony_ci int rc; 470462306a36Sopenharmony_ci 470562306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 470662306a36Sopenharmony_ci crq.generic.first = IBMVNIC_CRQ_INIT_CMD; 470762306a36Sopenharmony_ci crq.generic.cmd = IBMVNIC_CRQ_INIT; 470862306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Sending CRQ init\n"); 470962306a36Sopenharmony_ci 471062306a36Sopenharmony_ci do { 471162306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 471262306a36Sopenharmony_ci if (rc != H_CLOSED) 471362306a36Sopenharmony_ci break; 471462306a36Sopenharmony_ci retries--; 471562306a36Sopenharmony_ci msleep(50); 471662306a36Sopenharmony_ci 471762306a36Sopenharmony_ci } while (retries > 0); 471862306a36Sopenharmony_ci 471962306a36Sopenharmony_ci if (rc) { 472062306a36Sopenharmony_ci dev_err(dev, "Failed to send init request, rc = %d\n", rc); 472162306a36Sopenharmony_ci return rc; 472262306a36Sopenharmony_ci } 472362306a36Sopenharmony_ci 472462306a36Sopenharmony_ci return 0; 472562306a36Sopenharmony_ci} 472662306a36Sopenharmony_ci 472762306a36Sopenharmony_cistruct vnic_login_client_data { 472862306a36Sopenharmony_ci u8 type; 472962306a36Sopenharmony_ci __be16 len; 473062306a36Sopenharmony_ci char name[]; 473162306a36Sopenharmony_ci} __packed; 473262306a36Sopenharmony_ci 473362306a36Sopenharmony_cistatic int vnic_client_data_len(struct ibmvnic_adapter *adapter) 473462306a36Sopenharmony_ci{ 473562306a36Sopenharmony_ci int len; 473662306a36Sopenharmony_ci 473762306a36Sopenharmony_ci /* Calculate the amount of buffer space needed for the 473862306a36Sopenharmony_ci * vnic client data in the login buffer. There are four entries, 473962306a36Sopenharmony_ci * OS name, LPAR name, device name, and a null last entry. 474062306a36Sopenharmony_ci */ 474162306a36Sopenharmony_ci len = 4 * sizeof(struct vnic_login_client_data); 474262306a36Sopenharmony_ci len += 6; /* "Linux" plus NULL */ 474362306a36Sopenharmony_ci len += strlen(utsname()->nodename) + 1; 474462306a36Sopenharmony_ci len += strlen(adapter->netdev->name) + 1; 474562306a36Sopenharmony_ci 474662306a36Sopenharmony_ci return len; 474762306a36Sopenharmony_ci} 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_cistatic void vnic_add_client_data(struct ibmvnic_adapter *adapter, 475062306a36Sopenharmony_ci struct vnic_login_client_data *vlcd) 475162306a36Sopenharmony_ci{ 475262306a36Sopenharmony_ci const char *os_name = "Linux"; 475362306a36Sopenharmony_ci int len; 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_ci /* Type 1 - LPAR OS */ 475662306a36Sopenharmony_ci vlcd->type = 1; 475762306a36Sopenharmony_ci len = strlen(os_name) + 1; 475862306a36Sopenharmony_ci vlcd->len = cpu_to_be16(len); 475962306a36Sopenharmony_ci strscpy(vlcd->name, os_name, len); 476062306a36Sopenharmony_ci vlcd = (struct vnic_login_client_data *)(vlcd->name + len); 476162306a36Sopenharmony_ci 476262306a36Sopenharmony_ci /* Type 2 - LPAR name */ 476362306a36Sopenharmony_ci vlcd->type = 2; 476462306a36Sopenharmony_ci len = strlen(utsname()->nodename) + 1; 476562306a36Sopenharmony_ci vlcd->len = cpu_to_be16(len); 476662306a36Sopenharmony_ci strscpy(vlcd->name, utsname()->nodename, len); 476762306a36Sopenharmony_ci vlcd = (struct vnic_login_client_data *)(vlcd->name + len); 476862306a36Sopenharmony_ci 476962306a36Sopenharmony_ci /* Type 3 - device name */ 477062306a36Sopenharmony_ci vlcd->type = 3; 477162306a36Sopenharmony_ci len = strlen(adapter->netdev->name) + 1; 477262306a36Sopenharmony_ci vlcd->len = cpu_to_be16(len); 477362306a36Sopenharmony_ci strscpy(vlcd->name, adapter->netdev->name, len); 477462306a36Sopenharmony_ci} 477562306a36Sopenharmony_ci 477662306a36Sopenharmony_cistatic int send_login(struct ibmvnic_adapter *adapter) 477762306a36Sopenharmony_ci{ 477862306a36Sopenharmony_ci struct ibmvnic_login_rsp_buffer *login_rsp_buffer; 477962306a36Sopenharmony_ci struct ibmvnic_login_buffer *login_buffer; 478062306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 478162306a36Sopenharmony_ci struct vnic_login_client_data *vlcd; 478262306a36Sopenharmony_ci dma_addr_t rsp_buffer_token; 478362306a36Sopenharmony_ci dma_addr_t buffer_token; 478462306a36Sopenharmony_ci size_t rsp_buffer_size; 478562306a36Sopenharmony_ci union ibmvnic_crq crq; 478662306a36Sopenharmony_ci int client_data_len; 478762306a36Sopenharmony_ci size_t buffer_size; 478862306a36Sopenharmony_ci __be64 *tx_list_p; 478962306a36Sopenharmony_ci __be64 *rx_list_p; 479062306a36Sopenharmony_ci int rc; 479162306a36Sopenharmony_ci int i; 479262306a36Sopenharmony_ci 479362306a36Sopenharmony_ci if (!adapter->tx_scrq || !adapter->rx_scrq) { 479462306a36Sopenharmony_ci netdev_err(adapter->netdev, 479562306a36Sopenharmony_ci "RX or TX queues are not allocated, device login failed\n"); 479662306a36Sopenharmony_ci return -ENOMEM; 479762306a36Sopenharmony_ci } 479862306a36Sopenharmony_ci 479962306a36Sopenharmony_ci release_login_buffer(adapter); 480062306a36Sopenharmony_ci release_login_rsp_buffer(adapter); 480162306a36Sopenharmony_ci 480262306a36Sopenharmony_ci client_data_len = vnic_client_data_len(adapter); 480362306a36Sopenharmony_ci 480462306a36Sopenharmony_ci buffer_size = 480562306a36Sopenharmony_ci sizeof(struct ibmvnic_login_buffer) + 480662306a36Sopenharmony_ci sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues) + 480762306a36Sopenharmony_ci client_data_len; 480862306a36Sopenharmony_ci 480962306a36Sopenharmony_ci login_buffer = kzalloc(buffer_size, GFP_ATOMIC); 481062306a36Sopenharmony_ci if (!login_buffer) 481162306a36Sopenharmony_ci goto buf_alloc_failed; 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci buffer_token = dma_map_single(dev, login_buffer, buffer_size, 481462306a36Sopenharmony_ci DMA_TO_DEVICE); 481562306a36Sopenharmony_ci if (dma_mapping_error(dev, buffer_token)) { 481662306a36Sopenharmony_ci dev_err(dev, "Couldn't map login buffer\n"); 481762306a36Sopenharmony_ci goto buf_map_failed; 481862306a36Sopenharmony_ci } 481962306a36Sopenharmony_ci 482062306a36Sopenharmony_ci rsp_buffer_size = sizeof(struct ibmvnic_login_rsp_buffer) + 482162306a36Sopenharmony_ci sizeof(u64) * adapter->req_tx_queues + 482262306a36Sopenharmony_ci sizeof(u64) * adapter->req_rx_queues + 482362306a36Sopenharmony_ci sizeof(u64) * adapter->req_rx_queues + 482462306a36Sopenharmony_ci sizeof(u8) * IBMVNIC_TX_DESC_VERSIONS; 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci login_rsp_buffer = kmalloc(rsp_buffer_size, GFP_ATOMIC); 482762306a36Sopenharmony_ci if (!login_rsp_buffer) 482862306a36Sopenharmony_ci goto buf_rsp_alloc_failed; 482962306a36Sopenharmony_ci 483062306a36Sopenharmony_ci rsp_buffer_token = dma_map_single(dev, login_rsp_buffer, 483162306a36Sopenharmony_ci rsp_buffer_size, DMA_FROM_DEVICE); 483262306a36Sopenharmony_ci if (dma_mapping_error(dev, rsp_buffer_token)) { 483362306a36Sopenharmony_ci dev_err(dev, "Couldn't map login rsp buffer\n"); 483462306a36Sopenharmony_ci goto buf_rsp_map_failed; 483562306a36Sopenharmony_ci } 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_ci adapter->login_buf = login_buffer; 483862306a36Sopenharmony_ci adapter->login_buf_token = buffer_token; 483962306a36Sopenharmony_ci adapter->login_buf_sz = buffer_size; 484062306a36Sopenharmony_ci adapter->login_rsp_buf = login_rsp_buffer; 484162306a36Sopenharmony_ci adapter->login_rsp_buf_token = rsp_buffer_token; 484262306a36Sopenharmony_ci adapter->login_rsp_buf_sz = rsp_buffer_size; 484362306a36Sopenharmony_ci 484462306a36Sopenharmony_ci login_buffer->len = cpu_to_be32(buffer_size); 484562306a36Sopenharmony_ci login_buffer->version = cpu_to_be32(INITIAL_VERSION_LB); 484662306a36Sopenharmony_ci login_buffer->num_txcomp_subcrqs = cpu_to_be32(adapter->req_tx_queues); 484762306a36Sopenharmony_ci login_buffer->off_txcomp_subcrqs = 484862306a36Sopenharmony_ci cpu_to_be32(sizeof(struct ibmvnic_login_buffer)); 484962306a36Sopenharmony_ci login_buffer->num_rxcomp_subcrqs = cpu_to_be32(adapter->req_rx_queues); 485062306a36Sopenharmony_ci login_buffer->off_rxcomp_subcrqs = 485162306a36Sopenharmony_ci cpu_to_be32(sizeof(struct ibmvnic_login_buffer) + 485262306a36Sopenharmony_ci sizeof(u64) * adapter->req_tx_queues); 485362306a36Sopenharmony_ci login_buffer->login_rsp_ioba = cpu_to_be32(rsp_buffer_token); 485462306a36Sopenharmony_ci login_buffer->login_rsp_len = cpu_to_be32(rsp_buffer_size); 485562306a36Sopenharmony_ci 485662306a36Sopenharmony_ci tx_list_p = (__be64 *)((char *)login_buffer + 485762306a36Sopenharmony_ci sizeof(struct ibmvnic_login_buffer)); 485862306a36Sopenharmony_ci rx_list_p = (__be64 *)((char *)login_buffer + 485962306a36Sopenharmony_ci sizeof(struct ibmvnic_login_buffer) + 486062306a36Sopenharmony_ci sizeof(u64) * adapter->req_tx_queues); 486162306a36Sopenharmony_ci 486262306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues; i++) { 486362306a36Sopenharmony_ci if (adapter->tx_scrq[i]) { 486462306a36Sopenharmony_ci tx_list_p[i] = 486562306a36Sopenharmony_ci cpu_to_be64(adapter->tx_scrq[i]->crq_num); 486662306a36Sopenharmony_ci } 486762306a36Sopenharmony_ci } 486862306a36Sopenharmony_ci 486962306a36Sopenharmony_ci for (i = 0; i < adapter->req_rx_queues; i++) { 487062306a36Sopenharmony_ci if (adapter->rx_scrq[i]) { 487162306a36Sopenharmony_ci rx_list_p[i] = 487262306a36Sopenharmony_ci cpu_to_be64(adapter->rx_scrq[i]->crq_num); 487362306a36Sopenharmony_ci } 487462306a36Sopenharmony_ci } 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci /* Insert vNIC login client data */ 487762306a36Sopenharmony_ci vlcd = (struct vnic_login_client_data *) 487862306a36Sopenharmony_ci ((char *)rx_list_p + (sizeof(u64) * adapter->req_rx_queues)); 487962306a36Sopenharmony_ci login_buffer->client_data_offset = 488062306a36Sopenharmony_ci cpu_to_be32((char *)vlcd - (char *)login_buffer); 488162306a36Sopenharmony_ci login_buffer->client_data_len = cpu_to_be32(client_data_len); 488262306a36Sopenharmony_ci 488362306a36Sopenharmony_ci vnic_add_client_data(adapter, vlcd); 488462306a36Sopenharmony_ci 488562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Login Buffer:\n"); 488662306a36Sopenharmony_ci for (i = 0; i < (adapter->login_buf_sz - 1) / 8 + 1; i++) { 488762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "%016lx\n", 488862306a36Sopenharmony_ci ((unsigned long *)(adapter->login_buf))[i]); 488962306a36Sopenharmony_ci } 489062306a36Sopenharmony_ci 489162306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 489262306a36Sopenharmony_ci crq.login.first = IBMVNIC_CRQ_CMD; 489362306a36Sopenharmony_ci crq.login.cmd = LOGIN; 489462306a36Sopenharmony_ci crq.login.ioba = cpu_to_be32(buffer_token); 489562306a36Sopenharmony_ci crq.login.len = cpu_to_be32(buffer_size); 489662306a36Sopenharmony_ci 489762306a36Sopenharmony_ci adapter->login_pending = true; 489862306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 489962306a36Sopenharmony_ci if (rc) { 490062306a36Sopenharmony_ci adapter->login_pending = false; 490162306a36Sopenharmony_ci netdev_err(adapter->netdev, "Failed to send login, rc=%d\n", rc); 490262306a36Sopenharmony_ci goto buf_send_failed; 490362306a36Sopenharmony_ci } 490462306a36Sopenharmony_ci 490562306a36Sopenharmony_ci return 0; 490662306a36Sopenharmony_ci 490762306a36Sopenharmony_cibuf_send_failed: 490862306a36Sopenharmony_ci dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size, 490962306a36Sopenharmony_ci DMA_FROM_DEVICE); 491062306a36Sopenharmony_cibuf_rsp_map_failed: 491162306a36Sopenharmony_ci kfree(login_rsp_buffer); 491262306a36Sopenharmony_ci adapter->login_rsp_buf = NULL; 491362306a36Sopenharmony_cibuf_rsp_alloc_failed: 491462306a36Sopenharmony_ci dma_unmap_single(dev, buffer_token, buffer_size, DMA_TO_DEVICE); 491562306a36Sopenharmony_cibuf_map_failed: 491662306a36Sopenharmony_ci kfree(login_buffer); 491762306a36Sopenharmony_ci adapter->login_buf = NULL; 491862306a36Sopenharmony_cibuf_alloc_failed: 491962306a36Sopenharmony_ci return -ENOMEM; 492062306a36Sopenharmony_ci} 492162306a36Sopenharmony_ci 492262306a36Sopenharmony_cistatic int send_request_map(struct ibmvnic_adapter *adapter, dma_addr_t addr, 492362306a36Sopenharmony_ci u32 len, u8 map_id) 492462306a36Sopenharmony_ci{ 492562306a36Sopenharmony_ci union ibmvnic_crq crq; 492662306a36Sopenharmony_ci 492762306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 492862306a36Sopenharmony_ci crq.request_map.first = IBMVNIC_CRQ_CMD; 492962306a36Sopenharmony_ci crq.request_map.cmd = REQUEST_MAP; 493062306a36Sopenharmony_ci crq.request_map.map_id = map_id; 493162306a36Sopenharmony_ci crq.request_map.ioba = cpu_to_be32(addr); 493262306a36Sopenharmony_ci crq.request_map.len = cpu_to_be32(len); 493362306a36Sopenharmony_ci return ibmvnic_send_crq(adapter, &crq); 493462306a36Sopenharmony_ci} 493562306a36Sopenharmony_ci 493662306a36Sopenharmony_cistatic int send_request_unmap(struct ibmvnic_adapter *adapter, u8 map_id) 493762306a36Sopenharmony_ci{ 493862306a36Sopenharmony_ci union ibmvnic_crq crq; 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 494162306a36Sopenharmony_ci crq.request_unmap.first = IBMVNIC_CRQ_CMD; 494262306a36Sopenharmony_ci crq.request_unmap.cmd = REQUEST_UNMAP; 494362306a36Sopenharmony_ci crq.request_unmap.map_id = map_id; 494462306a36Sopenharmony_ci return ibmvnic_send_crq(adapter, &crq); 494562306a36Sopenharmony_ci} 494662306a36Sopenharmony_ci 494762306a36Sopenharmony_cistatic void send_query_map(struct ibmvnic_adapter *adapter) 494862306a36Sopenharmony_ci{ 494962306a36Sopenharmony_ci union ibmvnic_crq crq; 495062306a36Sopenharmony_ci 495162306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 495262306a36Sopenharmony_ci crq.query_map.first = IBMVNIC_CRQ_CMD; 495362306a36Sopenharmony_ci crq.query_map.cmd = QUERY_MAP; 495462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 495562306a36Sopenharmony_ci} 495662306a36Sopenharmony_ci 495762306a36Sopenharmony_ci/* Send a series of CRQs requesting various capabilities of the VNIC server */ 495862306a36Sopenharmony_cistatic void send_query_cap(struct ibmvnic_adapter *adapter) 495962306a36Sopenharmony_ci{ 496062306a36Sopenharmony_ci union ibmvnic_crq crq; 496162306a36Sopenharmony_ci int cap_reqs; 496262306a36Sopenharmony_ci 496362306a36Sopenharmony_ci /* We send out 25 QUERY_CAPABILITY CRQs below. Initialize this count 496462306a36Sopenharmony_ci * upfront. When the tasklet receives a response to all of these, it 496562306a36Sopenharmony_ci * can send out the next protocol messaage (REQUEST_CAPABILITY). 496662306a36Sopenharmony_ci */ 496762306a36Sopenharmony_ci cap_reqs = 25; 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_ci atomic_set(&adapter->running_cap_crqs, cap_reqs); 497062306a36Sopenharmony_ci 497162306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 497262306a36Sopenharmony_ci crq.query_capability.first = IBMVNIC_CRQ_CMD; 497362306a36Sopenharmony_ci crq.query_capability.cmd = QUERY_CAPABILITY; 497462306a36Sopenharmony_ci 497562306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MIN_TX_QUEUES); 497662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 497762306a36Sopenharmony_ci cap_reqs--; 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MIN_RX_QUEUES); 498062306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 498162306a36Sopenharmony_ci cap_reqs--; 498262306a36Sopenharmony_ci 498362306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MIN_RX_ADD_QUEUES); 498462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 498562306a36Sopenharmony_ci cap_reqs--; 498662306a36Sopenharmony_ci 498762306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MAX_TX_QUEUES); 498862306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 498962306a36Sopenharmony_ci cap_reqs--; 499062306a36Sopenharmony_ci 499162306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MAX_RX_QUEUES); 499262306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 499362306a36Sopenharmony_ci cap_reqs--; 499462306a36Sopenharmony_ci 499562306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MAX_RX_ADD_QUEUES); 499662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 499762306a36Sopenharmony_ci cap_reqs--; 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_ci crq.query_capability.capability = 500062306a36Sopenharmony_ci cpu_to_be16(MIN_TX_ENTRIES_PER_SUBCRQ); 500162306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 500262306a36Sopenharmony_ci cap_reqs--; 500362306a36Sopenharmony_ci 500462306a36Sopenharmony_ci crq.query_capability.capability = 500562306a36Sopenharmony_ci cpu_to_be16(MIN_RX_ADD_ENTRIES_PER_SUBCRQ); 500662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 500762306a36Sopenharmony_ci cap_reqs--; 500862306a36Sopenharmony_ci 500962306a36Sopenharmony_ci crq.query_capability.capability = 501062306a36Sopenharmony_ci cpu_to_be16(MAX_TX_ENTRIES_PER_SUBCRQ); 501162306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 501262306a36Sopenharmony_ci cap_reqs--; 501362306a36Sopenharmony_ci 501462306a36Sopenharmony_ci crq.query_capability.capability = 501562306a36Sopenharmony_ci cpu_to_be16(MAX_RX_ADD_ENTRIES_PER_SUBCRQ); 501662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 501762306a36Sopenharmony_ci cap_reqs--; 501862306a36Sopenharmony_ci 501962306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(TCP_IP_OFFLOAD); 502062306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 502162306a36Sopenharmony_ci cap_reqs--; 502262306a36Sopenharmony_ci 502362306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(PROMISC_SUPPORTED); 502462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 502562306a36Sopenharmony_ci cap_reqs--; 502662306a36Sopenharmony_ci 502762306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MIN_MTU); 502862306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 502962306a36Sopenharmony_ci cap_reqs--; 503062306a36Sopenharmony_ci 503162306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MAX_MTU); 503262306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 503362306a36Sopenharmony_ci cap_reqs--; 503462306a36Sopenharmony_ci 503562306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MAX_MULTICAST_FILTERS); 503662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 503762306a36Sopenharmony_ci cap_reqs--; 503862306a36Sopenharmony_ci 503962306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(VLAN_HEADER_INSERTION); 504062306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 504162306a36Sopenharmony_ci cap_reqs--; 504262306a36Sopenharmony_ci 504362306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(RX_VLAN_HEADER_INSERTION); 504462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 504562306a36Sopenharmony_ci cap_reqs--; 504662306a36Sopenharmony_ci 504762306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(MAX_TX_SG_ENTRIES); 504862306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 504962306a36Sopenharmony_ci cap_reqs--; 505062306a36Sopenharmony_ci 505162306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(RX_SG_SUPPORTED); 505262306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 505362306a36Sopenharmony_ci cap_reqs--; 505462306a36Sopenharmony_ci 505562306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(OPT_TX_COMP_SUB_QUEUES); 505662306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 505762306a36Sopenharmony_ci cap_reqs--; 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(OPT_RX_COMP_QUEUES); 506062306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 506162306a36Sopenharmony_ci cap_reqs--; 506262306a36Sopenharmony_ci 506362306a36Sopenharmony_ci crq.query_capability.capability = 506462306a36Sopenharmony_ci cpu_to_be16(OPT_RX_BUFADD_Q_PER_RX_COMP_Q); 506562306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 506662306a36Sopenharmony_ci cap_reqs--; 506762306a36Sopenharmony_ci 506862306a36Sopenharmony_ci crq.query_capability.capability = 506962306a36Sopenharmony_ci cpu_to_be16(OPT_TX_ENTRIES_PER_SUBCRQ); 507062306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 507162306a36Sopenharmony_ci cap_reqs--; 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_ci crq.query_capability.capability = 507462306a36Sopenharmony_ci cpu_to_be16(OPT_RXBA_ENTRIES_PER_SUBCRQ); 507562306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 507662306a36Sopenharmony_ci cap_reqs--; 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ci crq.query_capability.capability = cpu_to_be16(TX_RX_DESC_REQ); 507962306a36Sopenharmony_ci 508062306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 508162306a36Sopenharmony_ci cap_reqs--; 508262306a36Sopenharmony_ci 508362306a36Sopenharmony_ci /* Keep at end to catch any discrepancy between expected and actual 508462306a36Sopenharmony_ci * CRQs sent. 508562306a36Sopenharmony_ci */ 508662306a36Sopenharmony_ci WARN_ON(cap_reqs != 0); 508762306a36Sopenharmony_ci} 508862306a36Sopenharmony_ci 508962306a36Sopenharmony_cistatic void send_query_ip_offload(struct ibmvnic_adapter *adapter) 509062306a36Sopenharmony_ci{ 509162306a36Sopenharmony_ci int buf_sz = sizeof(struct ibmvnic_query_ip_offload_buffer); 509262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 509362306a36Sopenharmony_ci union ibmvnic_crq crq; 509462306a36Sopenharmony_ci 509562306a36Sopenharmony_ci adapter->ip_offload_tok = 509662306a36Sopenharmony_ci dma_map_single(dev, 509762306a36Sopenharmony_ci &adapter->ip_offload_buf, 509862306a36Sopenharmony_ci buf_sz, 509962306a36Sopenharmony_ci DMA_FROM_DEVICE); 510062306a36Sopenharmony_ci 510162306a36Sopenharmony_ci if (dma_mapping_error(dev, adapter->ip_offload_tok)) { 510262306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_CMO)) 510362306a36Sopenharmony_ci dev_err(dev, "Couldn't map offload buffer\n"); 510462306a36Sopenharmony_ci return; 510562306a36Sopenharmony_ci } 510662306a36Sopenharmony_ci 510762306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 510862306a36Sopenharmony_ci crq.query_ip_offload.first = IBMVNIC_CRQ_CMD; 510962306a36Sopenharmony_ci crq.query_ip_offload.cmd = QUERY_IP_OFFLOAD; 511062306a36Sopenharmony_ci crq.query_ip_offload.len = cpu_to_be32(buf_sz); 511162306a36Sopenharmony_ci crq.query_ip_offload.ioba = 511262306a36Sopenharmony_ci cpu_to_be32(adapter->ip_offload_tok); 511362306a36Sopenharmony_ci 511462306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 511562306a36Sopenharmony_ci} 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_cistatic void send_control_ip_offload(struct ibmvnic_adapter *adapter) 511862306a36Sopenharmony_ci{ 511962306a36Sopenharmony_ci struct ibmvnic_control_ip_offload_buffer *ctrl_buf = &adapter->ip_offload_ctrl; 512062306a36Sopenharmony_ci struct ibmvnic_query_ip_offload_buffer *buf = &adapter->ip_offload_buf; 512162306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 512262306a36Sopenharmony_ci netdev_features_t old_hw_features = 0; 512362306a36Sopenharmony_ci union ibmvnic_crq crq; 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_ci adapter->ip_offload_ctrl_tok = 512662306a36Sopenharmony_ci dma_map_single(dev, 512762306a36Sopenharmony_ci ctrl_buf, 512862306a36Sopenharmony_ci sizeof(adapter->ip_offload_ctrl), 512962306a36Sopenharmony_ci DMA_TO_DEVICE); 513062306a36Sopenharmony_ci 513162306a36Sopenharmony_ci if (dma_mapping_error(dev, adapter->ip_offload_ctrl_tok)) { 513262306a36Sopenharmony_ci dev_err(dev, "Couldn't map ip offload control buffer\n"); 513362306a36Sopenharmony_ci return; 513462306a36Sopenharmony_ci } 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci ctrl_buf->len = cpu_to_be32(sizeof(adapter->ip_offload_ctrl)); 513762306a36Sopenharmony_ci ctrl_buf->version = cpu_to_be32(INITIAL_VERSION_IOB); 513862306a36Sopenharmony_ci ctrl_buf->ipv4_chksum = buf->ipv4_chksum; 513962306a36Sopenharmony_ci ctrl_buf->ipv6_chksum = buf->ipv6_chksum; 514062306a36Sopenharmony_ci ctrl_buf->tcp_ipv4_chksum = buf->tcp_ipv4_chksum; 514162306a36Sopenharmony_ci ctrl_buf->udp_ipv4_chksum = buf->udp_ipv4_chksum; 514262306a36Sopenharmony_ci ctrl_buf->tcp_ipv6_chksum = buf->tcp_ipv6_chksum; 514362306a36Sopenharmony_ci ctrl_buf->udp_ipv6_chksum = buf->udp_ipv6_chksum; 514462306a36Sopenharmony_ci ctrl_buf->large_tx_ipv4 = buf->large_tx_ipv4; 514562306a36Sopenharmony_ci ctrl_buf->large_tx_ipv6 = buf->large_tx_ipv6; 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_ci /* large_rx disabled for now, additional features needed */ 514862306a36Sopenharmony_ci ctrl_buf->large_rx_ipv4 = 0; 514962306a36Sopenharmony_ci ctrl_buf->large_rx_ipv6 = 0; 515062306a36Sopenharmony_ci 515162306a36Sopenharmony_ci if (adapter->state != VNIC_PROBING) { 515262306a36Sopenharmony_ci old_hw_features = adapter->netdev->hw_features; 515362306a36Sopenharmony_ci adapter->netdev->hw_features = 0; 515462306a36Sopenharmony_ci } 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_ci adapter->netdev->hw_features = NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; 515762306a36Sopenharmony_ci 515862306a36Sopenharmony_ci if (buf->tcp_ipv4_chksum || buf->udp_ipv4_chksum) 515962306a36Sopenharmony_ci adapter->netdev->hw_features |= NETIF_F_IP_CSUM; 516062306a36Sopenharmony_ci 516162306a36Sopenharmony_ci if (buf->tcp_ipv6_chksum || buf->udp_ipv6_chksum) 516262306a36Sopenharmony_ci adapter->netdev->hw_features |= NETIF_F_IPV6_CSUM; 516362306a36Sopenharmony_ci 516462306a36Sopenharmony_ci if ((adapter->netdev->features & 516562306a36Sopenharmony_ci (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))) 516662306a36Sopenharmony_ci adapter->netdev->hw_features |= NETIF_F_RXCSUM; 516762306a36Sopenharmony_ci 516862306a36Sopenharmony_ci if (buf->large_tx_ipv4) 516962306a36Sopenharmony_ci adapter->netdev->hw_features |= NETIF_F_TSO; 517062306a36Sopenharmony_ci if (buf->large_tx_ipv6) 517162306a36Sopenharmony_ci adapter->netdev->hw_features |= NETIF_F_TSO6; 517262306a36Sopenharmony_ci 517362306a36Sopenharmony_ci if (adapter->state == VNIC_PROBING) { 517462306a36Sopenharmony_ci adapter->netdev->features |= adapter->netdev->hw_features; 517562306a36Sopenharmony_ci } else if (old_hw_features != adapter->netdev->hw_features) { 517662306a36Sopenharmony_ci netdev_features_t tmp = 0; 517762306a36Sopenharmony_ci 517862306a36Sopenharmony_ci /* disable features no longer supported */ 517962306a36Sopenharmony_ci adapter->netdev->features &= adapter->netdev->hw_features; 518062306a36Sopenharmony_ci /* turn on features now supported if previously enabled */ 518162306a36Sopenharmony_ci tmp = (old_hw_features ^ adapter->netdev->hw_features) & 518262306a36Sopenharmony_ci adapter->netdev->hw_features; 518362306a36Sopenharmony_ci adapter->netdev->features |= 518462306a36Sopenharmony_ci tmp & adapter->netdev->wanted_features; 518562306a36Sopenharmony_ci } 518662306a36Sopenharmony_ci 518762306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 518862306a36Sopenharmony_ci crq.control_ip_offload.first = IBMVNIC_CRQ_CMD; 518962306a36Sopenharmony_ci crq.control_ip_offload.cmd = CONTROL_IP_OFFLOAD; 519062306a36Sopenharmony_ci crq.control_ip_offload.len = 519162306a36Sopenharmony_ci cpu_to_be32(sizeof(adapter->ip_offload_ctrl)); 519262306a36Sopenharmony_ci crq.control_ip_offload.ioba = cpu_to_be32(adapter->ip_offload_ctrl_tok); 519362306a36Sopenharmony_ci ibmvnic_send_crq(adapter, &crq); 519462306a36Sopenharmony_ci} 519562306a36Sopenharmony_ci 519662306a36Sopenharmony_cistatic void handle_vpd_size_rsp(union ibmvnic_crq *crq, 519762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 519862306a36Sopenharmony_ci{ 519962306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 520062306a36Sopenharmony_ci 520162306a36Sopenharmony_ci if (crq->get_vpd_size_rsp.rc.code) { 520262306a36Sopenharmony_ci dev_err(dev, "Error retrieving VPD size, rc=%x\n", 520362306a36Sopenharmony_ci crq->get_vpd_size_rsp.rc.code); 520462306a36Sopenharmony_ci complete(&adapter->fw_done); 520562306a36Sopenharmony_ci return; 520662306a36Sopenharmony_ci } 520762306a36Sopenharmony_ci 520862306a36Sopenharmony_ci adapter->vpd->len = be64_to_cpu(crq->get_vpd_size_rsp.len); 520962306a36Sopenharmony_ci complete(&adapter->fw_done); 521062306a36Sopenharmony_ci} 521162306a36Sopenharmony_ci 521262306a36Sopenharmony_cistatic void handle_vpd_rsp(union ibmvnic_crq *crq, 521362306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 521462306a36Sopenharmony_ci{ 521562306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 521662306a36Sopenharmony_ci unsigned char *substr = NULL; 521762306a36Sopenharmony_ci u8 fw_level_len = 0; 521862306a36Sopenharmony_ci 521962306a36Sopenharmony_ci memset(adapter->fw_version, 0, 32); 522062306a36Sopenharmony_ci 522162306a36Sopenharmony_ci dma_unmap_single(dev, adapter->vpd->dma_addr, adapter->vpd->len, 522262306a36Sopenharmony_ci DMA_FROM_DEVICE); 522362306a36Sopenharmony_ci 522462306a36Sopenharmony_ci if (crq->get_vpd_rsp.rc.code) { 522562306a36Sopenharmony_ci dev_err(dev, "Error retrieving VPD from device, rc=%x\n", 522662306a36Sopenharmony_ci crq->get_vpd_rsp.rc.code); 522762306a36Sopenharmony_ci goto complete; 522862306a36Sopenharmony_ci } 522962306a36Sopenharmony_ci 523062306a36Sopenharmony_ci /* get the position of the firmware version info 523162306a36Sopenharmony_ci * located after the ASCII 'RM' substring in the buffer 523262306a36Sopenharmony_ci */ 523362306a36Sopenharmony_ci substr = strnstr(adapter->vpd->buff, "RM", adapter->vpd->len); 523462306a36Sopenharmony_ci if (!substr) { 523562306a36Sopenharmony_ci dev_info(dev, "Warning - No FW level has been provided in the VPD buffer by the VIOS Server\n"); 523662306a36Sopenharmony_ci goto complete; 523762306a36Sopenharmony_ci } 523862306a36Sopenharmony_ci 523962306a36Sopenharmony_ci /* get length of firmware level ASCII substring */ 524062306a36Sopenharmony_ci if ((substr + 2) < (adapter->vpd->buff + adapter->vpd->len)) { 524162306a36Sopenharmony_ci fw_level_len = *(substr + 2); 524262306a36Sopenharmony_ci } else { 524362306a36Sopenharmony_ci dev_info(dev, "Length of FW substr extrapolated VDP buff\n"); 524462306a36Sopenharmony_ci goto complete; 524562306a36Sopenharmony_ci } 524662306a36Sopenharmony_ci 524762306a36Sopenharmony_ci /* copy firmware version string from vpd into adapter */ 524862306a36Sopenharmony_ci if ((substr + 3 + fw_level_len) < 524962306a36Sopenharmony_ci (adapter->vpd->buff + adapter->vpd->len)) { 525062306a36Sopenharmony_ci strncpy((char *)adapter->fw_version, substr + 3, fw_level_len); 525162306a36Sopenharmony_ci } else { 525262306a36Sopenharmony_ci dev_info(dev, "FW substr extrapolated VPD buff\n"); 525362306a36Sopenharmony_ci } 525462306a36Sopenharmony_ci 525562306a36Sopenharmony_cicomplete: 525662306a36Sopenharmony_ci if (adapter->fw_version[0] == '\0') 525762306a36Sopenharmony_ci strscpy((char *)adapter->fw_version, "N/A", sizeof(adapter->fw_version)); 525862306a36Sopenharmony_ci complete(&adapter->fw_done); 525962306a36Sopenharmony_ci} 526062306a36Sopenharmony_ci 526162306a36Sopenharmony_cistatic void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter) 526262306a36Sopenharmony_ci{ 526362306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 526462306a36Sopenharmony_ci struct ibmvnic_query_ip_offload_buffer *buf = &adapter->ip_offload_buf; 526562306a36Sopenharmony_ci int i; 526662306a36Sopenharmony_ci 526762306a36Sopenharmony_ci dma_unmap_single(dev, adapter->ip_offload_tok, 526862306a36Sopenharmony_ci sizeof(adapter->ip_offload_buf), DMA_FROM_DEVICE); 526962306a36Sopenharmony_ci 527062306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Query IP Offload Buffer:\n"); 527162306a36Sopenharmony_ci for (i = 0; i < (sizeof(adapter->ip_offload_buf) - 1) / 8 + 1; i++) 527262306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "%016lx\n", 527362306a36Sopenharmony_ci ((unsigned long *)(buf))[i]); 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "ipv4_chksum = %d\n", buf->ipv4_chksum); 527662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "ipv6_chksum = %d\n", buf->ipv6_chksum); 527762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "tcp_ipv4_chksum = %d\n", 527862306a36Sopenharmony_ci buf->tcp_ipv4_chksum); 527962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "tcp_ipv6_chksum = %d\n", 528062306a36Sopenharmony_ci buf->tcp_ipv6_chksum); 528162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "udp_ipv4_chksum = %d\n", 528262306a36Sopenharmony_ci buf->udp_ipv4_chksum); 528362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "udp_ipv6_chksum = %d\n", 528462306a36Sopenharmony_ci buf->udp_ipv6_chksum); 528562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "large_tx_ipv4 = %d\n", 528662306a36Sopenharmony_ci buf->large_tx_ipv4); 528762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "large_tx_ipv6 = %d\n", 528862306a36Sopenharmony_ci buf->large_tx_ipv6); 528962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "large_rx_ipv4 = %d\n", 529062306a36Sopenharmony_ci buf->large_rx_ipv4); 529162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "large_rx_ipv6 = %d\n", 529262306a36Sopenharmony_ci buf->large_rx_ipv6); 529362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "max_ipv4_hdr_sz = %d\n", 529462306a36Sopenharmony_ci buf->max_ipv4_header_size); 529562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "max_ipv6_hdr_sz = %d\n", 529662306a36Sopenharmony_ci buf->max_ipv6_header_size); 529762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "max_tcp_hdr_size = %d\n", 529862306a36Sopenharmony_ci buf->max_tcp_header_size); 529962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "max_udp_hdr_size = %d\n", 530062306a36Sopenharmony_ci buf->max_udp_header_size); 530162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "max_large_tx_size = %d\n", 530262306a36Sopenharmony_ci buf->max_large_tx_size); 530362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "max_large_rx_size = %d\n", 530462306a36Sopenharmony_ci buf->max_large_rx_size); 530562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "ipv6_ext_hdr = %d\n", 530662306a36Sopenharmony_ci buf->ipv6_extension_header); 530762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "tcp_pseudosum_req = %d\n", 530862306a36Sopenharmony_ci buf->tcp_pseudosum_req); 530962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "num_ipv6_ext_hd = %d\n", 531062306a36Sopenharmony_ci buf->num_ipv6_ext_headers); 531162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "off_ipv6_ext_hd = %d\n", 531262306a36Sopenharmony_ci buf->off_ipv6_ext_headers); 531362306a36Sopenharmony_ci 531462306a36Sopenharmony_ci send_control_ip_offload(adapter); 531562306a36Sopenharmony_ci} 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_cistatic const char *ibmvnic_fw_err_cause(u16 cause) 531862306a36Sopenharmony_ci{ 531962306a36Sopenharmony_ci switch (cause) { 532062306a36Sopenharmony_ci case ADAPTER_PROBLEM: 532162306a36Sopenharmony_ci return "adapter problem"; 532262306a36Sopenharmony_ci case BUS_PROBLEM: 532362306a36Sopenharmony_ci return "bus problem"; 532462306a36Sopenharmony_ci case FW_PROBLEM: 532562306a36Sopenharmony_ci return "firmware problem"; 532662306a36Sopenharmony_ci case DD_PROBLEM: 532762306a36Sopenharmony_ci return "device driver problem"; 532862306a36Sopenharmony_ci case EEH_RECOVERY: 532962306a36Sopenharmony_ci return "EEH recovery"; 533062306a36Sopenharmony_ci case FW_UPDATED: 533162306a36Sopenharmony_ci return "firmware updated"; 533262306a36Sopenharmony_ci case LOW_MEMORY: 533362306a36Sopenharmony_ci return "low Memory"; 533462306a36Sopenharmony_ci default: 533562306a36Sopenharmony_ci return "unknown"; 533662306a36Sopenharmony_ci } 533762306a36Sopenharmony_ci} 533862306a36Sopenharmony_ci 533962306a36Sopenharmony_cistatic void handle_error_indication(union ibmvnic_crq *crq, 534062306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 534162306a36Sopenharmony_ci{ 534262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 534362306a36Sopenharmony_ci u16 cause; 534462306a36Sopenharmony_ci 534562306a36Sopenharmony_ci cause = be16_to_cpu(crq->error_indication.error_cause); 534662306a36Sopenharmony_ci 534762306a36Sopenharmony_ci dev_warn_ratelimited(dev, 534862306a36Sopenharmony_ci "Firmware reports %serror, cause: %s. Starting recovery...\n", 534962306a36Sopenharmony_ci crq->error_indication.flags 535062306a36Sopenharmony_ci & IBMVNIC_FATAL_ERROR ? "FATAL " : "", 535162306a36Sopenharmony_ci ibmvnic_fw_err_cause(cause)); 535262306a36Sopenharmony_ci 535362306a36Sopenharmony_ci if (crq->error_indication.flags & IBMVNIC_FATAL_ERROR) 535462306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_FATAL); 535562306a36Sopenharmony_ci else 535662306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_NON_FATAL); 535762306a36Sopenharmony_ci} 535862306a36Sopenharmony_ci 535962306a36Sopenharmony_cistatic int handle_change_mac_rsp(union ibmvnic_crq *crq, 536062306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 536162306a36Sopenharmony_ci{ 536262306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 536362306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 536462306a36Sopenharmony_ci long rc; 536562306a36Sopenharmony_ci 536662306a36Sopenharmony_ci rc = crq->change_mac_addr_rsp.rc.code; 536762306a36Sopenharmony_ci if (rc) { 536862306a36Sopenharmony_ci dev_err(dev, "Error %ld in CHANGE_MAC_ADDR_RSP\n", rc); 536962306a36Sopenharmony_ci goto out; 537062306a36Sopenharmony_ci } 537162306a36Sopenharmony_ci /* crq->change_mac_addr.mac_addr is the requested one 537262306a36Sopenharmony_ci * crq->change_mac_addr_rsp.mac_addr is the returned valid one. 537362306a36Sopenharmony_ci */ 537462306a36Sopenharmony_ci eth_hw_addr_set(netdev, &crq->change_mac_addr_rsp.mac_addr[0]); 537562306a36Sopenharmony_ci ether_addr_copy(adapter->mac_addr, 537662306a36Sopenharmony_ci &crq->change_mac_addr_rsp.mac_addr[0]); 537762306a36Sopenharmony_ciout: 537862306a36Sopenharmony_ci complete(&adapter->fw_done); 537962306a36Sopenharmony_ci return rc; 538062306a36Sopenharmony_ci} 538162306a36Sopenharmony_ci 538262306a36Sopenharmony_cistatic void handle_request_cap_rsp(union ibmvnic_crq *crq, 538362306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 538462306a36Sopenharmony_ci{ 538562306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 538662306a36Sopenharmony_ci u64 *req_value; 538762306a36Sopenharmony_ci char *name; 538862306a36Sopenharmony_ci 538962306a36Sopenharmony_ci atomic_dec(&adapter->running_cap_crqs); 539062306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Outstanding request-caps: %d\n", 539162306a36Sopenharmony_ci atomic_read(&adapter->running_cap_crqs)); 539262306a36Sopenharmony_ci switch (be16_to_cpu(crq->request_capability_rsp.capability)) { 539362306a36Sopenharmony_ci case REQ_TX_QUEUES: 539462306a36Sopenharmony_ci req_value = &adapter->req_tx_queues; 539562306a36Sopenharmony_ci name = "tx"; 539662306a36Sopenharmony_ci break; 539762306a36Sopenharmony_ci case REQ_RX_QUEUES: 539862306a36Sopenharmony_ci req_value = &adapter->req_rx_queues; 539962306a36Sopenharmony_ci name = "rx"; 540062306a36Sopenharmony_ci break; 540162306a36Sopenharmony_ci case REQ_RX_ADD_QUEUES: 540262306a36Sopenharmony_ci req_value = &adapter->req_rx_add_queues; 540362306a36Sopenharmony_ci name = "rx_add"; 540462306a36Sopenharmony_ci break; 540562306a36Sopenharmony_ci case REQ_TX_ENTRIES_PER_SUBCRQ: 540662306a36Sopenharmony_ci req_value = &adapter->req_tx_entries_per_subcrq; 540762306a36Sopenharmony_ci name = "tx_entries_per_subcrq"; 540862306a36Sopenharmony_ci break; 540962306a36Sopenharmony_ci case REQ_RX_ADD_ENTRIES_PER_SUBCRQ: 541062306a36Sopenharmony_ci req_value = &adapter->req_rx_add_entries_per_subcrq; 541162306a36Sopenharmony_ci name = "rx_add_entries_per_subcrq"; 541262306a36Sopenharmony_ci break; 541362306a36Sopenharmony_ci case REQ_MTU: 541462306a36Sopenharmony_ci req_value = &adapter->req_mtu; 541562306a36Sopenharmony_ci name = "mtu"; 541662306a36Sopenharmony_ci break; 541762306a36Sopenharmony_ci case PROMISC_REQUESTED: 541862306a36Sopenharmony_ci req_value = &adapter->promisc; 541962306a36Sopenharmony_ci name = "promisc"; 542062306a36Sopenharmony_ci break; 542162306a36Sopenharmony_ci default: 542262306a36Sopenharmony_ci dev_err(dev, "Got invalid cap request rsp %d\n", 542362306a36Sopenharmony_ci crq->request_capability.capability); 542462306a36Sopenharmony_ci return; 542562306a36Sopenharmony_ci } 542662306a36Sopenharmony_ci 542762306a36Sopenharmony_ci switch (crq->request_capability_rsp.rc.code) { 542862306a36Sopenharmony_ci case SUCCESS: 542962306a36Sopenharmony_ci break; 543062306a36Sopenharmony_ci case PARTIALSUCCESS: 543162306a36Sopenharmony_ci dev_info(dev, "req=%lld, rsp=%ld in %s queue, retrying.\n", 543262306a36Sopenharmony_ci *req_value, 543362306a36Sopenharmony_ci (long)be64_to_cpu(crq->request_capability_rsp.number), 543462306a36Sopenharmony_ci name); 543562306a36Sopenharmony_ci 543662306a36Sopenharmony_ci if (be16_to_cpu(crq->request_capability_rsp.capability) == 543762306a36Sopenharmony_ci REQ_MTU) { 543862306a36Sopenharmony_ci pr_err("mtu of %llu is not supported. Reverting.\n", 543962306a36Sopenharmony_ci *req_value); 544062306a36Sopenharmony_ci *req_value = adapter->fallback.mtu; 544162306a36Sopenharmony_ci } else { 544262306a36Sopenharmony_ci *req_value = 544362306a36Sopenharmony_ci be64_to_cpu(crq->request_capability_rsp.number); 544462306a36Sopenharmony_ci } 544562306a36Sopenharmony_ci 544662306a36Sopenharmony_ci send_request_cap(adapter, 1); 544762306a36Sopenharmony_ci return; 544862306a36Sopenharmony_ci default: 544962306a36Sopenharmony_ci dev_err(dev, "Error %d in request cap rsp\n", 545062306a36Sopenharmony_ci crq->request_capability_rsp.rc.code); 545162306a36Sopenharmony_ci return; 545262306a36Sopenharmony_ci } 545362306a36Sopenharmony_ci 545462306a36Sopenharmony_ci /* Done receiving requested capabilities, query IP offload support */ 545562306a36Sopenharmony_ci if (atomic_read(&adapter->running_cap_crqs) == 0) 545662306a36Sopenharmony_ci send_query_ip_offload(adapter); 545762306a36Sopenharmony_ci} 545862306a36Sopenharmony_ci 545962306a36Sopenharmony_cistatic int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, 546062306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 546162306a36Sopenharmony_ci{ 546262306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 546362306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 546462306a36Sopenharmony_ci struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf; 546562306a36Sopenharmony_ci struct ibmvnic_login_buffer *login = adapter->login_buf; 546662306a36Sopenharmony_ci u64 *tx_handle_array; 546762306a36Sopenharmony_ci u64 *rx_handle_array; 546862306a36Sopenharmony_ci int num_tx_pools; 546962306a36Sopenharmony_ci int num_rx_pools; 547062306a36Sopenharmony_ci u64 *size_array; 547162306a36Sopenharmony_ci u32 rsp_len; 547262306a36Sopenharmony_ci int i; 547362306a36Sopenharmony_ci 547462306a36Sopenharmony_ci /* CHECK: Test/set of login_pending does not need to be atomic 547562306a36Sopenharmony_ci * because only ibmvnic_tasklet tests/clears this. 547662306a36Sopenharmony_ci */ 547762306a36Sopenharmony_ci if (!adapter->login_pending) { 547862306a36Sopenharmony_ci netdev_warn(netdev, "Ignoring unexpected login response\n"); 547962306a36Sopenharmony_ci return 0; 548062306a36Sopenharmony_ci } 548162306a36Sopenharmony_ci adapter->login_pending = false; 548262306a36Sopenharmony_ci 548362306a36Sopenharmony_ci /* If the number of queues requested can't be allocated by the 548462306a36Sopenharmony_ci * server, the login response will return with code 1. We will need 548562306a36Sopenharmony_ci * to resend the login buffer with fewer queues requested. 548662306a36Sopenharmony_ci */ 548762306a36Sopenharmony_ci if (login_rsp_crq->generic.rc.code) { 548862306a36Sopenharmony_ci adapter->init_done_rc = login_rsp_crq->generic.rc.code; 548962306a36Sopenharmony_ci complete(&adapter->init_done); 549062306a36Sopenharmony_ci return 0; 549162306a36Sopenharmony_ci } 549262306a36Sopenharmony_ci 549362306a36Sopenharmony_ci if (adapter->failover_pending) { 549462306a36Sopenharmony_ci adapter->init_done_rc = -EAGAIN; 549562306a36Sopenharmony_ci netdev_dbg(netdev, "Failover pending, ignoring login response\n"); 549662306a36Sopenharmony_ci complete(&adapter->init_done); 549762306a36Sopenharmony_ci /* login response buffer will be released on reset */ 549862306a36Sopenharmony_ci return 0; 549962306a36Sopenharmony_ci } 550062306a36Sopenharmony_ci 550162306a36Sopenharmony_ci netdev->mtu = adapter->req_mtu - ETH_HLEN; 550262306a36Sopenharmony_ci 550362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Login Response Buffer:\n"); 550462306a36Sopenharmony_ci for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) { 550562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "%016lx\n", 550662306a36Sopenharmony_ci ((unsigned long *)(adapter->login_rsp_buf))[i]); 550762306a36Sopenharmony_ci } 550862306a36Sopenharmony_ci 550962306a36Sopenharmony_ci /* Sanity checks */ 551062306a36Sopenharmony_ci if (login->num_txcomp_subcrqs != login_rsp->num_txsubm_subcrqs || 551162306a36Sopenharmony_ci (be32_to_cpu(login->num_rxcomp_subcrqs) * 551262306a36Sopenharmony_ci adapter->req_rx_add_queues != 551362306a36Sopenharmony_ci be32_to_cpu(login_rsp->num_rxadd_subcrqs))) { 551462306a36Sopenharmony_ci dev_err(dev, "FATAL: Inconsistent login and login rsp\n"); 551562306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_FATAL); 551662306a36Sopenharmony_ci return -EIO; 551762306a36Sopenharmony_ci } 551862306a36Sopenharmony_ci 551962306a36Sopenharmony_ci rsp_len = be32_to_cpu(login_rsp->len); 552062306a36Sopenharmony_ci if (be32_to_cpu(login->login_rsp_len) < rsp_len || 552162306a36Sopenharmony_ci rsp_len <= be32_to_cpu(login_rsp->off_txsubm_subcrqs) || 552262306a36Sopenharmony_ci rsp_len <= be32_to_cpu(login_rsp->off_rxadd_subcrqs) || 552362306a36Sopenharmony_ci rsp_len <= be32_to_cpu(login_rsp->off_rxadd_buff_size) || 552462306a36Sopenharmony_ci rsp_len <= be32_to_cpu(login_rsp->off_supp_tx_desc)) { 552562306a36Sopenharmony_ci /* This can happen if a login request times out and there are 552662306a36Sopenharmony_ci * 2 outstanding login requests sent, the LOGIN_RSP crq 552762306a36Sopenharmony_ci * could have been for the older login request. So we are 552862306a36Sopenharmony_ci * parsing the newer response buffer which may be incomplete 552962306a36Sopenharmony_ci */ 553062306a36Sopenharmony_ci dev_err(dev, "FATAL: Login rsp offsets/lengths invalid\n"); 553162306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_FATAL); 553262306a36Sopenharmony_ci return -EIO; 553362306a36Sopenharmony_ci } 553462306a36Sopenharmony_ci 553562306a36Sopenharmony_ci size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + 553662306a36Sopenharmony_ci be32_to_cpu(adapter->login_rsp_buf->off_rxadd_buff_size)); 553762306a36Sopenharmony_ci /* variable buffer sizes are not supported, so just read the 553862306a36Sopenharmony_ci * first entry. 553962306a36Sopenharmony_ci */ 554062306a36Sopenharmony_ci adapter->cur_rx_buf_sz = be64_to_cpu(size_array[0]); 554162306a36Sopenharmony_ci 554262306a36Sopenharmony_ci num_tx_pools = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); 554362306a36Sopenharmony_ci num_rx_pools = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); 554462306a36Sopenharmony_ci 554562306a36Sopenharmony_ci tx_handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + 554662306a36Sopenharmony_ci be32_to_cpu(adapter->login_rsp_buf->off_txsubm_subcrqs)); 554762306a36Sopenharmony_ci rx_handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + 554862306a36Sopenharmony_ci be32_to_cpu(adapter->login_rsp_buf->off_rxadd_subcrqs)); 554962306a36Sopenharmony_ci 555062306a36Sopenharmony_ci for (i = 0; i < num_tx_pools; i++) 555162306a36Sopenharmony_ci adapter->tx_scrq[i]->handle = tx_handle_array[i]; 555262306a36Sopenharmony_ci 555362306a36Sopenharmony_ci for (i = 0; i < num_rx_pools; i++) 555462306a36Sopenharmony_ci adapter->rx_scrq[i]->handle = rx_handle_array[i]; 555562306a36Sopenharmony_ci 555662306a36Sopenharmony_ci adapter->num_active_tx_scrqs = num_tx_pools; 555762306a36Sopenharmony_ci adapter->num_active_rx_scrqs = num_rx_pools; 555862306a36Sopenharmony_ci release_login_rsp_buffer(adapter); 555962306a36Sopenharmony_ci release_login_buffer(adapter); 556062306a36Sopenharmony_ci complete(&adapter->init_done); 556162306a36Sopenharmony_ci 556262306a36Sopenharmony_ci return 0; 556362306a36Sopenharmony_ci} 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_cistatic void handle_request_unmap_rsp(union ibmvnic_crq *crq, 556662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 556762306a36Sopenharmony_ci{ 556862306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 556962306a36Sopenharmony_ci long rc; 557062306a36Sopenharmony_ci 557162306a36Sopenharmony_ci rc = crq->request_unmap_rsp.rc.code; 557262306a36Sopenharmony_ci if (rc) 557362306a36Sopenharmony_ci dev_err(dev, "Error %ld in REQUEST_UNMAP_RSP\n", rc); 557462306a36Sopenharmony_ci} 557562306a36Sopenharmony_ci 557662306a36Sopenharmony_cistatic void handle_query_map_rsp(union ibmvnic_crq *crq, 557762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 557862306a36Sopenharmony_ci{ 557962306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 558062306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 558162306a36Sopenharmony_ci long rc; 558262306a36Sopenharmony_ci 558362306a36Sopenharmony_ci rc = crq->query_map_rsp.rc.code; 558462306a36Sopenharmony_ci if (rc) { 558562306a36Sopenharmony_ci dev_err(dev, "Error %ld in QUERY_MAP_RSP\n", rc); 558662306a36Sopenharmony_ci return; 558762306a36Sopenharmony_ci } 558862306a36Sopenharmony_ci netdev_dbg(netdev, "page_size = %d\ntot_pages = %u\nfree_pages = %u\n", 558962306a36Sopenharmony_ci crq->query_map_rsp.page_size, 559062306a36Sopenharmony_ci __be32_to_cpu(crq->query_map_rsp.tot_pages), 559162306a36Sopenharmony_ci __be32_to_cpu(crq->query_map_rsp.free_pages)); 559262306a36Sopenharmony_ci} 559362306a36Sopenharmony_ci 559462306a36Sopenharmony_cistatic void handle_query_cap_rsp(union ibmvnic_crq *crq, 559562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 559662306a36Sopenharmony_ci{ 559762306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 559862306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 559962306a36Sopenharmony_ci long rc; 560062306a36Sopenharmony_ci 560162306a36Sopenharmony_ci atomic_dec(&adapter->running_cap_crqs); 560262306a36Sopenharmony_ci netdev_dbg(netdev, "Outstanding queries: %d\n", 560362306a36Sopenharmony_ci atomic_read(&adapter->running_cap_crqs)); 560462306a36Sopenharmony_ci rc = crq->query_capability.rc.code; 560562306a36Sopenharmony_ci if (rc) { 560662306a36Sopenharmony_ci dev_err(dev, "Error %ld in QUERY_CAP_RSP\n", rc); 560762306a36Sopenharmony_ci goto out; 560862306a36Sopenharmony_ci } 560962306a36Sopenharmony_ci 561062306a36Sopenharmony_ci switch (be16_to_cpu(crq->query_capability.capability)) { 561162306a36Sopenharmony_ci case MIN_TX_QUEUES: 561262306a36Sopenharmony_ci adapter->min_tx_queues = 561362306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 561462306a36Sopenharmony_ci netdev_dbg(netdev, "min_tx_queues = %lld\n", 561562306a36Sopenharmony_ci adapter->min_tx_queues); 561662306a36Sopenharmony_ci break; 561762306a36Sopenharmony_ci case MIN_RX_QUEUES: 561862306a36Sopenharmony_ci adapter->min_rx_queues = 561962306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 562062306a36Sopenharmony_ci netdev_dbg(netdev, "min_rx_queues = %lld\n", 562162306a36Sopenharmony_ci adapter->min_rx_queues); 562262306a36Sopenharmony_ci break; 562362306a36Sopenharmony_ci case MIN_RX_ADD_QUEUES: 562462306a36Sopenharmony_ci adapter->min_rx_add_queues = 562562306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 562662306a36Sopenharmony_ci netdev_dbg(netdev, "min_rx_add_queues = %lld\n", 562762306a36Sopenharmony_ci adapter->min_rx_add_queues); 562862306a36Sopenharmony_ci break; 562962306a36Sopenharmony_ci case MAX_TX_QUEUES: 563062306a36Sopenharmony_ci adapter->max_tx_queues = 563162306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 563262306a36Sopenharmony_ci netdev_dbg(netdev, "max_tx_queues = %lld\n", 563362306a36Sopenharmony_ci adapter->max_tx_queues); 563462306a36Sopenharmony_ci break; 563562306a36Sopenharmony_ci case MAX_RX_QUEUES: 563662306a36Sopenharmony_ci adapter->max_rx_queues = 563762306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 563862306a36Sopenharmony_ci netdev_dbg(netdev, "max_rx_queues = %lld\n", 563962306a36Sopenharmony_ci adapter->max_rx_queues); 564062306a36Sopenharmony_ci break; 564162306a36Sopenharmony_ci case MAX_RX_ADD_QUEUES: 564262306a36Sopenharmony_ci adapter->max_rx_add_queues = 564362306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 564462306a36Sopenharmony_ci netdev_dbg(netdev, "max_rx_add_queues = %lld\n", 564562306a36Sopenharmony_ci adapter->max_rx_add_queues); 564662306a36Sopenharmony_ci break; 564762306a36Sopenharmony_ci case MIN_TX_ENTRIES_PER_SUBCRQ: 564862306a36Sopenharmony_ci adapter->min_tx_entries_per_subcrq = 564962306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 565062306a36Sopenharmony_ci netdev_dbg(netdev, "min_tx_entries_per_subcrq = %lld\n", 565162306a36Sopenharmony_ci adapter->min_tx_entries_per_subcrq); 565262306a36Sopenharmony_ci break; 565362306a36Sopenharmony_ci case MIN_RX_ADD_ENTRIES_PER_SUBCRQ: 565462306a36Sopenharmony_ci adapter->min_rx_add_entries_per_subcrq = 565562306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 565662306a36Sopenharmony_ci netdev_dbg(netdev, "min_rx_add_entrs_per_subcrq = %lld\n", 565762306a36Sopenharmony_ci adapter->min_rx_add_entries_per_subcrq); 565862306a36Sopenharmony_ci break; 565962306a36Sopenharmony_ci case MAX_TX_ENTRIES_PER_SUBCRQ: 566062306a36Sopenharmony_ci adapter->max_tx_entries_per_subcrq = 566162306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 566262306a36Sopenharmony_ci netdev_dbg(netdev, "max_tx_entries_per_subcrq = %lld\n", 566362306a36Sopenharmony_ci adapter->max_tx_entries_per_subcrq); 566462306a36Sopenharmony_ci break; 566562306a36Sopenharmony_ci case MAX_RX_ADD_ENTRIES_PER_SUBCRQ: 566662306a36Sopenharmony_ci adapter->max_rx_add_entries_per_subcrq = 566762306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 566862306a36Sopenharmony_ci netdev_dbg(netdev, "max_rx_add_entrs_per_subcrq = %lld\n", 566962306a36Sopenharmony_ci adapter->max_rx_add_entries_per_subcrq); 567062306a36Sopenharmony_ci break; 567162306a36Sopenharmony_ci case TCP_IP_OFFLOAD: 567262306a36Sopenharmony_ci adapter->tcp_ip_offload = 567362306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 567462306a36Sopenharmony_ci netdev_dbg(netdev, "tcp_ip_offload = %lld\n", 567562306a36Sopenharmony_ci adapter->tcp_ip_offload); 567662306a36Sopenharmony_ci break; 567762306a36Sopenharmony_ci case PROMISC_SUPPORTED: 567862306a36Sopenharmony_ci adapter->promisc_supported = 567962306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 568062306a36Sopenharmony_ci netdev_dbg(netdev, "promisc_supported = %lld\n", 568162306a36Sopenharmony_ci adapter->promisc_supported); 568262306a36Sopenharmony_ci break; 568362306a36Sopenharmony_ci case MIN_MTU: 568462306a36Sopenharmony_ci adapter->min_mtu = be64_to_cpu(crq->query_capability.number); 568562306a36Sopenharmony_ci netdev->min_mtu = adapter->min_mtu - ETH_HLEN; 568662306a36Sopenharmony_ci netdev_dbg(netdev, "min_mtu = %lld\n", adapter->min_mtu); 568762306a36Sopenharmony_ci break; 568862306a36Sopenharmony_ci case MAX_MTU: 568962306a36Sopenharmony_ci adapter->max_mtu = be64_to_cpu(crq->query_capability.number); 569062306a36Sopenharmony_ci netdev->max_mtu = adapter->max_mtu - ETH_HLEN; 569162306a36Sopenharmony_ci netdev_dbg(netdev, "max_mtu = %lld\n", adapter->max_mtu); 569262306a36Sopenharmony_ci break; 569362306a36Sopenharmony_ci case MAX_MULTICAST_FILTERS: 569462306a36Sopenharmony_ci adapter->max_multicast_filters = 569562306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 569662306a36Sopenharmony_ci netdev_dbg(netdev, "max_multicast_filters = %lld\n", 569762306a36Sopenharmony_ci adapter->max_multicast_filters); 569862306a36Sopenharmony_ci break; 569962306a36Sopenharmony_ci case VLAN_HEADER_INSERTION: 570062306a36Sopenharmony_ci adapter->vlan_header_insertion = 570162306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 570262306a36Sopenharmony_ci if (adapter->vlan_header_insertion) 570362306a36Sopenharmony_ci netdev->features |= NETIF_F_HW_VLAN_STAG_TX; 570462306a36Sopenharmony_ci netdev_dbg(netdev, "vlan_header_insertion = %lld\n", 570562306a36Sopenharmony_ci adapter->vlan_header_insertion); 570662306a36Sopenharmony_ci break; 570762306a36Sopenharmony_ci case RX_VLAN_HEADER_INSERTION: 570862306a36Sopenharmony_ci adapter->rx_vlan_header_insertion = 570962306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 571062306a36Sopenharmony_ci netdev_dbg(netdev, "rx_vlan_header_insertion = %lld\n", 571162306a36Sopenharmony_ci adapter->rx_vlan_header_insertion); 571262306a36Sopenharmony_ci break; 571362306a36Sopenharmony_ci case MAX_TX_SG_ENTRIES: 571462306a36Sopenharmony_ci adapter->max_tx_sg_entries = 571562306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 571662306a36Sopenharmony_ci netdev_dbg(netdev, "max_tx_sg_entries = %lld\n", 571762306a36Sopenharmony_ci adapter->max_tx_sg_entries); 571862306a36Sopenharmony_ci break; 571962306a36Sopenharmony_ci case RX_SG_SUPPORTED: 572062306a36Sopenharmony_ci adapter->rx_sg_supported = 572162306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 572262306a36Sopenharmony_ci netdev_dbg(netdev, "rx_sg_supported = %lld\n", 572362306a36Sopenharmony_ci adapter->rx_sg_supported); 572462306a36Sopenharmony_ci break; 572562306a36Sopenharmony_ci case OPT_TX_COMP_SUB_QUEUES: 572662306a36Sopenharmony_ci adapter->opt_tx_comp_sub_queues = 572762306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 572862306a36Sopenharmony_ci netdev_dbg(netdev, "opt_tx_comp_sub_queues = %lld\n", 572962306a36Sopenharmony_ci adapter->opt_tx_comp_sub_queues); 573062306a36Sopenharmony_ci break; 573162306a36Sopenharmony_ci case OPT_RX_COMP_QUEUES: 573262306a36Sopenharmony_ci adapter->opt_rx_comp_queues = 573362306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 573462306a36Sopenharmony_ci netdev_dbg(netdev, "opt_rx_comp_queues = %lld\n", 573562306a36Sopenharmony_ci adapter->opt_rx_comp_queues); 573662306a36Sopenharmony_ci break; 573762306a36Sopenharmony_ci case OPT_RX_BUFADD_Q_PER_RX_COMP_Q: 573862306a36Sopenharmony_ci adapter->opt_rx_bufadd_q_per_rx_comp_q = 573962306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 574062306a36Sopenharmony_ci netdev_dbg(netdev, "opt_rx_bufadd_q_per_rx_comp_q = %lld\n", 574162306a36Sopenharmony_ci adapter->opt_rx_bufadd_q_per_rx_comp_q); 574262306a36Sopenharmony_ci break; 574362306a36Sopenharmony_ci case OPT_TX_ENTRIES_PER_SUBCRQ: 574462306a36Sopenharmony_ci adapter->opt_tx_entries_per_subcrq = 574562306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 574662306a36Sopenharmony_ci netdev_dbg(netdev, "opt_tx_entries_per_subcrq = %lld\n", 574762306a36Sopenharmony_ci adapter->opt_tx_entries_per_subcrq); 574862306a36Sopenharmony_ci break; 574962306a36Sopenharmony_ci case OPT_RXBA_ENTRIES_PER_SUBCRQ: 575062306a36Sopenharmony_ci adapter->opt_rxba_entries_per_subcrq = 575162306a36Sopenharmony_ci be64_to_cpu(crq->query_capability.number); 575262306a36Sopenharmony_ci netdev_dbg(netdev, "opt_rxba_entries_per_subcrq = %lld\n", 575362306a36Sopenharmony_ci adapter->opt_rxba_entries_per_subcrq); 575462306a36Sopenharmony_ci break; 575562306a36Sopenharmony_ci case TX_RX_DESC_REQ: 575662306a36Sopenharmony_ci adapter->tx_rx_desc_req = crq->query_capability.number; 575762306a36Sopenharmony_ci netdev_dbg(netdev, "tx_rx_desc_req = %llx\n", 575862306a36Sopenharmony_ci adapter->tx_rx_desc_req); 575962306a36Sopenharmony_ci break; 576062306a36Sopenharmony_ci 576162306a36Sopenharmony_ci default: 576262306a36Sopenharmony_ci netdev_err(netdev, "Got invalid cap rsp %d\n", 576362306a36Sopenharmony_ci crq->query_capability.capability); 576462306a36Sopenharmony_ci } 576562306a36Sopenharmony_ci 576662306a36Sopenharmony_ciout: 576762306a36Sopenharmony_ci if (atomic_read(&adapter->running_cap_crqs) == 0) 576862306a36Sopenharmony_ci send_request_cap(adapter, 0); 576962306a36Sopenharmony_ci} 577062306a36Sopenharmony_ci 577162306a36Sopenharmony_cistatic int send_query_phys_parms(struct ibmvnic_adapter *adapter) 577262306a36Sopenharmony_ci{ 577362306a36Sopenharmony_ci union ibmvnic_crq crq; 577462306a36Sopenharmony_ci int rc; 577562306a36Sopenharmony_ci 577662306a36Sopenharmony_ci memset(&crq, 0, sizeof(crq)); 577762306a36Sopenharmony_ci crq.query_phys_parms.first = IBMVNIC_CRQ_CMD; 577862306a36Sopenharmony_ci crq.query_phys_parms.cmd = QUERY_PHYS_PARMS; 577962306a36Sopenharmony_ci 578062306a36Sopenharmony_ci mutex_lock(&adapter->fw_lock); 578162306a36Sopenharmony_ci adapter->fw_done_rc = 0; 578262306a36Sopenharmony_ci reinit_completion(&adapter->fw_done); 578362306a36Sopenharmony_ci 578462306a36Sopenharmony_ci rc = ibmvnic_send_crq(adapter, &crq); 578562306a36Sopenharmony_ci if (rc) { 578662306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 578762306a36Sopenharmony_ci return rc; 578862306a36Sopenharmony_ci } 578962306a36Sopenharmony_ci 579062306a36Sopenharmony_ci rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); 579162306a36Sopenharmony_ci if (rc) { 579262306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 579362306a36Sopenharmony_ci return rc; 579462306a36Sopenharmony_ci } 579562306a36Sopenharmony_ci 579662306a36Sopenharmony_ci mutex_unlock(&adapter->fw_lock); 579762306a36Sopenharmony_ci return adapter->fw_done_rc ? -EIO : 0; 579862306a36Sopenharmony_ci} 579962306a36Sopenharmony_ci 580062306a36Sopenharmony_cistatic int handle_query_phys_parms_rsp(union ibmvnic_crq *crq, 580162306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 580262306a36Sopenharmony_ci{ 580362306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 580462306a36Sopenharmony_ci int rc; 580562306a36Sopenharmony_ci __be32 rspeed = cpu_to_be32(crq->query_phys_parms_rsp.speed); 580662306a36Sopenharmony_ci 580762306a36Sopenharmony_ci rc = crq->query_phys_parms_rsp.rc.code; 580862306a36Sopenharmony_ci if (rc) { 580962306a36Sopenharmony_ci netdev_err(netdev, "Error %d in QUERY_PHYS_PARMS\n", rc); 581062306a36Sopenharmony_ci return rc; 581162306a36Sopenharmony_ci } 581262306a36Sopenharmony_ci switch (rspeed) { 581362306a36Sopenharmony_ci case IBMVNIC_10MBPS: 581462306a36Sopenharmony_ci adapter->speed = SPEED_10; 581562306a36Sopenharmony_ci break; 581662306a36Sopenharmony_ci case IBMVNIC_100MBPS: 581762306a36Sopenharmony_ci adapter->speed = SPEED_100; 581862306a36Sopenharmony_ci break; 581962306a36Sopenharmony_ci case IBMVNIC_1GBPS: 582062306a36Sopenharmony_ci adapter->speed = SPEED_1000; 582162306a36Sopenharmony_ci break; 582262306a36Sopenharmony_ci case IBMVNIC_10GBPS: 582362306a36Sopenharmony_ci adapter->speed = SPEED_10000; 582462306a36Sopenharmony_ci break; 582562306a36Sopenharmony_ci case IBMVNIC_25GBPS: 582662306a36Sopenharmony_ci adapter->speed = SPEED_25000; 582762306a36Sopenharmony_ci break; 582862306a36Sopenharmony_ci case IBMVNIC_40GBPS: 582962306a36Sopenharmony_ci adapter->speed = SPEED_40000; 583062306a36Sopenharmony_ci break; 583162306a36Sopenharmony_ci case IBMVNIC_50GBPS: 583262306a36Sopenharmony_ci adapter->speed = SPEED_50000; 583362306a36Sopenharmony_ci break; 583462306a36Sopenharmony_ci case IBMVNIC_100GBPS: 583562306a36Sopenharmony_ci adapter->speed = SPEED_100000; 583662306a36Sopenharmony_ci break; 583762306a36Sopenharmony_ci case IBMVNIC_200GBPS: 583862306a36Sopenharmony_ci adapter->speed = SPEED_200000; 583962306a36Sopenharmony_ci break; 584062306a36Sopenharmony_ci default: 584162306a36Sopenharmony_ci if (netif_carrier_ok(netdev)) 584262306a36Sopenharmony_ci netdev_warn(netdev, "Unknown speed 0x%08x\n", rspeed); 584362306a36Sopenharmony_ci adapter->speed = SPEED_UNKNOWN; 584462306a36Sopenharmony_ci } 584562306a36Sopenharmony_ci if (crq->query_phys_parms_rsp.flags1 & IBMVNIC_FULL_DUPLEX) 584662306a36Sopenharmony_ci adapter->duplex = DUPLEX_FULL; 584762306a36Sopenharmony_ci else if (crq->query_phys_parms_rsp.flags1 & IBMVNIC_HALF_DUPLEX) 584862306a36Sopenharmony_ci adapter->duplex = DUPLEX_HALF; 584962306a36Sopenharmony_ci else 585062306a36Sopenharmony_ci adapter->duplex = DUPLEX_UNKNOWN; 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_ci return rc; 585362306a36Sopenharmony_ci} 585462306a36Sopenharmony_ci 585562306a36Sopenharmony_cistatic void ibmvnic_handle_crq(union ibmvnic_crq *crq, 585662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter) 585762306a36Sopenharmony_ci{ 585862306a36Sopenharmony_ci struct ibmvnic_generic_crq *gen_crq = &crq->generic; 585962306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 586062306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 586162306a36Sopenharmony_ci u64 *u64_crq = (u64 *)crq; 586262306a36Sopenharmony_ci long rc; 586362306a36Sopenharmony_ci 586462306a36Sopenharmony_ci netdev_dbg(netdev, "Handling CRQ: %016lx %016lx\n", 586562306a36Sopenharmony_ci (unsigned long)cpu_to_be64(u64_crq[0]), 586662306a36Sopenharmony_ci (unsigned long)cpu_to_be64(u64_crq[1])); 586762306a36Sopenharmony_ci switch (gen_crq->first) { 586862306a36Sopenharmony_ci case IBMVNIC_CRQ_INIT_RSP: 586962306a36Sopenharmony_ci switch (gen_crq->cmd) { 587062306a36Sopenharmony_ci case IBMVNIC_CRQ_INIT: 587162306a36Sopenharmony_ci dev_info(dev, "Partner initialized\n"); 587262306a36Sopenharmony_ci adapter->from_passive_init = true; 587362306a36Sopenharmony_ci /* Discard any stale login responses from prev reset. 587462306a36Sopenharmony_ci * CHECK: should we clear even on INIT_COMPLETE? 587562306a36Sopenharmony_ci */ 587662306a36Sopenharmony_ci adapter->login_pending = false; 587762306a36Sopenharmony_ci 587862306a36Sopenharmony_ci if (adapter->state == VNIC_DOWN) 587962306a36Sopenharmony_ci rc = ibmvnic_reset(adapter, VNIC_RESET_PASSIVE_INIT); 588062306a36Sopenharmony_ci else 588162306a36Sopenharmony_ci rc = ibmvnic_reset(adapter, VNIC_RESET_FAILOVER); 588262306a36Sopenharmony_ci 588362306a36Sopenharmony_ci if (rc && rc != -EBUSY) { 588462306a36Sopenharmony_ci /* We were unable to schedule the failover 588562306a36Sopenharmony_ci * reset either because the adapter was still 588662306a36Sopenharmony_ci * probing (eg: during kexec) or we could not 588762306a36Sopenharmony_ci * allocate memory. Clear the failover_pending 588862306a36Sopenharmony_ci * flag since no one else will. We ignore 588962306a36Sopenharmony_ci * EBUSY because it means either FAILOVER reset 589062306a36Sopenharmony_ci * is already scheduled or the adapter is 589162306a36Sopenharmony_ci * being removed. 589262306a36Sopenharmony_ci */ 589362306a36Sopenharmony_ci netdev_err(netdev, 589462306a36Sopenharmony_ci "Error %ld scheduling failover reset\n", 589562306a36Sopenharmony_ci rc); 589662306a36Sopenharmony_ci adapter->failover_pending = false; 589762306a36Sopenharmony_ci } 589862306a36Sopenharmony_ci 589962306a36Sopenharmony_ci if (!completion_done(&adapter->init_done)) { 590062306a36Sopenharmony_ci if (!adapter->init_done_rc) 590162306a36Sopenharmony_ci adapter->init_done_rc = -EAGAIN; 590262306a36Sopenharmony_ci complete(&adapter->init_done); 590362306a36Sopenharmony_ci } 590462306a36Sopenharmony_ci 590562306a36Sopenharmony_ci break; 590662306a36Sopenharmony_ci case IBMVNIC_CRQ_INIT_COMPLETE: 590762306a36Sopenharmony_ci dev_info(dev, "Partner initialization complete\n"); 590862306a36Sopenharmony_ci adapter->crq.active = true; 590962306a36Sopenharmony_ci send_version_xchg(adapter); 591062306a36Sopenharmony_ci break; 591162306a36Sopenharmony_ci default: 591262306a36Sopenharmony_ci dev_err(dev, "Unknown crq cmd: %d\n", gen_crq->cmd); 591362306a36Sopenharmony_ci } 591462306a36Sopenharmony_ci return; 591562306a36Sopenharmony_ci case IBMVNIC_CRQ_XPORT_EVENT: 591662306a36Sopenharmony_ci netif_carrier_off(netdev); 591762306a36Sopenharmony_ci adapter->crq.active = false; 591862306a36Sopenharmony_ci /* terminate any thread waiting for a response 591962306a36Sopenharmony_ci * from the device 592062306a36Sopenharmony_ci */ 592162306a36Sopenharmony_ci if (!completion_done(&adapter->fw_done)) { 592262306a36Sopenharmony_ci adapter->fw_done_rc = -EIO; 592362306a36Sopenharmony_ci complete(&adapter->fw_done); 592462306a36Sopenharmony_ci } 592562306a36Sopenharmony_ci 592662306a36Sopenharmony_ci /* if we got here during crq-init, retry crq-init */ 592762306a36Sopenharmony_ci if (!completion_done(&adapter->init_done)) { 592862306a36Sopenharmony_ci adapter->init_done_rc = -EAGAIN; 592962306a36Sopenharmony_ci complete(&adapter->init_done); 593062306a36Sopenharmony_ci } 593162306a36Sopenharmony_ci 593262306a36Sopenharmony_ci if (!completion_done(&adapter->stats_done)) 593362306a36Sopenharmony_ci complete(&adapter->stats_done); 593462306a36Sopenharmony_ci if (test_bit(0, &adapter->resetting)) 593562306a36Sopenharmony_ci adapter->force_reset_recovery = true; 593662306a36Sopenharmony_ci if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) { 593762306a36Sopenharmony_ci dev_info(dev, "Migrated, re-enabling adapter\n"); 593862306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_MOBILITY); 593962306a36Sopenharmony_ci } else if (gen_crq->cmd == IBMVNIC_DEVICE_FAILOVER) { 594062306a36Sopenharmony_ci dev_info(dev, "Backing device failover detected\n"); 594162306a36Sopenharmony_ci adapter->failover_pending = true; 594262306a36Sopenharmony_ci } else { 594362306a36Sopenharmony_ci /* The adapter lost the connection */ 594462306a36Sopenharmony_ci dev_err(dev, "Virtual Adapter failed (rc=%d)\n", 594562306a36Sopenharmony_ci gen_crq->cmd); 594662306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_FATAL); 594762306a36Sopenharmony_ci } 594862306a36Sopenharmony_ci return; 594962306a36Sopenharmony_ci case IBMVNIC_CRQ_CMD_RSP: 595062306a36Sopenharmony_ci break; 595162306a36Sopenharmony_ci default: 595262306a36Sopenharmony_ci dev_err(dev, "Got an invalid msg type 0x%02x\n", 595362306a36Sopenharmony_ci gen_crq->first); 595462306a36Sopenharmony_ci return; 595562306a36Sopenharmony_ci } 595662306a36Sopenharmony_ci 595762306a36Sopenharmony_ci switch (gen_crq->cmd) { 595862306a36Sopenharmony_ci case VERSION_EXCHANGE_RSP: 595962306a36Sopenharmony_ci rc = crq->version_exchange_rsp.rc.code; 596062306a36Sopenharmony_ci if (rc) { 596162306a36Sopenharmony_ci dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc); 596262306a36Sopenharmony_ci break; 596362306a36Sopenharmony_ci } 596462306a36Sopenharmony_ci ibmvnic_version = 596562306a36Sopenharmony_ci be16_to_cpu(crq->version_exchange_rsp.version); 596662306a36Sopenharmony_ci dev_info(dev, "Partner protocol version is %d\n", 596762306a36Sopenharmony_ci ibmvnic_version); 596862306a36Sopenharmony_ci send_query_cap(adapter); 596962306a36Sopenharmony_ci break; 597062306a36Sopenharmony_ci case QUERY_CAPABILITY_RSP: 597162306a36Sopenharmony_ci handle_query_cap_rsp(crq, adapter); 597262306a36Sopenharmony_ci break; 597362306a36Sopenharmony_ci case QUERY_MAP_RSP: 597462306a36Sopenharmony_ci handle_query_map_rsp(crq, adapter); 597562306a36Sopenharmony_ci break; 597662306a36Sopenharmony_ci case REQUEST_MAP_RSP: 597762306a36Sopenharmony_ci adapter->fw_done_rc = crq->request_map_rsp.rc.code; 597862306a36Sopenharmony_ci complete(&adapter->fw_done); 597962306a36Sopenharmony_ci break; 598062306a36Sopenharmony_ci case REQUEST_UNMAP_RSP: 598162306a36Sopenharmony_ci handle_request_unmap_rsp(crq, adapter); 598262306a36Sopenharmony_ci break; 598362306a36Sopenharmony_ci case REQUEST_CAPABILITY_RSP: 598462306a36Sopenharmony_ci handle_request_cap_rsp(crq, adapter); 598562306a36Sopenharmony_ci break; 598662306a36Sopenharmony_ci case LOGIN_RSP: 598762306a36Sopenharmony_ci netdev_dbg(netdev, "Got Login Response\n"); 598862306a36Sopenharmony_ci handle_login_rsp(crq, adapter); 598962306a36Sopenharmony_ci break; 599062306a36Sopenharmony_ci case LOGICAL_LINK_STATE_RSP: 599162306a36Sopenharmony_ci netdev_dbg(netdev, 599262306a36Sopenharmony_ci "Got Logical Link State Response, state: %d rc: %d\n", 599362306a36Sopenharmony_ci crq->logical_link_state_rsp.link_state, 599462306a36Sopenharmony_ci crq->logical_link_state_rsp.rc.code); 599562306a36Sopenharmony_ci adapter->logical_link_state = 599662306a36Sopenharmony_ci crq->logical_link_state_rsp.link_state; 599762306a36Sopenharmony_ci adapter->init_done_rc = crq->logical_link_state_rsp.rc.code; 599862306a36Sopenharmony_ci complete(&adapter->init_done); 599962306a36Sopenharmony_ci break; 600062306a36Sopenharmony_ci case LINK_STATE_INDICATION: 600162306a36Sopenharmony_ci netdev_dbg(netdev, "Got Logical Link State Indication\n"); 600262306a36Sopenharmony_ci adapter->phys_link_state = 600362306a36Sopenharmony_ci crq->link_state_indication.phys_link_state; 600462306a36Sopenharmony_ci adapter->logical_link_state = 600562306a36Sopenharmony_ci crq->link_state_indication.logical_link_state; 600662306a36Sopenharmony_ci if (adapter->phys_link_state && adapter->logical_link_state) 600762306a36Sopenharmony_ci netif_carrier_on(netdev); 600862306a36Sopenharmony_ci else 600962306a36Sopenharmony_ci netif_carrier_off(netdev); 601062306a36Sopenharmony_ci break; 601162306a36Sopenharmony_ci case CHANGE_MAC_ADDR_RSP: 601262306a36Sopenharmony_ci netdev_dbg(netdev, "Got MAC address change Response\n"); 601362306a36Sopenharmony_ci adapter->fw_done_rc = handle_change_mac_rsp(crq, adapter); 601462306a36Sopenharmony_ci break; 601562306a36Sopenharmony_ci case ERROR_INDICATION: 601662306a36Sopenharmony_ci netdev_dbg(netdev, "Got Error Indication\n"); 601762306a36Sopenharmony_ci handle_error_indication(crq, adapter); 601862306a36Sopenharmony_ci break; 601962306a36Sopenharmony_ci case REQUEST_STATISTICS_RSP: 602062306a36Sopenharmony_ci netdev_dbg(netdev, "Got Statistics Response\n"); 602162306a36Sopenharmony_ci complete(&adapter->stats_done); 602262306a36Sopenharmony_ci break; 602362306a36Sopenharmony_ci case QUERY_IP_OFFLOAD_RSP: 602462306a36Sopenharmony_ci netdev_dbg(netdev, "Got Query IP offload Response\n"); 602562306a36Sopenharmony_ci handle_query_ip_offload_rsp(adapter); 602662306a36Sopenharmony_ci break; 602762306a36Sopenharmony_ci case MULTICAST_CTRL_RSP: 602862306a36Sopenharmony_ci netdev_dbg(netdev, "Got multicast control Response\n"); 602962306a36Sopenharmony_ci break; 603062306a36Sopenharmony_ci case CONTROL_IP_OFFLOAD_RSP: 603162306a36Sopenharmony_ci netdev_dbg(netdev, "Got Control IP offload Response\n"); 603262306a36Sopenharmony_ci dma_unmap_single(dev, adapter->ip_offload_ctrl_tok, 603362306a36Sopenharmony_ci sizeof(adapter->ip_offload_ctrl), 603462306a36Sopenharmony_ci DMA_TO_DEVICE); 603562306a36Sopenharmony_ci complete(&adapter->init_done); 603662306a36Sopenharmony_ci break; 603762306a36Sopenharmony_ci case COLLECT_FW_TRACE_RSP: 603862306a36Sopenharmony_ci netdev_dbg(netdev, "Got Collect firmware trace Response\n"); 603962306a36Sopenharmony_ci complete(&adapter->fw_done); 604062306a36Sopenharmony_ci break; 604162306a36Sopenharmony_ci case GET_VPD_SIZE_RSP: 604262306a36Sopenharmony_ci handle_vpd_size_rsp(crq, adapter); 604362306a36Sopenharmony_ci break; 604462306a36Sopenharmony_ci case GET_VPD_RSP: 604562306a36Sopenharmony_ci handle_vpd_rsp(crq, adapter); 604662306a36Sopenharmony_ci break; 604762306a36Sopenharmony_ci case QUERY_PHYS_PARMS_RSP: 604862306a36Sopenharmony_ci adapter->fw_done_rc = handle_query_phys_parms_rsp(crq, adapter); 604962306a36Sopenharmony_ci complete(&adapter->fw_done); 605062306a36Sopenharmony_ci break; 605162306a36Sopenharmony_ci default: 605262306a36Sopenharmony_ci netdev_err(netdev, "Got an invalid cmd type 0x%02x\n", 605362306a36Sopenharmony_ci gen_crq->cmd); 605462306a36Sopenharmony_ci } 605562306a36Sopenharmony_ci} 605662306a36Sopenharmony_ci 605762306a36Sopenharmony_cistatic irqreturn_t ibmvnic_interrupt(int irq, void *instance) 605862306a36Sopenharmony_ci{ 605962306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = instance; 606062306a36Sopenharmony_ci 606162306a36Sopenharmony_ci tasklet_schedule(&adapter->tasklet); 606262306a36Sopenharmony_ci return IRQ_HANDLED; 606362306a36Sopenharmony_ci} 606462306a36Sopenharmony_ci 606562306a36Sopenharmony_cistatic void ibmvnic_tasklet(struct tasklet_struct *t) 606662306a36Sopenharmony_ci{ 606762306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = from_tasklet(adapter, t, tasklet); 606862306a36Sopenharmony_ci struct ibmvnic_crq_queue *queue = &adapter->crq; 606962306a36Sopenharmony_ci union ibmvnic_crq *crq; 607062306a36Sopenharmony_ci unsigned long flags; 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci spin_lock_irqsave(&queue->lock, flags); 607362306a36Sopenharmony_ci 607462306a36Sopenharmony_ci /* Pull all the valid messages off the CRQ */ 607562306a36Sopenharmony_ci while ((crq = ibmvnic_next_crq(adapter)) != NULL) { 607662306a36Sopenharmony_ci /* This barrier makes sure ibmvnic_next_crq()'s 607762306a36Sopenharmony_ci * crq->generic.first & IBMVNIC_CRQ_CMD_RSP is loaded 607862306a36Sopenharmony_ci * before ibmvnic_handle_crq()'s 607962306a36Sopenharmony_ci * switch(gen_crq->first) and switch(gen_crq->cmd). 608062306a36Sopenharmony_ci */ 608162306a36Sopenharmony_ci dma_rmb(); 608262306a36Sopenharmony_ci ibmvnic_handle_crq(crq, adapter); 608362306a36Sopenharmony_ci crq->generic.first = 0; 608462306a36Sopenharmony_ci } 608562306a36Sopenharmony_ci 608662306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->lock, flags); 608762306a36Sopenharmony_ci} 608862306a36Sopenharmony_ci 608962306a36Sopenharmony_cistatic int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *adapter) 609062306a36Sopenharmony_ci{ 609162306a36Sopenharmony_ci struct vio_dev *vdev = adapter->vdev; 609262306a36Sopenharmony_ci int rc; 609362306a36Sopenharmony_ci 609462306a36Sopenharmony_ci do { 609562306a36Sopenharmony_ci rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); 609662306a36Sopenharmony_ci } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); 609762306a36Sopenharmony_ci 609862306a36Sopenharmony_ci if (rc) 609962306a36Sopenharmony_ci dev_err(&vdev->dev, "Error enabling adapter (rc=%d)\n", rc); 610062306a36Sopenharmony_ci 610162306a36Sopenharmony_ci return rc; 610262306a36Sopenharmony_ci} 610362306a36Sopenharmony_ci 610462306a36Sopenharmony_cistatic int ibmvnic_reset_crq(struct ibmvnic_adapter *adapter) 610562306a36Sopenharmony_ci{ 610662306a36Sopenharmony_ci struct ibmvnic_crq_queue *crq = &adapter->crq; 610762306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 610862306a36Sopenharmony_ci struct vio_dev *vdev = adapter->vdev; 610962306a36Sopenharmony_ci int rc; 611062306a36Sopenharmony_ci 611162306a36Sopenharmony_ci /* Close the CRQ */ 611262306a36Sopenharmony_ci do { 611362306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); 611462306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 611562306a36Sopenharmony_ci 611662306a36Sopenharmony_ci /* Clean out the queue */ 611762306a36Sopenharmony_ci if (!crq->msgs) 611862306a36Sopenharmony_ci return -EINVAL; 611962306a36Sopenharmony_ci 612062306a36Sopenharmony_ci memset(crq->msgs, 0, PAGE_SIZE); 612162306a36Sopenharmony_ci crq->cur = 0; 612262306a36Sopenharmony_ci crq->active = false; 612362306a36Sopenharmony_ci 612462306a36Sopenharmony_ci /* And re-open it again */ 612562306a36Sopenharmony_ci rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, 612662306a36Sopenharmony_ci crq->msg_token, PAGE_SIZE); 612762306a36Sopenharmony_ci 612862306a36Sopenharmony_ci if (rc == H_CLOSED) 612962306a36Sopenharmony_ci /* Adapter is good, but other end is not ready */ 613062306a36Sopenharmony_ci dev_warn(dev, "Partner adapter not ready\n"); 613162306a36Sopenharmony_ci else if (rc != 0) 613262306a36Sopenharmony_ci dev_warn(dev, "Couldn't register crq (rc=%d)\n", rc); 613362306a36Sopenharmony_ci 613462306a36Sopenharmony_ci return rc; 613562306a36Sopenharmony_ci} 613662306a36Sopenharmony_ci 613762306a36Sopenharmony_cistatic void release_crq_queue(struct ibmvnic_adapter *adapter) 613862306a36Sopenharmony_ci{ 613962306a36Sopenharmony_ci struct ibmvnic_crq_queue *crq = &adapter->crq; 614062306a36Sopenharmony_ci struct vio_dev *vdev = adapter->vdev; 614162306a36Sopenharmony_ci long rc; 614262306a36Sopenharmony_ci 614362306a36Sopenharmony_ci if (!crq->msgs) 614462306a36Sopenharmony_ci return; 614562306a36Sopenharmony_ci 614662306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "Releasing CRQ\n"); 614762306a36Sopenharmony_ci free_irq(vdev->irq, adapter); 614862306a36Sopenharmony_ci tasklet_kill(&adapter->tasklet); 614962306a36Sopenharmony_ci do { 615062306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); 615162306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 615262306a36Sopenharmony_ci 615362306a36Sopenharmony_ci dma_unmap_single(&vdev->dev, crq->msg_token, PAGE_SIZE, 615462306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 615562306a36Sopenharmony_ci free_page((unsigned long)crq->msgs); 615662306a36Sopenharmony_ci crq->msgs = NULL; 615762306a36Sopenharmony_ci crq->active = false; 615862306a36Sopenharmony_ci} 615962306a36Sopenharmony_ci 616062306a36Sopenharmony_cistatic int init_crq_queue(struct ibmvnic_adapter *adapter) 616162306a36Sopenharmony_ci{ 616262306a36Sopenharmony_ci struct ibmvnic_crq_queue *crq = &adapter->crq; 616362306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 616462306a36Sopenharmony_ci struct vio_dev *vdev = adapter->vdev; 616562306a36Sopenharmony_ci int rc, retrc = -ENOMEM; 616662306a36Sopenharmony_ci 616762306a36Sopenharmony_ci if (crq->msgs) 616862306a36Sopenharmony_ci return 0; 616962306a36Sopenharmony_ci 617062306a36Sopenharmony_ci crq->msgs = (union ibmvnic_crq *)get_zeroed_page(GFP_KERNEL); 617162306a36Sopenharmony_ci /* Should we allocate more than one page? */ 617262306a36Sopenharmony_ci 617362306a36Sopenharmony_ci if (!crq->msgs) 617462306a36Sopenharmony_ci return -ENOMEM; 617562306a36Sopenharmony_ci 617662306a36Sopenharmony_ci crq->size = PAGE_SIZE / sizeof(*crq->msgs); 617762306a36Sopenharmony_ci crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE, 617862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 617962306a36Sopenharmony_ci if (dma_mapping_error(dev, crq->msg_token)) 618062306a36Sopenharmony_ci goto map_failed; 618162306a36Sopenharmony_ci 618262306a36Sopenharmony_ci rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, 618362306a36Sopenharmony_ci crq->msg_token, PAGE_SIZE); 618462306a36Sopenharmony_ci 618562306a36Sopenharmony_ci if (rc == H_RESOURCE) 618662306a36Sopenharmony_ci /* maybe kexecing and resource is busy. try a reset */ 618762306a36Sopenharmony_ci rc = ibmvnic_reset_crq(adapter); 618862306a36Sopenharmony_ci retrc = rc; 618962306a36Sopenharmony_ci 619062306a36Sopenharmony_ci if (rc == H_CLOSED) { 619162306a36Sopenharmony_ci dev_warn(dev, "Partner adapter not ready\n"); 619262306a36Sopenharmony_ci } else if (rc) { 619362306a36Sopenharmony_ci dev_warn(dev, "Error %d opening adapter\n", rc); 619462306a36Sopenharmony_ci goto reg_crq_failed; 619562306a36Sopenharmony_ci } 619662306a36Sopenharmony_ci 619762306a36Sopenharmony_ci retrc = 0; 619862306a36Sopenharmony_ci 619962306a36Sopenharmony_ci tasklet_setup(&adapter->tasklet, (void *)ibmvnic_tasklet); 620062306a36Sopenharmony_ci 620162306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "registering irq 0x%x\n", vdev->irq); 620262306a36Sopenharmony_ci snprintf(crq->name, sizeof(crq->name), "ibmvnic-%x", 620362306a36Sopenharmony_ci adapter->vdev->unit_address); 620462306a36Sopenharmony_ci rc = request_irq(vdev->irq, ibmvnic_interrupt, 0, crq->name, adapter); 620562306a36Sopenharmony_ci if (rc) { 620662306a36Sopenharmony_ci dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", 620762306a36Sopenharmony_ci vdev->irq, rc); 620862306a36Sopenharmony_ci goto req_irq_failed; 620962306a36Sopenharmony_ci } 621062306a36Sopenharmony_ci 621162306a36Sopenharmony_ci rc = vio_enable_interrupts(vdev); 621262306a36Sopenharmony_ci if (rc) { 621362306a36Sopenharmony_ci dev_err(dev, "Error %d enabling interrupts\n", rc); 621462306a36Sopenharmony_ci goto req_irq_failed; 621562306a36Sopenharmony_ci } 621662306a36Sopenharmony_ci 621762306a36Sopenharmony_ci crq->cur = 0; 621862306a36Sopenharmony_ci spin_lock_init(&crq->lock); 621962306a36Sopenharmony_ci 622062306a36Sopenharmony_ci /* process any CRQs that were queued before we enabled interrupts */ 622162306a36Sopenharmony_ci tasklet_schedule(&adapter->tasklet); 622262306a36Sopenharmony_ci 622362306a36Sopenharmony_ci return retrc; 622462306a36Sopenharmony_ci 622562306a36Sopenharmony_cireq_irq_failed: 622662306a36Sopenharmony_ci tasklet_kill(&adapter->tasklet); 622762306a36Sopenharmony_ci do { 622862306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); 622962306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 623062306a36Sopenharmony_cireg_crq_failed: 623162306a36Sopenharmony_ci dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); 623262306a36Sopenharmony_cimap_failed: 623362306a36Sopenharmony_ci free_page((unsigned long)crq->msgs); 623462306a36Sopenharmony_ci crq->msgs = NULL; 623562306a36Sopenharmony_ci return retrc; 623662306a36Sopenharmony_ci} 623762306a36Sopenharmony_ci 623862306a36Sopenharmony_cistatic int ibmvnic_reset_init(struct ibmvnic_adapter *adapter, bool reset) 623962306a36Sopenharmony_ci{ 624062306a36Sopenharmony_ci struct device *dev = &adapter->vdev->dev; 624162306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(20000); 624262306a36Sopenharmony_ci u64 old_num_rx_queues = adapter->req_rx_queues; 624362306a36Sopenharmony_ci u64 old_num_tx_queues = adapter->req_tx_queues; 624462306a36Sopenharmony_ci int rc; 624562306a36Sopenharmony_ci 624662306a36Sopenharmony_ci adapter->from_passive_init = false; 624762306a36Sopenharmony_ci 624862306a36Sopenharmony_ci rc = ibmvnic_send_crq_init(adapter); 624962306a36Sopenharmony_ci if (rc) { 625062306a36Sopenharmony_ci dev_err(dev, "Send crq init failed with error %d\n", rc); 625162306a36Sopenharmony_ci return rc; 625262306a36Sopenharmony_ci } 625362306a36Sopenharmony_ci 625462306a36Sopenharmony_ci if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { 625562306a36Sopenharmony_ci dev_err(dev, "Initialization sequence timed out\n"); 625662306a36Sopenharmony_ci return -ETIMEDOUT; 625762306a36Sopenharmony_ci } 625862306a36Sopenharmony_ci 625962306a36Sopenharmony_ci if (adapter->init_done_rc) { 626062306a36Sopenharmony_ci release_crq_queue(adapter); 626162306a36Sopenharmony_ci dev_err(dev, "CRQ-init failed, %d\n", adapter->init_done_rc); 626262306a36Sopenharmony_ci return adapter->init_done_rc; 626362306a36Sopenharmony_ci } 626462306a36Sopenharmony_ci 626562306a36Sopenharmony_ci if (adapter->from_passive_init) { 626662306a36Sopenharmony_ci adapter->state = VNIC_OPEN; 626762306a36Sopenharmony_ci adapter->from_passive_init = false; 626862306a36Sopenharmony_ci dev_err(dev, "CRQ-init failed, passive-init\n"); 626962306a36Sopenharmony_ci return -EINVAL; 627062306a36Sopenharmony_ci } 627162306a36Sopenharmony_ci 627262306a36Sopenharmony_ci if (reset && 627362306a36Sopenharmony_ci test_bit(0, &adapter->resetting) && !adapter->wait_for_reset && 627462306a36Sopenharmony_ci adapter->reset_reason != VNIC_RESET_MOBILITY) { 627562306a36Sopenharmony_ci if (adapter->req_rx_queues != old_num_rx_queues || 627662306a36Sopenharmony_ci adapter->req_tx_queues != old_num_tx_queues) { 627762306a36Sopenharmony_ci release_sub_crqs(adapter, 0); 627862306a36Sopenharmony_ci rc = init_sub_crqs(adapter); 627962306a36Sopenharmony_ci } else { 628062306a36Sopenharmony_ci /* no need to reinitialize completely, but we do 628162306a36Sopenharmony_ci * need to clean up transmits that were in flight 628262306a36Sopenharmony_ci * when we processed the reset. Failure to do so 628362306a36Sopenharmony_ci * will confound the upper layer, usually TCP, by 628462306a36Sopenharmony_ci * creating the illusion of transmits that are 628562306a36Sopenharmony_ci * awaiting completion. 628662306a36Sopenharmony_ci */ 628762306a36Sopenharmony_ci clean_tx_pools(adapter); 628862306a36Sopenharmony_ci 628962306a36Sopenharmony_ci rc = reset_sub_crq_queues(adapter); 629062306a36Sopenharmony_ci } 629162306a36Sopenharmony_ci } else { 629262306a36Sopenharmony_ci rc = init_sub_crqs(adapter); 629362306a36Sopenharmony_ci } 629462306a36Sopenharmony_ci 629562306a36Sopenharmony_ci if (rc) { 629662306a36Sopenharmony_ci dev_err(dev, "Initialization of sub crqs failed\n"); 629762306a36Sopenharmony_ci release_crq_queue(adapter); 629862306a36Sopenharmony_ci return rc; 629962306a36Sopenharmony_ci } 630062306a36Sopenharmony_ci 630162306a36Sopenharmony_ci rc = init_sub_crq_irqs(adapter); 630262306a36Sopenharmony_ci if (rc) { 630362306a36Sopenharmony_ci dev_err(dev, "Failed to initialize sub crq irqs\n"); 630462306a36Sopenharmony_ci release_crq_queue(adapter); 630562306a36Sopenharmony_ci } 630662306a36Sopenharmony_ci 630762306a36Sopenharmony_ci return rc; 630862306a36Sopenharmony_ci} 630962306a36Sopenharmony_ci 631062306a36Sopenharmony_cistatic struct device_attribute dev_attr_failover; 631162306a36Sopenharmony_ci 631262306a36Sopenharmony_cistatic int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) 631362306a36Sopenharmony_ci{ 631462306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 631562306a36Sopenharmony_ci struct net_device *netdev; 631662306a36Sopenharmony_ci unsigned char *mac_addr_p; 631762306a36Sopenharmony_ci unsigned long flags; 631862306a36Sopenharmony_ci bool init_success; 631962306a36Sopenharmony_ci int rc; 632062306a36Sopenharmony_ci 632162306a36Sopenharmony_ci dev_dbg(&dev->dev, "entering ibmvnic_probe for UA 0x%x\n", 632262306a36Sopenharmony_ci dev->unit_address); 632362306a36Sopenharmony_ci 632462306a36Sopenharmony_ci mac_addr_p = (unsigned char *)vio_get_attribute(dev, 632562306a36Sopenharmony_ci VETH_MAC_ADDR, NULL); 632662306a36Sopenharmony_ci if (!mac_addr_p) { 632762306a36Sopenharmony_ci dev_err(&dev->dev, 632862306a36Sopenharmony_ci "(%s:%3.3d) ERROR: Can't find MAC_ADDR attribute\n", 632962306a36Sopenharmony_ci __FILE__, __LINE__); 633062306a36Sopenharmony_ci return 0; 633162306a36Sopenharmony_ci } 633262306a36Sopenharmony_ci 633362306a36Sopenharmony_ci netdev = alloc_etherdev_mq(sizeof(struct ibmvnic_adapter), 633462306a36Sopenharmony_ci IBMVNIC_MAX_QUEUES); 633562306a36Sopenharmony_ci if (!netdev) 633662306a36Sopenharmony_ci return -ENOMEM; 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci adapter = netdev_priv(netdev); 633962306a36Sopenharmony_ci adapter->state = VNIC_PROBING; 634062306a36Sopenharmony_ci dev_set_drvdata(&dev->dev, netdev); 634162306a36Sopenharmony_ci adapter->vdev = dev; 634262306a36Sopenharmony_ci adapter->netdev = netdev; 634362306a36Sopenharmony_ci adapter->login_pending = false; 634462306a36Sopenharmony_ci memset(&adapter->map_ids, 0, sizeof(adapter->map_ids)); 634562306a36Sopenharmony_ci /* map_ids start at 1, so ensure map_id 0 is always "in-use" */ 634662306a36Sopenharmony_ci bitmap_set(adapter->map_ids, 0, 1); 634762306a36Sopenharmony_ci 634862306a36Sopenharmony_ci ether_addr_copy(adapter->mac_addr, mac_addr_p); 634962306a36Sopenharmony_ci eth_hw_addr_set(netdev, adapter->mac_addr); 635062306a36Sopenharmony_ci netdev->irq = dev->irq; 635162306a36Sopenharmony_ci netdev->netdev_ops = &ibmvnic_netdev_ops; 635262306a36Sopenharmony_ci netdev->ethtool_ops = &ibmvnic_ethtool_ops; 635362306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &dev->dev); 635462306a36Sopenharmony_ci 635562306a36Sopenharmony_ci INIT_WORK(&adapter->ibmvnic_reset, __ibmvnic_reset); 635662306a36Sopenharmony_ci INIT_DELAYED_WORK(&adapter->ibmvnic_delayed_reset, 635762306a36Sopenharmony_ci __ibmvnic_delayed_reset); 635862306a36Sopenharmony_ci INIT_LIST_HEAD(&adapter->rwi_list); 635962306a36Sopenharmony_ci spin_lock_init(&adapter->rwi_lock); 636062306a36Sopenharmony_ci spin_lock_init(&adapter->state_lock); 636162306a36Sopenharmony_ci mutex_init(&adapter->fw_lock); 636262306a36Sopenharmony_ci init_completion(&adapter->probe_done); 636362306a36Sopenharmony_ci init_completion(&adapter->init_done); 636462306a36Sopenharmony_ci init_completion(&adapter->fw_done); 636562306a36Sopenharmony_ci init_completion(&adapter->reset_done); 636662306a36Sopenharmony_ci init_completion(&adapter->stats_done); 636762306a36Sopenharmony_ci clear_bit(0, &adapter->resetting); 636862306a36Sopenharmony_ci adapter->prev_rx_buf_sz = 0; 636962306a36Sopenharmony_ci adapter->prev_mtu = 0; 637062306a36Sopenharmony_ci 637162306a36Sopenharmony_ci init_success = false; 637262306a36Sopenharmony_ci do { 637362306a36Sopenharmony_ci reinit_init_done(adapter); 637462306a36Sopenharmony_ci 637562306a36Sopenharmony_ci /* clear any failovers we got in the previous pass 637662306a36Sopenharmony_ci * since we are reinitializing the CRQ 637762306a36Sopenharmony_ci */ 637862306a36Sopenharmony_ci adapter->failover_pending = false; 637962306a36Sopenharmony_ci 638062306a36Sopenharmony_ci /* If we had already initialized CRQ, we may have one or 638162306a36Sopenharmony_ci * more resets queued already. Discard those and release 638262306a36Sopenharmony_ci * the CRQ before initializing the CRQ again. 638362306a36Sopenharmony_ci */ 638462306a36Sopenharmony_ci release_crq_queue(adapter); 638562306a36Sopenharmony_ci 638662306a36Sopenharmony_ci /* Since we are still in PROBING state, __ibmvnic_reset() 638762306a36Sopenharmony_ci * will not access the ->rwi_list and since we released CRQ, 638862306a36Sopenharmony_ci * we won't get _new_ transport events. But there maybe an 638962306a36Sopenharmony_ci * ongoing ibmvnic_reset() call. So serialize access to 639062306a36Sopenharmony_ci * rwi_list. If we win the race, ibvmnic_reset() could add 639162306a36Sopenharmony_ci * a reset after we purged but thats ok - we just may end 639262306a36Sopenharmony_ci * up with an extra reset (i.e similar to having two or more 639362306a36Sopenharmony_ci * resets in the queue at once). 639462306a36Sopenharmony_ci * CHECK. 639562306a36Sopenharmony_ci */ 639662306a36Sopenharmony_ci spin_lock_irqsave(&adapter->rwi_lock, flags); 639762306a36Sopenharmony_ci flush_reset_queue(adapter); 639862306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->rwi_lock, flags); 639962306a36Sopenharmony_ci 640062306a36Sopenharmony_ci rc = init_crq_queue(adapter); 640162306a36Sopenharmony_ci if (rc) { 640262306a36Sopenharmony_ci dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n", 640362306a36Sopenharmony_ci rc); 640462306a36Sopenharmony_ci goto ibmvnic_init_fail; 640562306a36Sopenharmony_ci } 640662306a36Sopenharmony_ci 640762306a36Sopenharmony_ci rc = ibmvnic_reset_init(adapter, false); 640862306a36Sopenharmony_ci } while (rc == -EAGAIN); 640962306a36Sopenharmony_ci 641062306a36Sopenharmony_ci /* We are ignoring the error from ibmvnic_reset_init() assuming that the 641162306a36Sopenharmony_ci * partner is not ready. CRQ is not active. When the partner becomes 641262306a36Sopenharmony_ci * ready, we will do the passive init reset. 641362306a36Sopenharmony_ci */ 641462306a36Sopenharmony_ci 641562306a36Sopenharmony_ci if (!rc) 641662306a36Sopenharmony_ci init_success = true; 641762306a36Sopenharmony_ci 641862306a36Sopenharmony_ci rc = init_stats_buffers(adapter); 641962306a36Sopenharmony_ci if (rc) 642062306a36Sopenharmony_ci goto ibmvnic_init_fail; 642162306a36Sopenharmony_ci 642262306a36Sopenharmony_ci rc = init_stats_token(adapter); 642362306a36Sopenharmony_ci if (rc) 642462306a36Sopenharmony_ci goto ibmvnic_stats_fail; 642562306a36Sopenharmony_ci 642662306a36Sopenharmony_ci rc = device_create_file(&dev->dev, &dev_attr_failover); 642762306a36Sopenharmony_ci if (rc) 642862306a36Sopenharmony_ci goto ibmvnic_dev_file_err; 642962306a36Sopenharmony_ci 643062306a36Sopenharmony_ci netif_carrier_off(netdev); 643162306a36Sopenharmony_ci 643262306a36Sopenharmony_ci if (init_success) { 643362306a36Sopenharmony_ci adapter->state = VNIC_PROBED; 643462306a36Sopenharmony_ci netdev->mtu = adapter->req_mtu - ETH_HLEN; 643562306a36Sopenharmony_ci netdev->min_mtu = adapter->min_mtu - ETH_HLEN; 643662306a36Sopenharmony_ci netdev->max_mtu = adapter->max_mtu - ETH_HLEN; 643762306a36Sopenharmony_ci } else { 643862306a36Sopenharmony_ci adapter->state = VNIC_DOWN; 643962306a36Sopenharmony_ci } 644062306a36Sopenharmony_ci 644162306a36Sopenharmony_ci adapter->wait_for_reset = false; 644262306a36Sopenharmony_ci adapter->last_reset_time = jiffies; 644362306a36Sopenharmony_ci 644462306a36Sopenharmony_ci rc = register_netdev(netdev); 644562306a36Sopenharmony_ci if (rc) { 644662306a36Sopenharmony_ci dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); 644762306a36Sopenharmony_ci goto ibmvnic_register_fail; 644862306a36Sopenharmony_ci } 644962306a36Sopenharmony_ci dev_info(&dev->dev, "ibmvnic registered\n"); 645062306a36Sopenharmony_ci 645162306a36Sopenharmony_ci rc = ibmvnic_cpu_notif_add(adapter); 645262306a36Sopenharmony_ci if (rc) { 645362306a36Sopenharmony_ci netdev_err(netdev, "Registering cpu notifier failed\n"); 645462306a36Sopenharmony_ci goto cpu_notif_add_failed; 645562306a36Sopenharmony_ci } 645662306a36Sopenharmony_ci 645762306a36Sopenharmony_ci complete(&adapter->probe_done); 645862306a36Sopenharmony_ci 645962306a36Sopenharmony_ci return 0; 646062306a36Sopenharmony_ci 646162306a36Sopenharmony_cicpu_notif_add_failed: 646262306a36Sopenharmony_ci unregister_netdev(netdev); 646362306a36Sopenharmony_ci 646462306a36Sopenharmony_ciibmvnic_register_fail: 646562306a36Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_failover); 646662306a36Sopenharmony_ci 646762306a36Sopenharmony_ciibmvnic_dev_file_err: 646862306a36Sopenharmony_ci release_stats_token(adapter); 646962306a36Sopenharmony_ci 647062306a36Sopenharmony_ciibmvnic_stats_fail: 647162306a36Sopenharmony_ci release_stats_buffers(adapter); 647262306a36Sopenharmony_ci 647362306a36Sopenharmony_ciibmvnic_init_fail: 647462306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 647562306a36Sopenharmony_ci release_crq_queue(adapter); 647662306a36Sopenharmony_ci 647762306a36Sopenharmony_ci /* cleanup worker thread after releasing CRQ so we don't get 647862306a36Sopenharmony_ci * transport events (i.e new work items for the worker thread). 647962306a36Sopenharmony_ci */ 648062306a36Sopenharmony_ci adapter->state = VNIC_REMOVING; 648162306a36Sopenharmony_ci complete(&adapter->probe_done); 648262306a36Sopenharmony_ci flush_work(&adapter->ibmvnic_reset); 648362306a36Sopenharmony_ci flush_delayed_work(&adapter->ibmvnic_delayed_reset); 648462306a36Sopenharmony_ci 648562306a36Sopenharmony_ci flush_reset_queue(adapter); 648662306a36Sopenharmony_ci 648762306a36Sopenharmony_ci mutex_destroy(&adapter->fw_lock); 648862306a36Sopenharmony_ci free_netdev(netdev); 648962306a36Sopenharmony_ci 649062306a36Sopenharmony_ci return rc; 649162306a36Sopenharmony_ci} 649262306a36Sopenharmony_ci 649362306a36Sopenharmony_cistatic void ibmvnic_remove(struct vio_dev *dev) 649462306a36Sopenharmony_ci{ 649562306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(&dev->dev); 649662306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 649762306a36Sopenharmony_ci unsigned long flags; 649862306a36Sopenharmony_ci 649962306a36Sopenharmony_ci spin_lock_irqsave(&adapter->state_lock, flags); 650062306a36Sopenharmony_ci 650162306a36Sopenharmony_ci /* If ibmvnic_reset() is scheduling a reset, wait for it to 650262306a36Sopenharmony_ci * finish. Then, set the state to REMOVING to prevent it from 650362306a36Sopenharmony_ci * scheduling any more work and to have reset functions ignore 650462306a36Sopenharmony_ci * any resets that have already been scheduled. Drop the lock 650562306a36Sopenharmony_ci * after setting state, so __ibmvnic_reset() which is called 650662306a36Sopenharmony_ci * from the flush_work() below, can make progress. 650762306a36Sopenharmony_ci */ 650862306a36Sopenharmony_ci spin_lock(&adapter->rwi_lock); 650962306a36Sopenharmony_ci adapter->state = VNIC_REMOVING; 651062306a36Sopenharmony_ci spin_unlock(&adapter->rwi_lock); 651162306a36Sopenharmony_ci 651262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->state_lock, flags); 651362306a36Sopenharmony_ci 651462306a36Sopenharmony_ci ibmvnic_cpu_notif_remove(adapter); 651562306a36Sopenharmony_ci 651662306a36Sopenharmony_ci flush_work(&adapter->ibmvnic_reset); 651762306a36Sopenharmony_ci flush_delayed_work(&adapter->ibmvnic_delayed_reset); 651862306a36Sopenharmony_ci 651962306a36Sopenharmony_ci rtnl_lock(); 652062306a36Sopenharmony_ci unregister_netdevice(netdev); 652162306a36Sopenharmony_ci 652262306a36Sopenharmony_ci release_resources(adapter); 652362306a36Sopenharmony_ci release_rx_pools(adapter); 652462306a36Sopenharmony_ci release_tx_pools(adapter); 652562306a36Sopenharmony_ci release_sub_crqs(adapter, 1); 652662306a36Sopenharmony_ci release_crq_queue(adapter); 652762306a36Sopenharmony_ci 652862306a36Sopenharmony_ci release_stats_token(adapter); 652962306a36Sopenharmony_ci release_stats_buffers(adapter); 653062306a36Sopenharmony_ci 653162306a36Sopenharmony_ci adapter->state = VNIC_REMOVED; 653262306a36Sopenharmony_ci 653362306a36Sopenharmony_ci rtnl_unlock(); 653462306a36Sopenharmony_ci mutex_destroy(&adapter->fw_lock); 653562306a36Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_failover); 653662306a36Sopenharmony_ci free_netdev(netdev); 653762306a36Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 653862306a36Sopenharmony_ci} 653962306a36Sopenharmony_ci 654062306a36Sopenharmony_cistatic ssize_t failover_store(struct device *dev, struct device_attribute *attr, 654162306a36Sopenharmony_ci const char *buf, size_t count) 654262306a36Sopenharmony_ci{ 654362306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 654462306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 654562306a36Sopenharmony_ci unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; 654662306a36Sopenharmony_ci __be64 session_token; 654762306a36Sopenharmony_ci long rc; 654862306a36Sopenharmony_ci 654962306a36Sopenharmony_ci if (!sysfs_streq(buf, "1")) 655062306a36Sopenharmony_ci return -EINVAL; 655162306a36Sopenharmony_ci 655262306a36Sopenharmony_ci rc = plpar_hcall(H_VIOCTL, retbuf, adapter->vdev->unit_address, 655362306a36Sopenharmony_ci H_GET_SESSION_TOKEN, 0, 0, 0); 655462306a36Sopenharmony_ci if (rc) { 655562306a36Sopenharmony_ci netdev_err(netdev, "Couldn't retrieve session token, rc %ld\n", 655662306a36Sopenharmony_ci rc); 655762306a36Sopenharmony_ci goto last_resort; 655862306a36Sopenharmony_ci } 655962306a36Sopenharmony_ci 656062306a36Sopenharmony_ci session_token = (__be64)retbuf[0]; 656162306a36Sopenharmony_ci netdev_dbg(netdev, "Initiating client failover, session id %llx\n", 656262306a36Sopenharmony_ci be64_to_cpu(session_token)); 656362306a36Sopenharmony_ci rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address, 656462306a36Sopenharmony_ci H_SESSION_ERR_DETECTED, session_token, 0, 0); 656562306a36Sopenharmony_ci if (rc) { 656662306a36Sopenharmony_ci netdev_err(netdev, 656762306a36Sopenharmony_ci "H_VIOCTL initiated failover failed, rc %ld\n", 656862306a36Sopenharmony_ci rc); 656962306a36Sopenharmony_ci goto last_resort; 657062306a36Sopenharmony_ci } 657162306a36Sopenharmony_ci 657262306a36Sopenharmony_ci return count; 657362306a36Sopenharmony_ci 657462306a36Sopenharmony_cilast_resort: 657562306a36Sopenharmony_ci netdev_dbg(netdev, "Trying to send CRQ_CMD, the last resort\n"); 657662306a36Sopenharmony_ci ibmvnic_reset(adapter, VNIC_RESET_FAILOVER); 657762306a36Sopenharmony_ci 657862306a36Sopenharmony_ci return count; 657962306a36Sopenharmony_ci} 658062306a36Sopenharmony_cistatic DEVICE_ATTR_WO(failover); 658162306a36Sopenharmony_ci 658262306a36Sopenharmony_cistatic unsigned long ibmvnic_get_desired_dma(struct vio_dev *vdev) 658362306a36Sopenharmony_ci{ 658462306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(&vdev->dev); 658562306a36Sopenharmony_ci struct ibmvnic_adapter *adapter; 658662306a36Sopenharmony_ci struct iommu_table *tbl; 658762306a36Sopenharmony_ci unsigned long ret = 0; 658862306a36Sopenharmony_ci int i; 658962306a36Sopenharmony_ci 659062306a36Sopenharmony_ci tbl = get_iommu_table_base(&vdev->dev); 659162306a36Sopenharmony_ci 659262306a36Sopenharmony_ci /* netdev inits at probe time along with the structures we need below*/ 659362306a36Sopenharmony_ci if (!netdev) 659462306a36Sopenharmony_ci return IOMMU_PAGE_ALIGN(IBMVNIC_IO_ENTITLEMENT_DEFAULT, tbl); 659562306a36Sopenharmony_ci 659662306a36Sopenharmony_ci adapter = netdev_priv(netdev); 659762306a36Sopenharmony_ci 659862306a36Sopenharmony_ci ret += PAGE_SIZE; /* the crq message queue */ 659962306a36Sopenharmony_ci ret += IOMMU_PAGE_ALIGN(sizeof(struct ibmvnic_statistics), tbl); 660062306a36Sopenharmony_ci 660162306a36Sopenharmony_ci for (i = 0; i < adapter->req_tx_queues + adapter->req_rx_queues; i++) 660262306a36Sopenharmony_ci ret += 4 * PAGE_SIZE; /* the scrq message queue */ 660362306a36Sopenharmony_ci 660462306a36Sopenharmony_ci for (i = 0; i < adapter->num_active_rx_pools; i++) 660562306a36Sopenharmony_ci ret += adapter->rx_pool[i].size * 660662306a36Sopenharmony_ci IOMMU_PAGE_ALIGN(adapter->rx_pool[i].buff_size, tbl); 660762306a36Sopenharmony_ci 660862306a36Sopenharmony_ci return ret; 660962306a36Sopenharmony_ci} 661062306a36Sopenharmony_ci 661162306a36Sopenharmony_cistatic int ibmvnic_resume(struct device *dev) 661262306a36Sopenharmony_ci{ 661362306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 661462306a36Sopenharmony_ci struct ibmvnic_adapter *adapter = netdev_priv(netdev); 661562306a36Sopenharmony_ci 661662306a36Sopenharmony_ci if (adapter->state != VNIC_OPEN) 661762306a36Sopenharmony_ci return 0; 661862306a36Sopenharmony_ci 661962306a36Sopenharmony_ci tasklet_schedule(&adapter->tasklet); 662062306a36Sopenharmony_ci 662162306a36Sopenharmony_ci return 0; 662262306a36Sopenharmony_ci} 662362306a36Sopenharmony_ci 662462306a36Sopenharmony_cistatic const struct vio_device_id ibmvnic_device_table[] = { 662562306a36Sopenharmony_ci {"network", "IBM,vnic"}, 662662306a36Sopenharmony_ci {"", "" } 662762306a36Sopenharmony_ci}; 662862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vio, ibmvnic_device_table); 662962306a36Sopenharmony_ci 663062306a36Sopenharmony_cistatic const struct dev_pm_ops ibmvnic_pm_ops = { 663162306a36Sopenharmony_ci .resume = ibmvnic_resume 663262306a36Sopenharmony_ci}; 663362306a36Sopenharmony_ci 663462306a36Sopenharmony_cistatic struct vio_driver ibmvnic_driver = { 663562306a36Sopenharmony_ci .id_table = ibmvnic_device_table, 663662306a36Sopenharmony_ci .probe = ibmvnic_probe, 663762306a36Sopenharmony_ci .remove = ibmvnic_remove, 663862306a36Sopenharmony_ci .get_desired_dma = ibmvnic_get_desired_dma, 663962306a36Sopenharmony_ci .name = ibmvnic_driver_name, 664062306a36Sopenharmony_ci .pm = &ibmvnic_pm_ops, 664162306a36Sopenharmony_ci}; 664262306a36Sopenharmony_ci 664362306a36Sopenharmony_ci/* module functions */ 664462306a36Sopenharmony_cistatic int __init ibmvnic_module_init(void) 664562306a36Sopenharmony_ci{ 664662306a36Sopenharmony_ci int ret; 664762306a36Sopenharmony_ci 664862306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/ibmvnic:online", 664962306a36Sopenharmony_ci ibmvnic_cpu_online, 665062306a36Sopenharmony_ci ibmvnic_cpu_down_prep); 665162306a36Sopenharmony_ci if (ret < 0) 665262306a36Sopenharmony_ci goto out; 665362306a36Sopenharmony_ci ibmvnic_online = ret; 665462306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_IBMVNIC_DEAD, "net/ibmvnic:dead", 665562306a36Sopenharmony_ci NULL, ibmvnic_cpu_dead); 665662306a36Sopenharmony_ci if (ret) 665762306a36Sopenharmony_ci goto err_dead; 665862306a36Sopenharmony_ci 665962306a36Sopenharmony_ci ret = vio_register_driver(&ibmvnic_driver); 666062306a36Sopenharmony_ci if (ret) 666162306a36Sopenharmony_ci goto err_vio_register; 666262306a36Sopenharmony_ci 666362306a36Sopenharmony_ci pr_info("%s: %s %s\n", ibmvnic_driver_name, ibmvnic_driver_string, 666462306a36Sopenharmony_ci IBMVNIC_DRIVER_VERSION); 666562306a36Sopenharmony_ci 666662306a36Sopenharmony_ci return 0; 666762306a36Sopenharmony_cierr_vio_register: 666862306a36Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_IBMVNIC_DEAD); 666962306a36Sopenharmony_cierr_dead: 667062306a36Sopenharmony_ci cpuhp_remove_multi_state(ibmvnic_online); 667162306a36Sopenharmony_ciout: 667262306a36Sopenharmony_ci return ret; 667362306a36Sopenharmony_ci} 667462306a36Sopenharmony_ci 667562306a36Sopenharmony_cistatic void __exit ibmvnic_module_exit(void) 667662306a36Sopenharmony_ci{ 667762306a36Sopenharmony_ci vio_unregister_driver(&ibmvnic_driver); 667862306a36Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_IBMVNIC_DEAD); 667962306a36Sopenharmony_ci cpuhp_remove_multi_state(ibmvnic_online); 668062306a36Sopenharmony_ci} 668162306a36Sopenharmony_ci 668262306a36Sopenharmony_cimodule_init(ibmvnic_module_init); 668362306a36Sopenharmony_cimodule_exit(ibmvnic_module_exit); 6684