[v4,3/3] ethdev: allow close function to return an error

Message ID 20201005170820.1018715-4-thomas@monjalon.net (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series cleanup ethdev close operation |

Checks

Context Check Description
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-testing success Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/travis-robot success Travis build: passed
ci/Intel-compilation success Compilation OK
ci/iol-mellanox-Performance success Performance Testing PASS
ci/checkpatch success coding style OK

Commit Message

Thomas Monjalon Oct. 5, 2020, 5:08 p.m. UTC
  The API function rte_eth_dev_close() was returning void.
The return type is changed to int for notifying of errors.

If an error happens during a close operation,
the status of the port is undefined,
a maximum of resources having been freed.

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
Reviewed-by: Liron Himi <lironh@marvell.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
---
 doc/guides/rel_notes/deprecation.rst    |  1 -
 doc/guides/rel_notes/release_20_11.rst  |  4 +++-
 drivers/net/cxgbe/cxgbe_ethdev.c        |  5 +++--
 drivers/net/cxgbe/cxgbevf_ethdev.c      |  5 +++--
 drivers/net/failsafe/failsafe_ether.c   |  6 +++++-
 drivers/net/failsafe/failsafe_ops.c     | 25 +++++++++++++++++--------
 drivers/net/memif/rte_eth_memif.c       |  4 +---
 drivers/net/mlx5/mlx5.c                 |  7 ++++---
 drivers/net/mvneta/mvneta_ethdev.c      |  5 +++--
 drivers/net/mvpp2/mrvl_ethdev.c         |  5 +++--
 drivers/net/netvsc/hn_ethdev.c          |  6 ++++--
 drivers/net/netvsc/hn_var.h             |  2 +-
 drivers/net/netvsc/hn_vf.c              |  7 +++++--
 drivers/net/virtio/virtio_user_ethdev.c |  4 +---
 lib/librte_ethdev/rte_ethdev.c          | 16 +++++++++++-----
 lib/librte_ethdev/rte_ethdev.h          |  5 ++++-
 16 files changed, 68 insertions(+), 39 deletions(-)
  

Comments

Ferruh Yigit Oct. 6, 2020, 9:43 a.m. UTC | #1
On 10/5/2020 6:08 PM, Thomas Monjalon wrote:
> The API function rte_eth_dev_close() was returning void.
> The return type is changed to int for notifying of errors.
> 
> If an error happens during a close operation,
> the status of the port is undefined,
> a maximum of resources having been freed.
> 
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> Reviewed-by: Liron Himi <lironh@marvell.com>
> Acked-by: Stephen Hemminger <stephen@networkplumber.org>

<...>

> -void
> +int
>   rte_eth_dev_close(uint16_t port_id)
>   {
>   	struct rte_eth_dev *dev;
> +	int firsterr, binerr;
> +	int *lasterr = &firsterr;
>   
> -	RTE_ETH_VALID_PORTID_OR_RET(port_id);
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
>   	dev = &rte_eth_devices[port_id];
>   
> -	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_close);
> -	(*dev->dev_ops->dev_close)(dev);
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
> +	*lasterr = (*dev->dev_ops->dev_close)(dev);
> +	if (*lasterr != 0)
> +		lasterr = &binerr;
>   
>   	rte_ethdev_trace_close(port_id);
> -	rte_eth_dev_release_port(dev);
> +	*lasterr = rte_eth_dev_release_port(dev);
> +
> +	return firsterr;
>   }

This may be personal taste but above error handling looks like unnecessary 
complex, what do you think something like below:

close_err = (*dev->dev_ops->dev_close)(dev);
release_err = rte_eth_dev_release_port(dev);
return close_err ? close_err : release_err;
  
