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 = 64 util::SDBusPlus::getProperty<T>(bus, path, interface, 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), _fan(fan), _name(FAN_SENSOR_PATH + id), 80 _invName(fs::path(fan.getName()) / id), _hasTarget(hasTarget), 81 _funcDelay(funcDelay), _interface(interface), _path(path), _factor(factor), 82 _offset(offset), _method(method), _threshold(threshold), 83 _ignoreAboveMax(ignoreAboveMax), _timeout(timeout), 84 _timerMode(TimerMode::func), 85 _timer(event, std::bind(&Fan::updateState, &fan, std::ref(*this))), 86 _errorDelay(errorDelay), _countInterval(countInterval) 87 { 88 _prevTachs.resize(MAX_PREV_TACHS); 89 90 if (_hasTarget) 91 { 92 _prevTargets.resize(MAX_PREV_TARGETS); 93 } 94 95 updateInventory(_functional); 96 97 // Load in current Target and Input values when entering monitor mode 98 #ifndef MONITOR_USE_JSON 99 if (mode != Mode::init) 100 { 101 #endif 102 try 103 { 104 updateTachAndTarget(); 105 } 106 catch (const std::exception& e) 107 { 108 // Until the parent Fan's monitor-ready timer expires, the 109 // object can be functional with a missing D-bus sensor. 110 } 111 112 auto match = getMatchString(std::nullopt, util::FAN_SENSOR_VALUE_INTF); 113 114 tachSignal = std::make_unique<sdbusplus::bus::match_t>( 115 _bus, match.c_str(), 116 [this](auto& msg) { this->handleTachChange(msg); }); 117 118 if (_hasTarget) 119 { 120 if (_path.empty()) 121 { 122 match = getMatchString(std::nullopt, _interface); 123 } 124 else 125 { 126 match = getMatchString(_path, _interface); 127 } 128 targetSignal = std::make_unique<sdbusplus::bus::match_t>( 129 _bus, match.c_str(), 130 [this](auto& msg) { this->handleTargetChange(msg); }); 131 } 132 133 if (_errorDelay) 134 { 135 _errorTimer = std::make_unique< 136 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 137 event, std::bind(&Fan::sensorErrorTimerExpired, &fan, 138 std::ref(*this))); 139 } 140 141 if (_method == MethodMode::count) 142 { 143 _countTimer = std::make_unique< 144 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 145 event, 146 std::bind(&Fan::countTimerExpired, &fan, std::ref(*this))); 147 } 148 #ifndef MONITOR_USE_JSON 149 } 150 #endif 151 } 152 153 void TachSensor::updateTachAndTarget() 154 { 155 _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>( 156 _bus, _name, util::FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY); 157 158 if (_hasTarget) 159 { 160 if (_path.empty()) 161 { 162 // Target path is optional 163 readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, 164 _tachTarget); 165 } 166 else 167 { 168 readProperty(_interface, FAN_TARGET_PROPERTY, _path, _bus, 169 _tachTarget); 170 } 171 172 // record previous target value 173 if (_prevTargets.front() != _tachTarget) 174 { 175 _prevTargets.push_front(_tachTarget); 176 177 _prevTargets.pop_back(); 178 } 179 } 180 181 // record previous tach value 182 _prevTachs.push_front(_tachInput); 183 184 _prevTachs.pop_back(); 185 } 186 187 std::string TachSensor::getMatchString(const std::optional<std::string> path, 188 const std::string& interface) 189 { 190 if (path) 191 { 192 return sdbusplus::bus::match::rules::propertiesChanged( 193 path.value(), interface); 194 } 195 return sdbusplus::bus::match::rules::propertiesChanged(_name, interface); 196 } 197 198 uint64_t TachSensor::getTarget() const 199 { 200 if (!_hasTarget) 201 { 202 return _fan.findTargetSpeed(); 203 } 204 return _tachTarget; 205 } 206 207 std::pair<uint64_t, std::optional<uint64_t>> TachSensor::getRange( 208 const size_t lowerDeviation, const size_t upperDeviation) const 209 { 210 // Determine min/max range applying the deviation 211 uint64_t min = getTarget() * (100 - lowerDeviation) / 100; 212 std::optional<uint64_t> max = getTarget() * (100 + upperDeviation) / 100; 213 214 // Adjust the min/max range by applying the factor & offset 215 min = min * _factor + _offset; 216 max = max.value() * _factor + _offset; 217 218 if (_ignoreAboveMax) 219 { 220 max = std::nullopt; 221 } 222 223 return std::make_pair(min, max); 224 } 225 226 void TachSensor::processState() 227 { 228 // This function runs from inside trust::Manager::checkTrust(), which, 229 // for sensors using the count method, runs right before process() 230 // is called anyway inside Fan::countTimerExpired() so don't call 231 // it now if using that method. 232 if (_method == MethodMode::timebased) 233 { 234 _fan.process(*this); 235 } 236 } 237 238 void TachSensor::resetMethod() 239 { 240 switch (_method) 241 { 242 case MethodMode::timebased: 243 if (timerRunning()) 244 { 245 stopTimer(); 246 } 247 break; 248 case MethodMode::count: 249 if (_functional) 250 { 251 _counter = 0; 252 } 253 else 254 { 255 _counter = _threshold; 256 } 257 break; 258 } 259 } 260 261 void TachSensor::setFunctional(bool functional, bool skipErrorTimer) 262 { 263 _functional = functional; 264 updateInventory(_functional); 265 266 if (!_errorTimer) 267 { 268 return; 269 } 270 271 if (!_functional) 272 { 273 if (_fan.present() && !skipErrorTimer) 274 { 275 _errorTimer->restartOnce(std::chrono::seconds(*_errorDelay)); 276 } 277 } 278 else if (_errorTimer->isEnabled()) 279 { 280 _errorTimer->setEnabled(false); 281 } 282 } 283 284 void TachSensor::handleTargetChange(sdbusplus::message_t& msg) 285 { 286 readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget); 287 288 // Check all tach sensors on the fan against the target 289 _fan.tachChanged(); 290 291 // record previous target value 292 if (_prevTargets.front() != _tachTarget) 293 { 294 _prevTargets.push_front(_tachTarget); 295 296 _prevTargets.pop_back(); 297 } 298 } 299 300 void TachSensor::handleTachChange(sdbusplus::message_t& msg) 301 { 302 readPropertyFromMessage(msg, util::FAN_SENSOR_VALUE_INTF, 303 FAN_VALUE_PROPERTY, _tachInput); 304 305 // Check just this sensor against the target 306 _fan.tachChanged(*this); 307 308 // record previous tach value 309 _prevTachs.push_front(_tachInput); 310 311 _prevTachs.pop_back(); 312 } 313 314 void TachSensor::startTimer(TimerMode mode) 315 { 316 using namespace std::chrono; 317 318 if (!timerRunning() || mode != _timerMode) 319 { 320 log<level::DEBUG>( 321 std::format("Start timer({}) on tach sensor {}. [delay = {}s]", 322 static_cast<int>(mode), _name, 323 duration_cast<seconds>(getDelay(mode)).count()) 324 .c_str()); 325 _timer.restartOnce(getDelay(mode)); 326 _timerMode = mode; 327 } 328 } 329 330 std::chrono::microseconds TachSensor::getDelay(TimerMode mode) 331 { 332 using namespace std::chrono; 333 334 switch (mode) 335 { 336 case TimerMode::nonfunc: 337 return duration_cast<microseconds>(seconds(_timeout)); 338 case TimerMode::func: 339 return duration_cast<microseconds>(seconds(_funcDelay)); 340 default: 341 // Log an internal error for undefined timer mode 342 log<level::ERR>("Undefined timer mode", 343 entry("TIMER_MODE=%u", mode)); 344 elog<InternalFailure>(); 345 return duration_cast<microseconds>(seconds(0)); 346 } 347 } 348 349 void TachSensor::setCounter(bool count) 350 { 351 if (count) 352 { 353 if (_counter < _threshold) 354 { 355 ++_counter; 356 log<level::DEBUG>( 357 std::format( 358 "Incremented error counter on {} to {} (threshold {})", 359 _name, _counter, _threshold) 360 .c_str()); 361 } 362 } 363 else 364 { 365 if (_counter > 0) 366 { 367 --_counter; 368 log<level::DEBUG>( 369 std::format( 370 "Decremented error counter on {} to {} (threshold {})", 371 _name, _counter, _threshold) 372 .c_str()); 373 } 374 } 375 } 376 377 void TachSensor::startCountTimer() 378 { 379 if (_countTimer) 380 { 381 log<level::DEBUG>( 382 std::format("Starting count timer on sensor {}", _name).c_str()); 383 _countTimer->restart(std::chrono::seconds(_countInterval)); 384 } 385 } 386 387 void TachSensor::stopCountTimer() 388 { 389 if (_countTimer && _countTimer->isEnabled()) 390 { 391 log<level::DEBUG>( 392 std::format("Stopping count timer on tach sensor {}.", _name) 393 .c_str()); 394 _countTimer->setEnabled(false); 395 } 396 } 397 398 void TachSensor::updateInventory(bool functional) 399 { 400 auto objectMap = 401 util::getObjMap<bool>(_invName, util::OPERATIONAL_STATUS_INTF, 402 util::FUNCTIONAL_PROPERTY, functional); 403 404 auto response = util::SDBusPlus::callMethod( 405 _bus, util::INVENTORY_SVC, util::INVENTORY_PATH, util::INVENTORY_INTF, 406 "Notify", objectMap); 407 408 if (response.is_method_error()) 409 { 410 log<level::ERR>("Error in notify update of tach sensor inventory"); 411 } 412 } 413 414 } // namespace monitor 415 } // namespace fan 416 } // namespace phosphor 417