1 /** 2 * Copyright © 2017 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "tach_sensor.hpp" 17 18 #include "fan.hpp" 19 #include "sdbusplus.hpp" 20 #include "utility.hpp" 21 22 #include <phosphor-logging/elog.hpp> 23 #include <phosphor-logging/log.hpp> 24 25 #include <filesystem> 26 #include <format> 27 #include <functional> 28 #include <optional> 29 #include <utility> 30 31 namespace phosphor 32 { 33 namespace fan 34 { 35 namespace monitor 36 { 37 38 constexpr auto FAN_TARGET_PROPERTY = "Target"; 39 constexpr auto FAN_VALUE_PROPERTY = "Value"; 40 constexpr auto MAX_PREV_TACHS = 8; 41 constexpr auto MAX_PREV_TARGETS = 8; 42 43 namespace fs = std::filesystem; 44 using InternalFailure = 45 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 46 47 /** 48 * @brief Helper function to read a property 49 * 50 * @param[in] interface - the interface the property is on 51 * @param[in] propertName - the name of the property 52 * @param[in] path - the dbus path 53 * @param[in] bus - the dbus object 54 * @param[out] value - filled in with the property value 55 */ 56 template <typename T> 57 static void 58 readProperty(const std::string& interface, const std::string& propertyName, 59 const std::string& path, sdbusplus::bus_t& bus, T& value) 60 { 61 try 62 { 63 value = util::SDBusPlus::getProperty<T>(bus, path, interface, 64 propertyName); 65 } 66 catch (const std::exception& e) 67 { 68 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 69 } 70 } 71 72 TachSensor::TachSensor([[maybe_unused]] Mode mode, sdbusplus::bus_t& bus, 73 Fan& fan, const std::string& id, bool hasTarget, 74 size_t funcDelay, const std::string& interface, 75 const std::string& path, double factor, int64_t offset, 76 size_t method, size_t threshold, bool ignoreAboveMax, 77 size_t timeout, const std::optional<size_t>& errorDelay, 78 size_t countInterval, const sdeventplus::Event& event) : 79 _bus(bus), 80 _fan(fan), _name(FAN_SENSOR_PATH + id), 81 _invName(fs::path(fan.getName()) / id), _hasTarget(hasTarget), 82 _funcDelay(funcDelay), _interface(interface), _path(path), _factor(factor), 83 _offset(offset), _method(method), _threshold(threshold), 84 _ignoreAboveMax(ignoreAboveMax), _timeout(timeout), 85 _timerMode(TimerMode::func), 86 _timer(event, std::bind(&Fan::updateState, &fan, std::ref(*this))), 87 _errorDelay(errorDelay), _countInterval(countInterval) 88 { 89 _prevTachs.resize(MAX_PREV_TACHS); 90 91 if (_hasTarget) 92 { 93 _prevTargets.resize(MAX_PREV_TARGETS); 94 } 95 96 updateInventory(_functional); 97 98 // Load in current Target and Input values when entering monitor mode 99 #ifndef MONITOR_USE_JSON 100 if (mode != Mode::init) 101 { 102 #endif 103 try 104 { 105 updateTachAndTarget(); 106 } 107 catch (const std::exception& e) 108 { 109 // Until the parent Fan's monitor-ready timer expires, the 110 // object can be functional with a missing D-bus sensor. 111 } 112 113 auto match = getMatchString(std::nullopt, util::FAN_SENSOR_VALUE_INTF); 114 115 tachSignal = std::make_unique<sdbusplus::bus::match_t>( 116 _bus, match.c_str(), 117 [this](auto& msg) { this->handleTachChange(msg); }); 118 119 if (_hasTarget) 120 { 121 if (_path.empty()) 122 { 123 match = getMatchString(std::nullopt, _interface); 124 } 125 else 126 { 127 match = getMatchString(_path, _interface); 128 } 129 targetSignal = std::make_unique<sdbusplus::bus::match_t>( 130 _bus, match.c_str(), 131 [this](auto& msg) { this->handleTargetChange(msg); }); 132 } 133 134 if (_errorDelay) 135 { 136 _errorTimer = std::make_unique< 137 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 138 event, std::bind(&Fan::sensorErrorTimerExpired, &fan, 139 std::ref(*this))); 140 } 141 142 if (_method == MethodMode::count) 143 { 144 _countTimer = std::make_unique< 145 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 146 event, 147 std::bind(&Fan::countTimerExpired, &fan, std::ref(*this))); 148 } 149 #ifndef MONITOR_USE_JSON 150 } 151 #endif 152 } 153 154 void TachSensor::updateTachAndTarget() 155 { 156 _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>( 157 _bus, _name, util::FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY); 158 159 if (_hasTarget) 160 { 161 if (_path.empty()) 162 { 163 // Target path is optional 164 readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, 165 _tachTarget); 166 } 167 else 168 { 169 readProperty(_interface, FAN_TARGET_PROPERTY, _path, _bus, 170 _tachTarget); 171 } 172 173 // record previous target value 174 if (_prevTargets.front() != _tachTarget) 175 { 176 _prevTargets.push_front(_tachTarget); 177 178 _prevTargets.pop_back(); 179 } 180 } 181 182 // record previous tach value 183 _prevTachs.push_front(_tachInput); 184 185 _prevTachs.pop_back(); 186 } 187 188 std::string TachSensor::getMatchString(const std::optional<std::string> path, 189 const std::string& interface) 190 { 191 if (path) 192 { 193 return sdbusplus::bus::match::rules::propertiesChanged(path.value(), 194 interface); 195 } 196 return sdbusplus::bus::match::rules::propertiesChanged(_name, interface); 197 } 198 199 uint64_t TachSensor::getTarget() const 200 { 201 if (!_hasTarget) 202 { 203 return _fan.findTargetSpeed(); 204 } 205 return _tachTarget; 206 } 207 208 std::pair<uint64_t, std::optional<uint64_t>> 209 TachSensor::getRange(const size_t lowerDeviation, 210 const size_t upperDeviation) const 211 { 212 // Determine min/max range applying the deviation 213 uint64_t min = getTarget() * (100 - lowerDeviation) / 100; 214 std::optional<uint64_t> max = getTarget() * (100 + upperDeviation) / 100; 215 216 // Adjust the min/max range by applying the factor & offset 217 min = min * _factor + _offset; 218 max = max.value() * _factor + _offset; 219 220 if (_ignoreAboveMax) 221 { 222 max = std::nullopt; 223 } 224 225 return std::make_pair(min, max); 226 } 227 228 void TachSensor::processState() 229 { 230 // This function runs from inside trust::Manager::checkTrust(), which, 231 // for sensors using the count method, runs right before process() 232 // is called anyway inside Fan::countTimerExpired() so don't call 233 // it now if using that method. 234 if (_method == MethodMode::timebased) 235 { 236 _fan.process(*this); 237 } 238 } 239 240 void TachSensor::resetMethod() 241 { 242 switch (_method) 243 { 244 case MethodMode::timebased: 245 if (timerRunning()) 246 { 247 stopTimer(); 248 } 249 break; 250 case MethodMode::count: 251 if (_functional) 252 { 253 _counter = 0; 254 } 255 else 256 { 257 _counter = _threshold; 258 } 259 break; 260 } 261 } 262 263 void TachSensor::setFunctional(bool functional, bool skipErrorTimer) 264 { 265 _functional = functional; 266 updateInventory(_functional); 267 268 if (!_errorTimer) 269 { 270 return; 271 } 272 273 if (!_functional) 274 { 275 if (_fan.present() && !skipErrorTimer) 276 { 277 _errorTimer->restartOnce(std::chrono::seconds(*_errorDelay)); 278 } 279 } 280 else if (_errorTimer->isEnabled()) 281 { 282 _errorTimer->setEnabled(false); 283 } 284 } 285 286 void TachSensor::handleTargetChange(sdbusplus::message_t& msg) 287 { 288 readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget); 289 290 // Check all tach sensors on the fan against the target 291 _fan.tachChanged(); 292 293 // record previous target value 294 if (_prevTargets.front() != _tachTarget) 295 { 296 _prevTargets.push_front(_tachTarget); 297 298 _prevTargets.pop_back(); 299 } 300 } 301 302 void TachSensor::handleTachChange(sdbusplus::message_t& msg) 303 { 304 readPropertyFromMessage(msg, util::FAN_SENSOR_VALUE_INTF, 305 FAN_VALUE_PROPERTY, _tachInput); 306 307 // Check just this sensor against the target 308 _fan.tachChanged(*this); 309 310 // record previous tach value 311 _prevTachs.push_front(_tachInput); 312 313 _prevTachs.pop_back(); 314 } 315 316 void TachSensor::startTimer(TimerMode mode) 317 { 318 using namespace std::chrono; 319 320 if (!timerRunning() || mode != _timerMode) 321 { 322 log<level::DEBUG>( 323 std::format("Start timer({}) on tach sensor {}. [delay = {}s]", 324 static_cast<int>(mode), _name, 325 duration_cast<seconds>(getDelay(mode)).count()) 326 .c_str()); 327 _timer.restartOnce(getDelay(mode)); 328 _timerMode = mode; 329 } 330 } 331 332 std::chrono::microseconds TachSensor::getDelay(TimerMode mode) 333 { 334 using namespace std::chrono; 335 336 switch (mode) 337 { 338 case TimerMode::nonfunc: 339 return duration_cast<microseconds>(seconds(_timeout)); 340 case TimerMode::func: 341 return duration_cast<microseconds>(seconds(_funcDelay)); 342 default: 343 // Log an internal error for undefined timer mode 344 log<level::ERR>("Undefined timer mode", 345 entry("TIMER_MODE=%u", mode)); 346 elog<InternalFailure>(); 347 return duration_cast<microseconds>(seconds(0)); 348 } 349 } 350 351 void TachSensor::setCounter(bool count) 352 { 353 if (count) 354 { 355 if (_counter < _threshold) 356 { 357 ++_counter; 358 log<level::DEBUG>( 359 std::format( 360 "Incremented error counter on {} to {} (threshold {})", 361 _name, _counter, _threshold) 362 .c_str()); 363 } 364 } 365 else 366 { 367 if (_counter > 0) 368 { 369 --_counter; 370 log<level::DEBUG>( 371 std::format( 372 "Decremented error counter on {} to {} (threshold {})", 373 _name, _counter, _threshold) 374 .c_str()); 375 } 376 } 377 } 378 379 void TachSensor::startCountTimer() 380 { 381 if (_countTimer) 382 { 383 log<level::DEBUG>( 384 std::format("Starting count timer on sensor {}", _name).c_str()); 385 _countTimer->restart(std::chrono::seconds(_countInterval)); 386 } 387 } 388 389 void TachSensor::stopCountTimer() 390 { 391 if (_countTimer && _countTimer->isEnabled()) 392 { 393 log<level::DEBUG>( 394 std::format("Stopping count timer on tach sensor {}.", _name) 395 .c_str()); 396 _countTimer->setEnabled(false); 397 } 398 } 399 400 void TachSensor::updateInventory(bool functional) 401 { 402 auto objectMap = 403 util::getObjMap<bool>(_invName, util::OPERATIONAL_STATUS_INTF, 404 util::FUNCTIONAL_PROPERTY, functional); 405 406 auto response = util::SDBusPlus::callMethod( 407 _bus, util::INVENTORY_SVC, util::INVENTORY_PATH, util::INVENTORY_INTF, 408 "Notify", objectMap); 409 410 if (response.is_method_error()) 411 { 412 log<level::ERR>("Error in notify update of tach sensor inventory"); 413 } 414 } 415 416 } // namespace monitor 417 } // namespace fan 418 } // namespace phosphor 419