File Index
Files and directories
Sign in
FILE

hinic3_netdev_ops.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c C
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
  3
  4#include <linux/etherdevice.h>
  5#include <linux/netdevice.h>
  6
  7#include "hinic3_hwif.h"
  8#include "hinic3_nic_cfg.h"
  9#include "hinic3_nic_dev.h"
 10#include "hinic3_nic_io.h"
 11#include "hinic3_rss.h"
 12#include "hinic3_rx.h"
 13#include "hinic3_tx.h"
 14
 15/* try to modify the number of irq to the target number,
 16 * and return the actual number of irq.
 17 */
 18static u16 hinic3_qp_irq_change(struct net_device *netdev,
 19				u16 dst_num_qp_irq)
 20{
 21	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
 22	struct msix_entry *qps_msix_entries;
 23	u16 resp_irq_num, irq_num_gap, i;
 24	u16 idx;
 25	int err;
 26
 27	qps_msix_entries = nic_dev->qps_msix_entries;
 28	if (dst_num_qp_irq > nic_dev->num_qp_irq) {
 29		irq_num_gap = dst_num_qp_irq - nic_dev->num_qp_irq;
 30		err = hinic3_alloc_irqs(nic_dev->hwdev, irq_num_gap,
 31					&qps_msix_entries[nic_dev->num_qp_irq],
 32					&resp_irq_num);
 33		if (err) {
 34			netdev_err(netdev, "Failed to alloc irqs\n");
 35			return nic_dev->num_qp_irq;
 36		}
 37
 38		nic_dev->num_qp_irq += resp_irq_num;
 39	} else if (dst_num_qp_irq < nic_dev->num_qp_irq) {
 40		irq_num_gap = nic_dev->num_qp_irq - dst_num_qp_irq;
 41		for (i = 0; i < irq_num_gap; i++) {
 42			idx = (nic_dev->num_qp_irq - i) - 1;
 43			hinic3_free_irq(nic_dev->hwdev,
 44					qps_msix_entries[idx].vector);
 45			qps_msix_entries[idx].vector = 0;
 46			qps_msix_entries[idx].entry = 0;
 47		}
 48		nic_dev->num_qp_irq = dst_num_qp_irq;
 49	}
 50
 51	return nic_dev->num_qp_irq;
 52}
 53
 54static void hinic3_config_num_qps(struct net_device *netdev,
 55				  struct hinic3_dyna_txrxq_params *q_params)
 56{
 57	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
 58	u16 alloc_num_irq, cur_num_irq;
 59	u16 dst_num_irq;
 60
 61	if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags))
 62		q_params->num_qps = 1;
 63
 64	if (nic_dev->num_qp_irq >= q_params->num_qps)
 65		goto out;
 66
 67	cur_num_irq = nic_dev->num_qp_irq;
 68
 69	alloc_num_irq = hinic3_qp_irq_change(netdev, q_params->num_qps);
 70	if (alloc_num_irq < q_params->num_qps) {
 71		q_params->num_qps = alloc_num_irq;
 72		netdev_warn(netdev, "Can not get enough irqs, adjust num_qps to %u\n",
 73			    q_params->num_qps);
 74
 75		/* The current irq may be in use, we must keep it */
 76		dst_num_irq = max_t(u16, cur_num_irq, q_params->num_qps);
 77		hinic3_qp_irq_change(netdev, dst_num_irq);
 78	}
 79
 80out:
 81	netdev_dbg(netdev, "No need to change irqs, num_qps is %u\n",
 82		   q_params->num_qps);
 83}
 84
 85static int hinic3_setup_num_qps(struct net_device *netdev)
 86{
 87	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
 88
 89	nic_dev->num_qp_irq = 0;
 90
 91	nic_dev->qps_msix_entries = kcalloc(nic_dev->max_qps,
 92					    sizeof(struct msix_entry),
 93					    GFP_KERNEL);
 94	if (!nic_dev->qps_msix_entries)
 95		return -ENOMEM;
 96
 97	hinic3_config_num_qps(netdev, &nic_dev->q_params);
 98
 99	return 0;
100}
101
102static void hinic3_destroy_num_qps(struct net_device *netdev)
103{
104	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
105	u16 i;
106
107	for (i = 0; i < nic_dev->num_qp_irq; i++)
108		hinic3_free_irq(nic_dev->hwdev,
109				nic_dev->qps_msix_entries[i].vector);
110
111	kfree(nic_dev->qps_msix_entries);
112}
113
114static int hinic3_alloc_txrxq_resources(struct net_device *netdev,
115					struct hinic3_dyna_txrxq_params *q_params)
116{
117	int err;
118
119	q_params->txqs_res = kcalloc(q_params->num_qps,
120				     sizeof(*q_params->txqs_res), GFP_KERNEL);
121	if (!q_params->txqs_res)
122		return -ENOMEM;
123
124	q_params->rxqs_res = kcalloc(q_params->num_qps,
125				     sizeof(*q_params->rxqs_res), GFP_KERNEL);
126	if (!q_params->rxqs_res) {
127		err = -ENOMEM;
128		goto err_free_txqs_res_arr;
129	}
130
131	q_params->irq_cfg = kcalloc(q_params->num_qps,
132				    sizeof(*q_params->irq_cfg), GFP_KERNEL);
133	if (!q_params->irq_cfg) {
134		err = -ENOMEM;
135		goto err_free_rxqs_res_arr;
136	}
137
138	err = hinic3_alloc_txqs_res(netdev, q_params->num_qps,
139				    q_params->sq_depth, q_params->txqs_res);
140	if (err) {
141		netdev_err(netdev, "Failed to alloc txqs resource\n");
142		goto err_free_irq_cfg;
143	}
144
145	err = hinic3_alloc_rxqs_res(netdev, q_params->num_qps,
146				    q_params->rq_depth, q_params->rxqs_res);
147	if (err) {
148		netdev_err(netdev, "Failed to alloc rxqs resource\n");
149		goto err_free_txqs_res;
150	}
151
152	return 0;
153
154err_free_txqs_res:
155	hinic3_free_txqs_res(netdev, q_params->num_qps, q_params->sq_depth,
156			     q_params->txqs_res);
157err_free_irq_cfg:
158	kfree(q_params->irq_cfg);
159	q_params->irq_cfg = NULL;
160err_free_rxqs_res_arr:
161	kfree(q_params->rxqs_res);
162	q_params->rxqs_res = NULL;
163err_free_txqs_res_arr:
164	kfree(q_params->txqs_res);
165	q_params->txqs_res = NULL;
166
167	return err;
168}
169
170static void hinic3_free_txrxq_resources(struct net_device *netdev,
171					struct hinic3_dyna_txrxq_params *q_params)
172{
173	hinic3_free_rxqs_res(netdev, q_params->num_qps, q_params->rq_depth,
174			     q_params->rxqs_res);
175	hinic3_free_txqs_res(netdev, q_params->num_qps, q_params->sq_depth,
176			     q_params->txqs_res);
177
178	kfree(q_params->irq_cfg);
179	q_params->irq_cfg = NULL;
180
181	kfree(q_params->rxqs_res);
182	q_params->rxqs_res = NULL;
183
184	kfree(q_params->txqs_res);
185	q_params->txqs_res = NULL;
186}
187
188static int hinic3_configure_txrxqs(struct net_device *netdev,
189				   struct hinic3_dyna_txrxq_params *q_params)
190{
191	int err;
192
193	err = hinic3_configure_txqs(netdev, q_params->num_qps,
194				    q_params->sq_depth, q_params->txqs_res);
195	if (err) {
196		netdev_err(netdev, "Failed to configure txqs\n");
197		return err;
198	}
199
200	err = hinic3_configure_rxqs(netdev, q_params->num_qps,
201				    q_params->rq_depth, q_params->rxqs_res);
202	if (err) {
203		netdev_err(netdev, "Failed to configure rxqs\n");
204		return err;
205	}
206
207	return 0;
208}
209
210static int hinic3_configure(struct net_device *netdev)
211{
212	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
213	int err;
214
215	netdev->min_mtu = HINIC3_MIN_MTU_SIZE;
216	netdev->max_mtu = HINIC3_MAX_JUMBO_FRAME_SIZE;
217	err = hinic3_set_port_mtu(netdev, netdev->mtu);
218	if (err) {
219		netdev_err(netdev, "Failed to set mtu\n");
220		return err;
221	}
222
223	/* Ensure DCB is disabled */
224	hinic3_sync_dcb_state(nic_dev->hwdev, 1, 0);
225
226	if (test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags)) {
227		err = hinic3_rss_init(netdev);
228		if (err) {
229			netdev_err(netdev, "Failed to init rss\n");
230			return err;
231		}
232	}
233
234	return 0;
235}
236
237static void hinic3_remove_configure(struct net_device *netdev)
238{
239	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
240
241	if (test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags))
242		hinic3_rss_uninit(netdev);
243}
244
245static int hinic3_alloc_channel_resources(struct net_device *netdev,
246					  struct hinic3_dyna_qp_params *qp_params,
247					  struct hinic3_dyna_txrxq_params *trxq_params)
248{
249	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
250	int err;
251
252	qp_params->num_qps = trxq_params->num_qps;
253	qp_params->sq_depth = trxq_params->sq_depth;
254	qp_params->rq_depth = trxq_params->rq_depth;
255
256	err = hinic3_alloc_qps(nic_dev, qp_params);
257	if (err) {
258		netdev_err(netdev, "Failed to alloc qps\n");
259		return err;
260	}
261
262	err = hinic3_alloc_txrxq_resources(netdev, trxq_params);
263	if (err) {
264		netdev_err(netdev, "Failed to alloc txrxq resources\n");
265		hinic3_free_qps(nic_dev, qp_params);
266		return err;
267	}
268
269	return 0;
270}
271
272static void hinic3_free_channel_resources(struct net_device *netdev,
273					  struct hinic3_dyna_qp_params *qp_params,
274					  struct hinic3_dyna_txrxq_params *trxq_params)
275{
276	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
277
278	hinic3_free_txrxq_resources(netdev, trxq_params);
279	hinic3_free_qps(nic_dev, qp_params);
280}
281
282static int hinic3_open_channel(struct net_device *netdev)
283{
284	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
285	int err;
286
287	err = hinic3_init_qp_ctxts(nic_dev);
288	if (err) {
289		netdev_err(netdev, "Failed to init qps\n");
290		return err;
291	}
292
293	err = hinic3_configure_txrxqs(netdev, &nic_dev->q_params);
294	if (err) {
295		netdev_err(netdev, "Failed to configure txrxqs\n");
296		goto err_free_qp_ctxts;
297	}
298
299	err = hinic3_qps_irq_init(netdev);
300	if (err) {
301		netdev_err(netdev, "Failed to init txrxq irq\n");
302		goto err_free_qp_ctxts;
303	}
304
305	err = hinic3_configure(netdev);
306	if (err) {
307		netdev_err(netdev, "Failed to configure device resources\n");
308		goto err_uninit_qps_irq;
309	}
310
311	return 0;
312
313err_uninit_qps_irq:
314	hinic3_qps_irq_uninit(netdev);
315err_free_qp_ctxts:
316	hinic3_free_qp_ctxts(nic_dev);
317
318	return err;
319}
320
321static void hinic3_close_channel(struct net_device *netdev)
322{
323	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
324
325	hinic3_remove_configure(netdev);
326	hinic3_qps_irq_uninit(netdev);
327	hinic3_free_qp_ctxts(nic_dev);
328}
329
330static int hinic3_vport_up(struct net_device *netdev)
331{
332	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
333	bool link_status_up;
334	u16 glb_func_id;
335	int err;
336
337	glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
338	err = hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, true);
339	if (err) {
340		netdev_err(netdev, "Failed to enable vport\n");
341		goto err_flush_qps_res;
342	}
343
344	err = netif_set_real_num_queues(netdev, nic_dev->q_params.num_qps,
345					nic_dev->q_params.num_qps);
346	if (err) {
347		netdev_err(netdev, "Failed to set real number of queues\n");
348		goto err_flush_qps_res;
349	}
350	netif_tx_start_all_queues(netdev);
351
352	err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up);
353	if (!err && link_status_up)
354		netif_carrier_on(netdev);
355
356	return 0;
357
358err_flush_qps_res:
359	hinic3_flush_qps_res(nic_dev->hwdev);
360	/* wait to guarantee that no packets will be sent to host */
361	msleep(100);
362
363	return err;
364}
365
366static void hinic3_vport_down(struct net_device *netdev)
367{
368	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
369	u16 glb_func_id;
370
371	netif_carrier_off(netdev);
372	netif_tx_disable(netdev);
373
374	glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
375	hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
376
377	hinic3_flush_txqs(netdev);
378	/* wait to guarantee that no packets will be sent to host */
379	msleep(100);
380	hinic3_flush_qps_res(nic_dev->hwdev);
381}
382
383static int hinic3_open(struct net_device *netdev)
384{
385	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
386	struct hinic3_dyna_qp_params qp_params;
387	int err;
388
389	err = hinic3_init_nicio_res(nic_dev);
390	if (err) {
391		netdev_err(netdev, "Failed to init nicio resources\n");
392		return err;
393	}
394
395	err = hinic3_setup_num_qps(netdev);
396	if (err) {
397		netdev_err(netdev, "Failed to setup num_qps\n");
398		goto err_free_nicio_res;
399	}
400
401	err = hinic3_alloc_channel_resources(netdev, &qp_params,
402					     &nic_dev->q_params);
403	if (err)
404		goto err_destroy_num_qps;
405
406	hinic3_init_qps(nic_dev, &qp_params);
407
408	err = hinic3_open_channel(netdev);
409	if (err)
410		goto err_uninit_qps;
411
412	err = hinic3_vport_up(netdev);
413	if (err)
414		goto err_close_channel;
415
416	return 0;
417
418err_close_channel:
419	hinic3_close_channel(netdev);
420err_uninit_qps:
421	hinic3_uninit_qps(nic_dev, &qp_params);
422	hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
423err_destroy_num_qps:
424	hinic3_destroy_num_qps(netdev);
425err_free_nicio_res:
426	hinic3_free_nicio_res(nic_dev);
427
428	return err;
429}
430
431static int hinic3_close(struct net_device *netdev)
432{
433	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
434	struct hinic3_dyna_qp_params qp_params;
435
436	hinic3_vport_down(netdev);
437	hinic3_close_channel(netdev);
438	hinic3_uninit_qps(nic_dev, &qp_params);
439	hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
440
441	return 0;
442}
443
444static int hinic3_change_mtu(struct net_device *netdev, int new_mtu)
445{
446	int err;
447
448	err = hinic3_set_port_mtu(netdev, new_mtu);
449	if (err) {
450		netdev_err(netdev, "Failed to change port mtu to %d\n",
451			   new_mtu);
452		return err;
453	}
454
455	netdev_dbg(netdev, "Change mtu from %u to %d\n", netdev->mtu, new_mtu);
456	WRITE_ONCE(netdev->mtu, new_mtu);
457
458	return 0;
459}
460
461static int hinic3_set_mac_addr(struct net_device *netdev, void *addr)
462{
463	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
464	struct sockaddr *saddr = addr;
465	int err;
466
467	if (!is_valid_ether_addr(saddr->sa_data))
468		return -EADDRNOTAVAIL;
469
470	if (ether_addr_equal(netdev->dev_addr, saddr->sa_data))
471		return 0;
472
473	err = hinic3_update_mac(nic_dev->hwdev, netdev->dev_addr,
474				saddr->sa_data, 0,
475				hinic3_global_func_id(nic_dev->hwdev));
476
477	if (err)
478		return err;
479
480	eth_hw_addr_set(netdev, saddr->sa_data);
481
482	return 0;
483}
484
485static const struct net_device_ops hinic3_netdev_ops = {
486	.ndo_open             = hinic3_open,
487	.ndo_stop             = hinic3_close,
488	.ndo_change_mtu       = hinic3_change_mtu,
489	.ndo_set_mac_address  = hinic3_set_mac_addr,
490	.ndo_start_xmit       = hinic3_xmit_frame,
491};
492
493void hinic3_set_netdev_ops(struct net_device *netdev)
494{
495	netdev->netdev_ops = &hinic3_netdev_ops;
496}
~/ Read-only file preview.