blob: ef1abbe74770854a7f6a84a25a5ecf7aefef86cb [file] [log] [blame]
Nikhil Potadeb669b6b2019-03-13 10:52:21 -07001/*
2// Copyright (c) 2019 Intel 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
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103017#include "NVMeBasicContext.hpp"
18#include "NVMeContext.hpp"
19#include "NVMeSensor.hpp"
20
Ed Tanous9b4a20e2022-09-06 08:47:11 -070021#include <boost/asio/steady_timer.hpp>
James Feist38fb5982020-05-28 10:09:54 -070022
Andrew Jeffery34123562021-12-02 14:38:41 +103023#include <optional>
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070024#include <regex>
25
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070026static NVMEMap nvmeDeviceMap;
27
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070028NVMEMap& getNVMEMap()
29{
30 return nvmeDeviceMap;
31}
32
Andrew Jeffery34123562021-12-02 14:38:41 +103033static std::optional<int>
34 extractBusNumber(const std::string& path,
35 const SensorBaseConfigMap& properties)
36{
37 auto findBus = properties.find("Bus");
38 if (findBus == properties.end())
39 {
40 std::cerr << "could not determine bus number for " << path << "\n";
41 return std::nullopt;
42 }
43
44 return std::visit(VariantToIntVisitor(), findBus->second);
45}
46
Andrew Jefferye671f052021-12-02 14:47:20 +103047static std::optional<std::string>
48 extractSensorName(const std::string& path,
49 const SensorBaseConfigMap& properties)
50{
51 auto findSensorName = properties.find("Name");
52 if (findSensorName == properties.end())
53 {
54 std::cerr << "could not determine configuration name for " << path
55 << "\n";
56 return std::nullopt;
57 }
58
59 return std::get<std::string>(findSensorName->second);
60}
61
Andrew Jeffery710562a2021-12-02 14:55:55 +103062static std::filesystem::path deriveRootBusPath(int busNumber)
63{
64 return "/sys/bus/i2c/devices/i2c-" + std::to_string(busNumber) +
65 "/mux_device";
66}
67
Andrew Jeffery66ab8402021-12-02 15:03:51 +103068static std::optional<int> deriveRootBus(std::optional<int> busNumber)
69{
70 if (!busNumber)
71 {
72 return std::nullopt;
73 }
74
75 std::filesystem::path muxPath = deriveRootBusPath(*busNumber);
76
77 if (!std::filesystem::is_symlink(muxPath))
78 {
79 return *busNumber;
80 }
81
82 std::string rootName = std::filesystem::read_symlink(muxPath).filename();
83 size_t dash = rootName.find('-');
84 if (dash == std::string::npos)
85 {
86 std::cerr << "Error finding root bus for " << rootName << "\n";
87 return std::nullopt;
88 }
89
90 return std::stoi(rootName.substr(0, dash));
91}
92
Andrew Jefferyc7a89e52021-12-02 15:10:23 +103093static std::shared_ptr<NVMeContext>
Ed Tanous1f978632023-02-28 18:16:39 -080094 provideRootBusContext(boost::asio::io_context& io, NVMEMap& map,
Andrew Jefferyc7a89e52021-12-02 15:10:23 +103095 int rootBus)
96{
97 auto findRoot = map.find(rootBus);
98 if (findRoot != map.end())
99 {
100 return findRoot->second;
101 }
102
103 std::shared_ptr<NVMeContext> context =
Andrew Jefferyc7a89e52021-12-02 15:10:23 +1030104 std::make_shared<NVMeBasicContext>(io, rootBus);
Andrew Jefferyc7a89e52021-12-02 15:10:23 +1030105 map[rootBus] = context;
106
107 return context;
108}
109
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030110static void handleSensorConfigurations(
Ed Tanous1f978632023-02-28 18:16:39 -0800111 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030112 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
113 const ManagedObjectType& sensorConfigurations)
114{
115 // todo: it'd be better to only update the ones we care about
116 for (const auto& [_, nvmeContextPtr] : nvmeDeviceMap)
117 {
118 if (nvmeContextPtr)
119 {
120 nvmeContextPtr->close();
121 }
122 }
123 nvmeDeviceMap.clear();
124
125 // iterate through all found configurations
Andrew Jefferyee164342021-12-02 16:02:19 +1030126 for (const auto& [interfacePath, sensorData] : sensorConfigurations)
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030127 {
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030128 // find base configuration
Zev Weiss054aad82022-08-18 01:37:34 -0700129 auto sensorBase =
130 sensorData.find(configInterfaceName(NVMeSensor::sensorType));
Zev Weissefae44c2022-08-16 15:39:20 -0700131 if (sensorBase == sensorData.end())
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030132 {
Zev Weissefae44c2022-08-16 15:39:20 -0700133 continue;
134 }
Andrew Jefferyee164342021-12-02 16:02:19 +1030135
Zev Weissefae44c2022-08-16 15:39:20 -0700136 const SensorBaseConfigMap& sensorConfig = sensorBase->second;
137 std::optional<int> busNumber =
138 extractBusNumber(interfacePath, sensorConfig);
139 std::optional<std::string> sensorName =
140 extractSensorName(interfacePath, sensorConfig);
141 std::optional<int> rootBus = deriveRootBus(busNumber);
Andrew Jefferyee164342021-12-02 16:02:19 +1030142
Zev Weissefae44c2022-08-16 15:39:20 -0700143 if (!(busNumber && sensorName && rootBus))
144 {
145 continue;
146 }
Andrew Jefferyee164342021-12-02 16:02:19 +1030147
Zev Weissefae44c2022-08-16 15:39:20 -0700148 std::vector<thresholds::Threshold> sensorThresholds;
149 if (!parseThresholdsFromConfig(sensorData, sensorThresholds))
150 {
151 std::cerr << "error populating thresholds for " << *sensorName
152 << "\n";
153 }
Andrew Jefferyee164342021-12-02 16:02:19 +1030154
Zev Weissefae44c2022-08-16 15:39:20 -0700155 try
156 {
157 // May throw for an invalid rootBus
158 std::shared_ptr<NVMeContext> context =
159 provideRootBusContext(io, nvmeDeviceMap, *rootBus);
Andrew Jeffery25e20bd2022-03-15 22:26:04 +1030160
Zev Weissefae44c2022-08-16 15:39:20 -0700161 // Construct the sensor after grabbing the context so we don't
162 // glitch D-Bus May throw for an invalid busNumber
163 std::shared_ptr<NVMeSensor> sensorPtr =
164 std::make_shared<NVMeSensor>(
165 objectServer, io, dbusConnection, *sensorName,
166 std::move(sensorThresholds), interfacePath, *busNumber);
167
168 context->addSensor(sensorPtr);
169 }
170 catch (const std::invalid_argument& ex)
171 {
172 std::cerr << "Failed to add sensor for "
173 << std::string(interfacePath) << ": " << ex.what()
174 << "\n";
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030175 }
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030176 }
177 for (const auto& [_, context] : nvmeDeviceMap)
178 {
179 context->pollNVMeDevices();
180 }
181}
182
Ed Tanous1f978632023-02-28 18:16:39 -0800183void createSensors(boost::asio::io_context& io,
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700184 sdbusplus::asio::object_server& objectServer,
185 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
186{
187
188 auto getter = std::make_shared<GetSensorConfiguration>(
Ed Tanous86d83012022-02-18 09:51:47 -0800189 dbusConnection, [&io, &objectServer, &dbusConnection](
190 const ManagedObjectType& sensorConfigurations) {
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030191 handleSensorConfigurations(io, objectServer, dbusConnection,
192 sensorConfigurations);
Ed Tanous86d83012022-02-18 09:51:47 -0800193 });
Zev Weiss054aad82022-08-18 01:37:34 -0700194 getter->getConfiguration(std::vector<std::string>{NVMeSensor::sensorType});
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700195}
196
Patrick Williams92f8f512022-07-22 19:26:55 -0500197static void interfaceRemoved(sdbusplus::message_t& message, NVMEMap& contexts)
Andrew Jefferyafd55b92021-12-08 10:58:17 +1030198{
199 if (message.is_method_error())
200 {
201 std::cerr << "interfacesRemoved callback method error\n";
202 return;
203 }
204
205 sdbusplus::message::object_path path;
206 std::vector<std::string> interfaces;
207
208 message.read(path, interfaces);
209
210 for (auto& [_, context] : contexts)
211 {
212 std::optional<std::shared_ptr<NVMeSensor>> sensor =
213 context->getSensorAtPath(path);
214 if (!sensor)
215 {
216 continue;
217 }
218
219 auto interface = std::find(interfaces.begin(), interfaces.end(),
220 (*sensor)->objectType);
221 if (interface == interfaces.end())
222 {
223 continue;
224 }
225
226 context->removeSensor(sensor.value());
227 }
228}
229
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700230int main()
231{
Ed Tanous1f978632023-02-28 18:16:39 -0800232 boost::asio::io_context io;
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700233 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
234 systemBus->request_name("xyz.openbmc_project.NVMeSensor");
Johnathan Mantey661d4372022-10-27 09:00:59 -0700235 sdbusplus::asio::object_server objectServer(systemBus, true);
Andrew Jefferyea148ec2023-01-17 15:43:33 +1030236 objectServer.add_manager("/xyz/openbmc_project/sensors");
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700237
Ed Tanous83db50c2023-03-01 10:20:24 -0800238 boost::asio::post(io,
239 [&]() { createSensors(io, objectServer, systemBus); });
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700240
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700241 boost::asio::steady_timer filterTimer(io);
Patrick Williams92f8f512022-07-22 19:26:55 -0500242 std::function<void(sdbusplus::message_t&)> eventHandler =
243 [&filterTimer, &io, &objectServer, &systemBus](sdbusplus::message_t&) {
Ed Tanousbb679322022-05-16 16:10:00 -0700244 // this implicitly cancels the timer
Ed Tanous83db50c2023-03-01 10:20:24 -0800245 filterTimer.expires_after(std::chrono::seconds(1));
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700246
Ed Tanousbb679322022-05-16 16:10:00 -0700247 filterTimer.async_wait([&](const boost::system::error_code& ec) {
248 if (ec == boost::asio::error::operation_aborted)
249 {
250 return; // we're being canceled
251 }
Andrew Jeffery7279f932021-05-25 13:35:27 +0930252
Ed Tanousbb679322022-05-16 16:10:00 -0700253 if (ec)
254 {
255 std::cerr << "Error: " << ec.message() << "\n";
256 return;
257 }
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700258
Ed Tanousbb679322022-05-16 16:10:00 -0700259 createSensors(io, objectServer, systemBus);
260 });
261 };
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700262
Zev Weiss214d9712022-08-12 12:54:31 -0700263 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
264 setupPropertiesChangedMatches(
Zev Weiss054aad82022-08-18 01:37:34 -0700265 *systemBus, std::to_array<const char*>({NVMeSensor::sensorType}),
Zev Weiss214d9712022-08-12 12:54:31 -0700266 eventHandler);
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700267
Andrew Jefferyafd55b92021-12-08 10:58:17 +1030268 // Watch for entity-manager to remove configuration interfaces
269 // so the corresponding sensors can be removed.
Patrick Williams92f8f512022-07-22 19:26:55 -0500270 auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
271 static_cast<sdbusplus::bus_t&>(*systemBus),
Andrew Jefferyafd55b92021-12-08 10:58:17 +1030272 "type='signal',member='InterfacesRemoved',arg0path='" +
273 std::string(inventoryPath) + "/'",
Patrick Williams92f8f512022-07-22 19:26:55 -0500274 [](sdbusplus::message_t& msg) {
Ed Tanousbb679322022-05-16 16:10:00 -0700275 interfaceRemoved(msg, nvmeDeviceMap);
Andrew Jefferyafd55b92021-12-08 10:58:17 +1030276 });
277
Bruce Lee1263c3d2021-06-04 15:16:33 +0800278 setupManufacturingModeMatch(*systemBus);
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700279 io.run();
280}