Skip to content

Commit a0417f6

Browse files
authored
[Buffer Manager][201911] Reclaim unused buffer for admin-down ports (#1837)
Depends on #1787 What I did To reclaim reserved buffer. As the way to do it differs among vendors, the environment ASIC_VENDOR is passed to swss docker and will be loaded when buffermgrd starts. After that, buffermgrd will: Handle port admin down on Mellanox platform. Not apply lossless buffer PG to an admin-down port Remove lossless buffer PG (3-4) from a port when it is shut down. Readd lossless buffer PG (3-4) to a port when a port is started up. Why I did it To support reclaiming reserved buffer when a port is shut down on Mellanox platform. How I verified it Regression test and vs test. Signed-off-by: Stephen Sun <[email protected]>
1 parent f77d393 commit a0417f6

File tree

3 files changed

+146
-36
lines changed

3 files changed

+146
-36
lines changed

cfgmgr/buffermgr.cpp

+64-35
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ BufferMgr::BufferMgr(DBConnector *cfgDb, DBConnector *stateDb, string pg_lookup_
2222
m_cfgLosslessPgPoolTable(cfgDb, CFG_BUFFER_POOL_TABLE_NAME)
2323
{
2424
readPgProfileLookupFile(pg_lookup_file);
25+
char *platform = getenv("ASIC_VENDOR");
26+
if (NULL == platform)
27+
{
28+
SWSS_LOG_WARN("Platform environment variable is not defined");
29+
}
30+
else
31+
{
32+
m_platform = platform;
33+
}
2534
}
2635

2736
//# speed, cable, size, xon, xoff, threshold, xon_offset
@@ -112,11 +121,27 @@ Create/update two tables: profile (in m_cfgBufferProfileTable) and port buffer (
112121
}
113122
}
114123
*/
115-
task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed)
124+
task_process_status BufferMgr::doPortTableUpdateTask(string port, string speed, bool admin_up)
116125
{
117-
vector<FieldValueTuple> fvVector;
126+
vector<FieldValueTuple> fvVectorPg, fvVectorProfile;
118127
string cable;
119128

129+
string buffer_pg_key = port + m_cfgBufferPgTable.getTableNameSeparator() + LOSSLESS_PGS;
130+
131+
m_cfgBufferPgTable.get(buffer_pg_key, fvVectorPg);
132+
133+
if (!admin_up && m_platform == "mellanox")
134+
{
135+
// Remove the entry in BUFFER_PG table if any
136+
if (!fvVectorPg.empty())
137+
{
138+
SWSS_LOG_NOTICE("Removing PG %s from port %s which is administrative down", buffer_pg_key.c_str(), port.c_str());
139+
m_cfgBufferPgTable.del(buffer_pg_key);
140+
}
141+
142+
return task_process_status::task_success;
143+
}
144+
120145
if (m_cableLenLookup.count(port) == 0)
121146
{
122147
SWSS_LOG_INFO("Unable to create/update PG profile for port %s. Cable length is not set", port.c_str());
@@ -137,8 +162,8 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed)
137162
string buffer_profile_key = "pg_lossless_" + speed + "_" + cable + "_profile";
138163

139164
// check if profile already exists - if yes - skip creation
140-
m_cfgBufferProfileTable.get(buffer_profile_key, fvVector);
141-
if (fvVector.size() == 0)
165+
m_cfgBufferProfileTable.get(buffer_profile_key, fvVectorProfile);
166+
if (fvVectorProfile.size() == 0)
142167
{
143168
SWSS_LOG_NOTICE("Creating new profile '%s'", buffer_profile_key.c_str());
144169

@@ -156,49 +181,42 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed)
156181
m_cfgBufferProfileTable.getTableNameSeparator() +
157182
INGRESS_LOSSLESS_PG_POOL_NAME;
158183

159-
fvVector.push_back(make_pair("pool", "[" + pg_pool_reference + "]"));
160-
fvVector.push_back(make_pair("xon", m_pgProfileLookup[speed][cable].xon));
184+
fvVectorProfile.push_back(make_pair("pool", "[" + pg_pool_reference + "]"));
185+
fvVectorProfile.push_back(make_pair("xon", m_pgProfileLookup[speed][cable].xon));
161186
if (m_pgProfileLookup[speed][cable].xon_offset.length() > 0) {
162-
fvVector.push_back(make_pair("xon_offset",
187+
fvVectorProfile.push_back(make_pair("xon_offset",
163188
m_pgProfileLookup[speed][cable].xon_offset));
164189
}
165-
fvVector.push_back(make_pair("xoff", m_pgProfileLookup[speed][cable].xoff));
166-
fvVector.push_back(make_pair("size", m_pgProfileLookup[speed][cable].size));
167-
fvVector.push_back(make_pair(mode, m_pgProfileLookup[speed][cable].threshold));
168-
m_cfgBufferProfileTable.set(buffer_profile_key, fvVector);
190+
fvVectorProfile.push_back(make_pair("xoff", m_pgProfileLookup[speed][cable].xoff));
191+
fvVectorProfile.push_back(make_pair("size", m_pgProfileLookup[speed][cable].size));
192+
fvVectorProfile.push_back(make_pair(mode, m_pgProfileLookup[speed][cable].threshold));
193+
m_cfgBufferProfileTable.set(buffer_profile_key, fvVectorProfile);
169194
}
170195
else
171196
{
172197
SWSS_LOG_NOTICE("Reusing existing profile '%s'", buffer_profile_key.c_str());
173198
}
174199

175-
fvVector.clear();
176-
177-
string buffer_pg_key = port + m_cfgBufferPgTable.getTableNameSeparator() + LOSSLESS_PGS;
178-
179200
string profile_ref = string("[") +
180201
CFG_BUFFER_PROFILE_TABLE_NAME +
181202
m_cfgBufferPgTable.getTableNameSeparator() +
182203
buffer_profile_key +
183204
"]";
184205

185206
/* Check if PG Mapping is already then log message and return. */
186-
187-
m_cfgBufferPgTable.get(buffer_pg_key, fvVector);
188-
189-
for (auto& prop : fvVector)
207+
for (auto& prop : fvVectorPg)
190208
{
191209
if ((fvField(prop) == "profile") && (profile_ref == fvValue(prop)))
192210
{
193211
SWSS_LOG_NOTICE("PG to Buffer Profile Mapping %s already present", buffer_pg_key.c_str());
194212
return task_process_status::task_success;
195213
}
196214
}
197-
198-
fvVector.clear();
199-
200-
fvVector.push_back(make_pair("profile", profile_ref));
201-
m_cfgBufferPgTable.set(buffer_pg_key, fvVector);
215+
216+
fvVectorPg.clear();
217+
218+
fvVectorPg.push_back(make_pair("profile", profile_ref));
219+
m_cfgBufferPgTable.set(buffer_pg_key, fvVectorPg);
202220
return task_process_status::task_success;
203221
}
204222

@@ -221,23 +239,34 @@ void BufferMgr::doTask(Consumer &consumer)
221239
task_process_status task_status = task_process_status::task_success;
222240
if (op == SET_COMMAND)
223241
{
224-
for (auto i : kfvFieldsValues(t))
242+
if (table_name == CFG_PORT_CABLE_LEN_TABLE_NAME)
225243
{
226-
if (table_name == CFG_PORT_CABLE_LEN_TABLE_NAME)
244+
// receive and cache cable length table
245+
for (auto i : kfvFieldsValues(t))
227246
{
228-
// receive and cache cable length table
229247
task_status = doCableTask(fvField(i), fvValue(i));
230248
}
231-
// In case of PORT table update, Buffer Manager is interested in speed update only
232-
if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME && fvField(i) == "speed")
233-
{
234-
// create/update profile for port
235-
task_status = doSpeedUpdateTask(port, fvValue(i));
236-
}
237-
if (task_status != task_process_status::task_success)
249+
}
250+
else if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME)
251+
{
252+
bool speed_updated = false, admin_status_updated = false, admin_up = false;
253+
string speed;
254+
for (auto i : kfvFieldsValues(t))
238255
{
239-
break;
256+
if (fvField(i) == "speed")
257+
{
258+
speed_updated = true;
259+
speed = fvValue(i);
260+
}
261+
else if (fvField(i) == "admin_status")
262+
{
263+
admin_status_updated = true;
264+
admin_up = ("up" == fvValue(i));
265+
}
240266
}
267+
268+
if (speed_updated || admin_status_updated)
269+
task_status = doPortTableUpdateTask(port, speed, admin_up);
241270
}
242271
}
243272

