xref: /openbmc/dbus-sensors/src/nvme/NVMeSensorMain.cpp (revision 7201be43f87dbbf32569cdb1ecf59de8b1d6f6ec)
1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2019 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu //      http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu 
17d7be555eSGeorge Liu #include "NVMeBasicContext.hpp"
18d7be555eSGeorge Liu #include "NVMeContext.hpp"
19d7be555eSGeorge Liu #include "NVMeSensor.hpp"
20d7be555eSGeorge Liu #include "Thresholds.hpp"
21d7be555eSGeorge Liu #include "Utils.hpp"
22d7be555eSGeorge Liu #include "VariantVisitors.hpp"
23d7be555eSGeorge Liu 
24d7be555eSGeorge Liu #include <boost/asio/error.hpp>
25d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
26d7be555eSGeorge Liu #include <boost/asio/post.hpp>
27d7be555eSGeorge Liu #include <boost/asio/steady_timer.hpp>
28*7201be43SGeorge Liu #include <phosphor-logging/lg2.hpp>
29d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
30d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
31d7be555eSGeorge Liu #include <sdbusplus/bus.hpp>
32d7be555eSGeorge Liu #include <sdbusplus/bus/match.hpp>
33d7be555eSGeorge Liu #include <sdbusplus/message.hpp>
34d7be555eSGeorge Liu #include <sdbusplus/message/native_types.hpp>
35d7be555eSGeorge Liu 
36d7be555eSGeorge Liu #include <algorithm>
37d7be555eSGeorge Liu #include <array>
38d7be555eSGeorge Liu #include <chrono>
39d7be555eSGeorge Liu #include <cstddef>
40d7be555eSGeorge Liu #include <cstdint>
41d7be555eSGeorge Liu #include <filesystem>
42d7be555eSGeorge Liu #include <functional>
43d7be555eSGeorge Liu #include <memory>
44d7be555eSGeorge Liu #include <optional>
45d7be555eSGeorge Liu #include <stdexcept>
46d7be555eSGeorge Liu #include <string>
47d7be555eSGeorge Liu #include <utility>
48d7be555eSGeorge Liu #include <variant>
49d7be555eSGeorge Liu #include <vector>
50d7be555eSGeorge Liu 
51d7be555eSGeorge Liu static constexpr uint8_t nvmeMiDefaultSlaveAddr = 0x6A;
52d7be555eSGeorge Liu 
53d7be555eSGeorge Liu static NVMEMap nvmeDeviceMap;
54d7be555eSGeorge Liu 
getNVMEMap()55d7be555eSGeorge Liu NVMEMap& getNVMEMap()
56d7be555eSGeorge Liu {
57d7be555eSGeorge Liu     return nvmeDeviceMap;
58d7be555eSGeorge Liu }
59d7be555eSGeorge Liu 
extractBusNumber(const std::string & path,const SensorBaseConfigMap & properties)60d7be555eSGeorge Liu static std::optional<int> extractBusNumber(
61d7be555eSGeorge Liu     const std::string& path, const SensorBaseConfigMap& properties)
62d7be555eSGeorge Liu {
63d7be555eSGeorge Liu     auto findBus = properties.find("Bus");
64d7be555eSGeorge Liu     if (findBus == properties.end())
65d7be555eSGeorge Liu     {
66*7201be43SGeorge Liu         lg2::error("could not determine bus number for '{PATH}'", "PATH", path);
67d7be555eSGeorge Liu         return std::nullopt;
68d7be555eSGeorge Liu     }
69d7be555eSGeorge Liu 
70d7be555eSGeorge Liu     return std::visit(VariantToIntVisitor(), findBus->second);
71d7be555eSGeorge Liu }
72d7be555eSGeorge Liu 
extractSlaveAddr(const std::string & path,const SensorBaseConfigMap & properties)73d7be555eSGeorge Liu static uint8_t extractSlaveAddr(const std::string& path,
74d7be555eSGeorge Liu                                 const SensorBaseConfigMap& properties)
75d7be555eSGeorge Liu {
76d7be555eSGeorge Liu     auto findSlaveAddr = properties.find("Address");
77d7be555eSGeorge Liu     if (findSlaveAddr == properties.end())
78d7be555eSGeorge Liu     {
79*7201be43SGeorge Liu         lg2::error(
80*7201be43SGeorge Liu             "could not determine slave address for '{PATH} 'using default as "
81*7201be43SGeorge Liu             "specified in nvme-mi",
82*7201be43SGeorge Liu             "PATH", path);
83d7be555eSGeorge Liu         return nvmeMiDefaultSlaveAddr;
84d7be555eSGeorge Liu     }
85d7be555eSGeorge Liu 
86d7be555eSGeorge Liu     return std::visit(VariantToUnsignedIntVisitor(), findSlaveAddr->second);
87d7be555eSGeorge Liu }
88d7be555eSGeorge Liu 
extractSensorName(const std::string & path,const SensorBaseConfigMap & properties)89d7be555eSGeorge Liu static std::optional<std::string> extractSensorName(
90d7be555eSGeorge Liu     const std::string& path, const SensorBaseConfigMap& properties)
91d7be555eSGeorge Liu {
92d7be555eSGeorge Liu     auto findSensorName = properties.find("Name");
93d7be555eSGeorge Liu     if (findSensorName == properties.end())
94d7be555eSGeorge Liu     {
95*7201be43SGeorge Liu         lg2::error("could not determine configuration name for '{PATH}'",
96*7201be43SGeorge Liu                    "PATH", path);
97d7be555eSGeorge Liu         return std::nullopt;
98d7be555eSGeorge Liu     }
99d7be555eSGeorge Liu 
100d7be555eSGeorge Liu     return std::get<std::string>(findSensorName->second);
101d7be555eSGeorge Liu }
102d7be555eSGeorge Liu 
deriveRootBusPath(int busNumber)103d7be555eSGeorge Liu static std::filesystem::path deriveRootBusPath(int busNumber)
104d7be555eSGeorge Liu {
105d7be555eSGeorge Liu     return "/sys/bus/i2c/devices/i2c-" + std::to_string(busNumber) +
106d7be555eSGeorge Liu            "/mux_device";
107d7be555eSGeorge Liu }
108d7be555eSGeorge Liu 
deriveRootBus(std::optional<int> busNumber)109d7be555eSGeorge Liu static std::optional<int> deriveRootBus(std::optional<int> busNumber)
110d7be555eSGeorge Liu {
111d7be555eSGeorge Liu     if (!busNumber)
112d7be555eSGeorge Liu     {
113d7be555eSGeorge Liu         return std::nullopt;
114d7be555eSGeorge Liu     }
115d7be555eSGeorge Liu 
116d7be555eSGeorge Liu     std::filesystem::path muxPath = deriveRootBusPath(*busNumber);
117d7be555eSGeorge Liu 
118d7be555eSGeorge Liu     if (!std::filesystem::is_symlink(muxPath))
119d7be555eSGeorge Liu     {
120d7be555eSGeorge Liu         return busNumber;
121d7be555eSGeorge Liu     }
122d7be555eSGeorge Liu 
123d7be555eSGeorge Liu     std::string rootName = std::filesystem::read_symlink(muxPath).filename();
124d7be555eSGeorge Liu     size_t dash = rootName.find('-');
125d7be555eSGeorge Liu     if (dash == std::string::npos)
126d7be555eSGeorge Liu     {
127*7201be43SGeorge Liu         lg2::error("Error finding root bus for '{NAME}'", "NAME", rootName);
128d7be555eSGeorge Liu         return std::nullopt;
129d7be555eSGeorge Liu     }
130d7be555eSGeorge Liu 
131d7be555eSGeorge Liu     return std::stoi(rootName.substr(0, dash));
132d7be555eSGeorge Liu }
133d7be555eSGeorge Liu 
provideRootBusContext(boost::asio::io_context & io,NVMEMap & map,int rootBus)134d7be555eSGeorge Liu static std::shared_ptr<NVMeContext> provideRootBusContext(
135d7be555eSGeorge Liu     boost::asio::io_context& io, NVMEMap& map, int rootBus)
136d7be555eSGeorge Liu {
137d7be555eSGeorge Liu     auto findRoot = map.find(rootBus);
138d7be555eSGeorge Liu     if (findRoot != map.end())
139d7be555eSGeorge Liu     {
140d7be555eSGeorge Liu         return findRoot->second;
141d7be555eSGeorge Liu     }
142d7be555eSGeorge Liu 
143d7be555eSGeorge Liu     std::shared_ptr<NVMeContext> context =
144d7be555eSGeorge Liu         std::make_shared<NVMeBasicContext>(io, rootBus);
145d7be555eSGeorge Liu     map[rootBus] = context;
146d7be555eSGeorge Liu 
147d7be555eSGeorge Liu     return context;
148d7be555eSGeorge Liu }
149d7be555eSGeorge Liu 
handleSensorConfigurations(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,const ManagedObjectType & sensorConfigurations)150d7be555eSGeorge Liu static void handleSensorConfigurations(
151d7be555eSGeorge Liu     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
152d7be555eSGeorge Liu     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
153d7be555eSGeorge Liu     const ManagedObjectType& sensorConfigurations)
154d7be555eSGeorge Liu {
155d7be555eSGeorge Liu     // todo: it'd be better to only update the ones we care about
156d7be555eSGeorge Liu     for (const auto& [_, nvmeContextPtr] : nvmeDeviceMap)
157d7be555eSGeorge Liu     {
158d7be555eSGeorge Liu         if (nvmeContextPtr)
159d7be555eSGeorge Liu         {
160d7be555eSGeorge Liu             nvmeContextPtr->close();
161d7be555eSGeorge Liu         }
162d7be555eSGeorge Liu     }
163d7be555eSGeorge Liu     nvmeDeviceMap.clear();
164d7be555eSGeorge Liu 
165d7be555eSGeorge Liu     // iterate through all found configurations
166d7be555eSGeorge Liu     for (const auto& [interfacePath, sensorData] : sensorConfigurations)
167d7be555eSGeorge Liu     {
168d7be555eSGeorge Liu         // find base configuration
169d7be555eSGeorge Liu         auto sensorBase =
170d7be555eSGeorge Liu             sensorData.find(configInterfaceName(NVMeSensor::sensorType));
171d7be555eSGeorge Liu         if (sensorBase == sensorData.end())
172d7be555eSGeorge Liu         {
173d7be555eSGeorge Liu             continue;
174d7be555eSGeorge Liu         }
175d7be555eSGeorge Liu 
176d7be555eSGeorge Liu         const SensorBaseConfigMap& sensorConfig = sensorBase->second;
177d7be555eSGeorge Liu         std::optional<int> busNumber =
178d7be555eSGeorge Liu             extractBusNumber(interfacePath, sensorConfig);
179d7be555eSGeorge Liu         std::optional<std::string> sensorName =
180d7be555eSGeorge Liu             extractSensorName(interfacePath, sensorConfig);
181d7be555eSGeorge Liu         uint8_t slaveAddr = extractSlaveAddr(interfacePath, sensorConfig);
182d7be555eSGeorge Liu         std::optional<int> rootBus = deriveRootBus(busNumber);
183d7be555eSGeorge Liu 
184d7be555eSGeorge Liu         if (!(busNumber && sensorName && rootBus))
185d7be555eSGeorge Liu         {
186d7be555eSGeorge Liu             continue;
187d7be555eSGeorge Liu         }
188d7be555eSGeorge Liu 
189d7be555eSGeorge Liu         std::vector<thresholds::Threshold> sensorThresholds;
190d7be555eSGeorge Liu         if (!parseThresholdsFromConfig(sensorData, sensorThresholds))
191d7be555eSGeorge Liu         {
192*7201be43SGeorge Liu             lg2::error("error populating thresholds for '{NAME}'", "NAME",
193*7201be43SGeorge Liu                        *sensorName);
194d7be555eSGeorge Liu         }
195d7be555eSGeorge Liu 
196d7be555eSGeorge Liu         try
197d7be555eSGeorge Liu         {
198d7be555eSGeorge Liu             // May throw for an invalid rootBus
199d7be555eSGeorge Liu             std::shared_ptr<NVMeContext> context =
200d7be555eSGeorge Liu                 provideRootBusContext(io, nvmeDeviceMap, *rootBus);
201d7be555eSGeorge Liu 
202d7be555eSGeorge Liu             // Construct the sensor after grabbing the context so we don't
203d7be555eSGeorge Liu             // glitch D-Bus May throw for an invalid busNumber
204d7be555eSGeorge Liu             std::shared_ptr<NVMeSensor> sensorPtr =
205d7be555eSGeorge Liu                 std::make_shared<NVMeSensor>(
206d7be555eSGeorge Liu                     objectServer, io, dbusConnection, *sensorName,
207d7be555eSGeorge Liu                     std::move(sensorThresholds), interfacePath, *busNumber,
208d7be555eSGeorge Liu                     slaveAddr);
209d7be555eSGeorge Liu 
210d7be555eSGeorge Liu             context->addSensor(sensorPtr);
211d7be555eSGeorge Liu         }
212d7be555eSGeorge Liu         catch (const std::invalid_argument& ex)
213d7be555eSGeorge Liu         {
214*7201be43SGeorge Liu             lg2::error("Failed to add sensor for '{PATH}': '{ERROR}'", "PATH",
215*7201be43SGeorge Liu                        interfacePath.str, "ERROR", ex);
216d7be555eSGeorge Liu         }
217d7be555eSGeorge Liu     }
218d7be555eSGeorge Liu     for (const auto& [_, context] : nvmeDeviceMap)
219d7be555eSGeorge Liu     {
220d7be555eSGeorge Liu         context->pollNVMeDevices();
221d7be555eSGeorge Liu     }
222d7be555eSGeorge Liu }
223d7be555eSGeorge Liu 
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)224d7be555eSGeorge Liu void createSensors(boost::asio::io_context& io,
225d7be555eSGeorge Liu                    sdbusplus::asio::object_server& objectServer,
226d7be555eSGeorge Liu                    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
227d7be555eSGeorge Liu {
228d7be555eSGeorge Liu     auto getter = std::make_shared<GetSensorConfiguration>(
229d7be555eSGeorge Liu         dbusConnection, [&io, &objectServer, &dbusConnection](
230d7be555eSGeorge Liu                             const ManagedObjectType& sensorConfigurations) {
231d7be555eSGeorge Liu             handleSensorConfigurations(io, objectServer, dbusConnection,
232d7be555eSGeorge Liu                                        sensorConfigurations);
233d7be555eSGeorge Liu         });
234d7be555eSGeorge Liu     getter->getConfiguration(std::vector<std::string>{NVMeSensor::sensorType});
235d7be555eSGeorge Liu }
236d7be555eSGeorge Liu 
interfaceRemoved(sdbusplus::message_t & message,NVMEMap & contexts)237d7be555eSGeorge Liu static void interfaceRemoved(sdbusplus::message_t& message, NVMEMap& contexts)
238d7be555eSGeorge Liu {
239d7be555eSGeorge Liu     if (message.is_method_error())
240d7be555eSGeorge Liu     {
241*7201be43SGeorge Liu         lg2::error("interfacesRemoved callback method error");
242d7be555eSGeorge Liu         return;
243d7be555eSGeorge Liu     }
244d7be555eSGeorge Liu 
245d7be555eSGeorge Liu     sdbusplus::message::object_path path;
246d7be555eSGeorge Liu     std::vector<std::string> interfaces;
247d7be555eSGeorge Liu 
248d7be555eSGeorge Liu     message.read(path, interfaces);
249d7be555eSGeorge Liu 
250d7be555eSGeorge Liu     for (auto& [_, context] : contexts)
251d7be555eSGeorge Liu     {
252d7be555eSGeorge Liu         std::optional<std::shared_ptr<NVMeSensor>> sensor =
253d7be555eSGeorge Liu             context->getSensorAtPath(path);
254d7be555eSGeorge Liu         if (!sensor)
255d7be555eSGeorge Liu         {
256d7be555eSGeorge Liu             continue;
257d7be555eSGeorge Liu         }
258d7be555eSGeorge Liu 
259d7be555eSGeorge Liu         auto interface = std::find(interfaces.begin(), interfaces.end(),
260d7be555eSGeorge Liu                                    (*sensor)->configInterface);
261d7be555eSGeorge Liu         if (interface == interfaces.end())
262d7be555eSGeorge Liu         {
263d7be555eSGeorge Liu             continue;
264d7be555eSGeorge Liu         }
265d7be555eSGeorge Liu 
266d7be555eSGeorge Liu         context->removeSensor(sensor.value());
267d7be555eSGeorge Liu     }
268d7be555eSGeorge Liu }
269d7be555eSGeorge Liu 
main()270d7be555eSGeorge Liu int main()
271d7be555eSGeorge Liu {
272d7be555eSGeorge Liu     boost::asio::io_context io;
273d7be555eSGeorge Liu     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
274d7be555eSGeorge Liu     systemBus->request_name("xyz.openbmc_project.NVMeSensor");
275d7be555eSGeorge Liu     sdbusplus::asio::object_server objectServer(systemBus, true);
276d7be555eSGeorge Liu     objectServer.add_manager("/xyz/openbmc_project/sensors");
277d7be555eSGeorge Liu 
278d7be555eSGeorge Liu     boost::asio::post(io,
279d7be555eSGeorge Liu                       [&]() { createSensors(io, objectServer, systemBus); });
280d7be555eSGeorge Liu 
281d7be555eSGeorge Liu     boost::asio::steady_timer filterTimer(io);
282d7be555eSGeorge Liu     std::function<void(sdbusplus::message_t&)> eventHandler =
283d7be555eSGeorge Liu         [&filterTimer, &io, &objectServer, &systemBus](sdbusplus::message_t&) {
284d7be555eSGeorge Liu             // this implicitly cancels the timer
285d7be555eSGeorge Liu             filterTimer.expires_after(std::chrono::seconds(1));
286d7be555eSGeorge Liu 
287d7be555eSGeorge Liu             filterTimer.async_wait([&](const boost::system::error_code& ec) {
288d7be555eSGeorge Liu                 if (ec == boost::asio::error::operation_aborted)
289d7be555eSGeorge Liu                 {
290d7be555eSGeorge Liu                     return; // we're being canceled
291d7be555eSGeorge Liu                 }
292d7be555eSGeorge Liu 
293d7be555eSGeorge Liu                 if (ec)
294d7be555eSGeorge Liu                 {
295*7201be43SGeorge Liu                     lg2::error("Error: '{ERROR_MESSAGE}'", "ERROR_MESSAGE",
296*7201be43SGeorge Liu                                ec.message());
297d7be555eSGeorge Liu                     return;
298d7be555eSGeorge Liu                 }
299d7be555eSGeorge Liu 
300d7be555eSGeorge Liu                 createSensors(io, objectServer, systemBus);
301d7be555eSGeorge Liu             });
302d7be555eSGeorge Liu         };
303d7be555eSGeorge Liu 
304d7be555eSGeorge Liu     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
305d7be555eSGeorge Liu         setupPropertiesChangedMatches(
306d7be555eSGeorge Liu             *systemBus, std::to_array<const char*>({NVMeSensor::sensorType}),
307d7be555eSGeorge Liu             eventHandler);
308d7be555eSGeorge Liu 
309d7be555eSGeorge Liu     // Watch for entity-manager to remove configuration interfaces
310d7be555eSGeorge Liu     // so the corresponding sensors can be removed.
311d7be555eSGeorge Liu     auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
312d7be555eSGeorge Liu         static_cast<sdbusplus::bus_t&>(*systemBus),
313d7be555eSGeorge Liu         "type='signal',member='InterfacesRemoved',arg0path='" +
314d7be555eSGeorge Liu             std::string(inventoryPath) + "/'",
315d7be555eSGeorge Liu         [](sdbusplus::message_t& msg) {
316d7be555eSGeorge Liu             interfaceRemoved(msg, nvmeDeviceMap);
317d7be555eSGeorge Liu         });
318d7be555eSGeorge Liu 
319d7be555eSGeorge Liu     setupManufacturingModeMatch(*systemBus);
320d7be555eSGeorge Liu     io.run();
321d7be555eSGeorge Liu }
322