Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/ibm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 184 kB image not shown  

Quelle  ibmvnic.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/**************************************************************************/
/*                                                                        */
/*  IBM System i and System p Virtual NIC Device Driver                   */
/*  Copyright (C) 2014 IBM Corp.                                          */
/*  Santiago Leon (santi_leon@yahoo.com)                                  */
/*  Thomas Falcon (tlfalcon@linux.vnet.ibm.com)                           */
/*  John Allen (jallen@linux.vnet.ibm.com)                                */
/*                                                                        */
/*                                                                        */
/* This module contains the implementation of a virtual ethernet device   */
/* for use with IBM i/p Series LPAR Linux. It utilizes the logical LAN    */
/* option of the RS/6000 Platform Architecture to interface with virtual  */
/* ethernet NICs that are presented to the partition by the hypervisor.   */
/*    */
/* Messages are passed between the VNIC driver and the VNIC server using  */
/* Command/Response Queues (CRQs) and sub CRQs (sCRQs). CRQs are used to  */
/* issue and receive commands that initiate communication with the server */
/* on driver initialization. Sub CRQs (sCRQs) are similar to CRQs, but    */
/* are used by the driver to notify the server that a packet is           */
/* ready for transmission or that a buffer has been added to receive a    */
/* packet. Subsequently, sCRQs are used by the server to notify the       */
/* driver that a packet transmission has been completed or that a packet  */
/* has been received and placed in a waiting buffer.                      */
/*                                                                        */
/* In lieu of a more conventional "on-the-fly" DMA mapping strategy in    */
/* which skbs are DMA mapped and immediately unmapped when the transmit   */
/* or receive has been completed, the VNIC driver is required to use      */
/* "long term mapping". This entails that large, continuous DMA mapped    */
/* buffers are allocated on driver initialization and these buffers are   */
/* then continuously reused to pass skbs to and from the VNIC server.     */
/*                                                                        */
/**************************************************************************/

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/completion.h>
#include <linux/ioport.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/ethtool.h>
#include <linux/proc_fs.h>
#include <linux/if_arp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kthread.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <net/net_namespace.h>
#include <asm/hvcall.h>
#include <linux/atomic.h>
#include <asm/vio.h>
#include <asm/xive.h>
#include <asm/iommu.h>
#include <linux/uaccess.h>
#include <asm/firmware.h>
#include <linux/workqueue.h>
#include <linux/if_vlan.h>
#include <linux/utsname.h>
#include <linux/cpu.h>

#include "ibmvnic.h"

static const char ibmvnic_driver_name[] = "ibmvnic";
static const char ibmvnic_driver_string[] = "IBM System i/p Virtual NIC Driver";

MODULE_AUTHOR("Santiago Leon");
MODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(IBMVNIC_DRIVER_VERSION);

static int ibmvnic_version = IBMVNIC_INITIAL_VERSION;
static void release_sub_crqs(struct ibmvnic_adapter *, bool);
static int ibmvnic_reset_crq(struct ibmvnic_adapter *);
static int ibmvnic_send_crq_init(struct ibmvnic_adapter *);
static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
static int ibmvnic_send_crq(struct ibmvnic_adapter *, union ibmvnic_crq *);
static int send_subcrq_indirect(struct ibmvnic_adapter *, u64, u64, u64);
static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance);
static int enable_scrq_irq(struct ibmvnic_adapter *,
      struct ibmvnic_sub_crq_queue *);
static int disable_scrq_irq(struct ibmvnic_adapter *,
       struct ibmvnic_sub_crq_queue *);
static int pending_scrq(struct ibmvnic_adapter *,
   struct ibmvnic_sub_crq_queue *);
static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *,
     struct ibmvnic_sub_crq_queue *);
static int ibmvnic_poll(struct napi_struct *napi, int data);
static int reset_sub_crq_queues(struct ibmvnic_adapter *adapter);
static inline void reinit_init_done(struct ibmvnic_adapter *adapter);
static void send_query_map(struct ibmvnic_adapter *adapter);
static int send_request_map(struct ibmvnic_adapter *, dma_addr_t, u32, u8);
static int send_request_unmap(struct ibmvnic_adapter *, u8);
static int send_login(struct ibmvnic_adapter *adapter);
static void send_query_cap(struct ibmvnic_adapter *adapter);
static int init_sub_crqs(struct ibmvnic_adapter *);
static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter);
static int ibmvnic_reset_init(struct ibmvnic_adapter *, bool reset);
static void release_crq_queue(struct ibmvnic_adapter *);
static int __ibmvnic_set_mac(struct net_device *, u8 *);
static int init_crq_queue(struct ibmvnic_adapter *adapter);
static int send_query_phys_parms(struct ibmvnic_adapter *adapter);
static void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter,
      struct ibmvnic_sub_crq_queue *tx_scrq);
static void free_long_term_buff(struct ibmvnic_adapter *adapter,
    struct ibmvnic_long_term_buff *ltb);
static void ibmvnic_disable_irqs(struct ibmvnic_adapter *adapter);
static void flush_reset_queue(struct ibmvnic_adapter *adapter);
static void print_subcrq_error(struct device *dev, int rc, const char *func);

struct ibmvnic_stat {
 char name[ETH_GSTRING_LEN];
 int offset;
};

#define IBMVNIC_STAT_OFF(stat) (offsetof(struct ibmvnic_adapter, stats) + \
        offsetof(struct ibmvnic_statistics, stat))
#define IBMVNIC_GET_STAT(a, off) (*((u64 *)(((unsigned long)(a)) + (off))))

static const struct ibmvnic_stat ibmvnic_stats[] = {
 {"rx_packets", IBMVNIC_STAT_OFF(rx_packets)},
 {"rx_bytes", IBMVNIC_STAT_OFF(rx_bytes)},
 {"tx_packets", IBMVNIC_STAT_OFF(tx_packets)},
 {"tx_bytes", IBMVNIC_STAT_OFF(tx_bytes)},
 {"ucast_tx_packets", IBMVNIC_STAT_OFF(ucast_tx_packets)},
 {"ucast_rx_packets", IBMVNIC_STAT_OFF(ucast_rx_packets)},
 {"mcast_tx_packets", IBMVNIC_STAT_OFF(mcast_tx_packets)},
 {"mcast_rx_packets", IBMVNIC_STAT_OFF(mcast_rx_packets)},
 {"bcast_tx_packets", IBMVNIC_STAT_OFF(bcast_tx_packets)},
 {"bcast_rx_packets", IBMVNIC_STAT_OFF(bcast_rx_packets)},
 {"align_errors", IBMVNIC_STAT_OFF(align_errors)},
 {"fcs_errors", IBMVNIC_STAT_OFF(fcs_errors)},
 {"single_collision_frames", IBMVNIC_STAT_OFF(single_collision_frames)},
 {"multi_collision_frames", IBMVNIC_STAT_OFF(multi_collision_frames)},
 {"sqe_test_errors", IBMVNIC_STAT_OFF(sqe_test_errors)},
 {"deferred_tx", IBMVNIC_STAT_OFF(deferred_tx)},
 {"late_collisions", IBMVNIC_STAT_OFF(late_collisions)},
 {"excess_collisions", IBMVNIC_STAT_OFF(excess_collisions)},
 {"internal_mac_tx_errors", IBMVNIC_STAT_OFF(internal_mac_tx_errors)},
 {"carrier_sense", IBMVNIC_STAT_OFF(carrier_sense)},
 {"too_long_frames", IBMVNIC_STAT_OFF(too_long_frames)},
 {"internal_mac_rx_errors", IBMVNIC_STAT_OFF(internal_mac_rx_errors)},
};

static int send_crq_init_complete(struct ibmvnic_adapter *adapter)
{
 union ibmvnic_crq crq;

 memset(&crq, 0, sizeof(crq));
 crq.generic.first = IBMVNIC_CRQ_INIT_CMD;
 crq.generic.cmd = IBMVNIC_CRQ_INIT_COMPLETE;

 return ibmvnic_send_crq(adapter, &crq);
}

static int send_version_xchg(struct ibmvnic_adapter *adapter)
{
 union ibmvnic_crq crq;

 memset(&crq, 0, sizeof(crq));
 crq.version_exchange.first = IBMVNIC_CRQ_CMD;
 crq.version_exchange.cmd = VERSION_EXCHANGE;
 crq.version_exchange.version = cpu_to_be16(ibmvnic_version);

 return ibmvnic_send_crq(adapter, &crq);
}

static void ibmvnic_clean_queue_affinity(struct ibmvnic_adapter *adapter,
      struct ibmvnic_sub_crq_queue *queue)
{
 if (!(queue && queue->irq))
  return;

 cpumask_clear(queue->affinity_mask);

 if (irq_set_affinity_and_hint(queue->irq, NULL))
  netdev_warn(adapter->netdev,
       "%s: Clear affinity failed, queue addr = %p, IRQ = %d\n",
       __func__, queue, queue->irq);
}

static void ibmvnic_clean_affinity(struct ibmvnic_adapter *adapter)
{
 struct ibmvnic_sub_crq_queue **rxqs;
 struct ibmvnic_sub_crq_queue **txqs;
 int num_rxqs, num_txqs;
 int i;

 rxqs = adapter->rx_scrq;
 txqs = adapter->tx_scrq;
 num_txqs = adapter->num_active_tx_scrqs;
 num_rxqs = adapter->num_active_rx_scrqs;

 netdev_dbg(adapter->netdev, "%s: Cleaning irq affinity hints", __func__);
 if (txqs) {
  for (i = 0; i < num_txqs; i++)
   ibmvnic_clean_queue_affinity(adapter, txqs[i]);
 }
 if (rxqs) {
  for (i = 0; i < num_rxqs; i++)
   ibmvnic_clean_queue_affinity(adapter, rxqs[i]);
 }
}

