diff options
Diffstat (limited to 'drivers/net/enic/enic_main.c')
-rw-r--r-- | drivers/net/enic/enic_main.c | 428 |
1 files changed, 162 insertions, 266 deletions
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index 8b9cad5e971..67a27cd304d 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -23,6 +23,7 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/pci.h> #include <linux/netdevice.h> @@ -35,6 +36,7 @@ #include <linux/ipv6.h> #include <linux/tcp.h> #include <linux/rtnetlink.h> +#include <linux/prefetch.h> #include <net/ip6_checksum.h> #include "cq_enet_desc.h" @@ -45,6 +47,7 @@ #include "enic_res.h" #include "enic.h" #include "enic_dev.h" +#include "enic_pp.h" #define ENIC_NOTIFY_TIMER_PERIOD (2 * HZ) #define WQ_ENET_MAX_DESC_LEN (1 << WQ_ENET_LEN_BITS) @@ -150,12 +153,12 @@ static inline unsigned int enic_legacy_notify_intr(void) static inline unsigned int enic_msix_rq_intr(struct enic *enic, unsigned int rq) { - return rq; + return enic->cq[enic_cq_rq(enic, rq)].interrupt_offset; } static inline unsigned int enic_msix_wq_intr(struct enic *enic, unsigned int wq) { - return enic->rq_count + wq; + return enic->cq[enic_cq_wq(enic, wq)].interrupt_offset; } static inline unsigned int enic_msix_err_intr(struct enic *enic) @@ -179,10 +182,10 @@ static int enic_get_settings(struct net_device *netdev, ecmd->transceiver = XCVR_EXTERNAL; if (netif_carrier_ok(netdev)) { - ecmd->speed = vnic_dev_port_speed(enic->vdev); + ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev)); ecmd->duplex = DUPLEX_FULL; } else { - ecmd->speed = -1; + ethtool_cmd_speed_set(ecmd, -1); ecmd->duplex = -1; } @@ -250,56 +253,6 @@ static void enic_get_ethtool_stats(struct net_device *netdev, *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].offset]; } -static u32 enic_get_rx_csum(struct net_device *netdev) -{ - struct enic *enic = netdev_priv(netdev); - return enic->csum_rx_enabled; -} - -static int enic_set_rx_csum(struct net_device *netdev, u32 data) -{ - struct enic *enic = netdev_priv(netdev); - - if (data && !ENIC_SETTING(enic, RXCSUM)) - return -EINVAL; - - enic->csum_rx_enabled = !!data; - - return 0; -} - -static int enic_set_tx_csum(struct net_device *netdev, u32 data) -{ - struct enic *enic = netdev_priv(netdev); - - if (data && !ENIC_SETTING(enic, TXCSUM)) - return -EINVAL; - - if (data) - netdev->features |= NETIF_F_HW_CSUM; - else - netdev->features &= ~NETIF_F_HW_CSUM; - - return 0; -} - -static int enic_set_tso(struct net_device *netdev, u32 data) -{ - struct enic *enic = netdev_priv(netdev); - - if (data && !ENIC_SETTING(enic, TSO)) - return -EINVAL; - - if (data) - netdev->features |= - NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN; - else - netdev->features &= - ~(NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN); - - return 0; -} - static u32 enic_get_msglevel(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); @@ -331,12 +284,10 @@ static int enic_set_coalesce(struct net_device *netdev, u32 rx_coalesce_usecs; unsigned int i, intr; - tx_coalesce_usecs = min_t(u32, - INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX), - ecmd->tx_coalesce_usecs); - rx_coalesce_usecs = min_t(u32, - INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX), - ecmd->rx_coalesce_usecs); + tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, + vnic_dev_get_intr_coal_timer_max(enic->vdev)); + rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, + vnic_dev_get_intr_coal_timer_max(enic->vdev)); switch (vnic_dev_get_intr_mode(enic->vdev)) { case VNIC_DEV_INTR_MODE_INTX: @@ -345,26 +296,26 @@ static int enic_set_coalesce(struct net_device *netdev, intr = enic_legacy_io_intr(); vnic_intr_coalescing_timer_set(&enic->intr[intr], - INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); + tx_coalesce_usecs); break; case VNIC_DEV_INTR_MODE_MSI: if (tx_coalesce_usecs != rx_coalesce_usecs) return -EINVAL; vnic_intr_coalescing_timer_set(&enic->intr[0], - INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); + tx_coalesce_usecs); break; case VNIC_DEV_INTR_MODE_MSIX: for (i = 0; i < enic->wq_count; i++) { intr = enic_msix_wq_intr(enic, i); vnic_intr_coalescing_timer_set(&enic->intr[intr], - INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); + tx_coalesce_usecs); } for (i = 0; i < enic->rq_count; i++) { intr = enic_msix_rq_intr(enic, i); vnic_intr_coalescing_timer_set(&enic->intr[intr], - INTR_COALESCE_USEC_TO_HW(rx_coalesce_usecs)); + rx_coalesce_usecs); } break; @@ -387,17 +338,8 @@ static const struct ethtool_ops enic_ethtool_ops = { .get_strings = enic_get_strings, .get_sset_count = enic_get_sset_count, .get_ethtool_stats = enic_get_ethtool_stats, - .get_rx_csum = enic_get_rx_csum, - .set_rx_csum = enic_set_rx_csum, - .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = enic_set_tx_csum, - .get_sg = ethtool_op_get_sg, - .set_sg = ethtool_op_set_sg, - .get_tso = ethtool_op_get_tso, - .set_tso = enic_set_tso, .get_coalesce = enic_get_coalesce, .set_coalesce = enic_set_coalesce, - .get_flags = ethtool_op_get_flags, }; static void enic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) @@ -480,11 +422,18 @@ static void enic_mtu_check(struct enic *enic) if (mtu && mtu != enic->port_mtu) { enic->port_mtu = mtu; - if (mtu < netdev->mtu) - netdev_warn(netdev, - "interface MTU (%d) set higher " - "than switch port MTU (%d)\n", - netdev->mtu, mtu); + if (enic_is_dynamic(enic)) { + mtu = max_t(int, ENIC_MIN_MTU, + min_t(int, ENIC_MAX_MTU, mtu)); + if (mtu != netdev->mtu) + schedule_work(&enic->change_mtu_work); + } else { + if (mtu < netdev->mtu) + netdev_warn(netdev, + "interface MTU (%d) set higher " + "than switch port MTU (%d)\n", + netdev->mtu, mtu); + } } } @@ -850,10 +799,10 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb, } /* dev_base_lock rwlock held, nominally process context */ -static struct net_device_stats *enic_get_stats(struct net_device *netdev) +static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev, + struct rtnl_link_stats64 *net_stats) { struct enic *enic = netdev_priv(netdev); - struct net_device_stats *net_stats = &netdev->stats; struct vnic_stats *stats; enic_dev_stats_dump(enic, &stats); @@ -874,7 +823,7 @@ static struct net_device_stats *enic_get_stats(struct net_device *netdev) return net_stats; } -static void enic_reset_addr_lists(struct enic *enic) +void enic_reset_addr_lists(struct enic *enic) { enic->mc_count = 0; enic->uc_count = 0; @@ -1080,14 +1029,6 @@ static void enic_set_rx_mode(struct net_device *netdev) } } -/* rtnl lock is held */ -static void enic_vlan_rx_register(struct net_device *netdev, - struct vlan_group *vlan_group) -{ - struct enic *enic = netdev_priv(netdev); - enic->vlan_group = vlan_group; -} - /* netif_tx_lock held, BHs disabled */ static void enic_tx_timeout(struct net_device *netdev) { @@ -1112,157 +1053,77 @@ static int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) return -EINVAL; } -static int enic_set_port_profile(struct enic *enic, u8 *mac) -{ - struct vic_provinfo *vp; - u8 oui[3] = VIC_PROVINFO_CISCO_OUI; - u16 os_type = VIC_GENERIC_PROV_OS_TYPE_LINUX; - char uuid_str[38]; - char client_mac_str[18]; - u8 *client_mac; - int err; - - err = enic_vnic_dev_deinit(enic); - if (err) - return err; - - enic_reset_addr_lists(enic); - - switch (enic->pp.request) { - - case PORT_REQUEST_ASSOCIATE: - - if (!(enic->pp.set & ENIC_SET_NAME) || !strlen(enic->pp.name)) - return -EINVAL; - - if (!is_valid_ether_addr(mac)) - return -EADDRNOTAVAIL; - - vp = vic_provinfo_alloc(GFP_KERNEL, oui, - VIC_PROVINFO_GENERIC_TYPE); - if (!vp) - return -ENOMEM; - - vic_provinfo_add_tlv(vp, - VIC_GENERIC_PROV_TLV_PORT_PROFILE_NAME_STR, - strlen(enic->pp.name) + 1, enic->pp.name); - - if (!is_zero_ether_addr(enic->pp.mac_addr)) - client_mac = enic->pp.mac_addr; - else - client_mac = mac; - - vic_provinfo_add_tlv(vp, - VIC_GENERIC_PROV_TLV_CLIENT_MAC_ADDR, - ETH_ALEN, client_mac); - - sprintf(client_mac_str, "%pM", client_mac); - vic_provinfo_add_tlv(vp, - VIC_GENERIC_PROV_TLV_CLUSTER_PORT_UUID_STR, - sizeof(client_mac_str), client_mac_str); - - if (enic->pp.set & ENIC_SET_INSTANCE) { - sprintf(uuid_str, "%pUB", enic->pp.instance_uuid); - vic_provinfo_add_tlv(vp, - VIC_GENERIC_PROV_TLV_CLIENT_UUID_STR, - sizeof(uuid_str), uuid_str); - } - - if (enic->pp.set & ENIC_SET_HOST) { - sprintf(uuid_str, "%pUB", enic->pp.host_uuid); - vic_provinfo_add_tlv(vp, - VIC_GENERIC_PROV_TLV_HOST_UUID_STR, - sizeof(uuid_str), uuid_str); - } - - os_type = htons(os_type); - vic_provinfo_add_tlv(vp, - VIC_GENERIC_PROV_TLV_OS_TYPE, - sizeof(os_type), &os_type); - - err = enic_dev_init_prov(enic, vp); - vic_provinfo_free(vp); - if (err) - return err; - break; - - case PORT_REQUEST_DISASSOCIATE: - break; - - default: - return -EINVAL; - } - - /* Set flag to indicate that the port assoc/disassoc - * request has been sent out to fw - */ - enic->pp.set |= ENIC_PORT_REQUEST_APPLIED; - - return 0; -} - static int enic_set_vf_port(struct net_device *netdev, int vf, struct nlattr *port[]) { struct enic *enic = netdev_priv(netdev); - struct enic_port_profile new_pp; - int err = 0; + struct enic_port_profile prev_pp; + int err = 0, restore_pp = 1; - memset(&new_pp, 0, sizeof(new_pp)); + /* don't support VFs, yet */ + if (vf != PORT_SELF_VF) + return -EOPNOTSUPP; - if (port[IFLA_PORT_REQUEST]) { - new_pp.set |= ENIC_SET_REQUEST; - new_pp.request = nla_get_u8(port[IFLA_PORT_REQUEST]); - } + if (!port[IFLA_PORT_REQUEST]) + return -EOPNOTSUPP; + + memcpy(&prev_pp, &enic->pp, sizeof(enic->pp)); + memset(&enic->pp, 0, sizeof(enic->pp)); + + enic->pp.set |= ENIC_SET_REQUEST; + enic->pp.request = nla_get_u8(port[IFLA_PORT_REQUEST]); if (port[IFLA_PORT_PROFILE]) { - new_pp.set |= ENIC_SET_NAME; - memcpy(new_pp.name, nla_data(port[IFLA_PORT_PROFILE]), + enic->pp.set |= ENIC_SET_NAME; + memcpy(enic->pp.name, nla_data(port[IFLA_PORT_PROFILE]), PORT_PROFILE_MAX); } if (port[IFLA_PORT_INSTANCE_UUID]) { - new_pp.set |= ENIC_SET_INSTANCE; - memcpy(new_pp.instance_uuid, + enic->pp.set |= ENIC_SET_INSTANCE; + memcpy(enic->pp.instance_uuid, nla_data(port[IFLA_PORT_INSTANCE_UUID]), PORT_UUID_MAX); } if (port[IFLA_PORT_HOST_UUID]) { - new_pp.set |= ENIC_SET_HOST; - memcpy(new_pp.host_uuid, + enic->pp.set |= ENIC_SET_HOST; + memcpy(enic->pp.host_uuid, nla_data(port[IFLA_PORT_HOST_UUID]), PORT_UUID_MAX); } - /* don't support VFs, yet */ - if (vf != PORT_SELF_VF) - return -EOPNOTSUPP; - - if (!(new_pp.set & ENIC_SET_REQUEST)) - return -EOPNOTSUPP; - - if (new_pp.request == PORT_REQUEST_ASSOCIATE) { - /* Special case handling */ - if (!is_zero_ether_addr(enic->pp.vf_mac)) - memcpy(new_pp.mac_addr, enic->pp.vf_mac, ETH_ALEN); + /* Special case handling: mac came from IFLA_VF_MAC */ + if (!is_zero_ether_addr(prev_pp.vf_mac)) + memcpy(enic->pp.mac_addr, prev_pp.vf_mac, ETH_ALEN); if (is_zero_ether_addr(netdev->dev_addr)) random_ether_addr(netdev->dev_addr); - } - memcpy(&enic->pp, &new_pp, sizeof(struct enic_port_profile)); + err = enic_process_set_pp_request(enic, &prev_pp, &restore_pp); + if (err) { + if (restore_pp) { + /* Things are still the way they were: Implicit + * DISASSOCIATE failed + */ + memcpy(&enic->pp, &prev_pp, sizeof(enic->pp)); + } else { + memset(&enic->pp, 0, sizeof(enic->pp)); + memset(netdev->dev_addr, 0, ETH_ALEN); + } + } else { + /* Set flag to indicate that the port assoc/disassoc + * request has been sent out to fw + */ + enic->pp.set |= ENIC_PORT_REQUEST_APPLIED; - err = enic_set_port_profile(enic, netdev->dev_addr); - if (err) - goto set_port_profile_cleanup; + /* If DISASSOCIATE, clean up all assigned/saved macaddresses */ + if (enic->pp.request == PORT_REQUEST_DISASSOCIATE) { + memset(enic->pp.mac_addr, 0, ETH_ALEN); + memset(netdev->dev_addr, 0, ETH_ALEN); + } + } -set_port_profile_cleanup: memset(enic->pp.vf_mac, 0, ETH_ALEN); - if (err || enic->pp.request == PORT_REQUEST_DISASSOCIATE) { - memset(netdev->dev_addr, 0, ETH_ALEN); - memset(enic->pp.mac_addr, 0, ETH_ALEN); - } - return err; } @@ -1270,34 +1131,15 @@ static int enic_get_vf_port(struct net_device *netdev, int vf, struct sk_buff *skb) { struct enic *enic = netdev_priv(netdev); - int err, error, done; u16 response = PORT_PROFILE_RESPONSE_SUCCESS; + int err; if (!(enic->pp.set & ENIC_PORT_REQUEST_APPLIED)) return -ENODATA; - err = enic_dev_init_done(enic, &done, &error); + err = enic_process_get_pp_request(enic, enic->pp.request, &response); if (err) - error = err; - - switch (error) { - case ERR_SUCCESS: - if (!done) - response = PORT_PROFILE_RESPONSE_INPROGRESS; - break; - case ERR_EINVAL: - response = PORT_PROFILE_RESPONSE_INVALID; - break; - case ERR_EBADSTATE: - response = PORT_PROFILE_RESPONSE_BADSTATE; - break; - case ERR_ENOMEM: - response = PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES; - break; - default: - response = PORT_PROFILE_RESPONSE_ERROR; - break; - } + return err; NLA_PUT_U16(skb, IFLA_PORT_REQUEST, enic->pp.request); NLA_PUT_U16(skb, IFLA_PORT_RESPONSE, response); @@ -1407,31 +1249,20 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, skb_put(skb, bytes_written); skb->protocol = eth_type_trans(skb, netdev); - if (enic->csum_rx_enabled && !csum_not_calc) { + if ((netdev->features & NETIF_F_RXCSUM) && !csum_not_calc) { skb->csum = htons(checksum); skb->ip_summed = CHECKSUM_COMPLETE; } skb->dev = netdev; - if (enic->vlan_group && vlan_stripped && - (vlan_tci & CQ_ENET_RQ_DESC_VLAN_TCI_VLAN_MASK)) { + if (vlan_stripped) + __vlan_hwaccel_put_tag(skb, vlan_tci); - if (netdev->features & NETIF_F_GRO) - vlan_gro_receive(&enic->napi[q_number], - enic->vlan_group, vlan_tci, skb); - else - vlan_hwaccel_receive_skb(skb, - enic->vlan_group, vlan_tci); - - } else { - - if (netdev->features & NETIF_F_GRO) - napi_gro_receive(&enic->napi[q_number], skb); - else - netif_receive_skb(skb); - - } + if (netdev->features & NETIF_F_GRO) + napi_gro_receive(&enic->napi[q_number], skb); + else + netif_receive_skb(skb); } else { /* Buffer overflow @@ -1716,7 +1547,7 @@ static void enic_notify_timer_start(struct enic *enic) default: /* Using intr for notification for INTx/MSI-X */ break; - }; + } } /* rtnl lock is held, process context */ @@ -1844,6 +1675,9 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu) if (new_mtu < ENIC_MIN_MTU || new_mtu > ENIC_MAX_MTU) return -EINVAL; + if (enic_is_dynamic(enic)) + return -EOPNOTSUPP; + if (running) enic_stop(netdev); @@ -1860,6 +1694,55 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +static void enic_change_mtu_work(struct work_struct *work) +{ + struct enic *enic = container_of(work, struct enic, change_mtu_work); + struct net_device *netdev = enic->netdev; + int new_mtu = vnic_dev_mtu(enic->vdev); + int err; + unsigned int i; + + new_mtu = max_t(int, ENIC_MIN_MTU, min_t(int, ENIC_MAX_MTU, new_mtu)); + + rtnl_lock(); + + /* Stop RQ */ + del_timer_sync(&enic->notify_timer); + + for (i = 0; i < enic->rq_count; i++) + napi_disable(&enic->napi[i]); + + vnic_intr_mask(&enic->intr[0]); + enic_synchronize_irqs(enic); + err = vnic_rq_disable(&enic->rq[0]); + if (err) { + netdev_err(netdev, "Unable to disable RQ.\n"); + return; + } + vnic_rq_clean(&enic->rq[0], enic_free_rq_buf); + vnic_cq_clean(&enic->cq[0]); + vnic_intr_clean(&enic->intr[0]); + + /* Fill RQ with new_mtu-sized buffers */ + netdev->mtu = new_mtu; + vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); + /* Need at least one buffer on ring to get going */ + if (vnic_rq_desc_used(&enic->rq[0]) == 0) { + netdev_err(netdev, "Unable to alloc receive buffers.\n"); + return; + } + + /* Start RQ */ + vnic_rq_enable(&enic->rq[0]); + napi_enable(&enic->napi[0]); + vnic_intr_unmask(&enic->intr[0]); + enic_notify_timer_start(enic); + + rtnl_unlock(); + + netdev_info(netdev, "interface MTU set as %d\n", netdev->mtu); +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void enic_poll_controller(struct net_device *netdev) { @@ -1874,8 +1757,12 @@ static void enic_poll_controller(struct net_device *netdev) enic_isr_msix_rq(enic->msix_entry[intr].vector, &enic->napi[i]); } - intr = enic_msix_wq_intr(enic, i); - enic_isr_msix_wq(enic->msix_entry[intr].vector, enic); + + for (i = 0; i < enic->wq_count; i++) { + intr = enic_msix_wq_intr(enic, i); + enic_isr_msix_wq(enic->msix_entry[intr].vector, enic); + } + break; case VNIC_DEV_INTR_MODE_MSI: enic_isr_msi(enic->pdev->irq, enic); @@ -2213,13 +2100,12 @@ static const struct net_device_ops enic_netdev_dynamic_ops = { .ndo_open = enic_open, .ndo_stop = enic_stop, .ndo_start_xmit = enic_hard_start_xmit, - .ndo_get_stats = enic_get_stats, + .ndo_get_stats64 = enic_get_stats, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = enic_set_rx_mode, .ndo_set_multicast_list = enic_set_rx_mode, .ndo_set_mac_address = enic_set_mac_address_dynamic, .ndo_change_mtu = enic_change_mtu, - .ndo_vlan_rx_register = enic_vlan_rx_register, .ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid, .ndo_tx_timeout = enic_tx_timeout, @@ -2235,13 +2121,12 @@ static const struct net_device_ops enic_netdev_ops = { .ndo_open = enic_open, .ndo_stop = enic_stop, .ndo_start_xmit = enic_hard_start_xmit, - .ndo_get_stats = enic_get_stats, + .ndo_get_stats64 = enic_get_stats, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = enic_set_mac_address, .ndo_set_rx_mode = enic_set_rx_mode, .ndo_set_multicast_list = enic_set_rx_mode, .ndo_change_mtu = enic_change_mtu, - .ndo_vlan_rx_register = enic_vlan_rx_register, .ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid, .ndo_tx_timeout = enic_tx_timeout, @@ -2268,6 +2153,14 @@ static int enic_dev_init(struct enic *enic) unsigned int i; int err; + /* Get interrupt coalesce timer info */ + err = enic_dev_intr_coal_timer_info(enic); + if (err) { + dev_warn(dev, "Using default conversion factor for " + "interrupt coalesce timer\n"); + vnic_dev_intr_coal_timer_info_default(enic->vdev); + } + /* Get vNIC configuration */ @@ -2501,6 +2394,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, enic->notify_timer.data = (unsigned long)enic; INIT_WORK(&enic->reset, enic_reset); + INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); for (i = 0; i < enic->wq_count; i++) spin_lock_init(&enic->wq_lock[i]); @@ -2536,17 +2430,18 @@ static int __devinit enic_probe(struct pci_dev *pdev, dev_info(dev, "loopback tag=0x%04x\n", enic->loop_tag); } if (ENIC_SETTING(enic, TXCSUM)) - netdev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + netdev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM; if (ENIC_SETTING(enic, TSO)) - netdev->features |= NETIF_F_TSO | + netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN; - if (ENIC_SETTING(enic, LRO)) - netdev->features |= NETIF_F_GRO; + if (ENIC_SETTING(enic, RXCSUM)) + netdev->hw_features |= NETIF_F_RXCSUM; + + netdev->features |= netdev->hw_features; + if (using_dac) netdev->features |= NETIF_F_HIGHDMA; - enic->csum_rx_enabled = ENIC_SETTING(enic, RXCSUM); - err = register_netdev(netdev); if (err) { dev_err(dev, "Cannot register net device, aborting\n"); @@ -2582,6 +2477,7 @@ static void __devexit enic_remove(struct pci_dev *pdev) struct enic *enic = netdev_priv(netdev); cancel_work_sync(&enic->reset); + cancel_work_sync(&enic->change_mtu_work); unregister_netdev(netdev); enic_dev_deinit(enic); vnic_dev_close(enic->vdev); |