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