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