Skip to content

Commit a26b26a

Browse files
authored
Dynamic port configuration - add port buffer cfg to the port ref counter (#2194)
- What I did This PR replace PR #2022 Added increasing/decreasing to the port ref counter each time a port buffer configuration is added or removed Implemented according to the - sonic-net/SONiC#900 - Why I did it In order to avoid cases where a port is removed before the buffer configuration on this port were removed also - How I verified it VS Test was added in order to test it. we remove a port with buffer configuration and the port is not removed. only after all buffer configurations on this port were removed - this port will be removed.
1 parent 486939a commit a26b26a

File tree

2 files changed

+320
-0
lines changed

2 files changed

+320
-0
lines changed

orchagent/bufferorch.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ map<string, string> buffer_to_ref_table_map = {
4444
{buffer_profile_list_field_name, APP_BUFFER_PROFILE_TABLE_NAME}
4545
};
4646

47+
std::map<string, std::map<size_t, string>> pg_port_flags;
48+
std::map<string, std::map<size_t, string>> queue_port_flags;
49+
4750
BufferOrch::BufferOrch(DBConnector *applDb, DBConnector *confDb, DBConnector *stateDb, vector<string> &tableNames) :
4851
Orch(applDb, tableNames),
4952
m_flexCounterDb(new DBConnector("FLEX_COUNTER_DB", 0)),
@@ -829,6 +832,39 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple)
829832
}
830833
}
831834
}
835+
836+
/* when we apply buffer configuration we need to increase the ref counter of this port
837+
* or decrease the ref counter for this port when we remove buffer cfg
838+
* so for each priority cfg in each port we will increase/decrease the ref counter
839+
* also we need to know when the set command is for creating a buffer cfg or modifying buffer cfg -
840+
* we need to increase ref counter only on create flow.
841+
* so we added a map that will help us to know what was the last command for this port and priority -
842+
* if the last command was set command then it is a modify command and we dont need to increase the buffer counter
843+
* all other cases (no last command exist or del command was the last command) it means that we need to increase the ref counter */
844+
if (op == SET_COMMAND)
845+
{
846+
if (queue_port_flags[port_name][ind] != SET_COMMAND)
847+
{
848+
/* if the last operation was not "set" then it's create and not modify - need to increase ref counter */
849+
gPortsOrch->increasePortRefCount(port_name);
850+
}
851+
}
852+
else if (op == DEL_COMMAND)
853+
{
854+
if (queue_port_flags[port_name][ind] == SET_COMMAND)
855+
{
856+
/* we need to decrease ref counter only if the last operation was "SET_COMMAND" */
857+
gPortsOrch->decreasePortRefCount(port_name);
858+
}
859+
}
860+
else
861+
{
862+
SWSS_LOG_ERROR("operation value is not SET or DEL (op = %s)", op.c_str());
863+
return task_process_status::task_invalid_entry;
864+
}
865+
/* save the last command (set or delete) */
866+
queue_port_flags[port_name][ind] = op;
867+
832868
}
833869
}
834870

@@ -976,6 +1012,39 @@ task_process_status BufferOrch::processPriorityGroup(KeyOpFieldsValuesTuple &tup
9761012
}
9771013
}
9781014
}
1015+
1016+
/* when we apply buffer configuration we need to increase the ref counter of this port
1017+
* or decrease the ref counter for this port when we remove buffer cfg
1018+
* so for each priority cfg in each port we will increase/decrease the ref counter
1019+
* also we need to know when the set command is for creating a buffer cfg or modifying buffer cfg -
1020+
* we need to increase ref counter only on create flow.
1021+
* so we added a map that will help us to know what was the last command for this port and priority -
1022+
* if the last command was set command then it is a modify command and we dont need to increase the buffer counter
1023+
* all other cases (no last command exist or del command was the last command) it means that we need to increase the ref counter */
1024+
if (op == SET_COMMAND)
1025+
{
1026+
if (pg_port_flags[port_name][ind] != SET_COMMAND)
1027+
{
1028+
/* if the last operation was not "set" then it's create and not modify - need to increase ref counter */
1029+
gPortsOrch->increasePortRefCount(port_name);
1030+
}
1031+
}
1032+
else if (op == DEL_COMMAND)
1033+
{
1034+
if (pg_port_flags[port_name][ind] == SET_COMMAND)
1035+
{
1036+
/* we need to decrease ref counter only if the last operation was "SET_COMMAND" */
1037+
gPortsOrch->decreasePortRefCount(port_name);
1038+
}
1039+
}
1040+
else
1041+
{
1042+
SWSS_LOG_ERROR("operation value is not SET or DEL (op = %s)", op.c_str());
1043+
return task_process_status::task_invalid_entry;
1044+
}
1045+
/* save the last command (set or delete) */
1046+
pg_port_flags[port_name][ind] = op;
1047+
9791048
}
9801049
}
9811050