Thomas Monjalon Oct. 6, 2020, 10:57 a.m. UTC | #2
06/10/2020 11:43, Ferruh Yigit:
> On 10/5/2020 6:08 PM, Thomas Monjalon wrote:
> > The API function rte_eth_dev_close() was returning void.
> > The return type is changed to int for notifying of errors.
> > 
> > If an error happens during a close operation,
> > the status of the port is undefined,
> > a maximum of resources having been freed.
> > 
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> > Reviewed-by: Liron Himi <lironh@marvell.com>
> > Acked-by: Stephen Hemminger <stephen@networkplumber.org>
> 
> <...>
> 
> > -void
> > +int
> >   rte_eth_dev_close(uint16_t port_id)
> >   {
> >   	struct rte_eth_dev *dev;
> > +	int firsterr, binerr;
> > +	int *lasterr = &firsterr;
> >   
> > -	RTE_ETH_VALID_PORTID_OR_RET(port_id);
> > +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
> >   	dev = &rte_eth_devices[port_id];
> >   
> > -	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_close);
> > -	(*dev->dev_ops->dev_close)(dev);
> > +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
> > +	*lasterr = (*dev->dev_ops->dev_close)(dev);
> > +	if (*lasterr != 0)
> > +		lasterr = &binerr;
> >   
> >   	rte_ethdev_trace_close(port_id);
> > -	rte_eth_dev_release_port(dev);
> > +	*lasterr = rte_eth_dev_release_port(dev);
> > +
> > +	return firsterr;
> >   }
> 
> This may be personal taste but above error handling looks like unnecessary 
> complex, what do you think something like below:
> 
> close_err = (*dev->dev_ops->dev_close)(dev);
> release_err = rte_eth_dev_release_port(dev);
> return close_err ? close_err : release_err;

This is what I did first. Then thought about this "elegant" handling.
The pointer solution is just one more line and is more future proof.

I'm fine with any choice. Andrew?
  
Andrew Rybchenko Oct. 13, 2020, 8:40 a.m. UTC | #3
On 10/6/20 1:57 PM, Thomas Monjalon wrote:
> 06/10/2020 11:43, Ferruh Yigit:
>> On 10/5/2020 6:08 PM, Thomas Monjalon wrote:
>>> The API function rte_eth_dev_close() was returning void.
>>> The return type is changed to int for notifying of errors.
>>>
>>> If an error happens during a close operation,
>>> the status of the port is undefined,
>>> a maximum of resources having been freed.
>>>
>>> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
>>> Reviewed-by: Liron Himi <lironh@marvell.com>
>>> Acked-by: Stephen Hemminger <stephen@networkplumber.org>
>>
>> <...>
>>
>>> -void
>>> +int
>>>   rte_eth_dev_close(uint16_t port_id)
>>>   {
>>>   	struct rte_eth_dev *dev;
>>> +	int firsterr, binerr;
>>> +	int *lasterr = &firsterr;
>>>   
>>> -	RTE_ETH_VALID_PORTID_OR_RET(port_id);
>>> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);

Shouldn't it be -ENODEV in fact?
IMHO it would be nice to make it uniform across ethdev API.

>>>   	dev = &rte_eth_devices[port_id];
>>>   
>>> -	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_close);
>>> -	(*dev->dev_ops->dev_close)(dev);
>>> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
>>> +	*lasterr = (*dev->dev_ops->dev_close)(dev);
>>> +	if (*lasterr != 0)
>>> +		lasterr = &binerr;
>>>   
>>>   	rte_ethdev_trace_close(port_id);
>>> -	rte_eth_dev_release_port(dev);
>>> +	*lasterr = rte_eth_dev_release_port(dev);
>>> +
>>> +	return firsterr;
>>>   }
>>
>> This may be personal taste but above error handling looks like unnecessary 
>> complex, what do you think something like below:
>>
>> close_err = (*dev->dev_ops->dev_close)(dev);
>> release_err = rte_eth_dev_release_port(dev);
>> return close_err ? close_err : release_err;
> 
> This is what I did first. Then thought about this "elegant" handling.
> The pointer solution is just one more line and is more future proof.
> 
> I'm fine with any choice. Andrew?
> 

