Skip to content

Commit a6ed6f2

Browse files
logostdavem330
authored andcommitted
net: aquantia: add support of L3/L4 ntuple filters
Add support of L3/L4 5-tuple {protocol, src-ip, dst-ip, src-port, dst-port} filters. Mask is not supported. Src-port and dst-port are only compared for TCP/UDP/SCTP packets. Both IPv4 and IPv6 are supported. The supported actions are the drop and the queue assignment. Due to fixed order of the rules in the NIC, the location 32-39 are reserved for L3/L4 5-tuple filters. The locations 32 and 36 are reserved for IPv6 filters. Examples: sudo ethtool -N eth0 flow-type ip6 src-ip 2001:db8:0:f101::2 \ dst-ip 2001:db8:0:f101::5 action -1 loc 36 sudo ethtool -N eth0 flow-type udp4 src-ip 10.0.0.4 \ dst-ip 10.0.0.7 src-port 2000 dst-port 2001 action 2 loc 32 Signed-off-by: Dmitry Bogdanov <[email protected]> Signed-off-by: Igor Russkikh <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8d0bcb0 commit a6ed6f2

File tree

5 files changed

+284
-3
lines changed

5 files changed

+284
-3
lines changed

drivers/net/ethernet/aquantia/atlantic/aq_filters.c

Lines changed: 165 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,46 @@ aq_rule_already_exists(struct aq_nic_s *aq_nic,
8585
return false;
8686
}
8787