cfgmgr/buffermgr.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class BufferMgr : public Orch
3333
using Orch::doTask;
3434

3535
private:
36+
std::string m_platform;
37+
3638
Table m_cfgPortTable;
3739
Table m_cfgCableLenTable;
3840
Table m_cfgBufferProfileTable;
@@ -45,7 +47,7 @@ class BufferMgr : public Orch
4547
std::string getPgPoolMode();
4648
void readPgProfileLookupFile(std::string);
4749
task_process_status doCableTask(std::string port, std::string cable_length);
48-
task_process_status doSpeedUpdateTask(std::string port, std::string speed);
50+
task_process_status doPortTableUpdateTask(std::string port, std::string speed, bool admin_up);
4951

5052
void doTask(Consumer &consumer);
5153
};

tests/test_buffer_manager.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import pytest
2+
import time
3+
from swsscommon import swsscommon
4+
5+
class TestBufferManager(object):
6+
def make_dict(self, input_list):
7+
return dict(input_list[1])
8+
9+
def setup_db(self, dvs):
10+
self.config_db = swsscommon.DBConnector(4, dvs.redis_sock, 0)
11+
self.buffer_pg_table = swsscommon.Table(self.config_db, "BUFFER_PG")
12+
self.port_table = swsscommon.Table(self.config_db, "PORT")
13+
cable_length_table = swsscommon.Table(self.config_db, "CABLE_LENGTH")
14+
cable_length_key = cable_length_table.getKeys()[0]
15+
self.cable_lengths = self.make_dict(cable_length_table.get(cable_length_key))
16+
self.buffer_profile_table = swsscommon.Table(self.config_db, "BUFFER_PROFILE")
17+
18+
def load_pg_profile_lookup(self, dvs):
19+
self.profile_lookup_info = {}
20+
lines = dvs.runcmd('cat /usr/share/sonic/hwsku/pg_profile_lookup.ini')[1].split('\n')
21+
22+
SPEED = 0
23+
CABLE_LENGTH = 1
24+
SIZE = 2
25+
XON = 3
26+
XOFF = 4
27+
THRESHOLD = 5
28+
XON_OFFSET = 6
29+
30+
for line in lines:
31+
if len(line) == 0 or line[0] == '#':
32+
continue
33+
tokens = line.split()
34+
self.profile_lookup_info[(tokens[SPEED], tokens[CABLE_LENGTH])] = {
35+
'size': tokens[SIZE],
36+
'xon': tokens[XON],
37+
'xoff': tokens[XOFF],
38+
'dynamic_th': tokens[THRESHOLD]
39+
}
40+
if XON_OFFSET < len(tokens):
41+
self.profile_lookup_info[(tokens[SPEED], tokens[CABLE_LENGTH])]['xon_offset'] = tokens[XON_OFFSET]
42+
43+
def test_buffer_pg(self, dvs):
44+
self.setup_db(dvs)
45+
46+
port = 'Ethernet0'
47+
pg = port + '|3-4'
48+
49+
port_info = self.make_dict(self.port_table.get(port))
50+
if 'up' == port_info.get('admin_status'):
51+
# By default, all ports should be admin down on VM.
52+
# However, in case the port under test was admin up before the test, we just shut down it.
53+
dvs.runcmd('config interface shutdown {}'.format(port))
54+
time.sleep(1)
55+
56+
# Make sure no lossless PG exists on an admin down port
57+
assert not self.buffer_pg_table.get(pg)[0]
58+
59+
try:
60+
# Startup the port. The lossless PG should be created according to speed and cable length
61+
dvs.runcmd('config interface startup {}'.format(port))
62+
63+
cable_length = self.cable_lengths.get(port)
64+
speed = port_info.get('speed')
65+
66+
expected_profile_name = 'pg_lossless_{}_{}_profile'.format(speed, cable_length)
67+
buffer_profile_info = self.make_dict(self.buffer_profile_table.get(expected_profile_name))
68+
buffer_pg_info = self.make_dict(self.buffer_pg_table.get(pg))
69+
assert buffer_pg_info['profile'] == '[BUFFER_PROFILE|{}]'.format(expected_profile_name)
70+
71+
self.load_pg_profile_lookup(dvs)
72+
expected_profile_info = self.profile_lookup_info[(speed, cable_length)]
73+
expected_profile_info['pool'] = '[BUFFER_POOL|ingress_lossless_pool]'
74+
assert buffer_profile_info == expected_profile_info
75+
finally:
76+
# Shutdown the port. The lossless PG should be removed
77+
dvs.runcmd('config interface shutdown {}'.format(port))
78+
time.sleep(1)
79+
assert not self.buffer_pg_table.get(pg)[0]

0 commit comments

Comments
 (0)