Hm, really hard to make a choice. I tend to choose Thomas's
version. Yes, I agree that it is a bit over-complicated in
this particular case, but thoughts to be future-proof
overweight for me. Plus it adds a pattern on how to handle
such cases in the future.
  
Thomas Monjalon Oct. 13, 2020, 8:55 a.m. UTC | #4
13/10/2020 10:40, Andrew Rybchenko:
> On 10/6/20 1:57 PM, Thomas Monjalon wrote:
> > 06/10/2020 11:43, Ferruh Yigit:
> >> On 10/5/2020 6:08 PM, Thomas Monjalon wrote:
> >>> The API function rte_eth_dev_close() was returning void.
> >>> The return type is changed to int for notifying of errors.
> >>>
> >>> If an error happens during a close operation,
> >>> the status of the port is undefined,
> >>> a maximum of resources having been freed.
> >>>
> >>> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> >>> Reviewed-by: Liron Himi <lironh@marvell.com>
> >>> Acked-by: Stephen Hemminger <stephen@networkplumber.org>
> >>
> >> <...>
> >>
> >>> -void
> >>> +int
> >>>   rte_eth_dev_close(uint16_t port_id)
> >>>   {
> >>>   	struct rte_eth_dev *dev;
> >>> +	int firsterr, binerr;
> >>> +	int *lasterr = &firsterr;
> >>>   
> >>> -	RTE_ETH_VALID_PORTID_OR_RET(port_id);
> >>> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
> 
> Shouldn't it be -ENODEV in fact?

Yes you're right.

> IMHO it would be nice to make it uniform across ethdev API.

Yes, do you want to make the patch to change other occurences to ENODEV?


> >>>   	dev = &rte_eth_devices[port_id];
> >>>   
> >>> -	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_close);
> >>> -	(*dev->dev_ops->dev_close)(dev);
> >>> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
> >>> +	*lasterr = (*dev->dev_ops->dev_close)(dev);
> >>> +	if (*lasterr != 0)
> >>> +		lasterr = &binerr;
> >>>   
> >>>   	rte_ethdev_trace_close(port_id);
> >>> -	rte_eth_dev_release_port(dev);
> >>> +	*lasterr = rte_eth_dev_release_port(dev);
> >>> +
> >>> +	return firsterr;
> >>>   }
> >>
> >> This may be personal taste but above error handling looks like unnecessary 
> >> complex, what do you think something like below:
> >>
> >> close_err = (*dev->dev_ops->dev_close)(dev);
> >> release_err = rte_eth_dev_release_port(dev);
> >> return close_err ? close_err : release_err;
> > 
> > This is what I did first. Then thought about this "elegant" handling.
> > The pointer solution is just one more line and is more future proof.
> > 
> > I'm fine with any choice. Andrew?
> > 
> 
> Hm, really hard to make a choice. I tend to choose Thomas's
> version. Yes, I agree that it is a bit over-complicated in
> this particular case, but thoughts to be future-proof
> overweight for me. Plus it adds a pattern on how to handle
> such cases in the future.

Yes it's an elegant pattern :)
  
Ferruh Yigit Oct. 13, 2020, 9:33 a.m. UTC | #5
On 10/13/2020 9:55 AM, Thomas Monjalon wrote:

<...>