88+
static int aq_check_approve_fl3l4(struct aq_nic_s *aq_nic,
89+
struct aq_hw_rx_fltrs_s *rx_fltrs,
90+
struct ethtool_rx_flow_spec *fsp)
91+
{
92+
if (fsp->location < AQ_RX_FIRST_LOC_FL3L4 ||
93+
fsp->location > AQ_RX_LAST_LOC_FL3L4) {
94+
netdev_err(aq_nic->ndev,
95+
"ethtool: location must be in range [%d, %d]",
96+
AQ_RX_FIRST_LOC_FL3L4,
97+
AQ_RX_LAST_LOC_FL3L4);
98+
return -EINVAL;
99+
}
100+
if (rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv4) {
101+
rx_fltrs->fl3l4.is_ipv6 = false;
102+
netdev_err(aq_nic->ndev,
103+
"ethtool: mixing ipv4 and ipv6 is not allowed");
104+
return -EINVAL;
105+
} else if (!rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv6) {
106+
rx_fltrs->fl3l4.is_ipv6 = true;
107+
netdev_err(aq_nic->ndev,
108+
"ethtool: mixing ipv4 and ipv6 is not allowed");
109+
return -EINVAL;
110+
} else if (rx_fltrs->fl3l4.is_ipv6 &&
111+
fsp->location != AQ_RX_FIRST_LOC_FL3L4 + 4 &&
112+
fsp->location != AQ_RX_FIRST_LOC_FL3L4) {
113+
netdev_err(aq_nic->ndev,
114+
"ethtool: The specified location for ipv6 must be %d or %d",
115+
AQ_RX_FIRST_LOC_FL3L4, AQ_RX_FIRST_LOC_FL3L4 + 4);
116+
return -EINVAL;
117+
}
118+
119+
return 0;
120+
}
121+
88122
static int __must_check
89123
aq_check_filter(struct aq_nic_s *aq_nic,
90124
struct ethtool_rx_flow_spec *fsp)
91125
{
92126
int err = 0;
127+
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
93128

94129
if (fsp->flow_type & FLOW_EXT) {
95130
err = -EOPNOTSUPP;
@@ -103,14 +138,16 @@ aq_check_filter(struct aq_nic_s *aq_nic,
103138
case SCTP_V4_FLOW:
104139
case IPV4_FLOW:
105140
case IP_USER_FLOW:
106-
err = -EOPNOTSUPP;
141+
rx_fltrs->fl3l4.is_ipv6 = false;
142+
err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp);
107143
break;
108144
case TCP_V6_FLOW:
109145
case UDP_V6_FLOW:
110146
case SCTP_V6_FLOW:
111147
case IPV6_FLOW:
112148
case IPV6_USER_FLOW:
113-
err = -EOPNOTSUPP;
149+
rx_fltrs->fl3l4.is_ipv6 = true;
150+
err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp);
114151
break;
115152
default:
116153
netdev_err(aq_nic->ndev,
@@ -156,6 +193,11 @@ aq_rule_is_not_correct(struct aq_nic_s *aq_nic,
156193

157194
if (!aq_nic) {
158195
rule_is_not_correct = true;
196+
} else if (fsp->location > AQ_RX_MAX_RXNFC_LOC) {
197+
netdev_err(aq_nic->ndev,
198+
"ethtool: The specified number %u rule is invalid\n",
199+
fsp->location);
200+
rule_is_not_correct = true;
159201
} else if (aq_check_filter(aq_nic, fsp)) {
160202
rule_is_not_correct = true;
161203
} else if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
@@ -187,6 +229,125 @@ aq_check_rule(struct aq_nic_s *aq_nic,
187229
return err;
188230
}
189231

232+
static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic,
233+
struct aq_rx_filter *aq_rx_fltr,
234+
struct aq_rx_filter_l3l4 *data, bool add)
235+
{
236+
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
237+
const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
238+
239+
memset(data, 0, sizeof(*data));
240+
241+
data->is_ipv6 = rx_fltrs->fl3l4.is_ipv6;
242+
data->location = HW_ATL_GET_REG_LOCATION_FL3L4(fsp->location);
243+
244+
if (!add) {
245+
if (!data->is_ipv6)
246+
rx_fltrs->fl3l4.active_ipv4 &= ~BIT(data->location);
247+
else
248+
rx_fltrs->fl3l4.active_ipv6 &=
249+
~BIT((data->location) / 4);
250+
251+
return 0;
252+
}
253+
254+
data->cmd |= HW_ATL_RX_ENABLE_FLTR_L3L4;
255+
256+
switch (fsp->flow_type) {
257+
case TCP_V4_FLOW:
258+
case TCP_V6_FLOW:
259+
data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
260+
break;
261+
case UDP_V4_FLOW:
262+
case UDP_V6_FLOW:
263+
data->cmd |= HW_ATL_RX_UDP;
264+
data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
265+
break;
266+
case SCTP_V4_FLOW:
267+
case SCTP_V6_FLOW:
268+
data->cmd |= HW_ATL_RX_SCTP;
269+
data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
270+
break;
271+
default:
272+
break;
273+
}
274+
275+
if (!data->is_ipv6) {
276+
data->ip_src[0] =
277+
ntohl(fsp->h_u.tcp_ip4_spec.ip4src);
278+
data->ip_dst[0] =
279+
ntohl(fsp->h_u.tcp_ip4_spec.ip4dst);
280+
rx_fltrs->fl3l4.active_ipv4 |= BIT(data->location);
281+
} else {
282+
int i;
283+
284+
rx_fltrs->fl3l4.active_ipv6 |= BIT((data->location) / 4);
285+
for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) {
286+
data->ip_dst[i] =
287+
ntohl(fsp->h_u.tcp_ip6_spec.ip6dst[i]);
288+
data->ip_src[i] =
289+
ntohl(fsp->h_u.tcp_ip6_spec.ip6src[i]);
290+
}
291+
data->cmd |= HW_ATL_RX_ENABLE_L3_IPV6;
292+
}
293+
if (fsp->flow_type != IP_USER_FLOW &&
294+
fsp->flow_type != IPV6_USER_FLOW) {
295+
if (!data->is_ipv6) {
296+
data->p_dst =
297+
ntohs(fsp->h_u.tcp_ip4_spec.pdst);
298+
data->p_src =
299+
ntohs(fsp->h_u.tcp_ip4_spec.psrc);
300+
} else {
301+
data->p_dst =
302+
ntohs(fsp->h_u.tcp_ip6_spec.pdst);
303+
data->p_src =
304+
ntohs(fsp->h_u.tcp_ip6_spec.psrc);
305+
}
306+
}
307+
if (data->ip_src[0] && !data->is_ipv6)
308+
data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3;
309+
if (data->ip_dst[0] && !data->is_ipv6)
310+
data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3;
311+
if (data->p_dst)
312+
data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4;
313+
if (data->p_src)
314+
data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4;
315+
if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
316+
data->cmd |= HW_ATL_RX_HOST << HW_ATL_RX_ACTION_FL3F4_SHIFT;
317+
data->cmd |= fsp->ring_cookie << HW_ATL_RX_QUEUE_FL3L4_SHIFT;
318+
data->cmd |= HW_ATL_RX_ENABLE_QUEUE_L3L4;
319+
} else {
320+
data->cmd |= HW_ATL_RX_DISCARD << HW_ATL_RX_ACTION_FL3F4_SHIFT;
321+
}
322+
323+
return 0;
324+
}
325+
326+
static int aq_set_fl3l4(struct aq_hw_s *aq_hw,
327+
const struct aq_hw_ops *aq_hw_ops,
328+
struct aq_rx_filter_l3l4 *data)
329+
{
330+
if (unlikely(!aq_hw_ops->hw_filter_l3l4_set))
331+
return -EOPNOTSUPP;
332+
333+
return aq_hw_ops->hw_filter_l3l4_set(aq_hw, data);
334+
}
335+
336+
static int aq_add_del_fl3l4(struct aq_nic_s *aq_nic,
337+
struct aq_rx_filter *aq_rx_fltr, bool add)
338+
{
339+
const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
340+
struct aq_hw_s *aq_hw = aq_nic->aq_hw;
341+
struct aq_rx_filter_l3l4 data;
342+
343+
if (unlikely(aq_rx_fltr->aq_fsp.location < AQ_RX_FIRST_LOC_FL3L4 ||
344+
aq_rx_fltr->aq_fsp.location > AQ_RX_LAST_LOC_FL3L4 ||
345+
aq_set_data_fl3l4(aq_nic, aq_rx_fltr, &data, add)))
346+
return -EINVAL;
347+
348+
return aq_set_fl3l4(aq_hw, aq_hw_ops, &data);
349+
}
350+
190351
static int aq_add_del_rule(struct aq_nic_s *aq_nic,
191352
struct aq_rx_filter *aq_rx_fltr, bool add)
192353
{
@@ -207,7 +368,8 @@ static int aq_add_del_rule(struct aq_nic_s *aq_nic,
207368
case UDP_V6_FLOW:
208369
case SCTP_V6_FLOW:
209370
case IPV6_USER_FLOW:
210-
err = -EOPNOTSUPP;
371+
aq_rx_fltr->type = aq_rx_filter_l3l4;
372+
err = aq_add_del_fl3l4(aq_nic, aq_rx_fltr, add);
211373
break;
212374
default:
213375
err = -EINVAL;

drivers/net/ethernet/aquantia/atlantic/aq_hw.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#include "aq_rss.h"
1919
#include "hw_atl/hw_atl_utils.h"
2020

21+
#define AQ_RX_FIRST_LOC_FL3L4 32U
22+
#define AQ_RX_LAST_LOC_FL3L4 39U
23+
#define AQ_RX_MAX_RXNFC_LOC AQ_RX_LAST_LOC_FL3L4
24+
2125
/* NIC H/W capabilities */
2226
struct aq_hw_caps_s {
2327
u64 hw_features;
@@ -130,6 +134,7 @@ struct aq_hw_s {
130134
struct aq_ring_s;
131135
struct aq_ring_param_s;
132136
struct sk_buff;
137+
struct aq_rx_filter_l3l4;
133138

134139
struct aq_hw_ops {
135140

@@ -183,6 +188,12 @@ struct aq_hw_ops {
183188
int (*hw_packet_filter_set)(struct aq_hw_s *self,
184189
unsigned int packet_filter);
185190

191+
int (*hw_filter_l3l4_set)(struct aq_hw_s *self,
192+
struct aq_rx_filter_l3l4 *data);
193+
194+
int (*hw_filter_l3l4_clear)(struct aq_hw_s *self,
195+
struct aq_rx_filter_l3l4 *data);
196+
186197
int (*hw_multicast_list_set)(struct aq_hw_s *self,
187198
u8 ar_mac[AQ_HW_MULTICAST_ADDRESS_MAX]
188199
[ETH_ALEN],

drivers/net/ethernet/aquantia/atlantic/aq_nic.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,16 @@ struct aq_nic_cfg_s {
6161
#define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \
6262
((_TC_) * AQ_CFG_TCS_MAX + (_VEC_))
6363

64+
struct aq_hw_rx_fl3l4 {
65+
u8 active_ipv4;
66+
u8 active_ipv6:2;
67+
u8 is_ipv6;
68+
};
69+
6470
struct aq_hw_rx_fltrs_s {
6571
struct hlist_head filter_list;
6672
u16 active_filters;
73+
struct aq_hw_rx_fl3l4 fl3l4;
6774
};
6875

6976
struct aq_nic_s {

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,63 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
946946
return aq_hw_err_from_flags(self);
947947
}
948948

949+
static int hw_atl_b0_hw_fl3l4_clear(struct aq_hw_s *self,
950+
struct aq_rx_filter_l3l4 *data)
951+
{
952+
u8 location = data->location;
953+
954+
if (!data->is_ipv6) {
955+
hw_atl_rpfl3l4_cmd_clear(self, location);
956+
hw_atl_rpf_l4_spd_set(self, 0U, location);
957+
hw_atl_rpf_l4_dpd_set(self, 0U, location);
958+
hw_atl_rpfl3l4_ipv4_src_addr_clear(self, location);
959+
hw_atl_rpfl3l4_ipv4_dest_addr_clear(self, location);
960+
} else {
961+
int i;
962+
963+
for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) {
964+
hw_atl_rpfl3l4_cmd_clear(self, location + i);
965+
hw_atl_rpf_l4_spd_set(self, 0U, location + i);
966+
hw_atl_rpf_l4_dpd_set(self, 0U, location + i);
967+
}
968+
hw_atl_rpfl3l4_ipv6_src_addr_clear(self, location);
969+
hw_atl_rpfl3l4_ipv6_dest_addr_clear(self, location);
970+
}
971+
972+
return aq_hw_err_from_flags(self);
973+
}
974+
975+
static int hw_atl_b0_hw_fl3l4_set(struct aq_hw_s *self,
976+
struct aq_rx_filter_l3l4 *data)
977+
{
978+
u8 location = data->location;
979+
980+
hw_atl_b0_hw_fl3l4_clear(self, data);
981+
982+
if (data->cmd) {
983+
if (!data->is_ipv6) {
984+
hw_atl_rpfl3l4_ipv4_dest_addr_set(self,
985+
location,
986+
data->ip_dst[0]);
987+
hw_atl_rpfl3l4_ipv4_src_addr_set(self,
988+
location,
989+
data->ip_src[0]);
990+
} else {
991+
hw_atl_rpfl3l4_ipv6_dest_addr_set(self,
992+
location,
993+
data->ip_dst);
994+
hw_atl_rpfl3l4_ipv6_src_addr_set(self,
995+
location,
996+
data->ip_src);
997+
}
998+
}
999+
hw_atl_rpf_l4_dpd_set(self, data->p_dst, location);
1000+
hw_atl_rpf_l4_spd_set(self, data->p_src, location);
1001+
hw_atl_rpfl3l4_cmd_set(self, location, data->cmd);
1002+
1003+
return aq_hw_err_from_flags(self);
1004+
}
1005+
9491006
const struct aq_hw_ops hw_atl_ops_b0 = {
9501007
.hw_set_mac_address = hw_atl_b0_hw_mac_addr_set,
9511008
.hw_init = hw_atl_b0_hw_init,
@@ -970,6 +1027,7 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
9701027
.hw_ring_rx_init = hw_atl_b0_hw_ring_rx_init,
9711028
.hw_ring_tx_init = hw_atl_b0_hw_ring_tx_init,
9721029
.hw_packet_filter_set = hw_atl_b0_hw_packet_filter_set,
1030+
.hw_filter_l3l4_set = hw_atl_b0_hw_fl3l4_set,
9731031
.hw_multicast_list_set = hw_atl_b0_hw_multicast_list_set,
9741032
.hw_interrupt_moderation_set = hw_atl_b0_hw_interrupt_moderation_set,
9751033
.hw_rss_set = hw_atl_b0_hw_rss_set,

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,49 @@ struct __packed offload_info {
240240
u8 buf[0];
241241
};
242242

243+
enum hw_atl_rx_action_with_traffic {
244+
HW_ATL_RX_DISCARD,
245+
HW_ATL_RX_HOST,
246+
};
247+
248+
struct aq_rx_filter_l3l4 {
249+
u32 cmd;
250+
u8 location;
251+
u32 ip_dst[4];
252+
u32 ip_src[4];
253+
u16 p_dst;
254+
u16 p_src;
255+
u8 is_ipv6;
256+
};
257+
258+
enum hw_atl_rx_protocol_value_l3l4 {
259+
HW_ATL_RX_TCP,
260+
HW_ATL_RX_UDP,
261+
HW_ATL_RX_SCTP,
262+
HW_ATL_RX_ICMP
263+
};
264+
265+
enum hw_atl_rx_ctrl_registers_l3l4 {
266+
HW_ATL_RX_ENABLE_MNGMNT_QUEUE_L3L4 = BIT(22),
267+
HW_ATL_RX_ENABLE_QUEUE_L3L4 = BIT(23),
268+
HW_ATL_RX_ENABLE_ARP_FLTR_L3 = BIT(24),
269+
HW_ATL_RX_ENABLE_CMP_PROT_L4 = BIT(25),
270+
HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4 = BIT(26),
271+
HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4 = BIT(27),
272+
HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3 = BIT(28),
273+
HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3 = BIT(29),
274+
HW_ATL_RX_ENABLE_L3_IPV6 = BIT(30),
275+
HW_ATL_RX_ENABLE_FLTR_L3L4 = BIT(31)
276+
};
277+
278+
#define HW_ATL_RX_QUEUE_FL3L4_SHIFT 8U
279+
#define HW_ATL_RX_ACTION_FL3F4_SHIFT 16U
280+
281+
#define HW_ATL_RX_CNT_REG_ADDR_IPV6 4U
282+
283+
#define HW_ATL_GET_REG_LOCATION_FL3L4(location) \
284+
((location) - AQ_RX_FIRST_LOC_FL3L4)
285+
243286
#define HAL_ATLANTIC_UTILS_CHIP_MIPS 0x00000001U
244287
#define HAL_ATLANTIC_UTILS_CHIP_TPO2 0x00000002U
245288
#define HAL_ATLANTIC_UTILS_CHIP_RPF2 0x00000004U

0 commit comments

Comments
 (0)