tests/test_port_add_remove.py

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import pytest
2+
import time
3+
import buffer_model
4+
from dvslib.dvs_common import PollingConfig
5+
6+
# the port to be removed and add
7+
PORT_A = "Ethernet64"
8+
PORT_B = "Ethernet68"
9+
10+
"""
11+
DELETE_CREATE_ITERATIONS defines the number of iteration of delete and create to ports,
12+
we add different timeouts between delete/create to catch potential race condition that can lead to system crush
13+
14+
Add \ Remove of Buffers can be done only when the model is dynamic.
15+
"""
16+
DELETE_CREATE_ITERATIONS = 10
17+
18+
@pytest.yield_fixture
19+
def dynamic_buffer(dvs):
20+
buffer_model.enable_dynamic_buffer(dvs.get_config_db(), dvs.runcmd)
21+
yield
22+
buffer_model.disable_dynamic_buffer(dvs.get_config_db(), dvs.runcmd)
23+
24+
25+
@pytest.mark.usefixtures('dvs_port_manager')
26+
@pytest.mark.usefixtures("dynamic_buffer")
27+
class TestPortAddRemove(object):
28+
29+
def set_mmu(self,dvs):
30+
state_db = dvs.get_state_db()
31+
# set mmu size
32+
fvs = {"mmu_size": "12766208"}
33+
state_db.create_entry("BUFFER_MAX_PARAM_TABLE", "global", fvs)
34+
35+
36+
def test_remove_add_remove_port_with_buffer_cfg(self, dvs, testlog):
37+
config_db = dvs.get_config_db()
38+
asic_db = dvs.get_asic_db()
39+
state_db = dvs.get_state_db()
40+
app_db = dvs.get_app_db()
41+
42+
# set mmu size
43+
self.set_mmu(dvs)
44+
45+
# Startup interface
46+
dvs.port_admin_set(PORT_A, 'up')
47+
48+
# get port info
49+
port_info = config_db.get_entry("PORT", PORT_A)
50+
51+
# get the number of ports before removal
52+
num_of_ports = len(asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT"))
53+
54+
# remove buffer pg cfg for the port (record the buffer pgs before removing them)
55+
pgs = config_db.get_keys('BUFFER_PG')
56+
buffer_pgs = {}
57+
for key in pgs:
58+
if PORT_A in key:
59+
buffer_pgs[key] = config_db.get_entry('BUFFER_PG', key)
60+
config_db.delete_entry('BUFFER_PG', key)
61+
app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", key)
62+
63+
# modify buffer queue entry to egress_lossless_profile instead of egress_lossy_profile
64+
config_db.update_entry("BUFFER_QUEUE", "%s|0-2"%PORT_A, {"profile": "egress_lossless_profile"})
65+
66+
# remove buffer queue cfg for the port
67+
queues = config_db.get_keys('BUFFER_QUEUE')
68+
buffer_queues = {}
69+
for key in queues:
70+
if PORT_A in key:
71+
buffer_queues[key] = config_db.get_entry('BUFFER_QUEUE', key)
72+
config_db.delete_entry('BUFFER_QUEUE', key)
73+
app_db.wait_for_deleted_entry('BUFFER_QUEUE_TABLE', key)
74+
75+
# Shutdown interface
76+
dvs.port_admin_set(PORT_A, 'down')
77+
78+
# try to remove this port
79+
config_db.delete_entry('PORT', PORT_A)
80+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
81+
num_of_ports-1,
82+
polling_config = PollingConfig(polling_interval = 1, timeout = 5.00, strict = True))
83+
84+
# verify that the port was removed properly since all buffer configuration was removed also
85+
assert len(num) == num_of_ports - 1
86+
87+
# set back the port
88+
config_db.update_entry("PORT", PORT_A, port_info)
89+
90+
# verify that the port has been readded
91+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
92+
num_of_ports,
93+
polling_config = PollingConfig(polling_interval = 1, timeout = 5.00, strict = True))
94+
95+
assert len(num) == num_of_ports
96+
97+
# re-add buffer pg and queue cfg to the port
98+
for key, pg in buffer_pgs.items():
99+
config_db.update_entry("BUFFER_PG", key, pg)
100+
101+
for key, queue in buffer_queues.items():
102+
config_db.update_entry("BUFFER_QUEUE", key, queue)
103+
104+
time.sleep(5)
105+
106+
# Remove the port with buffer configuration
107+
config_db.delete_entry('PORT', PORT_A)
108+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
109+
num_of_ports-1,
110+
polling_config = PollingConfig(polling_interval = 1, timeout = 5.00, strict = False))
111+
112+
# verify that the port wasn't removed since we still have buffer cfg
113+
assert len(num) == num_of_ports
114+
115+
# Remove buffer pgs
116+
for key in buffer_pgs.keys():
117+
config_db.delete_entry('BUFFER_PG', key)
118+
app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", key)
119+
120+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
121+
num_of_ports-1,
122+
polling_config = PollingConfig(polling_interval = 1, timeout = 5.00, strict = False))
123+
124+
# verify that the port wasn't removed since we still have buffer cfg
125+
assert len(num) == num_of_ports
126+
127+
# Remove buffer queue
128+
for key in buffer_queues.keys():
129+
config_db.delete_entry('BUFFER_QUEUE', key)
130+
app_db.wait_for_deleted_entry('BUFFER_QUEUE_TABLE', key)
131+
132+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
133+
num_of_ports-1,
134+
polling_config = PollingConfig(polling_interval = 1, timeout = 5.00, strict = True))
135+
136+
# verify that the port wasn't removed since we still have buffer cfg
137+
assert len(num) == num_of_ports - 1
138+
139+
# set back the port as it is required for next test
140+
config_db.update_entry("PORT", PORT_A, port_info)
141+
142+
143+
144+
@pytest.mark.parametrize("scenario", ["one_port", "all_ports"])
145+
def test_add_remove_all_the_ports(self, dvs, testlog, scenario):
146+
config_db = dvs.get_config_db()
147+
state_db = dvs.get_state_db()
148+
asic_db = dvs.get_asic_db()
149+
app_db = dvs.get_app_db()
150+
151+
# set mmu size
152+
self.set_mmu(dvs)
153+
154+
# get the number of ports before removal
155+
num_of_ports = len(asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT"))
156+
157+
# remove buffer pg cfg for the port
158+
if scenario == "all_ports":
159+
ports = config_db.get_keys('PORT')
160+
elif scenario == "one_port":
161+
ports = [PORT_A]
162+
else:
163+
assert False
164+
165+
# delete all PGs and QUEUEs from the relevant ports
166+
pgs = config_db.get_keys('BUFFER_PG')
167+
queues = config_db.get_keys('BUFFER_QUEUE')
168+
169+
for port in ports:
170+
for key in pgs:
171+
if port in key:
172+
config_db.delete_entry('BUFFER_PG', key)
173+
app_db.wait_for_deleted_entry('BUFFER_PG_TABLE', key)
174+
175+
for key in queues:
176+
if port in key:
177+
config_db.delete_entry('BUFFER_QUEUE', key)
178+
app_db.wait_for_deleted_entry('BUFFER_QUEUE_TABLE', key)
179+
180+
ports_info = {}
181+
182+
for key in ports:
183+
# read port info and save it
184+
ports_info[key] = config_db.get_entry("PORT", key)
185+
186+
187+
for i in range(DELETE_CREATE_ITERATIONS):
188+
# remove ports
189+
for key in ports:
190+
config_db.delete_entry('PORT',key)
191+
app_db.wait_for_deleted_entry("PORT_TABLE", key)
192+
193+
# verify remove port
194+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
195+
num_of_ports-len(ports))
196+
197+
assert len(num) == num_of_ports-len(ports)
198+
199+
# add port
200+
"""
201+
DELETE_CREATE_ITERATIONS defines the number of iteration of delete and create to ports,
202+
we add different timeouts between delete/create to catch potential race condition that can lead to system crush.
203+
"""
204+
time.sleep(i%3)
205+
for key in ports:
206+
config_db.update_entry("PORT", key, ports_info[key])
207+
app_db.wait_for_entry('PORT_TABLE',key)
208+
209+
# verify add port
210+
num = asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
211+
num_of_ports)
212+
213+
assert len(num) == num_of_ports
214+
215+
time.sleep((i%2)+1)
216+
217+
# run ping
218+
dvs.setup_db()
219+
220+
dvs.create_vlan("6")
221+
dvs.create_vlan_member("6", PORT_A)
222+
dvs.create_vlan_member("6", PORT_B)
223+
224+
port_entry_a = state_db.get_entry("PORT_TABLE",PORT_A)
225+
port_entry_b = state_db.get_entry("PORT_TABLE",PORT_B)
226+
port_admin_a = port_entry_a['admin_status']
227+
port_admin_b = port_entry_b['admin_status']
228+
229+
dvs.set_interface_status("Vlan6", "up")
230+
dvs.add_ip_address("Vlan6", "6.6.6.1/24")
231+
dvs.set_interface_status(PORT_A, "up")
232+
dvs.set_interface_status(PORT_B, "up")
233+
234+
dvs.servers[16].runcmd("ifconfig eth0 6.6.6.6/24 up")
235+
dvs.servers[16].runcmd("ip route add default via 6.6.6.1")
236+
dvs.servers[17].runcmd("ifconfig eth0 6.6.6.7/24 up")
237+
dvs.servers[17].runcmd("ip route add default via 6.6.6.1")
238+
239+
time.sleep(2)
240+
241+
rc = dvs.servers[16].runcmd("ping -c 1 6.6.6.7")
242+
assert rc == 0
243+
244+
rc = dvs.servers[17].runcmd("ping -c 1 6.6.6.6")
245+
assert rc == 0
246+
247+
dvs.set_interface_status(PORT_A, port_admin_a)
248+
dvs.set_interface_status(PORT_B, port_admin_b)
249+
dvs.remove_vlan_member("6", PORT_A)
250+
dvs.remove_vlan_member("6", PORT_B)
251+
dvs.remove_vlan("6")

0 commit comments

Comments
 (0)