Skip to content

Commit 4577b40

Browse files
authored
Add buffer pool watermark support (#853)
* Write fields to FLEX_COUNTER_GROUP_TABLE at the construction of a BufferOrch object: "POLL_INTERVAL" "BUFFER_POLL_PLUGIN_LIST" "STATS_MODE" Signed-off-by: Wenda Ni <[email protected]> * Update buffer pool name to oid mapping in COUNTERS_DB upon the set and del of its oid Signed-off-by: Wenda Ni <[email protected]> * Push buffer pool watermark COUNTER_ID_LIST to FLEX_COUNTER_TABLE Signed-off-by: Wenda Ni <[email protected]> * Implement user clear logic to buffer pool watermark Signed-off-by: Wenda Ni <[email protected]> * Add periodic clear to buffer pool watermark Signed-off-by: Wenda Ni <[email protected]> * Add lua script for watermark_bufferpool Signed-off-by: Wenda Ni <[email protected]> * Fix syntax error in buffer pool watermark lua script Signed-off-by: Wenda Ni <[email protected]> * Fix compile error in watermarkorch.cpp Signed-off-by: Wenda Ni <[email protected]> * Fix from dut verification Signed-off-by: Wenda Ni <[email protected]> * Add 6000 to read only polling mode Signed-off-by: Wenda Ni <[email protected]> * Touch-up to existing codes Signed-off-by: Wenda Ni <[email protected]> * Remove debugging symbols Signed-off-by: Wenda Ni <[email protected]> * Address comments Signed-off-by: Wenda Ni <[email protected]> * Address comments Signed-off-by: Wenda Ni <[email protected]>
1 parent 4a67378 commit 4577b40

7 files changed

+241
-16
lines changed

orchagent/Makefile.am

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ dist_swss_DATA = \
1111
pfc_detect_nephos.lua \
1212
pfc_restore.lua \
1313
watermark_queue.lua \
14-
watermark_pg.lua
14+
watermark_pg.lua \
15+
watermark_bufferpool.lua
1516

1617
bin_PROGRAMS = orchagent routeresync orchagent_restart_check
1718

orchagent/bufferorch.cpp

+97-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#include "tokenize.h"
2-
32
#include "bufferorch.h"
43
#include "logger.h"
4+
#include "sai_serialize.h"
55

66
#include <sstream>
77
#include <iostream>
88

9+
using namespace std;
10+
911
extern sai_port_api_t *sai_port_api;
1012
extern sai_queue_api_t *sai_queue_api;
1113
extern sai_switch_api_t *sai_switch_api;
@@ -14,7 +16,13 @@ extern sai_buffer_api_t *sai_buffer_api;
1416
extern PortsOrch *gPortsOrch;
1517
extern sai_object_id_t gSwitchId;
1618

17-
using namespace std;
19+
#define BUFFER_POOL_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000"
20+
21+
22+
static const vector<sai_buffer_pool_stat_t> bufferPoolWatermarkStatIds =
23+
{
24+
SAI_BUFFER_POOL_STAT_WATERMARK_BYTES,
25+
};
1826

1927
type_map BufferOrch::m_buffer_type_maps = {
2028
{CFG_BUFFER_POOL_TABLE_NAME, new object_map()},
@@ -25,11 +33,18 @@ type_map BufferOrch::m_buffer_type_maps = {
2533
{CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, new object_map()}
2634
};
2735

28-
BufferOrch::BufferOrch(DBConnector *db, vector<string> &tableNames) : Orch(db, tableNames)
36+
BufferOrch::BufferOrch(DBConnector *db, vector<string> &tableNames) :
37+
Orch(db, tableNames),
38+
m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)),
39+
m_flexCounterTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_TABLE)),
40+
m_flexCounterGroupTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_GROUP_TABLE)),
41+
m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)),
42+
m_countersDbRedisClient(m_countersDb.get())
2943
{
3044
SWSS_LOG_ENTER();
3145
initTableHandlers();
3246
initBufferReadyLists(db);
47+
initFlexCounterGroupTable();
3348
};
3449

3550
void BufferOrch::initTableHandlers()
@@ -82,6 +97,32 @@ void BufferOrch::initBufferReadyList(Table& table)
8297
}
8398
}
8499

