Loading drivers/net/ethernet/sfc/ethtool.c +149 −39 Original line number Diff line number Diff line Loading @@ -818,9 +818,58 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) return efx_reset(efx, rc); } static int efx_ethtool_get_class_rule(struct efx_nic *efx, struct ethtool_rx_flow_spec *rule) { struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; struct efx_filter_spec spec; u16 vid; u8 proto; int rc; rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL, rule->location, &spec); if (rc) return rc; if (spec.dmaq_id == 0xfff) rule->ring_cookie = RX_CLS_FLOW_DISC; else rule->ring_cookie = spec.dmaq_id; rc = efx_filter_get_eth_local(&spec, &vid, rule->h_u.ether_spec.h_dest); if (rc == 0) { rule->flow_type = ETHER_FLOW; memset(rule->m_u.ether_spec.h_dest, ~0, ETH_ALEN); if (vid != EFX_FILTER_VID_UNSPEC) { rule->flow_type |= FLOW_EXT; rule->h_ext.vlan_tci = htons(vid); rule->m_ext.vlan_tci = htons(0xfff); } return 0; } rc = efx_filter_get_ipv4_local(&spec, &proto, &ip_entry->ip4dst, &ip_entry->pdst); if (rc != 0) { rc = efx_filter_get_ipv4_full( &spec, &proto, &ip_entry->ip4src, &ip_entry->psrc, &ip_entry->ip4dst, &ip_entry->pdst); EFX_WARN_ON_PARANOID(rc); ip_mask->ip4src = ~0; ip_mask->psrc = ~0; } rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW; ip_mask->ip4dst = ~0; ip_mask->pdst = ~0; return rc; } static int efx_ethtool_get_rxnfc(struct net_device *net_dev, struct ethtool_rxnfc *info, u32 *rules __always_unused) struct ethtool_rxnfc *info, u32 *rule_locs) { struct efx_nic *efx = netdev_priv(net_dev); Loading Loading @@ -862,42 +911,80 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, return 0; } case ETHTOOL_GRXCLSRLCNT: info->data = efx_filter_get_rx_id_limit(efx); if (info->data == 0) return -EOPNOTSUPP; info->data |= RX_CLS_LOC_SPECIAL; info->rule_cnt = efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL); return 0; case ETHTOOL_GRXCLSRULE: if (efx_filter_get_rx_id_limit(efx) == 0) return -EOPNOTSUPP; return efx_ethtool_get_class_rule(efx, &info->fs); case ETHTOOL_GRXCLSRLALL: { s32 rc; info->data = efx_filter_get_rx_id_limit(efx); if (info->data == 0) return -EOPNOTSUPP; rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL, rule_locs, info->rule_cnt); if (rc < 0) return rc; info->rule_cnt = rc; return 0; } default: return -EOPNOTSUPP; } } static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, struct ethtool_rx_ntuple *ntuple) static int efx_ethtool_set_class_rule(struct efx_nic *efx, struct ethtool_rx_flow_spec *rule) { struct efx_nic *efx = netdev_priv(net_dev); struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec; struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec; struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec; struct efx_filter_spec filter; struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; struct ethhdr *mac_entry = &rule->h_u.ether_spec; struct ethhdr *mac_mask = &rule->m_u.ether_spec; struct efx_filter_spec spec; int rc; /* Range-check action */ if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR || ntuple->fs.action >= (s32)efx->n_rx_channels) /* Check that user wants us to choose the location */ if (rule->location != RX_CLS_LOC_ANY && rule->location != RX_CLS_LOC_FIRST && rule->location != RX_CLS_LOC_LAST) return -EINVAL; if (~ntuple->fs.data_mask) /* Range-check ring_cookie */ if (rule->ring_cookie >= efx->n_rx_channels && rule->ring_cookie != RX_CLS_FLOW_DISC) return -EINVAL; efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0, (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ? 0xfff : ntuple->fs.action); /* Check for unsupported extensions */ if ((rule->flow_type & FLOW_EXT) && (rule->m_ext.vlan_etype | rule->m_ext.data[0] | rule->m_ext.data[1])) return -EINVAL; efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, (rule->location == RX_CLS_LOC_FIRST) ? EFX_FILTER_FLAG_RX_OVERRIDE_IP : 0, (rule->ring_cookie == RX_CLS_FLOW_DISC) ? 0xfff : rule->ring_cookie); switch (ntuple->fs.flow_type) { switch (rule->flow_type) { case TCP_V4_FLOW: case UDP_V4_FLOW: { u8 proto = (ntuple->fs.flow_type == TCP_V4_FLOW ? u8 proto = (rule->flow_type == TCP_V4_FLOW ? IPPROTO_TCP : IPPROTO_UDP); /* Must match all of destination, */ if (ip_mask->ip4dst | ip_mask->pdst) if ((__force u32)~ip_mask->ip4dst | (__force u16)~ip_mask->pdst) return -EINVAL; /* all or none of source, */ if ((ip_mask->ip4src | ip_mask->psrc) && Loading @@ -905,17 +992,17 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, (__force u16)~ip_mask->psrc)) return -EINVAL; /* and nothing else */ if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask) if (ip_mask->tos | rule->m_ext.vlan_tci) return -EINVAL; if (!ip_mask->ip4src) rc = efx_filter_set_ipv4_full(&filter, proto, if (ip_mask->ip4src) rc = efx_filter_set_ipv4_full(&spec, proto, ip_entry->ip4dst, ip_entry->pdst, ip_entry->ip4src, ip_entry->psrc); else rc = efx_filter_set_ipv4_local(&filter, proto, rc = efx_filter_set_ipv4_local(&spec, proto, ip_entry->ip4dst, ip_entry->pdst); if (rc) Loading @@ -923,23 +1010,24 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, break; } case ETHER_FLOW: /* Must match all of destination, */ if (!is_zero_ether_addr(mac_mask->h_dest)) case ETHER_FLOW | FLOW_EXT: /* Must match all or none of VID */ if (rule->m_ext.vlan_tci != htons(0xfff) && rule->m_ext.vlan_tci != 0) return -EINVAL; /* all or none of VID, */ if (ntuple->fs.vlan_tag_mask != 0xf000 && ntuple->fs.vlan_tag_mask != 0xffff) case ETHER_FLOW: /* Must match all of destination */ if (!is_broadcast_ether_addr(mac_mask->h_dest)) return -EINVAL; /* and nothing else */ if (!is_broadcast_ether_addr(mac_mask->h_source) || mac_mask->h_proto != htons(0xffff)) if (!is_zero_ether_addr(mac_mask->h_source) || mac_mask->h_proto) return -EINVAL; rc = efx_filter_set_eth_local( &filter, (ntuple->fs.vlan_tag_mask == 0xf000) ? ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC, &spec, (rule->flow_type & FLOW_EXT && rule->m_ext.vlan_tci) ? ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC, mac_entry->h_dest); if (rc) return rc; Loading @@ -949,11 +1037,33 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, return -EINVAL; } if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR) return efx_filter_remove_filter(efx, &filter); rc = efx_filter_insert_filter(efx, &spec, true); if (rc < 0) return rc; rule->location = rc; return 0; } static int efx_ethtool_set_rxnfc(struct net_device *net_dev, struct ethtool_rxnfc *info) { struct efx_nic *efx = netdev_priv(net_dev); if (efx_filter_get_rx_id_limit(efx) == 0) return -EOPNOTSUPP; switch (info->cmd) { case ETHTOOL_SRXCLSRLINS: return efx_ethtool_set_class_rule(efx, &info->fs); case ETHTOOL_SRXCLSRLDEL: return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL, info->fs.location); rc = efx_filter_insert_filter(efx, &filter, true); return rc < 0 ? rc : 0; default: return -EOPNOTSUPP; } } static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) Loading Loading @@ -1007,7 +1117,7 @@ const struct ethtool_ops efx_ethtool_ops = { .set_wol = efx_ethtool_set_wol, .reset = efx_ethtool_reset, .get_rxnfc = efx_ethtool_get_rxnfc, .set_rx_ntuple = efx_ethtool_set_rx_ntuple, .set_rxnfc = efx_ethtool_set_rxnfc, .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, .get_rxfh_indir = efx_ethtool_get_rxfh_indir, .set_rxfh_indir = efx_ethtool_set_rxfh_indir, Loading Loading
drivers/net/ethernet/sfc/ethtool.c +149 −39 Original line number Diff line number Diff line Loading @@ -818,9 +818,58 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) return efx_reset(efx, rc); } static int efx_ethtool_get_class_rule(struct efx_nic *efx, struct ethtool_rx_flow_spec *rule) { struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; struct efx_filter_spec spec; u16 vid; u8 proto; int rc; rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL, rule->location, &spec); if (rc) return rc; if (spec.dmaq_id == 0xfff) rule->ring_cookie = RX_CLS_FLOW_DISC; else rule->ring_cookie = spec.dmaq_id; rc = efx_filter_get_eth_local(&spec, &vid, rule->h_u.ether_spec.h_dest); if (rc == 0) { rule->flow_type = ETHER_FLOW; memset(rule->m_u.ether_spec.h_dest, ~0, ETH_ALEN); if (vid != EFX_FILTER_VID_UNSPEC) { rule->flow_type |= FLOW_EXT; rule->h_ext.vlan_tci = htons(vid); rule->m_ext.vlan_tci = htons(0xfff); } return 0; } rc = efx_filter_get_ipv4_local(&spec, &proto, &ip_entry->ip4dst, &ip_entry->pdst); if (rc != 0) { rc = efx_filter_get_ipv4_full( &spec, &proto, &ip_entry->ip4src, &ip_entry->psrc, &ip_entry->ip4dst, &ip_entry->pdst); EFX_WARN_ON_PARANOID(rc); ip_mask->ip4src = ~0; ip_mask->psrc = ~0; } rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW; ip_mask->ip4dst = ~0; ip_mask->pdst = ~0; return rc; } static int efx_ethtool_get_rxnfc(struct net_device *net_dev, struct ethtool_rxnfc *info, u32 *rules __always_unused) struct ethtool_rxnfc *info, u32 *rule_locs) { struct efx_nic *efx = netdev_priv(net_dev); Loading Loading @@ -862,42 +911,80 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, return 0; } case ETHTOOL_GRXCLSRLCNT: info->data = efx_filter_get_rx_id_limit(efx); if (info->data == 0) return -EOPNOTSUPP; info->data |= RX_CLS_LOC_SPECIAL; info->rule_cnt = efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL); return 0; case ETHTOOL_GRXCLSRULE: if (efx_filter_get_rx_id_limit(efx) == 0) return -EOPNOTSUPP; return efx_ethtool_get_class_rule(efx, &info->fs); case ETHTOOL_GRXCLSRLALL: { s32 rc; info->data = efx_filter_get_rx_id_limit(efx); if (info->data == 0) return -EOPNOTSUPP; rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL, rule_locs, info->rule_cnt); if (rc < 0) return rc; info->rule_cnt = rc; return 0; } default: return -EOPNOTSUPP; } } static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, struct ethtool_rx_ntuple *ntuple) static int efx_ethtool_set_class_rule(struct efx_nic *efx, struct ethtool_rx_flow_spec *rule) { struct efx_nic *efx = netdev_priv(net_dev); struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec; struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec; struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec; struct efx_filter_spec filter; struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; struct ethhdr *mac_entry = &rule->h_u.ether_spec; struct ethhdr *mac_mask = &rule->m_u.ether_spec; struct efx_filter_spec spec; int rc; /* Range-check action */ if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR || ntuple->fs.action >= (s32)efx->n_rx_channels) /* Check that user wants us to choose the location */ if (rule->location != RX_CLS_LOC_ANY && rule->location != RX_CLS_LOC_FIRST && rule->location != RX_CLS_LOC_LAST) return -EINVAL; if (~ntuple->fs.data_mask) /* Range-check ring_cookie */ if (rule->ring_cookie >= efx->n_rx_channels && rule->ring_cookie != RX_CLS_FLOW_DISC) return -EINVAL; efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0, (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ? 0xfff : ntuple->fs.action); /* Check for unsupported extensions */ if ((rule->flow_type & FLOW_EXT) && (rule->m_ext.vlan_etype | rule->m_ext.data[0] | rule->m_ext.data[1])) return -EINVAL; efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, (rule->location == RX_CLS_LOC_FIRST) ? EFX_FILTER_FLAG_RX_OVERRIDE_IP : 0, (rule->ring_cookie == RX_CLS_FLOW_DISC) ? 0xfff : rule->ring_cookie); switch (ntuple->fs.flow_type) { switch (rule->flow_type) { case TCP_V4_FLOW: case UDP_V4_FLOW: { u8 proto = (ntuple->fs.flow_type == TCP_V4_FLOW ? u8 proto = (rule->flow_type == TCP_V4_FLOW ? IPPROTO_TCP : IPPROTO_UDP); /* Must match all of destination, */ if (ip_mask->ip4dst | ip_mask->pdst) if ((__force u32)~ip_mask->ip4dst | (__force u16)~ip_mask->pdst) return -EINVAL; /* all or none of source, */ if ((ip_mask->ip4src | ip_mask->psrc) && Loading @@ -905,17 +992,17 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, (__force u16)~ip_mask->psrc)) return -EINVAL; /* and nothing else */ if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask) if (ip_mask->tos | rule->m_ext.vlan_tci) return -EINVAL; if (!ip_mask->ip4src) rc = efx_filter_set_ipv4_full(&filter, proto, if (ip_mask->ip4src) rc = efx_filter_set_ipv4_full(&spec, proto, ip_entry->ip4dst, ip_entry->pdst, ip_entry->ip4src, ip_entry->psrc); else rc = efx_filter_set_ipv4_local(&filter, proto, rc = efx_filter_set_ipv4_local(&spec, proto, ip_entry->ip4dst, ip_entry->pdst); if (rc) Loading @@ -923,23 +1010,24 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, break; } case ETHER_FLOW: /* Must match all of destination, */ if (!is_zero_ether_addr(mac_mask->h_dest)) case ETHER_FLOW | FLOW_EXT: /* Must match all or none of VID */ if (rule->m_ext.vlan_tci != htons(0xfff) && rule->m_ext.vlan_tci != 0) return -EINVAL; /* all or none of VID, */ if (ntuple->fs.vlan_tag_mask != 0xf000 && ntuple->fs.vlan_tag_mask != 0xffff) case ETHER_FLOW: /* Must match all of destination */ if (!is_broadcast_ether_addr(mac_mask->h_dest)) return -EINVAL; /* and nothing else */ if (!is_broadcast_ether_addr(mac_mask->h_source) || mac_mask->h_proto != htons(0xffff)) if (!is_zero_ether_addr(mac_mask->h_source) || mac_mask->h_proto) return -EINVAL; rc = efx_filter_set_eth_local( &filter, (ntuple->fs.vlan_tag_mask == 0xf000) ? ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC, &spec, (rule->flow_type & FLOW_EXT && rule->m_ext.vlan_tci) ? ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC, mac_entry->h_dest); if (rc) return rc; Loading @@ -949,11 +1037,33 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, return -EINVAL; } if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR) return efx_filter_remove_filter(efx, &filter); rc = efx_filter_insert_filter(efx, &spec, true); if (rc < 0) return rc; rule->location = rc; return 0; } static int efx_ethtool_set_rxnfc(struct net_device *net_dev, struct ethtool_rxnfc *info) { struct efx_nic *efx = netdev_priv(net_dev); if (efx_filter_get_rx_id_limit(efx) == 0) return -EOPNOTSUPP; switch (info->cmd) { case ETHTOOL_SRXCLSRLINS: return efx_ethtool_set_class_rule(efx, &info->fs); case ETHTOOL_SRXCLSRLDEL: return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL, info->fs.location); rc = efx_filter_insert_filter(efx, &filter, true); return rc < 0 ? rc : 0; default: return -EOPNOTSUPP; } } static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) Loading Loading @@ -1007,7 +1117,7 @@ const struct ethtool_ops efx_ethtool_ops = { .set_wol = efx_ethtool_set_wol, .reset = efx_ethtool_reset, .get_rxnfc = efx_ethtool_get_rxnfc, .set_rx_ntuple = efx_ethtool_set_rx_ntuple, .set_rxnfc = efx_ethtool_set_rxnfc, .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, .get_rxfh_indir = efx_ethtool_get_rxfh_indir, .set_rxfh_indir = efx_ethtool_set_rxfh_indir, Loading