/** * Copyright © 2017 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "fan.hpp" #include "types.hpp" #include "utility.hpp" namespace phosphor { namespace fan { namespace monitor { using namespace phosphor::logging; using TimerType = phosphor::fan::util::Timer::TimerType; constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory"; constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager"; constexpr auto FUNCTIONAL_PROPERTY = "Functional"; constexpr auto OPERATIONAL_STATUS_INTF = "xyz.openbmc_project.State.Decorator.OperationalStatus"; Fan::Fan(sdbusplus::bus::bus& bus, phosphor::fan::event::EventPtr& events, const FanDefinition& def) : _bus(bus), _name(std::get(def)), _deviation(std::get(def)), _numSensorFailsForNonFunc(std::get(def)) { auto& sensors = std::get(def); for (auto& s : sensors) { _sensors.emplace_back( std::make_unique(bus, *this, std::get(s), std::get(s), std::get(def), events)); } //Start from a known state of functional updateInventory(true); //The TachSensors will now have already read the input //and target values, so check them. tachChanged(); } void Fan::tachChanged() { for (auto& s : _sensors) { tachChanged(*s); } } void Fan::tachChanged(TachSensor& sensor) { auto& timer = sensor.getTimer(); auto running = timer.running(); //If this sensor is out of range at this moment, start //its timer, at the end of which the inventory //for the fan may get updated to not functional. //If this sensor is OK, put everything back into a good state. if (outOfRange(sensor)) { if (sensor.functional() && !running) { timer.start(sensor.getTimeout(), TimerType::oneshot); } } else { if (!sensor.functional()) { sensor.setFunctional(true); } if (running) { timer.stop(); } //If the fan was nonfunctional and enough sensors are now OK, //the fan can go back to functional if (!_functional && !tooManySensorsNonfunctional()) { log("Setting a fan back to functional", entry("FAN=%s", _name.c_str())); updateInventory(true); } } } uint64_t Fan::getTargetSpeed(const TachSensor& sensor) { uint64_t target = 0; if (sensor.hasTarget()) { target = sensor.getTarget(); } else { //The sensor doesn't support a target, //so get it from another sensor. auto s = std::find_if(_sensors.begin(), _sensors.end(), [](const auto& s) { return s->hasTarget(); }); if (s != _sensors.end()) { target = (*s)->getTarget(); } } return target; } bool Fan::tooManySensorsNonfunctional() { size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(), [](const auto& s) { return !s->functional(); }); return (numFailed >= _numSensorFailsForNonFunc); } bool Fan::outOfRange(const TachSensor& sensor) { auto actual = static_cast(sensor.getInput()); auto target = getTargetSpeed(sensor); uint64_t min = target * (100 - _deviation) / 100; uint64_t max = target * (100 + _deviation) / 100; if ((actual < min) || (actual > max)) { return true; } return false; } void Fan::timerExpired(TachSensor& sensor) { sensor.setFunctional(false); //If the fan is currently functional, but too many //contained sensors are now nonfunctional, update //the whole fan nonfunctional. if (_functional && tooManySensorsNonfunctional()) { log("Setting a fan to nonfunctional", entry("FAN=%s", _name.c_str())); updateInventory(false); } } void Fan::updateInventory(bool functional) { ObjectMap objectMap = getObjectMap(functional); std::string service; try { service = phosphor::fan::util::getInvService(_bus); } catch (const std::runtime_error& err) { log(err.what()); return; } auto msg = _bus.new_method_call(service.c_str(), INVENTORY_PATH, INVENTORY_INTF, "Notify"); msg.append(std::move(objectMap)); auto response = _bus.call(msg); if (response.is_method_error()) { log("Error in Notify call to update inventory"); return; } //This will always track the current state of the inventory. _functional = functional; } Fan::ObjectMap Fan::getObjectMap(bool functional) { ObjectMap objectMap; InterfaceMap interfaceMap; PropertyMap propertyMap; propertyMap.emplace(FUNCTIONAL_PROPERTY, functional); interfaceMap.emplace(OPERATIONAL_STATUS_INTF, std::move(propertyMap)); objectMap.emplace(_name, std::move(interfaceMap)); return objectMap; } } } }