100+
void BufferOrch::initFlexCounterGroupTable(void)
101+
{
102+
string bufferPoolWmPluginName = "watermark_bufferpool.lua";
103+
104+
try
105+
{
106+
string bufferPoolLuaScript = swss::loadLuaScript(bufferPoolWmPluginName);
107+
string bufferPoolWmSha = swss::loadRedisScript(m_countersDb.get(), bufferPoolLuaScript);
108+
109+
vector<FieldValueTuple> fvTuples;
110+
fvTuples.emplace_back(BUFFER_POOL_PLUGIN_FIELD, bufferPoolWmSha);
111+
fvTuples.emplace_back(POLL_INTERVAL_FIELD, BUFFER_POOL_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS);
112+
113+
// TODO (work in progress):
114+
// Some platforms do not support buffer pool watermark clear operation on a particular pool
115+
// Invoke the SAI clear_stats API per pool to query the capability from the API call return status
116+
fvTuples.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ_AND_CLEAR);
117+
118+
m_flexCounterGroupTable->set(BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP, fvTuples);
119+
}
120+
catch (const runtime_error &e)
121+
{
122+
SWSS_LOG_ERROR("Buffer pool watermark lua script and/or flex counter group not set successfully. Runtime error: %s", e.what());
123+
}
124+
}
125+
85126
bool BufferOrch::isPortReady(const std::string& port_name) const
86127
{
87128
SWSS_LOG_ENTER();
@@ -105,6 +146,51 @@ bool BufferOrch::isPortReady(const std::string& port_name) const
105146
return result;
106147
}
107148

149+
void BufferOrch::generateBufferPoolWatermarkCounterIdList(void)
150+
{
151+
// This function will be called in FlexCounterOrch when field:value tuple "FLEX_COUNTER_STATUS":"enable"
152+
// is received on buffer pool watermark key under table "FLEX_COUNTER_GROUP_TABLE"
153+
// Because the SubscriberStateTable listens to the entire keyspace of "BUFFER_POOL_WATERMARK", any update
154+
// to field value tuples under key "BUFFER_POOL_WATERMARK" will cause this tuple to be heard again
155+
// To avoid resync the coutner ID list a second time, we introduce a data member variable to mark whether
156+
// this operation has already been done or not yet
157+
if (m_isBufferPoolWatermarkCounterIdListGenerated)
158+
{
159+
return;
160+
}
161+
162+
// Detokenize the SAI watermark stats to a string, separated by comma
163+
string statList;
164+
for (const auto &it : bufferPoolWatermarkStatIds)
165+
{
166+
statList += (sai_serialize_buffer_pool_stat(it) + list_item_delimiter);
167+
}
168+
if (!statList.empty())
169+
{
170+
statList.pop_back();
171+
}
172+
173+
vector<FieldValueTuple> fvTuples;
174+
fvTuples.emplace_back(BUFFER_POOL_COUNTER_ID_LIST, statList);
175+
176+
// Push buffer pool watermark COUNTER_ID_LIST to FLEX_COUNTER_TABLE on a per buffer pool basis
177+
for (const auto &it : *(m_buffer_type_maps[CFG_BUFFER_POOL_TABLE_NAME]))
178+
{
179+
string key = BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP ":" + sai_serialize_object_id(it.second);
180+
m_flexCounterTable->set(key, fvTuples);
181+
}
182+
183+
m_isBufferPoolWatermarkCounterIdListGenerated = true;
184+
}
185+
186+
const object_map &BufferOrch::getBufferPoolNameOidMap(void)
187+
{
188+
// In the case different Orches are running in
189+
// different threads, caller may need to grab a read lock
190+
// before calling this function
191+
return *m_buffer_type_maps[CFG_BUFFER_POOL_TABLE_NAME];
192+
}
193+
108194
task_process_status BufferOrch::processBufferPool(Consumer &consumer)
109195
{
110196
SWSS_LOG_ENTER();
@@ -209,6 +295,12 @@ task_process_status BufferOrch::processBufferPool(Consumer &consumer)
209295
}
210296
(*(m_buffer_type_maps[map_type_name]))[object_name] = sai_object;
211297
SWSS_LOG_NOTICE("Created buffer pool %s with type %s", object_name.c_str(), map_type_name.c_str());
298+
// Here we take the PFC watchdog approach to update the COUNTERS_DB metadata (e.g., PFC_WD_DETECTION_TIME per queue)
299+
// at initialization (creation and registration phase)
300+
// Specifically, we push the buffer pool name to oid mapping upon the creation of the oid
301+
// In pg and queue case, this mapping installment is deferred to FlexCounterOrch at a reception of field
302+
// "FLEX_COUNTER_STATUS"
303+
m_countersDbRedisClient.hset(COUNTERS_BUFFER_POOL_NAME_MAP, object_name, sai_serialize_object_id(sai_object));
212304
}
213305
}
214306
else if (op == DEL_COMMAND)
@@ -222,6 +314,7 @@ task_process_status BufferOrch::processBufferPool(Consumer &consumer)
222314
SWSS_LOG_NOTICE("Removed buffer pool %s with type %s", object_name.c_str(), map_type_name.c_str());
223315
auto it_to_delete = (m_buffer_type_maps[map_type_name])->find(object_name);
224316
(m_buffer_type_maps[map_type_name])->erase(it_to_delete);
317+
m_countersDbRedisClient.hdel(COUNTERS_BUFFER_POOL_NAME_MAP, object_name);
225318
}
226319
else
227320
{
@@ -370,7 +463,7 @@ task_process_status BufferOrch::processBufferProfile(Consumer &consumer)
370463
}
371464

