aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c
blob: 512d431489228eba07e4e333632f28ff2807ba6e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
// Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

#include "act.h"
#include "en/tc_priv.h"
#include "fs_core.h"

static bool police_act_validate_control(enum flow_action_id act_id,
					struct netlink_ext_ack *extack)
{
	if (act_id != FLOW_ACTION_PIPE &&
	    act_id != FLOW_ACTION_ACCEPT &&
	    act_id != FLOW_ACTION_JUMP &&
	    act_id != FLOW_ACTION_DROP) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Offload not supported when conform-exceed action is not pipe, ok, jump or drop");
		return false;
	}

	return true;
}

static int police_act_validate(const struct flow_action_entry *act,
			       struct netlink_ext_ack *extack)
{
	if (!police_act_validate_control(act->police.exceed.act_id, extack) ||
	    !police_act_validate_control(act->police.notexceed.act_id, extack))
		return -EOPNOTSUPP;

	if (act->police.peakrate_bytes_ps ||
	    act->police.avrate || act->police.overhead) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Offload not supported when peakrate/avrate/overhead is configured");
		return -EOPNOTSUPP;
	}

	if (act->police.rate_pkt_ps) {
		NL_SET_ERR_MSG_MOD(extack,
				   "QoS offload not support packets per second");
		return -EOPNOTSUPP;
	}

	return 0;
}

static bool
tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state,
			  const struct flow_action_entry *act,
			  int act_index,
			  struct mlx5_flow_attr *attr)
{
	int err;

	err = police_act_validate(act, parse_state->extack);
	if (err)
		return false;

	return !!mlx5e_get_flow_meters(parse_state->flow->priv->mdev);
}

static int
fill_meter_params_from_act(const struct flow_action_entry *act,
			   struct mlx5e_flow_meter_params *params)
{
	params->index = act->hw_index;
	if (act->police.rate_bytes_ps) {
		params->mode = MLX5_RATE_LIMIT_BPS;
		/* change rate to bits per second */
		params->rate = act->police.rate_bytes_ps << 3;
		params->burst = act->police.burst;
	} else if (act->police.rate_pkt_ps) {
		params->mode = MLX5_RATE_LIMIT_PPS;
		params->rate = act->police.rate_pkt_ps;
		params->burst = act->police.burst_pkt;
	} else if (act->police.mtu) {
		params->mtu = act->police.mtu;
	} else {
		return -EOPNOTSUPP;
	}

	return 0;
}

static int
tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state,
		    const struct flow_action_entry *act,
		    struct mlx5e_priv *priv,
		    struct mlx5_flow_attr *attr)
{
	enum mlx5_flow_namespace_type ns =  mlx5e_get_flow_namespace(parse_state->flow);
	struct mlx5e_flow_meter_params *params = &attr->meter_attr.params;
	int err;

	err = fill_meter_params_from_act(act, params);
	if (err)
		return err;

	if (params->mtu) {
		if (!(mlx5_fs_get_capabilities(priv->mdev, ns) &
		      MLX5_FLOW_STEERING_CAP_MATCH_RANGES))
			return -EOPNOTSUPP;

		attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
		attr->flags |= MLX5_ATTR_FLAG_MTU;
	} else {
		attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO;
		attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER;
	}

	return 0;
}

static bool
tc_act_is_multi_table_act_police(struct mlx5e_priv *priv,
				 const struct flow_action_entry *act,
				 struct mlx5_flow_attr *attr)
{
	return true;
}

static int
tc_act_police_offload(struct mlx5e_priv *priv,
		      struct flow_offload_action *fl_act,
		      struct flow_action_entry *act)
{
	struct mlx5e_flow_meter_params params = {};
	struct mlx5e_flow_meter_handle *meter;
	int err = 0;

	err = police_act_validate(act, fl_act->extack);
	if (err)
		return err;

	err = fill_meter_params_from_act(act, &params);
	if (err)
		return err;

	meter = mlx5e_tc_meter_get(priv->mdev, &params);
	if (IS_ERR(meter) && PTR_ERR(meter) == -ENOENT) {
		meter = mlx5e_tc_meter_replace(priv->mdev, &params);
	} else if (!IS_ERR(meter)) {
		err = mlx5e_tc_meter_update(meter, &params);
		mlx5e_tc_meter_put(meter);
	}

	if (IS_ERR(meter)) {
		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
		err = PTR_ERR(meter);
	}

	return err;
}

static int
tc_act_police_destroy(struct mlx5e_priv *priv,
		      struct flow_offload_action *fl_act)
{
	struct mlx5e_flow_meter_params params = {};
	struct mlx5e_flow_meter_handle *meter;

	params.index = fl_act->index;
	meter = mlx5e_tc_meter_get(priv->mdev, &params);
	if (IS_ERR(meter)) {
		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
		return PTR_ERR(meter);
	}
	/* first put for the get and second for cleanup */
	mlx5e_tc_meter_put(meter);
	mlx5e_tc_meter_put(meter);
	return 0;
}

static int
tc_act_police_stats(struct mlx5e_priv *priv,
		    struct flow_offload_action *fl_act)
{
	struct mlx5e_flow_meter_params params = {};
	struct mlx5e_flow_meter_handle *meter;
	u64 bytes, packets, drops, lastuse;

	params.index = fl_act->index;
	meter = mlx5e_tc_meter_get(priv->mdev, &params);
	if (IS_ERR(meter)) {
		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
		return PTR_ERR(meter);
	}

	mlx5e_tc_meter_get_stats(meter, &bytes, &packets, &drops, &lastuse);
	flow_stats_update(&fl_act->stats, bytes, packets, drops, lastuse,
			  FLOW_ACTION_HW_STATS_DELAYED);
	mlx5e_tc_meter_put(meter);
	return 0;
}

static bool
tc_act_police_get_branch_ctrl(const struct flow_action_entry *act,
			      struct mlx5e_tc_act_branch_ctrl *cond_true,
			      struct mlx5e_tc_act_branch_ctrl *cond_false)
{
	cond_true->act_id = act->police.notexceed.act_id;
	cond_true->extval = act->police.notexceed.extval;

	cond_false->act_id = act->police.exceed.act_id;
	cond_false->extval = act->police.exceed.extval;
	return true;
}

struct mlx5e_tc_act mlx5e_tc_act_police = {
	.can_offload = tc_act_can_offload_police,
	.parse_action = tc_act_parse_police,
	.is_multi_table_act = tc_act_is_multi_table_act_police,
	.offload_action = tc_act_police_offload,
	.destroy_action = tc_act_police_destroy,
	.stats_action = tc_act_police_stats,
	.get_branch_ctrl = tc_act_police_get_branch_ctrl,
};