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