xref: /linux/net/core/netdev_rx_queue.c (revision 170aafe35cb98e0f3fbacb446ea86389fbce22ea)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 #include <linux/netdevice.h>
4 #include <net/netdev_queues.h>
5 #include <net/netdev_rx_queue.h>
6 
7 int netdev_rx_queue_restart(struct net_device *dev, unsigned int rxq_idx)
8 {
9 	void *new_mem, *old_mem;
10 	int err;
11 
12 	if (!dev->queue_mgmt_ops || !dev->queue_mgmt_ops->ndo_queue_stop ||
13 	    !dev->queue_mgmt_ops->ndo_queue_mem_free ||
14 	    !dev->queue_mgmt_ops->ndo_queue_mem_alloc ||
15 	    !dev->queue_mgmt_ops->ndo_queue_start)
16 		return -EOPNOTSUPP;
17 
18 	ASSERT_RTNL();
19 
20 	new_mem = kvzalloc(dev->queue_mgmt_ops->ndo_queue_mem_size, GFP_KERNEL);
21 	if (!new_mem)
22 		return -ENOMEM;
23 
24 	old_mem = kvzalloc(dev->queue_mgmt_ops->ndo_queue_mem_size, GFP_KERNEL);
25 	if (!old_mem) {
26 		err = -ENOMEM;
27 		goto err_free_new_mem;
28 	}
29 
30 	err = dev->queue_mgmt_ops->ndo_queue_mem_alloc(dev, new_mem, rxq_idx);
31 	if (err)
32 		goto err_free_old_mem;
33 
34 	err = dev->queue_mgmt_ops->ndo_queue_stop(dev, old_mem, rxq_idx);
35 	if (err)
36 		goto err_free_new_queue_mem;
37 
38 	err = dev->queue_mgmt_ops->ndo_queue_start(dev, new_mem, rxq_idx);
39 	if (err)
40 		goto err_start_queue;
41 
42 	dev->queue_mgmt_ops->ndo_queue_mem_free(dev, old_mem);
43 
44 	kvfree(old_mem);
45 	kvfree(new_mem);
46 
47 	return 0;
48 
49 err_start_queue:
50 	/* Restarting the queue with old_mem should be successful as we haven't
51 	 * changed any of the queue configuration, and there is not much we can
52 	 * do to recover from a failure here.
53 	 *
54 	 * WARN if we fail to recover the old rx queue, and at least free
55 	 * old_mem so we don't also leak that.
56 	 */
57 	if (dev->queue_mgmt_ops->ndo_queue_start(dev, old_mem, rxq_idx)) {
58 		WARN(1,
59 		     "Failed to restart old queue in error path. RX queue %d may be unhealthy.",
60 		     rxq_idx);
61 		dev->queue_mgmt_ops->ndo_queue_mem_free(dev, old_mem);
62 	}
63 
64 err_free_new_queue_mem:
65 	dev->queue_mgmt_ops->ndo_queue_mem_free(dev, new_mem);
66 
67 err_free_old_mem:
68 	kvfree(old_mem);
69 
70 err_free_new_mem:
71 	kvfree(new_mem);
72 
73 	return err;
74 }
75