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