static int ibmvnic_set_queue_affinity(struct ibmvnic_sub_crq_queue *queue,
          unsigned int *cpu, int *stragglers,
          int stride)
{
 cpumask_var_t mask;
 int i;
 int rc = 0;

 if (!(queue && queue->irq))
  return rc;

 /* cpumask_var_t is either a pointer or array, allocation works here */
 if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
  return -ENOMEM;

 /* while we have extra cpu give one extra to this irq */
 if (*stragglers) {
  stride++;
  (*stragglers)--;
 }
 /* atomic write is safer than writing bit by bit directly */
 for_each_online_cpu_wrap(i, *cpu) {
  if (!stride--) {
   /* For the next queue we start from the first
 * unused CPU in this queue
 */

   *cpu = i;
   break;
  }
  cpumask_set_cpu(i, mask);
 }

 /* set queue affinity mask */
 cpumask_copy(queue->affinity_mask, mask);
 rc = irq_set_affinity_and_hint(queue->irq, queue->affinity_mask);
 free_cpumask_var(mask);

 return rc;
}

/* assumes cpu read lock is held */
static void ibmvnic_set_affinity(struct ibmvnic_adapter *adapter)
{
 struct ibmvnic_sub_crq_queue **rxqs = adapter->rx_scrq;
 struct ibmvnic_sub_crq_queue **txqs = adapter->tx_scrq;
 struct ibmvnic_sub_crq_queue *queue;
 int num_rxqs = adapter->num_active_rx_scrqs, i_rxqs = 0;
 int num_txqs = adapter->num_active_tx_scrqs, i_txqs = 0;
 int total_queues, stride, stragglers, i;
 unsigned int num_cpu, cpu = 0;
 bool is_rx_queue;
 int rc = 0;

 netdev_dbg(adapter->netdev, "%s: Setting irq affinity hints", __func__);
 if (!(adapter->rx_scrq && adapter->tx_scrq)) {
  netdev_warn(adapter->netdev,
       "%s: Set affinity failed, queues not allocated\n",
       __func__);
  return;
 }

 total_queues = num_rxqs + num_txqs;
 num_cpu = num_online_cpus();
 /* number of cpu's assigned per irq */
 stride = max_t(int, num_cpu / total_queues, 1);
 /* number of leftover cpu's */
 stragglers = num_cpu >= total_queues ? num_cpu % total_queues : 0;

 for (i = 0; i < total_queues; i++) {
  is_rx_queue = false;
  /* balance core load by alternating rx and tx assignments
 * ex: TX0 -> RX0 -> TX1 -> RX1 etc.
 */

  if ((i % 2 == 1 && i_rxqs < num_rxqs) || i_txqs == num_txqs) {
   queue = rxqs[i_rxqs++];
   is_rx_queue = true;
  } else {
   queue = txqs[i_txqs++];
  }

  rc = ibmvnic_set_queue_affinity(queue, &cpu, &stragglers,
      stride);
  if (rc)
   goto out;

  if (!queue || is_rx_queue)
   continue;

  rc = __netif_set_xps_queue(adapter->netdev,
        cpumask_bits(queue->affinity_mask),
        i_txqs - 1, XPS_CPUS);
  if (rc)
   netdev_warn(adapter->netdev, "%s: Set XPS on queue %d failed, rc = %d.\n",
        __func__, i_txqs - 1, rc);
 }

out:
 if (rc) {
  netdev_warn(adapter->netdev,
       "%s: Set affinity failed, queue addr = %p, IRQ = %d, rc = %d.\n",
       __func__, queue, queue->irq, rc);
  ibmvnic_clean_affinity(adapter);
 }
}

static int ibmvnic_cpu_online(unsigned int cpu, struct hlist_node *node)
{
 struct ibmvnic_adapter *adapter;

 adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node);
 ibmvnic_set_affinity(adapter);
 return 0;
}

static int ibmvnic_cpu_dead(unsigned int cpu, struct hlist_node *node)
{
 struct ibmvnic_adapter *adapter;

 adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node_dead);
 ibmvnic_set_affinity(adapter);
 return 0;
}

static int ibmvnic_cpu_down_prep(unsigned int cpu, struct hlist_node *node)
{
 struct ibmvnic_adapter *adapter;

 adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node);
 ibmvnic_clean_affinity(adapter);
 return 0;
}

static enum cpuhp_state ibmvnic_online;

static int ibmvnic_cpu_notif_add(struct ibmvnic_adapter *adapter)
{
 int ret;

 ret = cpuhp_state_add_instance_nocalls(ibmvnic_online, &adapter->node);
 if (ret)
  return ret;
 ret = cpuhp_state_add_instance_nocalls(CPUHP_IBMVNIC_DEAD,
            &adapter->node_dead);
 if (!ret)
  return ret;
 cpuhp_state_remove_instance_nocalls(ibmvnic_online, &adapter->node);
 return ret;
}

static void ibmvnic_cpu_notif_remove(struct ibmvnic_adapter *adapter)
{
 cpuhp_state_remove_instance_nocalls(ibmvnic_online, &adapter->node);
 cpuhp_state_remove_instance_nocalls(CPUHP_IBMVNIC_DEAD,
         &adapter->node_dead);
}

static long h_reg_sub_crq(unsigned long unit_address, unsigned long token,
     unsigned long length, unsigned long *number,
     unsigned long *irq)
{
 unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
 long rc;

 rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, token, length);
 *number = retbuf[0];
 *irq = retbuf[1];

 return rc;
}

/**
 * ibmvnic_wait_for_completion - Check device state and wait for completion
 * @adapter: private device data
 * @comp_done: completion structure to wait for
 * @timeout: time to wait in milliseconds
 *
 * Wait for a completion signal or until the timeout limit is reached
 * while checking that the device is still active.
 */

static int ibmvnic_wait_for_completion(struct ibmvnic_adapter *adapter,
           struct completion *comp_done,
           unsigned long timeout)
{
 struct net_device *netdev;
 unsigned long div_timeout;
 u8 retry;

 netdev = adapter->netdev;
 retry = 5;
 div_timeout = msecs_to_jiffies(timeout / retry);
 while (true) {
  if (!adapter->crq.active) {
   netdev_err(netdev, "Device down!\n");
   return -ENODEV;
  }
  if (!retry--)
   break;
  if (wait_for_completion_timeout(comp_done, div_timeout))
   return 0;
 }
 netdev_err(netdev, "Operation timed out.\n");
 return -ETIMEDOUT;
}

/**
 * reuse_ltb() - Check if a long term buffer can be reused
 * @ltb:  The long term buffer to be checked
 * @size: The size of the long term buffer.
 *
 * An LTB can be reused unless its size has changed.
 *
 * Return: Return true if the LTB can be reused, false otherwise.
 */

static bool reuse_ltb(struct ibmvnic_long_term_buff *ltb, int size)
{
 return (ltb->buff && ltb->size == size);
}

/**
 * alloc_long_term_buff() - Allocate a long term buffer (LTB)
 *
 * @adapter: ibmvnic adapter associated to the LTB
 * @ltb:     container object for the LTB
 * @size:    size of the LTB
 *
 * Allocate an LTB of the specified size and notify VIOS.
 *
 * If the given @ltb already has the correct size, reuse it. Otherwise if
 * its non-NULL, free it. Then allocate a new one of the correct size.
 * Notify the VIOS either way since we may now be working with a new VIOS.
 *
 * Allocating larger chunks of memory during resets, specially LPM or under
 * low memory situations can cause resets to fail/timeout and for LPAR to
 * lose connectivity. So hold onto the LTB even if we fail to communicate
 * with the VIOS and reuse it on next open. Free LTB when adapter is closed.
 *
 * Return: 0 if we were able to allocate the LTB and notify the VIOS and
 *    a negative value otherwise.
 */

static int alloc_long_term_buff(struct ibmvnic_adapter *adapter,
    struct ibmvnic_long_term_buff *ltb, int size)
{
 struct device *dev = &adapter->vdev->dev;
 u64 prev = 0;
 int rc;

 if (!reuse_ltb(ltb, size)) {
  dev_dbg(dev,
   "LTB size changed from 0x%llx to 0x%x, reallocating\n",
    ltb->size, size);
  prev = ltb->size;
  free_long_term_buff(adapter, ltb);
 }

 if (ltb->buff) {
  dev_dbg(dev, "Reusing LTB [map %d, size 0x%llx]\n",
   ltb->map_id, ltb->size);
 } else {
  ltb->buff = dma_alloc_coherent(dev, size, <b->addr,
            GFP_KERNEL);
  if (!ltb->buff) {
   dev_err(dev, "Couldn't alloc long term buffer\n");
   return -ENOMEM;
  }
  ltb->size = size;

  ltb->map_id = find_first_zero_bit(adapter->map_ids,
        MAX_MAP_ID);
  bitmap_set(adapter->map_ids, ltb->map_id, 1);

  dev_dbg(dev,
   "Allocated new LTB [map %d, size 0x%llx was 0x%llx]\n",
    ltb->map_id, ltb->size, prev);
 }

 /* Ensure ltb is zeroed - specially when reusing it. */
 memset(ltb->buff, 0, ltb->size);

 mutex_lock(&adapter->fw_lock);
 adapter->fw_done_rc = 0;
 reinit_completion(&adapter->fw_done);

 rc = send_request_map(adapter, ltb->addr, ltb->size, ltb->map_id);
 if (rc) {
  dev_err(dev, "send_request_map failed, rc = %d\n", rc);
  goto out;
 }

 rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000);
 if (rc) {
  dev_err(dev, "LTB map request aborted or timed out, rc = %d\n",
   rc);
  goto out;
 }

 if (adapter->fw_done_rc) {
  dev_err(dev, "Couldn't map LTB, rc = %d\n",
   adapter->fw_done_rc);
  rc = -EIO;
  goto out;
 }
 rc = 0;
