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