diff options
Diffstat (limited to 'drivers/net/ethernet/ibm/ibmvnic.c')
| -rw-r--r-- | drivers/net/ethernet/ibm/ibmvnic.c | 125 |
1 files changed, 101 insertions, 24 deletions
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index b65f5f3ac034..1b3cc8bb0705 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -59,6 +59,7 @@ #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> @@ -353,6 +354,8 @@ 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) @@ -598,6 +601,8 @@ static void release_vpd_data(struct ibmvnic_adapter *adapter) kfree(adapter->vpd->buff); kfree(adapter->vpd); + + adapter->vpd = NULL; } static void release_tx_pools(struct ibmvnic_adapter *adapter) @@ -786,6 +791,18 @@ static int ibmvnic_login(struct net_device *netdev) 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) { int i; @@ -808,6 +825,10 @@ static void release_resources(struct ibmvnic_adapter *adapter) } } } + kfree(adapter->napi); + adapter->napi = NULL; + + release_login_rsp_buffer(adapter); } static int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state) @@ -908,6 +929,7 @@ static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter) 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; } @@ -1051,6 +1073,35 @@ static int ibmvnic_open(struct net_device *netdev) return rc; } +static void clean_rx_pools(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_rx_pool *rx_pool; + u64 rx_entries; + int rx_scrqs; + int i, j; + + if (!adapter->rx_pool) + return; + + rx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + 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) + continue; + + netdev_dbg(adapter->netdev, "Cleaning rx_pool[%d]\n", i); + for (j = 0; j < rx_entries; 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; + } + } + } +} + static void clean_tx_pools(struct ibmvnic_adapter *adapter) { struct ibmvnic_tx_pool *tx_pool; @@ -1128,7 +1179,7 @@ static int __ibmvnic_close(struct net_device *netdev) } } } - + clean_rx_pools(adapter); clean_tx_pools(adapter); adapter->state = VNIC_CLOSED; return rc; @@ -1177,6 +1228,9 @@ static int build_hdr_data(u8 hdr_field, struct sk_buff *skb, 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); @@ -1410,9 +1464,7 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) hdrs += 2; } /* determine if l2/3/4 headers are sent to firmware */ - if ((*hdrs >> 7) & 1 && - (skb->protocol == htons(ETH_P_IP) || - skb->protocol == htons(ETH_P_IPV6))) { + if ((*hdrs >> 7) & 1) { build_hdr_descs_arr(tx_buff, &num_entries, *hdrs); tx_crq.v1.n_crq_elem = num_entries; tx_buff->indir_arr[0] = tx_crq; @@ -1543,15 +1595,19 @@ static int __ibmvnic_set_mac(struct net_device *netdev, struct sockaddr *p) 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], addr->sa_data); + + init_completion(&adapter->fw_done); ibmvnic_send_crq(adapter, &crq); + wait_for_completion(&adapter->fw_done); /* netdev->dev_addr is changed in handle_change_mac_rsp function */ - return 0; + return adapter->fw_done_rc ? -EIO : 0; } static int ibmvnic_set_mac(struct net_device *netdev, void *p) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; + int rc; if (adapter->state == VNIC_PROBED) { memcpy(&adapter->desired.mac, addr, sizeof(struct sockaddr)); @@ -1559,9 +1615,9 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) return 0; } - __ibmvnic_set_mac(netdev, addr); + rc = __ibmvnic_set_mac(netdev, addr); - return 0; + return rc; } /** @@ -1630,6 +1686,7 @@ static int do_reset(struct ibmvnic_adapter *adapter, return rc; } else if (adapter->req_rx_queues != old_num_rx_queues || adapter->req_tx_queues != old_num_tx_queues) { + adapter->map_id = 1; release_rx_pools(adapter); release_tx_pools(adapter); init_rx_pools(netdev); @@ -1658,8 +1715,6 @@ static int do_reset(struct ibmvnic_adapter *adapter, return 0; } - netif_carrier_on(netdev); - /* kick napi */ for (i = 0; i < adapter->req_rx_queues; i++) napi_schedule(&adapter->napi[i]); @@ -1667,6 +1722,8 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (adapter->reset_reason != VNIC_RESET_FAILOVER) netdev_notify_peers(netdev); + netif_carrier_on(netdev); + return 0; } @@ -1822,7 +1879,8 @@ restart_poll: u16 offset; u8 flags = 0; - if (unlikely(adapter->resetting)) { + if (unlikely(adapter->resetting && + adapter->reset_reason != VNIC_RESET_NON_FATAL)) { enable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]); napi_complete_done(napi, frames_processed); return frames_processed; @@ -1840,6 +1898,12 @@ restart_poll: be16_to_cpu(next->rx_comp.rc)); /* free the entry */ next->rx_comp.first = 0; + dev_kfree_skb_any(rx_buff->skb); + remove_buff_from_pool(adapter, rx_buff); + continue; + } else if (!rx_buff->skb) { + /* free the entry */ + next->rx_comp.first = 0; remove_buff_from_pool(adapter, rx_buff); continue; } @@ -2484,6 +2548,12 @@ static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance) struct ibmvnic_sub_crq_queue *scrq = instance; struct ibmvnic_adapter *adapter = scrq->adapter; + /* When booting a kdump kernel we can hit pending interrupts + * prior to completing driver initialization. + */ + if (unlikely(adapter->state != VNIC_OPEN)) + return IRQ_NONE; + adapter->rx_stats_buffers[scrq->scrq_num].interrupts++; if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) { @@ -2893,8 +2963,12 @@ static int ibmvnic_send_crq(struct ibmvnic_adapter *adapter, cpu_to_be64(u64_crq[1])); if (rc) { - if (rc == H_CLOSED) + if (rc == H_CLOSED) { dev_warn(dev, "CRQ Queue closed\n"); + if (adapter->resetting) + ibmvnic_reset(adapter, VNIC_RESET_FATAL); + } + dev_warn(dev, "Send error (rc=%d)\n", rc); } @@ -2990,6 +3064,7 @@ static void send_login(struct ibmvnic_adapter *adapter) struct vnic_login_client_data *vlcd; int i; + release_login_rsp_buffer(adapter); client_data_len = vnic_client_data_len(adapter); buffer_size = @@ -3271,7 +3346,7 @@ static void handle_vpd_rsp(union ibmvnic_crq *crq, struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; - unsigned char *substr = NULL, *ptr = NULL; + unsigned char *substr = NULL; u8 fw_level_len = 0; memset(adapter->fw_version, 0, 32); @@ -3290,7 +3365,7 @@ static void handle_vpd_rsp(union ibmvnic_crq *crq, */ substr = strnstr(adapter->vpd->buff, "RM", adapter->vpd->len); if (!substr) { - dev_info(dev, "No FW level provided by VPD\n"); + dev_info(dev, "Warning - No FW level has been provided in the VPD buffer by the VIOS Server\n"); goto complete; } @@ -3305,16 +3380,14 @@ static void handle_vpd_rsp(union ibmvnic_crq *crq, /* copy firmware version string from vpd into adapter */ if ((substr + 3 + fw_level_len) < (adapter->vpd->buff + adapter->vpd->len)) { - ptr = strncpy((char *)adapter->fw_version, - substr + 3, fw_level_len); - - if (!ptr) - dev_err(dev, "Failed to isolate FW level string\n"); + strncpy((char *)adapter->fw_version, substr + 3, fw_level_len); } else { dev_info(dev, "FW substr extrapolated VPD buff\n"); } complete: + if (adapter->fw_version[0] == '\0') + strncpy((char *)adapter->fw_version, "N/A", 3 * sizeof(char)); complete(&adapter->fw_done); } @@ -3558,8 +3631,8 @@ static void handle_error_indication(union ibmvnic_crq *crq, ibmvnic_reset(adapter, VNIC_RESET_NON_FATAL); } -static void handle_change_mac_rsp(union ibmvnic_crq *crq, - struct ibmvnic_adapter *adapter) +static int handle_change_mac_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct device *dev = &adapter->vdev->dev; @@ -3568,10 +3641,13 @@ static void handle_change_mac_rsp(union ibmvnic_crq *crq, rc = crq->change_mac_addr_rsp.rc.code; if (rc) { dev_err(dev, "Error %ld in CHANGE_MAC_ADDR_RSP\n", rc); - return; + goto out; } memcpy(netdev->dev_addr, &crq->change_mac_addr_rsp.mac_addr[0], ETH_ALEN); +out: + complete(&adapter->fw_done); + return rc; } static void handle_request_cap_rsp(union ibmvnic_crq *crq, @@ -3714,6 +3790,7 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, ibmvnic_remove(adapter->vdev); return -EIO; } + release_login_buffer(adapter); complete(&adapter->init_done); return 0; @@ -4031,7 +4108,7 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, break; case CHANGE_MAC_ADDR_RSP: netdev_dbg(netdev, "Got MAC address change Response\n"); - handle_change_mac_rsp(crq, adapter); + adapter->fw_done_rc = handle_change_mac_rsp(crq, adapter); break; case ERROR_INDICATION: netdev_dbg(netdev, "Got Error Indication\n"); @@ -4335,7 +4412,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) } netdev = alloc_etherdev_mq(sizeof(struct ibmvnic_adapter), - IBMVNIC_MAX_TX_QUEUES); + IBMVNIC_MAX_QUEUES); if (!netdev) return -ENOMEM; @@ -4461,7 +4538,7 @@ static ssize_t failover_store(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(failover, 0200, NULL, failover_store); +static DEVICE_ATTR_WO(failover); static unsigned long ibmvnic_get_desired_dma(struct vio_dev *vdev) { |