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