blob: fb92dd185161421a79f1d9ce544c80410151b2e2 [file] [log] [blame]
Jonathan Doman94c94bf2020-10-05 23:25:45 -07001// Copyright (c) 2020 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "speed_select.hpp"
16
17#include "cpuinfo.hpp"
Jonathan Doman703a1852020-11-11 13:04:02 -080018#include "cpuinfo_utils.hpp"
Jonathan Doman94c94bf2020-10-05 23:25:45 -070019
20#include <peci.h>
21
Jonathan Doman49ea8302022-05-26 14:29:46 -070022#include <boost/asio/error.hpp>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070023#include <boost/asio/steady_timer.hpp>
Jonathan Doman703a1852020-11-11 13:04:02 -080024#include <xyz/openbmc_project/Common/Device/error.hpp>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070025#include <xyz/openbmc_project/Common/error.hpp>
26#include <xyz/openbmc_project/Control/Processor/CurrentOperatingConfig/server.hpp>
27#include <xyz/openbmc_project/Inventory/Item/Cpu/OperatingConfig/server.hpp>
28
Jonathan Doman16a2ced2021-11-01 11:13:22 -070029#include <algorithm>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070030#include <iostream>
31#include <memory>
Jonathan Doman16a2ced2021-11-01 11:13:22 -070032#include <stdexcept>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070033#include <string>
34
35namespace cpu_info
36{
37namespace sst
38{
39
Jonathan Doman16a2ced2021-11-01 11:13:22 -070040// Specialize char to print the integer value instead of ascii. We basically
41// never want to print a single ascii char.
42std::ostream& operator<<(std::ostream& os, uint8_t value)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070043{
Jonathan Doman16a2ced2021-11-01 11:13:22 -070044 return os << static_cast<int>(value);
Jonathan Doman94c94bf2020-10-05 23:25:45 -070045}
46
Jonathan Doman16a2ced2021-11-01 11:13:22 -070047bool checkPECIStatus(EPECIStatus libStatus, uint8_t completionCode)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070048{
49 if (libStatus != PECI_CC_SUCCESS || completionCode != PECI_DEV_CC_SUCCESS)
50 {
51 std::cerr << "PECI command failed."
52 << " Driver Status = " << libStatus << ","
Jonathan Doman16a2ced2021-11-01 11:13:22 -070053 << " Completion Code = " << completionCode << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -070054 return false;
55 }
56 return true;
57}
58
Jonathan Doman49ea8302022-05-26 14:29:46 -070059std::vector<uint32_t> convertMaskToList(std::bitset<64> mask)
60{
61 std::vector<uint32_t> bitList;
62 for (size_t i = 0; i < mask.size(); ++i)
63 {
64 if (mask.test(i))
65 {
66 bitList.push_back(i);
67 }
68 }
69 return bitList;
70}
71
Jonathan Doman16a2ced2021-11-01 11:13:22 -070072static std::vector<BackendProvider>& getProviders()
Jonathan Doman94c94bf2020-10-05 23:25:45 -070073{
Jonathan Doman16a2ced2021-11-01 11:13:22 -070074 static auto* providers = new std::vector<BackendProvider>;
75 return *providers;
76}
Jonathan Doman94c94bf2020-10-05 23:25:45 -070077
Jonathan Doman16a2ced2021-11-01 11:13:22 -070078void registerBackend(BackendProvider providerFn)
79{
80 getProviders().push_back(providerFn);
81}
Jonathan Doman94c94bf2020-10-05 23:25:45 -070082
Jonathan Doman16a2ced2021-11-01 11:13:22 -070083std::unique_ptr<SSTInterface> getInstance(uint8_t address, CPUModel model)
84{
85 DEBUG_PRINT << "Searching for provider for " << address << ", model "
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -080086 << std::hex << model << std::dec << '\n';
Jonathan Doman16a2ced2021-11-01 11:13:22 -070087 for (const auto& provider : getProviders())
Jonathan Doman94c94bf2020-10-05 23:25:45 -070088 {
Jonathan Doman94c94bf2020-10-05 23:25:45 -070089 try
90 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -070091 auto interface = provider(address, model);
92 DEBUG_PRINT << "returned " << interface << '\n';
93 if (interface)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070094 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -070095 return interface;
Jonathan Doman94c94bf2020-10-05 23:25:45 -070096 }
97 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -070098 catch (...)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070099 {}
100 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700101 DEBUG_PRINT << "No supported backends found\n";
102 return nullptr;
103}
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700104
105using BaseCurrentOperatingConfig =
Jason M. Bills33ae81f2023-04-26 09:06:08 -0700106 sdbusplus::server::object_t<sdbusplus::server::xyz::openbmc_project::
107 control::processor::CurrentOperatingConfig>;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700108
109using BaseOperatingConfig =
Jason M. Bills33ae81f2023-04-26 09:06:08 -0700110 sdbusplus::server::object_t<sdbusplus::server::xyz::openbmc_project::
111 inventory::item::cpu::OperatingConfig>;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700112
113class OperatingConfig : public BaseOperatingConfig
114{
115 public:
116 std::string path;
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700117 unsigned int level;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700118
119 public:
120 using BaseOperatingConfig::BaseOperatingConfig;
Patrick Williams77b9c472022-07-22 19:26:57 -0500121 OperatingConfig(sdbusplus::bus_t& bus, unsigned int level_,
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700122 std::string path_) :
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700123 BaseOperatingConfig(bus, path_.c_str(), action::defer_emit),
124 path(std::move(path_)), level(level_)
125 {}
126};
127
128class CPUConfig : public BaseCurrentOperatingConfig
129{
130 private:
131 /** Objects describing all available SST configs - not modifiable. */
132 std::vector<std::unique_ptr<OperatingConfig>> availConfigs;
Patrick Williams77b9c472022-07-22 19:26:57 -0500133 sdbusplus::bus_t& bus;
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700134 const uint8_t peciAddress;
Jonathan Doman703a1852020-11-11 13:04:02 -0800135 const std::string path; ///< D-Bus path of CPU object
136 const CPUModel cpuModel;
Jonathan Doman703a1852020-11-11 13:04:02 -0800137
138 // Keep mutable copies of the properties so we can cache values that we
139 // retrieve in the getters. We don't want to throw an error on a D-Bus
140 // get-property call (extra error handling in clients), so by caching we can
141 // hide any temporary hiccup in PECI communication.
142 // These values can be changed by in-band software so we have to do a full
143 // PECI read on every get-property, and can't assume that values will change
144 // only when set-property is done.
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700145 mutable unsigned int currentLevel;
Jonathan Doman703a1852020-11-11 13:04:02 -0800146 mutable bool bfEnabled;
Jonathan Doman703a1852020-11-11 13:04:02 -0800147
148 /**
149 * Enforce common pre-conditions for D-Bus set property handlers.
150 */
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700151 void setPropertyCheckOrThrow(SSTInterface& sst)
Jonathan Doman703a1852020-11-11 13:04:02 -0800152 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700153 if (!sst.supportsControl())
Jonathan Doman703a1852020-11-11 13:04:02 -0800154 {
155 throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
156 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700157 if (hostState != HostState::postComplete || !sst.ready())
Jonathan Doman703a1852020-11-11 13:04:02 -0800158 {
159 throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
160 }
161 }
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700162
163 public:
Patrick Williams77b9c472022-07-22 19:26:57 -0500164 CPUConfig(sdbusplus::bus_t& bus_, uint8_t index, CPUModel model) :
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700165 BaseCurrentOperatingConfig(bus_, generatePath(index).c_str(),
166 action::defer_emit),
Jonathan Doman703a1852020-11-11 13:04:02 -0800167 bus(bus_), peciAddress(index + MIN_CLIENT_ADDR),
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700168 path(generatePath(index)), cpuModel(model), currentLevel(0),
169 bfEnabled(false)
Jonathan Doman703a1852020-11-11 13:04:02 -0800170 {}
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700171
172 //
173 // D-Bus Property Overrides
174 //
175
176 sdbusplus::message::object_path appliedConfig() const override
177 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700178 DEBUG_PRINT << "Reading AppliedConfig\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800179 if (hostState != HostState::off)
180 {
181 // Otherwise, try to read current state
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700182 auto sst = getInstance(peciAddress, cpuModel);
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800183 if (!sst || !sst->ready())
Jonathan Doman703a1852020-11-11 13:04:02 -0800184 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700185 std::cerr << __func__
186 << ": Failed to get SST provider instance\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800187 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700188 else
Jonathan Doman703a1852020-11-11 13:04:02 -0800189 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700190 try
191 {
192 currentLevel = sst->currentLevel();
193 }
194 catch (const PECIError& error)
195 {
196 std::cerr << "Failed to get SST-PP level: " << error.what()
197 << "\n";
198 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800199 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800200 }
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800201 return generateConfigPath(currentLevel);
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700202 }
203
204 bool baseSpeedPriorityEnabled() const override
205 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700206 DEBUG_PRINT << "Reading BaseSpeedPriorityEnabled\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800207 if (hostState != HostState::off)
208 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700209 auto sst = getInstance(peciAddress, cpuModel);
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800210 if (!sst || !sst->ready())
Jonathan Doman703a1852020-11-11 13:04:02 -0800211 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700212 std::cerr << __func__
213 << ": Failed to get SST provider instance\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800214 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700215 else
Jonathan Doman703a1852020-11-11 13:04:02 -0800216 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700217 try
218 {
219 bfEnabled = sst->bfEnabled(currentLevel);
220 }
221 catch (const PECIError& error)
222 {
223 std::cerr << "Failed to get SST-BF status: " << error.what()
224 << "\n";
225 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800226 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800227 }
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800228 return bfEnabled;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700229 }
230
231 sdbusplus::message::object_path
Jonathan Doman703a1852020-11-11 13:04:02 -0800232 appliedConfig(sdbusplus::message::object_path value) override
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700233 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700234 DEBUG_PRINT << "Writing AppliedConfig\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800235 const OperatingConfig* newConfig = nullptr;
236 for (const auto& config : availConfigs)
237 {
238 if (config->path == value.str)
239 {
240 newConfig = config.get();
241 }
242 }
243
244 if (newConfig == nullptr)
245 {
246 throw sdbusplus::xyz::openbmc_project::Common::Error::
247 InvalidArgument();
248 }
249
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700250 auto sst = getInstance(peciAddress, cpuModel);
251 if (!sst)
252 {
253 std::cerr << __func__ << ": Failed to get SST provider instance\n";
254 return sdbusplus::message::object_path();
255 }
256 setPropertyCheckOrThrow(*sst);
Jonathan Doman703a1852020-11-11 13:04:02 -0800257 try
258 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700259 sst->setCurrentLevel(newConfig->level);
Jonathan Doman703a1852020-11-11 13:04:02 -0800260 currentLevel = newConfig->level;
261 }
262 catch (const PECIError& error)
263 {
264 std::cerr << "Failed to set new SST-PP level: " << error.what()
265 << "\n";
266 throw sdbusplus::xyz::openbmc_project::Common::Device::Error::
267 WriteFailure();
268 }
269
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700270 // return value not used
271 return sdbusplus::message::object_path();
272 }
273
Jonathan Doman06639632022-06-13 22:00:45 -0700274 bool baseSpeedPriorityEnabled(bool /* value */) override
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700275 {
Jonathan Doman06639632022-06-13 22:00:45 -0700276 DEBUG_PRINT << "Writing BaseSpeedPriorityEnabled not allowed\n";
277 throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700278 // return value not used
279 return false;
280 }
281
282 //
283 // Additions
284 //
285
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700286 OperatingConfig& newConfig(unsigned int level)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700287 {
288 availConfigs.emplace_back(std::make_unique<OperatingConfig>(
289 bus, level, generateConfigPath(level)));
290 return *availConfigs.back();
291 }
292
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700293 std::string generateConfigPath(unsigned int level) const
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700294 {
295 return path + "/config" + std::to_string(level);
296 }
297
298 /**
299 * Emit the interface added signals which were deferred. This is required
Jonathan Doman703a1852020-11-11 13:04:02 -0800300 * for ObjectMapper to pick up the objects, if we initially defered the
301 * signal emitting.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700302 */
303 void finalize()
304 {
305 emit_added();
306 for (auto& config : availConfigs)
307 {
308 config->emit_added();
309 }
310 }
311
312 static std::string generatePath(int index)
313 {
Jonathan Doman0a385372021-03-08 17:04:13 -0800314 return cpuPath + std::to_string(index);
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700315 }
316};
317
318/**
319 * Retrieve the SST parameters for a single config and fill the values into the
320 * properties on the D-Bus interface.
321 *
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700322 * @param[in,out] sst Interface to SST backend.
323 * @param[in] level Config TDP level to retrieve.
324 * @param[out] config D-Bus interface to update.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700325 */
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700326static void getSingleConfig(SSTInterface& sst, unsigned int level,
327 OperatingConfig& config)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700328{
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700329 config.powerLimit(sst.tdp(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800330 DEBUG_PRINT << " TDP = " << config.powerLimit() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700331
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700332 config.availableCoreCount(sst.coreCount(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800333 DEBUG_PRINT << " coreCount = " << config.availableCoreCount() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700334
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700335 config.baseSpeed(sst.p1Freq(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800336 DEBUG_PRINT << " baseSpeed = " << config.baseSpeed() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700337
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700338 config.maxSpeed(sst.p0Freq(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800339 DEBUG_PRINT << " maxSpeed = " << config.maxSpeed() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700340
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700341 config.maxJunctionTemperature(sst.prochotTemp(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800342 DEBUG_PRINT << " procHot = " << config.maxJunctionTemperature() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700343
344 // Construct BaseSpeedPrioritySettings
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700345 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>> baseSpeeds;
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700346 if (sst.bfSupported(level))
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700347 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700348 std::vector<uint32_t> totalCoreList, loFreqCoreList, hiFreqCoreList;
349 totalCoreList = sst.enabledCoreList(level);
350 hiFreqCoreList = sst.bfHighPriorityCoreList(level);
351 std::set_difference(
352 totalCoreList.begin(), totalCoreList.end(), hiFreqCoreList.begin(),
353 hiFreqCoreList.end(),
354 std::inserter(loFreqCoreList, loFreqCoreList.begin()));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700355
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700356 baseSpeeds = {{sst.bfHighPriorityFreq(level), hiFreqCoreList},
357 {sst.bfLowPriorityFreq(level), loFreqCoreList}};
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700358 }
359 config.baseSpeedPrioritySettings(baseSpeeds);
360
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700361 config.turboProfile(sst.sseTurboProfile(level));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700362}
363
364/**
365 * Retrieve all SST configuration info for all discoverable CPUs, and publish
366 * the info on new D-Bus objects on the given bus connection.
367 *
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700368 * @param[in,out] ioc ASIO context.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700369 * @param[in,out] conn D-Bus ASIO connection.
370 *
Jonathan Doman703a1852020-11-11 13:04:02 -0800371 * @return Whether discovery was successfully finished.
372 *
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700373 * @throw PECIError A PECI command failed on a CPU which had previously
374 * responded to a command.
375 */
Jonathan Doman49ea8302022-05-26 14:29:46 -0700376static bool discoverCPUsAndConfigs(boost::asio::io_context& ioc,
377 sdbusplus::asio::connection& conn)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700378{
Jonathan Doman49ea8302022-05-26 14:29:46 -0700379 // Persistent list - only populated after complete/successful discovery
380 static std::vector<std::unique_ptr<CPUConfig>> cpus;
381 cpus.clear();
382
383 // Temporary staging list. In case there is any failure, these temporary
384 // objects will get dropped to avoid presenting incomplete info until the
385 // next discovery attempt.
386 std::vector<std::unique_ptr<CPUConfig>> cpuList;
387
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700388 for (uint8_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; ++i)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700389 {
Jonathan Doman703a1852020-11-11 13:04:02 -0800390 // Let the event handler run any waiting tasks. If there is a lot of
391 // PECI contention, SST discovery could take a long time. This lets us
392 // get updates to hostState and handle any D-Bus requests.
393 ioc.poll();
394
395 if (hostState == HostState::off)
396 {
397 return false;
398 }
399
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700400 unsigned int cpuIndex = i - MIN_CLIENT_ADDR;
401 DEBUG_PRINT << "Discovering CPU " << cpuIndex << '\n';
402
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700403 // We could possibly check D-Bus for CPU presence and model, but PECI is
404 // 10x faster and so much simpler.
405 uint8_t cc, stepping;
406 CPUModel cpuModel;
407 EPECIStatus status = peci_GetCPUID(i, &cpuModel, &stepping, &cc);
Jonathan Doman703a1852020-11-11 13:04:02 -0800408 if (status == PECI_CC_TIMEOUT)
409 {
410 // Timing out indicates the CPU is present but PCS services not
411 // working yet. Try again later.
412 throw PECIError("Get CPUID timed out");
413 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700414 if (status == PECI_CC_CPU_NOT_PRESENT)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700415 {
416 continue;
417 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700418 if (status != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700419 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700420 std::cerr << "GetCPUID returned status " << status
421 << ", cc = " << cc << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700422 continue;
423 }
424
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700425 std::unique_ptr<SSTInterface> sst = getInstance(i, cpuModel);
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700426
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700427 if (!sst)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700428 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700429 // No supported backend for this CPU.
430 continue;
431 }
432
433 if (!sst->ready())
434 {
435 // Supported CPU but it can't be queried yet. Try again later.
436 std::cerr << "sst not ready yet\n";
437 return false;
438 }
439
440 if (!sst->ppEnabled())
441 {
442 // Supported CPU but the specific SKU doesn't support SST-PP.
443 std::cerr << "CPU doesn't support SST-PP\n";
444 continue;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700445 }
446
447 // Create the per-CPU configuration object
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700448 cpuList.emplace_back(
449 std::make_unique<CPUConfig>(conn, cpuIndex, cpuModel));
450 CPUConfig& cpu = *cpuList.back();
451
452 bool foundCurrentLevel = false;
453
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800454 for (unsigned int level = 0; level <= sst->maxLevel(); ++level)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700455 {
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800456 DEBUG_PRINT << "checking level " << level << ": ";
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700457 // levels 1 and 2 were legacy/deprecated, originally used for AVX
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700458 // license pre-granting. They may be reused for more levels in
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700459 // future generations. So we need to check for discontinuities.
460 if (!sst->levelSupported(level))
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700461 {
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800462 DEBUG_PRINT << "not supported\n";
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700463 continue;
464 }
465
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800466 DEBUG_PRINT << "supported\n";
467
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700468 getSingleConfig(*sst, level, cpu.newConfig(level));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700469
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700470 if (level == sst->currentLevel())
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700471 {
472 foundCurrentLevel = true;
473 }
474 }
475
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800476 DEBUG_PRINT << "current level is " << sst->currentLevel() << '\n';
477
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700478 if (!foundCurrentLevel)
479 {
480 // In case we didn't encounter a PECI error, but also didn't find
481 // the config which is supposedly applied, we won't be able to
482 // populate the CurrentOperatingConfig so we have to remove this CPU
483 // from consideration.
484 std::cerr << "CPU " << cpuIndex
485 << " claimed SST support but invalid configs\n";
486 cpuList.pop_back();
487 continue;
488 }
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700489 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800490
Jonathan Doman49ea8302022-05-26 14:29:46 -0700491 cpuList.swap(cpus);
492 std::for_each(cpus.begin(), cpus.end(), [](auto& cpu) { cpu->finalize(); });
Jonathan Doman703a1852020-11-11 13:04:02 -0800493 return true;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700494}
495
Jonathan Doman49ea8302022-05-26 14:29:46 -0700496/**
497 * Attempt discovery process, and if it fails, wait for 10 seconds to try again.
498 */
499static void discoverOrWait()
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700500{
Jonathan Doman49ea8302022-05-26 14:29:46 -0700501 static boost::asio::steady_timer peciRetryTimer(dbus::getIOContext());
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700502 static int peciErrorCount = 0;
Jonathan Doman703a1852020-11-11 13:04:02 -0800503 bool finished = false;
Jonathan Doman49ea8302022-05-26 14:29:46 -0700504
505 // This function may be called from hostStateHandler or by retrying itself.
506 // In case those overlap, cancel any outstanding retry timer.
507 peciRetryTimer.cancel();
508
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700509 try
510 {
Jonathan Doman703a1852020-11-11 13:04:02 -0800511 DEBUG_PRINT << "Starting discovery\n";
Jonathan Doman49ea8302022-05-26 14:29:46 -0700512 finished = discoverCPUsAndConfigs(dbus::getIOContext(),
513 *dbus::getConnection());
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700514 }
515 catch (const PECIError& err)
516 {
517 std::cerr << "PECI Error: " << err.what() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700518
519 // In case of repeated failure to finish discovery, turn off this
520 // feature altogether. Possible cause is that the CPU model does not
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700521 // actually support the necessary commands.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700522 if (++peciErrorCount >= 50)
523 {
524 std::cerr << "Aborting SST discovery\n";
525 return;
526 }
527
528 std::cerr << "Retrying SST discovery later\n";
529 }
530
Jonathan Doman703a1852020-11-11 13:04:02 -0800531 DEBUG_PRINT << "Finished discovery attempt: " << finished << '\n';
532
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700533 // Retry later if no CPUs were available, or there was a PECI error.
Jonathan Doman703a1852020-11-11 13:04:02 -0800534 if (!finished)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700535 {
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700536 peciRetryTimer.expires_after(std::chrono::seconds(10));
Jonathan Doman49ea8302022-05-26 14:29:46 -0700537 peciRetryTimer.async_wait([](boost::system::error_code ec) {
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700538 if (ec)
539 {
Jonathan Doman49ea8302022-05-26 14:29:46 -0700540 if (ec != boost::asio::error::operation_aborted)
541 {
542 std::cerr << "SST PECI Retry Timer failed: " << ec << '\n';
543 }
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700544 return;
545 }
Jonathan Doman49ea8302022-05-26 14:29:46 -0700546 discoverOrWait();
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700547 });
548 }
549}
550
Jonathan Doman49ea8302022-05-26 14:29:46 -0700551static void hostStateHandler(HostState prevState, HostState)
552{
553 if (prevState == HostState::off)
554 {
555 // Start or re-start discovery any time the host moves out of the
556 // powered off state.
557 discoverOrWait();
558 }
559}
560
561void init()
562{
563 addHostStateCallback(hostStateHandler);
564}
565
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700566} // namespace sst
567} // namespace cpu_info