>>>>>    	dev = &rte_eth_devices[port_id];
>>>>>    
>>>>> -	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_close);
>>>>> -	(*dev->dev_ops->dev_close)(dev);
>>>>> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
>>>>> +	*lasterr = (*dev->dev_ops->dev_close)(dev);
>>>>> +	if (*lasterr != 0)
>>>>> +		lasterr = &binerr;
>>>>>    
>>>>>    	rte_ethdev_trace_close(port_id);
>>>>> -	rte_eth_dev_release_port(dev);
>>>>> +	*lasterr = rte_eth_dev_release_port(dev);
>>>>> +
>>>>> +	return firsterr;
>>>>>    }
>>>>
>>>> This may be personal taste but above error handling looks like unnecessary
>>>> complex, what do you think something like below:
>>>>
>>>> close_err = (*dev->dev_ops->dev_close)(dev);
>>>> release_err = rte_eth_dev_release_port(dev);
>>>> return close_err ? close_err : release_err;
>>>
>>> This is what I did first. Then thought about this "elegant" handling.
>>> The pointer solution is just one more line and is more future proof.
>>>
>>> I'm fine with any choice. Andrew?
>>>
>>
>> Hm, really hard to make a choice. I tend to choose Thomas's
>> version. Yes, I agree that it is a bit over-complicated in
>> this particular case, but thoughts to be future-proof
>> overweight for me. Plus it adds a pattern on how to handle
>> such cases in the future.
> 
> Yes it's an elegant pattern :)
> 

OK :)
  

Patch

diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 0be208edca..e0b302e080 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -135,7 +135,6 @@  Deprecation Notices
   invalid port ID, unsupported operation, failed operation):
 
   - ``rte_eth_dev_stop``
-  - ``rte_eth_dev_close``
 
 * ethdev: New offload flags ``DEV_RX_OFFLOAD_FLOW_MARK`` will be added in 19.11.
   This will allow application to enable or disable PMDs from updating
diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 4e61431c6c..617e54e981 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -189,7 +189,9 @@  API Changes
 
 * ethdev: ``rte_eth_rx_descriptor_done()`` API has been deprecated.
 
-* Renamed internal ethdev APIs:
+* ethdev: Added ``int`` return type to ``rte_eth_dev_close()``.
+
+* ethdev: Renamed internal functions:
 
   * ``_rte_eth_dev_callback_process()`` -> ``rte_eth_dev_callback_process()``
   * ``_rte_eth_dev_reset`` -> ``rte_eth_dev_internal_reset()``
diff --git a/drivers/net/cxgbe/cxgbe_ethdev.c b/drivers/net/cxgbe/cxgbe_ethdev.c
index 16beb2d435..fe488231a7 100644
--- a/drivers/net/cxgbe/cxgbe_ethdev.c
+++ b/drivers/net/cxgbe/cxgbe_ethdev.c
@@ -1296,12 +1296,13 @@  static int eth_cxgbe_dev_uninit(struct rte_eth_dev *eth_dev)
 {
 	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
 	uint16_t port_id;
+	int err = 0;
 
 	/* Free up other ports and all resources */
 	RTE_ETH_FOREACH_DEV_OF(port_id, &pci_dev->device)
-		rte_eth_dev_close(port_id);
+		err |= rte_eth_dev_close(port_id);
 
-	return 0;
+	return err == 0 ? 0 : -EIO;
 }
 
 static int eth_cxgbe_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
diff --git a/drivers/net/cxgbe/cxgbevf_ethdev.c b/drivers/net/cxgbe/cxgbevf_ethdev.c
index 947fcdd406..c2918f5356 100644
--- a/drivers/net/cxgbe/cxgbevf_ethdev.c
+++ b/drivers/net/cxgbe/cxgbevf_ethdev.c
@@ -183,12 +183,13 @@  static int eth_cxgbevf_dev_uninit(struct rte_eth_dev *eth_dev)
 {
 	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
 	uint16_t port_id;
+	int err = 0;
 
 	/* Free up other ports and all resources */
 	RTE_ETH_FOREACH_DEV_OF(port_id, &pci_dev->device)
-		rte_eth_dev_close(port_id);
+		err |= rte_eth_dev_close(port_id);
 
-	return 0;
+	return err == 0 ? 0 : -EIO;
 }
 
 static int eth_cxgbevf_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
