xref: /openbmc/phosphor-fan-presence/monitor/tach_sensor.cpp (revision 623635c62f229008400e69d6cbfdb84c12610807)
1 /**
2  * Copyright © 2017 IBM 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 #include "tach_sensor.hpp"
17 
18 #include "fan.hpp"
19 #include "sdbusplus.hpp"
20 #include "utility.hpp"
21 
22 #include <fmt/format.h>
23 
24 #include <phosphor-logging/elog.hpp>
25 #include <phosphor-logging/log.hpp>
26 
27 #include <experimental/filesystem>
28 #include <functional>
29 #include <utility>
30 
31 namespace phosphor
32 {
33 namespace fan
34 {
35 namespace monitor
36 {
37 
38 constexpr auto FAN_SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
39 constexpr auto FAN_TARGET_PROPERTY = "Target";
40 constexpr auto FAN_VALUE_PROPERTY = "Value";
41 
42 using namespace std::experimental::filesystem;
43 using InternalFailure =
44     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
45 
46 /**
47  * @brief Helper function to read a property
48  *
49  * @param[in] interface - the interface the property is on
50  * @param[in] propertName - the name of the property
51  * @param[in] path - the dbus path
52  * @param[in] bus - the dbus object
53  * @param[out] value - filled in with the property value
54  */
55 template <typename T>
56 static void
57     readProperty(const std::string& interface, const std::string& propertyName,
58                  const std::string& path, sdbusplus::bus::bus& bus, T& value)
59 {
60     try
61     {
62         value =
63             util::SDBusPlus::getProperty<T>(bus, path, interface, propertyName);
64     }
65     catch (std::exception& e)
66     {
67         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
68     }
69 }
70 
71 TachSensor::TachSensor(Mode mode, sdbusplus::bus::bus& bus, Fan& fan,
72                        const std::string& id, bool hasTarget, size_t funcDelay,
73                        const std::string& interface, double factor,
74                        int64_t offset, size_t method, size_t threshold,
75                        size_t timeout, const std::optional<size_t>& errorDelay,
76                        const sdeventplus::Event& event) :
77     _bus(bus),
78     _fan(fan), _name(FAN_SENSOR_PATH + id), _invName(path(fan.getName()) / id),
79     _hasTarget(hasTarget), _funcDelay(funcDelay), _interface(interface),
80     _factor(factor), _offset(offset), _method(method), _threshold(threshold),
81     _timeout(timeout), _timerMode(TimerMode::func),
82     _timer(event, std::bind(&Fan::updateState, &fan, std::ref(*this))),
83     _errorDelay(errorDelay)
84 {
85     // Start from a known state of functional
86     setFunctional(true);
87 
88     // Load in current Target and Input values when entering monitor mode
89 #ifndef MONITOR_USE_JSON
90     if (mode != Mode::init)
91     {
92 #endif
93         try
94         {
95             updateTachAndTarget();
96         }
97         catch (const std::exception& e)
98         {
99             // Until the parent Fan's monitor-ready timer expires, the
100             // object can be functional with a missing D-bus sensor.
101         }
102 
103         auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
104 
105         tachSignal = std::make_unique<sdbusplus::server::match::match>(
106             _bus, match.c_str(),
107             [this](auto& msg) { this->handleTachChange(msg); });
108 
109         if (_hasTarget)
110         {
111             match = getMatchString(_interface);
112 
113             targetSignal = std::make_unique<sdbusplus::server::match::match>(
114                 _bus, match.c_str(),
115                 [this](auto& msg) { this->handleTargetChange(msg); });
116         }
117 
118         if (_errorDelay)
119         {
120             _errorTimer = std::make_unique<
121                 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
122                 event, std::bind(&Fan::sensorErrorTimerExpired, &fan,
123                                  std::ref(*this)));
124         }
125 #ifndef MONITOR_USE_JSON
126     }
127 #endif
128 }
129 
130 void TachSensor::updateTachAndTarget()
131 {
132     _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>(
133         _bus, _name, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY);
134 
135     if (_hasTarget)
136     {
137         readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, _tachTarget);
138     }
139 }
140 
141 std::string TachSensor::getMatchString(const std::string& interface)
142 {
143     return sdbusplus::bus::match::rules::propertiesChanged(_name, interface);
144 }
145 
146 uint64_t TachSensor::getTarget() const
147 {
148     if (!_hasTarget)
149     {
150         return _fan.findTargetSpeed();
151     }
152     return _tachTarget;
153 }
154 
155 std::pair<uint64_t, uint64_t> TachSensor::getRange(const size_t deviation) const
156 {
157     // Determine min/max range applying the deviation
158     uint64_t min = getTarget() * (100 - deviation) / 100;
159     uint64_t max = getTarget() * (100 + deviation) / 100;
160 
161     // Adjust the min/max range by applying the factor & offset
162     min = min * _factor + _offset;
163     max = max * _factor + _offset;
164 
165     return std::make_pair(min, max);
166 }
167 
168 void TachSensor::processState()
169 {
170     // This function runs from inside trust::Manager::checkTrust(), which,
171     // for sensors using the count method, runs right before process()
172     // is called anyway inside Fan::countTimerExpired() so don't call
173     // it now if using that method.
174     if (_method == MethodMode::timebased)
175     {
176         _fan.process(*this);
177     }
178 }
179 
180 void TachSensor::resetMethod()
181 {
182     switch (_method)
183     {
184         case MethodMode::timebased:
185             if (timerRunning())
186             {
187                 stopTimer();
188             }
189             break;
190         case MethodMode::count:
191             if (_functional)
192             {
193                 _counter = 0;
194             }
195             else
196             {
197                 _counter = _threshold;
198             }
199             break;
200     }
201 }
202 
203 void TachSensor::setFunctional(bool functional)
204 {
205     _functional = functional;
206     updateInventory(_functional);
207 
208     if (!_errorTimer)
209     {
210         return;
211     }
212 
213     if (!_functional)
214     {
215         if (_fan.present())
216         {
217             _errorTimer->restartOnce(std::chrono::seconds(*_errorDelay));
218         }
219     }
220     else if (_errorTimer->isEnabled())
221     {
222         _errorTimer->setEnabled(false);
223     }
224 }
225 
226 void TachSensor::handleTargetChange(sdbusplus::message::message& msg)
227 {
228     readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget);
229 
230     // Check all tach sensors on the fan against the target
231     _fan.tachChanged();
232 }
233 
234 void TachSensor::handleTachChange(sdbusplus::message::message& msg)
235 {
236     readPropertyFromMessage(msg, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY,
237                             _tachInput);
238 
239     // Check just this sensor against the target
240     _fan.tachChanged(*this);
241 }
242 
243 void TachSensor::startTimer(TimerMode mode)
244 {
245     using namespace std::chrono;
246 
247     if (!timerRunning() || mode != _timerMode)
248     {
249         log<level::DEBUG>(
250             fmt::format("Start timer({}) on tach sensor {}. [delay = {}s]",
251                         mode, _name,
252                         duration_cast<seconds>(getDelay(mode)).count())
253                 .c_str());
254         _timer.restartOnce(getDelay(mode));
255         _timerMode = mode;
256     }
257 }
258 
259 std::chrono::microseconds TachSensor::getDelay(TimerMode mode)
260 {
261     using namespace std::chrono;
262 
263     switch (mode)
264     {
265         case TimerMode::nonfunc:
266             return duration_cast<microseconds>(seconds(_timeout));
267         case TimerMode::func:
268             return duration_cast<microseconds>(seconds(_funcDelay));
269         default:
270             // Log an internal error for undefined timer mode
271             log<level::ERR>("Undefined timer mode",
272                             entry("TIMER_MODE=%u", mode));
273             elog<InternalFailure>();
274             return duration_cast<microseconds>(seconds(0));
275     }
276 }
277 
278 void TachSensor::setCounter(bool count)
279 {
280     if (count)
281     {
282         if (_counter < _threshold)
283         {
284             ++_counter;
285             log<level::DEBUG>(
286                 fmt::format(
287                     "Incremented error counter on {} to {} (threshold {})",
288                     _name, _counter, _threshold)
289                     .c_str());
290         }
291     }
292     else
293     {
294         if (_counter > 0)
295         {
296             --_counter;
297             log<level::DEBUG>(
298                 fmt::format(
299                     "Decremented error counter on {} to {} (threshold {})",
300                     _name, _counter, _threshold)
301                     .c_str());
302         }
303     }
304 }
305 
306 void TachSensor::updateInventory(bool functional)
307 {
308     auto objectMap =
309         util::getObjMap<bool>(_invName, util::OPERATIONAL_STATUS_INTF,
310                               util::FUNCTIONAL_PROPERTY, functional);
311     auto response = util::SDBusPlus::lookupAndCallMethod(
312         _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
313     if (response.is_method_error())
314     {
315         log<level::ERR>("Error in notify update of tach sensor inventory");
316     }
317 }
318 
319 } // namespace monitor
320 } // namespace fan
321 } // namespace phosphor
322