xref: /openbmc/phosphor-hwmon/hwmonio.cpp (revision e289100ec0dc9944439092d1d8f97de8123ed9c6)
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      * Some devices return this if the hardware is being
63      * powered off in a normal manner, as incomplete data
64      * is received. Retrying allows time for the system to
65      * clean up driver devices, or in the event of a real
66      * failure, attempt to get the rest of the data.
67      */
68     EMSGSIZE,
69 };
70 
71 HwmonIO::HwmonIO(const std::string& path) : p(path)
72 {
73 }
74 
75 int64_t HwmonIO::read(
76         const std::string& type,
77         const std::string& id,
78         const std::string& sensor,
79         size_t retries,
80         std::chrono::milliseconds delay) const
81 {
82     int64_t val;
83     std::ifstream ifs;
84     auto fullPath = sysfs::make_sysfs_path(
85             p, type, id, sensor);
86 
87     ifs.exceptions(
88             std::ifstream::failbit |
89                 std::ifstream::badbit |
90                 std::ifstream::eofbit);
91 
92     while (true)
93     {
94         try
95         {
96             errno = 0;
97             if (!ifs.is_open())
98                 ifs.open(fullPath);
99             ifs.clear();
100             ifs.seekg(0);
101             ifs >> val;
102         }
103         catch (const std::exception& e)
104         {
105             auto rc = errno;
106 
107             if (!rc)
108             {
109                 throw;
110             }
111 
112             if (rc == ENOENT || rc == ENODEV)
113             {
114                 // If the directory or device disappeared then this application
115                 // should gracefully exit.  There are race conditions between the
116                 // unloading of a hwmon driver and the stopping of this service
117                 // by systemd.  To prevent this application from falsely failing
118                 // in these scenarios, it will simply exit if the directory or
119                 // file can not be found.  It is up to the user(s) of this
120                 // provided hwmon object to log the appropriate errors if the
121                 // object disappears when it should not.
122                 exit(0);
123             }
124 
125             if (0 == std::count(
126                         retryableErrors.begin(),
127                         retryableErrors.end(),
128                         rc) ||
129                     !retries)
130             {
131                 // Not a retryable error or out of retries.
132 #ifdef NEGATIVE_ERRNO_ON_FAIL
133                 return -rc;
134 #endif
135 
136                 // Work around GCC bugs 53984 and 66145 for callers by
137                 // explicitly raising system_error here.
138                 throw std::system_error(rc, std::generic_category());
139             }
140 
141             --retries;
142             std::this_thread::sleep_for(delay);
143             continue;
144         }
145         break;
146     }
147 
148     return val;
149 }
150 
151 void HwmonIO::write(
152         uint32_t val,
153         const std::string& type,
154         const std::string& id,
155         const std::string& sensor,
156         size_t retries,
157         std::chrono::milliseconds delay) const
158 
159 {
160     std::ofstream ofs;
161     auto fullPath = sysfs::make_sysfs_path(
162             p, type, id, sensor);
163 
164     ofs.exceptions(
165             std::ofstream::failbit |
166                 std::ofstream::badbit |
167                 std::ofstream::eofbit);
168 
169     // See comments in the read method for an explanation of the odd exception
170     // handling behavior here.
171 
172     while (true)
173     {
174         try
175         {
176             errno = 0;
177             if (!ofs.is_open())
178                 ofs.open(fullPath);
179             ofs.clear();
180             ofs.seekp(0);
181             ofs << val;
182             ofs.flush();
183         }
184         catch (const std::exception& e)
185         {
186             auto rc = errno;
187 
188             if (!rc)
189             {
190                 throw;
191             }
192 
193             if (rc == ENOENT)
194             {
195                 exit(0);
196             }
197 
198             if (0 == std::count(
199                         retryableErrors.begin(),
200                         retryableErrors.end(),
201                         rc) ||
202                     !retries)
203             {
204                 // Not a retryable error or out of retries.
205 
206                 // Work around GCC bugs 53984 and 66145 for callers by
207                 // explicitly raising system_error here.
208                 throw std::system_error(rc, std::generic_category());
209             }
210 
211             --retries;
212             std::this_thread::sleep_for(delay);
213             continue;
214         }
215         break;
216     }
217 }
218 
219 std::string HwmonIO::path() const
220 {
221     return p;
222 }
223 
224 } // hwmonio
225