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