blob: 04276b988e6f6e283e6688e051280e8c32e42863 [file] [log] [blame]
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +05301#include "config.h"
2
Gunnar Mills94df8c92018-09-14 14:50:03 -05003#include "occ_manager.hpp"
4
5#include "i2c_occ.hpp"
Chicago Duanbb895cb2021-06-18 19:37:16 +08006#include "occ_dbus.hpp"
Gunnar Mills94df8c92018-09-14 14:50:03 -05007#include "utils.hpp"
8
Matt Spinlerd267cec2021-09-01 14:49:19 -05009#include <chrono>
Chicago Duanbb895cb2021-06-18 19:37:16 +080010#include <cmath>
Gunnar Mills94df8c92018-09-14 14:50:03 -050011#include <experimental/filesystem>
12#include <phosphor-logging/elog-errors.hpp>
13#include <phosphor-logging/log.hpp>
Chicago Duanbb895cb2021-06-18 19:37:16 +080014#include <regex>
Gunnar Mills94df8c92018-09-14 14:50:03 -050015#include <xyz/openbmc_project/Common/error.hpp>
16
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053017namespace open_power
18{
19namespace occ
20{
21
Matt Spinler8b8abee2021-08-25 15:18:21 -050022constexpr uint32_t fruTypeNotAvailable = 0xFF;
Matt Spinlera26f1522021-08-25 15:50:20 -050023constexpr auto fruTypeSuffix = "fru_type";
24constexpr auto faultSuffix = "fault";
25constexpr auto inputSuffix = "input";
Matt Spinler8b8abee2021-08-25 15:18:21 -050026
Chris Caina8857c52021-01-27 11:53:05 -060027using namespace phosphor::logging;
28
Matt Spinlera26f1522021-08-25 15:50:20 -050029template <typename T>
30T readFile(const std::string& path)
31{
32 std::ifstream ifs;
33 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
34 std::ifstream::eofbit);
35 T data;
36
37 try
38 {
39 ifs.open(path);
40 ifs >> data;
41 ifs.close();
42 }
43 catch (const std::exception& e)
44 {
45 auto err = errno;
46 throw std::system_error(err, std::generic_category());
47 }
48
49 return data;
50}
51
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053052void Manager::findAndCreateObjects()
53{
Matt Spinlerd267cec2021-09-01 14:49:19 -050054#ifndef POWER10
Deepak Kodihalli370f06b2017-10-25 04:26:07 -050055 for (auto id = 0; id < MAX_CPUS; ++id)
56 {
Deepak Kodihalli30417a12017-12-04 00:54:01 -060057 // Create one occ per cpu
58 auto occ = std::string(OCC_NAME) + std::to_string(id);
59 createObjects(occ);
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053060 }
Matt Spinlerd267cec2021-09-01 14:49:19 -050061#else
62 // Create the OCCs based on on the /dev/occX devices
63 auto occs = findOCCsInDev();
64
65 if (occs.empty() || (prevOCCSearch.size() != occs.size()))
66 {
67 // Something changed or no OCCs yet, try again in 10s.
68 // Note on the first pass prevOCCSearch will be empty,
69 // so there will be at least one delay to give things
70 // a chance to settle.
71 prevOCCSearch = occs;
72
73 using namespace std::literals::chrono_literals;
74 discoverTimer->restartOnce(10s);
75 }
76 else
77 {
78 discoverTimer.reset();
79
80 // createObjects requires OCC0 first.
81 std::sort(occs.begin(), occs.end());
82
83 for (auto id : occs)
84 {
85 createObjects(std::string(OCC_NAME) + std::to_string(id));
86 }
87 }
88#endif
89}
90
91std::vector<int> Manager::findOCCsInDev()
92{
93 std::vector<int> occs;
94 std::regex expr{R"(occ(\d+)$)"};
95
96 for (auto& file : fs::directory_iterator("/dev"))
97 {
98 std::smatch match;
99 std::string path{file.path().string()};
100 if (std::regex_search(path, match, expr))
101 {
102 auto num = std::stoi(match[1].str());
103
104 // /dev numbering starts at 1, ours starts at 0.
105 occs.push_back(num - 1);
106 }
107 }
108
109 return occs;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530110}
111
112int Manager::cpuCreated(sdbusplus::message::message& msg)
113{
114 namespace fs = std::experimental::filesystem;
115
116 sdbusplus::message::object_path o;
117 msg.read(o);
118 fs::path cpuPath(std::string(std::move(o)));
119
120 auto name = cpuPath.filename().string();
121 auto index = name.find(CPU_NAME);
122 name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
123
124 createObjects(name);
125
126 return 0;
127}
128
129void Manager::createObjects(const std::string& occ)
130{
131 auto path = fs::path(OCC_CONTROL_ROOT) / occ;
132
133 passThroughObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +0800134 std::make_unique<PassThrough>(path.c_str()));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530135
Gunnar Mills94df8c92018-09-14 14:50:03 -0500136 statusObjects.emplace_back(std::make_unique<Status>(
George Liuf3b75142021-06-10 11:22:50 +0800137 event, path.c_str(), *this,
Gunnar Mills94df8c92018-09-14 14:50:03 -0500138 std::bind(std::mem_fn(&Manager::statusCallBack), this,
Tom Joseph00325232020-07-29 17:51:48 +0530139 std::placeholders::_1)
140#ifdef PLDM
141 ,
142 std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
143 std::placeholders::_1)
144#endif
145 ));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530146
147 // Create the power cap monitor object for master occ (0)
148 if (!pcap)
149 {
150 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +0800151 *statusObjects.front());
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530152 }
Chris Cain78e86012021-03-04 16:15:31 -0600153
154#ifdef POWER10
155 // Create the power mode monitor object for master occ (0)
156 if (!pmode)
157 {
158 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
159 *statusObjects.front());
160 }
161#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530162}
163
164void Manager::statusCallBack(bool status)
165{
Gunnar Mills94df8c92018-09-14 14:50:03 -0500166 using InternalFailure =
167 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530168
169 // At this time, it won't happen but keeping it
170 // here just in case something changes in the future
171 if ((activeCount == 0) && (!status))
172 {
173 log<level::ERR>("Invalid update on OCCActive");
174 elog<InternalFailure>();
175 }
176
177 activeCount += status ? 1 : -1;
Eddie Jamesdae2d942017-12-20 10:50:03 -0600178
179 // Only start presence detection if all the OCCs are bound
180 if (activeCount == statusObjects.size())
181 {
Gunnar Mills94df8c92018-09-14 14:50:03 -0500182 for (auto& obj : statusObjects)
Eddie Jamesdae2d942017-12-20 10:50:03 -0600183 {
184 obj->addPresenceWatchMaster();
185 }
186 }
Chris Caina8857c52021-01-27 11:53:05 -0600187
188 if ((!_pollTimer->isEnabled()) && (activeCount > 0))
189 {
190 log<level::INFO>(fmt::format("Manager::statusCallBack(): {} OCCs will "
191 "be polled every {} seconds",
192 activeCount, pollInterval)
193 .c_str());
194
195 // Send poll and start OCC poll timer
196 pollerTimerExpired();
197 }
198 else if ((_pollTimer->isEnabled()) && (activeCount == 0))
199 {
200 // Stop OCC poll timer
201 log<level::INFO>("Manager::statusCallBack(): OCCs are not running, "
202 "stopping poll timer");
203 _pollTimer->setEnabled(false);
Matt Spinler53f68142021-08-25 15:47:31 -0500204
205#ifdef READ_OCC_SENSORS
206 for (auto& obj : statusObjects)
207 {
208 setSensorValueToNaN(obj->getOccInstanceID());
209 }
210#endif
Chris Caina8857c52021-01-27 11:53:05 -0600211 }
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530212}
213
214#ifdef I2C_OCC
215void Manager::initStatusObjects()
216{
217 // Make sure we have a valid path string
218 static_assert(sizeof(DEV_PATH) != 0);
219
220 auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
Lei YU41470e52017-11-30 16:03:50 +0800221 auto occMasterName = deviceNames.front();
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530222 for (auto& name : deviceNames)
223 {
224 i2c_occ::i2cToDbus(name);
Lei YUb5259a12017-09-01 16:22:40 +0800225 name = std::string(OCC_NAME) + '_' + name;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530226 auto path = fs::path(OCC_CONTROL_ROOT) / name;
227 statusObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +0800228 std::make_unique<Status>(event, path.c_str(), *this));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530229 }
Lei YU41470e52017-11-30 16:03:50 +0800230 // The first device is master occ
231 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +0800232 *statusObjects.front(), occMasterName);
Chris Cain78e86012021-03-04 16:15:31 -0600233#ifdef POWER10
234 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
235 *statusObjects.front());
236#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530237}
238#endif
239
Tom Joseph815f9f52020-07-27 12:12:13 +0530240#ifdef PLDM
241bool Manager::updateOCCActive(instanceID instance, bool status)
242{
243 return (statusObjects[instance])->occActive(status);
244}
245#endif
246
Chris Caina8857c52021-01-27 11:53:05 -0600247void Manager::pollerTimerExpired()
248{
249 if (activeCount == 0)
250 {
251 // No OCCs running, so poll timer will not be restarted
252 log<level::INFO>("Manager::pollerTimerExpire(): No OCCs running, poll "
253 "timer not restarted");
254 }
255
256 if (!_pollTimer)
257 {
258 log<level::ERR>(
259 "Manager::pollerTimerExpired() ERROR: Timer not defined");
260 return;
261 }
262
263 for (auto& obj : statusObjects)
264 {
265 // Read sysfs to force kernel to poll OCC
266 obj->readOccState();
Chicago Duanbb895cb2021-06-18 19:37:16 +0800267
268#ifdef READ_OCC_SENSORS
269 // Read occ sensor values
270 auto id = obj->getOccInstanceID();
271 if (!obj->occActive())
272 {
273 // Occ not activated
274 setSensorValueToNaN(id);
275 continue;
276 }
277 getSensorValues(id, obj->isMasterOcc());
278#endif
Chris Caina8857c52021-01-27 11:53:05 -0600279 }
280
281 // Restart OCC poll timer
282 _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
283}
284
Chicago Duanbb895cb2021-06-18 19:37:16 +0800285#ifdef READ_OCC_SENSORS
286void Manager::readTempSensors(const fs::path& path, uint32_t id)
287{
Chicago Duanbb895cb2021-06-18 19:37:16 +0800288 std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
289 for (auto& file : fs::directory_iterator(path))
290 {
291 if (!std::regex_search(file.path().string(), expr))
292 {
293 continue;
294 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800295
Matt Spinlera26f1522021-08-25 15:50:20 -0500296 uint32_t labelValue{0};
297
298 try
299 {
300 labelValue = readFile<uint32_t>(file.path());
301 }
302 catch (const std::system_error& e)
303 {
304 log<level::DEBUG>(
305 fmt::format("readTempSensors: Failed reading {}, errno = {}",
306 file.path().string(), e.code().value())
307 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800308 continue;
309 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800310
311 const std::string& tempLabel = "label";
312 const std::string filePathString = file.path().string().substr(
313 0, file.path().string().length() - tempLabel.length());
Matt Spinlera26f1522021-08-25 15:50:20 -0500314
315 uint32_t fruTypeValue{0};
316 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800317 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500318 fruTypeValue = readFile<uint32_t>(filePathString + fruTypeSuffix);
319 }
320 catch (const std::system_error& e)
321 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800322 log<level::DEBUG>(
Matt Spinlera26f1522021-08-25 15:50:20 -0500323 fmt::format("readTempSensors: Failed reading {}, errno = {}",
324 filePathString + fruTypeSuffix, e.code().value())
Chicago Duanbb895cb2021-06-18 19:37:16 +0800325 .c_str());
326 continue;
327 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800328
329 std::string sensorPath =
330 OCC_SENSORS_ROOT + std::string("/temperature/");
331
332 if (fruTypeValue == VRMVdd)
333 {
334 sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp");
335 }
336 else
337 {
Matt Spinler14d14022021-08-25 15:38:29 -0500338 uint16_t type = (labelValue & 0xFF000000) >> 24;
339 uint16_t instanceID = labelValue & 0x0000FFFF;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800340
341 if (type == OCC_DIMM_TEMP_SENSOR_TYPE)
342 {
Matt Spinler8b8abee2021-08-25 15:18:21 -0500343 if (fruTypeValue == fruTypeNotAvailable)
344 {
345 // Not all DIMM related temps are available to read
346 // (no _input file in this case)
347 continue;
348 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800349 auto iter = dimmTempSensorName.find(fruTypeValue);
350 if (iter == dimmTempSensorName.end())
351 {
352 log<level::ERR>(fmt::format("readTempSensors: Fru type "
353 "error! fruTypeValue = {}) ",
354 fruTypeValue)
355 .c_str());
356 continue;
357 }
358
359 sensorPath.append("dimm" + std::to_string(instanceID) +
360 iter->second);
361 }
362 else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
363 {
364 if (fruTypeValue != processorCore)
365 {
Matt Spinlerabbcfc52021-08-25 15:22:22 -0500366 // TODO: support IO ring temp
Chicago Duanbb895cb2021-06-18 19:37:16 +0800367 continue;
368 }
369
370 sensorPath.append("proc" + std::to_string(id) + "_core" +
371 std::to_string(instanceID) + "_temp");
372 }
373 else
374 {
375 continue;
376 }
377 }
378
Matt Spinlera26f1522021-08-25 15:50:20 -0500379 uint32_t faultValue{0};
380 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800381 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500382 faultValue = readFile<uint32_t>(filePathString + faultSuffix);
383 }
384 catch (const std::system_error& e)
385 {
386 log<level::DEBUG>(
387 fmt::format("readTempSensors: Failed reading {}, errno = {}",
388 filePathString + faultSuffix, e.code().value())
389 .c_str());
390 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800391 }
392
Matt Spinlera26f1522021-08-25 15:50:20 -0500393 if (faultValue != 0)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800394 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800395 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
Matt Spinlera26f1522021-08-25 15:50:20 -0500396 sensorPath, std::numeric_limits<double>::quiet_NaN());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800397
398 open_power::occ::dbus::OccDBusSensors::getOccDBus()
Matt Spinlera26f1522021-08-25 15:50:20 -0500399 .setOperationalStatus(sensorPath, false);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800400
Matt Spinlera26f1522021-08-25 15:50:20 -0500401 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800402 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500403
404 double tempValue{0};
405
406 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800407 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500408 tempValue = readFile<double>(filePathString + inputSuffix);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800409 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500410 catch (const std::system_error& e)
411 {
412 log<level::DEBUG>(
413 fmt::format("readTempSensors: Failed reading {}, errno = {}",
414 filePathString + inputSuffix, e.code().value())
415 .c_str());
416 continue;
417 }
418
419 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
420 sensorPath, tempValue * std::pow(10, -3));
421
422 open_power::occ::dbus::OccDBusSensors::getOccDBus()
423 .setOperationalStatus(sensorPath, true);
424
425 existingSensors[sensorPath] = id;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800426 }
427 return;
428}
429
430std::optional<std::string>
431 Manager::getPowerLabelFunctionID(const std::string& value)
432{
433 // If the value is "system", then the FunctionID is "system".
434 if (value == "system")
435 {
436 return value;
437 }
438
439 // If the value is not "system", then the label value have 3 numbers, of
440 // which we only care about the middle one:
441 // <sensor id>_<function id>_<apss channel>
442 // eg: The value is "0_10_5" , then the FunctionID is "10".
443 if (value.find("_") == std::string::npos)
444 {
445 return std::nullopt;
446 }
447
448 auto powerLabelValue = value.substr((value.find("_") + 1));
449
450 if (powerLabelValue.find("_") == std::string::npos)
451 {
452 return std::nullopt;
453 }
454
455 return powerLabelValue.substr(0, powerLabelValue.find("_"));
456}
457
458void Manager::readPowerSensors(const fs::path& path, uint32_t id)
459{
Chicago Duanbb895cb2021-06-18 19:37:16 +0800460 std::regex expr{"power\\d+_label$"}; // Example: power5_label
461 for (auto& file : fs::directory_iterator(path))
462 {
463 if (!std::regex_search(file.path().string(), expr))
464 {
465 continue;
466 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800467
Matt Spinlera26f1522021-08-25 15:50:20 -0500468 std::string labelValue;
469 try
470 {
471 labelValue = readFile<std::string>(file.path());
472 }
473 catch (const std::system_error& e)
474 {
475 log<level::DEBUG>(
476 fmt::format("readPowerSensors: Failed reading {}, errno = {}",
477 file.path().string(), e.code().value())
478 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800479 continue;
480 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800481
482 auto functionID = getPowerLabelFunctionID(labelValue);
483 if (functionID == std::nullopt)
484 {
485 continue;
486 }
487
488 const std::string& tempLabel = "label";
489 const std::string filePathString = file.path().string().substr(
490 0, file.path().string().length() - tempLabel.length());
491
492 std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/");
493
494 auto iter = powerSensorName.find(*functionID);
495 if (iter == powerSensorName.end())
496 {
497 continue;
498 }
499 sensorPath.append(iter->second);
500
Matt Spinlera26f1522021-08-25 15:50:20 -0500501 double tempValue{0};
502
503 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800504 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500505 tempValue = readFile<double>(filePathString + inputSuffix);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800506 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500507 catch (const std::system_error& e)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800508 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800509 log<level::DEBUG>(
Matt Spinlera26f1522021-08-25 15:50:20 -0500510 fmt::format("readTempSensors: Failed reading {}, errno = {}",
511 filePathString + inputSuffix, e.code().value())
Chicago Duanbb895cb2021-06-18 19:37:16 +0800512 .c_str());
Matt Spinlera26f1522021-08-25 15:50:20 -0500513 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800514 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500515
516 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
517 sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3));
518
519 open_power::occ::dbus::OccDBusSensors::getOccDBus()
520 .setOperationalStatus(sensorPath, true);
521
522 existingSensors[sensorPath] = id;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800523 }
524 return;
525}
526
527void Manager::setSensorValueToNaN(uint32_t id)
528{
529 for (const auto& [sensorPath, occId] : existingSensors)
530 {
531 if (occId == id)
532 {
533 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
534 sensorPath, std::numeric_limits<double>::quiet_NaN());
535 }
536 }
537 return;
538}
539
540void Manager::getSensorValues(uint32_t id, bool masterOcc)
541{
542 const auto occ = std::string("occ-hwmon.") + std::to_string(id + 1);
543
544 fs::path fileName{OCC_HWMON_PATH + occ + "/hwmon/"};
545
546 // Need to get the hwmonXX directory name, there better only be 1 dir
547 assert(std::distance(fs::directory_iterator(fileName),
548 fs::directory_iterator{}) == 1);
549 // Now set our path to this full path, including this hwmonXX directory
550 fileName = fs::path(*fs::directory_iterator(fileName));
551
552 // Read temperature sensors
553 readTempSensors(fileName, id);
554
555 if (masterOcc)
556 {
557 // Read power sensors
558 readPowerSensors(fileName, id);
559 }
560
561 return;
562}
563#endif
564
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530565} // namespace occ
566} // namespace open_power