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