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 _presenceIfaceAddedMatch( 59 bus, 60 rules::interfacesAdded() + 61 rules::argNpath(0, util::INVENTORY_PATH + _name), 62 std::bind(std::mem_fn(&Fan::presenceIfaceAdded), this, 63 std::placeholders::_1)), 64 _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def)) 65 { 66 // Start from a known state of functional (even if 67 // _numSensorFailsForNonFunc is 0) 68 updateInventory(true); 69 70 // Setup tach sensors for monitoring 71 auto& sensors = std::get<sensorListField>(def); 72 for (auto& s : sensors) 73 { 74 _sensors.emplace_back(std::make_shared<TachSensor>( 75 mode, bus, *this, std::get<sensorNameField>(s), 76 std::get<hasTargetField>(s), std::get<funcDelay>(def), 77 std::get<targetInterfaceField>(s), std::get<factorField>(s), 78 std::get<offsetField>(s), std::get<methodField>(def), 79 std::get<thresholdField>(s), std::get<timeoutField>(def), 80 std::get<nonfuncRotorErrDelayField>(def), event)); 81 82 _trustManager->registerSensor(_sensors.back()); 83 } 84 85 #ifndef MONITOR_USE_JSON 86 // Check current tach state when entering monitor mode 87 if (mode != Mode::init) 88 { 89 _monitorReady = true; 90 91 // The TachSensors will now have already read the input 92 // and target values, so check them. 93 tachChanged(); 94 } 95 #else 96 if (_system.isPowerOn()) 97 { 98 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay)); 99 } 100 #endif 101 102 if (_fanMissingErrorDelay) 103 { 104 _fanMissingErrorTimer = std::make_unique< 105 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>( 106 event, std::bind(&System::fanMissingErrorTimerExpired, &system, 107 std::ref(*this))); 108 } 109 110 try 111 { 112 _present = util::SDBusPlus::getProperty<bool>( 113 util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present"); 114 115 if (!_present) 116 { 117 getLogger().log( 118 fmt::format("On startup, fan {} is missing", _name)); 119 if (_system.isPowerOn() && _fanMissingErrorTimer) 120 { 121 _fanMissingErrorTimer->restartOnce( 122 std::chrono::seconds{*_fanMissingErrorDelay}); 123 } 124 } 125 } 126 catch (const util::DBusServiceError& e) 127 { 128 // This could happen on the first BMC boot if the presence 129 // detect app hasn't started yet and there isn't an inventory 130 // cache yet. 131 } 132 } 133 134 void Fan::presenceIfaceAdded(sdbusplus::message::message& msg) 135 { 136 sdbusplus::message::object_path path; 137 std::map<std::string, std::map<std::string, std::variant<bool>>> interfaces; 138 139 msg.read(path, interfaces); 140 141 auto properties = interfaces.find(util::INV_ITEM_IFACE); 142 if (properties == interfaces.end()) 143 { 144 return; 145 } 146 147 auto property = properties->second.find("Present"); 148 if (property == properties->second.end()) 149 { 150 return; 151 } 152 153 _present = std::get<bool>(property->second); 154 155 if (!_present) 156 { 157 getLogger().log(fmt::format( 158 "New fan {} interface added and fan is not present", _name)); 159 if (_system.isPowerOn() && _fanMissingErrorTimer) 160 { 161 _fanMissingErrorTimer->restartOnce( 162 std::chrono::seconds{*_fanMissingErrorDelay}); 163 } 164 } 165 166 _system.fanStatusChange(*this); 167 } 168 169 void Fan::startMonitor() 170 { 171 _monitorReady = true; 172 173 std::for_each(_sensors.begin(), _sensors.end(), [this](auto& sensor) { 174 if (_present) 175 { 176 try 177 { 178 // Force a getProperty call to check if the tach sensor is 179 // on D-Bus. If it isn't, now set it to nonfunctional. 180 // This isn't done earlier so that code watching for 181 // nonfunctional tach sensors doesn't take actions before 182 // those sensors show up on D-Bus. 183 sensor->updateTachAndTarget(); 184 tachChanged(*sensor); 185 } 186 catch (const util::DBusServiceError& e) 187 { 188 // The tach property still isn't on D-Bus, ensure 189 // sensor is nonfunctional. 190 getLogger().log(fmt::format( 191 "Monitoring starting but {} sensor value not on D-Bus", 192 sensor->name())); 193 194 sensor->setFunctional(false); 195 196 if (_numSensorFailsForNonFunc) 197 { 198 if (_functional && (countNonFunctionalSensors() >= 199 _numSensorFailsForNonFunc)) 200 { 201 updateInventory(false); 202 } 203 } 204 205 _system.fanStatusChange(*this); 206 } 207 } 208 }); 209 } 210 211 void Fan::tachChanged() 212 { 213 if (_monitorReady) 214 { 215 for (auto& s : _sensors) 216 { 217 tachChanged(*s); 218 } 219 } 220 } 221 222 void Fan::tachChanged(TachSensor& sensor) 223 { 224 if (!_system.isPowerOn() || !_monitorReady) 225 { 226 return; 227 } 228 229 if (_trustManager->active()) 230 { 231 if (!_trustManager->checkTrust(sensor)) 232 { 233 return; 234 } 235 } 236 237 process(sensor); 238 } 239 240 void Fan::process(TachSensor& sensor) 241 { 242 // If this sensor is out of range at this moment, start 243 // its timer, at the end of which the inventory 244 // for the fan may get updated to not functional. 245 246 // If this sensor is OK, put everything back into a good state. 247 248 if (outOfRange(sensor)) 249 { 250 if (sensor.functional()) 251 { 252 switch (sensor.getMethod()) 253 { 254 case MethodMode::timebased: 255 // Start nonfunctional timer if not already running 256 sensor.startTimer(TimerMode::nonfunc); 257 break; 258 case MethodMode::count: 259 sensor.setCounter(true); 260 if (sensor.getCounter() >= sensor.getThreshold()) 261 { 262 updateState(sensor); 263 } 264 break; 265 } 266 } 267 } 268 else 269 { 270 switch (sensor.getMethod()) 271 { 272 case MethodMode::timebased: 273 if (sensor.functional()) 274 { 275 if (sensor.timerRunning()) 276 { 277 sensor.stopTimer(); 278 } 279 } 280 else 281 { 282 // Start functional timer if not already running 283 sensor.startTimer(TimerMode::func); 284 } 285 break; 286 case MethodMode::count: 287 sensor.setCounter(false); 288 if (!sensor.functional() && sensor.getCounter() == 0) 289 { 290 updateState(sensor); 291 } 292 break; 293 } 294 } 295 } 296 297 uint64_t Fan::findTargetSpeed() 298 { 299 uint64_t target = 0; 300 // The sensor doesn't support a target, 301 // so get it from another sensor. 302 auto s = std::find_if(_sensors.begin(), _sensors.end(), 303 [](const auto& s) { return s->hasTarget(); }); 304 305 if (s != _sensors.end()) 306 { 307 target = (*s)->getTarget(); 308 } 309 310 return target; 311 } 312 313 size_t Fan::countNonFunctionalSensors() 314 { 315 return std::count_if(_sensors.begin(), _sensors.end(), 316 [](const auto& s) { return !s->functional(); }); 317 } 318 319 bool Fan::outOfRange(const TachSensor& sensor) 320 { 321 auto actual = static_cast<uint64_t>(sensor.getInput()); 322 auto range = sensor.getRange(_deviation); 323 324 if ((actual < range.first) || (actual > range.second)) 325 { 326 return true; 327 } 328 329 return false; 330 } 331 332 void Fan::updateState(TachSensor& sensor) 333 { 334 auto range = sensor.getRange(_deviation); 335 336 if (!_system.isPowerOn()) 337 { 338 return; 339 } 340 341 sensor.setFunctional(!sensor.functional()); 342 getLogger().log( 343 fmt::format("Setting tach sensor {} functional state to {}. " 344 "[target = {}, input = {}, allowed range = ({} - {})]", 345 sensor.name(), sensor.functional(), sensor.getTarget(), 346 sensor.getInput(), range.first, range.second)); 347 348 // A zero value for _numSensorFailsForNonFunc means we aren't dealing 349 // with fan FRU functional status, only sensor functional status. 350 if (_numSensorFailsForNonFunc) 351 { 352 auto numNonFuncSensors = countNonFunctionalSensors(); 353 // If the fan was nonfunctional and enough sensors are now OK, 354 // the fan can be set to functional 355 if (!_functional && !(numNonFuncSensors >= _numSensorFailsForNonFunc)) 356 { 357 getLogger().log(fmt::format("Setting fan {} to functional, number " 358 "of nonfunctional sensors = {}", 359 _name, numNonFuncSensors)); 360 updateInventory(true); 361 } 362 363 // If the fan is currently functional, but too many 364 // contained sensors are now nonfunctional, update 365 // the fan to nonfunctional. 366 if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc)) 367 { 368 getLogger().log(fmt::format("Setting fan {} to nonfunctional, " 369 "number of nonfunctional sensors = {}", 370 _name, numNonFuncSensors)); 371 updateInventory(false); 372 } 373 } 374 375 _system.fanStatusChange(*this); 376 } 377 378 void Fan::updateInventory(bool functional) 379 { 380 auto objectMap = 381 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF, 382 util::FUNCTIONAL_PROPERTY, functional); 383 auto response = util::SDBusPlus::lookupAndCallMethod( 384 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap); 385 if (response.is_method_error()) 386 { 387 log<level::ERR>("Error in Notify call to update inventory"); 388 return; 389 } 390 391 // This will always track the current state of the inventory. 392 _functional = functional; 393 } 394 395 void Fan::presenceChanged(sdbusplus::message::message& msg) 396 { 397 std::string interface; 398 std::map<std::string, std::variant<bool>> properties; 399 400 msg.read(interface, properties); 401 402 auto presentProp = properties.find("Present"); 403 if (presentProp != properties.end()) 404 { 405 _present = std::get<bool>(presentProp->second); 406 407 getLogger().log( 408 fmt::format("Fan {} presence state change to {}", _name, _present)); 409 410 _system.fanStatusChange(*this); 411 412 if (_fanMissingErrorDelay) 413 { 414 if (!_present && _system.isPowerOn()) 415 { 416 _fanMissingErrorTimer->restartOnce( 417 std::chrono::seconds{*_fanMissingErrorDelay}); 418 } 419 else if (_present && _fanMissingErrorTimer->isEnabled()) 420 { 421 _fanMissingErrorTimer->setEnabled(false); 422 } 423 } 424 } 425 } 426 427 void Fan::sensorErrorTimerExpired(const TachSensor& sensor) 428 { 429 if (_present && _system.isPowerOn()) 430 { 431 _system.sensorErrorTimerExpired(*this, sensor); 432 } 433 } 434 435 void Fan::powerStateChanged(bool powerStateOn) 436 { 437 #ifdef MONITOR_USE_JSON 438 if (powerStateOn) 439 { 440 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay)); 441 442 if (_present) 443 { 444 std::for_each( 445 _sensors.begin(), _sensors.end(), [this](auto& sensor) { 446 try 447 { 448 // Force a getProperty call. If sensor is on D-Bus, 449 // then make sure it's functional. 450 sensor->updateTachAndTarget(); 451 452 // If not functional, set it back to functional. 453 if (!sensor->functional()) 454 { 455 sensor->setFunctional(true); 456 _system.fanStatusChange(*this, true); 457 } 458 } 459 catch (const util::DBusServiceError& e) 460 { 461 // Properties still aren't on D-Bus. Let startMonitor() 462 // deal with it. 463 getLogger().log(fmt::format( 464 "At power on, tach sensor {} value not on D-Bus", 465 sensor->name())); 466 } 467 }); 468 469 // If configured to change functional state on the fan itself, 470 // Set it back to true now if necessary. 471 if (_numSensorFailsForNonFunc) 472 { 473 if (!_functional && 474 (countNonFunctionalSensors() < _numSensorFailsForNonFunc)) 475 { 476 updateInventory(true); 477 } 478 } 479 } 480 else 481 { 482 getLogger().log( 483 fmt::format("At power on, fan {} is missing", _name)); 484 485 if (_fanMissingErrorTimer) 486 { 487 _fanMissingErrorTimer->restartOnce( 488 std::chrono::seconds{*_fanMissingErrorDelay}); 489 } 490 } 491 } 492 else 493 { 494 _monitorReady = false; 495 496 if (_monitorTimer.isEnabled()) 497 { 498 _monitorTimer.setEnabled(false); 499 } 500 501 if (_fanMissingErrorTimer && _fanMissingErrorTimer->isEnabled()) 502 { 503 _fanMissingErrorTimer->setEnabled(false); 504 } 505 506 std::for_each(_sensors.begin(), _sensors.end(), [](auto& sensor) { 507 if (sensor->timerRunning()) 508 { 509 sensor->stopTimer(); 510 } 511 }); 512 } 513 #endif 514 } 515 516 } // namespace monitor 517 } // namespace fan 518 } // namespace phosphor 519