xref: /linux/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c (revision 8a5f956a9fb7d74fff681145082acfad5afa6bb8)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
3 
4 #include <linux/ethtool.h>
5 
6 #include "hinic3_cmdq.h"
7 #include "hinic3_hwdev.h"
8 #include "hinic3_hwif.h"
9 #include "hinic3_mbox.h"
10 #include "hinic3_nic_cfg.h"
11 #include "hinic3_nic_dev.h"
12 #include "hinic3_rss.h"
13 
14 static void hinic3_fillout_indir_tbl(struct net_device *netdev, u16 *indir)
15 {
16 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
17 	u16 i, num_qps;
18 
19 	num_qps = nic_dev->q_params.num_qps;
20 	for (i = 0; i < L2NIC_RSS_INDIR_SIZE; i++)
21 		indir[i] = ethtool_rxfh_indir_default(i, num_qps);
22 }
23 
24 static int hinic3_rss_cfg(struct hinic3_hwdev *hwdev, u8 rss_en, u16 num_qps)
25 {
26 	struct mgmt_msg_params msg_params = {};
27 	struct l2nic_cmd_cfg_rss rss_cfg = {};
28 	int err;
29 
30 	rss_cfg.func_id = hinic3_global_func_id(hwdev);
31 	rss_cfg.rss_en = rss_en;
32 	rss_cfg.rq_priority_number = 0;
33 	rss_cfg.num_qps = num_qps;
34 
35 	mgmt_msg_params_init_default(&msg_params, &rss_cfg, sizeof(rss_cfg));
36 
37 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
38 				       L2NIC_CMD_CFG_RSS, &msg_params);
39 	if (err || rss_cfg.msg_head.status) {
40 		dev_err(hwdev->dev, "Failed to set rss cfg, err: %d, status: 0x%x\n",
41 			err, rss_cfg.msg_head.status);
42 		return -EINVAL;
43 	}
44 
45 	return 0;
46 }
47 
48 static void hinic3_init_rss_parameters(struct net_device *netdev)
49 {
50 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
51 
52 	nic_dev->rss_hash_type = HINIC3_RSS_HASH_ENGINE_TYPE_XOR;
53 	nic_dev->rss_type.tcp_ipv6_ext = 1;
54 	nic_dev->rss_type.ipv6_ext = 1;
55 	nic_dev->rss_type.tcp_ipv6 = 1;
56 	nic_dev->rss_type.ipv6 = 1;
57 	nic_dev->rss_type.tcp_ipv4 = 1;
58 	nic_dev->rss_type.ipv4 = 1;
59 	nic_dev->rss_type.udp_ipv6 = 1;
60 	nic_dev->rss_type.udp_ipv4 = 1;
61 }
62 
63 static void decide_num_qps(struct net_device *netdev)
64 {
65 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
66 	unsigned int dev_cpus;
67 
68 	dev_cpus = netif_get_num_default_rss_queues();
69 	nic_dev->q_params.num_qps = min(dev_cpus, nic_dev->max_qps);
70 }
71 
72 static int alloc_rss_resource(struct net_device *netdev)
73 {
74 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
75 
76 	nic_dev->rss_hkey = kmalloc_array(L2NIC_RSS_KEY_SIZE,
77 					  sizeof(nic_dev->rss_hkey[0]),
78 					  GFP_KERNEL);
79 	if (!nic_dev->rss_hkey)
80 		return -ENOMEM;
81 
82 	netdev_rss_key_fill(nic_dev->rss_hkey, L2NIC_RSS_KEY_SIZE);
83 
84 	nic_dev->rss_indir = kcalloc(L2NIC_RSS_INDIR_SIZE, sizeof(u16),
85 				     GFP_KERNEL);
86 	if (!nic_dev->rss_indir) {
87 		kfree(nic_dev->rss_hkey);
88 		nic_dev->rss_hkey = NULL;
89 		return -ENOMEM;
90 	}
91 
92 	return 0;
93 }
94 
95 static int hinic3_rss_set_indir_tbl(struct hinic3_hwdev *hwdev,
96 				    const u16 *indir_table)
97 {
98 	struct l2nic_cmd_rss_set_indir_tbl *indir_tbl;
99 	struct hinic3_cmd_buf *cmd_buf;
100 	__le64 out_param;
101 	int err;
102 	u32 i;
103 
104 	cmd_buf = hinic3_alloc_cmd_buf(hwdev);
105 	if (!cmd_buf) {
106 		dev_err(hwdev->dev, "Failed to allocate cmd buf\n");
107 		return -ENOMEM;
108 	}
109 
110 	cmd_buf->size = cpu_to_le16(sizeof(struct l2nic_cmd_rss_set_indir_tbl));
111 	indir_tbl = cmd_buf->buf;
112 	memset(indir_tbl, 0, sizeof(*indir_tbl));
113 
114 	for (i = 0; i < L2NIC_RSS_INDIR_SIZE; i++)
115 		indir_tbl->entry[i] = cpu_to_le16(indir_table[i]);
116 
117 	hinic3_cmdq_buf_swab32(indir_tbl, sizeof(*indir_tbl));
118 
119 	err = hinic3_cmdq_direct_resp(hwdev, MGMT_MOD_L2NIC,
120 				      L2NIC_UCODE_CMD_SET_RSS_INDIR_TBL,
121 				      cmd_buf, &out_param);
122 	if (err || out_param) {
123 		dev_err(hwdev->dev, "Failed to set rss indir table\n");
124 		err = -EFAULT;
125 	}
126 
127 	hinic3_free_cmd_buf(hwdev, cmd_buf);
128 
129 	return err;
130 }
131 
132 static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,
133 			       struct hinic3_rss_type rss_type)
134 {
135 	struct l2nic_cmd_set_rss_ctx_tbl ctx_tbl = {};
136 	struct mgmt_msg_params msg_params = {};
137 	u32 ctx;
138 	int err;
139 
140 	ctx_tbl.func_id = hinic3_global_func_id(hwdev);
141 	ctx = L2NIC_RSS_TYPE_SET(1, VALID) |
142 	      L2NIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) |
143 	      L2NIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) |
144 	      L2NIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) |
145 	      L2NIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) |
146 	      L2NIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) |
147 	      L2NIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) |
148 	      L2NIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) |
149 	      L2NIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6);
150 	ctx_tbl.context = ctx;
151 
152 	mgmt_msg_params_init_default(&msg_params, &ctx_tbl, sizeof(ctx_tbl));
153 
154 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
155 				       L2NIC_CMD_SET_RSS_CTX_TBL, &msg_params);
156 
157 	if (ctx_tbl.msg_head.status == MGMT_STATUS_CMD_UNSUPPORTED) {
158 		return MGMT_STATUS_CMD_UNSUPPORTED;
159 	} else if (err || ctx_tbl.msg_head.status) {
160 		dev_err(hwdev->dev, "mgmt Failed to set rss context offload, err: %d, status: 0x%x\n",
161 			err, ctx_tbl.msg_head.status);
162 		return -EINVAL;
163 	}
164 
165 	return 0;
166 }
167 
168 static int hinic3_rss_cfg_hash_type(struct hinic3_hwdev *hwdev, u8 opcode,
169 				    enum hinic3_rss_hash_type *type)
170 {
171 	struct l2nic_cmd_cfg_rss_engine hash_type_cmd = {};
172 	struct mgmt_msg_params msg_params = {};
173 	int err;
174 
175 	hash_type_cmd.func_id = hinic3_global_func_id(hwdev);
176 	hash_type_cmd.opcode = opcode;
177 
178 	if (opcode == MGMT_MSG_CMD_OP_SET)
179 		hash_type_cmd.hash_engine = *type;
180 
181 	mgmt_msg_params_init_default(&msg_params, &hash_type_cmd,
182 				     sizeof(hash_type_cmd));
183 
184 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
185 				       L2NIC_CMD_CFG_RSS_HASH_ENGINE,
186 				       &msg_params);
187 	if (err || hash_type_cmd.msg_head.status) {
188 		dev_err(hwdev->dev, "Failed to %s hash engine, err: %d, status: 0x%x\n",
189 			opcode == MGMT_MSG_CMD_OP_SET ? "set" : "get",
190 			err, hash_type_cmd.msg_head.status);
191 		return -EIO;
192 	}
193 
194 	if (opcode == MGMT_MSG_CMD_OP_GET)
195 		*type = hash_type_cmd.hash_engine;
196 
197 	return 0;
198 }
199 
200 static int hinic3_rss_set_hash_type(struct hinic3_hwdev *hwdev,
201 				    enum hinic3_rss_hash_type type)
202 {
203 	return hinic3_rss_cfg_hash_type(hwdev, MGMT_MSG_CMD_OP_SET, &type);
204 }
205 
206 static int hinic3_config_rss_hw_resource(struct net_device *netdev,
207 					 u16 *indir_tbl)
208 {
209 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
210 	int err;
211 
212 	err = hinic3_rss_set_indir_tbl(nic_dev->hwdev, indir_tbl);
213 	if (err)
214 		return err;
215 
216 	err = hinic3_set_rss_type(nic_dev->hwdev, nic_dev->rss_type);
217 	if (err)
218 		return err;
219 
220 	return hinic3_rss_set_hash_type(nic_dev->hwdev, nic_dev->rss_hash_type);
221 }
222 
223 static int hinic3_rss_cfg_hash_key(struct hinic3_hwdev *hwdev, u8 opcode,
224 				   u8 *key)
225 {
226 	struct l2nic_cmd_cfg_rss_hash_key hash_key = {};
227 	struct mgmt_msg_params msg_params = {};
228 	int err;
229 
230 	hash_key.func_id = hinic3_global_func_id(hwdev);
231 	hash_key.opcode = opcode;
232 
233 	if (opcode == MGMT_MSG_CMD_OP_SET)
234 		memcpy(hash_key.key, key, L2NIC_RSS_KEY_SIZE);
235 
236 	mgmt_msg_params_init_default(&msg_params, &hash_key, sizeof(hash_key));
237 
238 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
239 				       L2NIC_CMD_CFG_RSS_HASH_KEY, &msg_params);
240 	if (err || hash_key.msg_head.status) {
241 		dev_err(hwdev->dev, "Failed to %s hash key, err: %d, status: 0x%x\n",
242 			opcode == MGMT_MSG_CMD_OP_SET ? "set" : "get",
243 			err, hash_key.msg_head.status);
244 		return -EINVAL;
245 	}
246 
247 	if (opcode == MGMT_MSG_CMD_OP_GET)
248 		memcpy(key, hash_key.key, L2NIC_RSS_KEY_SIZE);
249 
250 	return 0;
251 }
252 
253 static int hinic3_rss_set_hash_key(struct hinic3_hwdev *hwdev, u8 *key)
254 {
255 	return hinic3_rss_cfg_hash_key(hwdev, MGMT_MSG_CMD_OP_SET, key);
256 }
257 
258 static int hinic3_set_hw_rss_parameters(struct net_device *netdev, u8 rss_en)
259 {
260 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
261 	int err;
262 
263 	err = hinic3_rss_set_hash_key(nic_dev->hwdev, nic_dev->rss_hkey);
264 	if (err)
265 		return err;
266 
267 	hinic3_fillout_indir_tbl(netdev, nic_dev->rss_indir);
268 
269 	err = hinic3_config_rss_hw_resource(netdev, nic_dev->rss_indir);
270 	if (err)
271 		return err;
272 
273 	err = hinic3_rss_cfg(nic_dev->hwdev, rss_en, nic_dev->q_params.num_qps);
274 	if (err)
275 		return err;
276 
277 	return 0;
278 }
279 
280 int hinic3_rss_init(struct net_device *netdev)
281 {
282 	return hinic3_set_hw_rss_parameters(netdev, 1);
283 }
284 
285 void hinic3_rss_uninit(struct net_device *netdev)
286 {
287 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
288 
289 	hinic3_rss_cfg(nic_dev->hwdev, 0, 0);
290 }
291 
292 void hinic3_clear_rss_config(struct net_device *netdev)
293 {
294 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
295 
296 	kfree(nic_dev->rss_hkey);
297 	nic_dev->rss_hkey = NULL;
298 
299 	kfree(nic_dev->rss_indir);
300 	nic_dev->rss_indir = NULL;
301 }
302 
303 void hinic3_try_to_enable_rss(struct net_device *netdev)
304 {
305 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
306 	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
307 	int err;
308 
309 	nic_dev->max_qps = hinic3_func_max_qnum(hwdev);
310 	if (nic_dev->max_qps <= 1 ||
311 	    !hinic3_test_support(nic_dev, HINIC3_NIC_F_RSS))
312 		goto err_reset_q_params;
313 
314 	err = alloc_rss_resource(netdev);
315 	if (err) {
316 		nic_dev->max_qps = 1;
317 		goto err_reset_q_params;
318 	}
319 
320 	set_bit(HINIC3_RSS_ENABLE, &nic_dev->flags);
321 	decide_num_qps(netdev);
322 	hinic3_init_rss_parameters(netdev);
323 	err = hinic3_set_hw_rss_parameters(netdev, 0);
324 	if (err) {
325 		dev_err(hwdev->dev, "Failed to set hardware rss parameters\n");
326 		hinic3_clear_rss_config(netdev);
327 		nic_dev->max_qps = 1;
328 		goto err_reset_q_params;
329 	}
330 
331 	return;
332 
333 err_reset_q_params:
334 	clear_bit(HINIC3_RSS_ENABLE, &nic_dev->flags);
335 	nic_dev->q_params.num_qps = nic_dev->max_qps;
336 }
337