1 /** 2 * Copyright © 2021 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 "pcie_card_floors.hpp" 17 18 #include "../manager.hpp" 19 #include "json_config.hpp" 20 #include "sdbusplus.hpp" 21 #include "sdeventplus.hpp" 22 23 namespace phosphor::fan::control::json 24 { 25 26 constexpr auto floorIndexParam = "pcie_floor_index"; 27 constexpr auto pcieDeviceIface = 28 "xyz.openbmc_project.Inventory.Item.PCIeDevice"; 29 constexpr auto powerStateIface = 30 "xyz.openbmc_project.State.Decorator.PowerState"; 31 constexpr auto deviceIDProp = "Function0DeviceId"; 32 constexpr auto vendorIDProp = "Function0VendorId"; 33 constexpr auto subsystemIDProp = "Function0SubsystemId"; 34 constexpr auto subsystemVendorIDProp = "Function0SubsystemVendorId"; 35 36 PCIeCardFloors::PCIeCardFloors(const json& jsonObj, 37 const std::vector<Group>& groups) : 38 ActionBase(jsonObj, groups) 39 { 40 loadCardJSON(jsonObj); 41 } 42 43 void PCIeCardFloors::run(Zone& zone) 44 { 45 if (_settleTimer) 46 { 47 _settleTimer->setEnabled(false); 48 } 49 else 50 { 51 _settleTimer = 52 std::make_unique<Timer>(util::SDEventPlus::getEvent(), 53 [&zone, this](Timer&) { execute(zone); }); 54 } 55 _settleTimer->restartOnce(_settleTime); 56 } 57 58 void PCIeCardFloors::execute(Zone& zone) 59 { 60 size_t hotCards = 0; 61 size_t numTempSensorCards = 0; 62 size_t uninterestingCards = 0; 63 int32_t floorIndex = -1; 64 65 for (const auto& group : _groups) 66 { 67 if (group.getInterface() != powerStateIface) 68 { 69 log<level::DEBUG>( 70 fmt::format("Wrong interface {} in PCIe card floor group", 71 group.getInterface()) 72 .c_str()); 73 continue; 74 } 75 76 for (const auto& slotPath : group.getMembers()) 77 { 78 PropertyVariantType powerState; 79 80 try 81 { 82 powerState = Manager::getObjValueVariant( 83 slotPath, group.getInterface(), group.getProperty()); 84 } 85 catch (const std::out_of_range& oore) 86 { 87 log<level::ERR>( 88 fmt::format("Could not get power state for {}", slotPath) 89 .c_str()); 90 continue; 91 } 92 93 if (std::get<std::string>(powerState) != 94 "xyz.openbmc_project.State.Decorator.PowerState.State.On") 95 { 96 continue; 97 } 98 99 auto floorIndexOrTempSensor = getFloorIndexFromSlot(slotPath); 100 if (floorIndexOrTempSensor) 101 { 102 if (std::holds_alternative<int32_t>(*floorIndexOrTempSensor)) 103 { 104 hotCards++; 105 floorIndex = std::max( 106 floorIndex, std::get<int32_t>(*floorIndexOrTempSensor)); 107 } 108 else 109 { 110 numTempSensorCards++; 111 } 112 } 113 else 114 { 115 uninterestingCards++; 116 } 117 } 118 } 119 120 auto status = fmt::format( 121 "Found {} hot cards, {} with temp sensors, {} uninteresting", hotCards, 122 numTempSensorCards, uninterestingCards); 123 if (status != _lastStatus) 124 { 125 record(status); 126 _lastStatus = status; 127 } 128 129 int32_t origIndex = -1; 130 auto origIndexVariant = Manager::getParameter(floorIndexParam); 131 if (origIndexVariant) 132 { 133 origIndex = std::get<int32_t>(*origIndexVariant); 134 } 135 136 if (floorIndex != -1) 137 { 138 if (origIndex != floorIndex) 139 { 140 record(fmt::format("Setting {} parameter to {}", floorIndexParam, 141 floorIndex)); 142 Manager::setParameter(floorIndexParam, floorIndex); 143 } 144 } 145 else if (origIndexVariant) 146 { 147 record(fmt::format("Removing parameter {}", floorIndexParam)); 148 Manager::setParameter(floorIndexParam, std::nullopt); 149 } 150 } 151 152 void PCIeCardFloors::loadCardJSON(const json& jsonObj) 153 { 154 std::string baseConfigFile; 155 bool useConfigSpecificFiles = false; 156 157 if (jsonObj.contains("settle_time")) 158 { 159 _settleTime = 160 std::chrono::seconds(jsonObj.at("settle_time").get<size_t>()); 161 } 162 163 if (jsonObj.contains("use_config_specific_files")) 164 { 165 useConfigSpecificFiles = 166 jsonObj.at("use_config_specific_files").get<bool>(); 167 } 168 169 std::vector<std::string> names; 170 if (useConfigSpecificFiles) 171 { 172 names = phosphor::fan::JsonConfig::getCompatValues(); 173 } 174 175 _cardMetadata = std::make_unique<PCIeCardMetadata>(names); 176 } 177 178 uint16_t PCIeCardFloors::getPCIeDeviceProperty(const std::string& objectPath, 179 const std::string& propertyName) 180 { 181 PropertyVariantType variantValue; 182 uint16_t value{}; 183 184 try 185 { 186 variantValue = Manager::getObjValueVariant(objectPath, pcieDeviceIface, 187 propertyName); 188 } 189 catch (const std::out_of_range& oore) 190 { 191 log<level::ERR>( 192 fmt::format( 193 "{}: Could not get PCIeDevice property {} {} from cache ", 194 ActionBase::getName(), objectPath, propertyName) 195 .c_str()); 196 throw; 197 } 198 199 try 200 { 201 value = std::stoul(std::get<std::string>(variantValue), nullptr, 0); 202 return value; 203 } 204 catch (const std::invalid_argument& e) 205 { 206 log<level::INFO>( 207 fmt::format("{}: {} has invalid PCIeDevice property {} value: {}", 208 ActionBase::getName(), objectPath, propertyName, 209 std::get<std::string>(variantValue)) 210 .c_str()); 211 throw; 212 } 213 } 214 215 std::optional<std::variant<int32_t, bool>> 216 PCIeCardFloors::getFloorIndexFromSlot(const std::string& slotPath) 217 { 218 const auto& card = getCardFromSlot(slotPath); 219 220 try 221 { 222 auto deviceID = getPCIeDeviceProperty(card, deviceIDProp); 223 auto vendorID = getPCIeDeviceProperty(card, vendorIDProp); 224 auto subsystemID = getPCIeDeviceProperty(card, subsystemIDProp); 225 auto subsystemVendorID = 226 getPCIeDeviceProperty(card, subsystemVendorIDProp); 227 228 return _cardMetadata->lookup(deviceID, vendorID, subsystemID, 229 subsystemVendorID); 230 } 231 catch (const std::exception& e) 232 {} 233 234 return std::nullopt; 235 } 236 237 const std::string& PCIeCardFloors::getCardFromSlot(const std::string& slotPath) 238 { 239 auto cardIt = _cards.find(slotPath); 240 241 if (cardIt != _cards.end()) 242 { 243 return cardIt->second; 244 } 245 246 // Just the first time, find all the PCIeDevice objects 247 if (_pcieDevices.empty()) 248 { 249 _pcieDevices = util::SDBusPlus::getSubTreePaths( 250 util::SDBusPlus::getBus(), "/", pcieDeviceIface, 0); 251 } 252 253 // Find the card that plugs in this slot based on if the 254 // slot is part of the path, like slotA/cardA 255 auto it = std::find_if( 256 _pcieDevices.begin(), _pcieDevices.end(), [slotPath](const auto& path) { 257 return path.find(slotPath + '/') != std::string::npos; 258 }); 259 260 if (it == _pcieDevices.end()) 261 { 262 throw std::runtime_error(fmt::format( 263 "Could not find PCIe card object path for slot {}", slotPath)); 264 } 265 266 _cards.emplace(slotPath, *it); 267 268 return _cards.at(slotPath); 269 } 270 271 } // namespace phosphor::fan::control::json 272