out:
 /* don't free LTB on communication error - see function header */
 mutex_unlock(&adapter->fw_lock);
 return rc;
}

static void free_long_term_buff(struct ibmvnic_adapter *adapter,
    struct ibmvnic_long_term_buff *ltb)
{
 struct device *dev = &adapter->vdev->dev;

 if (!ltb->buff)
  return;

 /* VIOS automatically unmaps the long term buffer at remote
 * end for the following resets:
 * FAILOVER, MOBILITY, TIMEOUT.
 */

 if (adapter->reset_reason != VNIC_RESET_FAILOVER &&
     adapter->reset_reason != VNIC_RESET_MOBILITY &&
     adapter->reset_reason != VNIC_RESET_TIMEOUT)
  send_request_unmap(adapter, ltb->map_id);

 dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr);

 ltb->buff = NULL;
 /* mark this map_id free */
 bitmap_clear(adapter->map_ids, ltb->map_id, 1);
 ltb->map_id = 0;
}

/**
 * free_ltb_set - free the given set of long term buffers (LTBS)
 * @adapter: The ibmvnic adapter containing this ltb set
 * @ltb_set: The ltb_set to be freed
 *
 * Free the set of LTBs in the given set.
 */


static void free_ltb_set(struct ibmvnic_adapter *adapter,
    struct ibmvnic_ltb_set *ltb_set)
{
 int i;

 for (i = 0; i < ltb_set->num_ltbs; i++)
  free_long_term_buff(adapter, <b_set->ltbs[i]);

 kfree(ltb_set->ltbs);
 ltb_set->ltbs = NULL;
 ltb_set->num_ltbs = 0;
}

/**
 * alloc_ltb_set() - Allocate a set of long term buffers (LTBs)
 *
 * @adapter: ibmvnic adapter associated to the LTB
 * @ltb_set: container object for the set of LTBs
 * @num_buffs: Number of buffers in the LTB
 * @buff_size: Size of each buffer in the LTB
 *
 * Allocate a set of LTBs to accommodate @num_buffs buffers of @buff_size
 * each. We currently cap size each LTB to IBMVNIC_ONE_LTB_SIZE. If the
 * new set of LTBs have fewer LTBs than the old set, free the excess LTBs.
 * If new set needs more than in old set, allocate the remaining ones.
 * Try and reuse as many LTBs as possible and avoid reallocation.
 *
 * Any changes to this allocation strategy must be reflected in
 * map_rxpool_buff_to_ltb() and map_txpool_buff_to_ltb().
 */

static int alloc_ltb_set(struct ibmvnic_adapter *adapter,
    struct ibmvnic_ltb_set *ltb_set, int num_buffs,
    int buff_size)
{
 struct device *dev = &adapter->vdev->dev;
 struct ibmvnic_ltb_set old_set;
 struct ibmvnic_ltb_set new_set;
 int rem_size;
 int tot_size;  /* size of all ltbs */
 int ltb_size;  /* size of one ltb */
 int nltbs;
 int rc;
 int n;
 int i;

 dev_dbg(dev, "%s() num_buffs %d, buff_size %d\n", __func__, num_buffs,
  buff_size);

 ltb_size = rounddown(IBMVNIC_ONE_LTB_SIZE, buff_size);
 tot_size = num_buffs * buff_size;

 if (ltb_size > tot_size)
  ltb_size = tot_size;

 nltbs = tot_size / ltb_size;
 if (tot_size % ltb_size)
  nltbs++;

 old_set = *ltb_set;

 if (old_set.num_ltbs == nltbs) {
  new_set = old_set;
 } else {
  int tmp = nltbs * sizeof(struct ibmvnic_long_term_buff);

  new_set.ltbs = kzalloc(tmp, GFP_KERNEL);
  if (!new_set.ltbs)
   return -ENOMEM;

  new_set.num_ltbs = nltbs;

  /* Free any excess ltbs in old set */
  for (i = new_set.num_ltbs; i < old_set.num_ltbs; i++)
   free_long_term_buff(adapter, &old_set.ltbs[i]);

  /* Copy remaining ltbs to new set. All LTBs except the
 * last one are of the same size. alloc_long_term_buff()
 * will realloc if the size changes.
 */

  n = min(old_set.num_ltbs, new_set.num_ltbs);
  for (i = 0; i < n; i++)
   new_set.ltbs[i] = old_set.ltbs[i];

  /* Any additional ltbs in new set will have NULL ltbs for
 * now and will be allocated in alloc_long_term_buff().
 */


  /* We no longer need the old_set so free it. Note that we
 * may have reused some ltbs from old set and freed excess
 * ltbs above. So we only need to free the container now
 * not the LTBs themselves. (i.e. dont free_ltb_set()!)
 */

  kfree(old_set.ltbs);
  old_set.ltbs = NULL;
  old_set.num_ltbs = 0;

  /* Install the new set. If allocations fail below, we will
 * retry later and know what size LTBs we need.
 */

  *ltb_set = new_set;
 }

 i = 0;
 rem_size = tot_size;
 while (rem_size) {
  if (ltb_size > rem_size)
   ltb_size = rem_size;

  rem_size -= ltb_size;

  rc = alloc_long_term_buff(adapter, &new_set.ltbs[i], ltb_size);
  if (rc)
   goto out;
  i++;
 }

 WARN_ON(i != new_set.num_ltbs);

 return 0;
out:
 /* We may have allocated one/more LTBs before failing and we
 * want to try and reuse on next reset. So don't free ltb set.
 */

 return rc;
}

/**
 * map_rxpool_buf_to_ltb - Map given rxpool buffer to offset in an LTB.
 * @rxpool: The receive buffer pool containing buffer
 * @bufidx: Index of buffer in rxpool
 * @ltbp: (Output) pointer to the long term buffer containing the buffer
 * @offset: (Output) offset of buffer in the LTB from @ltbp
 *
 * Map the given buffer identified by [rxpool, bufidx] to an LTB in the
 * pool and its corresponding offset. Assume for now that each LTB is of
 * different size but could possibly be optimized based on the allocation
 * strategy in alloc_ltb_set().
 */

static void map_rxpool_buf_to_ltb(struct ibmvnic_rx_pool *rxpool,
      unsigned int bufidx,
      struct ibmvnic_long_term_buff **ltbp,
      unsigned int *offset)
{
 struct ibmvnic_long_term_buff *ltb;
 int nbufs; /* # of buffers in one ltb */
 int i;

 WARN_ON(bufidx >= rxpool->size);

 for (i = 0; i < rxpool->ltb_set.num_ltbs; i++) {
  ltb = &rxpool->ltb_set.ltbs[i];
  nbufs = ltb->size / rxpool->buff_size;
  if (bufidx < nbufs)
   break;
  bufidx -= nbufs;
 }

 *ltbp = ltb;
 *offset = bufidx * rxpool->buff_size;
}

/**
 * map_txpool_buf_to_ltb - Map given txpool buffer to offset in an LTB.
 * @txpool: The transmit buffer pool containing buffer
 * @bufidx: Index of buffer in txpool
 * @ltbp: (Output) pointer to the long term buffer (LTB) containing the buffer
 * @offset: (Output) offset of buffer in the LTB from @ltbp
 *
 * Map the given buffer identified by [txpool, bufidx] to an LTB in the
 * pool and its corresponding offset.
 */

static void map_txpool_buf_to_ltb(struct ibmvnic_tx_pool *txpool,
      unsigned int bufidx,
      struct ibmvnic_long_term_buff **ltbp,
      unsigned int *offset)
{
 struct ibmvnic_long_term_buff *ltb;
 int nbufs; /* # of buffers in one ltb */
 int i;

 WARN_ON_ONCE(bufidx >= txpool->num_buffers);

 for (i = 0; i < txpool->ltb_set.num_ltbs; i++) {
  ltb = &txpool->ltb_set.ltbs[i];
  nbufs = ltb->size / txpool->buf_size;
  if (bufidx < nbufs)
   break;
  bufidx -= nbufs;
 }

 *ltbp = ltb;
 *offset = bufidx * txpool->buf_size;
}

static void deactivate_rx_pools(struct ibmvnic_adapter *adapter)
{
 int i;

 for (i = 0; i < adapter->num_active_rx_pools; i++)
  adapter->rx_pool[i].active = 0;
}

static void replenish_rx_pool(struct ibmvnic_adapter *adapter,
         struct ibmvnic_rx_pool *pool)
{
 int count = pool->size - atomic_read(&pool->available);
 u64 handle = adapter->rx_scrq[pool->index]->handle;
 struct device *dev = &adapter->vdev->dev;
 struct ibmvnic_ind_xmit_queue *ind_bufp;
 struct ibmvnic_sub_crq_queue *rx_scrq;
 struct ibmvnic_long_term_buff *ltb;
 union sub_crq *sub_crq;
 int buffers_added = 0;
 unsigned long lpar_rc;
 struct sk_buff *skb;
 unsigned int offset;
 dma_addr_t dma_addr;
 unsigned char *dst;
 int shift = 0;
 int bufidx;
 int i;

 if (!pool->active)
  return;

 rx_scrq = adapter->rx_scrq[pool->index];
 ind_bufp = &rx_scrq->ind_buf;

 /* netdev_skb_alloc() could have failed after we saved a few skbs
 * in the indir_buf and we would not have sent them to VIOS yet.
 * To account for them, start the loop at ind_bufp->index rather
 * than 0. If we pushed all the skbs to VIOS, ind_bufp->index will
 * be 0.
 */

