// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2021 Huawei Technologies Co., Ltd */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic3_hw.h" #include "hinic3_crm.h" #include "hinic3_nic_dev.h" #include "hinic3_tx.h" #include "hinic3_rx.h" #include "hinic3_rss.h" #define COALESCE_ALL_QUEUE 0xFFFF #define COALESCE_PENDING_LIMIT_UNIT 8 #define COALESCE_TIMER_CFG_UNIT 5 #define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT) #define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT) #define HINIC3_WAIT_PKTS_TO_RX_BUFFER 200 #define HINIC3_WAIT_CLEAR_LP_TEST 100 #ifndef SET_ETHTOOL_OPS #define SET_ETHTOOL_OPS(netdev, ops) \ ((netdev)->ethtool_ops = (ops)) #endif static void hinic3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct pci_dev *pdev = nic_dev->pdev; u8 mgmt_ver[HINIC3_MGMT_VERSION_MAX_LEN] = {0}; int err; strscpy(info->driver, HINIC3_NIC_DRV_NAME, sizeof(info->driver)); strscpy(info->version, HINIC3_NIC_DRV_VERSION, sizeof(info->version)); strscpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info)); err = hinic3_get_mgmt_version(nic_dev->hwdev, mgmt_ver, HINIC3_MGMT_VERSION_MAX_LEN, HINIC3_CHANNEL_NIC); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get fw version\n"); return; } err = snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver); if (err < 0) nicif_err(nic_dev, drv, netdev, "Failed to snprintf fw version\n"); } static u32 hinic3_get_msglevel(struct net_device *netdev) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); return nic_dev->msg_enable; } static void hinic3_set_msglevel(struct net_device *netdev, u32 data) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); nic_dev->msg_enable = data; nicif_info(nic_dev, drv, netdev, "Set message level: 0x%x\n", data); } static int hinic3_nway_reset(struct net_device *netdev) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct nic_port_info port_info = {0}; int err; while (test_and_set_bit(HINIC3_AUTONEG_RESET, &nic_dev->flags)) msleep(100); /* sleep 100 ms, waiting for another autoneg restart progress done */ err = hinic3_get_port_info(nic_dev->hwdev, &port_info, HINIC3_CHANNEL_NIC); if (err) { nicif_err(nic_dev, drv, netdev, "Get port info failed\n"); err = -EFAULT; goto reset_err; } if (port_info.autoneg_state != PORT_CFG_AN_ON) { nicif_err(nic_dev, drv, netdev, "Autonegotiation is not on, don't support to restart it\n"); err = -EOPNOTSUPP; goto reset_err; } err = hinic3_set_autoneg(nic_dev->hwdev, false); if (err) { nicif_err(nic_dev, drv, netdev, "Set autonegotiation off failed\n"); err = -EFAULT; goto reset_err; } msleep(200); /* sleep 200 ms, waiting for status polling finished */ err = hinic3_set_autoneg(nic_dev->hwdev, true); if (err) { nicif_err(nic_dev, drv, netdev, "Set autonegotiation on failed\n"); err = -EFAULT; goto reset_err; } msleep(200); /* sleep 200 ms, waiting for status polling finished */ nicif_info(nic_dev, drv, netdev, "Restart autonegotiation successfully\n"); reset_err: clear_bit(HINIC3_AUTONEG_RESET, &nic_dev->flags); return err; } static void hinic3_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, struct netlink_ext_ack *extack) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); ring->rx_max_pending = HINIC3_MAX_RX_QUEUE_DEPTH; ring->tx_max_pending = HINIC3_MAX_TX_QUEUE_DEPTH; ring->rx_pending = nic_dev->rxqs[0].q_depth; ring->tx_pending = nic_dev->txqs[0].q_depth; } static void hinic3_update_qp_depth(struct hinic3_nic_dev *nic_dev, u32 sq_depth, u32 rq_depth) { u16 i; nic_dev->q_params.sq_depth = sq_depth; nic_dev->q_params.rq_depth = rq_depth; for (i = 0; i < nic_dev->max_qps; i++) { nic_dev->txqs[i].q_depth = sq_depth; nic_dev->txqs[i].q_mask = sq_depth - 1; nic_dev->rxqs[i].q_depth = rq_depth; nic_dev->rxqs[i].q_mask = rq_depth - 1; } } static int check_ringparam_valid(struct net_device *netdev, const struct ethtool_ringparam *ring) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); if (ring->rx_jumbo_pending || ring->rx_mini_pending) { nicif_err(nic_dev, drv, netdev, "Unsupported rx_jumbo_pending/rx_mini_pending\n"); return -EINVAL; } if (ring->tx_pending > HINIC3_MAX_TX_QUEUE_DEPTH || ring->tx_pending < HINIC3_MIN_QUEUE_DEPTH || ring->rx_pending > HINIC3_MAX_RX_QUEUE_DEPTH || ring->rx_pending < HINIC3_MIN_QUEUE_DEPTH) { nicif_err(nic_dev, drv, netdev, "Queue depth out of rang tx[%d-%d] rx[%d-%d]\n", HINIC3_MIN_QUEUE_DEPTH, HINIC3_MAX_TX_QUEUE_DEPTH, HINIC3_MIN_QUEUE_DEPTH, HINIC3_MAX_RX_QUEUE_DEPTH); return -EINVAL; } return 0; } static int hinic3_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, struct netlink_ext_ack *extack) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct hinic3_dyna_txrxq_params q_params = {0}; u32 new_sq_depth, new_rq_depth; int err; err = check_ringparam_valid(netdev, ring); if (err) return err; new_sq_depth = (u32)(1U << (u16)ilog2(ring->tx_pending)); new_rq_depth = (u32)(1U << (u16)ilog2(ring->rx_pending)); if (new_sq_depth == nic_dev->q_params.sq_depth && new_rq_depth == nic_dev->q_params.rq_depth) return 0; /* nothing to do */ nicif_info(nic_dev, drv, netdev, "Change Tx/Rx ring depth from %u/%u to %u/%u\n", nic_dev->q_params.sq_depth, nic_dev->q_params.rq_depth, new_sq_depth, new_rq_depth); if (!netif_running(netdev)) { hinic3_update_qp_depth(nic_dev, new_sq_depth, new_rq_depth); } else { q_params = nic_dev->q_params; q_params.sq_depth = new_sq_depth; q_params.rq_depth = new_rq_depth; q_params.txqs_res = NULL; q_params.rxqs_res = NULL; q_params.irq_cfg = NULL; nicif_info(nic_dev, drv, netdev, "Restarting channel\n"); err = hinic3_change_channel_settings(nic_dev, &q_params, NULL, NULL); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to change channel settings\n"); return -EFAULT; } } return 0; } static int get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, u16 queue) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct hinic3_intr_coal_info *interrupt_info = NULL; if (queue == COALESCE_ALL_QUEUE) { /* get tx/rx irq0 as default parameters */ interrupt_info = &nic_dev->intr_coalesce[0]; } else { if (queue >= nic_dev->q_params.num_qps) { nicif_err(nic_dev, drv, netdev, "Invalid queue_id: %u\n", queue); return -EINVAL; } interrupt_info = &nic_dev->intr_coalesce[queue]; } /* coalescs_timer is in unit of 5us */ coal->rx_coalesce_usecs = interrupt_info->coalesce_timer_cfg * COALESCE_TIMER_CFG_UNIT; /* coalescs_frams is in unit of 8 */ coal->rx_max_coalesced_frames = interrupt_info->pending_limt * COALESCE_PENDING_LIMIT_UNIT; /* tx/rx use the same interrupt */ coal->tx_coalesce_usecs = coal->rx_coalesce_usecs; coal->tx_max_coalesced_frames = coal->rx_max_coalesced_frames; coal->use_adaptive_rx_coalesce = nic_dev->adaptive_rx_coal; coal->pkt_rate_high = (u32)interrupt_info->pkt_rate_high; coal->rx_coalesce_usecs_high = interrupt_info->rx_usecs_high * COALESCE_TIMER_CFG_UNIT; coal->rx_max_coalesced_frames_high = interrupt_info->rx_pending_limt_high * COALESCE_PENDING_LIMIT_UNIT; coal->pkt_rate_low = (u32)interrupt_info->pkt_rate_low; coal->rx_coalesce_usecs_low = interrupt_info->rx_usecs_low * COALESCE_TIMER_CFG_UNIT; coal->rx_max_coalesced_frames_low = interrupt_info->rx_pending_limt_low * COALESCE_PENDING_LIMIT_UNIT; return 0; } static int set_queue_coalesce(struct hinic3_nic_dev *nic_dev, u16 q_id, struct hinic3_intr_coal_info *coal) { struct hinic3_intr_coal_info *intr_coal; struct interrupt_info info = {0}; struct net_device *netdev = nic_dev->netdev; int err; intr_coal = &nic_dev->intr_coalesce[q_id]; if (intr_coal->coalesce_timer_cfg != coal->coalesce_timer_cfg || intr_coal->pending_limt != coal->pending_limt) intr_coal->user_set_intr_coal_flag = 1; intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg; intr_coal->pending_limt = coal->pending_limt; intr_coal->pkt_rate_low = coal->pkt_rate_low; intr_coal->rx_usecs_low = coal->rx_usecs_low; intr_coal->rx_pending_limt_low = coal->rx_pending_limt_low; intr_coal->pkt_rate_high = coal->pkt_rate_high; intr_coal->rx_usecs_high = coal->rx_usecs_high; intr_coal->rx_pending_limt_high = coal->rx_pending_limt_high; /* netdev not running or qp not in using, * don't need to set coalesce to hw */ if (!test_bit(HINIC3_INTF_UP, &nic_dev->flags) || q_id >= nic_dev->q_params.num_qps || nic_dev->adaptive_rx_coal) return 0; info.msix_index = nic_dev->q_params.irq_cfg[q_id].msix_entry_idx; info.lli_set = 0; info.interrupt_coalesc_set = 1; info.coalesc_timer_cfg = intr_coal->coalesce_timer_cfg; info.pending_limt = intr_coal->pending_limt; info.resend_timer_cfg = intr_coal->resend_timer_cfg; nic_dev->rxqs[q_id].last_coalesc_timer_cfg = intr_coal->coalesce_timer_cfg; nic_dev->rxqs[q_id].last_pending_limt = intr_coal->pending_limt; err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info, HINIC3_CHANNEL_NIC); if (err) nicif_warn(nic_dev, drv, netdev, "Failed to set queue%u coalesce", q_id); return err; } static int is_coalesce_exceed_limit(struct net_device *netdev, const struct ethtool_coalesce *coal) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG) { nicif_err(nic_dev, drv, netdev, "rx_coalesce_usecs out of range[%d-%d]\n", 0, COALESCE_MAX_TIMER_CFG); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT) { nicif_err(nic_dev, drv, netdev, "rx_max_coalesced_frames out of range[%d-%d]\n", 0, COALESCE_MAX_PENDING_LIMIT); return -EOPNOTSUPP; } if (coal->rx_coalesce_usecs_low > COALESCE_MAX_TIMER_CFG) { nicif_err(nic_dev, drv, netdev, "rx_coalesce_usecs_low out of range[%d-%d]\n", 0, COALESCE_MAX_TIMER_CFG); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames_low > COALESCE_MAX_PENDING_LIMIT) { nicif_err(nic_dev, drv, netdev, "rx_max_coalesced_frames_low out of range[%d-%d]\n", 0, COALESCE_MAX_PENDING_LIMIT); return -EOPNOTSUPP; } if (coal->rx_coalesce_usecs_high > COALESCE_MAX_TIMER_CFG) { nicif_err(nic_dev, drv, netdev, "rx_coalesce_usecs_high out of range[%d-%d]\n", 0, COALESCE_MAX_TIMER_CFG); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames_high > COALESCE_MAX_PENDING_LIMIT) { nicif_err(nic_dev, drv, netdev, "rx_max_coalesced_frames_high out of range[%d-%d]\n", 0, COALESCE_MAX_PENDING_LIMIT); return -EOPNOTSUPP; } return 0; } static int is_coalesce_legal(struct net_device *netdev, const struct ethtool_coalesce *coal) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct ethtool_coalesce tmp_coal = {0}; int err; if (coal->rx_coalesce_usecs != coal->tx_coalesce_usecs) { nicif_err(nic_dev, drv, netdev, "tx-usecs must be equal to rx-usecs\n"); return -EINVAL; } if (coal->rx_max_coalesced_frames != coal->tx_max_coalesced_frames) { nicif_err(nic_dev, drv, netdev, "tx-frames must be equal to rx-frames\n"); return -EINVAL; } tmp_coal.cmd = coal->cmd; tmp_coal.rx_coalesce_usecs = coal->rx_coalesce_usecs; tmp_coal.rx_max_coalesced_frames = coal->rx_max_coalesced_frames; tmp_coal.tx_coalesce_usecs = coal->tx_coalesce_usecs; tmp_coal.tx_max_coalesced_frames = coal->tx_max_coalesced_frames; tmp_coal.use_adaptive_rx_coalesce = coal->use_adaptive_rx_coalesce; tmp_coal.pkt_rate_low = coal->pkt_rate_low; tmp_coal.rx_coalesce_usecs_low = coal->rx_coalesce_usecs_low; tmp_coal.rx_max_coalesced_frames_low = coal->rx_max_coalesced_frames_low; tmp_coal.pkt_rate_high = coal->pkt_rate_high; tmp_coal.rx_coalesce_usecs_high = coal->rx_coalesce_usecs_high; tmp_coal.rx_max_coalesced_frames_high = coal->rx_max_coalesced_frames_high; if (memcmp(coal, &tmp_coal, sizeof(struct ethtool_coalesce))) { nicif_err(nic_dev, drv, netdev, "Only support to change rx/tx-usecs and rx/tx-frames\n"); return -EOPNOTSUPP; } err = is_coalesce_exceed_limit(netdev, coal); if (err) return err; if (coal->rx_coalesce_usecs_low / COALESCE_TIMER_CFG_UNIT >= coal->rx_coalesce_usecs_high / COALESCE_TIMER_CFG_UNIT) { nicif_err(nic_dev, drv, netdev, "coalesce_usecs_high(%u) must more than coalesce_usecs_low(%u), after dividing %d usecs unit\n", coal->rx_coalesce_usecs_high, coal->rx_coalesce_usecs_low, COALESCE_TIMER_CFG_UNIT); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames_low / COALESCE_PENDING_LIMIT_UNIT >= coal->rx_max_coalesced_frames_high / COALESCE_PENDING_LIMIT_UNIT) { nicif_err(nic_dev, drv, netdev, "coalesced_frames_high(%u) must more than coalesced_frames_low(%u),after dividing %d frames unit\n", coal->rx_max_coalesced_frames_high, coal->rx_max_coalesced_frames_low, COALESCE_PENDING_LIMIT_UNIT); return -EOPNOTSUPP; } if (coal->pkt_rate_low >= coal->pkt_rate_high) { nicif_err(nic_dev, drv, netdev, "pkt_rate_high(%u) must more than pkt_rate_low(%u)\n", coal->pkt_rate_high, coal->pkt_rate_low); return -EOPNOTSUPP; } return 0; } static inline void check_coalesce_align(struct hinic3_nic_dev *nic_dev, struct net_device *netdev, u32 item, u32 unit, char *str) { if (item % unit) nicif_warn(nic_dev, drv, netdev, "%s in %d units, change to %u\n", str, unit, item - item % unit); } #define CHECK_COALESCE_ALIGN(member, unit) \ check_coalesce_align(nic_dev, netdev, member, unit, #member) static inline void check_coalesce_changed(struct hinic3_nic_dev *nic_dev, struct net_device *netdev, u32 item, u32 unit, u32 ori_val, char *obj_str, char *str) { if ((item / unit) != ori_val) nicif_info(nic_dev, drv, netdev, "Change %s from %d to %u %s\n", str, ori_val * unit, item - item % unit, obj_str); } #define CHECK_COALESCE_CHANGED(member, unit, ori_val, obj_str) \ check_coalesce_changed(nic_dev, netdev, member, unit, ori_val, obj_str, #member) static inline void check_pkt_rate_changed(struct hinic3_nic_dev *nic_dev, struct net_device *netdev, u32 item, u32 ori_val, char *obj_str, char *str) { if (item != ori_val) nicif_info(nic_dev, drv, netdev, "Change %s from %d to %u %s\n", str, ori_val, item, obj_str); } #define CHECK_PKT_RATE_CHANGED(member, ori_val, obj_str) \ check_pkt_rate_changed(nic_dev, netdev, member, ori_val, obj_str, #member) static int set_hw_coal_param(struct hinic3_nic_dev *nic_dev, struct hinic3_intr_coal_info *intr_coal, u16 queue) { u16 i; if (queue == COALESCE_ALL_QUEUE) { for (i = 0; i < nic_dev->max_qps; i++) set_queue_coalesce(nic_dev, i, intr_coal); } else { if (queue >= nic_dev->q_params.num_qps) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid queue_id: %u\n", queue); return -EINVAL; } set_queue_coalesce(nic_dev, queue, intr_coal); } return 0; } static int set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, u16 queue) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct hinic3_intr_coal_info intr_coal = {0}; struct hinic3_intr_coal_info *ori_intr_coal = NULL; u32 last_adaptive_rx; char obj_str[32] = {0}; int err = 0; err = is_coalesce_legal(netdev, coal); if (err) return err; CHECK_COALESCE_ALIGN(coal->rx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT); CHECK_COALESCE_ALIGN(coal->rx_max_coalesced_frames, COALESCE_PENDING_LIMIT_UNIT); CHECK_COALESCE_ALIGN(coal->rx_coalesce_usecs_high, COALESCE_TIMER_CFG_UNIT); CHECK_COALESCE_ALIGN(coal->rx_max_coalesced_frames_high, COALESCE_PENDING_LIMIT_UNIT); CHECK_COALESCE_ALIGN(coal->rx_coalesce_usecs_low, COALESCE_TIMER_CFG_UNIT); CHECK_COALESCE_ALIGN(coal->rx_max_coalesced_frames_low, COALESCE_PENDING_LIMIT_UNIT); if (queue == COALESCE_ALL_QUEUE) { ori_intr_coal = &nic_dev->intr_coalesce[0]; snprintf(obj_str, sizeof(obj_str), "for netdev"); } else { ori_intr_coal = &nic_dev->intr_coalesce[queue]; snprintf(obj_str, sizeof(obj_str), "for queue %u", queue); } CHECK_COALESCE_CHANGED(coal->rx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT, ori_intr_coal->coalesce_timer_cfg, obj_str); CHECK_COALESCE_CHANGED(coal->rx_max_coalesced_frames, COALESCE_PENDING_LIMIT_UNIT, ori_intr_coal->pending_limt, obj_str); CHECK_PKT_RATE_CHANGED(coal->pkt_rate_high, ori_intr_coal->pkt_rate_high, obj_str); CHECK_COALESCE_CHANGED(coal->rx_coalesce_usecs_high, COALESCE_TIMER_CFG_UNIT, ori_intr_coal->rx_usecs_high, obj_str); CHECK_COALESCE_CHANGED(coal->rx_max_coalesced_frames_high, COALESCE_PENDING_LIMIT_UNIT, ori_intr_coal->rx_pending_limt_high, obj_str); CHECK_PKT_RATE_CHANGED(coal->pkt_rate_low, ori_intr_coal->pkt_rate_low, obj_str); CHECK_COALESCE_CHANGED(coal->rx_coalesce_usecs_low, COALESCE_TIMER_CFG_UNIT, ori_intr_coal->rx_usecs_low, obj_str); CHECK_COALESCE_CHANGED(coal->rx_max_coalesced_frames_low, COALESCE_PENDING_LIMIT_UNIT, ori_intr_coal->rx_pending_limt_low, obj_str); intr_coal.coalesce_timer_cfg = (u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT); intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames / COALESCE_PENDING_LIMIT_UNIT); last_adaptive_rx = nic_dev->adaptive_rx_coal; nic_dev->adaptive_rx_coal = coal->use_adaptive_rx_coalesce; intr_coal.pkt_rate_high = coal->pkt_rate_high; intr_coal.rx_usecs_high = (u8)(coal->rx_coalesce_usecs_high / COALESCE_TIMER_CFG_UNIT); intr_coal.rx_pending_limt_high = (u8)(coal->rx_max_coalesced_frames_high / COALESCE_PENDING_LIMIT_UNIT); intr_coal.pkt_rate_low = coal->pkt_rate_low; intr_coal.rx_usecs_low = (u8)(coal->rx_coalesce_usecs_low / COALESCE_TIMER_CFG_UNIT); intr_coal.rx_pending_limt_low = (u8)(coal->rx_max_coalesced_frames_low / COALESCE_PENDING_LIMIT_UNIT); /* coalesce timer or pending set to zero will disable coalesce */ if (!nic_dev->adaptive_rx_coal && (!intr_coal.coalesce_timer_cfg || !intr_coal.pending_limt)) nicif_warn(nic_dev, drv, netdev, "Coalesce will be disabled\n"); /* ensure coalesce paramester will not be changed in auto * moderation work */ if (HINIC3_CHANNEL_RES_VALID(nic_dev)) { if (!nic_dev->adaptive_rx_coal) cancel_delayed_work_sync(&nic_dev->moderation_task); else if (!last_adaptive_rx) queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task, HINIC3_MODERATONE_DELAY); } return set_hw_coal_param(nic_dev, &intr_coal, queue); } static int hinic3_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { return get_coalesce(netdev, coal, COALESCE_ALL_QUEUE); } static int hinic3_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { return set_coalesce(netdev, coal, COALESCE_ALL_QUEUE); } #if defined(ETHTOOL_PERQUEUE) && defined(ETHTOOL_GCOALESCE) static int hinic3_get_per_queue_coalesce(struct net_device *netdev, u32 queue, struct ethtool_coalesce *coal) { return get_coalesce(netdev, coal, (u16)queue); } static int hinic3_set_per_queue_coalesce(struct net_device *netdev, u32 queue, struct ethtool_coalesce *coal) { return set_coalesce(netdev, coal, (u16)queue); } #endif #ifdef HAVE_ETHTOOL_SET_PHYS_ID static int hinic3_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); int err; switch (state) { case ETHTOOL_ID_ACTIVE: err = hinic3_set_led_status(nic_dev->hwdev, MAG_CMD_LED_TYPE_ALARM, MAG_CMD_LED_MODE_FORCE_BLINK_2HZ); if (err) nicif_err(nic_dev, drv, netdev, "Set LED blinking in 2HZ failed\n"); else nicif_info(nic_dev, drv, netdev, "Set LED blinking in 2HZ success\n"); break; case ETHTOOL_ID_INACTIVE: err = hinic3_set_led_status(nic_dev->hwdev, MAG_CMD_LED_TYPE_ALARM, MAG_CMD_LED_MODE_DEFAULT); if (err) nicif_err(nic_dev, drv, netdev, "Reset LED to original status failed\n"); else nicif_info(nic_dev, drv, netdev, "Reset LED to original status success\n"); break; default: return -EOPNOTSUPP; } return err; } #else static int hinic3_phys_id(struct net_device *netdev, u32 data) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); nicif_err(nic_dev, drv, netdev, "Not support to set phys id\n"); return -EOPNOTSUPP; } #endif static void hinic3_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct nic_pause_config nic_pause = {0}; int err; err = hinic3_get_pause_info(nic_dev->hwdev, &nic_pause); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get pauseparam from hw\n"); } else { pause->autoneg = nic_pause.auto_neg == PORT_CFG_AN_ON ? AUTONEG_ENABLE : AUTONEG_DISABLE; pause->rx_pause = nic_pause.rx_pause; pause->tx_pause = nic_pause.tx_pause; } } static int hinic3_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); struct nic_pause_config nic_pause = {0}; struct nic_port_info port_info = {0}; u32 auto_neg; int err; err = hinic3_get_port_info(nic_dev->hwdev, &port_info, HINIC3_CHANNEL_NIC); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get auto-negotiation state\n"); return -EFAULT; } auto_neg = port_info.autoneg_state == PORT_CFG_AN_ON ? AUTONEG_ENABLE : AUTONEG_DISABLE; if (pause->autoneg != auto_neg) { nicif_err(nic_dev, drv, netdev, "To change autoneg please use: ethtool -s autoneg \n"); return -EOPNOTSUPP; } nic_pause.auto_neg = pause->autoneg == AUTONEG_ENABLE ? PORT_CFG_AN_ON : PORT_CFG_AN_OFF; nic_pause.rx_pause = (u8)pause->rx_pause; nic_pause.tx_pause = (u8)pause->tx_pause; err = hinic3_set_pause_info(nic_dev->hwdev, nic_pause); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set pauseparam\n"); return err; } nicif_info(nic_dev, drv, netdev, "Set pause options, tx: %s, rx: %s\n", pause->tx_pause ? "on" : "off", pause->rx_pause ? "on" : "off"); return 0; } #ifdef ETHTOOL_GMODULEEEPROM static int hinic3_get_module_info(struct net_device *netdev, struct ethtool_modinfo *modinfo) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); u8 sfp_type = 0; u8 sfp_type_ext = 0; int err; err = hinic3_get_sfp_type(nic_dev->hwdev, &sfp_type, &sfp_type_ext); if (err) return err; switch (sfp_type) { case MODULE_TYPE_SFP: modinfo->type = ETH_MODULE_SFF_8472; modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; break; case MODULE_TYPE_QSFP: modinfo->type = ETH_MODULE_SFF_8436; modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; break; case MODULE_TYPE_QSFP_PLUS: if (sfp_type_ext >= 0x3) { modinfo->type = ETH_MODULE_SFF_8636; modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; } else { modinfo->type = ETH_MODULE_SFF_8436; modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; } break; case MODULE_TYPE_QSFP28: modinfo->type = ETH_MODULE_SFF_8636; modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; break; default: nicif_warn(nic_dev, drv, netdev, "Optical module unknown: 0x%x\n", sfp_type); return -EINVAL; } return 0; } static int hinic3_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, u8 *data) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; int err; if (!ee->len || ((ee->len + ee->offset) > STD_SFP_INFO_MAX_SIZE)) return -EINVAL; memset(data, 0, ee->len); err = hinic3_get_sfp_eeprom(nic_dev->hwdev, (u8 *)sfp_data, ee->len); if (err) return err; memcpy(data, sfp_data + ee->offset, ee->len); return 0; } #endif /* ETHTOOL_GMODULEEEPROM */ #define HINIC3_PRIV_FLAGS_SYMM_RSS BIT(0) #define HINIC3_PRIV_FLAGS_LINK_UP BIT(1) #define HINIC3_PRIV_FLAGS_RXQ_RECOVERY BIT(2) static u32 hinic3_get_priv_flags(struct net_device *netdev) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); u32 priv_flags = 0; if (test_bit(HINIC3_SAME_RXTX, &nic_dev->flags)) priv_flags |= HINIC3_PRIV_FLAGS_SYMM_RSS; if (test_bit(HINIC3_FORCE_LINK_UP, &nic_dev->flags)) priv_flags |= HINIC3_PRIV_FLAGS_LINK_UP; if (test_bit(HINIC3_RXQ_RECOVERY, &nic_dev->flags)) priv_flags |= HINIC3_PRIV_FLAGS_RXQ_RECOVERY; return priv_flags; } int hinic3_set_rxq_recovery_flag(struct net_device *netdev, u32 priv_flags) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); if (priv_flags & HINIC3_PRIV_FLAGS_RXQ_RECOVERY) { if (!HINIC3_SUPPORT_RXQ_RECOVERY(nic_dev->hwdev)) { nicif_info(nic_dev, drv, netdev, "Unsupport open rxq recovery\n"); return -EOPNOTSUPP; } if (test_and_set_bit(HINIC3_RXQ_RECOVERY, &nic_dev->flags)) return 0; queue_delayed_work(nic_dev->workq, &nic_dev->rxq_check_work, HZ); nicif_info(nic_dev, drv, netdev, "open rxq recovery\n"); } else { if (!test_and_clear_bit(HINIC3_RXQ_RECOVERY, &nic_dev->flags)) return 0; cancel_delayed_work_sync(&nic_dev->rxq_check_work); nicif_info(nic_dev, drv, netdev, "close rxq recovery\n"); } return 0; } static int hinic3_set_symm_rss_flag(struct net_device *netdev, u32 priv_flags) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); if (priv_flags & HINIC3_PRIV_FLAGS_SYMM_RSS) { if (test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags)) { nicif_err(nic_dev, drv, netdev, "Failed to open Symmetric RSS while DCB is enabled\n"); return -EOPNOTSUPP; } if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags)) { nicif_err(nic_dev, drv, netdev, "Failed to open Symmetric RSS while RSS is disabled\n"); return -EOPNOTSUPP; } set_bit(HINIC3_SAME_RXTX, &nic_dev->flags); } else { clear_bit(HINIC3_SAME_RXTX, &nic_dev->flags); } return 0; } static int hinic3_set_force_link_flag(struct net_device *netdev, u32 priv_flags) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); u8 link_status = 0; int err; if (priv_flags & HINIC3_PRIV_FLAGS_LINK_UP) { if (test_and_set_bit(HINIC3_FORCE_LINK_UP, &nic_dev->flags)) return 0; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) return 0; if (netif_carrier_ok(netdev)) return 0; nic_dev->link_status = true; netif_carrier_on(netdev); nicif_info(nic_dev, link, netdev, "Set link up\n"); if (!HINIC3_FUNC_IS_VF(nic_dev->hwdev)) hinic3_notify_all_vfs_link_changed(nic_dev->hwdev, nic_dev->link_status); } else { if (!test_and_clear_bit(HINIC3_FORCE_LINK_UP, &nic_dev->flags)) return 0; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) return 0; err = hinic3_get_link_state(nic_dev->hwdev, &link_status); if (err) { nicif_err(nic_dev, link, netdev, "Get link state err: %d\n", err); return err; } nic_dev->link_status = link_status; if (link_status) { if (netif_carrier_ok(netdev)) return 0; netif_carrier_on(netdev); nicif_info(nic_dev, link, netdev, "Link state is up\n"); } else { if (!netif_carrier_ok(netdev)) return 0; netif_carrier_off(netdev); nicif_info(nic_dev, link, netdev, "Link state is down\n"); } if (!HINIC3_FUNC_IS_VF(nic_dev->hwdev)) hinic3_notify_all_vfs_link_changed(nic_dev->hwdev, nic_dev->link_status); } return 0; } static int hinic3_set_priv_flags(struct net_device *netdev, u32 priv_flags) { int err; err = hinic3_set_symm_rss_flag(netdev, priv_flags); if (err) return err; err = hinic3_set_rxq_recovery_flag(netdev, priv_flags); if (err) return err; return hinic3_set_force_link_flag(netdev, priv_flags); } #define PORT_DOWN_ERR_IDX 0 #define LP_DEFAULT_TIME 5 /* seconds */ #define LP_PKT_LEN 60 #define TEST_TIME_MULTIPLE 5 static int hinic3_run_lp_test(struct hinic3_nic_dev *nic_dev, u32 test_time) { u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf; struct net_device *netdev = nic_dev->netdev; u32 cnt = test_time * TEST_TIME_MULTIPLE; struct sk_buff *skb_tmp = NULL; struct ethhdr *eth_hdr = NULL; struct sk_buff *skb = NULL; u8 *test_data = NULL; u32 i; u8 j; skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC); if (!skb_tmp) return -ENOMEM; eth_hdr = __skb_put(skb_tmp, ETH_HLEN); eth_hdr->h_proto = htons(ETH_P_ARP); ether_addr_copy(eth_hdr->h_dest, nic_dev->netdev->dev_addr); eth_zero_addr(eth_hdr->h_source); skb_reset_mac_header(skb_tmp); test_data = __skb_put(skb_tmp, LP_PKT_LEN - ETH_HLEN); for (i = ETH_HLEN; i < LP_PKT_LEN; i++) test_data[i] = i & 0xFF; skb_tmp->queue_mapping = 0; skb_tmp->dev = netdev; skb_tmp->protocol = htons(ETH_P_ARP); for (i = 0; i < cnt; i++) { nic_dev->lb_test_rx_idx = 0; memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN); for (j = 0; j < LP_PKT_CNT; j++) { skb = pskb_copy(skb_tmp, GFP_ATOMIC); if (!skb) { dev_kfree_skb_any(skb_tmp); nicif_err(nic_dev, drv, netdev, "Copy skb failed for loopback test\n"); return -ENOMEM; } /* mark index for every pkt */ skb->data[LP_PKT_LEN - 1] = j; if (hinic3_lb_xmit_frame(skb, netdev)) { dev_kfree_skb_any(skb); dev_kfree_skb_any(skb_tmp); nicif_err(nic_dev, drv, netdev, "Xmit pkt failed for loopback test\n"); return -EBUSY; } } /* wait till all pkts received to RX buffer */ msleep(HINIC3_WAIT_PKTS_TO_RX_BUFFER); for (j = 0; j < LP_PKT_CNT; j++) { if (memcmp((lb_test_rx_buf + (j * LP_PKT_LEN)), skb_tmp->data, (LP_PKT_LEN - 1)) || (*(lb_test_rx_buf + ((j * LP_PKT_LEN) + (LP_PKT_LEN - 1))) != j)) { dev_kfree_skb_any(skb_tmp); nicif_err(nic_dev, drv, netdev, "Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n", (j + (i * LP_PKT_CNT)), (LP_PKT_LEN - 1), *(lb_test_rx_buf + (((j * LP_PKT_LEN) + (LP_PKT_LEN - 1))))); return -EIO; } } } dev_kfree_skb_any(skb_tmp); nicif_info(nic_dev, drv, netdev, "Loopback test succeed.\n"); return 0; } enum diag_test_index { INTERNAL_LP_TEST = 0, EXTERNAL_LP_TEST = 1, DIAG_TEST_MAX = 2, }; #define HINIC3_INTERNAL_LP_MODE 5 static int do_lp_test(struct hinic3_nic_dev *nic_dev, u32 *flags, u32 test_time, enum diag_test_index *test_index) { struct net_device *netdev = nic_dev->netdev; u8 *lb_test_rx_buf = NULL; int err = 0; if (!(*flags & ETH_TEST_FL_EXTERNAL_LB)) { *test_index = INTERNAL_LP_TEST; if (hinic3_set_loopback_mode(nic_dev->hwdev, HINIC3_INTERNAL_LP_MODE, true)) { nicif_err(nic_dev, drv, netdev, "Failed to set port loopback mode before loopback test\n"); return -EFAULT; } /* suspend 5000 ms, waiting for port to stop receiving frames */ msleep(5000); } else { *test_index = EXTERNAL_LP_TEST; } lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN); if (!lb_test_rx_buf) { err = -ENOMEM; } else { nic_dev->lb_test_rx_buf = lb_test_rx_buf; nic_dev->lb_pkt_len = LP_PKT_LEN; set_bit(HINIC3_LP_TEST, &nic_dev->flags); if (hinic3_run_lp_test(nic_dev, test_time)) err = -EFAULT; clear_bit(HINIC3_LP_TEST, &nic_dev->flags); msleep(HINIC3_WAIT_CLEAR_LP_TEST); vfree(lb_test_rx_buf); nic_dev->lb_test_rx_buf = NULL; } if (!(*flags & ETH_TEST_FL_EXTERNAL_LB)) { if (hinic3_set_loopback_mode(nic_dev->hwdev, HINIC3_INTERNAL_LP_MODE, false)) { nicif_err(nic_dev, drv, netdev, "Failed to cancel port loopback mode after loopback test\n"); err = -EFAULT; } } else { *flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; } return err; } static void hinic3_lp_test(struct net_device *netdev, struct ethtool_test *eth_test, u64 *data, u32 test_time) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); enum diag_test_index test_index = 0; u8 link_status = 0; int err; u32 test_time_real = test_time; /* don't support loopback test when netdev is closed. */ if (!test_bit(HINIC3_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, netdev, "Do not support loopback test when netdev is closed\n"); eth_test->flags |= ETH_TEST_FL_FAILED; data[PORT_DOWN_ERR_IDX] = 1; return; } if (test_time_real == 0) test_time_real = LP_DEFAULT_TIME; netif_carrier_off(netdev); netif_tx_disable(netdev); err = do_lp_test(nic_dev, ð_test->flags, test_time_real, &test_index); if (err) { eth_test->flags |= ETH_TEST_FL_FAILED; data[test_index] = 1; } netif_tx_wake_all_queues(netdev); err = hinic3_get_link_state(nic_dev->hwdev, &link_status); if (!err && link_status) netif_carrier_on(netdev); } static void hinic3_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, u64 *data) { memset(data, 0, DIAG_TEST_MAX * sizeof(u64)); hinic3_lp_test(netdev, eth_test, data, 0); } static const struct ethtool_ops hinic3_ethtool_ops = { #ifdef SUPPORTED_COALESCE_PARAMS .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_PKT_RATE_RX_USECS, #endif #ifdef ETHTOOL_GLINKSETTINGS #ifndef XENSERVER_HAVE_NEW_ETHTOOL_OPS .get_link_ksettings = hinic3_get_link_ksettings, .set_link_ksettings = hinic3_set_link_ksettings, #endif #endif #ifndef HAVE_NEW_ETHTOOL_LINK_SETTINGS_ONLY .get_settings = hinic3_get_settings, .set_settings = hinic3_set_settings, #endif .get_drvinfo = hinic3_get_drvinfo, .get_msglevel = hinic3_get_msglevel, .set_msglevel = hinic3_set_msglevel, .nway_reset = hinic3_nway_reset, #ifdef CONFIG_MODULE_PROF .get_link = hinic3_get_link, #else .get_link = ethtool_op_get_link, #endif .get_ringparam = hinic3_get_ringparam, .set_ringparam = hinic3_set_ringparam, .get_pauseparam = hinic3_get_pauseparam, .set_pauseparam = hinic3_set_pauseparam, .get_sset_count = hinic3_get_sset_count, .get_ethtool_stats = hinic3_get_ethtool_stats, .get_strings = hinic3_get_strings, .self_test = hinic3_diag_test, #ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT #ifdef HAVE_ETHTOOL_SET_PHYS_ID .set_phys_id = hinic3_set_phys_id, #else .phys_id = hinic3_phys_id, #endif #endif .get_coalesce = hinic3_get_coalesce, .set_coalesce = hinic3_set_coalesce, #if defined(ETHTOOL_PERQUEUE) && defined(ETHTOOL_GCOALESCE) .get_per_queue_coalesce = hinic3_get_per_queue_coalesce, .set_per_queue_coalesce = hinic3_set_per_queue_coalesce, #endif .get_rxnfc = hinic3_get_rxnfc, .set_rxnfc = hinic3_set_rxnfc, .get_priv_flags = hinic3_get_priv_flags, .set_priv_flags = hinic3_set_priv_flags, #ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT .get_channels = hinic3_get_channels, .set_channels = hinic3_set_channels, #ifdef ETHTOOL_GMODULEEEPROM .get_module_info = hinic3_get_module_info, .get_module_eeprom = hinic3_get_module_eeprom, #endif #ifndef NOT_HAVE_GET_RXFH_INDIR_SIZE .get_rxfh_indir_size = hinic3_get_rxfh_indir_size, #endif #if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) .get_rxfh_key_size = hinic3_get_rxfh_key_size, .get_rxfh = hinic3_get_rxfh, .set_rxfh = hinic3_set_rxfh, #else .get_rxfh_indir = hinic3_get_rxfh_indir, .set_rxfh_indir = hinic3_set_rxfh_indir, #endif #endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ }; #ifdef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT static const struct ethtool_ops_ext hinic3_ethtool_ops_ext = { .size = sizeof(struct ethtool_ops_ext), .set_phys_id = hinic3_set_phys_id, .get_channels = hinic3_get_channels, .set_channels = hinic3_set_channels, #ifdef ETHTOOL_GMODULEEEPROM .get_module_info = hinic3_get_module_info, .get_module_eeprom = hinic3_get_module_eeprom, #endif #ifndef NOT_HAVE_GET_RXFH_INDIR_SIZE .get_rxfh_indir_size = hinic3_get_rxfh_indir_size, #endif #if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) .get_rxfh_key_size = hinic3_get_rxfh_key_size, .get_rxfh = hinic3_get_rxfh, .set_rxfh = hinic3_set_rxfh, #else .get_rxfh_indir = hinic3_get_rxfh_indir, .set_rxfh_indir = hinic3_set_rxfh_indir, #endif }; #endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ static const struct ethtool_ops hinic3vf_ethtool_ops = { #ifdef SUPPORTED_COALESCE_PARAMS .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_PKT_RATE_RX_USECS, #endif #ifdef ETHTOOL_GLINKSETTINGS #ifndef XENSERVER_HAVE_NEW_ETHTOOL_OPS .get_link_ksettings = hinic3_get_link_ksettings, #endif #else .get_settings = hinic3_get_settings, #endif .get_drvinfo = hinic3_get_drvinfo, .get_msglevel = hinic3_get_msglevel, .set_msglevel = hinic3_set_msglevel, .get_link = ethtool_op_get_link, .get_ringparam = hinic3_get_ringparam, .set_ringparam = hinic3_set_ringparam, .get_sset_count = hinic3_get_sset_count, .get_ethtool_stats = hinic3_get_ethtool_stats, .get_strings = hinic3_get_strings, .get_coalesce = hinic3_get_coalesce, .set_coalesce = hinic3_set_coalesce, #if defined(ETHTOOL_PERQUEUE) && defined(ETHTOOL_GCOALESCE) .get_per_queue_coalesce = hinic3_get_per_queue_coalesce, .set_per_queue_coalesce = hinic3_set_per_queue_coalesce, #endif .get_rxnfc = hinic3_get_rxnfc, .set_rxnfc = hinic3_set_rxnfc, .get_priv_flags = hinic3_get_priv_flags, .set_priv_flags = hinic3_set_priv_flags, #ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT .get_channels = hinic3_get_channels, .set_channels = hinic3_set_channels, #ifndef NOT_HAVE_GET_RXFH_INDIR_SIZE .get_rxfh_indir_size = hinic3_get_rxfh_indir_size, #endif #if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) .get_rxfh_key_size = hinic3_get_rxfh_key_size, .get_rxfh = hinic3_get_rxfh, .set_rxfh = hinic3_set_rxfh, #else .get_rxfh_indir = hinic3_get_rxfh_indir, .set_rxfh_indir = hinic3_set_rxfh_indir, #endif #endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ }; #ifdef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT static const struct ethtool_ops_ext hinic3vf_ethtool_ops_ext = { .size = sizeof(struct ethtool_ops_ext), .get_channels = hinic3_get_channels, .set_channels = hinic3_set_channels, #ifndef NOT_HAVE_GET_RXFH_INDIR_SIZE .get_rxfh_indir_size = hinic3_get_rxfh_indir_size, #endif #if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) .get_rxfh_key_size = hinic3_get_rxfh_key_size, .get_rxfh = hinic3_get_rxfh, .set_rxfh = hinic3_set_rxfh, #else .get_rxfh_indir = hinic3_get_rxfh_indir, .set_rxfh_indir = hinic3_set_rxfh_indir, #endif }; #endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ void hinic3_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &hinic3_ethtool_ops); #ifdef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT set_ethtool_ops_ext(netdev, &hinic3_ethtool_ops_ext); #endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ } void hinic3vf_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &hinic3vf_ethtool_ops); #ifdef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT set_ethtool_ops_ext(netdev, &hinic3vf_ethtool_ops_ext); #endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ }