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 <chrono> 17 #include <phosphor-logging/log.hpp> 18 #include <phosphor-logging/elog.hpp> 19 #include <phosphor-logging/elog-errors.hpp> 20 #include <xyz/openbmc_project/Common/error.hpp> 21 #include "zone.hpp" 22 #include "utility.hpp" 23 #include "sdbusplus.hpp" 24 25 namespace phosphor 26 { 27 namespace fan 28 { 29 namespace control 30 { 31 32 using namespace std::chrono; 33 using namespace phosphor::fan; 34 using namespace phosphor::logging; 35 using InternalFailure = sdbusplus::xyz::openbmc_project::Common:: 36 Error::InternalFailure; 37 38 Zone::Zone(Mode mode, 39 sdbusplus::bus::bus& bus, 40 phosphor::fan::event::EventPtr& events, 41 const ZoneDefinition& def) : 42 _bus(bus), 43 _fullSpeed(std::get<fullSpeedPos>(def)), 44 _zoneNum(std::get<zoneNumPos>(def)), 45 _defFloorSpeed(std::get<floorSpeedPos>(def)), 46 _defCeilingSpeed(std::get<fullSpeedPos>(def)), 47 _incDelay(std::get<incDelayPos>(def)), 48 _decInterval(std::get<decIntervalPos>(def)), 49 _incTimer(events, [this](){ this->incTimerExpired(); }), 50 _decTimer(events, [this](){ this->decTimerExpired(); }), 51 _sdEvents(events) 52 { 53 auto& fanDefs = std::get<fanListPos>(def); 54 55 for (auto& def : fanDefs) 56 { 57 _fans.emplace_back(std::make_unique<Fan>(bus, def)); 58 } 59 60 // Do not enable set speed events when in init mode 61 if (mode != Mode::init) 62 { 63 // Setup signal trigger for set speed events 64 for (auto& event : std::get<setSpeedEventsPos>(def)) 65 { 66 initEvent(event); 67 } 68 // Start timer for fan speed decreases 69 if (!_decTimer.running() && _decInterval != seconds::zero()) 70 { 71 _decTimer.start(_decInterval, 72 util::Timer::TimerType::repeating); 73 } 74 } 75 } 76 77 void Zone::setSpeed(uint64_t speed) 78 { 79 if (_isActive) 80 { 81 _targetSpeed = speed; 82 for (auto& fan : _fans) 83 { 84 fan->setSpeed(_targetSpeed); 85 } 86 } 87 } 88 89 void Zone::setFullSpeed() 90 { 91 if (_fullSpeed != 0) 92 { 93 _targetSpeed = _fullSpeed; 94 for (auto& fan : _fans) 95 { 96 fan->setSpeed(_targetSpeed); 97 } 98 } 99 } 100 101 void Zone::setActiveAllow(const Group* group, bool isActiveAllow) 102 { 103 _active[*(group)] = isActiveAllow; 104 if (!isActiveAllow) 105 { 106 _isActive = false; 107 } 108 else 109 { 110 // Check all entries are set to allow control active 111 auto actPred = [](auto const& entry) {return entry.second;}; 112 _isActive = std::all_of(_active.begin(), 113 _active.end(), 114 actPred); 115 } 116 } 117 118 void Zone::setFloor(uint64_t speed) 119 { 120 _floorSpeed = speed; 121 // Floor speed above target, update target to floor speed 122 if (_targetSpeed < _floorSpeed) 123 { 124 requestSpeedIncrease(_floorSpeed - _targetSpeed); 125 } 126 } 127 128 void Zone::requestSpeedIncrease(uint64_t targetDelta) 129 { 130 // Only increase speed when delta is higher than 131 // the current increase delta for the zone and currently under ceiling 132 if (targetDelta > _incSpeedDelta && 133 _targetSpeed < _ceilingSpeed) 134 { 135 auto requestTarget = getRequestSpeedBase(); 136 requestTarget = (targetDelta - _incSpeedDelta) + requestTarget; 137 _incSpeedDelta = targetDelta; 138 // Target speed can not go above a defined ceiling speed 139 if (requestTarget > _ceilingSpeed) 140 { 141 requestTarget = _ceilingSpeed; 142 } 143 // Cancel current timer countdown 144 if (_incTimer.running()) 145 { 146 _incTimer.stop(); 147 } 148 setSpeed(requestTarget); 149 // Start timer countdown for fan speed increase 150 _incTimer.start(_incDelay, 151 util::Timer::TimerType::oneshot); 152 } 153 } 154 155 void Zone::incTimerExpired() 156 { 157 // Clear increase delta when timer expires allowing additional speed 158 // increase requests or speed decreases to occur 159 _incSpeedDelta = 0; 160 } 161 162 void Zone::requestSpeedDecrease(uint64_t targetDelta) 163 { 164 // Only decrease the lowest target delta requested 165 if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta) 166 { 167 _decSpeedDelta = targetDelta; 168 } 169 } 170 171 void Zone::decTimerExpired() 172 { 173 // Only decrease speeds when no requested increases exist and 174 // the increase timer is not running (i.e. not in the middle of increasing) 175 if (_incSpeedDelta == 0 && !_incTimer.running()) 176 { 177 auto requestTarget = getRequestSpeedBase(); 178 // Target speed can not go below the defined floor speed 179 if ((requestTarget < _decSpeedDelta) || 180 (requestTarget - _decSpeedDelta < _floorSpeed)) 181 { 182 requestTarget = _floorSpeed; 183 } 184 else 185 { 186 requestTarget = requestTarget - _decSpeedDelta; 187 } 188 setSpeed(requestTarget); 189 } 190 // Clear decrease delta when timer expires 191 _decSpeedDelta = 0; 192 // Decrease timer is restarted since its repeating 193 } 194 195 void Zone::initEvent(const SetSpeedEvent& event) 196 { 197 // Get the current value for each property 198 for (auto& group : std::get<groupPos>(event)) 199 { 200 try 201 { 202 refreshProperty(_bus, 203 group.first, 204 std::get<intfPos>(group.second), 205 std::get<propPos>(group.second)); 206 } 207 catch (const InternalFailure& ife) 208 { 209 log<level::INFO>( 210 "Unable to find property", 211 entry("PATH=%s", group.first.c_str()), 212 entry("INTERFACE=%s", std::get<intfPos>(group.second).c_str()), 213 entry("PROPERTY=%s", std::get<propPos>(group.second).c_str())); 214 } 215 } 216 // Setup signal matches for property change events 217 for (auto& prop : std::get<propChangeListPos>(event)) 218 { 219 std::unique_ptr<EventData> eventData = 220 std::make_unique<EventData>( 221 EventData 222 { 223 std::get<groupPos>(event), 224 std::get<handlerObjPos>(prop), 225 std::get<actionsPos>(event) 226 } 227 ); 228 std::unique_ptr<sdbusplus::server::match::match> match = 229 std::make_unique<sdbusplus::server::match::match>( 230 _bus, 231 std::get<signaturePos>(prop).c_str(), 232 std::bind(std::mem_fn(&Zone::handleEvent), 233 this, 234 std::placeholders::_1, 235 eventData.get()) 236 ); 237 _signalEvents.emplace_back(std::move(eventData), std::move(match)); 238 } 239 // Attach a timer to run the action of an event 240 auto eventTimer = std::get<timerPos>(event); 241 if (std::get<intervalPos>(eventTimer) != seconds(0)) 242 { 243 std::unique_ptr<util::Timer> timer = 244 std::make_unique<util::Timer>( 245 _sdEvents, 246 [this, 247 action = &(std::get<actionsPos>(event)), 248 group = &(std::get<groupPos>(event))]() 249 { 250 this->timerExpired(*group, *action); 251 }); 252 if (!timer->running()) 253 { 254 timer->start(std::get<intervalPos>(eventTimer), 255 util::Timer::TimerType::repeating); 256 } 257 _timerEvents.emplace_back(std::move(timer)); 258 } 259 // Run action functions for initial event state 260 std::for_each( 261 std::get<actionsPos>(event).begin(), 262 std::get<actionsPos>(event).end(), 263 [this, &event](auto const& action) 264 { 265 action(*this, 266 std::get<groupPos>(event)); 267 }); 268 } 269 270 void Zone::removeEvent(const SetSpeedEvent& event) 271 { 272 // Find the signal event to be removed 273 auto it = std::find_if( 274 _signalEvents.begin(), 275 _signalEvents.end(), 276 [&event](auto const& se) 277 { 278 auto seEventData = *std::get<signalEventDataPos>(se); 279 if (std::get<eventActionsPos>(seEventData).size() != 280 std::get<actionsPos>(event).size()) 281 { 282 return false; 283 } 284 else 285 { 286 // TODO openbmc/openbmc#2328 - Use the action function target 287 // for comparison 288 auto actsEqual = [](auto const& a1, 289 auto const& a2) 290 { 291 return a1.target_type().name() == 292 a2.target_type().name(); 293 }; 294 return 295 ( 296 std::get<eventGroupPos>(seEventData) == 297 std::get<groupPos>(event) && 298 std::equal(std::get<actionsPos>(event).begin(), 299 std::get<actionsPos>(event).end(), 300 std::get<eventActionsPos>(seEventData).begin(), 301 actsEqual) 302 ); 303 } 304 }); 305 if (it != std::end(_signalEvents)) 306 { 307 std::get<signalEventDataPos>(*it).reset(); 308 std::get<signalMatchPos>(*it).reset(); 309 _signalEvents.erase(it); 310 } 311 } 312 313 void Zone::refreshProperty(sdbusplus::bus::bus& bus, 314 const std::string& path, 315 const std::string& iface, 316 const std::string& prop) 317 { 318 PropertyVariantType property; 319 getProperty(_bus, path, iface, prop, property); 320 setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property); 321 } 322 323 void Zone::getProperty(sdbusplus::bus::bus& bus, 324 const std::string& path, 325 const std::string& iface, 326 const std::string& prop, 327 PropertyVariantType& value) 328 { 329 auto serv = util::SDBusPlus::getService(bus, path, iface); 330 auto hostCall = bus.new_method_call(serv.c_str(), 331 path.c_str(), 332 "org.freedesktop.DBus.Properties", 333 "Get"); 334 hostCall.append(iface); 335 hostCall.append(prop); 336 auto hostResponseMsg = bus.call(hostCall); 337 if (hostResponseMsg.is_method_error()) 338 { 339 log<level::INFO>("Host call response error for retrieving property"); 340 elog<InternalFailure>(); 341 } 342 hostResponseMsg.read(value); 343 } 344 345 void Zone::timerExpired(Group eventGroup, std::vector<Action> eventActions) 346 { 347 // Perform the actions 348 std::for_each(eventActions.begin(), 349 eventActions.end(), 350 [this, &eventGroup](auto const& action) 351 { 352 action(*this, eventGroup); 353 }); 354 } 355 356 void Zone::handleEvent(sdbusplus::message::message& msg, 357 const EventData* eventData) 358 { 359 // Handle the callback 360 std::get<eventHandlerPos>(*eventData)(_bus, msg, *this); 361 // Perform the actions 362 std::for_each( 363 std::get<eventActionsPos>(*eventData).begin(), 364 std::get<eventActionsPos>(*eventData).end(), 365 [this, &eventData](auto const& action) 366 { 367 action(*this, 368 std::get<eventGroupPos>(*eventData)); 369 }); 370 } 371 372 } 373 } 374 } 375