 for (i = ind_bufp->index; i < count; ++i) {
  bufidx = pool->free_map[pool->next_free];

  /* We maybe reusing the skb from earlier resets. Allocate
 * only if necessary. But since the LTB may have changed
 * during reset (see init_rx_pools()), update LTB below
 * even if reusing skb.
 */

  skb = pool->rx_buff[bufidx].skb;
  if (!skb) {
   skb = netdev_alloc_skb(adapter->netdev,
            pool->buff_size);
   if (!skb) {
    dev_err(dev, "Couldn't replenish rx buff\n");
    adapter->replenish_no_mem++;
    break;
   }
  }

  pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP;
  pool->next_free = (pool->next_free + 1) % pool->size;

  /* Copy the skb to the long term mapped DMA buffer */
  map_rxpool_buf_to_ltb(pool, bufidx, <b, &offset);
  dst = ltb->buff + offset;
  memset(dst, 0, pool->buff_size);
  dma_addr = ltb->addr + offset;

  /* add the skb to an rx_buff in the pool */
  pool->rx_buff[bufidx].data = dst;
  pool->rx_buff[bufidx].dma = dma_addr;
  pool->rx_buff[bufidx].skb = skb;
  pool->rx_buff[bufidx].pool_index = pool->index;
  pool->rx_buff[bufidx].size = pool->buff_size;

  /* queue the rx_buff for the next send_subcrq_indirect */
  sub_crq = &ind_bufp->indir_arr[ind_bufp->index++];
  memset(sub_crq, 0, sizeof(*sub_crq));
  sub_crq->rx_add.first = IBMVNIC_CRQ_CMD;
  sub_crq->rx_add.correlator =
      cpu_to_be64((u64)&pool->rx_buff[bufidx]);
  sub_crq->rx_add.ioba = cpu_to_be32(dma_addr);
  sub_crq->rx_add.map_id = ltb->map_id;

  /* The length field of the sCRQ is defined to be 24 bits so the
 * buffer size needs to be left shifted by a byte before it is
 * converted to big endian to prevent the last byte from being
 * truncated.
 */

#ifdef __LITTLE_ENDIAN__
  shift = 8;
#endif
  sub_crq->rx_add.len = cpu_to_be32(pool->buff_size << shift);

  /* if send_subcrq_indirect queue is full, flush to VIOS */
  if (ind_bufp->index == IBMVNIC_MAX_IND_DESCS ||
      i == count - 1) {
   lpar_rc =
    send_subcrq_indirect(adapter, handle,
           (u64)ind_bufp->indir_dma,
           (u64)ind_bufp->index);
   if (lpar_rc != H_SUCCESS)
    goto failure;
   buffers_added += ind_bufp->index;
   adapter->replenish_add_buff_success += ind_bufp->index;
   ind_bufp->index = 0;
  }
 }
 atomic_add(buffers_added, &pool->available);
 return;

failure:
 if (lpar_rc != H_PARAMETER && lpar_rc != H_CLOSED)
  dev_err_ratelimited(dev, "rx: replenish packet buffer failed\n");
 for (i = ind_bufp->index - 1; i >= 0; --i) {
  struct ibmvnic_rx_buff *rx_buff;

  pool->next_free = pool->next_free == 0 ?
      pool->size - 1 : pool->next_free - 1;
  sub_crq = &ind_bufp->indir_arr[i];
  rx_buff = (struct ibmvnic_rx_buff *)
    be64_to_cpu(sub_crq->rx_add.correlator);
  bufidx = (int)(rx_buff - pool->rx_buff);
  pool->free_map[pool->next_free] = bufidx;
  dev_kfree_skb_any(pool->rx_buff[bufidx].skb);
  pool->rx_buff[bufidx].skb = NULL;
 }
 adapter->replenish_add_buff_failure += ind_bufp->index;
 atomic_add(buffers_added, &pool->available);
 ind_bufp->index = 0;
 if (lpar_rc == H_CLOSED || adapter->failover_pending) {
  /* Disable buffer pool replenishment and report carrier off if
 * queue is closed or pending failover.
 * Firmware guarantees that a signal will be sent to the
 * driver, triggering a reset.
 */

  deactivate_rx_pools(adapter);
  netif_carrier_off(adapter->netdev);
 }
}

static void replenish_pools(struct ibmvnic_adapter *adapter)
{
 int i;

 adapter->replenish_task_cycles++;
 for (i = 0; i < adapter->num_active_rx_pools; i++) {
  if (adapter->rx_pool[i].active)
   replenish_rx_pool(adapter, &adapter->rx_pool[i]);
 }

 netdev_dbg(adapter->netdev, "Replenished %d pools\n", i);
}

static void release_stats_buffers(struct ibmvnic_adapter *adapter)
{
 kfree(adapter->tx_stats_buffers);
 kfree(adapter->rx_stats_buffers);
 adapter->tx_stats_buffers = NULL;
 adapter->rx_stats_buffers = NULL;
}

static int init_stats_buffers(struct ibmvnic_adapter *adapter)
{
 adapter->tx_stats_buffers =
    kcalloc(IBMVNIC_MAX_QUEUES,
     sizeof(struct ibmvnic_tx_queue_stats),
     GFP_KERNEL);
 if (!adapter->tx_stats_buffers)
  return -ENOMEM;

 adapter->rx_stats_buffers =
    kcalloc(IBMVNIC_MAX_QUEUES,
     sizeof(struct ibmvnic_rx_queue_stats),
     GFP_KERNEL);
 if (!adapter->rx_stats_buffers)
  return -ENOMEM;

 return 0;
}

static void release_stats_token(struct ibmvnic_adapter *adapter)
{
 struct device *dev = &adapter->vdev->dev;

 if (!adapter->stats_token)
  return;

 dma_unmap_single(dev, adapter->stats_token,
    sizeof(struct ibmvnic_statistics),
    DMA_FROM_DEVICE);
 adapter->stats_token = 0;
}

static int init_stats_token(struct ibmvnic_adapter *adapter)
{
 struct device *dev = &adapter->vdev->dev;
 dma_addr_t stok;
 int rc;

 stok = dma_map_single(dev, &adapter->stats,
         sizeof(struct ibmvnic_statistics),
         DMA_FROM_DEVICE);
 rc = dma_mapping_error(dev, stok);
 if (rc) {
  dev_err(dev, "Couldn't map stats buffer, rc = %d\n", rc);
  return rc;
 }

 adapter->stats_token = stok;
 netdev_dbg(adapter->netdev, "Stats token initialized (%llx)\n", stok);
 return 0;
}

/**
 * release_rx_pools() - Release any rx pools attached to @adapter.
 * @adapter: ibmvnic adapter
 *
 * Safe to call this multiple times - even if no pools are attached.
 */

static void release_rx_pools(struct ibmvnic_adapter *adapter)
{
 struct ibmvnic_rx_pool *rx_pool;
 int i, j;

 if (!adapter->rx_pool)
  return;

 for (i = 0; i < adapter->num_active_rx_pools; i++) {
  rx_pool = &adapter->rx_pool[i];

  netdev_dbg(adapter->netdev, "Releasing rx_pool[%d]\n", i);

  kfree(rx_pool->free_map);

  free_ltb_set(adapter, &rx_pool->ltb_set);

  if (!rx_pool->rx_buff)
   continue;

  for (j = 0; j < rx_pool->size; j++) {
   if (rx_pool->rx_buff[j].skb) {
    dev_kfree_skb_any(rx_pool->rx_buff[j].skb);
    rx_pool->rx_buff[j].skb = NULL;
   }
  }

  kfree(rx_pool->rx_buff);
 }

 kfree(adapter->rx_pool);
 adapter->rx_pool = NULL;
 adapter->num_active_rx_pools = 0;
 adapter->prev_rx_pool_size = 0;
}

/**
 * reuse_rx_pools() - Check if the existing rx pools can be reused.
 * @adapter: ibmvnic adapter
 *
 * Check if the existing rx pools in the adapter can be reused. The
 * pools can be reused if the pool parameters (number of pools,
 * number of buffers in the pool and size of each buffer) have not
 * changed.
 *
 * NOTE: This assumes that all pools have the same number of buffers
 *       which is the case currently. If that changes, we must fix this.
 *
 * Return: true if the rx pools can be reused, false otherwise.
 */

static bool reuse_rx_pools(struct ibmvnic_adapter *adapter)
{
 u64 old_num_pools, new_num_pools;
 u64 old_pool_size, new_pool_size;
 u64 old_buff_size, new_buff_size;

 if (!adapter->rx_pool)
  return false;

 old_num_pools = adapter->num_active_rx_pools;
 new_num_pools = adapter->req_rx_queues;

 old_pool_size = adapter->prev_rx_pool_size;
 new_pool_size = adapter->req_rx_add_entries_per_subcrq;

 old_buff_size = adapter->prev_rx_buf_sz;
 new_buff_size = adapter->cur_rx_buf_sz;

 if (old_buff_size != new_buff_size ||
     old_num_pools != new_num_pools ||
     old_pool_size != new_pool_size)
  return false;

 return true;
}

/**
 * init_rx_pools(): Initialize the set of receiver pools in the adapter.
 * @netdev: net device associated with the vnic interface
 *
 * Initialize the set of receiver pools in the ibmvnic adapter associated
 * with the net_device @netdev. If possible, reuse the existing rx pools.
 * Otherwise free any existing pools and  allocate a new set of pools
 * before initializing them.
 *
 * Return: 0 on success and negative value on error.
 */

