diff mbox series

[ovs-dev] netdev-dpdk: add support for the RTE_ETH_EVENT_INTR_RESET event

Message ID 7a84d2f4050ed1a43c51e93ee8cb1a96e1ebadc5.1567683598.git.echaudro@redhat.com
State Superseded
Headers show
Series [ovs-dev] netdev-dpdk: add support for the RTE_ETH_EVENT_INTR_RESET event | expand

Commit Message

Eelco Chaudron Sept. 5, 2019, 11:48 a.m. UTC
Currently, OVS does not register and therefore not handle the
interface reset event from the DPDK framework. This would cause a
problem in cases where a VF is used as an interface, and its
configuration changes.

As an example in the following scenario the MAC change is not
detected/acted upon until OVS is restarted without the patch applied:

$ echo 1 > /sys/bus/pci/devices/0000:05:00.1/sriov_numvfs
$ ovs-vsctl add-port ovs_pvp_br0 dpdk0 -- \
          set Interface dpdk0 type=dpdk -- \
          set Interface dpdk0 options:dpdk-devargs=0000:05:0a.0

$ ip link set p5p2 vf 0 mac 52:54:00:92:d3:33

Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
---
 lib/netdev-dpdk.c | 49 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

Comments

Ilya Maximets Sept. 5, 2019, 12:40 p.m. UTC | #1
Hi Eelco,
Thanks for the patch! Looks reasonable.

One comment is that it's better to explicitly initialize the
flag in common_construct.  I see that we doesn't initialize
'started' flag, but this might be fixed too.

More comments inline.

Best regards, Ilya Maximets.

