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 & zone)45 void PCIeCardFloors::run(Zone& zone)
46 {
47 if (_settleTimer)
48 {
49 _settleTimer->setEnabled(false);
50 }
51 else
52 {
53 _settleTimer =
54 std::make_unique<Timer>(util::SDEventPlus::getEvent(),
55 [&zone, this](Timer&) { execute(); });
56 }
57 _settleTimer->restartOnce(_settleTime);
58 }
59
execute()60 void PCIeCardFloors::execute()
61 {
62 size_t hotCards = 0;
63 size_t numTempSensorCards = 0;
64 size_t uninterestingCards = 0;
65 int32_t floorIndex = -1;
66
67 for (const auto& group : _groups)
68 {
69 if (group.getInterface() != powerStateIface)
70 {
71 lg2::debug(
72 "Wrong interface {GROUP_INTERFACE} in PCIe card floor group",
73 "GROUP_INTERFACE", group.getInterface());
74 continue;
75 }
76
77 for (const auto& slotPath : group.getMembers())
78 {
79 PropertyVariantType powerState;
80
81 try
82 {
83 powerState = Manager::getObjValueVariant(
84 slotPath, group.getInterface(), group.getProperty());
85 }
86 catch (const std::out_of_range& oore)
87 {
88 lg2::error("Could not get power state for {SLOT_PATH}",
89 "SLOT_PATH", slotPath);
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 = std::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(std::format("Setting {} parameter to {}", floorIndexParam,
141 floorIndex));
142 Manager::setParameter(floorIndexParam, floorIndex);
143 }
144 }
145 else if (origIndexVariant)
146 {
147 record(std::format("Removing parameter {}", floorIndexParam));
148 Manager::setParameter(floorIndexParam, std::nullopt);
149 }
150 }
151
loadCardJSON(const json & jsonObj)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
getPCIeDeviceProperty(const std::string & objectPath,const std::string & propertyName)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 lg2::error(
191 "{ACTION_NAME}: Could not get PCIeDevice property {OBJECT_PATH} {PROPERTY_NAME} from cache ",
192 "ACTION_NAME", ActionBase::getName(), "OBJECT_PATH", objectPath,
193 "PROPERTY_NAME", propertyName);
194 throw;
195 }
196
197 try
198 {
199 value = std::stoul(std::get<std::string>(variantValue), nullptr, 0);
200 return value;
201 }
202 catch (const std::invalid_argument& e)
203 {
204 lg2::info(
205 "{ACTION_NAME}: {OBJECT_PATH} has invalid PCIeDevice property {PROPERTY_NAME} value: {VALUE}",
206 "ACTION_NAME", ActionBase::getName(), "OBJECT_PATH", objectPath,
207 "PROPERTY_NAME", propertyName, "VALUE",
208 std::get<std::string>(variantValue));
209 throw;
210 }
211 }
212
213 std::optional<std::variant<int32_t, bool>>
getFloorIndexFromSlot(const std::string & slotPath)214 PCIeCardFloors::getFloorIndexFromSlot(const std::string& slotPath)
215 {
216 const auto& card = getCardFromSlot(slotPath);
217
218 try
219 {
220 auto deviceID = getPCIeDeviceProperty(card, deviceIDProp);
221 auto vendorID = getPCIeDeviceProperty(card, vendorIDProp);
222 auto subsystemID = getPCIeDeviceProperty(card, subsystemIDProp);
223 auto subsystemVendorID =
224 getPCIeDeviceProperty(card, subsystemVendorIDProp);
225
226 return _cardMetadata->lookup(deviceID, vendorID, subsystemID,
227 subsystemVendorID);
228 }
229 catch (const std::exception& e)
230 {}
231
232 return std::nullopt;
233 }
234
getCardFromSlot(const std::string & slotPath)235 const std::string& PCIeCardFloors::getCardFromSlot(const std::string& slotPath)
236 {
237 auto cardIt = _cards.find(slotPath);
238
239 if (cardIt != _cards.end())
240 {
241 return cardIt->second;
242 }
243
244 // Just the first time, find all the PCIeDevice objects
245 if (_pcieDevices.empty())
246 {
247 _pcieDevices = util::SDBusPlus::getSubTreePaths(
248 util::SDBusPlus::getBus(), "/", pcieDeviceIface, 0);
249 }
250
251 // Find the card that plugs in this slot based on if the
252 // slot is part of the path, like slotA/cardA
253 auto it = std::find_if(
254 _pcieDevices.begin(), _pcieDevices.end(), [slotPath](const auto& path) {
255 return path.find(slotPath + '/') != std::string::npos;
256 });
257
258 if (it == _pcieDevices.end())
259 {
260 throw std::runtime_error(std::format(
261 "Could not find PCIe card object path for slot {}", slotPath));
262 }
263
264 _cards.emplace(slotPath, *it);
265
266 return _cards.at(slotPath);
267 }
268
269 } // namespace phosphor::fan::control::json
270