| // 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/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/iommu.h> |
| #include <linux/uaccess.h> |
| #include <asm/firmware.h> |
| #include <linux/workqueue.h> |
| #include <linux/if_vlan.h> |
| #include <linux/utsname.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 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); |
| |
| 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 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; |
| int rc; |
| |
| if (!reuse_ltb(ltb, size)) { |
| dev_dbg(dev, |
| "LTB size changed from 0x%llx to 0x%x, reallocating\n", |
| ltb->size, 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]\n", |
| ltb->map_id, ltb->size); |
| } |
| |
| /* 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 = -1; |
| 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; |
| } |
| |
| 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; |
| 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 index; |
| 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) { |
| index = 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[index].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 */ |
| offset = index * pool->buff_size; |
| dst = pool->long_term_buff.buff + offset; |
| memset(dst, 0, pool->buff_size); |
| dma_addr = pool->long_term_buff.addr + offset; |
| |
| /* add the skb to an rx_buff in the pool */ |
| pool->rx_buff[index].data = dst; |
| pool->rx_buff[index].dma = dma_addr; |
| pool->rx_buff[index].skb = skb; |
| pool->rx_buff[index].pool_index = pool->index; |
| pool->rx_buff[index].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[index]); |
| sub_crq->rx_add.ioba = cpu_to_be32(dma_addr); |
| sub_crq->rx_add.map_id = pool->long_term_buff.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); |
| index = (int)(rx_buff - pool->rx_buff); |
| pool->free_map[pool->next_free] = index; |
| dev_kfree_skb_any(pool->rx_buff[index].skb); |
| pool->rx_buff[index].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; |
| |
| stok = dma_map_single(dev, &adapter->stats, |
| sizeof(struct ibmvnic_statistics), |
| DMA_FROM_DEVICE); |
| if (dma_mapping_error(dev, stok)) { |
| dev_err(dev, "Couldn't map stats buffer\n"); |
| return -1; |
| } |
| |
| 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_long_term_buff(adapter, &rx_pool->long_term_buff); |
| |
| 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; |
| |
| /* Require buff size to be exactly same for now */ |
| if (old_buff_size != new_buff_size) |
| return false; |
| |
| if (old_num_pools == new_num_pools && old_pool_size == new_pool_size) |
| return true; |
| |
| if (old_num_pools < adapter->min_rx_queues || |
| old_num_pools > adapter->max_rx_queues || |
| old_pool_size < adapter->min_rx_add_entries_per_subcrq || |
| old_pool_size > adapter->max_rx_add_entries_per_subcrq) |
| 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; |
| |
| 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 -1; |
| } |
| |
| /* 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); |
| 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"); |
| 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); |
| |
| if (alloc_long_term_buff(adapter, &rx_pool->long_term_buff, |
| rx_pool->size * rx_pool->buff_size)) |
| 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 -1; |
| } |
| |
| 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_long_term_buff(adapter, &tx_pool->long_term_buff); |
| } |
| |
| /** |
| * 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 -1; |
| |
| 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 -1; |
| } |
| |
| 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; |
| |
| /* Require MTU to be exactly same to reuse pools for now */ |
| if (old_mtu != new_mtu) |
| return false; |
| |
| if (old_num_pools == new_num_pools && old_pool_size == new_pool_size) |
| return true; |
| |
| if (old_num_pools < adapter->min_tx_queues || |
| old_num_pools > adapter->max_tx_queues || |
| old_pool_size < adapter->min_tx_entries_per_subcrq || |
| old_pool_size > adapter->max_tx_entries_per_subcrq) |
| 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 -1; |
| |
| 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 -1; |
| } |
| |
| /* 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; |
| u32 ltb_size; |
| |
| tx_pool = &adapter->tx_pool[i]; |
| ltb_size = tx_pool->num_buffers * tx_pool->buf_size; |
| if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, |
| ltb_size)) |
| goto out; |
| |
| dev_dbg(dev, "Updated LTB for tx pool %d [%p, %d, %d]\n", |
| i, tx_pool->long_term_buff.buff, |
| tx_pool->num_buffers, tx_pool->buf_size); |
| |
| 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]; |
| ltb_size = tso_pool->num_buffers * tso_pool->buf_size; |
| if (alloc_long_term_buff(adapter, &tso_pool->long_term_buff, |
| ltb_size)) |
| goto out; |
| |
| dev_dbg(dev, "Updated LTB for tso pool %d [%p, %d, %d]\n", |
| i, tso_pool->long_term_buff.buff, |
| tso_pool->num_buffers, tso_pool->buf_size); |
| |
| 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, NAPI_POLL_WEIGHT); |
| } |
| |
| 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) |
| { |
| struct ibmvnic_adapter *adapter = netdev_priv(netdev); |
| unsigned long timeout = msecs_to_jiffies(20000); |
| 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 -1; |
| } |
| |
| 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, retrying...\n"); |
| retry = true; |
| adapter->init_done_rc = 0; |
| retry_count++; |
| continue; |
| } |
| |
| 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 -1; |
| } |
| |
| rc = init_sub_crqs(adapter); |
| if (rc) { |
| netdev_warn(netdev, |
| "SCRQ initialization failed\n"); |
| return -1; |
| } |
| |
| rc = init_sub_crq_irqs(adapter); |
| if (rc) { |
| netdev_warn(netdev, |
| "SCRQ irq initialization failed\n"); |
| return -1; |
| } |
| } else if (adapter->init_done_rc) { |
| netdev_warn(netdev, "Adapter login failed\n"); |
| return -1; |
| } |
| } 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) |
| { |
| kfree(adapter->login_buf); |
| adapter->login_buf = NULL; |
| } |
| |
| static void release_login_rsp_buffer(struct ibmvnic_adapter *adapter) |
| { |
| 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 -1; |
| } |
| |
| 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(netdev_get_tx_queue(netdev, i)); |
| } |
| |
| rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_UP); |
| if (rc) { |
| ibmvnic_napi_disable(adapter); |
| release_resources(adapter); |
| return rc; |
| } |
| |
| 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"); |
| release_resources(adapter); |
| release_rx_pools(adapter); |
| release_tx_pools(adapter); |
| 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; |
| } |
| 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 */ |
| 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; |
| } |
| |
| /** |
| * build_hdr_data - creates L2/L3/L4 header data buffer |
| * @hdr_field: bitfield determining needed headers |
| * @skb: socket buffer |
| * @hdr_len: array of header lengths |
| * @hdr_data: buffer to write the header to |
| * |
| * 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. |
| */ |
| static int build_hdr_data(u8 hdr_field, struct sk_buff *skb, |
| int *hdr_len, u8 *hdr_data) |
| { |
| int len = 0; |
| u8 *hdr; |
| |
| if (skb_vlan_tagged(skb) && !skb_vlan_tag_present(skb)) |
| hdr_len[0] = sizeof(struct vlan_ethhdr); |
| else |
| hdr_len[0] = sizeof(struct ethhdr); |
| |
| if (skb->protocol == htons(ETH_P_IP)) { |
| hdr_len[1] = ip_hdr(skb)->ihl * 4; |
| 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)) { |
| hdr_len[1] = sizeof(struct ipv6hdr); |
| 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); |
| } else if (skb->protocol == htons(ETH_P_ARP)) { |
| hdr_len[1] = arp_hdr_len(skb->dev); |
| hdr_len[2] = 0; |
| } |
| |
| memset(hdr_data, 0, 120); |
| if ((hdr_field >> 6) & 1) { |
| hdr = skb_mac_header(skb); |
| memcpy(hdr_data, hdr, hdr_len[0]); |
| len += hdr_len[0]; |
| } |
| |
| if ((hdr_field >> 5) & 1) { |
| hdr = skb_network_header(skb); |
| memcpy(hdr_data + len, hdr, hdr_len[1]); |
| len += hdr_len[1]; |
| } |
| |
| if ((hdr_field >> 4) & 1) { |
| hdr = skb_transport_header(skb); |
| memcpy(hdr_data + len, hdr, hdr_len[2]); |
| len += hdr_len[2]; |
| } |
| return len; |
| } |
| |
| /** |
| * 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 |
| */ |
| |
| 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; |
| |
| memset(&hdr_desc, 0, sizeof(hdr_desc)); |
| if (cur != hdr_data) { |
| 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; |
| *scrq_arr = hdr_desc; |
| scrq_arr++; |
| 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}; |
| u8 hdr_data[140] = {0}; |
| int tot_len; |
| |
| tot_len = build_hdr_data(hdr_field, skb, hdr_len, |
| hdr_data); |
| *num_entries += create_hdr_descs(hdr_field, hdr_data, 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->netdev->stats.tx_packets--; |
| adapter->netdev->stats.tx_bytes -= tx_buff->skb->len; |
| adapter->tx_stats_buffers[queue_num].packets--; |
| adapter->tx_stats_buffers[queue_num].bytes -= |
| tx_buff->skb->len; |
| dev_kfree_skb_any(tx_buff->skb); |
| tx_buff->skb = NULL; |
| adapter->netdev->stats.tx_dropped++; |
| } |
| ind_bufp->index = 0; |
| if (atomic_sub_return(entries, &tx_scrq->used) <= |
| (adapter->req_tx_entries_per_subcrq / 2) && |
| __netif_subqueue_stopped(adapter->netdev, queue_num) && |
| !test_bit(0, &adapter->resetting)) { |
| netif_wake_subqueue(adapter->netdev, queue_num); |
| netdev_dbg(adapter->netdev, "Started queue %d\n", |
| queue_num); |
| } |
| } |
| |
| static int ibmvnic_tx_scrq_flush(struct ibmvnic_adapter *adapter, |
| struct ibmvnic_sub_crq_queue *tx_scrq) |
| { |
| struct ibmvnic_ind_xmit_queue *ind_bufp; |
| u64 dma_addr; |
| u64 entries; |
| u64 handle; |
| int rc; |
| |
| ind_bufp = &tx_scrq->ind_buf; |
| dma_addr = (u64)ind_bufp->indir_dma; |
| entries = (u64)ind_bufp->index; |
| handle = tx_scrq->handle; |
| |
| if (!entries) |
| return 0; |
| rc = send_subcrq_indirect(adapter, handle, dma_addr, entries); |
| if (rc) |
| ibmvnic_tx_scrq_clean_buffer(adapter, tx_scrq); |
| else |
| ind_bufp->index = 0; |
| return 0; |
| } |
| |
| static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) |
| { |
| struct ibmvnic_adapter *adapter = netdev_priv(netdev); |
| int queue_num = skb_get_queue_mapping(skb); |
| u8 *hdrs = (u8 *)&adapter->tx_rx_desc_req; |
| struct device *dev = &adapter->vdev->dev; |
| struct ibmvnic_ind_xmit_queue *ind_bufp; |
| struct ibmvnic_tx_buff *tx_buff = NULL; |
| struct ibmvnic_sub_crq_queue *tx_scrq; |
| struct ibmvnic_tx_pool *tx_pool; |
| unsigned int tx_send_failed = 0; |
| netdev_tx_t ret = NETDEV_TX_OK; |
| unsigned int tx_map_failed = 0; |
| union sub_crq indir_arr[16]; |
| unsigned int tx_dropped = 0; |
| unsigned int tx_packets = 0; |
| unsigned int tx_bytes = 0; |
| dma_addr_t data_dma_addr; |
| struct netdev_queue *txq; |
| unsigned long lpar_rc; |
| union sub_crq tx_crq; |
| unsigned int offset; |
| int num_entries = 1; |
| unsigned char *dst; |
| int index = 0; |
| u8 proto = 0; |
| |
| tx_scrq = adapter->tx_scrq[queue_num]; |
| txq = netdev_get_tx_queue(netdev, queue_num); |
| ind_bufp = &tx_scrq->ind_buf; |
| |
| if (test_bit(0, &adapter->resetting)) { |
| dev_kfree_skb_any(skb); |
| |
| tx_send_failed++; |
| tx_dropped++; |
| ret = NETDEV_TX_OK; |
| goto out; |
| } |
| |
| if (ibmvnic_xmit_workarounds(skb, netdev)) { |
| tx_dropped++; |
| tx_send_failed++; |
| ret = NETDEV_TX_OK; |
| ibmvnic_tx_scrq_flush(adapter, tx_scrq); |
| goto out; |
| } |
| if (skb_is_gso(skb)) |
| tx_pool = &adapter->tso_pool[queue_num]; |
| else |
| tx_pool = &adapter->tx_pool[queue_num]; |
| |
| index = tx_pool->free_map[tx_pool->consumer_index]; |
| |
| if (index == IBMVNIC_INVALID_MAP) { |
| dev_kfree_skb_any(skb); |
| tx_send_failed++; |
| tx_dropped++; |
| ibmvnic_tx_scrq_flush(adapter, tx_scrq); |
| ret = NETDEV_TX_OK; |
| goto out; |
| } |
| |
| tx_pool->free_map[tx_pool->consumer_index] = IBMVNIC_INVALID_MAP; |
| |
| offset = index * tx_pool->buf_size; |
| dst = tx_pool->long_term_buff.buff + offset; |
| memset(dst, 0, tx_pool->buf_size); |
| data_dma_addr = tx_pool->long_term_buff.addr + offset; |
| |
| if (skb_shinfo(skb)->nr_frags) { |
| int cur, i; |
| |
| /* Copy the head */ |
| skb_copy_from_linear_data(skb, dst, skb_headlen(skb)); |
| cur = skb_headlen(skb); |
| |
| /* Copy the frags */ |
| for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { |
| const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; |
| |
| memcpy(dst + cur, skb_frag_address(frag), |
| skb_frag_size(frag)); |
| cur += skb_frag_size(frag); |
| } |
| } else { |
| skb_copy_from_linear_data(skb, dst, skb->len); |
| } |
| |
| /* post changes to long_term_buff *dst before VIOS accessing it */ |
| dma_wmb(); |
| |
| tx_pool->consumer_index = |
| (tx_pool->consumer_index + 1) % tx_pool->num_buffers; |
| |
| tx_buff = &tx_pool->tx_buff[index]; |
| tx_buff->skb = skb; |
| tx_buff->index = index; |
| tx_buff->pool_index = queue_num; |
| |
| memset(&tx_crq, 0, sizeof(tx_crq)); |
| tx_crq.v1.first = IBMVNIC_CRQ_CMD; |
| tx_crq.v1.type = IBMVNIC_TX_DESC; |
| tx_crq.v1.n_crq_elem = 1; |
| tx_crq.v1.n_sge = 1; |
| tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED; |
| |
| if (skb_is_gso(skb)) |
| tx_crq.v1.correlator = |
| cpu_to_be32(index | IBMVNIC_TSO_POOL_MASK); |
| else |
| tx_crq.v1.correlator = cpu_to_be32(index); |
| tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id); |
| tx_crq.v1.sge_len = cpu_to_be32(skb->len); |
| tx_crq.v1.ioba = cpu_to_be64(data_dma_addr); |
| |
| if (adapter->vlan_header_insertion && skb_vlan_tag_present(skb)) { |
| tx_crq.v1.flags2 |= IBMVNIC_TX_VLAN_INSERT; |
| tx_crq.v1.vlan_id = cpu_to_be16(skb->vlan_tci); |
| } |
| |
| if (skb->protocol == htons(ETH_P_IP)) { |
| tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV4; |
| proto = ip_hdr(skb)->protocol; |
| } else if (skb->protocol == htons(ETH_P_IPV6)) { |
| tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV6; |
| proto = ipv6_hdr(skb)->nexthdr; |
| } |
| |
| if (proto == IPPROTO_TCP) |
| tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_TCP; |
| else if (proto == IPPROTO_UDP) |
| tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_UDP; |
| |
| if (skb->ip_summed == CHECKSUM_PARTIAL) { |
| tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD; |
| hdrs += 2; |
| } |
| if (skb_is_gso(skb)) { |
| tx_crq.v1.flags1 |= IBMVNIC_TX_LSO; |
| tx_crq.v1.mss = cpu_to_be16(skb_shinfo(skb)->gso_size); |
| hdrs += 2; |
| } |
| |
| if ((*hdrs >> 7) & 1) |
| build_hdr_descs_arr(skb, indir_arr, &num_entries, *hdrs); |
| |
| tx_crq.v1.n_crq_elem = num_entries; |
| tx_buff->num_entries = num_entries; |
| /* flush buffer if current entry can not fit */ |
| if (num_entries + ind_bufp->index > IBMVNIC_MAX_IND_DESCS) { |
| lpar_rc = ibmvnic_tx_scrq_flush(adapter, tx_scrq); |
| if (lpar_rc != H_SUCCESS) |
| goto tx_flush_err; |
| } |
| |
| indir_arr[0] = tx_crq; |
| memcpy(&ind_bufp->indir_arr[ind_bufp->index], &indir_arr[0], |
| num_entries * sizeof(struct ibmvnic_generic_scrq)); |
| ind_bufp->index += num_entries; |
| if (__netdev_tx_sent_queue(txq, skb->len, |
| netdev_xmit_more() && |
| ind_bufp->index < IBMVNIC_MAX_IND_DESCS)) { |
| lpar_rc = ibmvnic_tx_scrq_flush(adapter, tx_scrq); |
| if (lpar_rc != H_SUCCESS) |
| goto tx_err; |
| } |
| |
| if (atomic_add_return(num_entries, &tx_scrq->used) |
| >= adapter->req_tx_entries_per_subcrq) { |
| netdev_dbg(netdev, "Stopping queue %d\n", queue_num); |
| netif_stop_subqueue(netdev, queue_num); |
| } |
| |
| tx_packets++; |
| tx_bytes += skb->len; |
| txq->trans_start = jiffies; |
| ret = NETDEV_TX_OK; |
| goto out; |
| |
| tx_flush_err: |
| dev_kfree_skb_any(skb); |
| tx_buff->skb = NULL; |
| tx_pool->consumer_index = tx_pool->consumer_index == 0 ? |
| tx_pool->num_buffers - 1 : |
| tx_pool->consumer_index - 1; |
| tx_dropped++; |
| tx_err: |
| if (lpar_rc != H_CLOSED && lpar_rc != H_PARAMETER) |
| dev_err_ratelimited(dev, "tx: send failed\n"); |
| |
| if (lpar_rc == H_CLOSED || adapter->failover_pending) { |
| /* Disable TX 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 or some other action. |
| */ |
| netif_tx_stop_all_queues(netdev); |
| netif_carrier_off(netdev); |
| } |
| out: |
| netdev->stats.tx_dropped += tx_dropped; |
| netdev->stats.tx_bytes += tx_bytes; |
| netdev->stats.tx_packets += tx_packets; |
| adapter->tx_send_failed += tx_send_failed; |
| adapter->tx_map_failed += tx_map_failed; |
| adapter->tx_stats_buffers[queue_num].packets += tx_packets; |
| adapter->tx_stats_buffers[queue_num].bytes += tx_bytes; |
| adapter->tx_stats_buffers[queue_num].dropped_packets += tx_dropped; |
| |
| return ret; |
| } |
| |
| static void ibmvnic_set_multi(struct net_device *netdev) |
| { |
| struct ibmvnic_adapter *adapter = netdev_priv(netdev); |
| struct netdev_hw_addr *ha; |
| union ibmvnic_crq crq; |
| |
| memset(&crq, 0, sizeof(crq)); |
| crq.request_capability.first = IBMVNIC_CRQ_CMD; |
| crq.request_capability.cmd = REQUEST_CAPABILITY; |
| |
| if (netdev->flags & IFF_PROMISC) { |
| if (!adapter->promisc_supported) |
| return; |
| } else { |
| if (netdev->flags & IFF_ALLMULTI) { |
| /* Accept all multicast */ |
| memset(&crq, 0, sizeof(crq)); |
| crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; |
| crq.multicast_ctrl.cmd = MULTICAST_CTRL; |
| crq.multicast_ctrl.flags = IBMVNIC_ENABLE_ALL; |
| ibmvnic_send_crq(adapter, &crq); |
| } else if (netdev_mc_empty(netdev)) { |
| /* Reject all multicast */ |
| memset(&crq, 0, sizeof(crq)); |
| crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; |
| crq.multicast_ctrl.cmd = MULTICAST_CTRL; |
| crq.multicast_ctrl.flags = IBMVNIC_DISABLE_ALL; |
| ibmvnic_send_crq(adapter, &crq); |
| } else { |
| /* Accept one or more multicast(s) */ |
| netdev_for_each_mc_addr(ha, netdev) { |
| memset(&crq, 0, sizeof(crq)); |
| crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; |
| crq.multicast_ctrl.cmd = MULTICAST_CTRL; |
| crq.multicast_ctrl.flags = IBMVNIC_ENABLE_MC; |
| ether_addr_copy(&crq.multicast_ctrl.mac_addr[0], |
| ha->addr); |
| ibmvnic_send_crq(adapter, &crq); |
| } |
| } |
| } |
| } |
| |
| static int __ibmvnic_set_mac(struct net_device *netdev, u8 *dev_addr) |
| { |
| struct ibmvnic_adapter *adapter = netdev_priv(netdev); |
| union ibmvnic_crq crq; |
| int rc; |
| |
| if (!is_valid_ether_addr(dev_addr)) { |
| rc = -EADDRNOTAVAIL; |
| goto err; |
| } |
| |
| memset(&crq, 0, sizeof(crq)); |
| crq.change_mac_addr.first = IBMVNIC_CRQ_CMD; |
| crq.change_mac_addr.cmd = CHANGE_MAC_ADDR; |
| ether_addr_copy(&crq.change_mac_addr.mac_addr[0], dev_addr); |
| |
| mutex_lock(&adapter->fw_lock); |
| adapter->fw_done_rc = 0; |
| reinit_completion(&adapter->fw_done); |
| |
| rc = ibmvnic_send_crq(adapter, &crq); |
| if (rc) { |
| rc = -EIO; |
| mutex_unlock(&adapter->fw_lock); |
| goto err; |
| } |
| |
| rc = ibmvnic_wait_for_completion(adapter, &adapter->fw_done, 10000); |
| /* netdev->dev_addr is changed in handle_change_mac_rsp function */ |
| if (rc || adapter->fw_done_rc) { |
| rc = -EIO; |
| mutex_unlock(&adapter->fw_lock); |
| goto err; |
| } |
| mutex_unlock(&adapter->fw_lock); |
| return 0; |
| err: |
| ether_addr_copy(adapter->mac_addr, netdev->dev_addr); |
| return rc; |
| } |
| |
| static int ibmvnic_set_mac(struct net_device *netdev, void *p) |
| { |
| struct ibmvnic_adapter *adapter = netdev_priv(netdev); |
| struct sockaddr *addr = p; |
| int rc; |
| |
| rc = 0; |
| if (!is_valid_ether_addr(addr->sa_data)) |
| return -EADDRNOTAVAIL; |
| |
| ether_addr_copy(adapter->mac_addr, addr->sa_data); |
| if (adapter->state != VNIC_PROBED) |
| rc = __ibmvnic_set_mac(netdev, addr->sa_data); |
| |
| return rc; |
| } |
| |
| static const char *reset_reason_to_string(enum ibmvnic_reset_reason reason) |
| { |
| switch (reason) { |
| case VNIC_RESET_FAILOVER: |
| return "FAILOVER"; |
| case VNIC_RESET_MOBILITY: |
| return "MOBILITY"; |
| case VNIC_RESET_FATAL: |
| return "FATAL"; |
| case VNIC_RESET_NON_FATAL: |
| return "NON_FATAL"; |
| case VNIC_RESET_TIMEOUT: |
| return "TIMEOUT"; |
| case VNIC_RESET_CHANGE_PARAM: |
| return "CHANGE_PARAM"; |
| case VNIC_RESET_PASSIVE_INIT: |
| return "PASSIVE_INIT"; |
| } |
| return "UNKNOWN"; |
| } |
| |
| /* |
| * do_reset returns zero if we are able to keep processing reset events, or |
| * non-zero if we hit a fatal error and must halt. |
| */ |
| static int do_reset(struct ibmvnic_adapter *adapter, |
| struct ibmvnic_rwi *rwi, u32 reset_state) |
| { |
| struct net_device *netdev = adapter->netdev; |
| u64 old_num_rx_queues, old_num_tx_queues; |
| u64 old_num_rx_slots, old_num_tx_slots; |
| int rc; |
| |
| netdev_dbg(adapter->netdev, |
| "[S:%s FOP:%d] Reset reason: %s, reset_state: %s\n", |
| adapter_state_to_string(adapter->state), |
| adapter->failover_pending, |
| reset_reason_to_string(rwi->reset_reason), |
| adapter_state_to_string(reset_state)); |
| |
| adapter->reset_reason = rwi->reset_reason; |
| /* requestor of VNIC_RESET_CHANGE_PARAM already has the rtnl lock */ |
| if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM)) |
| rtnl_lock(); |
| |
| /* Now that we have the rtnl lock, clear any pending failover. |
| * This will ensure ibmvnic_open() has either completed or will |
| * block until failover is complete. |
| */ |
| if (rwi->reset_reason == VNIC_RESET_FAILOVER) |
| adapter->failover_pending = false; |
| |
| /* read the state and check (again) after getting rtnl */ |
| reset_state = adapter->state; |
| |
| if (reset_state == VNIC_REMOVING || reset_state == VNIC_REMOVED) { |
| rc = -EBUSY; |
| goto out; |
| } |
| |
| netif_carrier_off(netdev); |
| |
| old_num_rx_queues = adapter->req_rx_queues; |
| old_num_tx_queues = adapter->req_tx_queues; |
| old_num_rx_slots = adapter->req_rx_add_entries_per_subcrq; |
| old_num_tx_slots = adapter->req_tx_entries_per_subcrq; |
| |
| ibmvnic_cleanup(netdev); |
| |
| if (reset_state == VNIC_OPEN && |
| adapter->reset_reason != VNIC_RESET_MOBILITY && |
| adapter->reset_reason != VNIC_RESET_FAILOVER) { |
| if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { |
| rc = __ibmvnic_close(netdev); |
| if (rc) |
| goto out; |
| } else { |
| adapter->state = VNIC_CLOSING; |
| |
| /* Release the RTNL lock before link state change and |
| * re-acquire after the link state change to allow |
| * linkwatch_event to grab the RTNL lock and run during |
| * a reset. |
| */ |
| rtnl_unlock(); |
| rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); |
| rtnl_lock(); |
| if (rc) |
| goto out; |
| |
| if (adapter->state == VNIC_OPEN) { |
| /* When we dropped rtnl, ibmvnic_open() got |
| * it and noticed that we are resetting and |
| * set the adapter state to OPEN. Update our |
| * new "target" state, and resume the reset |
| * from VNIC_CLOSING state. |
| */ |
| netdev_dbg(netdev, |
| "Open changed state from %s, updating.\n", |
| adapter_state_to_string(reset_state)); |
| reset_state = VNIC_OPEN; |
| adapter->state = VNIC_CLOSING; |
| } |
| |
| if (adapter->state != VNIC_CLOSING) { |
| /* If someone else changed the adapter state |
| * when we dropped the rtnl, fail the reset |
| */ |
| rc = -1; |
| goto out; |
| } |
| adapter->state = VNIC_CLOSED; |
| } |
| } |
| |
| if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { |
| release_resources(adapter); |
| release_sub_crqs(adapter, 1); |
| release_crq_queue(adapter); |
| } |
| |
| if (adapter->reset_reason != VNIC_RESET_NON_FATAL) { |
| /* remove the closed state so when we call open it appears |
| * we are coming from the probed state. |
| */ |
| adapter->state = VNIC_PROBED; |
| |
| if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { |
| rc = init_crq_queue(adapter); |
| } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) { |
| rc = ibmvnic_reenable_crq_queue(adapter); |
| release_sub_crqs(adapter, 1); |
| } else { |
| rc = ibmvnic_reset_crq(adapter); |
| if (rc == H_CLOSED || rc == H_SUCCESS) { |
| rc = vio_enable_interrupts(adapter->vdev); |
| if (rc) |
| netdev_err(adapter->netdev, |
| "Reset failed to enable interrupts. rc=%d\n", |
| rc); |
| } |
| } |
| |
| if (rc) { |
| netdev_err(adapter->netdev, |
| "Reset couldn't initialize crq. rc=%d\n", rc); |
| goto out; |
| } |
| |
| rc = ibmvnic_reset_init(adapter, true); |
| if (rc) { |
| rc = IBMVNIC_INIT_FAILED; |
| goto out; |
| } |
| |
| /* If the adapter was in PROBE or DOWN state prior to the reset, |
| * exit here. |
| */ |
| if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN) { |
| rc = 0; |
| goto out; |
| } |
| |
| rc = ibmvnic_login(netdev); |
| if (rc) |
| goto out; |
| |
| if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { |
| rc = init_resources(adapter); |
| if (rc) |
| goto out; |
| } else if (adapter->req_rx_queues != old_num_rx_queues || |
| adapter->req_tx_queues != old_num_tx_queues || |
| adapter->req_rx_add_entries_per_subcrq != |
| old_num_rx_slots || |
| adapter->req_tx_entries_per_subcrq != |
| old_num_tx_slots || |
| !adapter->rx_pool || |
| !adapter->tso_pool || |
| !adapter->tx_pool) { |
| release_napi(adapter); |
| release_vpd_data(adapter); |
| |
| rc = init_resources(adapter); |
| if (rc) |
| goto out; |
| |
| } else { |
| rc = init_tx_pools(netdev); |
| if (rc) { |
| netdev_dbg(netdev, |
| "init tx pools failed (%d)\n", |
| rc); |
| goto out; |
| } |
| |
| rc = init_rx_pools(netdev); |
| if (rc) { |
| netdev_dbg(netdev, |
| "init rx pools failed (%d)\n", |
| rc); |
| goto out; |
| } |
| } |
| ibmvnic_disable_irqs(adapter); |
| } |
| adapter->state = VNIC_CLOSED; |
| |
| if (reset_state == VNIC_CLOSED) { |
| rc = 0; |
| goto out; |
| } |
| |
| rc = __ibmvnic_open(netdev); |
| if (rc) { |
| rc = IBMVNIC_OPEN_FAILED; |
| goto out; |
| } |
| |
| /* refresh device's multicast list */ |
| ibmvnic_set_multi(netdev); |
| |
| if (adapter->reset_reason == VNIC_RESET_FAILOVER || |
| adapter->reset_reason == VNIC_RESET_MOBILITY) |
| __netdev_notify_peers(netdev); |
| |
| rc = 0; |
| |
| out: |
| /* restore the adapter state if reset failed */ |
| if (rc) |
| adapter->state = reset_state; |
| /* requestor of VNIC_RESET_CHANGE_PARAM should still hold the rtnl lock */ |
| if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM)) |
| rtnl_unlock(); |
| |
| netdev_dbg(adapter->netdev, "[S:%s FOP:%d] Reset done, rc %d\n", |
| adapter_state_to_string(adapter->state), |
| adapter->failover_pending, rc); |
| return rc; |
| } |
| |
| static int do_hard_reset(struct ibmvnic_adapter *adapter, |
| struct ibmvnic_rwi *rwi, u32 reset_state) |
| { |
| struct net_device *netdev = adapter->netdev; |
| int rc; |
| |
| netdev_dbg(adapter->netdev, "Hard resetting driver (%s)\n", |
| reset_reason_to_string(rwi->reset_reason)); |
| |
| /* read the state and check (again) after getting rtnl */ |
| reset_state = adapter->state; |
| |
| if (reset_state == VNIC_REMOVING || reset_state == VNIC_REMOVED) { |
| rc = -EBUSY; |
| goto out; |
| } |
| |
| netif_carrier_off(netdev); |
| adapter->reset_reason = rwi->reset_reason; |
| |
| ibmvnic_cleanup(netdev); |
| release_resources(adapter); |
| release_sub_crqs(adapter, 0); |
| release_crq_queue(adapter); |
| |
| /* remove the closed state so when we call open it appears |
| * we are coming from the probed state. |
| */ |
| adapter->state = VNIC_PROBED; |
| |
| reinit_completion(&adapter->init_done); |
| rc = init_crq_queue(adapter); |
| if (rc) { |
| netdev_err(adapter->netdev, |
| "Couldn't initialize crq. rc=%d\n", rc); |
| goto out; |
| } |
| |
| rc = ibmvnic_reset_init(adapter, false); |
| if (rc) |
| goto out; |
| |
| /* If the adapter was in PROBE or DOWN state prior to the reset, |
| * exit here. |
| */ |
| if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN) |
| goto out; |
| |
| rc = ibmvnic_login(netdev); |
| if (rc) |
| goto out; |
| |
| rc = init_resources(adapter); |
| if (rc) |
| goto out; |
| |
| ibmvnic_disable_irqs(adapter); |
| adapter->state = VNIC_CLOSED; |
| |
| if (reset_state == VNIC_CLOSED) |
| goto out; |
| |
| rc = __ibmvnic_open(netdev); |
| if (rc) { |
| rc = IBMVNIC_OPEN_FAILED; |
| goto out; |
| } |
| |
| __netdev_notify_peers(netdev); |
| out: |
| /* restore adapter state if reset failed */ |
| if (rc) |
| adapter->state = reset_state; |
| netdev_dbg(adapter->netdev, "[S:%s FOP:%d] Hard reset done, rc %d\n", |
| adapter_state_to_string(adapter->state), |
| adapter->failover_pending, rc); |
| return rc; |
| } |
| |
| static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter) |
| { |
| struct ibmvnic_rwi *rwi; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->rwi_lock, flags); |
| |
| if (!list_empty(&adapter->rwi_list)) { |
| rwi = list_first_entry(&adapter->rwi_list, struct ibmvnic_rwi, |
| list); |
| list_del(&rwi->list); |
| } else { |
| rwi = NULL; |
| } |
| |
| spin_unlock_irqrestore(&adapter->rwi_lock, flags); |
| return rwi; |
| } |
| |
| /** |
| * do_passive_init - complete probing when partner device is detected. |
| * @adapter: ibmvnic_adapter struct |
| * |
| * If the ibmvnic device does not have a partner device to communicate with at boot |
| * and that partner device comes online at a later time, this function is called |
| * to complete the initialization process of ibmvnic device. |
| * Caller is expected to hold rtnl_lock(). |
| * |
| * Returns non-zero if sub-CRQs are not initialized properly leaving the device |
| * in the down state. |
| * Returns 0 upon success and the device is in PROBED state. |
| */ |
| |
| static int do_passive_init(struct ibmvnic_adapter *adapter) |
| { |
| unsigned long timeout = msecs_to_jiffies(30000); |
| struct net_device *netdev = adapter->netdev; |
| struct device *dev = &adapter->vdev->dev; |
| int rc; |
| |
| netdev_dbg(netdev, "Partner device found, probing.\n"); |
| |
| adapter->state = VNIC_PROBING; |
| reinit_completion(&adapter->init_done); |
| adapter->init_done_rc = 0; |
| adapter->crq.active = true; |
| |
| rc = send_crq_init_complete(adapter); |
| if (rc) |
| goto out; |
| |
| rc = send_version_xchg(adapter); |
| if (rc) |
| netdev_dbg(adapter->netdev, "send_version_xchg failed, rc=%d\n", rc); |
| |
| if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { |
| dev_err(dev, "Initialization sequence timed out\n"); |
| rc = -ETIMEDOUT; |
| goto out; |
| } |
| |
| rc = init_sub_crqs(adapter); |
| if (rc) { |
| dev_err(dev, "Initialization of sub crqs failed, rc=%d\n", rc); |
| goto out; |
| } |
| |
| rc = init_sub_crq_irqs(adapter); |
| if (rc) { |
| dev_err(dev, "Failed to initialize sub crq irqs\n, rc=%d", rc); |
| goto init_failed; |
| } |
| |
| netdev->mtu = adapter->req_mtu - ETH_HLEN; |
| netdev->min_mtu = adapter->min_mtu - ETH_HLEN; |
| netdev->max_mtu = adapter->max_mtu - ETH_HLEN; |
| |
| adapter->state = VNIC_PROBED; |
| netdev_dbg(netdev, "Probed successfully. Waiting for signal from partner device.\n"); |
| |
| return 0; |
| |
| init_failed: |
| release_sub_crqs(adapter, 1); |
| out: |
| adapter->state = VNIC_DOWN; |
| return rc; |
| } |
| |
| static void __ibmvnic_reset(struct work_struct *work) |
| { |
| struct ibmvnic_adapter *adapter; |
| bool saved_state = false; |
| struct ibmvnic_rwi *tmprwi; |
| struct ibmvnic_rwi *rwi; |
| unsigned long flags; |
| u32 reset_state; |
| int rc = 0; |
| |
| adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); |
| |
| if (test_and_set_bit_lock(0, &adapter->resetting)) { |
| queue_delayed_work(system_long_wq, |
| &adapter->ibmvnic_delayed_reset, |
| IBMVNIC_RESET_DELAY); |
| return; |
| } |
| |
| rwi = get_next_rwi(adapter); |
| while (rwi) { |
| spin_lock_irqsave(&adapter->state_lock, flags); |
| |
| if (adapter->state == VNIC_REMOVING || |
| adapter->state == VNIC_REMOVED) { |
| spin_unlock_irqrestore(&adapter->state_lock, flags); |
| kfree(rwi); |
| rc = EBUSY; |
| break; |
| } |
| |
| if (!saved_state) { |
| reset_state = adapter->state; |
| saved_state = true; |
| } |
| spin_unlock_irqrestore(&adapter->state_lock, flags); |
| |
| if (rwi->reset_reason == VNIC_RESET_PASSIVE_INIT) { |
| rtnl_lock(); |
| rc = do_passive_init(adapter); |
| rtnl_unlock(); |
| if (!rc) |
| netif_carrier_on(adapter->netdev); |
| } else if (adapter->force_reset_recovery) { |
| /* Since we are doing a hard reset now, clear the |
| * failover_pending flag so we don't ignore any |
| * future MOBILITY or other resets. |
| */ |
| adapter->failover_pending = false; |
| |
| /* Transport event occurred during previous reset */ |
| if (adapter->wait_for_reset) { |
| /* Previous was CHANGE_PARAM; caller locked */ |
| adapter->force_reset_recovery = false; |
| rc = do_hard_reset(adapter, rwi, reset_state); |
| } else { |
| rtnl_lock(); |
| adapter->force_reset_recovery = false; |
| rc = do_hard_reset(adapter, rwi, reset_state); |
| rtnl_unlock(); |
| } |
| if (rc) { |
| /* give backing device time to settle down */ |
| netdev_dbg(adapter->netdev, |
| "[S:%s] Hard reset failed, waiting 60 secs\n", |
| adapter_state_to_string(adapter->state)); |
| set_current_state(TASK_UNINTERRUPTIBLE); |
| schedule_timeout(60 * HZ); |
| } |
| } else { |
| rc = do_reset(adapter, rwi, reset_state); |
| } |
| tmprwi = rwi; |
| adapter->last_reset_time = jiffies; |
| |
| if (rc) |
| netdev_dbg(adapter->netdev, "Reset failed, rc=%d\n", rc); |
| |
| rwi = get_next_rwi(adapter); |
| |
| /* |
| * If there is another reset queued, free the previous rwi |
| * and process the new reset even if previous reset failed |
| * (the previous reset could have failed because of a fail |
| * over for instance, so process the fail over). |
| * |
| * If there are no resets queued and the previous reset failed, |
| * the adapter would be in an undefined state. So retry the |
| * previous reset as a hard reset. |
| */ |
| if (rwi) |
| kfree(tmprwi); |
| else if (rc) |
| rwi = tmprwi; |
| |
| if (rwi && (rwi->reset_reason == VNIC_RESET_FAILOVER || |
| rwi->reset_reason == VNIC_RESET_MOBILITY || rc)) |
| adapter->force_reset_recovery = true; |
| } |
| |
| if (adapter->wait_for_reset) { |
| adapter->reset_done_rc = rc; |
| complete(&adapter->reset_done); |
| } |
| |
| clear_bit_unlock(0, &adapter->resetting); |
| |
| netdev_dbg(adapter->netdev, |
| "[S:%s FRR:%d WFR:%d] Done processing resets\n", |
| adapter_state_to_string(adapter->state), |
| adapter->force_reset_recovery, |
| adapter->wait_for_reset); |
| } |
| |
| static void __ibmvnic_delayed_reset(struct work_struct *work) |
| { |
| struct ibmvnic_adapter *adapter; |
| |
| adapter = container_of(work, struct ibmvnic_adapter, |
| ibmvnic_delayed_reset.work); |
| __ibmvnic_reset(&adapter->ibmvnic_reset); |
| } |
| |
| static int ibmvnic_reset(struct ibmvnic_adapter *adapter, |
| enum ibmvnic_reset_reason reason) |
| { |
| struct list_head *entry, *tmp_entry; |
| struct ibmvnic_rwi *rwi, *tmp; |
| struct net_device *netdev = adapter->netdev; |
| unsigned long flags; |
| int ret; |
| |
| spin_lock_irqsave(&adapter->rwi_lock, flags); |
| |
| /* If failover is pending don't schedule any other reset. |
| * Instead let the failover complete. If there is already a |
| * a failover reset scheduled, we will detect and drop the |
| * duplicate reset when walking the ->rwi_list below. |
| */ |
| if (adapter->state == VNIC_REMOVING || |
| adapter->state == VNIC_REMOVED || |
| (adapter->failover_pending && reason != VNIC_RESET_FAILOVER)) { |
| ret = EBUSY; |
| netdev_dbg(netdev, "Adapter removing or pending failover, skipping reset\n"); |
| goto err; |
| } |
| |
| if (adapter->state == VNIC_PROBING) { |
| netdev_warn(netdev, "Adapter reset during probe\n"); |
| adapter->init_done_rc = -EAGAIN; |
| ret = EAGAIN; |
|