Skip to content

Commit baded9b

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Fix ingress bandwidth limit in the openvswitch agent" into stable/yoga
2 parents 4128af6 + 8c80660 commit baded9b

File tree

4 files changed

+99
-20
lines changed

4 files changed

+99
-20
lines changed

neutron/agent/common/ovs_lib.py

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,17 @@ def get_vif_port_by_id(self, port_id):
724724
LOG.info("Port %(port_id)s not present in bridge %(br_name)s",
725725
{'port_id': port_id, 'br_name': self.br_name})
726726

727+
def get_bridge_patch_ports_ofports(self):
728+
ports = self.ovsdb.db_find(
729+
'Interface', ('type', '=', 'patch'),
730+
columns=['name', 'ofport']).execute()
731+
patch_ports = []
732+
for port in ports:
733+
if self.br_name != self.get_bridge_for_iface(port['name']):
734+
continue
735+
patch_ports.append(port['ofport'])
736+
return patch_ports
737+
727738
def delete_ports(self, all_ports=False):
728739
if all_ports:
729740
port_names = self.get_port_name_list()
@@ -800,6 +811,23 @@ def _update_bw_limit_profile_dpdk(self, txn, port_name, qos_uuid,
800811
other_config=other_config))
801812
return qos_uuid
802813

814+
def set_queue_for_ingress_bandwidth_limit(self):
815+
# reg3 is used to memoize if queue was set or not. If it is first visit
816+
# to table 0 for a packet (i.e. reg3 == 0), set queue and memoize (i.e.
817+
# load 1 to reg3), then goto table 0 again. The packet will be handled
818+
# as usual when the second visit to table 0.
819+
# For min bw reg4 is used for the same purpose. In case if there we
820+
# would need one of those registries for something else in the future
821+
# we can try to use same reg4 for both OF rules, this one and the one
822+
# which sets pkt_mark for minimum bandwidth and play with bitmask
823+
self.add_flow(
824+
table=constants.LOCAL_SWITCHING,
825+
reg3=0,
826+
priority=200,
827+
actions=("set_queue:%s,load:1->NXM_NX_REG3[0],"
828+
"resubmit(,%s)" % (QOS_DEFAULT_QUEUE,
829+
constants.LOCAL_SWITCHING)))
830+
803831
def _update_ingress_bw_limit_for_port(
804832
self, port_name, max_kbps, max_burst_kbps):
805833
queue_id = self._update_queue(
@@ -926,7 +954,7 @@ def get_egress_min_bw_for_port(self, port_id):
926954
min_bps = queue['other_config'].get('min-rate')
927955
return int(int(min_bps) / 1000) if min_bps else None
928956

929-
def _set_queue_for_minimum_bandwidth(self, queue_num):
957+
def _set_pkt_mark_for_minimum_bandwidth(self, queue_num):
930958
# reg4 is used to memoize if queue was set or not. If it is first visit
931959
# to table 0 for a packet (i.e. reg4 == 0), set queue and memoize (i.e.
932960
# load 1 to reg4), then goto table 0 again. The packet will be handled
@@ -936,10 +964,26 @@ def _set_queue_for_minimum_bandwidth(self, queue_num):
936964
in_port=queue_num,
937965
reg4=0,
938966
priority=200,
939-
actions=("set_queue:%s,load:1->NXM_NX_REG4[0],"
967+
actions=("set_field:%s->pkt_mark,load:1->NXM_NX_REG4[0],"
940968
"resubmit(,%s)" % (queue_num, constants.LOCAL_SWITCHING)))
941969

942-
def _unset_queue_for_minimum_bandwidth(self, queue_num):
970+
def set_queue_for_minimum_bandwidth(self, queue_num):
971+
# reg4 is used to memoize if queue was set or not. If it is first visit
972+
# to table 0 for a packet (i.e. reg4 == 0), set queue and memoize (i.e.
973+
# load 1 to reg4), then goto table 0 again. The packet will be handled
974+
# as usual when the second visit to table 0.
975+
patch_ports = self.get_bridge_patch_ports_ofports()
976+
for patch_port in patch_ports:
977+
self.add_flow(
978+
table=0,
979+
in_port=patch_port,
980+
pkt_mark=queue_num,
981+
reg4=0,
982+
priority=200,
983+
actions=("set_queue:%s,load:1->NXM_NX_REG4[0],"
984+
"resubmit(,0)" % queue_num))
985+
986+
def _unset_pkt_mark_for_minimum_bandwidth(self, queue_num):
943987
self.delete_flows(
944988
table=constants.LOCAL_SWITCHING,
945989
in_port=queue_num,
@@ -964,26 +1008,31 @@ def update_minimum_bandwidth_queue(self, port_id, egress_port_names,
9641008
qos_id=qos_id, queues=qos_queues)
9651009
for egress_port_name in egress_port_names:
9661010
self._set_port_qos(egress_port_name, qos_id=qos_id)
967-
self._set_queue_for_minimum_bandwidth(queue_num)
1011+
self._set_pkt_mark_for_minimum_bandwidth(queue_num)
9681012
return qos_id
9691013

9701014
def delete_minimum_bandwidth_queue(self, port_id):
9711015
queue = self._find_queue(port_id)
9721016
if not queue:
9731017
return
9741018
queue_num = int(queue['external_ids']['queue-num'])
975-
self._unset_queue_for_minimum_bandwidth(queue_num)
1019+
self._unset_pkt_mark_for_minimum_bandwidth(queue_num)
9761020
qos_id, qos_queues = self._find_qos(
9771021
self._min_bw_qos_id,
9781022
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)
9791023
if not qos_queues:
9801024
return
9811025
if queue_num in qos_queues.keys():
9821026
qos_queues.pop(queue_num)
983-
self._update_qos(
984-
self._min_bw_qos_id,
985-
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH,
986-
qos_id=qos_id, queues=qos_queues)
1027+
if qos_queues:
1028+
self._update_qos(
1029+
self._min_bw_qos_id,
1030+
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH,
1031+
qos_id=qos_id, queues=qos_queues)
1032+
self.ovsdb.db_clear('Port', port_id, 'qos').execute(
1033+
check_error=False)
1034+
if not qos_queues:
1035+
self._delete_qos(qos_id)
9871036
self._delete_queue(
9881037
queue['_uuid'], qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)
9891038

neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def _qos_bandwidth_initialize(self):
5050
be down until the QoS rules are rebuilt.
5151
"""
5252
self.br_int.clear_bandwidth_qos()
53+
self.br_int.set_queue_for_ingress_bandwidth_limit()
5354

5455
def initialize(self):
5556
self.br_int = self.agent_api.request_int_br()
@@ -188,6 +189,8 @@ def update_minimum_bandwidth(self, port, rule):
188189
egress_port_names.extend(ports)
189190
qos_id = self.br_int.update_minimum_bandwidth_queue(
190191
port['port_id'], egress_port_names, vif_port.ofport, rule.min_kbps)
192+
for phy_br in self.agent_api.request_phy_brs():
193+
phy_br.set_queue_for_minimum_bandwidth(vif_port.ofport)
191194
LOG.debug('Minimum bandwidth egress rule was updated/created for port '
192195
'%(port_id)s and rule %(rule_id)s. QoS ID: %(qos_id)s. '
193196
'Egress ports with QoS applied: %(ports)s',

neutron/tests/fullstack/test_qos.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@
2828
from neutron.tests.fullstack.resources import machine
2929
from neutron.tests.unit import testlib_api
3030

31+
from neutron.agent.common import ovs_lib
3132
from neutron.conf.plugins.ml2.drivers import linuxbridge as \
3233
linuxbridge_agent_config
3334
from neutron.plugins.ml2.drivers.linuxbridge.agent import \
3435
linuxbridge_neutron_agent as linuxbridge_agent
36+
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
37+
import constants as ovs_constants
3538
from neutron.services.qos.drivers.linuxbridge import driver as lb_drv
3639
from neutron.services.qos.drivers.openvswitch import driver as ovs_drv
3740

@@ -365,6 +368,16 @@ def _wait_for_bw_rule_applied(self, vm, limit, burst, direction):
365368
lambda: vm.bridge.get_ingress_bw_limit_for_port(
366369
vm.port.name) == (limit, burst),
367370
timeout=10)
371+
br_int_flows = vm.bridge.dump_flows_for_table(
372+
ovs_constants.LOCAL_SWITCHING)
373+
expected = (
374+
'priority=200,reg3=0 '
375+
'actions=set_queue:%(queue_num)s,'
376+
'load:0x1->NXM_NX_REG3[0],resubmit(,0)' % {
377+
'queue_num': ovs_lib.QOS_DEFAULT_QUEUE
378+
}
379+
)
380+
self.assertIn(expected, br_int_flows)
368381

369382
def test_bw_limit_qos_port_removed(self):
370383
"""Test if rate limit config is properly removed when whole port is

neutron/tests/functional/agent/common/test_ovs_lib.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,19 @@ def test_egress_bw_limit(self):
382382
self.assertIsNone(max_rate)
383383
self.assertIsNone(burst)
384384

385+
def test_set_pkt_mark_for_ingress_bandwidth_limit(self):
386+
self._create_bridge()
387+
self.ovs.set_queue_for_ingress_bandwidth_limit()
388+
flows = self.ovs.dump_flows_for_table(ovs_constants.LOCAL_SWITCHING)
389+
expected = (
390+
'priority=200,reg3=0 '
391+
'actions=set_queue:%(queue_num)s,'
392+
'load:0x1->NXM_NX_REG3[0],resubmit(,0)' % {
393+
'queue_num': ovs_lib.QOS_DEFAULT_QUEUE
394+
}
395+
)
396+
self.assertIn(expected, flows)
397+
385398
def test_ingress_bw_limit(self):
386399
port_name = ('port-' + uuidutils.generate_uuid())[:8]
387400
self._create_bridge()
@@ -438,18 +451,22 @@ def test_ingress_bw_limit_dpdk_port(self):
438451
qos = self._list_qos(qos_id)
439452
self.assertEqual(0, len(qos['queues']))
440453

441-
def test__set_queue_for_minimum_bandwidth(self):
454+
def test__set_pkt_mark_for_minimum_bandwidth(self):
442455
self._create_bridge()
443-
self.ovs._set_queue_for_minimum_bandwidth(1234)
456+
self.ovs._set_pkt_mark_for_minimum_bandwidth(1234)
444457
flows = self.ovs.dump_flows_for_table(ovs_constants.LOCAL_SWITCHING)
445-
expected = 'priority=200,reg4=0,in_port=1234 actions=set_queue:1234,' \
446-
'load:0x1->NXM_NX_REG4[0],resubmit(,0)'
458+
# NOTE(slaweq) 1234 in dec is 0x4d2,
459+
# action set_field:1234->pkt mark is shown in the OF output as
460+
# load:0x4d2->NXM_NX_PKT_MARK[]
461+
expected = ('priority=200,reg4=0,in_port=1234 '
462+
'actions=load:0x4d2->NXM_NX_PKT_MARK[],'
463+
'load:0x1->NXM_NX_REG4[0],resubmit(,0)')
447464
self.assertIn(expected, flows)
448465

449-
def test__unset_queue_for_minimum_bandwidth(self):
450-
self.test__set_queue_for_minimum_bandwidth()
466+
def test__unset_pkt_mark_for_minimum_bandwidth(self):
467+
self.test__set_pkt_mark_for_minimum_bandwidth()
451468

452-
self.ovs._unset_queue_for_minimum_bandwidth(1234)
469+
self.ovs._unset_pkt_mark_for_minimum_bandwidth(1234)
453470
flows = self.ovs.dump_flows_for_table(ovs_constants.LOCAL_SWITCHING)
454471
expected = 'in_port=1234'
455472
self.assertNotIn(expected, flows)
@@ -521,10 +538,7 @@ def test_delete_minimum_bandwidth_queue(self):
521538
self.assertEqual(queue_id_1, qos['queues'][1].uuid)
522539

523540
self.ovs.delete_minimum_bandwidth_queue(neutron_port_id_1)
524-
self._check_value({'_uuid': qos_id}, self._list_qos, qos_id,
525-
keys_to_check=['_uuid'])
526-
qos = self._list_qos(qos_id)
527-
self.assertEqual(0, len(qos['queues']))
541+
self.assertIsNone(self._list_qos(qos_id))
528542

529543
def test_delete_minimum_bandwidth_queue_no_qos_found(self):
530544
queue_id, neutron_port_id = self._create_queue(queue_num=1)

0 commit comments

Comments
 (0)