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