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 <algorithm> 17 #include <phosphor-logging/log.hpp> 18 #include "fan.hpp" 19 #include "types.hpp" 20 #include "utility.hpp" 21 22 namespace phosphor 23 { 24 namespace fan 25 { 26 namespace monitor 27 { 28 29 using namespace phosphor::logging; 30 using TimerType = phosphor::fan::util::Timer::TimerType; 31 32 constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory"; 33 constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager"; 34 35 constexpr auto FUNCTIONAL_PROPERTY = "Functional"; 36 constexpr auto OPERATIONAL_STATUS_INTF = 37 "xyz.openbmc_project.State.Decorator.OperationalStatus"; 38 39 40 Fan::Fan(sdbusplus::bus::bus& bus, 41 phosphor::fan::event::EventPtr& events, 42 const FanDefinition& def) : 43 _bus(bus), 44 _name(std::get<fanNameField>(def)), 45 _deviation(std::get<fanDeviationField>(def)), 46 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)) 47 { 48 auto& sensors = std::get<sensorListField>(def); 49 50 for (auto& s : sensors) 51 { 52 _sensors.emplace_back( 53 std::make_unique<TachSensor>(bus, 54 *this, 55 std::get<sensorNameField>(s), 56 std::get<hasTargetField>(s), 57 std::get<timeoutField>(def), 58 events)); 59 } 60 61 //Start from a known state of functional 62 updateInventory(true); 63 64 //The TachSensors will now have already read the input 65 //and target values, so check them. 66 tachChanged(); 67 } 68 69 70 void Fan::tachChanged() 71 { 72 for (auto& s : _sensors) 73 { 74 tachChanged(*s); 75 } 76 } 77 78 79 void Fan::tachChanged(TachSensor& sensor) 80 { 81 auto& timer = sensor.getTimer(); 82 auto running = timer.running(); 83 84 //If this sensor is out of range at this moment, start 85 //its timer, at the end of which the inventory 86 //for the fan may get updated to not functional. 87 88 //If this sensor is OK, put everything back into a good state. 89 90 if (outOfRange(sensor)) 91 { 92 if (sensor.functional() && !running) 93 { 94 timer.start(sensor.getTimeout(), TimerType::oneshot); 95 } 96 } 97 else 98 { 99 if (!sensor.functional()) 100 { 101 sensor.setFunctional(true); 102 } 103 104 if (running) 105 { 106 timer.stop(); 107 } 108 109 //If the fan was nonfunctional and enough sensors are now OK, 110 //the fan can go back to functional 111 if (!_functional && !tooManySensorsNonfunctional()) 112 { 113 log<level::INFO>("Setting a fan back to functional", 114 entry("FAN=%s", _name.c_str())); 115 116 updateInventory(true); 117 } 118 } 119 } 120 121 122 uint64_t Fan::getTargetSpeed(const TachSensor& sensor) 123 { 124 uint64_t target = 0; 125 126 if (sensor.hasTarget()) 127 { 128 target = sensor.getTarget(); 129 } 130 else 131 { 132 //The sensor doesn't support a target, 133 //so get it from another sensor. 134 auto s = std::find_if(_sensors.begin(), _sensors.end(), 135 [](const auto& s) 136 { 137 return s->hasTarget(); 138 }); 139 140 if (s != _sensors.end()) 141 { 142 target = (*s)->getTarget(); 143 } 144 } 145 146 return target; 147 } 148 149 150 bool Fan::tooManySensorsNonfunctional() 151 { 152 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(), 153 [](const auto& s) 154 { 155 return !s->functional(); 156 }); 157 158 return (numFailed >= _numSensorFailsForNonFunc); 159 } 160 161 162 bool Fan::outOfRange(const TachSensor& sensor) 163 { 164 auto actual = static_cast<uint64_t>(sensor.getInput()); 165 auto target = getTargetSpeed(sensor); 166 167 uint64_t min = target * (100 - _deviation) / 100; 168 uint64_t max = target * (100 + _deviation) / 100; 169 170 if ((actual < min) || (actual > max)) 171 { 172 return true; 173 } 174 175 return false; 176 } 177 178 179 void Fan::timerExpired(TachSensor& sensor) 180 { 181 sensor.setFunctional(false); 182 183 //If the fan is currently functional, but too many 184 //contained sensors are now nonfunctional, update 185 //the whole fan nonfunctional. 186 187 if (_functional && tooManySensorsNonfunctional()) 188 { 189 log<level::ERR>("Setting a fan to nonfunctional", 190 entry("FAN=%s", _name.c_str()), 191 entry("TACH_SENSOR=%s", sensor.name().c_str()), 192 entry("ACTUAL_SPEED=%lld", sensor.getInput()), 193 entry("TARGET_SPEED=%lld", getTargetSpeed(sensor))); 194 195 updateInventory(false); 196 } 197 } 198 199 200 void Fan::updateInventory(bool functional) 201 { 202 ObjectMap objectMap = getObjectMap(functional); 203 std::string service; 204 205 service = phosphor::fan::util::getInvService(_bus); 206 207 auto msg = _bus.new_method_call(service.c_str(), 208 INVENTORY_PATH, 209 INVENTORY_INTF, 210 "Notify"); 211 212 msg.append(std::move(objectMap)); 213 auto response = _bus.call(msg); 214 if (response.is_method_error()) 215 { 216 log<level::ERR>("Error in Notify call to update inventory"); 217 return; 218 } 219 220 //This will always track the current state of the inventory. 221 _functional = functional; 222 } 223 224 225 Fan::ObjectMap Fan::getObjectMap(bool functional) 226 { 227 ObjectMap objectMap; 228 InterfaceMap interfaceMap; 229 PropertyMap propertyMap; 230 231 propertyMap.emplace(FUNCTIONAL_PROPERTY, functional); 232 interfaceMap.emplace(OPERATIONAL_STATUS_INTF, std::move(propertyMap)); 233 objectMap.emplace(_name, std::move(interfaceMap)); 234 235 return objectMap; 236 } 237 238 239 } 240 } 241 } 242