538 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */3 4#include <linux/pci.h>5#include <linux/phylink.h>6#include <linux/netdevice.h>7 8#include "../libwx/wx_ethtool.h"9#include "../libwx/wx_type.h"10#include "../libwx/wx_lib.h"11#include "txgbe_type.h"12#include "txgbe_fdir.h"13#include "txgbe_ethtool.h"14 15static int txgbe_set_ringparam(struct net_device *netdev,16 struct ethtool_ringparam *ring,17 struct kernel_ethtool_ringparam *kernel_ring,18 struct netlink_ext_ack *extack)19{20 struct wx *wx = netdev_priv(netdev);21 u32 new_rx_count, new_tx_count;22 struct wx_ring *temp_ring;23 int i, err = 0;24 25 new_tx_count = clamp_t(u32, ring->tx_pending, WX_MIN_TXD, WX_MAX_TXD);26 new_tx_count = ALIGN(new_tx_count, WX_REQ_TX_DESCRIPTOR_MULTIPLE);27 28 new_rx_count = clamp_t(u32, ring->rx_pending, WX_MIN_RXD, WX_MAX_RXD);29 new_rx_count = ALIGN(new_rx_count, WX_REQ_RX_DESCRIPTOR_MULTIPLE);30 31 if (new_tx_count == wx->tx_ring_count &&32 new_rx_count == wx->rx_ring_count)33 return 0;34 35 err = wx_set_state_reset(wx);36 if (err)37 return err;38 39 if (!netif_running(wx->netdev)) {40 for (i = 0; i < wx->num_tx_queues; i++)41 wx->tx_ring[i]->count = new_tx_count;42 for (i = 0; i < wx->num_rx_queues; i++)43 wx->rx_ring[i]->count = new_rx_count;44 wx->tx_ring_count = new_tx_count;45 wx->rx_ring_count = new_rx_count;46 47 goto clear_reset;48 }49 50 /* allocate temporary buffer to store rings in */51 i = max_t(int, wx->num_tx_queues, wx->num_rx_queues);52 temp_ring = kvmalloc_array(i, sizeof(struct wx_ring), GFP_KERNEL);53 if (!temp_ring) {54 err = -ENOMEM;55 goto clear_reset;56 }57 58 txgbe_down(wx);59 60 wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);61 kvfree(temp_ring);62 63 txgbe_up(wx);64 65clear_reset:66 clear_bit(WX_STATE_RESETTING, wx->state);67 return err;68}69 70static int txgbe_set_channels(struct net_device *dev,71 struct ethtool_channels *ch)72{73 int err;74 75 err = wx_set_channels(dev, ch);76 if (err < 0)77 return err;78 79 /* use setup TC to update any traffic class queue mapping */80 return txgbe_setup_tc(dev, netdev_get_num_tc(dev));81}82 83static int txgbe_get_ethtool_fdir_entry(struct txgbe *txgbe,84 struct ethtool_rxnfc *cmd)85{86 struct ethtool_rx_flow_spec *fsp =87 (struct ethtool_rx_flow_spec *)&cmd->fs;88 union txgbe_atr_input *mask = &txgbe->fdir_mask;89 struct txgbe_fdir_filter *rule = NULL;90 struct hlist_node *node;91 92 /* report total rule count */93 cmd->data = (1024 << TXGBE_FDIR_PBALLOC_64K) - 2;94 95 hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,96 fdir_node) {97 if (fsp->location <= rule->sw_idx)98 break;99 }100 101 if (!rule || fsp->location != rule->sw_idx)102 return -EINVAL;103 104 /* set flow type field */105 switch (rule->filter.formatted.flow_type) {106 case TXGBE_ATR_FLOW_TYPE_TCPV4:107 fsp->flow_type = TCP_V4_FLOW;108 break;109 case TXGBE_ATR_FLOW_TYPE_UDPV4:110 fsp->flow_type = UDP_V4_FLOW;111 break;112 case TXGBE_ATR_FLOW_TYPE_SCTPV4:113 fsp->flow_type = SCTP_V4_FLOW;114 break;115 case TXGBE_ATR_FLOW_TYPE_IPV4:116 fsp->flow_type = IP_USER_FLOW;117 fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;118 fsp->h_u.usr_ip4_spec.proto = 0;119 fsp->m_u.usr_ip4_spec.proto = 0;120 break;121 default:122 return -EINVAL;123 }124 125 fsp->h_u.tcp_ip4_spec.psrc = rule->filter.formatted.src_port;126 fsp->m_u.tcp_ip4_spec.psrc = mask->formatted.src_port;127 fsp->h_u.tcp_ip4_spec.pdst = rule->filter.formatted.dst_port;128 fsp->m_u.tcp_ip4_spec.pdst = mask->formatted.dst_port;129 fsp->h_u.tcp_ip4_spec.ip4src = rule->filter.formatted.src_ip[0];130 fsp->m_u.tcp_ip4_spec.ip4src = mask->formatted.src_ip[0];131 fsp->h_u.tcp_ip4_spec.ip4dst = rule->filter.formatted.dst_ip[0];132 fsp->m_u.tcp_ip4_spec.ip4dst = mask->formatted.dst_ip[0];133 fsp->h_ext.vlan_etype = rule->filter.formatted.flex_bytes;134 fsp->m_ext.vlan_etype = mask->formatted.flex_bytes;135 fsp->h_ext.data[1] = htonl(rule->filter.formatted.vm_pool);136 fsp->m_ext.data[1] = htonl(mask->formatted.vm_pool);137 fsp->flow_type |= FLOW_EXT;138 139 /* record action */140 if (rule->action == TXGBE_RDB_FDIR_DROP_QUEUE)141 fsp->ring_cookie = RX_CLS_FLOW_DISC;142 else143 fsp->ring_cookie = rule->action;144 145 return 0;146}147 148static int txgbe_get_ethtool_fdir_all(struct txgbe *txgbe,149 struct ethtool_rxnfc *cmd,150 u32 *rule_locs)151{152 struct txgbe_fdir_filter *rule;153 struct hlist_node *node;154 int cnt = 0;155 156 /* report total rule count */157 cmd->data = (1024 << TXGBE_FDIR_PBALLOC_64K) - 2;158 159 hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,160 fdir_node) {161 if (cnt == cmd->rule_cnt)162 return -EMSGSIZE;163 rule_locs[cnt] = rule->sw_idx;164 cnt++;165 }166 167 cmd->rule_cnt = cnt;168 169 return 0;170}171 172static int txgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,173 u32 *rule_locs)174{175 struct wx *wx = netdev_priv(dev);176 struct txgbe *txgbe = wx->priv;177 int ret = -EOPNOTSUPP;178 179 switch (cmd->cmd) {180 case ETHTOOL_GRXRINGS:181 cmd->data = wx->num_rx_queues;182 ret = 0;183 break;184 case ETHTOOL_GRXCLSRLCNT:185 cmd->rule_cnt = txgbe->fdir_filter_count;186 ret = 0;187 break;188 case ETHTOOL_GRXCLSRULE:189 ret = txgbe_get_ethtool_fdir_entry(txgbe, cmd);190 break;191 case ETHTOOL_GRXCLSRLALL:192 ret = txgbe_get_ethtool_fdir_all(txgbe, cmd, (u32 *)rule_locs);193 break;194 default:195 break;196 }197 198 return ret;199}200 201static int txgbe_flowspec_to_flow_type(struct ethtool_rx_flow_spec *fsp,202 u8 *flow_type)203{204 switch (fsp->flow_type & ~FLOW_EXT) {205 case TCP_V4_FLOW:206 *flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;207 break;208 case UDP_V4_FLOW:209 *flow_type = TXGBE_ATR_FLOW_TYPE_UDPV4;210 break;211 case SCTP_V4_FLOW:212 *flow_type = TXGBE_ATR_FLOW_TYPE_SCTPV4;213 break;214 case IP_USER_FLOW:215 switch (fsp->h_u.usr_ip4_spec.proto) {216 case IPPROTO_TCP:217 *flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;218 break;219 case IPPROTO_UDP:220 *flow_type = TXGBE_ATR_FLOW_TYPE_UDPV4;221 break;222 case IPPROTO_SCTP:223 *flow_type = TXGBE_ATR_FLOW_TYPE_SCTPV4;224 break;225 case 0:226 if (!fsp->m_u.usr_ip4_spec.proto) {227 *flow_type = TXGBE_ATR_FLOW_TYPE_IPV4;228 break;229 }230 fallthrough;231 default:232 return -EINVAL;233 }234 break;235 default:236 return -EINVAL;237 }238 239 return 0;240}241 242static bool txgbe_match_ethtool_fdir_entry(struct txgbe *txgbe,243 struct txgbe_fdir_filter *input)244{245 struct txgbe_fdir_filter *rule = NULL;246 struct hlist_node *node2;247 248 hlist_for_each_entry_safe(rule, node2, &txgbe->fdir_filter_list,249 fdir_node) {250 if (rule->filter.formatted.bkt_hash ==251 input->filter.formatted.bkt_hash &&252 rule->action == input->action) {253 wx_dbg(txgbe->wx, "FDIR entry already exist\n");254 return true;255 }256 }257 return false;258}259 260static int txgbe_update_ethtool_fdir_entry(struct txgbe *txgbe,261 struct txgbe_fdir_filter *input,262 u16 sw_idx)263{264 struct hlist_node *node = NULL, *parent = NULL;265 struct txgbe_fdir_filter *rule;266 struct wx *wx = txgbe->wx;267 bool deleted = false;268 int err;269 270 hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,271 fdir_node) {272 /* hash found, or no matching entry */273 if (rule->sw_idx >= sw_idx)274 break;275 parent = node;276 }277 278 /* if there is an old rule occupying our place remove it */279 if (rule && rule->sw_idx == sw_idx) {280 /* hardware filters are only configured when interface is up,281 * and we should not issue filter commands while the interface282 * is down283 */284 if (netif_running(wx->netdev) &&285 (!input || rule->filter.formatted.bkt_hash !=286 input->filter.formatted.bkt_hash)) {287 err = txgbe_fdir_erase_perfect_filter(wx,288 &rule->filter,289 sw_idx);290 if (err)291 return -EINVAL;292 }293 294 hlist_del(&rule->fdir_node);295 kfree(rule);296 txgbe->fdir_filter_count--;297 deleted = true;298 }299 300 /* If we weren't given an input, then this was a request to delete a301 * filter. We should return -EINVAL if the filter wasn't found, but302 * return 0 if the rule was successfully deleted.303 */304 if (!input)305 return deleted ? 0 : -EINVAL;306 307 /* initialize node and set software index */308 INIT_HLIST_NODE(&input->fdir_node);309 310 /* add filter to the list */311 if (parent)312 hlist_add_behind(&input->fdir_node, parent);313 else314 hlist_add_head(&input->fdir_node,315 &txgbe->fdir_filter_list);316 317 /* update counts */318 txgbe->fdir_filter_count++;319 320 return 0;321}322 323static int txgbe_add_ethtool_fdir_entry(struct txgbe *txgbe,324 struct ethtool_rxnfc *cmd)325{326 struct ethtool_rx_flow_spec *fsp =327 (struct ethtool_rx_flow_spec *)&cmd->fs;328 struct txgbe_fdir_filter *input;329 union txgbe_atr_input mask;330 struct wx *wx = txgbe->wx;331 int err = -EINVAL;332 u16 ptype = 0;333 u8 queue;334 335 if (!(test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)))336 return -EOPNOTSUPP;337 338 /* ring_cookie is a masked into a set of queues and txgbe pools or339 * we use drop index340 */341 if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {342 queue = TXGBE_RDB_FDIR_DROP_QUEUE;343 } else {344 u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);345 346 if (ring >= wx->num_rx_queues)347 return -EINVAL;348 349 /* Map the ring onto the absolute queue index */350 queue = wx->rx_ring[ring]->reg_idx;351 }352 353 /* Don't allow indexes to exist outside of available space */354 if (fsp->location >= ((1024 << TXGBE_FDIR_PBALLOC_64K) - 2)) {355 wx_err(wx, "Location out of range\n");356 return -EINVAL;357 }358 359 input = kzalloc(sizeof(*input), GFP_ATOMIC);360 if (!input)361 return -ENOMEM;362 363 memset(&mask, 0, sizeof(union txgbe_atr_input));364 365 /* set SW index */366 input->sw_idx = fsp->location;367 368 /* record flow type */369 if (txgbe_flowspec_to_flow_type(fsp,370 &input->filter.formatted.flow_type)) {371 wx_err(wx, "Unrecognized flow type\n");372 goto err_out;373 }374 375 mask.formatted.flow_type = TXGBE_ATR_L4TYPE_IPV6_MASK |376 TXGBE_ATR_L4TYPE_MASK;377 378 if (input->filter.formatted.flow_type == TXGBE_ATR_FLOW_TYPE_IPV4)379 mask.formatted.flow_type &= TXGBE_ATR_L4TYPE_IPV6_MASK;380 381 /* Copy input into formatted structures */382 input->filter.formatted.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;383 mask.formatted.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;384 input->filter.formatted.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;385 mask.formatted.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;386 input->filter.formatted.src_port = fsp->h_u.tcp_ip4_spec.psrc;387 mask.formatted.src_port = fsp->m_u.tcp_ip4_spec.psrc;388 input->filter.formatted.dst_port = fsp->h_u.tcp_ip4_spec.pdst;389 mask.formatted.dst_port = fsp->m_u.tcp_ip4_spec.pdst;390 391 if (fsp->flow_type & FLOW_EXT) {392 input->filter.formatted.vm_pool =393 (unsigned char)ntohl(fsp->h_ext.data[1]);394 mask.formatted.vm_pool =395 (unsigned char)ntohl(fsp->m_ext.data[1]);396 input->filter.formatted.flex_bytes =397 fsp->h_ext.vlan_etype;398 mask.formatted.flex_bytes = fsp->m_ext.vlan_etype;399 }400 401 switch (input->filter.formatted.flow_type) {402 case TXGBE_ATR_FLOW_TYPE_TCPV4:403 ptype = WX_PTYPE_L2_IPV4_TCP;404 break;405 case TXGBE_ATR_FLOW_TYPE_UDPV4:406 ptype = WX_PTYPE_L2_IPV4_UDP;407 break;408 case TXGBE_ATR_FLOW_TYPE_SCTPV4:409 ptype = WX_PTYPE_L2_IPV4_SCTP;410 break;411 case TXGBE_ATR_FLOW_TYPE_IPV4:412 ptype = WX_PTYPE_L2_IPV4;413 break;414 default:415 break;416 }417 418 input->filter.formatted.vlan_id = htons(ptype);419 if (mask.formatted.flow_type & TXGBE_ATR_L4TYPE_MASK)420 mask.formatted.vlan_id = htons(0xFFFF);421 else422 mask.formatted.vlan_id = htons(0xFFF8);423 424 /* determine if we need to drop or route the packet */425 if (fsp->ring_cookie == RX_CLS_FLOW_DISC)426 input->action = TXGBE_RDB_FDIR_DROP_QUEUE;427 else428 input->action = fsp->ring_cookie;429 430 spin_lock(&txgbe->fdir_perfect_lock);431 432 if (hlist_empty(&txgbe->fdir_filter_list)) {433 /* save mask and program input mask into HW */434 memcpy(&txgbe->fdir_mask, &mask, sizeof(mask));435 err = txgbe_fdir_set_input_mask(wx, &mask);436 if (err)437 goto err_unlock;438 } else if (memcmp(&txgbe->fdir_mask, &mask, sizeof(mask))) {439 wx_err(wx, "Hardware only supports one mask per port. To change the mask you must first delete all the rules.\n");440 goto err_unlock;441 }442 443 /* apply mask and compute/store hash */444 txgbe_atr_compute_perfect_hash(&input->filter, &mask);445 446 /* check if new entry does not exist on filter list */447 if (txgbe_match_ethtool_fdir_entry(txgbe, input))448 goto err_unlock;449 450 /* only program filters to hardware if the net device is running, as451 * we store the filters in the Rx buffer which is not allocated when452 * the device is down453 */454 if (netif_running(wx->netdev)) {455 err = txgbe_fdir_write_perfect_filter(wx, &input->filter,456 input->sw_idx, queue);457 if (err)458 goto err_unlock;459 }460 461 txgbe_update_ethtool_fdir_entry(txgbe, input, input->sw_idx);462 463 spin_unlock(&txgbe->fdir_perfect_lock);464 465 return 0;466err_unlock:467 spin_unlock(&txgbe->fdir_perfect_lock);468err_out:469 kfree(input);470 return err;471}472 473static int txgbe_del_ethtool_fdir_entry(struct txgbe *txgbe,474 struct ethtool_rxnfc *cmd)475{476 struct ethtool_rx_flow_spec *fsp =477 (struct ethtool_rx_flow_spec *)&cmd->fs;478 int err = 0;479 480 spin_lock(&txgbe->fdir_perfect_lock);481 err = txgbe_update_ethtool_fdir_entry(txgbe, NULL, fsp->location);482 spin_unlock(&txgbe->fdir_perfect_lock);483 484 return err;485}486 487static int txgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)488{489 struct wx *wx = netdev_priv(dev);490 struct txgbe *txgbe = wx->priv;491 int ret = -EOPNOTSUPP;492 493 switch (cmd->cmd) {494 case ETHTOOL_SRXCLSRLINS:495 ret = txgbe_add_ethtool_fdir_entry(txgbe, cmd);496 break;497 case ETHTOOL_SRXCLSRLDEL:498 ret = txgbe_del_ethtool_fdir_entry(txgbe, cmd);499 break;500 default:501 break;502 }503 504 return ret;505}506 507static const struct ethtool_ops txgbe_ethtool_ops = {508 .supported_coalesce_params = ETHTOOL_COALESCE_USECS |509 ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ,510 .get_drvinfo = wx_get_drvinfo,511 .nway_reset = wx_nway_reset,512 .get_link = ethtool_op_get_link,513 .get_link_ksettings = wx_get_link_ksettings,514 .set_link_ksettings = wx_set_link_ksettings,515 .get_sset_count = wx_get_sset_count,516 .get_strings = wx_get_strings,517 .get_ethtool_stats = wx_get_ethtool_stats,518 .get_eth_mac_stats = wx_get_mac_stats,519 .get_pause_stats = wx_get_pause_stats,520 .get_pauseparam = wx_get_pauseparam,521 .set_pauseparam = wx_set_pauseparam,522 .get_ringparam = wx_get_ringparam,523 .set_ringparam = txgbe_set_ringparam,524 .get_coalesce = wx_get_coalesce,525 .set_coalesce = wx_set_coalesce,526 .get_channels = wx_get_channels,527 .set_channels = txgbe_set_channels,528 .get_rxnfc = txgbe_get_rxnfc,529 .set_rxnfc = txgbe_set_rxnfc,530 .get_msglevel = wx_get_msglevel,531 .set_msglevel = wx_set_msglevel,532};533 534void txgbe_set_ethtool_ops(struct net_device *netdev)535{536 netdev->ethtool_ops = &txgbe_ethtool_ops;537}538