blob: d5e3026566c278cc963be29511b2f857538a4730 [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 Jefferye3e3c972021-05-26 14:37:07 +093017#include <NVMeBasicContext.hpp>
Andrew Jefferydae6e182021-05-21 16:23:07 +093018#include <NVMeContext.hpp>
Ed Tanous8a57ec02020-10-09 12:46:52 -070019#include <NVMeSensor.hpp>
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070020#include <boost/asio/deadline_timer.hpp>
James Feist38fb5982020-05-28 10:09:54 -070021
Andrew Jeffery34123562021-12-02 14:38:41 +103022#include <optional>
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070023#include <regex>
24
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070025static NVMEMap nvmeDeviceMap;
26
Nikhil Potadeb669b6b2019-03-13 10:52:21 -070027NVMEMap& getNVMEMap()
28{
29 return nvmeDeviceMap;
30}
31
Andrew Jeffery34123562021-12-02 14:38:41 +103032static std::optional<int>
33 extractBusNumber(const std::string& path,
34 const SensorBaseConfigMap& properties)
35{
36 auto findBus = properties.find("Bus");
37 if (findBus == properties.end())
38 {
39 std::cerr << "could not determine bus number for " << path << "\n";
40 return std::nullopt;
41 }
42
43 return std::visit(VariantToIntVisitor(), findBus->second);
44}
45
Andrew Jefferye671f052021-12-02 14:47:20 +103046static std::optional<std::string>
47 extractSensorName(const std::string& path,
48 const SensorBaseConfigMap& properties)
49{
50 auto findSensorName = properties.find("Name");
51 if (findSensorName == properties.end())
52 {
53 std::cerr << "could not determine configuration name for " << path
54 << "\n";
55 return std::nullopt;
56 }
57
58 return std::get<std::string>(findSensorName->second);
59}
60
Andrew Jeffery710562a2021-12-02 14:55:55 +103061static std::filesystem::path deriveRootBusPath(int busNumber)
62{
63 return "/sys/bus/i2c/devices/i2c-" + std::to_string(busNumber) +
64 "/mux_device";
65}
66
Andrew Jeffery66ab8402021-12-02 15:03:51 +103067static std::optional<int> deriveRootBus(std::optional<int> busNumber)
68{
69 if (!busNumber)
70 {
71 return std::nullopt;
72 }
73
74 std::filesystem::path muxPath = deriveRootBusPath(*busNumber);
75
76 if (!std::filesystem::is_symlink(muxPath))
77 {
78 return *busNumber;
79 }
80
81 std::string rootName = std::filesystem::read_symlink(muxPath).filename();
82 size_t dash = rootName.find('-');
83 if (dash == std::string::npos)
84 {
85 std::cerr << "Error finding root bus for " << rootName << "\n";
86 return std::nullopt;
87 }
88
89 return std::stoi(rootName.substr(0, dash));
90}
91
Andrew Jefferyc7a89e52021-12-02 15:10:23 +103092static std::shared_ptr<NVMeContext>
93 provideRootBusContext(boost::asio::io_service& io, NVMEMap& map,
94 int rootBus)
95{
96 auto findRoot = map.find(rootBus);
97 if (findRoot != map.end())
98 {
99 return findRoot->second;
100 }
101
102 std::shared_ptr<NVMeContext> context =
Andrew Jefferyc7a89e52021-12-02 15:10:23 +1030103 std::make_shared<NVMeBasicContext>(io, rootBus);
Andrew Jefferyc7a89e52021-12-02 15:10:23 +1030104 map[rootBus] = context;
105
106 return context;
107}
108
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030109static void handleSensorConfigurations(
110 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
111 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
112 const ManagedObjectType& sensorConfigurations)
113{
114 // todo: it'd be better to only update the ones we care about
115 for (const auto& [_, nvmeContextPtr] : nvmeDeviceMap)
116 {
117 if (nvmeContextPtr)
118 {
119 nvmeContextPtr->close();
120 }
121 }
122 nvmeDeviceMap.clear();
123
124 // iterate through all found configurations
Andrew Jefferyee164342021-12-02 16:02:19 +1030125 for (const auto& [interfacePath, sensorData] : sensorConfigurations)
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030126 {
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030127 // find base configuration
Ed Tanous74cffa82022-01-25 13:00:28 -0800128 auto sensorBase = sensorData.find(NVMeSensor::configType);
Andrew Jefferyee164342021-12-02 16:02:19 +1030129 if (sensorBase != sensorData.end())
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030130 {
Andrew Jefferyee164342021-12-02 16:02:19 +1030131 const SensorBaseConfigMap& sensorConfig = sensorBase->second;
132 std::optional<int> busNumber =
133 extractBusNumber(interfacePath, sensorConfig);
134 std::optional<std::string> sensorName =
135 extractSensorName(interfacePath, sensorConfig);
136 std::optional<int> rootBus = deriveRootBus(busNumber);
137
138 if (!(busNumber && sensorName && rootBus))
139 {
140 continue;
141 }
142
143 std::vector<thresholds::Threshold> sensorThresholds;
144 if (!parseThresholdsFromConfig(sensorData, sensorThresholds))
145 {
146 std::cerr << "error populating thresholds for " << *sensorName
147 << "\n";
148 }
149
Andrew Jeffery25e20bd2022-03-15 22:26:04 +1030150 try
151 {
152 // May throw for an invalid rootBus
153 std::shared_ptr<NVMeContext> context =
154 provideRootBusContext(io, nvmeDeviceMap, *rootBus);
Andrew Jefferyee164342021-12-02 16:02:19 +1030155
Andrew Jeffery25e20bd2022-03-15 22:26:04 +1030156 // Construct the sensor after grabbing the context so we don't
157 // glitch D-Bus May throw for an invalid busNumber
158 std::shared_ptr<NVMeSensor> sensorPtr =
159 std::make_shared<NVMeSensor>(
160 objectServer, io, dbusConnection, *sensorName,
161 std::move(sensorThresholds), interfacePath, *busNumber);
162
163 context->addSensor(sensorPtr);
164 }
165 catch (const std::invalid_argument& ex)
166 {
167 std::cerr << "Failed to add sensor for "
168 << std::string(interfacePath) << ": " << ex.what()
169 << "\n";
170 }
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030171 }
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030172 }
173 for (const auto& [_, context] : nvmeDeviceMap)
174 {
175 context->pollNVMeDevices();
176 }
177}
178
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700179void createSensors(boost::asio::io_service& io,
180 sdbusplus::asio::object_server& objectServer,
181 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
182{
183
184 auto getter = std::make_shared<GetSensorConfiguration>(
Ed Tanous86d83012022-02-18 09:51:47 -0800185 dbusConnection, [&io, &objectServer, &dbusConnection](
186 const ManagedObjectType& sensorConfigurations) {
Andrew Jefferyf690f0c2021-12-02 11:42:58 +1030187 handleSensorConfigurations(io, objectServer, dbusConnection,
188 sensorConfigurations);
Ed Tanous86d83012022-02-18 09:51:47 -0800189 });
Ed Tanous74cffa82022-01-25 13:00:28 -0800190 getter->getConfiguration(std::vector<std::string>{NVMeSensor::configType});
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700191}
192
Andrew Jefferyafd55b92021-12-08 10:58:17 +1030193static void interfaceRemoved(sdbusplus::message::message& message,
194 NVMEMap& contexts)
195{
196 if (message.is_method_error())
197 {
198 std::cerr << "interfacesRemoved callback method error\n";
199 return;
200 }
201
202 sdbusplus::message::object_path path;
203 std::vector<std::string> interfaces;
204
205 message.read(path, interfaces);
206
207 for (auto& [_, context] : contexts)
208 {
209 std::optional<std::shared_ptr<NVMeSensor>> sensor =
210 context->getSensorAtPath(path);
211 if (!sensor)
212 {
213 continue;
214 }
215
216 auto interface = std::find(interfaces.begin(), interfaces.end(),
217 (*sensor)->objectType);
218 if (interface == interfaces.end())
219 {
220 continue;
221 }
222
223 context->removeSensor(sensor.value());
224 }
225}
226
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700227int main()
228{
229 boost::asio::io_service io;
230 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
231 systemBus->request_name("xyz.openbmc_project.NVMeSensor");
232 sdbusplus::asio::object_server objectServer(systemBus);
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700233
234 io.post([&]() { createSensors(io, objectServer, systemBus); });
235
236 boost::asio::deadline_timer filterTimer(io);
237 std::function<void(sdbusplus::message::message&)> eventHandler =
238 [&filterTimer, &io, &objectServer,
James Feist7d7579f2020-09-02 14:13:08 -0700239 &systemBus](sdbusplus::message::message&) {
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700240 // this implicitly cancels the timer
241 filterTimer.expires_from_now(boost::posix_time::seconds(1));
242
243 filterTimer.async_wait([&](const boost::system::error_code& ec) {
244 if (ec == boost::asio::error::operation_aborted)
245 {
246 return; // we're being canceled
247 }
Andrew Jeffery7279f932021-05-25 13:35:27 +0930248
249 if (ec)
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700250 {
251 std::cerr << "Error: " << ec.message() << "\n";
252 return;
253 }
254
255 createSensors(io, objectServer, systemBus);
256 });
257 };
258
259 sdbusplus::bus::match::match configMatch(
260 static_cast<sdbusplus::bus::bus&>(*systemBus),
261 "type='signal',member='PropertiesChanged',path_namespace='" +
262 std::string(inventoryPath) + "',arg0namespace='" +
Ed Tanous74cffa82022-01-25 13:00:28 -0800263 std::string(NVMeSensor::configType) + "'",
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700264 eventHandler);
265
Andrew Jefferyafd55b92021-12-08 10:58:17 +1030266 // Watch for entity-manager to remove configuration interfaces
267 // so the corresponding sensors can be removed.
268 auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match::match>(
269 static_cast<sdbusplus::bus::bus&>(*systemBus),
270 "type='signal',member='InterfacesRemoved',arg0path='" +
271 std::string(inventoryPath) + "/'",
272 [](sdbusplus::message::message& msg) {
273 interfaceRemoved(msg, nvmeDeviceMap);
274 });
275
Bruce Lee1263c3d2021-06-04 15:16:33 +0800276 setupManufacturingModeMatch(*systemBus);
Nikhil Potadeb669b6b2019-03-13 10:52:21 -0700277 io.run();
278}