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 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) 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 #ifndef MONITOR_USE_JSON 126 } 127 #endif 128 } 129 130 void TachSensor::updateTachAndTarget() 131 { 132 _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>( 133 _bus, _name, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY); 134 135 if (_hasTarget) 136 { 137 readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, _tachTarget); 138 } 139 } 140 141 std::string TachSensor::getMatchString(const std::string& interface) 142 { 143 return sdbusplus::bus::match::rules::propertiesChanged(_name, interface); 144 } 145 146 uint64_t TachSensor::getTarget() const 147 { 148 if (!_hasTarget) 149 { 150 return _fan.findTargetSpeed(); 151 } 152 return _tachTarget; 153 } 154 155 std::pair<uint64_t, uint64_t> TachSensor::getRange(const size_t deviation) const 156 { 157 // Determine min/max range applying the deviation 158 uint64_t min = getTarget() * (100 - deviation) / 100; 159 uint64_t max = getTarget() * (100 + deviation) / 100; 160 161 // Adjust the min/max range by applying the factor & offset 162 min = min * _factor + _offset; 163 max = max * _factor + _offset; 164 165 return std::make_pair(min, max); 166 } 167 168 void TachSensor::processState() 169 { 170 // This function runs from inside trust::Manager::checkTrust(), which, 171 // for sensors using the count method, runs right before process() 172 // is called anyway inside Fan::countTimerExpired() so don't call 173 // it now if using that method. 174 if (_method == MethodMode::timebased) 175 { 176 _fan.process(*this); 177 } 178 } 179 180 void TachSensor::resetMethod() 181 { 182 switch (_method) 183 { 184 case MethodMode::timebased: 185 if (timerRunning()) 186 { 187 stopTimer(); 188 } 189 break; 190 case MethodMode::count: 191 if (_functional) 192 { 193 _counter = 0; 194 } 195 else 196 { 197 _counter = _threshold; 198 } 199 break; 200 } 201 } 202 203 void TachSensor::setFunctional(bool functional) 204 { 205 _functional = functional; 206 updateInventory(_functional); 207 208 if (!_errorTimer) 209 { 210 return; 211 } 212 213 if (!_functional) 214 { 215 if (_fan.present()) 216 { 217 _errorTimer->restartOnce(std::chrono::seconds(*_errorDelay)); 218 } 219 } 220 else if (_errorTimer->isEnabled()) 221 { 222 _errorTimer->setEnabled(false); 223 } 224 } 225 226 void TachSensor::handleTargetChange(sdbusplus::message::message& msg) 227 { 228 readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget); 229 230 // Check all tach sensors on the fan against the target 231 _fan.tachChanged(); 232 } 233 234 void TachSensor::handleTachChange(sdbusplus::message::message& msg) 235 { 236 readPropertyFromMessage(msg, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY, 237 _tachInput); 238 239 // Check just this sensor against the target 240 _fan.tachChanged(*this); 241 } 242 243 void TachSensor::startTimer(TimerMode mode) 244 { 245 using namespace std::chrono; 246 247 if (!timerRunning() || mode != _timerMode) 248 { 249 log<level::DEBUG>( 250 fmt::format("Start timer({}) on tach sensor {}. [delay = {}s]", 251 mode, _name, 252 duration_cast<seconds>(getDelay(mode)).count()) 253 .c_str()); 254 _timer.restartOnce(getDelay(mode)); 255 _timerMode = mode; 256 } 257 } 258 259 std::chrono::microseconds TachSensor::getDelay(TimerMode mode) 260 { 261 using namespace std::chrono; 262 263 switch (mode) 264 { 265 case TimerMode::nonfunc: 266 return duration_cast<microseconds>(seconds(_timeout)); 267 case TimerMode::func: 268 return duration_cast<microseconds>(seconds(_funcDelay)); 269 default: 270 // Log an internal error for undefined timer mode 271 log<level::ERR>("Undefined timer mode", 272 entry("TIMER_MODE=%u", mode)); 273 elog<InternalFailure>(); 274 return duration_cast<microseconds>(seconds(0)); 275 } 276 } 277 278 void TachSensor::setCounter(bool count) 279 { 280 if (count) 281 { 282 if (_counter < _threshold) 283 { 284 ++_counter; 285 log<level::DEBUG>( 286 fmt::format( 287 "Incremented error counter on {} to {} (threshold {})", 288 _name, _counter, _threshold) 289 .c_str()); 290 } 291 } 292 else 293 { 294 if (_counter > 0) 295 { 296 --_counter; 297 log<level::DEBUG>( 298 fmt::format( 299 "Decremented error counter on {} to {} (threshold {})", 300 _name, _counter, _threshold) 301 .c_str()); 302 } 303 } 304 } 305 306 void TachSensor::updateInventory(bool functional) 307 { 308 auto objectMap = 309 util::getObjMap<bool>(_invName, util::OPERATIONAL_STATUS_INTF, 310 util::FUNCTIONAL_PROPERTY, functional); 311 auto response = util::SDBusPlus::lookupAndCallMethod( 312 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap); 313 if (response.is_method_error()) 314 { 315 log<level::ERR>("Error in notify update of tach sensor inventory"); 316 } 317 } 318 319 } // namespace monitor 320 } // namespace fan 321 } // namespace phosphor 322