blob: f57596cdd13536a8f0f093e3486485b70d7c4816 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#include "config.h"
2
3#include "manager.hpp"
4
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05005#include "constants.hpp"
6#include "exceptions.hpp"
7#include "logger.hpp"
8#include "parser.hpp"
9#include "parser_factory.hpp"
10#include "parser_interface.hpp"
Rekha Aparnaffdff312025-03-25 01:10:56 -050011#include "single_fab.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050012#include "types.hpp"
13#include "utility/dbus_utility.hpp"
14#include "utility/json_utility.hpp"
15#include "utility/vpd_specific_utility.hpp"
16
17#include <boost/asio/steady_timer.hpp>
18#include <sdbusplus/bus/match.hpp>
19#include <sdbusplus/message.hpp>
20
21namespace vpd
22{
23Manager::Manager(
24 const std::shared_ptr<boost::asio::io_context>& ioCon,
25 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
26 const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
27 m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
28{
Jinu Joy Thomas3419f5d2025-04-09 04:04:18 -050029#ifdef IBM_SYSTEM
Rekha Aparnaffdff312025-03-25 01:10:56 -050030 if (!dbusUtility::isChassisPowerOn())
31 {
32 SingleFab l_singleFab;
33 const int& l_rc = l_singleFab.singleFabImOverride();
34
35 if (l_rc == constants::FAILURE)
36 {
37 throw std::runtime_error(
38 std::string(__FUNCTION__) +
39 " : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced.");
40 }
41 }
42#endif
43
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050044 try
45 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050046 // For backward compatibility. Should be depricated.
47 iFace->register_method(
48 "WriteKeyword",
49 [this](const sdbusplus::message::object_path i_path,
50 const std::string i_recordName, const std::string i_keyword,
51 const types::BinaryVector i_value) -> int {
52 return this->updateKeyword(
53 i_path, std::make_tuple(i_recordName, i_keyword, i_value));
54 });
55
56 // Register methods under com.ibm.VPD.Manager interface
57 iFace->register_method(
58 "UpdateKeyword",
59 [this](const types::Path i_vpdPath,
60 const types::WriteVpdParams i_paramsToWriteData) -> int {
61 return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
62 });
63
64 iFace->register_method(
65 "WriteKeywordOnHardware",
66 [this](const types::Path i_fruPath,
67 const types::WriteVpdParams i_paramsToWriteData) -> int {
68 return this->updateKeywordOnHardware(i_fruPath,
69 i_paramsToWriteData);
70 });
71
72 iFace->register_method(
73 "ReadKeyword",
74 [this](const types::Path i_fruPath,
75 const types::ReadVpdParams i_paramsToReadData)
76 -> types::DbusVariantType {
77 return this->readKeyword(i_fruPath, i_paramsToReadData);
78 });
79
80 iFace->register_method(
81 "CollectFRUVPD",
82 [this](const sdbusplus::message::object_path& i_dbusObjPath) {
83 this->collectSingleFruVpd(i_dbusObjPath);
84 });
85
86 iFace->register_method(
87 "deleteFRUVPD",
88 [this](const sdbusplus::message::object_path& i_dbusObjPath) {
89 this->deleteSingleFruVpd(i_dbusObjPath);
90 });
91
92 iFace->register_method(
93 "GetExpandedLocationCode",
94 [this](const std::string& i_unexpandedLocationCode,
95 uint16_t& i_nodeNumber) -> std::string {
96 return this->getExpandedLocationCode(i_unexpandedLocationCode,
97 i_nodeNumber);
98 });
99
100 iFace->register_method("GetFRUsByExpandedLocationCode",
101 [this](const std::string& i_expandedLocationCode)
102 -> types::ListOfPaths {
103 return this->getFrusByExpandedLocationCode(
104 i_expandedLocationCode);
105 });
106
107 iFace->register_method(
108 "GetFRUsByUnexpandedLocationCode",
109 [this](const std::string& i_unexpandedLocationCode,
110 uint16_t& i_nodeNumber) -> types::ListOfPaths {
111 return this->getFrusByUnexpandedLocationCode(
112 i_unexpandedLocationCode, i_nodeNumber);
113 });
114
115 iFace->register_method(
116 "GetHardwarePath",
117 [this](const sdbusplus::message::object_path& i_dbusObjPath)
118 -> std::string { return this->getHwPath(i_dbusObjPath); });
119
120 iFace->register_method("PerformVPDRecollection", [this]() {
121 this->performVpdRecollection();
122 });
123
124 // Indicates FRU VPD collection for the system has not started.
125 iFace->register_property_rw<std::string>(
126 "CollectionStatus", sdbusplus::vtable::property_::emits_change,
Souvik Roya6832472025-05-22 13:18:12 -0500127 [this](const std::string& l_currStatus, const auto&) {
Souvik Roy251d85e2025-05-26 04:30:30 -0500128 if (m_vpdCollectionStatus != l_currStatus)
129 {
130 m_vpdCollectionStatus = l_currStatus;
131 m_interface->signal_property("CollectionStatus");
132 }
Souvik Roya6832472025-05-22 13:18:12 -0500133 return true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500134 },
135 [this](const auto&) { return m_vpdCollectionStatus; });
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530136
137 // If required, instantiate OEM specific handler here.
138#ifdef IBM_SYSTEM
139 m_ibmHandler = std::make_shared<IbmHandler>(
140 m_worker, m_backupAndRestoreObj, m_interface, m_ioContext,
141 m_asioConnection);
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530142#else
143 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
144 m_interface->set_property("CollectionStatus", std::string("Completed"));
145#endif
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500146 }
147 catch (const std::exception& e)
148 {
149 logging::logMessage(
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530150 "Manager class instantiation failed. " + std::string(e.what()));
151
152 vpd::EventLogger::createSyncPel(
153 vpd::EventLogger::getErrorType(e), vpd::types::SeverityType::Error,
154 __FILE__, __FUNCTION__, 0, vpd::EventLogger::getErrorMsg(e),
155 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500156 }
157}
158
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500159int Manager::updateKeyword(const types::Path i_vpdPath,
160 const types::WriteVpdParams i_paramsToWriteData)
161{
162 if (i_vpdPath.empty())
163 {
164 logging::logMessage("Given VPD path is empty.");
165 return -1;
166 }
167
168 types::Path l_fruPath;
169 nlohmann::json l_sysCfgJsonObj{};
170
171 if (m_worker.get() != nullptr)
172 {
173 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
174
175 // Get the EEPROM path
176 if (!l_sysCfgJsonObj.empty())
177 {
RekhaAparna017fea9f52025-02-17 04:14:02 -0600178 l_fruPath =
179 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500180 }
181 }
182
183 if (l_fruPath.empty())
184 {
185 l_fruPath = i_vpdPath;
186 }
187
188 try
189 {
190 std::shared_ptr<Parser> l_parserObj =
191 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
Anupama B R8dedd1e2025-03-24 07:43:47 -0500192 auto l_rc = l_parserObj->updateVpdKeyword(i_paramsToWriteData);
193
194 if (l_rc != constants::FAILURE && m_backupAndRestoreObj)
195 {
196 if (m_backupAndRestoreObj->updateKeywordOnPrimaryOrBackupPath(
197 l_fruPath, i_paramsToWriteData) < constants::VALUE_0)
198 {
199 logging::logMessage(
200 "Write success, but backup and restore failed for file[" +
201 l_fruPath + "]");
202 }
203 }
Souvik Roy2ee8a212025-04-24 02:37:59 -0500204
205 // update keyword in inherited FRUs
206 if (l_rc != constants::FAILURE)
207 {
208 vpdSpecificUtility::updateKwdOnInheritedFrus(
209 l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj);
210 }
211
Souvik Roy96ebe962025-04-29 04:01:07 -0500212 // update common interface(s) properties
213 if (l_rc != constants::FAILURE)
214 {
215 vpdSpecificUtility::updateCiPropertyOfInheritedFrus(
216 l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj);
217 }
218
Anupama B R8dedd1e2025-03-24 07:43:47 -0500219 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500220 }
221 catch (const std::exception& l_exception)
222 {
223 // TODO:: error log needed
224 logging::logMessage("Update keyword failed for file[" + i_vpdPath +
225 "], reason: " + std::string(l_exception.what()));
226 return -1;
227 }
228}
229
230int Manager::updateKeywordOnHardware(
231 const types::Path i_fruPath,
232 const types::WriteVpdParams i_paramsToWriteData) noexcept
233{
234 try
235 {
236 if (i_fruPath.empty())
237 {
238 throw std::runtime_error("Given FRU path is empty");
239 }
240
241 nlohmann::json l_sysCfgJsonObj{};
242
243 if (m_worker.get() != nullptr)
244 {
245 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
246 }
247
248 std::shared_ptr<Parser> l_parserObj =
249 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
250 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
251 }
252 catch (const std::exception& l_exception)
253 {
254 EventLogger::createAsyncPel(
255 types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
256 __FILE__, __FUNCTION__, 0,
257 "Update keyword on hardware failed for file[" + i_fruPath +
258 "], reason: " + std::string(l_exception.what()),
259 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
260
261 return constants::FAILURE;
262 }
263}
264
265types::DbusVariantType Manager::readKeyword(
266 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
267{
268 try
269 {
270 nlohmann::json l_jsonObj{};
271
272 if (m_worker.get() != nullptr)
273 {
274 l_jsonObj = m_worker->getSysCfgJsonObj();
275 }
276
277 std::error_code ec;
278
279 // Check if given path is filesystem path
280 if (!std::filesystem::exists(i_fruPath, ec) && (ec))
281 {
282 throw std::runtime_error(
283 "Given file path " + i_fruPath + " not found.");
284 }
285
286 logging::logMessage("Performing VPD read on " + i_fruPath);
287
288 std::shared_ptr<vpd::Parser> l_parserObj =
289 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
290
291 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
292 l_parserObj->getVpdParserInstance();
293
294 return (
295 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
296 }
297 catch (const std::exception& e)
298 {
299 logging::logMessage(
300 e.what() + std::string(". VPD manager read operation failed for ") +
301 i_fruPath);
302 throw types::DeviceError::ReadFailure();
303 }
304}
305
306void Manager::collectSingleFruVpd(
307 const sdbusplus::message::object_path& i_dbusObjPath)
308{
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530309 if (m_vpdCollectionStatus != "Completed")
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500310 {
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530311 logging::logMessage(
312 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
313 std::string(i_dbusObjPath));
314 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500315 }
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600316
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530317 if (m_worker.get() != nullptr)
318 {
319 m_worker->collectSingleFruVpd(i_dbusObjPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500320 }
321}
322
323void Manager::deleteSingleFruVpd(
324 const sdbusplus::message::object_path& i_dbusObjPath)
325{
326 try
327 {
328 if (std::string(i_dbusObjPath).empty())
329 {
330 throw std::runtime_error(
331 "Given DBus object path is empty. Aborting FRU VPD deletion.");
332 }
333
334 if (m_worker.get() == nullptr)
335 {
336 throw std::runtime_error(
337 "Worker object not found, can't perform FRU VPD deletion for: " +
338 std::string(i_dbusObjPath));
339 }
340
341 m_worker->deleteFruVpd(std::string(i_dbusObjPath));
342 }
343 catch (const std::exception& l_ex)
344 {
345 // TODO: Log PEL
346 logging::logMessage(l_ex.what());
347 }
348}
349
350bool Manager::isValidUnexpandedLocationCode(
351 const std::string& i_unexpandedLocationCode)
352{
353 if ((i_unexpandedLocationCode.length() <
354 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
355 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
356 constants::STR_CMP_SUCCESS) &&
357 (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
358 constants::STR_CMP_SUCCESS)) ||
359 ((i_unexpandedLocationCode.length() >
360 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
361 (i_unexpandedLocationCode.find("-") != 4)))
362 {
363 return false;
364 }
365
366 return true;
367}
368
369std::string Manager::getExpandedLocationCode(
370 const std::string& i_unexpandedLocationCode,
371 [[maybe_unused]] const uint16_t i_nodeNumber)
372{
373 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
374 {
375 phosphor::logging::elog<types::DbusInvalidArgument>(
376 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
377 types::InvalidArgument::ARGUMENT_VALUE(
378 i_unexpandedLocationCode.c_str()));
379 }
380
381 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
382 if (!l_sysCfgJsonObj.contains("frus"))
383 {
384 logging::logMessage("Missing frus tag in system config JSON");
385 }
386
387 const nlohmann::json& l_listOfFrus =
388 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
389
390 for (const auto& l_frus : l_listOfFrus.items())
391 {
392 for (const auto& l_aFru : l_frus.value())
393 {
394 if (l_aFru["extraInterfaces"].contains(
395 constants::locationCodeInf) &&
396 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
397 "LocationCode", "") == i_unexpandedLocationCode)
398 {
399 return std::get<std::string>(dbusUtility::readDbusProperty(
400 l_aFru["serviceName"], l_aFru["inventoryPath"],
401 constants::locationCodeInf, "LocationCode"));
402 }
403 }
404 }
405 phosphor::logging::elog<types::DbusInvalidArgument>(
406 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
407 types::InvalidArgument::ARGUMENT_VALUE(
408 i_unexpandedLocationCode.c_str()));
409}
410
411types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
412 const std::string& i_unexpandedLocationCode,
413 [[maybe_unused]] const uint16_t i_nodeNumber)
414{
415 types::ListOfPaths l_inventoryPaths;
416
417 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
418 {
419 phosphor::logging::elog<types::DbusInvalidArgument>(
420 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
421 types::InvalidArgument::ARGUMENT_VALUE(
422 i_unexpandedLocationCode.c_str()));
423 }
424
425 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
426 if (!l_sysCfgJsonObj.contains("frus"))
427 {
428 logging::logMessage("Missing frus tag in system config JSON");
429 }
430
431 const nlohmann::json& l_listOfFrus =
432 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
433
434 for (const auto& l_frus : l_listOfFrus.items())
435 {
436 for (const auto& l_aFru : l_frus.value())
437 {
438 if (l_aFru["extraInterfaces"].contains(
439 constants::locationCodeInf) &&
440 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
441 "LocationCode", "") == i_unexpandedLocationCode)
442 {
443 l_inventoryPaths.push_back(
444 l_aFru.at("inventoryPath")
445 .get_ref<const nlohmann::json::string_t&>());
446 }
447 }
448 }
449
450 if (l_inventoryPaths.empty())
451 {
452 phosphor::logging::elog<types::DbusInvalidArgument>(
453 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
454 types::InvalidArgument::ARGUMENT_VALUE(
455 i_unexpandedLocationCode.c_str()));
456 }
457
458 return l_inventoryPaths;
459}
460
Patrick Williams43fedab2025-02-03 14:28:05 -0500461std::string Manager::getHwPath(
462 const sdbusplus::message::object_path& i_dbusObjPath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500463{
464 // Dummy code to supress unused variable warning. To be removed.
465 logging::logMessage(std::string(i_dbusObjPath));
466
467 return std::string{};
468}
469
470std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
471 const std::string& i_expandedLocationCode)
472{
473 /**
474 * Location code should always start with U and fulfil minimum length
475 * criteria.
476 */
477 if (i_expandedLocationCode[0] != 'U' ||
478 i_expandedLocationCode.length() <
479 constants::EXP_LOCATION_CODE_MIN_LENGTH)
480 {
481 phosphor::logging::elog<types::DbusInvalidArgument>(
482 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
483 types::InvalidArgument::ARGUMENT_VALUE(
484 i_expandedLocationCode.c_str()));
485 }
486
487 std::string l_fcKwd;
488
489 auto l_fcKwdValue = dbusUtility::readDbusProperty(
490 "xyz.openbmc_project.Inventory.Manager",
491 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
492 "com.ibm.ipzvpd.VCEN", "FC");
493
494 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
495 {
496 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
497 }
498
499 // Get the first part of expanded location code to check for FC or TM.
500 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
501
502 std::string l_unexpandedLocationCode{};
503 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
504
505 // Check if this value matches the value of FC keyword.
506 if (l_fcKwd.substr(0, 4) == l_firstKwd)
507 {
508 /**
509 * Period(.) should be there in expanded location code to seggregate
510 * FC, node number and SE values.
511 */
512 size_t l_nodeStartPos = i_expandedLocationCode.find('.');
513 if (l_nodeStartPos == std::string::npos)
514 {
515 phosphor::logging::elog<types::DbusInvalidArgument>(
516 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
517 types::InvalidArgument::ARGUMENT_VALUE(
518 i_expandedLocationCode.c_str()));
519 }
520
521 size_t l_nodeEndPos =
522 i_expandedLocationCode.find('.', l_nodeStartPos + 1);
523 if (l_nodeEndPos == std::string::npos)
524 {
525 phosphor::logging::elog<types::DbusInvalidArgument>(
526 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
527 types::InvalidArgument::ARGUMENT_VALUE(
528 i_expandedLocationCode.c_str()));
529 }
530
531 // Skip 3 bytes for '.ND'
532 l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
533 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
534
535 /**
536 * Confirm if there are other details apart FC, node number and SE
537 * in location code
538 */
539 if (i_expandedLocationCode.length() >
540 constants::EXP_LOCATION_CODE_MIN_LENGTH)
541 {
542 l_unexpandedLocationCode =
543 i_expandedLocationCode[0] + std::string("fcs") +
544 i_expandedLocationCode.substr(
545 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
546 std::string::npos);
547 }
548 else
549 {
550 l_unexpandedLocationCode = "Ufcs";
551 }
552 }
553 else
554 {
555 std::string l_tmKwd;
556 // Read TM keyword value.
557 auto l_tmKwdValue = dbusUtility::readDbusProperty(
558 "xyz.openbmc_project.Inventory.Manager",
559 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
560 "com.ibm.ipzvpd.VSYS", "TM");
561
562 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
563 {
564 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
565 }
566
567 // Check if the substr matches to TM keyword value.
568 if (l_tmKwd.substr(0, 4) == l_firstKwd)
569 {
570 /**
571 * System location code will not have node number and any other
572 * details.
573 */
574 l_unexpandedLocationCode = "Umts";
575 }
576 // The given location code is neither "fcs" or "mts".
577 else
578 {
579 phosphor::logging::elog<types::DbusInvalidArgument>(
580 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
581 types::InvalidArgument::ARGUMENT_VALUE(
582 i_expandedLocationCode.c_str()));
583 }
584 }
585
586 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
587}
588
589types::ListOfPaths Manager::getFrusByExpandedLocationCode(
590 const std::string& i_expandedLocationCode)
591{
592 std::tuple<std::string, uint16_t> l_locationAndNodePair =
593 getUnexpandedLocationCode(i_expandedLocationCode);
594
595 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
596 std::get<1>(l_locationAndNodePair));
597}
598
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500599void Manager::performVpdRecollection()
600{
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530601 if (m_worker.get() != nullptr)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500602 {
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530603 m_worker->performVpdRecollection();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500604 }
605}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500606} // namespace vpd