Avoid race with unregister interrupt hanlder if interrupt source has some active callbacks at the moment, use wrapper around rte_intr_callback_unregister() to check for -EAGAIN return value and to loop until rte_intr_callback_unregister() succeeds.
Signed-off-by: Renata Saiakhova <renata.saiakh...@ekinops.com> Acked-by: Anatoly Burakov <anatoly.bura...@intel.com> --- drivers/bus/pci/linux/pci_vfio.c | 2 +- lib/librte_eal/freebsd/eal_interrupts.c | 12 ++++++++++++ lib/librte_eal/include/rte_interrupts.h | 25 +++++++++++++++++++++++++ lib/librte_eal/linux/eal_interrupts.c | 12 ++++++++++++ lib/librte_eal/version.map | 1 + 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c index e3f7b6abe..c14f7f8c4 100644 --- a/drivers/bus/pci/linux/pci_vfio.c +++ b/drivers/bus/pci/linux/pci_vfio.c @@ -414,7 +414,7 @@ pci_vfio_disable_notifier(struct rte_pci_device *dev) return -1; } - ret = rte_intr_callback_unregister(&dev->vfio_req_intr_handle, + ret = rte_intr_callback_unregister_sync(&dev->vfio_req_intr_handle, pci_vfio_req_handler, (void *)&dev->device); if (ret < 0) { diff --git a/lib/librte_eal/freebsd/eal_interrupts.c b/lib/librte_eal/freebsd/eal_interrupts.c index 72eeacbc1..86810845f 100644 --- a/lib/librte_eal/freebsd/eal_interrupts.c +++ b/lib/librte_eal/freebsd/eal_interrupts.c @@ -345,6 +345,18 @@ rte_intr_callback_unregister(const struct rte_intr_handle *intr_handle, return ret; } +int +rte_intr_callback_unregister_sync(const struct rte_intr_handle *intr_handle, + rte_intr_callback_fn cb_fn, void *cb_arg) +{ + int ret = 0; + + while ((ret = rte_intr_callback_unregister(intr_handle, cb_fn, cb_arg)) == -EAGAIN) + rte_pause(); + + return ret; +} + int rte_intr_enable(const struct rte_intr_handle *intr_handle) { diff --git a/lib/librte_eal/include/rte_interrupts.h b/lib/librte_eal/include/rte_interrupts.h index e3b406abc..cc3bf45d8 100644 --- a/lib/librte_eal/include/rte_interrupts.h +++ b/lib/librte_eal/include/rte_interrupts.h @@ -94,6 +94,31 @@ rte_intr_callback_unregister_pending(const struct rte_intr_handle *intr_handle, rte_intr_callback_fn cb_fn, void *cb_arg, rte_intr_unregister_callback_fn ucb_fn); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Loop until rte_intr_callback_unregister() succeeds. + * After a call to this function, + * the callback provided by the specified interrupt handle is unregistered. + * + * @param intr_handle + * pointer to the interrupt handle. + * @param cb + * callback address. + * @param cb_arg + * address of parameter for callback, (void *)-1 means to remove all + * registered which has the same callback address. + * + * @return + * - On success, return the number of callback entities removed. + * - On failure, a negative value. + */ +__rte_experimental +int +rte_intr_callback_unregister_sync(const struct rte_intr_handle *intr_handle, + rte_intr_callback_fn cb, void *cb_arg); + /** * It enables the interrupt for the specified handle. * diff --git a/lib/librte_eal/linux/eal_interrupts.c b/lib/librte_eal/linux/eal_interrupts.c index 1dd994bd1..22b3b7bcd 100644 --- a/lib/librte_eal/linux/eal_interrupts.c +++ b/lib/librte_eal/linux/eal_interrupts.c @@ -662,6 +662,18 @@ rte_intr_callback_unregister(const struct rte_intr_handle *intr_handle, return ret; } +int +rte_intr_callback_unregister_sync(const struct rte_intr_handle *intr_handle, + rte_intr_callback_fn cb_fn, void *cb_arg) +{ + int ret = 0; + + while ((ret = rte_intr_callback_unregister(intr_handle, cb_fn, cb_arg)) == -EAGAIN) + rte_pause(); + + return ret; +} + int rte_intr_enable(const struct rte_intr_handle *intr_handle) { diff --git a/lib/librte_eal/version.map b/lib/librte_eal/version.map index fce90a112..56caa9cc9 100644 --- a/lib/librte_eal/version.map +++ b/lib/librte_eal/version.map @@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_find_rev_biggest_free; rte_fbarray_find_rev_biggest_used; rte_intr_callback_unregister_pending; + rte_intr_callback_unregister_sync; rte_realloc_socket; # added in 19.08 -- 2.17.2