static int init_rx_pools(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 struct device *dev = &adapter->vdev->dev;
 struct ibmvnic_rx_pool *rx_pool;
 u64 num_pools;
 u64 pool_size;  /* # of buffers in one pool */
 u64 buff_size;
 int i, j, rc;

 pool_size = adapter->req_rx_add_entries_per_subcrq;
 num_pools = adapter->req_rx_queues;
 buff_size = adapter->cur_rx_buf_sz;

 if (reuse_rx_pools(adapter)) {
  dev_dbg(dev, "Reusing rx pools\n");
  goto update_ltb;
 }

 /* Allocate/populate the pools. */
 release_rx_pools(adapter);

 adapter->rx_pool = kcalloc(num_pools,
       sizeof(struct ibmvnic_rx_pool),
       GFP_KERNEL);
 if (!adapter->rx_pool) {
  dev_err(dev, "Failed to allocate rx pools\n");
  return -ENOMEM;
 }

 /* Set num_active_rx_pools early. If we fail below after partial
 * allocation, release_rx_pools() will know how many to look for.
 */

 adapter->num_active_rx_pools = num_pools;

 for (i = 0; i < num_pools; i++) {
  rx_pool = &adapter->rx_pool[i];

  netdev_dbg(adapter->netdev,
      "Initializing rx_pool[%d], %lld buffs, %lld bytes each\n",
      i, pool_size, buff_size);

  rx_pool->size = pool_size;
  rx_pool->index = i;
  rx_pool->buff_size = ALIGN(buff_size, L1_CACHE_BYTES);

  rx_pool->free_map = kcalloc(rx_pool->size, sizeof(int),
         GFP_KERNEL);
  if (!rx_pool->free_map) {
   dev_err(dev, "Couldn't alloc free_map %d\n", i);
   rc = -ENOMEM;
   goto out_release;
  }

  rx_pool->rx_buff = kcalloc(rx_pool->size,
        sizeof(struct ibmvnic_rx_buff),
        GFP_KERNEL);
  if (!rx_pool->rx_buff) {
   dev_err(dev, "Couldn't alloc rx buffers\n");
   rc = -ENOMEM;
   goto out_release;
  }
 }

 adapter->prev_rx_pool_size = pool_size;
 adapter->prev_rx_buf_sz = adapter->cur_rx_buf_sz;

update_ltb:
 for (i = 0; i < num_pools; i++) {
  rx_pool = &adapter->rx_pool[i];
  dev_dbg(dev, "Updating LTB for rx pool %d [%d, %d]\n",
   i, rx_pool->size, rx_pool->buff_size);

  rc = alloc_ltb_set(adapter, &rx_pool->ltb_set,
       rx_pool->size, rx_pool->buff_size);
  if (rc)
   goto out;

  for (j = 0; j < rx_pool->size; ++j) {
   struct ibmvnic_rx_buff *rx_buff;

   rx_pool->free_map[j] = j;

   /* NOTE: Don't clear rx_buff->skb here - will leak
 * memory! replenish_rx_pool() will reuse skbs or
 * allocate as necessary.
 */

   rx_buff = &rx_pool->rx_buff[j];
   rx_buff->dma = 0;
   rx_buff->data = 0;
   rx_buff->size = 0;
   rx_buff->pool_index = 0;
  }

  /* Mark pool "empty" so replenish_rx_pools() will
 * update the LTB info for each buffer
 */

  atomic_set(&rx_pool->available, 0);
  rx_pool->next_alloc = 0;
  rx_pool->next_free = 0;
  /* replenish_rx_pool() may have called deactivate_rx_pools()
 * on failover. Ensure pool is active now.
 */

  rx_pool->active = 1;
 }
 return 0;
out_release:
 release_rx_pools(adapter);
out:
 /* We failed to allocate one or more LTBs or map them on the VIOS.
 * Hold onto the pools and any LTBs that we did allocate/map.
 */

 return rc;
}

static void release_vpd_data(struct ibmvnic_adapter *adapter)
{
 if (!adapter->vpd)
  return;

 kfree(adapter->vpd->buff);
 kfree(adapter->vpd);

 adapter->vpd = NULL;
}

static void release_one_tx_pool(struct ibmvnic_adapter *adapter,
    struct ibmvnic_tx_pool *tx_pool)
{
 kfree(tx_pool->tx_buff);
 kfree(tx_pool->free_map);
 free_ltb_set(adapter, &tx_pool->ltb_set);
}

/**
 * release_tx_pools() - Release any tx pools attached to @adapter.
 * @adapter: ibmvnic adapter
 *
 * Safe to call this multiple times - even if no pools are attached.
 */

static void release_tx_pools(struct ibmvnic_adapter *adapter)
{
 int i;

 /* init_tx_pools() ensures that ->tx_pool and ->tso_pool are
 * both NULL or both non-NULL. So we only need to check one.
 */

 if (!adapter->tx_pool)
  return;

 for (i = 0; i < adapter->num_active_tx_pools; i++) {
  release_one_tx_pool(adapter, &adapter->tx_pool[i]);
  release_one_tx_pool(adapter, &adapter->tso_pool[i]);
 }

 kfree(adapter->tx_pool);
 adapter->tx_pool = NULL;
 kfree(adapter->tso_pool);
 adapter->tso_pool = NULL;
 adapter->num_active_tx_pools = 0;
 adapter->prev_tx_pool_size = 0;
}

static int init_one_tx_pool(struct net_device *netdev,
       struct ibmvnic_tx_pool *tx_pool,
       int pool_size, int buf_size)
{
 int i;

 tx_pool->tx_buff = kcalloc(pool_size,
       sizeof(struct ibmvnic_tx_buff),
       GFP_KERNEL);
 if (!tx_pool->tx_buff)
  return -ENOMEM;

 tx_pool->free_map = kcalloc(pool_size, sizeof(int), GFP_KERNEL);
 if (!tx_pool->free_map) {
  kfree(tx_pool->tx_buff);
  tx_pool->tx_buff = NULL;
  return -ENOMEM;
 }

 for (i = 0; i < pool_size; i++)
  tx_pool->free_map[i] = i;

 tx_pool->consumer_index = 0;
 tx_pool->producer_index = 0;
 tx_pool->num_buffers = pool_size;
 tx_pool->buf_size = buf_size;

 return 0;
}

/**
 * reuse_tx_pools() - Check if the existing tx pools can be reused.
 * @adapter: ibmvnic adapter
 *
 * Check if the existing tx pools in the adapter can be reused. The
 * pools can be reused if the pool parameters (number of pools,
 * number of buffers in the pool and mtu) have not changed.
 *
 * NOTE: This assumes that all pools have the same number of buffers
 *       which is the case currently. If that changes, we must fix this.
 *
 * Return: true if the tx pools can be reused, false otherwise.
 */

static bool reuse_tx_pools(struct ibmvnic_adapter *adapter)
{
 u64 old_num_pools, new_num_pools;
 u64 old_pool_size, new_pool_size;
 u64 old_mtu, new_mtu;

 if (!adapter->tx_pool)
  return false;

 old_num_pools = adapter->num_active_tx_pools;
 new_num_pools = adapter->num_active_tx_scrqs;
 old_pool_size = adapter->prev_tx_pool_size;
 new_pool_size = adapter->req_tx_entries_per_subcrq;
 old_mtu = adapter->prev_mtu;
 new_mtu = adapter->req_mtu;

 if (old_mtu != new_mtu ||
     old_num_pools != new_num_pools ||
     old_pool_size != new_pool_size)
  return false;

 return true;
}

/**
 * init_tx_pools(): Initialize the set of transmit pools in the adapter.
 * @netdev: net device associated with the vnic interface
 *
 * Initialize the set of transmit pools in the ibmvnic adapter associated
 * with the net_device @netdev. If possible, reuse the existing tx pools.
 * Otherwise free any existing pools and  allocate a new set of pools
 * before initializing them.
 *
 * Return: 0 on success and negative value on error.
 */

static int init_tx_pools(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 struct device *dev = &adapter->vdev->dev;
 int num_pools;
 u64 pool_size;  /* # of buffers in pool */
 u64 buff_size;
 int i, j, rc;

 num_pools = adapter->req_tx_queues;

 /* We must notify the VIOS about the LTB on all resets - but we only
 * need to alloc/populate pools if either the number of buffers or
 * size of each buffer in the pool has changed.
 */

 if (reuse_tx_pools(adapter)) {
  netdev_dbg(netdev, "Reusing tx pools\n");
  goto update_ltb;
 }

 /* Allocate/populate the pools. */
 release_tx_pools(adapter);

 pool_size = adapter->req_tx_entries_per_subcrq;
 num_pools = adapter->num_active_tx_scrqs;

 adapter->tx_pool = kcalloc(num_pools,
       sizeof(struct ibmvnic_tx_pool), GFP_KERNEL);
 if (!adapter->tx_pool)
  return -ENOMEM;

 adapter->tso_pool = kcalloc(num_pools,
        sizeof(struct ibmvnic_tx_pool), GFP_KERNEL);
 /* To simplify release_tx_pools() ensure that ->tx_pool and
 * ->tso_pool are either both NULL or both non-NULL.
 */

 if (!adapter->tso_pool) {
  kfree(adapter->tx_pool);
  adapter->tx_pool = NULL;
  return -ENOMEM;
 }

 /* Set num_active_tx_pools early. If we fail below after partial
 * allocation, release_tx_pools() will know how many to look for.
 */

 adapter->num_active_tx_pools = num_pools;

 buff_size = adapter->req_mtu + VLAN_HLEN;
 buff_size = ALIGN(buff_size, L1_CACHE_BYTES);

 for (i = 0; i < num_pools; i++) {
  dev_dbg(dev, "Init tx pool %d [%llu, %llu]\n",
   i, adapter->req_tx_entries_per_subcrq, buff_size);

  rc = init_one_tx_pool(netdev, &adapter->tx_pool[i],
          pool_size, buff_size);
  if (rc)
   goto out_release;

  rc = init_one_tx_pool(netdev, &adapter->tso_pool[i],
          IBMVNIC_TSO_BUFS,
          IBMVNIC_TSO_BUF_SZ);
  if (rc)
   goto out_release;
 }

 adapter->prev_tx_pool_size = pool_size;
 adapter->prev_mtu = adapter->req_mtu;

update_ltb:
 /* NOTE: All tx_pools have the same number of buffers (which is
 *       same as pool_size). All tso_pools have IBMVNIC_TSO_BUFS
 *       buffers (see calls init_one_tx_pool() for these).
 *       For consistency, we use tx_pool->num_buffers and
 *       tso_pool->num_buffers below.
 */

 rc = -1;
 for (i = 0; i < num_pools; i++) {
  struct ibmvnic_tx_pool *tso_pool;
  struct ibmvnic_tx_pool *tx_pool;

  tx_pool = &adapter->tx_pool[i];

  dev_dbg(dev, "Updating LTB for tx pool %d [%d, %d]\n",
   i, tx_pool->num_buffers, tx_pool->buf_size);

  rc = alloc_ltb_set(adapter, &tx_pool->ltb_set,
       tx_pool->num_buffers, tx_pool->buf_size);
  if (rc)
   goto out;

  tx_pool->consumer_index = 0;
  tx_pool->producer_index = 0;

  for (j = 0; j < tx_pool->num_buffers; j++)
   tx_pool->free_map[j] = j;

  tso_pool = &adapter->tso_pool[i];

  dev_dbg(dev, "Updating LTB for tso pool %d [%d, %d]\n",
   i, tso_pool->num_buffers, tso_pool->buf_size);

  rc = alloc_ltb_set(adapter, &tso_pool->ltb_set,
       tso_pool->num_buffers, tso_pool->buf_size);
  if (rc)
   goto out;

  tso_pool->consumer_index = 0;
  tso_pool->producer_index = 0;

  for (j = 0; j < tso_pool->num_buffers; j++)
   tso_pool->free_map[j] = j;
 }

 return 0;
out_release:
 release_tx_pools(adapter);
out:
 /* We failed to allocate one or more LTBs or map them on the VIOS.
 * Hold onto the pools and any LTBs that we did allocate/map.
 */

 return rc;
}

