- Get total number of rules. There's no user interface for this. It is used to allocate an appropriately sized buffer for getting all the rules. - Get specific rule $ ethtool -u ens9 rule 0 Filter: 0 Rule Type: UDP over IPv4 Src IP addr: 0.0.0.0 mask: 255.255.255.255 Dest IP addr: 192.168.5.2 mask: 0.0.0.0 TOS: 0x0 mask: 0xff Src port: 0 mask: 0xffff Dest port: 4321 mask: 0x0 Action: Direct to queue 16 - Get all rules: $ ethtool -u ens9 31 RX rings available Total 2 rules Filter: 0 Rule Type: UDP over IPv4 Src IP addr: 0.0.0.0 mask: 255.255.255.255 Dest IP addr: 192.168.5.2 mask: 0.0.0.0 ... Filter: 1 Flow Type: Raw Ethernet Src MAC addr: 00:00:00:00:00:00 mask: FF:FF:FF:FF:FF:FF Dest MAC addr: 08:11:22:33:44:54 mask: 00:00:00:00:00:00 Signed-off-by: Daniel Jurgens Reviewed-by: Parav Pandit Reviewed-by: Shahar Shitrit --- v4: Answered questions about rules_limit overflow with no changes. --- drivers/net/virtio_net.c | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 623b318e5375..65c5e75fc8d8 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -307,6 +307,13 @@ static int virtnet_ethtool_flow_insert(struct virtnet_ff *ff, struct ethtool_rx_flow_spec *fs, u16 curr_queue_pairs); static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location); +static int virtnet_ethtool_get_flow_count(struct virtnet_ff *ff, + struct ethtool_rxnfc *info); +static int virtnet_ethtool_get_flow(struct virtnet_ff *ff, + struct ethtool_rxnfc *info); +static int +virtnet_ethtool_get_all_flows(struct virtnet_ff *ff, + struct ethtool_rxnfc *info, u32 *rule_locs); #define VIRTNET_Q_TYPE_RX 0 #define VIRTNET_Q_TYPE_TX 1 @@ -5645,6 +5652,28 @@ static u32 virtnet_get_rx_ring_count(struct net_device *dev) return vi->curr_queue_pairs; } +static int virtnet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rule_locs) +{ + struct virtnet_info *vi = netdev_priv(dev); + int rc = 0; + + switch (info->cmd) { + case ETHTOOL_GRXCLSRLCNT: + rc = virtnet_ethtool_get_flow_count(&vi->ff, info); + break; + case ETHTOOL_GRXCLSRULE: + rc = virtnet_ethtool_get_flow(&vi->ff, info); + break; + case ETHTOOL_GRXCLSRLALL: + rc = virtnet_ethtool_get_all_flows(&vi->ff, info, rule_locs); + break; + default: + rc = -EOPNOTSUPP; + } + + return rc; +} + static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) { struct virtnet_info *vi = netdev_priv(dev); @@ -5686,6 +5715,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = { .get_rxfh_fields = virtnet_get_hashflow, .set_rxfh_fields = virtnet_set_hashflow, .get_rx_ring_count = virtnet_get_rx_ring_count, + .get_rxnfc = virtnet_get_rxnfc, .set_rxnfc = virtnet_set_rxnfc, }; @@ -7605,6 +7635,54 @@ static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location) return err; } +static int virtnet_ethtool_get_flow_count(struct virtnet_ff *ff, + struct ethtool_rxnfc *info) +{ + if (!ff->ff_supported) + return -EOPNOTSUPP; + + info->rule_cnt = ff->ethtool.num_rules; + info->data = le32_to_cpu(ff->ff_caps->rules_limit) | RX_CLS_LOC_SPECIAL; + + return 0; +} + +static int virtnet_ethtool_get_flow(struct virtnet_ff *ff, + struct ethtool_rxnfc *info) +{ + struct virtnet_ethtool_rule *eth_rule; + + if (!ff->ff_supported) + return -EOPNOTSUPP; + + eth_rule = xa_load(&ff->ethtool.rules, info->fs.location); + if (!eth_rule) + return -ENOENT; + + info->fs = eth_rule->flow_spec; + + return 0; +} + +static int +virtnet_ethtool_get_all_flows(struct virtnet_ff *ff, + struct ethtool_rxnfc *info, u32 *rule_locs) +{ + struct virtnet_ethtool_rule *eth_rule; + unsigned long i = 0; + int idx = 0; + + if (!ff->ff_supported) + return -EOPNOTSUPP; + + xa_for_each(&ff->ethtool.rules, i, eth_rule) + rule_locs[idx++] = i; + + info->data = le32_to_cpu(ff->ff_caps->rules_limit); + + return 0; +} + static size_t get_mask_size(u16 type) { switch (type) { -- 2.50.1