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