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