372465
/*
373-
Input sample "BUFFER_QUEUE_TABLE:Ethernet4,Ethernet45:10-15"
466+
Input sample "BUFFER_QUEUE|Ethernet4,Ethernet45|10-15"
374467
*/
375468
task_process_status BufferOrch::processQueue(Consumer &consumer)
376469
{

orchagent/bufferorch.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#include <unordered_map>
77
#include "orch.h"
88
#include "portsorch.h"
9+
#include "redisapi.h"
10+
#include "redisclient.h"
11+
12+
#define BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP "BUFFER_POOL_WATERMARK_STAT_COUNTER"
913

1014
const string buffer_size_field_name = "size";
1115
const string buffer_pool_type_field_name = "type";
@@ -30,16 +34,20 @@ class BufferOrch : public Orch
3034
BufferOrch(DBConnector *db, vector<string> &tableNames);
3135
bool isPortReady(const std::string& port_name) const;
3236
static type_map m_buffer_type_maps;
37+
void generateBufferPoolWatermarkCounterIdList(void);
38+
const object_map &getBufferPoolNameOidMap(void);
39+
3340
private:
3441
typedef task_process_status (BufferOrch::*buffer_table_handler)(Consumer& consumer);
3542
typedef map<string, buffer_table_handler> buffer_table_handler_map;
3643
typedef pair<string, buffer_table_handler> buffer_handler_pair;
3744

38-
virtual void doTask() override;
45+
void doTask() override;
3946
virtual void doTask(Consumer& consumer);
4047
void initTableHandlers();
4148
void initBufferReadyLists(DBConnector *db);
4249
void initBufferReadyList(Table& table);
50+
void initFlexCounterGroupTable(void);
4351
task_process_status processBufferPool(Consumer &consumer);
4452
task_process_status processBufferProfile(Consumer &consumer);
4553
task_process_status processQueue(Consumer &consumer);
@@ -50,6 +58,15 @@ class BufferOrch : public Orch
5058
buffer_table_handler_map m_bufferHandlerMap;
5159
std::unordered_map<std::string, bool> m_ready_list;
5260
std::unordered_map<std::string, std::vector<std::string>> m_port_ready_list_ref;
61+
62+
unique_ptr<DBConnector> m_flexCounterDb;
63+
unique_ptr<ProducerTable> m_flexCounterGroupTable;
64+
unique_ptr<ProducerTable> m_flexCounterTable;
65+
66+
unique_ptr<DBConnector> m_countersDb;
67+
RedisClient m_countersDbRedisClient;
68+
69+
bool m_isBufferPoolWatermarkCounterIdListGenerated = false;
5370
};
5471
#endif /* SWSS_BUFFORCH_H */
5572

orchagent/flexcounterorch.cpp

+24-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
#include "redisclient.h"
77
#include "sai_serialize.h"
88
#include "pfcwdorch.h"
9+
#include "bufferorch.h"
910

1011
extern sai_port_api_t *sai_port_api;
1112

1213
extern PortsOrch *gPortsOrch;
1314
extern IntfsOrch *gIntfsOrch;
15+
extern BufferOrch *gBufferOrch;
16+
17+
#define BUFFER_POOL_WATERMARK_KEY "BUFFER_POOL_WATERMARK"
1418

1519
unordered_map<string, string> flexCounterGroupMap =
1620
{
@@ -19,6 +23,7 @@ unordered_map<string, string> flexCounterGroupMap =
1923
{"PFCWD", PFC_WD_FLEX_COUNTER_GROUP},
2024
{"QUEUE_WATERMARK", QUEUE_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP},
2125
{"PG_WATERMARK", PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP},
26+
{BUFFER_POOL_WATERMARK_KEY, BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP},
2227
{"RIF", RIF_STAT_COUNTER_FLEX_COUNTER_GROUP},
2328
};
2429

