1 /** 2 * Copyright © 2022 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(); }); 54 } 55 _settleTimer->restartOnce(_settleTime); 56 } 57 58 void PCIeCardFloors::execute() 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 bool useConfigSpecificFiles = false; 155 156 if (jsonObj.contains("settle_time")) 157 { 158 _settleTime = 159 std::chrono::seconds(jsonObj.at("settle_time").get<size_t>()); 160 } 161 162 if (jsonObj.contains("use_config_specific_files")) 163 { 164 useConfigSpecificFiles = 165 jsonObj.at("use_config_specific_files").get<bool>(); 166 } 167 168 std::vector<std::string> names; 169 if (useConfigSpecificFiles) 170 { 171 names = phosphor::fan::JsonConfig::getCompatValues(); 172 } 173 174 _cardMetadata = std::make_unique<PCIeCardMetadata>(names); 175 } 176 177 uint16_t PCIeCardFloors::getPCIeDeviceProperty(const std::string& objectPath, 178 const std::string& propertyName) 179 { 180 PropertyVariantType variantValue; 181 uint16_t value{}; 182 183 try 184 { 185 variantValue = Manager::getObjValueVariant(objectPath, pcieDeviceIface, 186 propertyName); 187 } 188 catch (const std::out_of_range& oore) 189 { 190 log<level::ERR>( 191 fmt::format( 192 "{}: Could not get PCIeDevice property {} {} from cache ", 193 ActionBase::getName(), objectPath, propertyName) 194 .c_str()); 195 throw; 196 } 197 198 try 199 { 200 value = std::stoul(std::get<std::string>(variantValue), nullptr, 0); 201 return value; 202 } 203 catch (const std::invalid_argument& e) 204 { 205 log<level::INFO>( 206 fmt::format("{}: {} has invalid PCIeDevice property {} value: {}", 207 ActionBase::getName(), objectPath, propertyName, 208 std::get<std::string>(variantValue)) 209 .c_str()); 210 throw; 211 } 212 } 213 214 std::optional<std::variant<int32_t, bool>> 215 PCIeCardFloors::getFloorIndexFromSlot(const std::string& slotPath) 216 { 217 const auto& card = getCardFromSlot(slotPath); 218 219 try 220 { 221 auto deviceID = getPCIeDeviceProperty(card, deviceIDProp); 222 auto vendorID = getPCIeDeviceProperty(card, vendorIDProp); 223 auto subsystemID = getPCIeDeviceProperty(card, subsystemIDProp); 224 auto subsystemVendorID = getPCIeDeviceProperty(card, 225 subsystemVendorIDProp); 226 227 return _cardMetadata->lookup(deviceID, vendorID, subsystemID, 228 subsystemVendorID); 229 } 230 catch (const std::exception& e) 231 {} 232 233 return std::nullopt; 234 } 235 236 const std::string& PCIeCardFloors::getCardFromSlot(const std::string& slotPath) 237 { 238 auto cardIt = _cards.find(slotPath); 239 240 if (cardIt != _cards.end()) 241 { 242 return cardIt->second; 243 } 244 245 // Just the first time, find all the PCIeDevice objects 246 if (_pcieDevices.empty()) 247 { 248 _pcieDevices = util::SDBusPlus::getSubTreePaths( 249 util::SDBusPlus::getBus(), "/", pcieDeviceIface, 0); 250 } 251 252 // Find the card that plugs in this slot based on if the 253 // slot is part of the path, like slotA/cardA 254 auto it = std::find_if(_pcieDevices.begin(), _pcieDevices.end(), 255 [slotPath](const auto& path) { 256 return path.find(slotPath + '/') != std::string::npos; 257 }); 258 259 if (it == _pcieDevices.end()) 260 { 261 throw std::runtime_error(fmt::format( 262 "Could not find PCIe card object path for slot {}", slotPath)); 263 } 264 265 _cards.emplace(slotPath, *it); 266 267 return _cards.at(slotPath); 268 } 269 270 } // namespace phosphor::fan::control::json 271