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