@@ -76,11 +81,28 @@ void FlexCounterOrch::doTask(Consumer &consumer)
7681
}
7782
else if(field == FLEX_COUNTER_STATUS_FIELD)
7883
{
79-
// Currently the counters are disabled by default
80-
// The queue maps will be generated as soon as counters are enabled
84+
// Currently, the counters are disabled for polling by default
85+
// The queue maps will be generated as soon as counters are enabled for polling
86+
// Counter polling is enabled by pushing the COUNTER_ID_LIST/ATTR_ID_LIST, which contains
87+
// the list of SAI stats/attributes of polling interest, to the FLEX_COUNTER_DB under the
88+
// additional condition that the polling interval at that time is set nonzero positive,
89+
// which is automatically satisfied upon the creation of the orch object that requires
90+
// the syncd flex counter polling service
91+
// This postponement is introduced by design to accelerate the initialization process
92+
//
93+
// generateQueueMap() is called as long as a field "FLEX_COUNTER_STATUS" event is heard,
94+
// regardless of whether the key is "QUEUE" or whether the value is "enable" or "disable"
95+
// This can be because generateQueueMap() installs a fundamental list of queue stats
96+
// that need to be polled. So my doubt here is if queue watermark stats shall be piggybacked
97+
// into the same function as they may not be counted as fundamental
8198
gPortsOrch->generateQueueMap();
8299
gPortsOrch->generatePriorityGroupMap();
83100
gIntfsOrch->generateInterfaceMap();
101+
// Install COUNTER_ID_LIST/ATTR_ID_LIST only when hearing buffer pool watermark enable event
102+
if ((key == BUFFER_POOL_WATERMARK_KEY) && (value == "enable"))
103+
{
104+
gBufferOrch->generateBufferPoolWatermarkCounterIdList();
105+
}
84106

85107
vector<FieldValueTuple> fieldValues;
86108
fieldValues.emplace_back(FLEX_COUNTER_STATUS_FIELD, value);

orchagent/watermark_bufferpool.lua

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
-- KEYS - buffer IDs
2+
-- ARGV[1] - counters db index
3+
-- ARGV[2] - counters table name
4+
-- ARGV[3] - poll time interval
5+
-- return nothing for now
6+
7+
local counters_db = ARGV[1]
8+
local counters_table_name = 'COUNTERS'
9+
10+
local user_table_name = 'USER_WATERMARKS'
11+
local persistent_table_name = 'PERSISTENT_WATERMARKS'
12+
local periodic_table_name = 'PERIODIC_WATERMARKS'
13+
14+
local sai_buffer_pool_watermark_stat_name = 'SAI_BUFFER_POOL_STAT_WATERMARK_BYTES'
15+
16+
local rets = {}
17+
18+
redis.call('SELECT', counters_db)
19+
20+
-- Iterate through each buffer pool oid
21+
local n = table.getn(KEYS)
22+
for i = n, 1, -1 do
23+
-- Get new watermark value from COUNTERS
24+
local wm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name)
25+
if wm then
26+
wm = tonumber(wm)
27+
28+
-- Get last value from *_WATERMARKS
29+
local user_wm_last = redis.call('HGET', user_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name)
30+
31+
-- Set higher value to *_WATERMARKS
32+
if user_wm_last then
33+
user_wm_last = tonumber(user_wm_last)
34+
if wm > user_wm_last then
35+
redis.call('HSET', user_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name, wm)
36+
end
37+
else
38+
redis.call('HSET', user_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name, wm)
39+
end
40+
41+
local persistent_wm_last = redis.call('HGET', persistent_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name)
42+
if persistent_wm_last then
43+
persistent_wm_last = tonumber(persistent_wm_last)
44+
if wm > persistent_wm_last then
45+
redis.call('HSET', persistent_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name, wm)
46+
end
47+
else
48+
redis.call('HSET', persistent_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name, wm)
49+
end
50+
51+
local periodic_wm_last = redis.call('HGET', periodic_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name)
52+
if periodic_wm_last then
53+
periodic_wm_last = tonumber(periodic_wm_last)
54+
if wm > periodic_wm_last then
55+
redis.call('HSET', periodic_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name, wm)
56+
end
57+
else
58+
redis.call('HSET', periodic_table_name .. ':' .. KEYS[i], sai_buffer_pool_watermark_stat_name, wm)
59+
end
60+
end
61+
end
62+
63+
return rets

0 commit comments

Comments
 (0)