xref: /openbmc/phosphor-fan-presence/control/json/actions/pcie_card_floors.cpp (revision 64b5ac203518568ec8b7569d0e785352278f2472)
1d4c7fb7eSMatt Spinler /**
2b2e9a4fcSMike Capps  * Copyright © 2022 IBM Corporation
3d4c7fb7eSMatt Spinler  *
4d4c7fb7eSMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
5d4c7fb7eSMatt Spinler  * you may not use this file except in compliance with the License.
6d4c7fb7eSMatt Spinler  * You may obtain a copy of the License at
7d4c7fb7eSMatt Spinler  *
8d4c7fb7eSMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
9d4c7fb7eSMatt Spinler  *
10d4c7fb7eSMatt Spinler  * Unless required by applicable law or agreed to in writing, software
11d4c7fb7eSMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
12d4c7fb7eSMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d4c7fb7eSMatt Spinler  * See the License for the specific language governing permissions and
14d4c7fb7eSMatt Spinler  * limitations under the License.
15d4c7fb7eSMatt Spinler  */
16d4c7fb7eSMatt Spinler #include "pcie_card_floors.hpp"
17d4c7fb7eSMatt Spinler 
18d4c7fb7eSMatt Spinler #include "../manager.hpp"
19d4c7fb7eSMatt Spinler #include "json_config.hpp"
20d4c7fb7eSMatt Spinler #include "sdbusplus.hpp"
21d4c7fb7eSMatt Spinler #include "sdeventplus.hpp"
22d4c7fb7eSMatt Spinler 
23*64b5ac20SAnwaar Hadi #include <phosphor-logging/lg2.hpp>
24*64b5ac20SAnwaar Hadi 
25d4c7fb7eSMatt Spinler namespace phosphor::fan::control::json
26d4c7fb7eSMatt Spinler {
27d4c7fb7eSMatt Spinler 
28d4c7fb7eSMatt Spinler constexpr auto floorIndexParam = "pcie_floor_index";
29d4c7fb7eSMatt Spinler constexpr auto pcieDeviceIface =
30d4c7fb7eSMatt Spinler     "xyz.openbmc_project.Inventory.Item.PCIeDevice";
31d4c7fb7eSMatt Spinler constexpr auto powerStateIface =
32d4c7fb7eSMatt Spinler     "xyz.openbmc_project.State.Decorator.PowerState";
33d4c7fb7eSMatt Spinler constexpr auto deviceIDProp = "Function0DeviceId";
34d4c7fb7eSMatt Spinler constexpr auto vendorIDProp = "Function0VendorId";
35d4c7fb7eSMatt Spinler constexpr auto subsystemIDProp = "Function0SubsystemId";
36d4c7fb7eSMatt Spinler constexpr auto subsystemVendorIDProp = "Function0SubsystemVendorId";
37d4c7fb7eSMatt Spinler 
PCIeCardFloors(const json & jsonObj,const std::vector<Group> & groups)38d4c7fb7eSMatt Spinler PCIeCardFloors::PCIeCardFloors(const json& jsonObj,
39d4c7fb7eSMatt Spinler                                const std::vector<Group>& groups) :
40d4c7fb7eSMatt Spinler     ActionBase(jsonObj, groups)
41d4c7fb7eSMatt Spinler {
42d4c7fb7eSMatt Spinler     loadCardJSON(jsonObj);
43d4c7fb7eSMatt Spinler }
44d4c7fb7eSMatt Spinler 
run(Zone & zone)45d4c7fb7eSMatt Spinler void PCIeCardFloors::run(Zone& zone)
46d4c7fb7eSMatt Spinler {
47d4c7fb7eSMatt Spinler     if (_settleTimer)
48d4c7fb7eSMatt Spinler     {
49d4c7fb7eSMatt Spinler         _settleTimer->setEnabled(false);
50d4c7fb7eSMatt Spinler     }
51d4c7fb7eSMatt Spinler     else
52d4c7fb7eSMatt Spinler     {
53d4c7fb7eSMatt Spinler         _settleTimer =
54d4c7fb7eSMatt Spinler             std::make_unique<Timer>(util::SDEventPlus::getEvent(),
55b2e9a4fcSMike Capps                                     [&zone, this](Timer&) { execute(); });
56d4c7fb7eSMatt Spinler     }
57d4c7fb7eSMatt Spinler     _settleTimer->restartOnce(_settleTime);
58d4c7fb7eSMatt Spinler }
59d4c7fb7eSMatt Spinler 
execute()60b2e9a4fcSMike Capps void PCIeCardFloors::execute()
61d4c7fb7eSMatt Spinler {
62d4c7fb7eSMatt Spinler     size_t hotCards = 0;
63d4c7fb7eSMatt Spinler     size_t numTempSensorCards = 0;
64d4c7fb7eSMatt Spinler     size_t uninterestingCards = 0;
65d4c7fb7eSMatt Spinler     int32_t floorIndex = -1;
66d4c7fb7eSMatt Spinler 
67d4c7fb7eSMatt Spinler     for (const auto& group : _groups)
68d4c7fb7eSMatt Spinler     {
69d4c7fb7eSMatt Spinler         if (group.getInterface() != powerStateIface)
70d4c7fb7eSMatt Spinler         {
71*64b5ac20SAnwaar Hadi             lg2::debug(
72*64b5ac20SAnwaar Hadi                 "Wrong interface {GROUP_INTERFACE} in PCIe card floor group",
73*64b5ac20SAnwaar Hadi                 "GROUP_INTERFACE", group.getInterface());
74d4c7fb7eSMatt Spinler             continue;
75d4c7fb7eSMatt Spinler         }
76d4c7fb7eSMatt Spinler 
77d4c7fb7eSMatt Spinler         for (const auto& slotPath : group.getMembers())
78d4c7fb7eSMatt Spinler         {
79d4c7fb7eSMatt Spinler             PropertyVariantType powerState;
80d4c7fb7eSMatt Spinler 
81d4c7fb7eSMatt Spinler             try
82d4c7fb7eSMatt Spinler             {
83d4c7fb7eSMatt Spinler                 powerState = Manager::getObjValueVariant(
84d4c7fb7eSMatt Spinler                     slotPath, group.getInterface(), group.getProperty());
85d4c7fb7eSMatt Spinler             }
86d4c7fb7eSMatt Spinler             catch (const std::out_of_range& oore)
87d4c7fb7eSMatt Spinler             {
88*64b5ac20SAnwaar Hadi                 lg2::error("Could not get power state for {SLOT_PATH}",
89*64b5ac20SAnwaar Hadi                            "SLOT_PATH", slotPath);
90d4c7fb7eSMatt Spinler                 continue;
91d4c7fb7eSMatt Spinler             }
92d4c7fb7eSMatt Spinler 
93d4c7fb7eSMatt Spinler             if (std::get<std::string>(powerState) !=
94d4c7fb7eSMatt Spinler                 "xyz.openbmc_project.State.Decorator.PowerState.State.On")
95d4c7fb7eSMatt Spinler             {
96d4c7fb7eSMatt Spinler                 continue;
97d4c7fb7eSMatt Spinler             }
98d4c7fb7eSMatt Spinler 
99d4c7fb7eSMatt Spinler             auto floorIndexOrTempSensor = getFloorIndexFromSlot(slotPath);
100d4c7fb7eSMatt Spinler             if (floorIndexOrTempSensor)
101d4c7fb7eSMatt Spinler             {
102d4c7fb7eSMatt Spinler                 if (std::holds_alternative<int32_t>(*floorIndexOrTempSensor))
103d4c7fb7eSMatt Spinler                 {
104d4c7fb7eSMatt Spinler                     hotCards++;
105d4c7fb7eSMatt Spinler                     floorIndex = std::max(
106d4c7fb7eSMatt Spinler                         floorIndex, std::get<int32_t>(*floorIndexOrTempSensor));
107d4c7fb7eSMatt Spinler                 }
108d4c7fb7eSMatt Spinler                 else
109d4c7fb7eSMatt Spinler                 {
110d4c7fb7eSMatt Spinler                     numTempSensorCards++;
111d4c7fb7eSMatt Spinler                 }
112d4c7fb7eSMatt Spinler             }
113d4c7fb7eSMatt Spinler             else
114d4c7fb7eSMatt Spinler             {
115d4c7fb7eSMatt Spinler                 uninterestingCards++;
116d4c7fb7eSMatt Spinler             }
117d4c7fb7eSMatt Spinler         }
118d4c7fb7eSMatt Spinler     }
119d4c7fb7eSMatt Spinler 
120fbf4703fSPatrick Williams     auto status = std::format(
121d4c7fb7eSMatt Spinler         "Found {} hot cards, {} with temp sensors, {} uninteresting", hotCards,
122d4c7fb7eSMatt Spinler         numTempSensorCards, uninterestingCards);
123d4c7fb7eSMatt Spinler     if (status != _lastStatus)
124d4c7fb7eSMatt Spinler     {
125d4c7fb7eSMatt Spinler         record(status);
126d4c7fb7eSMatt Spinler         _lastStatus = status;
127d4c7fb7eSMatt Spinler     }
128d4c7fb7eSMatt Spinler 
129d4c7fb7eSMatt Spinler     int32_t origIndex = -1;
130d4c7fb7eSMatt Spinler     auto origIndexVariant = Manager::getParameter(floorIndexParam);
131d4c7fb7eSMatt Spinler     if (origIndexVariant)
132d4c7fb7eSMatt Spinler     {
133d4c7fb7eSMatt Spinler         origIndex = std::get<int32_t>(*origIndexVariant);
134d4c7fb7eSMatt Spinler     }
135d4c7fb7eSMatt Spinler 
136d4c7fb7eSMatt Spinler     if (floorIndex != -1)
137d4c7fb7eSMatt Spinler     {
138d4c7fb7eSMatt Spinler         if (origIndex != floorIndex)
139d4c7fb7eSMatt Spinler         {
140fbf4703fSPatrick Williams             record(std::format("Setting {} parameter to {}", floorIndexParam,
141d4c7fb7eSMatt Spinler                                floorIndex));
142d4c7fb7eSMatt Spinler             Manager::setParameter(floorIndexParam, floorIndex);
143d4c7fb7eSMatt Spinler         }
144d4c7fb7eSMatt Spinler     }
145d4c7fb7eSMatt Spinler     else if (origIndexVariant)
146d4c7fb7eSMatt Spinler     {
147fbf4703fSPatrick Williams         record(std::format("Removing parameter {}", floorIndexParam));
148d4c7fb7eSMatt Spinler         Manager::setParameter(floorIndexParam, std::nullopt);
149d4c7fb7eSMatt Spinler     }
150d4c7fb7eSMatt Spinler }
151d4c7fb7eSMatt Spinler 
loadCardJSON(const json & jsonObj)152d4c7fb7eSMatt Spinler void PCIeCardFloors::loadCardJSON(const json& jsonObj)
153d4c7fb7eSMatt Spinler {
154d4c7fb7eSMatt Spinler     bool useConfigSpecificFiles = false;
155d4c7fb7eSMatt Spinler 
156d4c7fb7eSMatt Spinler     if (jsonObj.contains("settle_time"))
157d4c7fb7eSMatt Spinler     {
158d4c7fb7eSMatt Spinler         _settleTime =
159d4c7fb7eSMatt Spinler             std::chrono::seconds(jsonObj.at("settle_time").get<size_t>());
160d4c7fb7eSMatt Spinler     }
161d4c7fb7eSMatt Spinler 
162d4c7fb7eSMatt Spinler     if (jsonObj.contains("use_config_specific_files"))
163d4c7fb7eSMatt Spinler     {
164d4c7fb7eSMatt Spinler         useConfigSpecificFiles =
165d4c7fb7eSMatt Spinler             jsonObj.at("use_config_specific_files").get<bool>();
166d4c7fb7eSMatt Spinler     }
167d4c7fb7eSMatt Spinler 
168d4c7fb7eSMatt Spinler     std::vector<std::string> names;
169d4c7fb7eSMatt Spinler     if (useConfigSpecificFiles)
170d4c7fb7eSMatt Spinler     {
171d4c7fb7eSMatt Spinler         names = phosphor::fan::JsonConfig::getCompatValues();
172d4c7fb7eSMatt Spinler     }
173d4c7fb7eSMatt Spinler 
174d4c7fb7eSMatt Spinler     _cardMetadata = std::make_unique<PCIeCardMetadata>(names);
175d4c7fb7eSMatt Spinler }
176d4c7fb7eSMatt Spinler 
getPCIeDeviceProperty(const std::string & objectPath,const std::string & propertyName)177d4c7fb7eSMatt Spinler uint16_t PCIeCardFloors::getPCIeDeviceProperty(const std::string& objectPath,
178d4c7fb7eSMatt Spinler                                                const std::string& propertyName)
179d4c7fb7eSMatt Spinler {
180d4c7fb7eSMatt Spinler     PropertyVariantType variantValue;
181d4c7fb7eSMatt Spinler     uint16_t value{};
182d4c7fb7eSMatt Spinler 
183d4c7fb7eSMatt Spinler     try
184d4c7fb7eSMatt Spinler     {
185d4c7fb7eSMatt Spinler         variantValue = Manager::getObjValueVariant(objectPath, pcieDeviceIface,
186d4c7fb7eSMatt Spinler                                                    propertyName);
187d4c7fb7eSMatt Spinler     }
188d4c7fb7eSMatt Spinler     catch (const std::out_of_range& oore)
189d4c7fb7eSMatt Spinler     {
190*64b5ac20SAnwaar Hadi         lg2::error(
191*64b5ac20SAnwaar Hadi             "{ACTION_NAME}: Could not get PCIeDevice property {OBJECT_PATH} {PROPERTY_NAME} from cache ",
192*64b5ac20SAnwaar Hadi             "ACTION_NAME", ActionBase::getName(), "OBJECT_PATH", objectPath,
193*64b5ac20SAnwaar Hadi             "PROPERTY_NAME", propertyName);
194d4c7fb7eSMatt Spinler         throw;
195d4c7fb7eSMatt Spinler     }
196d4c7fb7eSMatt Spinler 
197d4c7fb7eSMatt Spinler     try
198d4c7fb7eSMatt Spinler     {
199d4c7fb7eSMatt Spinler         value = std::stoul(std::get<std::string>(variantValue), nullptr, 0);
200d4c7fb7eSMatt Spinler         return value;
201d4c7fb7eSMatt Spinler     }
202d4c7fb7eSMatt Spinler     catch (const std::invalid_argument& e)
203d4c7fb7eSMatt Spinler     {
204*64b5ac20SAnwaar Hadi         lg2::info(
205*64b5ac20SAnwaar Hadi             "{ACTION_NAME}: {OBJECT_PATH} has invalid PCIeDevice property {PROPERTY_NAME} value: {VALUE}",
206*64b5ac20SAnwaar Hadi             "ACTION_NAME", ActionBase::getName(), "OBJECT_PATH", objectPath,
207*64b5ac20SAnwaar Hadi             "PROPERTY_NAME", propertyName, "VALUE",
208*64b5ac20SAnwaar Hadi             std::get<std::string>(variantValue));
209d4c7fb7eSMatt Spinler         throw;
210d4c7fb7eSMatt Spinler     }
211d4c7fb7eSMatt Spinler }
212d4c7fb7eSMatt Spinler 
213d4c7fb7eSMatt Spinler std::optional<std::variant<int32_t, bool>>
getFloorIndexFromSlot(const std::string & slotPath)214d4c7fb7eSMatt Spinler     PCIeCardFloors::getFloorIndexFromSlot(const std::string& slotPath)
215d4c7fb7eSMatt Spinler {
216d4c7fb7eSMatt Spinler     const auto& card = getCardFromSlot(slotPath);
217d4c7fb7eSMatt Spinler 
218d4c7fb7eSMatt Spinler     try
219d4c7fb7eSMatt Spinler     {
220d4c7fb7eSMatt Spinler         auto deviceID = getPCIeDeviceProperty(card, deviceIDProp);
221d4c7fb7eSMatt Spinler         auto vendorID = getPCIeDeviceProperty(card, vendorIDProp);
222d4c7fb7eSMatt Spinler         auto subsystemID = getPCIeDeviceProperty(card, subsystemIDProp);
223dfddd648SPatrick Williams         auto subsystemVendorID =
224dfddd648SPatrick Williams             getPCIeDeviceProperty(card, subsystemVendorIDProp);
225d4c7fb7eSMatt Spinler 
226d4c7fb7eSMatt Spinler         return _cardMetadata->lookup(deviceID, vendorID, subsystemID,
227d4c7fb7eSMatt Spinler                                      subsystemVendorID);
228d4c7fb7eSMatt Spinler     }
229d4c7fb7eSMatt Spinler     catch (const std::exception& e)
230d4c7fb7eSMatt Spinler     {}
231d4c7fb7eSMatt Spinler 
232d4c7fb7eSMatt Spinler     return std::nullopt;
233d4c7fb7eSMatt Spinler }
234d4c7fb7eSMatt Spinler 
getCardFromSlot(const std::string & slotPath)235d4c7fb7eSMatt Spinler const std::string& PCIeCardFloors::getCardFromSlot(const std::string& slotPath)
236d4c7fb7eSMatt Spinler {
237d4c7fb7eSMatt Spinler     auto cardIt = _cards.find(slotPath);
238d4c7fb7eSMatt Spinler 
239d4c7fb7eSMatt Spinler     if (cardIt != _cards.end())
240d4c7fb7eSMatt Spinler     {
241d4c7fb7eSMatt Spinler         return cardIt->second;
242d4c7fb7eSMatt Spinler     }
243d4c7fb7eSMatt Spinler 
244d4c7fb7eSMatt Spinler     // Just the first time, find all the PCIeDevice objects
245d4c7fb7eSMatt Spinler     if (_pcieDevices.empty())
246d4c7fb7eSMatt Spinler     {
247d4c7fb7eSMatt Spinler         _pcieDevices = util::SDBusPlus::getSubTreePaths(
248d4c7fb7eSMatt Spinler             util::SDBusPlus::getBus(), "/", pcieDeviceIface, 0);
249d4c7fb7eSMatt Spinler     }
250d4c7fb7eSMatt Spinler 
251d4c7fb7eSMatt Spinler     // Find the card that plugs in this slot based on if the
252d4c7fb7eSMatt Spinler     // slot is part of the path, like slotA/cardA
253dfddd648SPatrick Williams     auto it = std::find_if(
254dfddd648SPatrick Williams         _pcieDevices.begin(), _pcieDevices.end(), [slotPath](const auto& path) {
255d4c7fb7eSMatt Spinler             return path.find(slotPath + '/') != std::string::npos;
256d4c7fb7eSMatt Spinler         });
257d4c7fb7eSMatt Spinler 
258d4c7fb7eSMatt Spinler     if (it == _pcieDevices.end())
259d4c7fb7eSMatt Spinler     {
260fbf4703fSPatrick Williams         throw std::runtime_error(std::format(
261d4c7fb7eSMatt Spinler             "Could not find PCIe card object path for slot {}", slotPath));
262d4c7fb7eSMatt Spinler     }
263d4c7fb7eSMatt Spinler 
264d4c7fb7eSMatt Spinler     _cards.emplace(slotPath, *it);
265d4c7fb7eSMatt Spinler 
266d4c7fb7eSMatt Spinler     return _cards.at(slotPath);
267d4c7fb7eSMatt Spinler }
268d4c7fb7eSMatt Spinler 
269d4c7fb7eSMatt Spinler } // namespace phosphor::fan::control::json
270