diff --git a/drivers/net/failsafe/failsafe_ether.c b/drivers/net/failsafe/failsafe_ether.c
index 7c68bbdec0..950d35dac4 100644
--- a/drivers/net/failsafe/failsafe_ether.c
+++ b/drivers/net/failsafe/failsafe_ether.c
@@ -287,7 +287,11 @@  fs_dev_remove(struct sub_device *sdev)
 		/* fallthrough */
 	case DEV_ACTIVE:
 		failsafe_eth_dev_unregister_callbacks(sdev);
-		rte_eth_dev_close(PORT_ID(sdev));
+		ret = rte_eth_dev_close(PORT_ID(sdev));
+		if (ret < 0) {
+			ERROR("Port close failed for sub-device %u",
+			      PORT_ID(sdev));
+		}
 		sdev->state = DEV_PROBED;
 		/* fallthrough */
 	case DEV_PROBED:
diff --git a/drivers/net/failsafe/failsafe_ops.c b/drivers/net/failsafe/failsafe_ops.c
index 0ce7dfc8a6..5c606ff077 100644
--- a/drivers/net/failsafe/failsafe_ops.c
+++ b/drivers/net/failsafe/failsafe_ops.c
@@ -638,7 +638,7 @@  failsafe_eth_dev_close(struct rte_eth_dev *dev)
 {
 	struct sub_device *sdev;
 	uint8_t i;
-	int ret;
+	int err, ret = 0;
 
 	fs_lock(dev, 0);
 	failsafe_hotplug_alarm_cancel(dev);
@@ -648,29 +648,38 @@  failsafe_eth_dev_close(struct rte_eth_dev *dev)
 	FOREACH_SUBDEV_STATE(sdev, i, dev, DEV_ACTIVE) {
 		DEBUG("Closing sub_device %d", i);
 		failsafe_eth_dev_unregister_callbacks(sdev);
-		rte_eth_dev_close(PORT_ID(sdev));
+		err = rte_eth_dev_close(PORT_ID(sdev));
+		if (err) {
+			ret = ret ? ret : err;
+			ERROR("Error while closing sub-device %u",
+					PORT_ID(sdev));
+		}
 		sdev->state = DEV_ACTIVE - 1;
 	}
 	rte_eth_dev_callback_unregister(RTE_ETH_ALL, RTE_ETH_EVENT_NEW,
 					failsafe_eth_new_event_callback, dev);
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
 		fs_unlock(dev, 0);
-		return 0;
+		return ret;
 	}
 	fs_dev_free_queues(dev);
-	ret = failsafe_eal_uninit(dev);
-	if (ret)
+	err = failsafe_eal_uninit(dev);
+	if (err) {
+		ret = ret ? ret : err;
 		ERROR("Error while uninitializing sub-EAL");
+	}
 	failsafe_args_free(dev);
 	rte_free(PRIV(dev)->subs);
 	rte_free(PRIV(dev)->mcast_addrs);
 	/* mac_addrs must not be freed alone because part of dev_private */
 	dev->data->mac_addrs = NULL;
 	fs_unlock(dev, 0);
-	ret = pthread_mutex_destroy(&PRIV(dev)->hotplug_mutex);
-	if (ret)
+	err = pthread_mutex_destroy(&PRIV(dev)->hotplug_mutex);
+	if (err) {
+		ret = ret ? ret : err;
 		ERROR("Error while destroying hotplug mutex");
-	return 0;
+	}
+	return ret;
 }
 
 static int
diff --git a/drivers/net/memif/rte_eth_memif.c b/drivers/net/memif/rte_eth_memif.c
index ff8a58081f..33bf5c68a3 100644
--- a/drivers/net/memif/rte_eth_memif.c
+++ b/drivers/net/memif/rte_eth_memif.c
@@ -1798,9 +1798,7 @@  rte_pmd_memif_remove(struct rte_vdev_device *vdev)
 	if (eth_dev == NULL)
 		return 0;
 
