xref: /openbmc/phosphor-hwmon/hwmonio.cpp (revision e8771fd4)
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 "config.h"
15 
16 #include "hwmonio.hpp"
17 
18 #include "sysfs.hpp"
19 
20 #include <algorithm>
21 #include <exception>
22 #include <fstream>
23 #include <thread>
24 
25 namespace hwmonio
26 {
27 
read(const std::string & path) const28 int64_t FileSystem::read(const std::string& path) const
29 {
30     int64_t val;
31     std::ifstream ifs;
32     ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
33                    std::ifstream::eofbit);
34 
35     errno = 0;
36     if (!ifs.is_open())
37         ifs.open(path);
38     ifs.clear();
39     ifs.seekg(0);
40     ifs >> val;
41 
42     return val;
43 }
44 
write(const std::string & path,uint32_t value) const45 void FileSystem::write(const std::string& path, uint32_t value) const
46 {
47     std::ofstream ofs;
48     ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit |
49                    std::ofstream::eofbit);
50 
51     errno = 0;
52     if (!ofs.is_open())
53         ofs.open(path);
54     ofs.clear();
55     ofs.seekp(0);
56     ofs << value;
57     ofs.flush();
58 }
59 
60 FileSystem fileSystemImpl;
61 
62 static constexpr auto retryableErrors = {
63     /*
64      * Retry on bus or device errors or timeouts in case
65      * they are transient.
66      */
67     EIO,
68     ETIMEDOUT,
69 
70     /*
71      * Retry CRC errors.
72      */
73     EBADMSG,
74 
75     /*
76      * Some hwmon drivers do this when they aren't ready
77      * instead of blocking.  Retry.
78      */
79     EAGAIN,
80     /*
81      * We'll see this when for example i2c devices are
82      * unplugged but the driver is still bound.  Retry
83      * rather than exit on the off chance the device is
84      * plugged back in and the driver doesn't do a
85      * remove/probe.  If a remove does occur, we'll
86      * eventually get ENOENT.
87      */
88     ENXIO,
89 
90     /*
91      * Some devices return this when they are busy doing
92      * something else.  Even if being busy isn't the cause,
93      * a retry still gives this app a shot at getting data
94      * as opposed to failing out on the first try.
95      */
96     ENODATA,
97 
98     /*
99      * Some devices return this if the hardware is being
100      * powered off in a normal manner, as incomplete data
101      * is received. Retrying allows time for the system to
102      * clean up driver devices, or in the event of a real
103      * failure, attempt to get the rest of the data.
104      */
105     EMSGSIZE,
106 };
107 
HwmonIO(const std::string & path,const FileSystemInterface * intf)108 HwmonIO::HwmonIO(const std::string& path, const FileSystemInterface* intf) :
109     _p(path), _intf(intf)
110 {}
111 
read(const std::string & type,const std::string & id,const std::string & sensor,size_t retries,std::chrono::milliseconds delay) const112 int64_t HwmonIO::read(const std::string& type, const std::string& id,
113                       const std::string& sensor, size_t retries,
114                       std::chrono::milliseconds delay) const
115 {
116     int64_t val;
117     auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
118 
119     while (true)
120     {
121         try
122         {
123             val = _intf->read(fullPath);
124         }
125         catch (const std::exception& e)
126         {
127             auto rc = errno;
128 
129             if (!rc)
130             {
131                 throw;
132             }
133 
134             if (rc == ENOENT || rc == ENODEV)
135             {
136                 // If the directory or device disappeared then this application
137                 // should gracefully exit.  There are race conditions between
138                 // the unloading of a hwmon driver and the stopping of this
139                 // service by systemd.  To prevent this application from falsely
140                 // failing in these scenarios, it will simply exit if the
141                 // directory or file can not be found.  It is up to the user(s)
142                 // of this provided hwmon object to log the appropriate errors
143                 // if the object disappears when it should not.
144                 exit(0);
145             }
146 
147             if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
148                                 rc) ||
149                 !retries)
150             {
151                 // Not a retryable error or out of retries.
152 #if NEGATIVE_ERRNO_ON_FAIL
153                 return -rc;
154 #endif
155 
156                 // Work around GCC bugs 53984 and 66145 for callers by
157                 // explicitly raising system_error here.
158                 throw std::system_error(rc, std::generic_category());
159             }
160 
161             --retries;
162             std::this_thread::sleep_for(delay);
163             continue;
164         }
165         break;
166     }
167 
168     return val;
169 }
170 
write(uint32_t val,const std::string & type,const std::string & id,const std::string & sensor,size_t retries,std::chrono::milliseconds delay) const171 void HwmonIO::write(uint32_t val, const std::string& type,
172                     const std::string& id, const std::string& sensor,
173                     size_t retries, std::chrono::milliseconds delay) const
174 
175 {
176     auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
177 
178     // See comments in the read method for an explanation of the odd exception
179     // handling behavior here.
180 
181     while (true)
182     {
183         try
184         {
185             _intf->write(fullPath, val);
186         }
187         catch (const std::exception& e)
188         {
189             auto rc = errno;
190 
191             if (!rc)
192             {
193                 throw;
194             }
195 
196             if (rc == ENOENT)
197             {
198                 exit(0);
199             }
200 
201             if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
202                                 rc) ||
203                 !retries)
204             {
205                 // Not a retryable error or out of retries.
206 
207                 // Work around GCC bugs 53984 and 66145 for callers by
208                 // explicitly raising system_error here.
209                 throw std::system_error(rc, std::generic_category());
210             }
211 
212             --retries;
213             std::this_thread::sleep_for(delay);
214             continue;
215         }
216         break;
217     }
218 }
219 
path() const220 std::string HwmonIO::path() const
221 {
222     return _p;
223 }
224 
225 } // namespace hwmonio
226