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
PCIeCardFloors(const json & jsonObj,const std::vector<Group> & groups)36 PCIeCardFloors::PCIeCardFloors(const json& jsonObj,
37 const std::vector<Group>& groups) :
38 ActionBase(jsonObj, groups)
39 {
40 loadCardJSON(jsonObj);
41 }
42
run(Zone & zone)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
execute()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 std::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 std::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 = 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 log<level::ERR>(
191 std::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 std::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>>
getFloorIndexFromSlot(const std::string & slotPath)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 =
225 getPCIeDeviceProperty(card, 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
getCardFromSlot(const std::string & slotPath)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(
255 _pcieDevices.begin(), _pcieDevices.end(), [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(std::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