-	rte_eth_dev_close(eth_dev->data->port_id);
-
-	return 0;
+	return rte_eth_dev_close(eth_dev->data->port_id);
 }
 
 static struct rte_vdev_driver pmd_memif_drv = {
diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index 01ead6e6af..2e2f0b274c 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -2010,6 +2010,7 @@  static int
 mlx5_pci_remove(struct rte_pci_device *pci_dev)
 {
 	uint16_t port_id;
+	int ret = 0;
 
 	RTE_ETH_FOREACH_DEV_OF(port_id, &pci_dev->device) {
 		/*
@@ -2017,11 +2018,11 @@  mlx5_pci_remove(struct rte_pci_device *pci_dev)
 		 * call the close function explicitly for secondary process.
 		 */
 		if (rte_eal_process_type() == RTE_PROC_SECONDARY)
-			mlx5_dev_close(&rte_eth_devices[port_id]);
+			ret |= mlx5_dev_close(&rte_eth_devices[port_id]);
 		else
-			rte_eth_dev_close(port_id);
+			ret |= rte_eth_dev_close(port_id);
 	}
-	return 0;
+	return ret == 0 ? 0 : -EIO;
 }
 
 static const struct rte_pci_id mlx5_pci_id_map[] = {
diff --git a/drivers/net/mvneta/mvneta_ethdev.c b/drivers/net/mvneta/mvneta_ethdev.c
index 3c0332ab4d..1574bf35a8 100644
--- a/drivers/net/mvneta/mvneta_ethdev.c
+++ b/drivers/net/mvneta/mvneta_ethdev.c
@@ -966,14 +966,15 @@  static int
 rte_pmd_mvneta_remove(struct rte_vdev_device *vdev)
 {
 	uint16_t port_id;
+	int ret = 0;
 
 	RTE_ETH_FOREACH_DEV(port_id) {
 		if (rte_eth_devices[port_id].device != &vdev->device)
 			continue;
-		rte_eth_dev_close(port_id);
+		ret |= rte_eth_dev_close(port_id);
 	}
 
-	return 0;
+	return ret == 0 ? 0 : -EIO;
 }
 
 static struct rte_vdev_driver pmd_mvneta_drv = {
diff --git a/drivers/net/mvpp2/mrvl_ethdev.c b/drivers/net/mvpp2/mrvl_ethdev.c
index a230a96840..df9f336fab 100644
--- a/drivers/net/mvpp2/mrvl_ethdev.c
+++ b/drivers/net/mvpp2/mrvl_ethdev.c
@@ -3022,14 +3022,15 @@  static int
 rte_pmd_mrvl_remove(struct rte_vdev_device *vdev)
 {
 	uint16_t port_id;
+	int ret = 0;
 
 	RTE_ETH_FOREACH_DEV(port_id) {
 		if (rte_eth_devices[port_id].device != &vdev->device)
 			continue;
-		rte_eth_dev_close(port_id);
+		ret |= rte_eth_dev_close(port_id);
 	}
 
-	return 0;
+	return ret == 0 ? 0 : -EIO;
 }
 
 static struct rte_vdev_driver pmd_mrvl_drv = {
diff --git a/drivers/net/netvsc/hn_ethdev.c b/drivers/net/netvsc/hn_ethdev.c
index 8743e6e69a..40e6d25f34 100644
--- a/drivers/net/netvsc/hn_ethdev.c
+++ b/drivers/net/netvsc/hn_ethdev.c
@@ -842,14 +842,16 @@  hn_dev_stop(struct rte_eth_dev *dev)
 static int
 hn_dev_close(struct rte_eth_dev *dev)
 {
+	int ret;
+
 	PMD_INIT_FUNC_TRACE();
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		return 0;
 
-	hn_vf_close(dev);
+	ret = hn_vf_close(dev);
 	hn_dev_free_queues(dev);
 
-	return 0;
+	return ret;
 }
 
 static const struct eth_dev_ops hn_eth_dev_ops = {
diff --git a/drivers/net/netvsc/hn_var.h b/drivers/net/netvsc/hn_var.h
index 4b63f87607..74f30669ac 100644
--- a/drivers/net/netvsc/hn_var.h
+++ b/drivers/net/netvsc/hn_var.h
@@ -217,7 +217,7 @@  const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev);
 int	hn_vf_start(struct rte_eth_dev *dev);
 void	hn_vf_reset(struct rte_eth_dev *dev);
 void	hn_vf_stop(struct rte_eth_dev *dev);
-void	hn_vf_close(struct rte_eth_dev *dev);
+int	hn_vf_close(struct rte_eth_dev *dev);
 
 int	hn_vf_allmulticast_enable(struct rte_eth_dev *dev);
 int	hn_vf_allmulticast_disable(struct rte_eth_dev *dev);
diff --git a/drivers/net/netvsc/hn_vf.c b/drivers/net/netvsc/hn_vf.c
index f5f15c0462..d29eee7627 100644
--- a/drivers/net/netvsc/hn_vf.c
+++ b/drivers/net/netvsc/hn_vf.c
@@ -316,18 +316,21 @@  void hn_vf_reset(struct rte_eth_dev *dev)
 	VF_ETHDEV_FUNC(dev, rte_eth_dev_reset);
 }
 
-void hn_vf_close(struct rte_eth_dev *dev)
+int hn_vf_close(struct rte_eth_dev *dev)
 {
 	struct hn_data *hv = dev->data->dev_private;
 	uint16_t vf_port;
+	int ret = 0;
 
 	rte_rwlock_read_lock(&hv->vf_lock);
 	vf_port = hv->vf_port;
 	if (vf_port != HN_INVALID_PORT)
-		rte_eth_dev_close(vf_port);
+		ret = rte_eth_dev_close(vf_port);
 
 	hv->vf_port = HN_INVALID_PORT;
 	rte_rwlock_read_unlock(&hv->vf_lock);
+
+	return ret;
 }
 
 int hn_vf_stats_reset(struct rte_eth_dev *dev)
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index f56fc238c4..042665bc09 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -881,9 +881,7 @@  virtio_user_pmd_remove(struct rte_vdev_device *vdev)
 		return rte_eth_dev_release_port(eth_dev);
 
 	/* make sure the device is stopped, queues freed */
-	rte_eth_dev_close(eth_dev->data->port_id);
-
-	return 0;
+	return rte_eth_dev_close(eth_dev->data->port_id);
 }
 
 static int virtio_user_pmd_dma_map(struct rte_vdev_device *vdev, void *addr,
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 7b33faa58b..8331273370 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -1716,19 +1716,25 @@  rte_eth_dev_set_link_down(uint16_t port_id)
 	return eth_err(port_id, (*dev->dev_ops->dev_set_link_down)(dev));
 }
 
-void
+int
 rte_eth_dev_close(uint16_t port_id)
 {
 	struct rte_eth_dev *dev;
+	int firsterr, binerr;
+	int *lasterr = &firsterr;
 
-	RTE_ETH_VALID_PORTID_OR_RET(port_id);
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
 	dev = &rte_eth_devices[port_id];
 
-	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_close);
-	(*dev->dev_ops->dev_close)(dev);
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
+	*lasterr = (*dev->dev_ops->dev_close)(dev);
+	if (*lasterr != 0)
+		lasterr = &binerr;
 
 	rte_ethdev_trace_close(port_id);
-	rte_eth_dev_release_port(dev);
+	*lasterr = rte_eth_dev_release_port(dev);
+
+	return firsterr;
 }
 
 int
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index d2bf74f128..92f03d9778 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2281,8 +2281,11 @@  int rte_eth_dev_set_link_down(uint16_t port_id);
  *
  * @param port_id
  *   The port identifier of the Ethernet device.
+ * @return
+ *   - Zero if the port is closed successfully.
+ *   - Negative if something went wrong.
  */
-void rte_eth_dev_close(uint16_t port_id);
+int rte_eth_dev_close(uint16_t port_id);
 
 /**
  * Reset a Ethernet device and keep its port id.