static void ibmvnic_napi_enable(struct ibmvnic_adapter *adapter)
{
 int i;

 if (adapter->napi_enabled)
  return;

 for (i = 0; i < adapter->req_rx_queues; i++)
  napi_enable(&adapter->napi[i]);

 adapter->napi_enabled = true;
}

static void ibmvnic_napi_disable(struct ibmvnic_adapter *adapter)
{
 int i;

 if (!adapter->napi_enabled)
  return;

 for (i = 0; i < adapter->req_rx_queues; i++) {
  netdev_dbg(adapter->netdev, "Disabling napi[%d]\n", i);
  napi_disable(&adapter->napi[i]);
 }

 adapter->napi_enabled = false;
}

static int init_napi(struct ibmvnic_adapter *adapter)
{
 int i;

 adapter->napi = kcalloc(adapter->req_rx_queues,
    sizeof(struct napi_struct), GFP_KERNEL);
 if (!adapter->napi)
  return -ENOMEM;

 for (i = 0; i < adapter->req_rx_queues; i++) {
  netdev_dbg(adapter->netdev, "Adding napi[%d]\n", i);
  netif_napi_add(adapter->netdev, &adapter->napi[i],
          ibmvnic_poll);
 }

 adapter->num_active_rx_napi = adapter->req_rx_queues;
 return 0;
}

static void release_napi(struct ibmvnic_adapter *adapter)
{
 int i;

 if (!adapter->napi)
  return;

 for (i = 0; i < adapter->num_active_rx_napi; i++) {
  netdev_dbg(adapter->netdev, "Releasing napi[%d]\n", i);
  netif_napi_del(&adapter->napi[i]);
 }

 kfree(adapter->napi);
 adapter->napi = NULL;
 adapter->num_active_rx_napi = 0;
 adapter->napi_enabled = false;
}

static const char *adapter_state_to_string(enum vnic_state state)
{
 switch (state) {
 case VNIC_PROBING:
  return "PROBING";
 case VNIC_PROBED:
  return "PROBED";
 case VNIC_OPENING:
  return "OPENING";
 case VNIC_OPEN:
  return "OPEN";
 case VNIC_CLOSING:
  return "CLOSING";
 case VNIC_CLOSED:
  return "CLOSED";
 case VNIC_REMOVING:
  return "REMOVING";
 case VNIC_REMOVED:
  return "REMOVED";
 case VNIC_DOWN:
  return "DOWN";
 }
 return "UNKNOWN";
}

static int ibmvnic_login(struct net_device *netdev)
{
 unsigned long flags, timeout = msecs_to_jiffies(20000);
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 int retry_count = 0;
 int retries = 10;
 bool retry;
 int rc;

 do {
  retry = false;
  if (retry_count > retries) {
   netdev_warn(netdev, "Login attempts exceeded\n");
   return -EACCES;
  }

  adapter->init_done_rc = 0;
  reinit_completion(&adapter->init_done);
  rc = send_login(adapter);
  if (rc)
   return rc;

  if (!wait_for_completion_timeout(&adapter->init_done,
       timeout)) {
   netdev_warn(netdev, "Login timed out\n");
   adapter->login_pending = false;
   goto partial_reset;
  }

  if (adapter->init_done_rc == ABORTED) {
   netdev_warn(netdev, "Login aborted, retrying...\n");
   retry = true;
   adapter->init_done_rc = 0;
   retry_count++;
   /* FW or device may be busy, so
 * wait a bit before retrying login
 */

   msleep(500);
  } else if (adapter->init_done_rc == PARTIALSUCCESS) {
   retry_count++;
   release_sub_crqs(adapter, 1);

   retry = true;
   netdev_dbg(netdev,
       "Received partial success, retrying...\n");
   adapter->init_done_rc = 0;
   reinit_completion(&adapter->init_done);
   send_query_cap(adapter);
   if (!wait_for_completion_timeout(&adapter->init_done,
        timeout)) {
    netdev_warn(netdev,
         "Capabilities query timed out\n");
    return -ETIMEDOUT;
   }

   rc = init_sub_crqs(adapter);
   if (rc) {
    netdev_warn(netdev,
         "SCRQ initialization failed\n");
    return rc;
   }

   rc = init_sub_crq_irqs(adapter);
   if (rc) {
    netdev_warn(netdev,
         "SCRQ irq initialization failed\n");
    return rc;
   }
  /* Default/timeout error handling, reset and start fresh */
  } else if (adapter->init_done_rc) {
   netdev_warn(netdev, "Adapter login failed, init_done_rc = %d\n",
        adapter->init_done_rc);

partial_reset:
   /* adapter login failed, so free any CRQs or sub-CRQs
 * and register again before attempting to login again.
 * If we don't do this then the VIOS may think that
 * we are already logged in and reject any subsequent
 * attempts
 */

   netdev_warn(netdev,
        "Freeing and re-registering CRQs before attempting to login again\n");
   retry = true;
   adapter->init_done_rc = 0;
   release_sub_crqs(adapter, true);
   /* Much of this is similar logic as ibmvnic_probe(),
 * we are essentially re-initializing communication
 * with the server. We really should not run any
 * resets/failovers here because this is already a form
 * of reset and we do not want parallel resets occurring
 */

   do {
    reinit_init_done(adapter);
    /* Clear any failovers we got in the previous
 * pass since we are re-initializing the CRQ
 */

    adapter->failover_pending = false;
    release_crq_queue(adapter);
    /* If we don't sleep here then we risk an
 * unnecessary failover event from the VIOS.
 * This is a known VIOS issue caused by a vnic
 * device freeing and registering a CRQ too
 * quickly.
 */

    msleep(1500);
    /* Avoid any resets, since we are currently
 * resetting.
 */

    spin_lock_irqsave(&adapter->rwi_lock, flags);
    flush_reset_queue(adapter);
    spin_unlock_irqrestore(&adapter->rwi_lock,
             flags);

    rc = init_crq_queue(adapter);
    if (rc) {
     netdev_err(netdev, "login recovery: init CRQ failed %d\n",
         rc);
     return -EIO;
    }

    rc = ibmvnic_reset_init(adapter, false);
    if (rc)
     netdev_err(netdev, "login recovery: Reset init failed %d\n",
         rc);
    /* IBMVNIC_CRQ_INIT will return EAGAIN if it
 * fails, since ibmvnic_reset_init will free
 * irq's in failure, we won't be able to receive
 * new CRQs so we need to keep trying. probe()
 * handles this similarly.
 */

   } while (rc == -EAGAIN && retry_count++ < retries);
  }
 } while (retry);

 __ibmvnic_set_mac(netdev, adapter->mac_addr);

 netdev_dbg(netdev, "[S:%s] Login succeeded\n", adapter_state_to_string(adapter->state));
 return 0;
}

static void release_login_buffer(struct ibmvnic_adapter *adapter)
{
 if (!adapter->login_buf)
  return;

 dma_unmap_single(&adapter->vdev->dev, adapter->login_buf_token,
    adapter->login_buf_sz, DMA_TO_DEVICE);
 kfree(adapter->login_buf);
 adapter->login_buf = NULL;
}

static void release_login_rsp_buffer(struct ibmvnic_adapter *adapter)
{
 if (!adapter->login_rsp_buf)
  return;

 dma_unmap_single(&adapter->vdev->dev, adapter->login_rsp_buf_token,
    adapter->login_rsp_buf_sz, DMA_FROM_DEVICE);
 kfree(adapter->login_rsp_buf);
 adapter->login_rsp_buf = NULL;
}

static void release_resources(struct ibmvnic_adapter *adapter)
{
 release_vpd_data(adapter);

 release_napi(adapter);
 release_login_buffer(adapter);
 release_login_rsp_buffer(adapter);
}

