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 // Use getProperty directly to allow a missing sensor object 96 // to abort construction. 97 _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>( 98 _bus, _name, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY); 99 } 100 catch (std::exception& e) 101 { 102 log<level::ERR>( 103 fmt::format("Failed to retrieve tach sensor {}", _name) 104 .c_str()); 105 // Mark tach sensor as nonfunctional 106 setFunctional(false); 107 throw InvalidSensorError(); 108 } 109 110 if (_hasTarget) 111 { 112 readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, 113 _tachTarget); 114 } 115 116 auto match = getMatchString(FAN_SENSOR_VALUE_INTF); 117 118 tachSignal = std::make_unique<sdbusplus::server::match::match>( 119 _bus, match.c_str(), 120 [this](auto& msg) { this->handleTachChange(msg); }); 121 122 if (_hasTarget) 123 { 124 match = getMatchString(_interface); 125 126 targetSignal = std::make_unique<sdbusplus::server::match::match>( 127 _bus, match.c_str(), 128 [this](auto& msg) { this->handleTargetChange(msg); }); 129 } 130 131 if (_errorDelay) 132 { 133 _errorTimer = std::make_unique< 134 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 135 event, std::bind(&Fan::sensorErrorTimerExpired, &fan, 136 std::ref(*this))); 137 } 138 #ifndef MONITOR_USE_JSON 139 } 140 #endif 141 } 142 143 std::string TachSensor::getMatchString(const std::string& interface) 144 { 145 return sdbusplus::bus::match::rules::propertiesChanged(_name, interface); 146 } 147 148 uint64_t TachSensor::getTarget() const 149 { 150 if (!_hasTarget) 151 { 152 return _fan.findTargetSpeed(); 153 } 154 return _tachTarget; 155 } 156 157 std::pair<uint64_t, uint64_t> TachSensor::getRange(const size_t deviation) const 158 { 159 // Determine min/max range applying the deviation 160 uint64_t min = getTarget() * (100 - deviation) / 100; 161 uint64_t max = getTarget() * (100 + deviation) / 100; 162 163 // Adjust the min/max range by applying the factor & offset 164 min = min * _factor + _offset; 165 max = max * _factor + _offset; 166 167 return std::make_pair(min, max); 168 } 169 170 void TachSensor::processState() 171 { 172 _fan.process(*this); 173 } 174 175 void TachSensor::resetMethod() 176 { 177 switch (_method) 178 { 179 case MethodMode::timebased: 180 if (timerRunning()) 181 { 182 stopTimer(); 183 } 184 break; 185 case MethodMode::count: 186 if (_functional) 187 { 188 _counter = 0; 189 } 190 else 191 { 192 _counter = _threshold; 193 } 194 break; 195 } 196 } 197 198 void TachSensor::setFunctional(bool functional) 199 { 200 _functional = functional; 201 updateInventory(_functional); 202 203 if (!_errorTimer) 204 { 205 return; 206 } 207 208 if (!_functional) 209 { 210 if (_fan.present()) 211 { 212 _errorTimer->restartOnce(std::chrono::seconds(*_errorDelay)); 213 } 214 } 215 else if (_errorTimer->isEnabled()) 216 { 217 _errorTimer->setEnabled(false); 218 } 219 } 220 221 void TachSensor::handleTargetChange(sdbusplus::message::message& msg) 222 { 223 readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget); 224 225 // Check all tach sensors on the fan against the target 226 _fan.tachChanged(); 227 } 228 229 void TachSensor::handleTachChange(sdbusplus::message::message& msg) 230 { 231 readPropertyFromMessage(msg, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY, 232 _tachInput); 233 234 // Check just this sensor against the target 235 _fan.tachChanged(*this); 236 } 237 238 void TachSensor::startTimer(TimerMode mode) 239 { 240 using namespace std::chrono; 241 242 if (!timerRunning() || mode != _timerMode) 243 { 244 log<level::INFO>( 245 fmt::format("Start timer({}) on tach sensor {}. [delay = {}s]", 246 mode, _name, 247 duration_cast<seconds>(getDelay(mode)).count()) 248 .c_str()); 249 _timer.restartOnce(getDelay(mode)); 250 _timerMode = mode; 251 } 252 } 253 254 std::chrono::microseconds TachSensor::getDelay(TimerMode mode) 255 { 256 using namespace std::chrono; 257 258 switch (mode) 259 { 260 case TimerMode::nonfunc: 261 return duration_cast<microseconds>(seconds(_timeout)); 262 case TimerMode::func: 263 return duration_cast<microseconds>(seconds(_funcDelay)); 264 default: 265 // Log an internal error for undefined timer mode 266 log<level::ERR>("Undefined timer mode", 267 entry("TIMER_MODE=%u", mode)); 268 elog<InternalFailure>(); 269 return duration_cast<microseconds>(seconds(0)); 270 } 271 } 272 273 void TachSensor::setCounter(bool count) 274 { 275 if (count) 276 { 277 if (_counter < _threshold) 278 { 279 ++_counter; 280 } 281 } 282 else 283 { 284 if (_counter > 0) 285 { 286 --_counter; 287 } 288 } 289 } 290 291 void TachSensor::updateInventory(bool functional) 292 { 293 auto objectMap = 294 util::getObjMap<bool>(_invName, util::OPERATIONAL_STATUS_INTF, 295 util::FUNCTIONAL_PROPERTY, functional); 296 auto response = util::SDBusPlus::lookupAndCallMethod( 297 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap); 298 if (response.is_method_error()) 299 { 300 log<level::ERR>("Error in notify update of tach sensor inventory"); 301 } 302 } 303 304 } // namespace monitor 305 } // namespace fan 306 } // namespace phosphor 307