On 05.09.2019 14:48, Eelco Chaudron wrote:
> Currently, OVS does not register and therefore not handle the
> interface reset event from the DPDK framework. This would cause a
> problem in cases where a VF is used as an interface, and its
> configuration changes.
> 
> As an example in the following scenario the MAC change is not
> detected/acted upon until OVS is restarted without the patch applied:
> 
> $ echo 1 > /sys/bus/pci/devices/0000:05:00.1/sriov_numvfs
> $ ovs-vsctl add-port ovs_pvp_br0 dpdk0 -- \
>           set Interface dpdk0 type=dpdk -- \
>           set Interface dpdk0 options:dpdk-devargs=0000:05:0a.0
> 
> $ ip link set p5p2 vf 0 mac 52:54:00:92:d3:33
> 
> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
> ---
>  lib/netdev-dpdk.c | 49 +++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 47 insertions(+), 2 deletions(-)
> 
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index bc20d6843..a23150387 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -362,6 +362,7 @@ struct netdev_dpdk {
>          bool attached;
>          /* If true, rte_eth_dev_start() was successfully called */
>          bool started;
> +        bool reset_needed;

This will produce a hole in the structure as members will no longer
fit into a single cacheline.  And cacheline markers will be misaligned.

See details for similar issue here:
https://mail.openvswitch.org/pipermail/ovs-dev/2019-September/362349.html

Possible solution is to turn existing flags into bit fields or stop
avoiding the problem and drop most of padding in the structure.


>          struct eth_addr hwaddr;
>          int mtu;
>          int socket_id;
> @@ -1735,6 +1736,34 @@ netdev_dpdk_process_devargs(struct netdev_dpdk *dev,
>      return new_port_id;
>  }
>  
> +static int
> +dpdk_eth_event_callback(uint16_t port_id, enum rte_eth_event_type type,

s/uint16_t/dpdk_port_t/

> +                        void *param OVS_UNUSED, void *ret_param OVS_UNUSED)
> +{
> +    struct netdev_dpdk *dev;
> +
> +    switch ((int)type) {

Do we need this cast?

> +

Empty line not needed.

> +    case RTE_ETH_EVENT_INTR_RESET:
> +

Empty line not needed.

> +        ovs_mutex_lock(&dpdk_mutex);
> +        dev = netdev_dpdk_lookup_by_port_id(port_id);
> +        if (dev) {
> +            ovs_mutex_lock(&dev->mutex);
> +            dev->reset_needed = true;
> +            netdev_request_reconfigure(&dev->up);> +            ovs_mutex_unlock(&dev->mutex);

Maybe it'll be good to add at leas debug logs here?

> +        }
> +        ovs_mutex_unlock(&dpdk_mutex);
> +        break;
> +
> +    default:
> +        /* Ignore all other types */
> +        break;
> +   }
> +   return 0;
> +}
> +
>  static void
>  dpdk_set_rxq_config(struct netdev_dpdk *dev, const struct smap *args)
>      OVS_REQUIRES(dev->mutex)
> @@ -3777,6 +3806,8 @@ netdev_dpdk_class_init(void)
>      /* This function can be called for different classes.  The initialization
>       * needs to be done only once */
>      if (ovsthread_once_start(&once)) {
> +        int ret;
> +
>          ovs_thread_create("dpdk_watchdog", dpdk_watchdog, NULL);
>          unixctl_command_register("netdev-dpdk/set-admin-state",
>                                   "[netdev] up|down", 1, 2,
> @@ -3790,6 +3821,14 @@ netdev_dpdk_class_init(void)
>                                   "[netdev]", 0, 1,
>                                   netdev_dpdk_get_mempool_info, NULL);
>  
> +        ret = rte_eth_dev_callback_register(RTE_ETH_ALL,
> +                                            RTE_ETH_EVENT_INTR_RESET,
> +                                            dpdk_eth_event_callback, NULL);
> +
> +        if (ret != 0) {
> +            VLOG_ERR("Ethernet device callback register error %d", ret);

It's better to print rte_strerror(-ret).

> +        }
> +
>          ovsthread_once_done(&once);
>      }
>  
> @@ -4150,13 +4189,19 @@ netdev_dpdk_reconfigure(struct netdev *netdev)
>          && dev->rxq_size == dev->requested_rxq_size
>          && dev->txq_size == dev->requested_txq_size
>          && dev->socket_id == dev->requested_socket_id
> -        && dev->started) {
> +        && dev->started && !dev->reset_needed) {
>          /* Reconfiguration is unnecessary */
>  
>          goto out;
>      }
>  
> -    rte_eth_dev_stop(dev->port_id);
> +    if (dev->reset_needed) {
> +        rte_eth_dev_reset(dev->port_id);
> +        dev->reset_needed = false;
> +    } else {
> +        rte_eth_dev_stop(dev->port_id);
> +    }
> +
>      dev->started = false;
>  
>      err = netdev_dpdk_mempool_configure(dev);
>
Eelco Chaudron Sept. 5, 2019, 2:44 p.m. UTC | #2
See inlines below, and will sent a v2 early next week.

On 5 Sep 2019, at 14:40, Ilya Maximets wrote:

> Hi Eelco,
> Thanks for the patch! Looks reasonable.
>
> One comment is that it's better to explicitly initialize the
> flag in common_construct.  I see that we doesn't initialize
> 'started' flag, but this might be fixed too.
>
> More comments inline.
>
> Best regards, Ilya Maximets.
>
> On 05.09.2019 14:48, Eelco Chaudron wrote:
>> Currently, OVS does not register and therefore not handle the
>> interface reset event from the DPDK framework. This would cause a
>> problem in cases where a VF is used as an interface, and its
>> configuration changes.
>>
>> As an example in the following scenario the MAC change is not
>> detected/acted upon until OVS is restarted without the patch applied:
>>
>> $ echo 1 > /sys/bus/pci/devices/0000:05:00.1/sriov_numvfs
>> $ ovs-vsctl add-port ovs_pvp_br0 dpdk0 -- \
>>           set Interface dpdk0 type=dpdk -- \
>>           set Interface dpdk0 options:dpdk-devargs=0000:05:0a.0
>>
>> $ ip link set p5p2 vf 0 mac 52:54:00:92:d3:33
>>
>> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
>> ---
>>  lib/netdev-dpdk.c | 49 
>> +++++++++++++++++++++++++++++++++++++++++++++--
>>  1 file changed, 47 insertions(+), 2 deletions(-)
>>
>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>> index bc20d6843..a23150387 100644
>> --- a/lib/netdev-dpdk.c
>> +++ b/lib/netdev-dpdk.c
>> @@ -362,6 +362,7 @@ struct netdev_dpdk {
>>          bool attached;
>>          /* If true, rte_eth_dev_start() was successfully called */
>>          bool started;
>> +        bool reset_needed;
>
> This will produce a hole in the structure as members will no longer
> fit into a single cacheline.  And cacheline markers will be 
> misaligned.
>
> See details for similar issue here:
> https://mail.openvswitch.org/pipermail/ovs-dev/2019-September/362349.html
>
> Possible solution is to turn existing flags into bit fields or stop
> avoiding the problem and drop most of padding in the structure.

Will change this…

>>          struct eth_addr hwaddr;
>>          int mtu;
>>          int socket_id;
>> @@ -1735,6 +1736,34 @@ netdev_dpdk_process_devargs(struct netdev_dpdk 
>> *dev,
>>      return new_port_id;
>>  }
>>
>> +static int
>> +dpdk_eth_event_callback(uint16_t port_id, enum rte_eth_event_type 
>> type,
>
> s/uint16_t/dpdk_port_t/
>
>> +                        void *param OVS_UNUSED, void *ret_param 
>> OVS_UNUSED)
>> +{
>> +    struct netdev_dpdk *dev;
>> +
>> +    switch ((int)type) {
>
> Do we need this cast?

In my environment this flag is enabled, not sure if I should change this 
some where else…

lib/netdev-dpdk.c:1747:5: error: enumeration value 
'RTE_ETH_EVENT_UNKNOWN' not handled in switch [-Werror=switch-enum]
      switch (type) {

>> +
>
> Empty line not needed.
>
ack
>> +    case RTE_ETH_EVENT_INTR_RESET:
>> +
>
> Empty line not needed.
>
ack
>> +        ovs_mutex_lock(&dpdk_mutex);
>> +        dev = netdev_dpdk_lookup_by_port_id(port_id);
>> +        if (dev) {
>> +            ovs_mutex_lock(&dev->mutex);
>> +            dev->reset_needed = true;
>> +            netdev_request_reconfigure(&dev->up);> +            
>> ovs_mutex_unlock(&dev->mutex);
>
> Maybe it'll be good to add at leas debug logs here?
Will ad a log message
>> +        }
>> +        ovs_mutex_unlock(&dpdk_mutex);
>> +        break;
>> +
>> +    default:
>> +        /* Ignore all other types */
>> +        break;
>> +   }
>> +   return 0;
>> +}
>> +
>>  static void
>>  dpdk_set_rxq_config(struct netdev_dpdk *dev, const struct smap 
>> *args)
>>      OVS_REQUIRES(dev->mutex)
>> @@ -3777,6 +3806,8 @@ netdev_dpdk_class_init(void)
>>      /* This function can be called for different classes.  The 
>> initialization
>>       * needs to be done only once */
>>      if (ovsthread_once_start(&once)) {
>> +        int ret;
>> +
>>          ovs_thread_create("dpdk_watchdog", dpdk_watchdog, NULL);
>>          unixctl_command_register("netdev-dpdk/set-admin-state",
>>                                   "[netdev] up|down", 1, 2,
>> @@ -3790,6 +3821,14 @@ netdev_dpdk_class_init(void)
>>                                   "[netdev]", 0, 1,
>>                                   netdev_dpdk_get_mempool_info, 
>> NULL);
>>
>> +        ret = rte_eth_dev_callback_register(RTE_ETH_ALL,
>> +                                            
>> RTE_ETH_EVENT_INTR_RESET,
>> +                                            dpdk_eth_event_callback, 
>> NULL);
>> +
>> +        if (ret != 0) {
>> +            VLOG_ERR("Ethernet device callback register error %d", 
>> ret);
>
> It's better to print rte_strerror(-ret).
ack
>> +        }
>> +
>>          ovsthread_once_done(&once);
>>      }
>>
>> @@ -4150,13 +4189,19 @@ netdev_dpdk_reconfigure(struct netdev 
>> *netdev)
>>          && dev->rxq_size == dev->requested_rxq_size
>>          && dev->txq_size == dev->requested_txq_size
>>          && dev->socket_id == dev->requested_socket_id
>> -        && dev->started) {
>> +        && dev->started && !dev->reset_needed) {
>>          /* Reconfiguration is unnecessary */
>>
>>          goto out;
>>      }
>>
>> -    rte_eth_dev_stop(dev->port_id);
>> +    if (dev->reset_needed) {
>> +        rte_eth_dev_reset(dev->port_id);
>> +        dev->reset_needed = false;
>> +    } else {
>> +        rte_eth_dev_stop(dev->port_id);
>> +    }
>> +
>>      dev->started = false;
>>
>>      err = netdev_dpdk_mempool_configure(dev);
>>
Ilya Maximets Sept. 6, 2019, 11:51 a.m. UTC | #3
On 05.09.2019 17:44, Eelco Chaudron wrote:
> See inlines below, and will sent a v2 early next week.
> 
> On 5 Sep 2019, at 14:40, Ilya Maximets wrote:
> 
>> Hi Eelco,
>> Thanks for the patch! Looks reasonable.
>>
>> One comment is that it's better to explicitly initialize the
>> flag in common_construct.  I see that we doesn't initialize
>> 'started' flag, but this might be fixed too.
>>
>> More comments inline.
>>
>> Best regards, Ilya Maximets.
>>
>> On 05.09.2019 14:48, Eelco Chaudron wrote:
>>> Currently, OVS does not register and therefore not handle the
>>> interface reset event from the DPDK framework. This would cause a
>>> problem in cases where a VF is used as an interface, and its
>>> configuration changes.
>>>
>>> As an example in the following scenario the MAC change is not
>>> detected/acted upon until OVS is restarted without the patch applied:
>>>
>>> $ echo 1 > /sys/bus/pci/devices/0000:05:00.1/sriov_numvfs
>>> $ ovs-vsctl add-port ovs_pvp_br0 dpdk0 -- \
>>>           set Interface dpdk0 type=dpdk -- \
>>>           set Interface dpdk0 options:dpdk-devargs=0000:05:0a.0
>>>
>>> $ ip link set p5p2 vf 0 mac 52:54:00:92:d3:33
>>>
>>> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
>>> ---
>>>  lib/netdev-dpdk.c | 49 +++++++++++++++++++++++++++++++++++++++++++++--
>>>  1 file changed, 47 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>>> index bc20d6843..a23150387 100644
>>> --- a/lib/netdev-dpdk.c
>>> +++ b/lib/netdev-dpdk.c
>>> @@ -362,6 +362,7 @@ struct netdev_dpdk {
>>>          bool attached;
>>>          /* If true, rte_eth_dev_start() was successfully called */
>>>          bool started;
>>> +        bool reset_needed;
>>
>> This will produce a hole in the structure as members will no longer
>> fit into a single cacheline.  And cacheline markers will be misaligned.
>>
>> See details for similar issue here:
>> https://mail.openvswitch.org/pipermail/ovs-dev/2019-September/362349.html
>>
>> Possible solution is to turn existing flags into bit fields or stop
>> avoiding the problem and drop most of padding in the structure.
> 
> Will change this…
> 
>>>          struct eth_addr hwaddr;
>>>          int mtu;
>>>          int socket_id;
>>> @@ -1735,6 +1736,34 @@ netdev_dpdk_process_devargs(struct netdev_dpdk *dev,
>>>      return new_port_id;
>>>  }
>>>
>>> +static int
>>> +dpdk_eth_event_callback(uint16_t port_id, enum rte_eth_event_type type,
>>
>> s/uint16_t/dpdk_port_t/
>>
>>> +                        void *param OVS_UNUSED, void *ret_param OVS_UNUSED)
>>> +{
>>> +    struct netdev_dpdk *dev;
>>> +
>>> +    switch ((int)type) {
>>
>> Do we need this cast?
> 
> In my environment this flag is enabled, not sure if I should change this some where else…
> 
> lib/netdev-dpdk.c:1747:5: error: enumeration value 'RTE_ETH_EVENT_UNKNOWN' not handled in switch [-Werror=switch-enum]
>      switch (type) {
> 

OK. I see.
Please, add a space between cast and variable: 'switch ((int) type) {'.

>>> +
>>
>> Empty line not needed.
>>
> ack
>>> +    case RTE_ETH_EVENT_INTR_RESET:
>>> +
>>
>> Empty line not needed.
>>
> ack
>>> +        ovs_mutex_lock(&dpdk_mutex);
>>> +        dev = netdev_dpdk_lookup_by_port_id(port_id);
>>> +        if (dev) {
>>> +            ovs_mutex_lock(&dev->mutex);
>>> +            dev->reset_needed = true;
>>> +            netdev_request_reconfigure(&dev->up);> +            ovs_mutex_unlock(&dev->mutex);
>>
>> Maybe it'll be good to add at leas debug logs here?
> Will ad a log message
>>> +        }
>>> +        ovs_mutex_unlock(&dpdk_mutex);
>>> +        break;
>>> +
>>> +    default:
>>> +        /* Ignore all other types */
>>> +        break;
>>> +   }
>>> +   return 0;
>>> +}
>>> +
>>>  static void
>>>  dpdk_set_rxq_config(struct netdev_dpdk *dev, const struct smap *args)
>>>      OVS_REQUIRES(dev->mutex)
>>> @@ -3777,6 +3806,8 @@ netdev_dpdk_class_init(void)
>>>      /* This function can be called for different classes.  The initialization
>>>       * needs to be done only once */
>>>      if (ovsthread_once_start(&once)) {
>>> +        int ret;
>>> +
>>>          ovs_thread_create("dpdk_watchdog", dpdk_watchdog, NULL);
>>>          unixctl_command_register("netdev-dpdk/set-admin-state",
>>>                                   "[netdev] up|down", 1, 2,
>>> @@ -3790,6 +3821,14 @@ netdev_dpdk_class_init(void)
>>>                                   "[netdev]", 0, 1,
>>>                                   netdev_dpdk_get_mempool_info, NULL);
>>>
>>> +        ret = rte_eth_dev_callback_register(RTE_ETH_ALL,
>>> +                                            RTE_ETH_EVENT_INTR_RESET,
>>> +                                            dpdk_eth_event_callback, NULL);
>>> +
>>> +        if (ret != 0) {
>>> +            VLOG_ERR("Ethernet device callback register error %d", ret);
>>
>> It's better to print rte_strerror(-ret).
> ack
>>> +        }
>>> +
>>>          ovsthread_once_done(&once);
>>>      }
>>>
>>> @@ -4150,13 +4189,19 @@ netdev_dpdk_reconfigure(struct netdev *netdev)
>>>          && dev->rxq_size == dev->requested_rxq_size
>>>          && dev->txq_size == dev->requested_txq_size
>>>          && dev->socket_id == dev->requested_socket_id
>>> -        && dev->started) {
>>> +        && dev->started && !dev->reset_needed) {
>>>          /* Reconfiguration is unnecessary */
>>>
>>>          goto out;
>>>      }
>>>
>>> -    rte_eth_dev_stop(dev->port_id);
>>> +    if (dev->reset_needed) {
>>> +        rte_eth_dev_reset(dev->port_id);
>>> +        dev->reset_needed = false;
>>> +    } else {
>>> +        rte_eth_dev_stop(dev->port_id);
>>> +    }
>>> +
>>>      dev->started = false;
>>>
>>>      err = netdev_dpdk_mempool_configure(dev);
>>>
> 
>
Eelco Chaudron Sept. 10, 2019, 11:11 a.m. UTC | #4
On 5 Sep 2019, at 14:40, Ilya Maximets wrote:

> Hi Eelco,

<SNIP>
>> , 2 deletions(-)
>>
>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>> index bc20d6843..a23150387 100644
>> --- a/lib/netdev-dpdk.c
>> +++ b/lib/netdev-dpdk.c
>> @@ -362,6 +362,7 @@ struct netdev_dpdk {
>>          bool attached;
>>          /* If true, rte_eth_dev_start() was successfully called */
>>          bool started;
>> +        bool reset_needed;
>
> This will produce a hole in the structure as members will no longer
> fit into a single cacheline.  And cacheline markers will be 
> misaligned.
>
> See details for similar issue here:
> https://mail.openvswitch.org/pipermail/ovs-dev/2019-September/362349.html
>
> Possible solution is to turn existing flags into bit fields or stop
> avoiding the problem and drop most of padding in the structure.
>
>
>>          struct eth_addr hwaddr;
>>          int mtu;
>>          int socket_id;

I did a pahole check, as my counting is always off, and it look ok, even 
with the change:

struct netdev_dpdk {
	union {
		OVS_CACHE_LINE_MARKER cacheline0;        /*     0     1 */
		struct {
			dpdk_port_t port_id;             /*     0     2 */
			_Bool      attached;             /*     2     1 */
			_Bool      started;              /*     3     1 */
			_Bool      reset_needed;         /*     4     1 */

			/* XXX 1 byte hole, try to pack */

			struct eth_addr hwaddr;          /*     6     6 */
			int        mtu;                  /*    12     4 */
			int        socket_id;            /*    16     4 */
			int        buf_size;             /*    20     4 */
			int        max_packet_len;       /*    24     4 */
			enum dpdk_dev_type type;         /*    28     4 */
			enum netdev_flags flags;         /*    32     4 */
			int        link_reset_cnt;       /*    36     4 */
			union {
				char * devargs;          /*    40     8 */
				char * vhost_id;         /*    40     8 */
			};                               /*    40     8 */
			struct dpdk_tx_queue * tx_q;     /*    48     8 */
			struct rte_eth_link link;        /*    56     8 */

			/* XXX last struct has 2 bytes of padding */
		};                                       /*     0    64 */
		uint8_t            pad49[64];            /*     0    64 */
	};


<SNIP>
Ilya Maximets Sept. 10, 2019, 11:42 a.m. UTC | #5
On 10.09.2019 14:11, Eelco Chaudron wrote:
> 
> 
> On 5 Sep 2019, at 14:40, Ilya Maximets wrote:
> 
>> Hi Eelco,
> 
> <SNIP>
>>> , 2 deletions(-)
>>>
>>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>>> index bc20d6843..a23150387 100644
>>> --- a/lib/netdev-dpdk.c
>>> +++ b/lib/netdev-dpdk.c
>>> @@ -362,6 +362,7 @@ struct netdev_dpdk {
>>>          bool attached;
>>>          /* If true, rte_eth_dev_start() was successfully called */
>>>          bool started;
>>> +        bool reset_needed;
>>
>> This will produce a hole in the structure as members will no longer
>> fit into a single cacheline.  And cacheline markers will be misaligned.
>>
>> See details for similar issue here:
>> https://protect2.fireeye.com/url?k=66886626-3bef2200-6689ed69-0cc47a31384a-d13032efc289ec6c&u=https://mail.openvswitch.org/pipermail/ovs-dev/2019-September/362349.html
>>
>> Possible solution is to turn existing flags into bit fields or stop
>> avoiding the problem and drop most of padding in the structure.
>>
>>
>>>          struct eth_addr hwaddr;
>>>          int mtu;
>>>          int socket_id;
> 
> I did a pahole check, as my counting is always off, and it look ok, even with the change:
> 
> struct netdev_dpdk {
>     union {
>         OVS_CACHE_LINE_MARKER cacheline0;        /*     0     1 */
>         struct {
>             dpdk_port_t port_id;             /*     0     2 */
>             _Bool      attached;             /*     2     1 */
>             _Bool      started;              /*     3     1 */
>             _Bool      reset_needed;         /*     4     1 */
> 
>             /* XXX 1 byte hole, try to pack */
> 
>             struct eth_addr hwaddr;          /*     6     6 */
>             int        mtu;                  /*    12     4 */

OK. I see. There was a 2 byte hole after 'hwaddr' and with your patch
it moved earlier moving the 'hwaddr' from byte 4 to byte 6.
Maybe you could add a /* 1 pad byte here. */ after the new flag?

Best regards, Ilya Maximets.
diff mbox series

Patch

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index bc20d6843..a23150387 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -362,6 +362,7 @@  struct netdev_dpdk {
         bool attached;
         /* If true, rte_eth_dev_start() was successfully called */
         bool started;
+        bool reset_needed;
         struct eth_addr hwaddr;
         int mtu;
         int socket_id;
@@ -1735,6 +1736,34 @@  netdev_dpdk_process_devargs(struct netdev_dpdk *dev,
     return new_port_id;
 }
 
+static int
+dpdk_eth_event_callback(uint16_t port_id, enum rte_eth_event_type type,
+                        void *param OVS_UNUSED, void *ret_param OVS_UNUSED)
+{
+    struct netdev_dpdk *dev;
+
+    switch ((int)type) {
+
+    case RTE_ETH_EVENT_INTR_RESET:
+
+        ovs_mutex_lock(&dpdk_mutex);
+        dev = netdev_dpdk_lookup_by_port_id(port_id);
+        if (dev) {
+            ovs_mutex_lock(&dev->mutex);
+            dev->reset_needed = true;
+            netdev_request_reconfigure(&dev->up);
+            ovs_mutex_unlock(&dev->mutex);
+        }
+        ovs_mutex_unlock(&dpdk_mutex);
+        break;
+
+    default:
+        /* Ignore all other types */
+        break;
+   }
+   return 0;
+}
+
 static void
 dpdk_set_rxq_config(struct netdev_dpdk *dev, const struct smap *args)
     OVS_REQUIRES(dev->mutex)
@@ -3777,6 +3806,8 @@  netdev_dpdk_class_init(void)
     /* This function can be called for different classes.  The initialization
      * needs to be done only once */
     if (ovsthread_once_start(&once)) {
+        int ret;
+
         ovs_thread_create("dpdk_watchdog", dpdk_watchdog, NULL);
         unixctl_command_register("netdev-dpdk/set-admin-state",
                                  "[netdev] up|down", 1, 2,
@@ -3790,6 +3821,14 @@  netdev_dpdk_class_init(void)
                                  "[netdev]", 0, 1,
                                  netdev_dpdk_get_mempool_info, NULL);
 
+        ret = rte_eth_dev_callback_register(RTE_ETH_ALL,
+                                            RTE_ETH_EVENT_INTR_RESET,
+                                            dpdk_eth_event_callback, NULL);
+
+        if (ret != 0) {
+            VLOG_ERR("Ethernet device callback register error %d", ret);
+        }
+
         ovsthread_once_done(&once);
     }
 
@@ -4150,13 +4189,19 @@  netdev_dpdk_reconfigure(struct netdev *netdev)
         && dev->rxq_size == dev->requested_rxq_size
         && dev->txq_size == dev->requested_txq_size
         && dev->socket_id == dev->requested_socket_id
-        && dev->started) {
+        && dev->started && !dev->reset_needed) {
         /* Reconfiguration is unnecessary */
 
         goto out;
     }
 
-    rte_eth_dev_stop(dev->port_id);
+    if (dev->reset_needed) {
+        rte_eth_dev_reset(dev->port_id);
+        dev->reset_needed = false;
+    } else {
+        rte_eth_dev_stop(dev->port_id);
+    }
+
     dev->started = false;
 
     err = netdev_dpdk_mempool_configure(dev);