static int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state)
{
 struct net_device *netdev = adapter->netdev;
 unsigned long timeout = msecs_to_jiffies(20000);
 union ibmvnic_crq crq;
 bool resend;
 int rc;

 netdev_dbg(netdev, "setting link state %d\n", link_state);

 memset(&crq, 0, sizeof(crq));
 crq.logical_link_state.first = IBMVNIC_CRQ_CMD;
 crq.logical_link_state.cmd = LOGICAL_LINK_STATE;
 crq.logical_link_state.link_state = link_state;

 do {
  resend = false;

  reinit_completion(&adapter->init_done);
  rc = ibmvnic_send_crq(adapter, &crq);
  if (rc) {
   netdev_err(netdev, "Failed to set link state\n");
   return rc;
  }

  if (!wait_for_completion_timeout(&adapter->init_done,
       timeout)) {
   netdev_err(netdev, "timeout setting link state\n");
   return -ETIMEDOUT;
  }

  if (adapter->init_done_rc == PARTIALSUCCESS) {
   /* Partuial success, delay and re-send */
   mdelay(1000);
   resend = true;
  } else if (adapter->init_done_rc) {
   netdev_warn(netdev, "Unable to set link state, rc=%d\n",
        adapter->init_done_rc);
   return adapter->init_done_rc;
  }
 } while (resend);

 return 0;
}

static int set_real_num_queues(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 int rc;

 netdev_dbg(netdev, "Setting real tx/rx queues (%llx/%llx)\n",
     adapter->req_tx_queues, adapter->req_rx_queues);

 rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues);
 if (rc) {
  netdev_err(netdev, "failed to set the number of tx queues\n");
  return rc;
 }

 rc = netif_set_real_num_rx_queues(netdev, adapter->req_rx_queues);
 if (rc)
  netdev_err(netdev, "failed to set the number of rx queues\n");

 return rc;
}

static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter)
{
 struct device *dev = &adapter->vdev->dev;
 union ibmvnic_crq crq;
 int len = 0;
 int rc;

 if (adapter->vpd->buff)
  len = adapter->vpd->len;

 mutex_lock(&adapter->fw_lock);
 adapter->fw_done_rc = 0;
 reinit_completion(&adapter->fw_done);

 crq.get_vpd_size.first = IBMVNIC_CRQ_CMD;
 crq.get_vpd_size.cmd = GET_VPD_SIZE;
 rc = ibmvnic_send_crq(adapter, &crq);
 if (rc) {
  mutex_unlock(&adapter->fw_lock);
  return rc;
 }

 rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000);
 if (rc) {
  dev_err(dev, "Could not retrieve VPD size, rc = %d\n", rc);
  mutex_unlock(&adapter->fw_lock);
  return rc;
 }
 mutex_unlock(&adapter->fw_lock);

 if (!adapter->vpd->len)
  return -ENODATA;

 if (!adapter->vpd->buff)
  adapter->vpd->buff = kzalloc(adapter->vpd->len, GFP_KERNEL);
 else if (adapter->vpd->len != len)
  adapter->vpd->buff =
   krealloc(adapter->vpd->buff,
     adapter->vpd->len, GFP_KERNEL);

 if (!adapter->vpd->buff) {
  dev_err(dev, "Could allocate VPD buffer\n");
  return -ENOMEM;
 }

 adapter->vpd->dma_addr =
  dma_map_single(dev, adapter->vpd->buff, adapter->vpd->len,
          DMA_FROM_DEVICE);
 if (dma_mapping_error(dev, adapter->vpd->dma_addr)) {
  dev_err(dev, "Could not map VPD buffer\n");
  kfree(adapter->vpd->buff);
  adapter->vpd->buff = NULL;
  return -ENOMEM;
 }

 mutex_lock(&adapter->fw_lock);
 adapter->fw_done_rc = 0;
 reinit_completion(&adapter->fw_done);

 crq.get_vpd.first = IBMVNIC_CRQ_CMD;
 crq.get_vpd.cmd = GET_VPD;
 crq.get_vpd.ioba = cpu_to_be32(adapter->vpd->dma_addr);
 crq.get_vpd.len = cpu_to_be32((u32)adapter->vpd->len);
 rc = ibmvnic_send_crq(adapter, &crq);
 if (rc) {
  kfree(adapter->vpd->buff);
  adapter->vpd->buff = NULL;
  mutex_unlock(&adapter->fw_lock);
  return rc;
 }

 rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000);
 if (rc) {
  dev_err(dev, "Unable to retrieve VPD, rc = %d\n", rc);
  kfree(adapter->vpd->buff);
  adapter->vpd->buff = NULL;
  mutex_unlock(&adapter->fw_lock);
  return rc;
 }

 mutex_unlock(&adapter->fw_lock);
 return 0;
}

static int init_resources(struct ibmvnic_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 int rc;

 rc = set_real_num_queues(netdev);
 if (rc)
  return rc;

 adapter->vpd = kzalloc(sizeof(*adapter->vpd), GFP_KERNEL);
 if (!adapter->vpd)
  return -ENOMEM;

 /* Vital Product Data (VPD) */
 rc = ibmvnic_get_vpd(adapter);
 if (rc) {
  netdev_err(netdev, "failed to initialize Vital Product Data (VPD)\n");
  return rc;
 }

 rc = init_napi(adapter);
 if (rc)
  return rc;

 send_query_map(adapter);

 rc = init_rx_pools(netdev);
 if (rc)
  return rc;

 rc = init_tx_pools(netdev);
 return rc;
}

static int __ibmvnic_open(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 enum vnic_state prev_state = adapter->state;
 int i, rc;

 adapter->state = VNIC_OPENING;
 replenish_pools(adapter);
 ibmvnic_napi_enable(adapter);

 /* We're ready to receive frames, enable the sub-crq interrupts and
 * set the logical link state to up
 */

 for (i = 0; i < adapter->req_rx_queues; i++) {
  netdev_dbg(netdev, "Enabling rx_scrq[%d] irq\n", i);
  if (prev_state == VNIC_CLOSED)
   enable_irq(adapter->rx_scrq[i]->irq);
  enable_scrq_irq(adapter, adapter->rx_scrq[i]);
 }

 for (i = 0; i < adapter->req_tx_queues; i++) {
  netdev_dbg(netdev, "Enabling tx_scrq[%d] irq\n", i);
  if (prev_state == VNIC_CLOSED)
   enable_irq(adapter->tx_scrq[i]->irq);
  enable_scrq_irq(adapter, adapter->tx_scrq[i]);
  /* netdev_tx_reset_queue will reset dql stats. During NON_FATAL
 * resets, don't reset the stats because there could be batched
 * skb's waiting to be sent. If we reset dql stats, we risk
 * num_completed being greater than num_queued. This will cause
 * a BUG_ON in dql_completed().
 */

  if (adapter->reset_reason != VNIC_RESET_NON_FATAL)
   netdev_tx_reset_queue(netdev_get_tx_queue(netdev, i));
 }

 rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_UP);
 if (rc) {
  ibmvnic_napi_disable(adapter);
  ibmvnic_disable_irqs(adapter);
  return rc;
 }

 adapter->tx_queues_active = true;

 /* Since queues were stopped until now, there shouldn't be any
 * one in ibmvnic_complete_tx() or ibmvnic_xmit() so maybe we
 * don't need the synchronize_rcu()? Leaving it for consistency
 * with setting ->tx_queues_active = false.
 */

 synchronize_rcu();

 netif_tx_start_all_queues(netdev);

 if (prev_state == VNIC_CLOSED) {
  for (i = 0; i < adapter->req_rx_queues; i++)
   napi_schedule(&adapter->napi[i]);
 }

 adapter->state = VNIC_OPEN;
 return rc;
}

static int ibmvnic_open(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 int rc;

 ASSERT_RTNL();

 /* If device failover is pending or we are about to reset, just set
 * device state and return. Device operation will be handled by reset
 * routine.
 *
 * It should be safe to overwrite the adapter->state here. Since
 * we hold the rtnl, either the reset has not actually started or
 * the rtnl got dropped during the set_link_state() in do_reset().
 * In the former case, no one else is changing the state (again we
 * have the rtnl) and in the latter case, do_reset() will detect and
 * honor our setting below.
 */

 if (adapter->failover_pending || (test_bit(0, &adapter->resetting))) {
  netdev_dbg(netdev, "[S:%s FOP:%d] Resetting, deferring open\n",
      adapter_state_to_string(adapter->state),
      adapter->failover_pending);
  adapter->state = VNIC_OPEN;
  rc = 0;
  goto out;
 }

 if (adapter->state != VNIC_CLOSED) {
  rc = ibmvnic_login(netdev);
  if (rc)
   goto out;

  rc = init_resources(adapter);
  if (rc) {
   netdev_err(netdev, "failed to initialize resources\n");
   goto out;
  }
 }

 rc = __ibmvnic_open(netdev);

out:
 /* If open failed and there is a pending failover or in-progress reset,
 * set device state and return. Device operation will be handled by
 * reset routine. See also comments above regarding rtnl.
 */

 if (rc &&
     (adapter->failover_pending || (test_bit(0, &adapter->resetting)))) {
  adapter->state = VNIC_OPEN;
  rc = 0;
 }

 if (rc) {
  release_resources(adapter);
  release_rx_pools(adapter);
  release_tx_pools(adapter);
 }

 return rc;
}

static void clean_rx_pools(struct ibmvnic_adapter *adapter)
{
 struct ibmvnic_rx_pool *rx_pool;
 struct ibmvnic_rx_buff *rx_buff;
 u64 rx_entries;
 int rx_scrqs;
 int i, j;

 if (!adapter->rx_pool)
  return;

 rx_scrqs = adapter->num_active_rx_pools;
 rx_entries = adapter->req_rx_add_entries_per_subcrq;

 /* Free any remaining skbs in the rx buffer pools */
 for (i = 0; i < rx_scrqs; i++) {
  rx_pool = &adapter->rx_pool[i];
  if (!rx_pool || !rx_pool->rx_buff)
   continue;

  netdev_dbg(adapter->netdev, "Cleaning rx_pool[%d]\n", i);
  for (j = 0; j < rx_entries; j++) {
   rx_buff = &rx_pool->rx_buff[j];
   if (rx_buff && rx_buff->skb) {
    dev_kfree_skb_any(rx_buff->skb);
    rx_buff->skb = NULL;
   }
  }
 }
}

