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