Workaround for TCP checksum issue with ovs-dpdk and veth pair
The need for this change stems from following issues:
1) When ovs_use_veth = False with ovs-dpdk issue with ovs
was observed - after vswitch restart interface is not comming up.
Meaning ovs-dpdk uses ovs internal ports and it is not able to bring
them up on restart.
2) When ovs_use_veth = True and ovs-dpkd is used, packets sent with
incorrect checksum due to the fact that ovs-dpdk does not do checksum
calculations for veth interface.
This commit allows to use second option and resolve checksum issue by
disabling checksum offload.
Closes-Bug: #1832021
Related-Bug: #1831935
Change-Id: Iecce8d2c6c2c46718cc1020c6e8f914cd4560e4b
(cherry picked from commit 11838a2bc5
)
This commit is contained in:
parent
36fc4708be
commit
e87f18d2fc
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
# dhcp-agent
|
# dhcp-agent
|
||||||
dnsmasq: CommandFilter, dnsmasq, root
|
dnsmasq: CommandFilter, dnsmasq, root
|
||||||
|
ethtool: CommandFilter, ethtool, root
|
||||||
# dhcp-agent uses kill as well, that's handled by the generic KillFilter
|
# dhcp-agent uses kill as well, that's handled by the generic KillFilter
|
||||||
# it looks like these are the only signals needed, per
|
# it looks like these are the only signals needed, per
|
||||||
# neutron/agent/linux/dhcp.py
|
# neutron/agent/linux/dhcp.py
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
arping: CommandFilter, arping, root
|
arping: CommandFilter, arping, root
|
||||||
|
|
||||||
# l3_agent
|
# l3_agent
|
||||||
|
ethtool: CommandFilter, ethtool, root
|
||||||
sysctl: CommandFilter, sysctl, root
|
sysctl: CommandFilter, sysctl, root
|
||||||
route: CommandFilter, route, root
|
route: CommandFilter, route, root
|
||||||
radvd: CommandFilter, radvd, root
|
radvd: CommandFilter, radvd, root
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright 2020 OpenStack Foundation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from neutron.agent.linux import ip_lib
|
||||||
|
|
||||||
|
|
||||||
|
class Ethtool(object):
|
||||||
|
|
||||||
|
COMMAND = 'ethtool'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _cmd(cmd, namespace, **kwargs):
|
||||||
|
ip_wrapper = ip_lib.IPWrapper(namespace)
|
||||||
|
return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def offload(cls, device, rx, tx, namespace=None):
|
||||||
|
rx = 'on' if rx else 'off'
|
||||||
|
tx = 'on' if tx else 'off'
|
||||||
|
cmd = ['--offload', device, 'rx', rx, 'tx', tx]
|
||||||
|
cmd = [cls.COMMAND] + cmd
|
||||||
|
cls._cmd(cmd, namespace)
|
|
@ -22,9 +22,13 @@ from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from neutron.agent.common import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
|
from neutron.agent.linux import ethtool
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
|
from neutron.conf.plugins.ml2.drivers import ovs_conf
|
||||||
|
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
|
||||||
|
import constants as ovs_const
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -354,6 +358,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
super(OVSInterfaceDriver, self).__init__(conf)
|
super(OVSInterfaceDriver, self).__init__(conf)
|
||||||
|
ovs_conf.register_ovs_agent_opts(self.conf)
|
||||||
if self.conf.ovs_use_veth:
|
if self.conf.ovs_use_veth:
|
||||||
self.DEV_NAME_PREFIX = 'ns-'
|
self.DEV_NAME_PREFIX = 'ns-'
|
||||||
|
|
||||||
|
@ -434,6 +439,11 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
||||||
if link_up:
|
if link_up:
|
||||||
ns_dev.link.set_up()
|
ns_dev.link.set_up()
|
||||||
if self.conf.ovs_use_veth:
|
if self.conf.ovs_use_veth:
|
||||||
|
# ovs-dpdk does not do checksum calculations for veth interface
|
||||||
|
# (bug 1832021)
|
||||||
|
if self.conf.OVS.datapath_type == ovs_const.OVS_DATAPATH_NETDEV:
|
||||||
|
ethtool.Ethtool.offload(ns_dev.name, rx=False, tx=False,
|
||||||
|
namespace=namespace)
|
||||||
root_dev.link.set_up()
|
root_dev.link.set_up()
|
||||||
|
|
||||||
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
|
|
|
@ -17,9 +17,13 @@ import mock
|
||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
|
|
||||||
from neutron.agent.common import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
|
from neutron.agent.linux import ethtool
|
||||||
from neutron.agent.linux import interface
|
from neutron.agent.linux import interface
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.conf.agent import common as config
|
from neutron.conf.agent import common as config
|
||||||
|
from neutron.conf.plugins.ml2.drivers import ovs_conf
|
||||||
|
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
|
||||||
|
import constants as ovs_const
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +73,8 @@ class TestBase(base.BaseTestCase):
|
||||||
super(TestBase, self).setUp()
|
super(TestBase, self).setUp()
|
||||||
self.conf = config.setup_conf()
|
self.conf = config.setup_conf()
|
||||||
config.register_interface_opts(self.conf)
|
config.register_interface_opts(self.conf)
|
||||||
|
self.eth_tool_p = mock.patch.object(ethtool, 'Ethtool')
|
||||||
|
self.eth_tool = self.eth_tool_p.start()
|
||||||
self.ip_dev_p = mock.patch.object(ip_lib, 'IPDevice')
|
self.ip_dev_p = mock.patch.object(ip_lib, 'IPDevice')
|
||||||
self.ip_dev = self.ip_dev_p.start()
|
self.ip_dev = self.ip_dev_p.start()
|
||||||
self.ip_p = mock.patch.object(ip_lib, 'IPWrapper')
|
self.ip_p = mock.patch.object(ip_lib, 'IPWrapper')
|
||||||
|
@ -482,7 +488,12 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestOVSInterfaceDriverWithVeth, self).setUp()
|
super(TestOVSInterfaceDriverWithVeth, self).setUp()
|
||||||
|
ovs_conf.register_ovs_agent_opts(self.conf)
|
||||||
self.conf.set_override('ovs_use_veth', True)
|
self.conf.set_override('ovs_use_veth', True)
|
||||||
|
self.conf.set_override(
|
||||||
|
'datapath_type',
|
||||||
|
ovs_const.OVS_DATAPATH_NETDEV,
|
||||||
|
group='OVS')
|
||||||
|
|
||||||
def test_get_device_name(self):
|
def test_get_device_name(self):
|
||||||
br = interface.OVSInterfaceDriver(self.conf)
|
br = interface.OVSInterfaceDriver(self.conf)
|
||||||
|
@ -513,6 +524,7 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver):
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
interface, '_get_veth',
|
interface, '_get_veth',
|
||||||
return_value=(root_dev, ns_dev)).start()
|
return_value=(root_dev, ns_dev)).start()
|
||||||
|
ns_dev.name = devname
|
||||||
|
|
||||||
expected = [mock.call(),
|
expected = [mock.call(),
|
||||||
mock.call().add_veth('tap0', devname,
|
mock.call().add_veth('tap0', devname,
|
||||||
|
@ -543,6 +555,9 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver):
|
||||||
self.ip.assert_has_calls(expected)
|
self.ip.assert_has_calls(expected)
|
||||||
root_dev.assert_has_calls([mock.call.link.set_up()])
|
root_dev.assert_has_calls([mock.call.link.set_up()])
|
||||||
ns_dev.assert_has_calls([mock.call.link.set_up()])
|
ns_dev.assert_has_calls([mock.call.link.set_up()])
|
||||||
|
self.eth_tool.assert_has_calls([mock.call.offload(
|
||||||
|
devname, rx=False,
|
||||||
|
tx=False, namespace=namespace)])
|
||||||
|
|
||||||
def test_unplug(self, bridge=None):
|
def test_unplug(self, bridge=None):
|
||||||
if not bridge:
|
if not bridge:
|
||||||
|
|
Loading…
Reference in New Issue