Message ID | 20240613013557.1169171-2-almasrymina@google.com |
---|---|
State | Superseded |
Headers | show |
Series | [net-next,v12,01/13] netdev: add netdev_rx_queue_restart() | expand |
On 6/13/24 02:35, Mina Almasry wrote: > Add netdev_rx_queue_restart() function to netdev_rx_queue.h see nit below Reviewed-by: Pavel Begunkov <asml.silence@gmail.com> > Signed-off-by: David Wei <dw@davidwei.uk> > Signed-off-by: Mina Almasry <almasrymina@google.com> > > --- > > v11: > - Fix not checking dev->queue_mgmt_ops (Pavel). > - Fix ndo_queue_mem_free call that passed the wrong pointer (David). > > v9: https://lore.kernel.org/all/20240502045410.3524155-4-dw@davidwei.uk/ > (submitted by David). > - fixed SPDX license identifier (Simon). > - Rebased on top of merged queue API definition, and changed > implementation to match that. > - Replace rtnl_lock() with rtnl_is_locked() to make it useable from my > netlink code where rtnl is already locked. > > --- > include/net/netdev_rx_queue.h | 3 ++ > net/core/Makefile | 1 + > net/core/netdev_rx_queue.c | 74 +++++++++++++++++++++++++++++++++++ > 3 files changed, 78 insertions(+) > create mode 100644 net/core/netdev_rx_queue.c > > diff --git a/include/net/netdev_rx_queue.h b/include/net/netdev_rx_queue.h > index aa1716fb0e53c..e78ca52d67fbf 100644 > --- a/include/net/netdev_rx_queue.h > +++ b/include/net/netdev_rx_queue.h > @@ -54,4 +54,7 @@ get_netdev_rx_queue_index(struct netdev_rx_queue *queue) > return index; > } > #endif > + > +int netdev_rx_queue_restart(struct net_device *dev, unsigned int rxq); > + > #endif > diff --git a/net/core/Makefile b/net/core/Makefile > index 62be9aef25285..f82232b358a2c 100644 > --- a/net/core/Makefile > +++ b/net/core/Makefile > @@ -19,6 +19,7 @@ obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o > > obj-y += net-sysfs.o > obj-y += hotdata.o > +obj-y += netdev_rx_queue.o > obj-$(CONFIG_PAGE_POOL) += page_pool.o page_pool_user.o > obj-$(CONFIG_PROC_FS) += net-procfs.o > obj-$(CONFIG_NET_PKTGEN) += pktgen.o > diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c > new file mode 100644 > index 0000000000000..de0575cf6df5d > --- /dev/null > +++ b/net/core/netdev_rx_queue.c > @@ -0,0 +1,74 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > + > +#include <linux/netdevice.h> > +#include <net/netdev_queues.h> > +#include <net/netdev_rx_queue.h> > + > +int netdev_rx_queue_restart(struct net_device *dev, unsigned int rxq_idx) > +{ > + void *new_mem, *old_mem; > + int err; > + > + if (!dev->queue_mgmt_ops || !dev->queue_mgmt_ops->ndo_queue_stop || > + !dev->queue_mgmt_ops->ndo_queue_mem_free || > + !dev->queue_mgmt_ops->ndo_queue_mem_alloc || > + !dev->queue_mgmt_ops->ndo_queue_start) > + return -EOPNOTSUPP; > + > + DEBUG_NET_WARN_ON_ONCE(!rtnl_is_locked()); > + > + new_mem = kvzalloc(dev->queue_mgmt_ops->ndo_queue_mem_size, GFP_KERNEL); > + if (!new_mem) > + return -ENOMEM; > + > + old_mem = kvzalloc(dev->queue_mgmt_ops->ndo_queue_mem_size, GFP_KERNEL); > + if (!old_mem) { > + err = -ENOMEM; > + goto err_free_new_mem; > + } > + > + err = dev->queue_mgmt_ops->ndo_queue_mem_alloc(dev, new_mem, rxq_idx); > + if (err) > + goto err_free_old_mem; > + > + err = dev->queue_mgmt_ops->ndo_queue_stop(dev, old_mem, rxq_idx); > + if (err) > + goto err_free_new_queue_mem; > + > + err = dev->queue_mgmt_ops->ndo_queue_start(dev, new_mem, rxq_idx); > + if (err) > + goto err_start_queue; > + > + dev->queue_mgmt_ops->ndo_queue_mem_free(dev, old_mem); > + > + kvfree(old_mem); > + kvfree(new_mem); > + > + return 0; > + > +err_start_queue: > + /* Restarting the queue with old_mem should be successful as we haven't > + * changed any of the queue configuration, and there is not much we can > + * do to recover from a failure here. > + * > + * WARN if the we fail to recover the old rx queue, and at least free nit "if the we" > + * old_mem so we don't also leak that. > + */ > + if (dev->queue_mgmt_ops->ndo_queue_start(dev, old_mem, rxq_idx)) { > + WARN(1, > + "Failed to restart old queue in error path. RX queue %d may be unhealthy.", > + rxq_idx); > + dev->queue_mgmt_ops->ndo_queue_mem_free(dev, old_mem); > + } > + > +err_free_new_queue_mem: > + dev->queue_mgmt_ops->ndo_queue_mem_free(dev, new_mem); > + > +err_free_old_mem: > + kvfree(old_mem); > + > +err_free_new_mem: > + kvfree(new_mem); > + > + return err; > +}
diff --git a/include/net/netdev_rx_queue.h b/include/net/netdev_rx_queue.h index aa1716fb0e53c..e78ca52d67fbf 100644 --- a/include/net/netdev_rx_queue.h +++ b/include/net/netdev_rx_queue.h @@ -54,4 +54,7 @@ get_netdev_rx_queue_index(struct netdev_rx_queue *queue) return index; } #endif + +int netdev_rx_queue_restart(struct net_device *dev, unsigned int rxq); + #endif diff --git a/net/core/Makefile b/net/core/Makefile index 62be9aef25285..f82232b358a2c 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o obj-y += net-sysfs.o obj-y += hotdata.o +obj-y += netdev_rx_queue.o obj-$(CONFIG_PAGE_POOL) += page_pool.o page_pool_user.o obj-$(CONFIG_PROC_FS) += net-procfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c new file mode 100644 index 0000000000000..de0575cf6df5d --- /dev/null +++ b/net/core/netdev_rx_queue.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/netdevice.h> +#include <net/netdev_queues.h> +#include <net/netdev_rx_queue.h> + +int netdev_rx_queue_restart(struct net_device *dev, unsigned int rxq_idx) +{ + void *new_mem, *old_mem; + int err; + + if (!dev->queue_mgmt_ops || !dev->queue_mgmt_ops->ndo_queue_stop || + !dev->queue_mgmt_ops->ndo_queue_mem_free || + !dev->queue_mgmt_ops->ndo_queue_mem_alloc || + !dev->queue_mgmt_ops->ndo_queue_start) + return -EOPNOTSUPP; + + DEBUG_NET_WARN_ON_ONCE(!rtnl_is_locked()); + + new_mem = kvzalloc(dev->queue_mgmt_ops->ndo_queue_mem_size, GFP_KERNEL); + if (!new_mem) + return -ENOMEM; + + old_mem = kvzalloc(dev->queue_mgmt_ops->ndo_queue_mem_size, GFP_KERNEL); + if (!old_mem) { + err = -ENOMEM; + goto err_free_new_mem; + } + + err = dev->queue_mgmt_ops->ndo_queue_mem_alloc(dev, new_mem, rxq_idx); + if (err) + goto err_free_old_mem; + + err = dev->queue_mgmt_ops->ndo_queue_stop(dev, old_mem, rxq_idx); + if (err) + goto err_free_new_queue_mem; + + err = dev->queue_mgmt_ops->ndo_queue_start(dev, new_mem, rxq_idx); + if (err) + goto err_start_queue; + + dev->queue_mgmt_ops->ndo_queue_mem_free(dev, old_mem); + + kvfree(old_mem); + kvfree(new_mem); + + return 0; + +err_start_queue: + /* Restarting the queue with old_mem should be successful as we haven't + * changed any of the queue configuration, and there is not much we can + * do to recover from a failure here. + * + * WARN if the we fail to recover the old rx queue, and at least free + * old_mem so we don't also leak that. + */ + if (dev->queue_mgmt_ops->ndo_queue_start(dev, old_mem, rxq_idx)) { + WARN(1, + "Failed to restart old queue in error path. RX queue %d may be unhealthy.", + rxq_idx); + dev->queue_mgmt_ops->ndo_queue_mem_free(dev, old_mem); + } + +err_free_new_queue_mem: + dev->queue_mgmt_ops->ndo_queue_mem_free(dev, new_mem); + +err_free_old_mem: + kvfree(old_mem); + +err_free_new_mem: + kvfree(new_mem); + + return err; +}