static void clean_one_tx_pool(struct ibmvnic_adapter *adapter,
         struct ibmvnic_tx_pool *tx_pool)
{
 struct ibmvnic_tx_buff *tx_buff;
 u64 tx_entries;
 int i;

 if (!tx_pool || !tx_pool->tx_buff)
  return;

 tx_entries = tx_pool->num_buffers;

 for (i = 0; i < tx_entries; i++) {
  tx_buff = &tx_pool->tx_buff[i];
  if (tx_buff && tx_buff->skb) {
   dev_kfree_skb_any(tx_buff->skb);
   tx_buff->skb = NULL;
  }
 }
}

static void clean_tx_pools(struct ibmvnic_adapter *adapter)
{
 int tx_scrqs;
 int i;

 if (!adapter->tx_pool || !adapter->tso_pool)
  return;

 tx_scrqs = adapter->num_active_tx_pools;

 /* Free any remaining skbs in the tx buffer pools */
 for (i = 0; i < tx_scrqs; i++) {
  netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i);
  clean_one_tx_pool(adapter, &adapter->tx_pool[i]);
  clean_one_tx_pool(adapter, &adapter->tso_pool[i]);
 }
}

static void ibmvnic_disable_irqs(struct ibmvnic_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 int i;

 if (adapter->tx_scrq) {
  for (i = 0; i < adapter->req_tx_queues; i++)
   if (adapter->tx_scrq[i]->irq) {
    netdev_dbg(netdev,
        "Disabling tx_scrq[%d] irq\n", i);
    disable_scrq_irq(adapter, adapter->tx_scrq[i]);
    disable_irq(adapter->tx_scrq[i]->irq);
   }
 }

 if (adapter->rx_scrq) {
  for (i = 0; i < adapter->req_rx_queues; i++) {
   if (adapter->rx_scrq[i]->irq) {
    netdev_dbg(netdev,
        "Disabling rx_scrq[%d] irq\n", i);
    disable_scrq_irq(adapter, adapter->rx_scrq[i]);
    disable_irq(adapter->rx_scrq[i]->irq);
   }
  }
 }
}

static void ibmvnic_cleanup(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);

 /* ensure that transmissions are stopped if called by do_reset */

 adapter->tx_queues_active = false;

 /* Ensure complete_tx() and ibmvnic_xmit() see ->tx_queues_active
 * update so they don't restart a queue after we stop it below.
 */

 synchronize_rcu();

 if (test_bit(0, &adapter->resetting))
  netif_tx_disable(netdev);
 else
  netif_tx_stop_all_queues(netdev);

 ibmvnic_napi_disable(adapter);
 ibmvnic_disable_irqs(adapter);
}

static int __ibmvnic_close(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 int rc = 0;

 adapter->state = VNIC_CLOSING;
 rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN);
 adapter->state = VNIC_CLOSED;
 return rc;
}

static int ibmvnic_close(struct net_device *netdev)
{
 struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 int rc;

 netdev_dbg(netdev, "[S:%s FOP:%d FRR:%d] Closing\n",
     adapter_state_to_string(adapter->state),
     adapter->failover_pending,
     adapter->force_reset_recovery);

 /* If device failover is pending, just set device state and return.
 * Device operation will be handled by reset routine.
 */

 if (adapter->failover_pending) {
  adapter->state = VNIC_CLOSED;
  return 0;
 }

 rc = __ibmvnic_close(netdev);
 ibmvnic_cleanup(netdev);
 clean_rx_pools(adapter);
 clean_tx_pools(adapter);

 return rc;
}

/**
 * get_hdr_lens - fills list of L2/L3/L4 hdr lens
 * @hdr_field: bitfield determining needed headers
 * @skb: socket buffer
 * @hdr_len: array of header lengths to be filled
 *
 * Reads hdr_field to determine which headers are needed by firmware.
 * Builds a buffer containing these headers.  Saves individual header
 * lengths and total buffer length to be used to build descriptors.
 *
 * Return: total len of all headers
 */

static int get_hdr_lens(u8 hdr_field, struct sk_buff *skb,
   int *hdr_len)
{
 int len = 0;


 if ((hdr_field >> 6) & 1) {
  hdr_len[0] = skb_mac_header_len(skb);
  len += hdr_len[0];
 }

 if ((hdr_field >> 5) & 1) {
  hdr_len[1] = skb_network_header_len(skb);
  len += hdr_len[1];
 }

 if (!((hdr_field >> 4) & 1))
  return len;

 if (skb->protocol == htons(ETH_P_IP)) {
  if (ip_hdr(skb)->protocol == IPPROTO_TCP)
   hdr_len[2] = tcp_hdrlen(skb);
  else if (ip_hdr(skb)->protocol == IPPROTO_UDP)
   hdr_len[2] = sizeof(struct udphdr);
 } else if (skb->protocol == htons(ETH_P_IPV6)) {
  if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
   hdr_len[2] = tcp_hdrlen(skb);
  else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
   hdr_len[2] = sizeof(struct udphdr);
 }

 return len + hdr_len[2];
}

/**
 * create_hdr_descs - create header and header extension descriptors
 * @hdr_field: bitfield determining needed headers
 * @hdr_data: buffer containing header data
 * @len: length of data buffer
 * @hdr_len: array of individual header lengths
 * @scrq_arr: descriptor array
 *
 * Creates header and, if needed, header extension descriptors and
 * places them in a descriptor array, scrq_arr
 *
 * Return: Number of header descs
 */


static int create_hdr_descs(u8 hdr_field, u8 *hdr_data, int len, int *hdr_len,
       union sub_crq *scrq_arr)
{
 union sub_crq *hdr_desc;
 int tmp_len = len;
 int num_descs = 0;
 u8 *data, *cur;
 int tmp;

 while (tmp_len > 0) {
  cur = hdr_data + len - tmp_len;

  hdr_desc = &scrq_arr[num_descs];
  if (num_descs) {
   data = hdr_desc->hdr_ext.data;
   tmp = tmp_len > 29 ? 29 : tmp_len;
   hdr_desc->hdr_ext.first = IBMVNIC_CRQ_CMD;
   hdr_desc->hdr_ext.type = IBMVNIC_HDR_EXT_DESC;
   hdr_desc->hdr_ext.len = tmp;
  } else {
   data = hdr_desc->hdr.data;
   tmp = tmp_len > 24 ? 24 : tmp_len;
   hdr_desc->hdr.first = IBMVNIC_CRQ_CMD;
   hdr_desc->hdr.type = IBMVNIC_HDR_DESC;
   hdr_desc->hdr.len = tmp;
   hdr_desc->hdr.l2_len = (u8)hdr_len[0];
   hdr_desc->hdr.l3_len = cpu_to_be16((u16)hdr_len[1]);
   hdr_desc->hdr.l4_len = (u8)hdr_len[2];
   hdr_desc->hdr.flag = hdr_field << 1;
  }
  memcpy(data, cur, tmp);
  tmp_len -= tmp;
  num_descs++;
 }

 return num_descs;
}

/**
 * build_hdr_descs_arr - build a header descriptor array
 * @skb: tx socket buffer
 * @indir_arr: indirect array
 * @num_entries: number of descriptors to be sent
 * @hdr_field: bit field determining which headers will be sent
 *
 * This function will build a TX descriptor array with applicable
 * L2/L3/L4 packet header descriptors to be sent by send_subcrq_indirect.
 */


static void build_hdr_descs_arr(struct sk_buff *skb,
    union sub_crq *indir_arr,
    int *num_entries, u8 hdr_field)
{
 int hdr_len[3] = {0, 0, 0};
 int tot_len;

 tot_len = get_hdr_lens(hdr_field, skb, hdr_len);
 *num_entries += create_hdr_descs(hdr_field, skb_mac_header(skb),
      tot_len, hdr_len, indir_arr + 1);
}

static int ibmvnic_xmit_workarounds(struct sk_buff *skb,
        struct net_device *netdev)
{
 /* For some backing devices, mishandling of small packets
 * can result in a loss of connection or TX stall. Device
 * architects recommend that no packet should be smaller
 * than the minimum MTU value provided to the driver, so
 * pad any packets to that length
 */

 if (skb->len < netdev->min_mtu)
  return skb_put_padto(skb, netdev->min_mtu);

 return 0;
}

static void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter,
      struct ibmvnic_sub_crq_queue *tx_scrq)
{
 struct ibmvnic_ind_xmit_queue *ind_bufp;
 struct ibmvnic_tx_buff *tx_buff;
 struct ibmvnic_tx_pool *tx_pool;
 union sub_crq tx_scrq_entry;
 int queue_num;
 int entries;
 int index;
 int i;

 ind_bufp = &tx_scrq->ind_buf;
 entries = (u64)ind_bufp->index;
 queue_num = tx_scrq->pool_index;

 for (i = entries - 1; i >= 0; --i) {
  tx_scrq_entry = ind_bufp->indir_arr[i];
  if (tx_scrq_entry.v1.type != IBMVNIC_TX_DESC)
   continue;
  index = be32_to_cpu(tx_scrq_entry.v1.correlator);
  if (index & IBMVNIC_TSO_POOL_MASK) {
   tx_pool = &adapter->tso_pool[queue_num];
   index &= ~IBMVNIC_TSO_POOL_MASK;
  } else {
   tx_pool = &adapter->tx_pool[queue_num];
  }
  tx_pool->free_map[tx_pool->consumer_index] = index;
  tx_pool->consumer_index = tx_pool->consumer_index == 0 ?
       tx_pool->num_buffers - 1 :
       tx_pool->consumer_index - 1;
  tx_buff = &tx_pool->tx_buff[index];
  adapter->tx_stats_buffers[queue_num].batched_packets--;
  adapter->tx_stats_buffers[queue_num].bytes -=
      tx_buff->skb->len;
  dev_kfree_skb_any(tx_buff->skb);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=85 G=89

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.