xref: /openbmc/phosphor-hwmon/hwmonio.cpp (revision 75e56c67a10e9f4c617f9c72a87deb695322e212)
1 /**
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at
5  *
6  *     http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 #include <algorithm>
15 #include <exception>
16 #include <fstream>
17 #include <thread>
18 
19 #include "config.h"
20 #include "hwmonio.hpp"
21 #include "sysfs.hpp"
22 
23 namespace hwmonio {
24 
25 static constexpr auto retryableErrors = {
26     /*
27      * Retry on bus or device errors or timeouts in case
28      * they are transient.
29      */
30     EIO,
31     ETIMEDOUT,
32 
33     /*
34      * Retry CRC errors.
35      */
36     EBADMSG,
37 
38     /*
39      * Some hwmon drivers do this when they aren't ready
40      * instead of blocking.  Retry.
41      */
42     EAGAIN,
43     /*
44      * We'll see this when for example i2c devices are
45      * unplugged but the driver is still bound.  Retry
46      * rather than exit on the off chance the device is
47      * plugged back in and the driver doesn't do a
48      * remove/probe.  If a remove does occur, we'll
49      * eventually get ENOENT.
50      */
51     ENXIO,
52 
53     /*
54      * Some devices return this when they are busy doing
55      * something else.  Even if being busy isn't the cause,
56      * a retry still gives this app a shot at getting data
57      * as opposed to failing out on the first try.
58      */
59     ENODATA,
60 };
61 
62 HwmonIO::HwmonIO(const std::string& path) : p(path)
63 {
64 }
65 
66 int64_t HwmonIO::read(
67         const std::string& type,
68         const std::string& id,
69         const std::string& sensor,
70         size_t retries,
71         std::chrono::milliseconds delay,
72         bool isOCC) const
73 {
74     int64_t val;
75     std::ifstream ifs;
76     auto fullPath = sysfs::make_sysfs_path(
77             p, type, id, sensor);
78 
79     ifs.exceptions(
80             std::ifstream::failbit |
81                 std::ifstream::badbit |
82                 std::ifstream::eofbit);
83 
84     while (true)
85     {
86         try
87         {
88             errno = 0;
89             if (!ifs.is_open())
90                 ifs.open(fullPath);
91             ifs.clear();
92             ifs.seekg(0);
93             ifs >> val;
94         }
95         catch (const std::exception& e)
96         {
97             auto rc = errno;
98 
99             if (!rc)
100             {
101                 throw;
102             }
103 
104             if (rc == ENOENT || rc == ENODEV)
105             {
106                 // If the directory or device disappeared then this application
107                 // should gracefully exit.  There are race conditions between the
108                 // unloading of a hwmon driver and the stopping of this service
109                 // by systemd.  To prevent this application from falsely failing
110                 // in these scenarios, it will simply exit if the directory or
111                 // file can not be found.  It is up to the user(s) of this
112                 // provided hwmon object to log the appropriate errors if the
113                 // object disappears when it should not.
114                 exit(0);
115             }
116 
117             if (isOCC)
118             {
119                 if (rc == EREMOTEIO)
120                 {
121                     // For the OCCs, when an EREMOTEIO is return, set the
122                     // value to 255*1000
123                     // (0xFF = sensor is failed, 1000 = sensor factor)
124                     val = 255000;
125                     break;
126                 }
127             }
128 
129             if (0 == std::count(
130                         retryableErrors.begin(),
131                         retryableErrors.end(),
132                         rc) ||
133                     !retries)
134             {
135                 // Not a retryable error or out of retries.
136 #ifdef NEGATIVE_ERRNO_ON_FAIL
137                 return -rc;
138 #endif
139 
140                 // Work around GCC bugs 53984 and 66145 for callers by
141                 // explicitly raising system_error here.
142                 throw std::system_error(rc, std::generic_category());
143             }
144 
145             --retries;
146             std::this_thread::sleep_for(delay);
147             continue;
148         }
149         break;
150     }
151 
152     return val;
153 }
154 
155 void HwmonIO::write(
156         uint32_t val,
157         const std::string& type,
158         const std::string& id,
159         const std::string& sensor,
160         size_t retries,
161         std::chrono::milliseconds delay) const
162 
163 {
164     std::ofstream ofs;
165     auto fullPath = sysfs::make_sysfs_path(
166             p, type, id, sensor);
167 
168     ofs.exceptions(
169             std::ofstream::failbit |
170                 std::ofstream::badbit |
171                 std::ofstream::eofbit);
172 
173     // See comments in the read method for an explanation of the odd exception
174     // handling behavior here.
175 
176     while (true)
177     {
178         try
179         {
180             errno = 0;
181             if (!ofs.is_open())
182                 ofs.open(fullPath);
183             ofs.clear();
184             ofs.seekp(0);
185             ofs << val;
186             ofs.flush();
187         }
188         catch (const std::exception& e)
189         {
190             auto rc = errno;
191 
192             if (!rc)
193             {
194                 throw;
195             }
196 
197             if (rc == ENOENT)
198             {
199                 exit(0);
200             }
201 
202             if (0 == std::count(
203                         retryableErrors.begin(),
204                         retryableErrors.end(),
205                         rc) ||
206                     !retries)
207             {
208                 // Not a retryable error or out of retries.
209 
210                 // Work around GCC bugs 53984 and 66145 for callers by
211                 // explicitly raising system_error here.
212                 throw std::system_error(rc, std::generic_category());
213             }
214 
215             --retries;
216             std::this_thread::sleep_for(delay);
217             continue;
218         }
219         break;
220     }
221 }
222 
223 std::string HwmonIO::path() const
224 {
225     return p;
226 }
227 
228 } // hwmonio
229