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