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