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 "fan.hpp" 17 18 #include "logging.hpp" 19 #include "sdbusplus.hpp" 20 #include "system.hpp" 21 #include "types.hpp" 22 #include "utility.hpp" 23 24 #include <fmt/format.h> 25 26 #include <phosphor-logging/log.hpp> 27 28 #include <algorithm> 29 30 namespace phosphor 31 { 32 namespace fan 33 { 34 namespace monitor 35 { 36 37 using namespace phosphor::logging; 38 using namespace sdbusplus::bus::match; 39 40 Fan::Fan(Mode mode, sdbusplus::bus::bus& bus, const sdeventplus::Event& event, 41 std::unique_ptr<trust::Manager>& trust, const FanDefinition& def, 42 System& system) : 43 _bus(bus), 44 _name(std::get<fanNameField>(def)), 45 _deviation(std::get<fanDeviationField>(def)), 46 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)), 47 _trustManager(trust), 48 #ifdef MONITOR_USE_JSON 49 _monitorDelay(std::get<monitorStartDelayField>(def)), 50 _monitorTimer(event, std::bind(std::mem_fn(&Fan::startMonitor), this)), 51 #endif 52 _system(system), 53 _presenceMatch(bus, 54 rules::propertiesChanged(util::INVENTORY_PATH + _name, 55 util::INV_ITEM_IFACE), 56 std::bind(std::mem_fn(&Fan::presenceChanged), this, 57 std::placeholders::_1)), 58 _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def)) 59 { 60 // Start from a known state of functional (even if 61 // _numSensorFailsForNonFunc is 0) 62 updateInventory(true); 63 64 // Setup tach sensors for monitoring 65 auto& sensors = std::get<sensorListField>(def); 66 for (auto& s : sensors) 67 { 68 try 69 { 70 _sensors.emplace_back(std::make_shared<TachSensor>( 71 mode, bus, *this, std::get<sensorNameField>(s), 72 std::get<hasTargetField>(s), std::get<funcDelay>(def), 73 std::get<targetInterfaceField>(s), std::get<factorField>(s), 74 std::get<offsetField>(s), std::get<methodField>(def), 75 std::get<thresholdField>(s), std::get<timeoutField>(def), 76 std::get<nonfuncRotorErrDelayField>(def), event)); 77 78 _trustManager->registerSensor(_sensors.back()); 79 } 80 catch (InvalidSensorError& e) 81 { 82 // Count the number of failed tach sensors, though if 83 // _numSensorFailsForNonFunc is zero that means the fan should not 84 // be set to nonfunctional. 85 if (_numSensorFailsForNonFunc && 86 (++_numFailedSensor >= _numSensorFailsForNonFunc)) 87 { 88 // Mark associated fan as nonfunctional 89 updateInventory(false); 90 } 91 } 92 } 93 94 #ifndef MONITOR_USE_JSON 95 // Check current tach state when entering monitor mode 96 if (mode != Mode::init) 97 { 98 _monitorReady = true; 99 100 // The TachSensors will now have already read the input 101 // and target values, so check them. 102 tachChanged(); 103 } 104 #else 105 // If it used the JSON config, then it also will do all the work 106 // out of fan-monitor-init, after _monitorDelay. 107 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay)); 108 #endif 109 110 // Get the initial presence state 111 bool available = true; 112 113 try 114 { 115 _present = util::SDBusPlus::getProperty<bool>( 116 util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present"); 117 } 118 catch (const util::DBusServiceError& e) 119 { 120 // This could be the initial boot and phosphor-fan-presence hasn't 121 // written to the inventory yet. 122 available = false; 123 } 124 125 if (_fanMissingErrorDelay) 126 { 127 _fanMissingErrorTimer = std::make_unique< 128 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 129 event, std::bind(&System::fanMissingErrorTimerExpired, &system, 130 std::ref(*this))); 131 132 if (!_present && available) 133 { 134 getLogger().log( 135 fmt::format("On startup, fan {} is missing", _name)); 136 _fanMissingErrorTimer->restartOnce( 137 std::chrono::seconds{*_fanMissingErrorDelay}); 138 } 139 } 140 } 141 142 void Fan::startMonitor() 143 { 144 _monitorReady = true; 145 146 tachChanged(); 147 } 148 149 void Fan::tachChanged() 150 { 151 if (_monitorReady) 152 { 153 for (auto& s : _sensors) 154 { 155 tachChanged(*s); 156 } 157 } 158 } 159 160 void Fan::tachChanged(TachSensor& sensor) 161 { 162 if (_trustManager->active()) 163 { 164 if (!_trustManager->checkTrust(sensor)) 165 { 166 return; 167 } 168 } 169 170 process(sensor); 171 } 172 173 void Fan::process(TachSensor& sensor) 174 { 175 // If this sensor is out of range at this moment, start 176 // its timer, at the end of which the inventory 177 // for the fan may get updated to not functional. 178 179 // If this sensor is OK, put everything back into a good state. 180 181 if (outOfRange(sensor)) 182 { 183 if (sensor.functional()) 184 { 185 switch (sensor.getMethod()) 186 { 187 case MethodMode::timebased: 188 // Start nonfunctional timer if not already running 189 sensor.startTimer(TimerMode::nonfunc); 190 break; 191 case MethodMode::count: 192 sensor.setCounter(true); 193 if (sensor.getCounter() >= sensor.getThreshold()) 194 { 195 updateState(sensor); 196 } 197 break; 198 } 199 } 200 } 201 else 202 { 203 switch (sensor.getMethod()) 204 { 205 case MethodMode::timebased: 206 if (sensor.functional()) 207 { 208 if (sensor.timerRunning()) 209 { 210 sensor.stopTimer(); 211 } 212 } 213 else 214 { 215 // Start functional timer if not already running 216 sensor.startTimer(TimerMode::func); 217 } 218 break; 219 case MethodMode::count: 220 sensor.setCounter(false); 221 if (!sensor.functional() && sensor.getCounter() == 0) 222 { 223 updateState(sensor); 224 } 225 break; 226 } 227 } 228 } 229 230 uint64_t Fan::findTargetSpeed() 231 { 232 uint64_t target = 0; 233 // The sensor doesn't support a target, 234 // so get it from another sensor. 235 auto s = std::find_if(_sensors.begin(), _sensors.end(), 236 [](const auto& s) { return s->hasTarget(); }); 237 238 if (s != _sensors.end()) 239 { 240 target = (*s)->getTarget(); 241 } 242 243 return target; 244 } 245 246 size_t Fan::countNonFunctionalSensors() 247 { 248 return std::count_if(_sensors.begin(), _sensors.end(), 249 [](const auto& s) { return !s->functional(); }); 250 } 251 252 bool Fan::outOfRange(const TachSensor& sensor) 253 { 254 auto actual = static_cast<uint64_t>(sensor.getInput()); 255 auto range = sensor.getRange(_deviation); 256 257 if ((actual < range.first) || (actual > range.second)) 258 { 259 return true; 260 } 261 262 return false; 263 } 264 265 void Fan::updateState(TachSensor& sensor) 266 { 267 auto range = sensor.getRange(_deviation); 268 sensor.setFunctional(!sensor.functional()); 269 getLogger().log( 270 fmt::format("Setting tach sensor {} functional state to {}. " 271 "[target = {}, input = {}, allowed range = ({} - {})]", 272 sensor.name(), sensor.functional(), sensor.getTarget(), 273 sensor.getInput(), range.first, range.second)); 274 275 // A zero value for _numSensorFailsForNonFunc means we aren't dealing 276 // with fan FRU functional status, only sensor functional status. 277 if (_numSensorFailsForNonFunc) 278 { 279 auto numNonFuncSensors = countNonFunctionalSensors(); 280 // If the fan was nonfunctional and enough sensors are now OK, 281 // the fan can be set to functional 282 if (!_functional && !(numNonFuncSensors >= _numSensorFailsForNonFunc)) 283 { 284 getLogger().log(fmt::format("Setting fan {} to functional, number " 285 "of nonfunctional sensors = {}", 286 _name, numNonFuncSensors)); 287 updateInventory(true); 288 } 289 290 // If the fan is currently functional, but too many 291 // contained sensors are now nonfunctional, update 292 // the fan to nonfunctional. 293 if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc)) 294 { 295 getLogger().log(fmt::format("Setting fan {} to nonfunctional, " 296 "number of nonfunctional sensors = {}", 297 _name, numNonFuncSensors)); 298 updateInventory(false); 299 } 300 } 301 302 _system.fanStatusChange(*this); 303 } 304 305 void Fan::updateInventory(bool functional) 306 { 307 auto objectMap = 308 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF, 309 util::FUNCTIONAL_PROPERTY, functional); 310 auto response = util::SDBusPlus::lookupAndCallMethod( 311 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap); 312 if (response.is_method_error()) 313 { 314 log<level::ERR>("Error in Notify call to update inventory"); 315 return; 316 } 317 318 // This will always track the current state of the inventory. 319 _functional = functional; 320 } 321 322 void Fan::presenceChanged(sdbusplus::message::message& msg) 323 { 324 std::string interface; 325 std::map<std::string, std::variant<bool>> properties; 326 327 msg.read(interface, properties); 328 329 auto presentProp = properties.find("Present"); 330 if (presentProp != properties.end()) 331 { 332 _present = std::get<bool>(presentProp->second); 333 334 getLogger().log( 335 fmt::format("Fan {} presence state change to {}", _name, _present)); 336 337 _system.fanStatusChange(*this); 338 339 if (_fanMissingErrorDelay) 340 { 341 if (!_present) 342 { 343 _fanMissingErrorTimer->restartOnce( 344 std::chrono::seconds{*_fanMissingErrorDelay}); 345 } 346 else if (_fanMissingErrorTimer->isEnabled()) 347 { 348 _fanMissingErrorTimer->setEnabled(false); 349 } 350 } 351 } 352 } 353 354 void Fan::sensorErrorTimerExpired(const TachSensor& sensor) 355 { 356 if (_present) 357 { 358 _system.sensorErrorTimerExpired(*this, sensor); 359 } 360 } 361 362 } // namespace monitor 363 } // namespace fan 364 } // namespace phosphor 365