1 #pragma once 2 3 #include <phosphor-logging/log.hpp> 4 #include <sdbusplus/bus.hpp> 5 #include <sdbusplus/bus/match.hpp> 6 #include <sdeventplus/clock.hpp> 7 #include <sdeventplus/event.hpp> 8 #include <sdeventplus/utility/timer.hpp> 9 10 #include <chrono> 11 #include <deque> 12 #include <format> 13 #include <optional> 14 #include <utility> 15 16 namespace phosphor 17 { 18 namespace fan 19 { 20 namespace monitor 21 { 22 23 class Fan; 24 25 constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/"; 26 27 /** 28 * The mode fan monitor will run in: 29 * - init - only do the initialization steps 30 * - monitor - run normal monitoring algorithm 31 */ 32 enum class Mode 33 { 34 init, 35 monitor 36 }; 37 38 /** 39 * The mode that the timer is running in: 40 * - func - Transition to functional state timer 41 * - nonfunc - Transition to nonfunctional state timer 42 */ 43 enum class TimerMode 44 { 45 func, 46 nonfunc 47 }; 48 49 /** 50 * The mode that the method is running in: 51 * - time - Use a percentage based deviation 52 * - count - Run up/down count fault detection 53 */ 54 enum MethodMode 55 { 56 timebased = 0, 57 count 58 }; 59 60 /** 61 * @class TachSensor 62 * 63 * This class represents the sensor that reads a tach value. 64 * It may also support a Target, which is the property used to 65 * set a speed. Since it doesn't necessarily have a Target, it 66 * won't for sure know if it is running too slow, so it leaves 67 * that determination to other code. 68 * 69 * This class has a parent Fan object that knows about all 70 * sensors for that fan. 71 */ 72 class TachSensor 73 { 74 public: 75 TachSensor() = delete; 76 TachSensor(const TachSensor&) = delete; 77 // TachSensor is not moveable since the this pointer is used as systemd 78 // callback context. 79 TachSensor(TachSensor&&) = delete; 80 TachSensor& operator=(const TachSensor&) = delete; 81 TachSensor& operator=(TachSensor&&) = delete; 82 ~TachSensor() = default; 83 84 /** 85 * @brief Constructor 86 * 87 * @param[in] mode - mode of fan monitor 88 * @param[in] bus - the dbus object 89 * @param[in] fan - the parent fan object 90 * @param[in] id - the id of the sensor 91 * @param[in] hasTarget - if the sensor supports 92 * setting the speed 93 * @param[in] funcDelay - Delay to mark functional 94 * @param[in] interface - the interface of the target 95 * @param[in] path - the object path of the sensor target 96 * @param[in] factor - the factor of the sensor target 97 * @param[in] offset - the offset of the sensor target 98 * @param[in] method - the method of out of range 99 * @param[in] threshold - the threshold of counter method 100 * @param[in] ignoreAboveMax - whether to ignore being above max or not 101 * @param[in] timeout - Normal timeout value to use 102 * @param[in] errorDelay - Delay in seconds before creating an error 103 * or std::nullopt if no errors. 104 * @param[in] countInterval - In count mode interval 105 * 106 * @param[in] event - Event loop reference 107 */ 108 TachSensor(Mode mode, sdbusplus::bus_t& bus, Fan& fan, 109 const std::string& id, bool hasTarget, size_t funcDelay, 110 const std::string& interface, const std::string& path, 111 double factor, int64_t offset, size_t method, size_t threshold, 112 bool ignoreAboveMax, size_t timeout, 113 const std::optional<size_t>& errorDelay, size_t countInterval, 114 const sdeventplus::Event& event); 115 116 /** 117 * @brief Reads a property from the input message and stores it in value. 118 * T is the value type. 119 * 120 * Note: This can only be called once per message. 121 * 122 * @param[in] msg - the dbus message that contains the data 123 * @param[in] interface - the interface the property is on 124 * @param[in] propertName - the name of the property 125 * @param[out] value - the value to store the property value in 126 */ 127 template <typename T> 128 static void readPropertyFromMessage(sdbusplus::message_t& msg, 129 const std::string& interface, 130 const std::string& propertyName, 131 T& value) 132 { 133 std::string sensor; 134 std::map<std::string, std::variant<T>> data; 135 msg.read(sensor, data); 136 137 if (sensor.compare(interface) == 0) 138 { 139 auto propertyMap = data.find(propertyName); 140 if (propertyMap != data.end()) 141 { 142 value = std::get<T>(propertyMap->second); 143 } 144 } 145 } 146 147 /** 148 * @brief Returns the target speed value 149 */ 150 uint64_t getTarget() const; 151 152 /** 153 * @brief Returns the input speed value 154 */ 155 inline double getInput() const 156 { 157 return _tachInput; 158 } 159 160 /** 161 * @brief Returns true if sensor has a target 162 */ 163 inline bool hasTarget() const 164 { 165 return _hasTarget; 166 } 167 168 /** 169 * @brief Returns the interface of the sensor target 170 */ 171 inline std::string getInterface() const 172 { 173 return _interface; 174 } 175 176 /** 177 * @brief Returns true if sensor has a D-Bus owner 178 */ 179 inline bool hasOwner() const 180 { 181 return _hasOwner; 182 } 183 184 /** 185 * @brief sets D-Bus owner status 186 * 187 * @param[in] val - new owner status 188 */ 189 inline void setOwner(bool val) 190 { 191 _hasOwner = val; 192 } 193 194 /** 195 * @brief Returns the factor of the sensor target 196 */ 197 inline double getFactor() const 198 { 199 return _factor; 200 } 201 202 /** 203 * @brief Returns a reference to the sensor's Fan object 204 */ 205 inline Fan& getFan() const 206 { 207 return _fan; 208 } 209 210 /** 211 * @brief Returns the offset of the sensor target 212 */ 213 inline int64_t getOffset() const 214 { 215 return _offset; 216 } 217 218 /** 219 * @brief Returns the method of out of range 220 */ 221 inline size_t getMethod() const 222 { 223 return _method; 224 } 225 226 /** 227 * @brief Returns the threshold of count method 228 */ 229 inline size_t getThreshold() const 230 { 231 return _threshold; 232 } 233 234 /** 235 * Set the sensor faulted counter 236 */ 237 void setCounter(bool count); 238 239 /** 240 * @brief Returns the sensor faulted count 241 */ 242 inline size_t getCounter() const 243 { 244 return _counter; 245 } 246 247 /** 248 * Returns true if the hardware behind this 249 * sensor is considered working OK/functional. 250 */ 251 inline bool functional() const 252 { 253 return _functional; 254 } 255 256 /** 257 * Set the functional status and update inventory to match 258 * 259 * @param[in] functional - The new state 260 * @param[in] skipErrorTimer - If setting the sensor to 261 * nonfunctional, don't start the error timer. 262 */ 263 void setFunctional(bool functional, bool skipErrorTimer = false); 264 265 /** 266 * @brief Says if the timer is running or not 267 * 268 * @return bool - if timer is currently running 269 */ 270 inline bool timerRunning() 271 { 272 return _timer.isEnabled(); 273 } 274 275 /** 276 * @brief Stops the timer when the given mode differs and starts 277 * the associated timer for the mode given if not already running 278 * 279 * @param[in] mode - mode of timer to start 280 */ 281 void startTimer(TimerMode mode); 282 283 /** 284 * @brief Stops the timer 285 */ 286 inline void stopTimer() 287 { 288 phosphor::logging::log<phosphor::logging::level::DEBUG>( 289 std::format("Stop running timer on tach sensor {}.", _name) 290 .c_str()); 291 _timer.setEnabled(false); 292 } 293 294 /** 295 * @brief Says if the count timer is running 296 * 297 * @return bool - If count timer running 298 */ 299 inline bool countTimerRunning() const 300 { 301 return _countTimer && _countTimer->isEnabled(); 302 } 303 304 /** 305 * @brief Stops the count timer 306 */ 307 void stopCountTimer(); 308 309 /** 310 * @brief Starts the count timer 311 */ 312 void startCountTimer(); 313 314 /** 315 * @brief Return the given timer mode's delay time 316 * 317 * @param[in] mode - mode of timer to get delay time for 318 */ 319 std::chrono::microseconds getDelay(TimerMode mode); 320 321 /** 322 * Returns the sensor name 323 */ 324 inline const std::string& name() const 325 { 326 return _name; 327 }; 328 329 /** 330 * @brief Says if the error timer is running 331 * 332 * @return bool - If the timer is running 333 */ 334 bool errorTimerRunning() const 335 { 336 if (_errorTimer && _errorTimer->isEnabled()) 337 { 338 return true; 339 } 340 return false; 341 } 342 343 /** 344 * @brief Get the current allowed range of speeds 345 * 346 * @param[in] lowerDeviation - The configured lower deviation(in percent) 347 * allowed 348 * @param[in] upperDeviation - The configured upper deviation(in percent) 349 * allowed 350 * 351 * @return pair - Min/Max(optional) range of speeds allowed 352 */ 353 std::pair<uint64_t, std::optional<uint64_t>> 354 getRange(const size_t lowerDeviation, 355 const size_t upperDeviation) const; 356 357 /** 358 * @brief Processes the current state of the sensor 359 */ 360 void processState(); 361 362 /** 363 * @brief Resets the monitoring method of the sensor 364 */ 365 void resetMethod(); 366 367 /** 368 * @brief Refreshes the tach input and target values by 369 * reading them from D-Bus. 370 */ 371 void updateTachAndTarget(); 372 373 /** 374 * @brief return the previous tach values 375 */ 376 const std::deque<uint64_t>& getPrevTach() const 377 { 378 return _prevTachs; 379 } 380 381 /** 382 * @brief return the previous target values 383 */ 384 const std::deque<uint64_t>& getPrevTarget() const 385 { 386 return _prevTargets; 387 } 388 389 private: 390 /** 391 * @brief Returns the match string to use for matching 392 * on a properties changed signal. 393 */ 394 std::string getMatchString(const std::optional<std::string> path, 395 const std::string& interface); 396 397 /** 398 * @brief Reads the Target property and stores in _tachTarget. 399 * Also calls Fan::tachChanged(). 400 * 401 * @param[in] msg - the dbus message 402 */ 403 void handleTargetChange(sdbusplus::message_t& msg); 404 405 /** 406 * @brief Reads the Value property and stores in _tachInput. 407 * Also calls Fan::tachChanged(). 408 * 409 * @param[in] msg - the dbus message 410 */ 411 void handleTachChange(sdbusplus::message_t& msg); 412 413 /** 414 * @brief Updates the Functional property in the inventory 415 * for this tach sensor based on the value passed in. 416 * 417 * @param[in] functional - If the Functional property should 418 * be set to true or false. 419 */ 420 void updateInventory(bool functional); 421 422 /** 423 * @brief the dbus object 424 */ 425 sdbusplus::bus_t& _bus; 426 427 /** 428 * @brief Reference to the parent Fan object 429 */ 430 Fan& _fan; 431 432 /** 433 * @brief The name of the sensor, including the full path 434 * 435 * For example /xyz/openbmc_project/sensors/fan_tach/fan0 436 */ 437 const std::string _name; 438 439 /** 440 * @brief The inventory name of the sensor, including the full path 441 */ 442 const std::string _invName; 443 444 /** 445 * @brief If functional (not too slow). The parent 446 * fan object sets this. 447 */ 448 bool _functional = true; 449 450 /** 451 * @brief If the sensor has a Target property (can set speed) 452 */ 453 const bool _hasTarget; 454 455 /** 456 * @brief If the sensor has a D-Bus owner 457 */ 458 bool _hasOwner = true; 459 460 /** 461 * @brief Amount of time to delay updating to functional 462 */ 463 const size_t _funcDelay; 464 465 /** 466 * @brief The interface that the target implements 467 */ 468 const std::string _interface; 469 470 /** 471 * @brief The object path to set sensor's target 472 */ 473 const std::string _path; 474 475 /** 476 * @brief The factor of target to get fan rpm 477 */ 478 const double _factor; 479 480 /** 481 * @brief The offset of target to get fan rpm 482 */ 483 const int64_t _offset; 484 485 /** 486 * @brief The method of out of range 487 */ 488 const size_t _method; 489 490 /** 491 * @brief The threshold for count method 492 */ 493 const size_t _threshold; 494 495 /** 496 * @brief Whether to ignore being above the max or not 497 */ 498 const bool _ignoreAboveMax; 499 500 /** 501 * @brief The counter for count method 502 */ 503 size_t _counter = 0; 504 505 /** 506 * @brief The input speed, from the Value dbus property 507 */ 508 double _tachInput = 0; 509 510 /** 511 * @brief The current target speed, from the Target dbus property 512 * (if applicable) 513 */ 514 uint64_t _tachTarget = 0; 515 516 /** 517 * @brief The timeout value to use 518 */ 519 const size_t _timeout; 520 521 /** 522 * @brief Mode that current timer is in 523 */ 524 TimerMode _timerMode; 525 526 /** 527 * The timer object 528 */ 529 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer; 530 531 /** 532 * @brief The match object for the Value properties changed signal 533 */ 534 std::unique_ptr<sdbusplus::bus::match_t> tachSignal; 535 536 /** 537 * @brief The match object for the Target properties changed signal 538 */ 539 std::unique_ptr<sdbusplus::bus::match_t> targetSignal; 540 541 /** 542 * @brief The number of seconds to wait between a sensor being set 543 * to nonfunctional and creating an error for it. 544 * 545 * If std::nullopt, no errors will be created. 546 */ 547 const std::optional<size_t> _errorDelay; 548 549 /** 550 * @brief The timer that uses _errorDelay. When it expires an error 551 * will be created for a faulted fan sensor (rotor). 552 * 553 * If _errorDelay is std::nullopt, then this won't be created. 554 */ 555 std::unique_ptr< 556 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>> 557 _errorTimer; 558 559 /** 560 * @brief The interval, in seconds, to use for the timer that runs 561 * the checks when using the 'count' method. 562 */ 563 size_t _countInterval; 564 565 /** 566 * @brief The timer used by the 'count' method for determining 567 * functional status. 568 */ 569 std::unique_ptr< 570 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>> 571 _countTimer; 572 573 /** 574 * @brief record of previous targets 575 */ 576 std::deque<uint64_t> _prevTargets; 577 578 /** 579 * @brief record of previous tach readings 580 */ 581 std::deque<uint64_t> _prevTachs; 582 }; 583 584 } // namespace monitor 585 } // namespace fan 586 } // namespace phosphor 587