xref: /openbmc/pldm/platform-mc/sensor_manager.cpp (revision 70eca96bebade89aceab0762e3615b816e29fba7)
1 #include "sensor_manager.hpp"
2 
3 #include "manager.hpp"
4 #include "terminus_manager.hpp"
5 
6 #include <phosphor-logging/lg2.hpp>
7 
8 #include <exception>
9 
10 namespace pldm
11 {
12 namespace platform_mc
13 {
14 
SensorManager(sdeventplus::Event & event,TerminusManager & terminusManager,TerminiMapper & termini,Manager * manager)15 SensorManager::SensorManager(sdeventplus::Event& event,
16                              TerminusManager& terminusManager,
17                              TerminiMapper& termini, Manager* manager) :
18     event(event), terminusManager(terminusManager), termini(termini),
19     pollingTime(SENSOR_POLLING_TIME), manager(manager)
20 {}
21 
startPolling(pldm_tid_t tid)22 void SensorManager::startPolling(pldm_tid_t tid)
23 {
24     if (!termini.contains(tid))
25     {
26         return;
27     }
28 
29     /* tid already initializes roundRobinSensors list */
30     if (sensorPollTimers.contains(tid))
31     {
32         lg2::info("Terminus ID {TID}: sensor poll timer already exists.", "TID",
33                   tid);
34         return;
35     }
36 
37     roundRobinSensorItMap[tid] = 0;
38 
39     updateAvailableState(tid, true);
40 
41     sensorPollTimers[tid] = std::make_unique<sdbusplus::Timer>(
42         event.get(), [this, tid] { this->doSensorPolling(tid); });
43 
44     startSensorPollTimer(tid);
45 }
46 
startSensorPollTimer(pldm_tid_t tid)47 void SensorManager::startSensorPollTimer(pldm_tid_t tid)
48 {
49     try
50     {
51         if (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning())
52         {
53             sensorPollTimers[tid]->start(
54                 duration_cast<std::chrono::milliseconds>(
55                     std::chrono::milliseconds(pollingTime)),
56                 true);
57         }
58     }
59     catch (const std::exception& e)
60     {
61         lg2::error(
62             "Terminus ID {TID}: Failed to start sensor polling timer. Exception: {EXCEPTION}",
63             "TID", tid, "EXCEPTION", e);
64         return;
65     }
66 }
67 
disableTerminusSensors(pldm_tid_t tid)68 void SensorManager::disableTerminusSensors(pldm_tid_t tid)
69 {
70     if (!termini.contains(tid))
71     {
72         return;
73     }
74 
75     // numeric sensor
76     auto terminus = termini[tid];
77     if (!terminus)
78     {
79         return;
80     }
81 
82     for (auto& sensor : terminus->numericSensors)
83     {
84         sensor->updateReading(true, false,
85                               std::numeric_limits<double>::quiet_NaN());
86     }
87 }
88 
stopPolling(pldm_tid_t tid)89 void SensorManager::stopPolling(pldm_tid_t tid)
90 {
91     /* Stop polling timer */
92     if (sensorPollTimers.contains(tid))
93     {
94         sensorPollTimers[tid]->stop();
95         sensorPollTimers.erase(tid);
96     }
97 
98     roundRobinSensorItMap.erase(tid);
99 
100     if (doSensorPollingTaskHandles.contains(tid))
101     {
102         auto& [scope, rcOpt] = doSensorPollingTaskHandles[tid];
103         scope.request_stop();
104         doSensorPollingTaskHandles.erase(tid);
105     }
106 
107     availableState.erase(tid);
108 }
109 
doSensorPolling(pldm_tid_t tid)110 void SensorManager::doSensorPolling(pldm_tid_t tid)
111 {
112     auto it = doSensorPollingTaskHandles.find(tid);
113     if (it != doSensorPollingTaskHandles.end())
114     {
115         auto& [scope, rcOpt] = it->second;
116         if (!rcOpt.has_value())
117         {
118             return;
119         }
120         doSensorPollingTaskHandles.erase(tid);
121     }
122 
123     auto& [scope, rcOpt] =
124         doSensorPollingTaskHandles
125             .emplace(std::piecewise_construct, std::forward_as_tuple(tid),
126                      std::forward_as_tuple())
127             .first->second;
128     scope.spawn(
129         stdexec::just() | stdexec::let_value([this, &rcOpt,
130                                               tid] -> exec::task<void> {
131             auto res =
132                 co_await stdexec::stopped_as_optional(doSensorPollingTask(tid));
133             if (res.has_value())
134             {
135                 rcOpt = *res;
136             }
137             else
138             {
139                 lg2::info("Stopped polling for Terminus ID {TID}", "TID", tid);
140                 try
141                 {
142                     if (sensorPollTimers.contains(tid) &&
143                         sensorPollTimers[tid] &&
144                         sensorPollTimers[tid]->isRunning())
145                     {
146                         sensorPollTimers[tid]->stop();
147                     }
148                 }
149                 catch (const std::exception& e)
150                 {
151                     lg2::error(
152                         "Terminus ID {TID}: Failed to stop polling timer. Exception: {EXCEPTION}",
153                         "TID", tid, "EXCEPTION", e);
154                 }
155                 rcOpt = PLDM_SUCCESS;
156             }
157         }),
158         exec::default_task_context<void>(exec::inline_scheduler{}));
159 }
160 
doSensorPollingTask(pldm_tid_t tid)161 exec::task<int> SensorManager::doSensorPollingTask(pldm_tid_t tid)
162 {
163     uint64_t t0 = 0;
164     uint64_t t1 = 0;
165     uint64_t elapsed = 0;
166     uint64_t pollingTimeInUsec = pollingTime * 1000;
167     uint8_t rc = PLDM_SUCCESS;
168 
169     do
170     {
171         if ((!sensorPollTimers.contains(tid)) ||
172             (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()))
173         {
174             co_return PLDM_ERROR;
175         }
176 
177         sd_event_now(event.get(), CLOCK_MONOTONIC, &t0);
178 
179         /**
180          * Terminus is not available for PLDM request.
181          * The terminus manager will trigger recovery process to recovery the
182          * communication between the local terminus and the remote terminus.
183          * The sensor polling should be stopped while recovering the
184          * communication.
185          */
186         if (!getAvailableState(tid))
187         {
188             lg2::info(
189                 "Terminus ID {TID} is not available for PLDM request from {NOW}.",
190                 "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
191             co_await stdexec::just_stopped();
192         }
193 
194         if (!termini.contains(tid))
195         {
196             co_return PLDM_SUCCESS;
197         }
198 
199         auto& terminus = termini[tid];
200         if (!terminus)
201         {
202             lg2::info(
203                 "Terminus ID {TID} does not have a valid Terminus object {NOW}.",
204                 "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
205             co_return PLDM_ERROR;
206         }
207 
208         if (manager && terminus->pollEvent)
209         {
210             co_await manager->pollForPlatformEvent(
211                 tid, terminus->pollEventId, terminus->pollDataTransferHandle);
212         }
213 
214         if (manager && (!terminus->pollEvent))
215         {
216             co_await manager->oemPollForPlatformEvent(tid);
217         }
218 
219         sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
220 
221         auto& numericSensors = terminus->numericSensors;
222         auto toBeUpdated = numericSensors.size();
223 
224         if (!roundRobinSensorItMap.contains(tid))
225         {
226             lg2::info(
227                 "Terminus ID {TID} does not have a round robin sensor iteration {NOW}.",
228                 "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
229             co_return PLDM_ERROR;
230         }
231         auto& sensorIt = roundRobinSensorItMap[tid];
232 
233         while (((t1 - t0) < pollingTimeInUsec) && (toBeUpdated > 0))
234         {
235             if (!getAvailableState(tid))
236             {
237                 lg2::info(
238                     "Terminus ID {TID} is not available for PLDM request from {NOW}.",
239                     "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
240                 co_await stdexec::just_stopped();
241             }
242 
243             if (sensorIt >= numericSensors.size())
244             {
245                 sensorIt = 0;
246             }
247 
248             auto sensor = numericSensors[sensorIt];
249 
250             sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
251             elapsed = t1 - sensor->timeStamp;
252             if ((sensor->updateTime <= elapsed) || (!sensor->timeStamp))
253             {
254                 rc = co_await getSensorReading(sensor);
255 
256                 if ((!sensorPollTimers.contains(tid)) ||
257                     (sensorPollTimers[tid] &&
258                      !sensorPollTimers[tid]->isRunning()))
259                 {
260                     co_return PLDM_ERROR;
261                 }
262                 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
263                 if (rc == PLDM_SUCCESS)
264                 {
265                     sensor->timeStamp = t1;
266                 }
267                 else
268                 {
269                     lg2::error(
270                         "Failed to get sensor value for terminus {TID}, error: {RC}",
271                         "TID", tid, "RC", rc);
272                 }
273             }
274 
275             toBeUpdated--;
276             sensorIt++;
277 
278             sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
279         }
280 
281         sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
282     } while ((t1 - t0) >= pollingTimeInUsec);
283 
284     co_return PLDM_SUCCESS;
285 }
286 
getSensorReading(std::shared_ptr<NumericSensor> sensor)287 exec::task<int> SensorManager::getSensorReading(
288     std::shared_ptr<NumericSensor> sensor)
289 {
290     if (!sensor)
291     {
292         lg2::error("Call `getSensorReading` with null `sensor` pointer.");
293         co_return PLDM_ERROR_INVALID_DATA;
294     }
295 
296     auto tid = sensor->tid;
297     auto sensorId = sensor->sensorId;
298     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_SENSOR_READING_REQ_BYTES);
299     auto requestMsg = new (request.data()) pldm_msg;
300     auto rc = encode_get_sensor_reading_req(0, sensorId, false, requestMsg);
301     if (rc)
302     {
303         lg2::error(
304             "Failed to encode request GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.",
305             "TID", tid, "ID", sensorId, "RC", rc);
306         co_return rc;
307     }
308 
309     if (!getAvailableState(tid))
310     {
311         lg2::info(
312             "Terminus ID {TID} is not available for PLDM request from {NOW}.",
313             "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
314         co_await stdexec::just_stopped();
315     }
316 
317     const pldm_msg* responseMsg = nullptr;
318     size_t responseLen = 0;
319     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
320                                                   &responseLen);
321     if (rc)
322     {
323         lg2::error(
324             "Failed to send GetSensorReading message for terminus {TID}, sensor Id {ID}, error {RC}",
325             "TID", tid, "ID", sensorId, "RC", rc);
326         co_return rc;
327     }
328 
329     if ((!sensorPollTimers.contains(tid)) ||
330         (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()))
331     {
332         co_return PLDM_ERROR;
333     }
334 
335     uint8_t completionCode = PLDM_SUCCESS;
336     uint8_t sensorDataSize = PLDM_SENSOR_DATA_SIZE_SINT32;
337     uint8_t sensorOperationalState = 0;
338     uint8_t sensorEventMessageEnable = 0;
339     uint8_t presentState = 0;
340     uint8_t previousState = 0;
341     uint8_t eventState = 0;
342     union_sensor_data_size presentReading;
343     rc = decode_get_sensor_reading_resp(
344         responseMsg, responseLen, &completionCode, &sensorDataSize,
345         &sensorOperationalState, &sensorEventMessageEnable, &presentState,
346         &previousState, &eventState,
347         reinterpret_cast<uint8_t*>(&presentReading));
348     if (rc)
349     {
350         lg2::error(
351             "Failed to decode response GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.",
352             "TID", tid, "ID", sensorId, "RC", rc);
353         sensor->handleErrGetSensorReading();
354         co_return rc;
355     }
356 
357     if (completionCode != PLDM_SUCCESS)
358     {
359         lg2::error(
360             "Error : GetSensorReading for terminus ID {TID}, sensor Id {ID}, complete code {CC}.",
361             "TID", tid, "ID", sensorId, "CC", completionCode);
362         co_return completionCode;
363     }
364 
365     double value = std::numeric_limits<double>::quiet_NaN();
366     switch (sensorOperationalState)
367     {
368         case PLDM_SENSOR_ENABLED:
369             break;
370         case PLDM_SENSOR_DISABLED:
371             sensor->updateReading(false, true, value);
372             co_return completionCode;
373         case PLDM_SENSOR_FAILED:
374             sensor->updateReading(true, false, value);
375             co_return completionCode;
376         case PLDM_SENSOR_UNAVAILABLE:
377         default:
378             sensor->updateReading(false, false, value);
379             co_return completionCode;
380     }
381 
382     switch (sensorDataSize)
383     {
384         case PLDM_SENSOR_DATA_SIZE_UINT8:
385             value = static_cast<double>(presentReading.value_u8);
386             break;
387         case PLDM_SENSOR_DATA_SIZE_SINT8:
388             value = static_cast<double>(presentReading.value_s8);
389             break;
390         case PLDM_SENSOR_DATA_SIZE_UINT16:
391             value = static_cast<double>(presentReading.value_u16);
392             break;
393         case PLDM_SENSOR_DATA_SIZE_SINT16:
394             value = static_cast<double>(presentReading.value_s16);
395             break;
396         case PLDM_SENSOR_DATA_SIZE_UINT32:
397             value = static_cast<double>(presentReading.value_u32);
398             break;
399         case PLDM_SENSOR_DATA_SIZE_SINT32:
400             value = static_cast<double>(presentReading.value_s32);
401             break;
402         default:
403             value = std::numeric_limits<double>::quiet_NaN();
404             break;
405     }
406 
407     sensor->updateReading(true, true, value);
408     co_return completionCode;
409 }
410 
411 } // namespace platform_mc
412 } // namespace pldm
413