xref: /openbmc/phosphor-fan-presence/control/json/actions/pcie_card_floors.cpp (revision 64b5ac203518568ec8b7569d0e785352278f2472)
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