Add hwmon model option

On NVME devices a file is available to differentiate them by model.

My thinkpad has one internal NVME slot, but I also use external USB-C
and Thunderbolt NVME enclosures.

The internal slot shows up as hwmon3:
`/sys/class/hwmon/hwmon3/device/model`

Which contains:
`WD_BLACK SN770 1TB`
and some trailing white-space characters.

This change adds an option to allow you to specify the model with or
without the hwmon-name.

I have tested both configurations successfully:

```
  - hwmon: /sys/class/hwmon
    name: nvme
    model: WD_BLACK SN770 1TB
    indices: [1]
```
and
```
  - hwmon: /sys/class/hwmon
    model: WD_BLACK SN770 1TB
    indices: [1]
```

Signed-off-by: Bryan Hundven <bryanhundven@gmail.com>
This commit is contained in:
Bryan Hundven 2022-11-20 12:56:55 -08:00
parent c6e48a95b3
commit b90cd6e18c
7 changed files with 81 additions and 9 deletions

View File

@ -201,7 +201,7 @@ string TpFanDriver::type_name() const
HwmonFanDriver::HwmonFanDriver(const string &path)
: HwmonFanDriver(
std::make_shared<HwmonInterface<FanDriver>>(path, nullopt, nullopt),
std::make_shared<HwmonInterface<FanDriver>>(path, nullopt, nullopt, nullopt),
false,
0
)

View File

@ -65,7 +65,6 @@ vector<string> HwmonInterface<HwmonT>::find_files(const string &path, const vect
return rv;
}
template<>
string HwmonInterface<SensorDriver>::filename(int index)
{ return "temp" + std::to_string(index) + "_input"; }
@ -81,13 +80,13 @@ HwmonInterface<HwmonT>::HwmonInterface()
{}
template<class HwmonT>
HwmonInterface<HwmonT>::HwmonInterface(const string &base_path, opt<const string> name, opt<vector<unsigned int>> indices)
HwmonInterface<HwmonT>::HwmonInterface(const string &base_path, opt<const string> name, opt<const string> model, opt<vector<unsigned int>> indices)
: base_path_(base_path)
, name_(name)
, model_(model)
, indices_(indices)
{}
template<class HwmonT>
vector<string> HwmonInterface<HwmonT>::find_hwmons_by_name(
const string &path,
@ -131,6 +130,51 @@ vector<string> HwmonInterface<HwmonT>::find_hwmons_by_name(
return result;
}
template<class HwmonT>
vector<string> HwmonInterface<HwmonT>::find_hwmons_by_model(
const string &path,
const string &model,
unsigned char depth
) {
const unsigned char max_depth = 5;
vector<string> result;
ifstream f(path + "/model");
if (f.is_open() && f.good()) {
string tmp;
if (getline(f, tmp)) {
tmp = tmp.erase(tmp.find_last_not_of(" \t\n\r\f\v") + 1);
if (tmp == model) {
result.push_back(path);
return result;
}
}
}
if (depth >= max_depth) {
return result; // don't recurse to subdirs
}
struct dirent **entries;
int nentries = ::scandir(path.c_str(), &entries, filter_subdirs, nullptr);
if (nentries == -1) {
return result;
}
for (int i = 0; i < nentries; i++) {
auto subdir = path + "/" + entries[i]->d_name;
free(entries[i]);
struct stat statbuf;
int err = stat(path.c_str(), &statbuf);
if (err || (statbuf.st_mode & S_IFMT) != S_IFDIR)
continue;
auto found = find_hwmons_by_model(subdir, model, depth + 1);
result.insert(result.end(), found.begin(), found.end());
}
free(entries);
return result;
}
template<class HwmonT>
vector<string> HwmonInterface<HwmonT>::find_hwmons_by_indices(
@ -197,7 +241,21 @@ string HwmonInterface<HwmonT>::lookup()
}
path = paths[0];
}
if (model_) {
vector<string> paths = find_hwmons_by_model(path, model_.value(), 1);
if (paths.size() != 1) {
string msg(path + ": ");
if (paths.size() == 0) {
msg += "Could not find a hwmon with this model: " + model_.value();
} else {
msg += MSG_MULTIPLE_HWMONS_FOUND;
for (string hwmon_path : paths)
msg += " " + hwmon_path;
}
throw DriverInitError(msg);
}
path = paths[0];
}
if (indices_) {
found_paths_ = find_hwmons_by_indices(path, indices_.value(), 0);
if (found_paths_.size() == 0)

View File

@ -36,7 +36,7 @@ template<class HwmonT>
class HwmonInterface {
public:
HwmonInterface();
HwmonInterface(const string &base_path, opt<const string> name, opt<vector<unsigned int>> indices);
HwmonInterface(const string &base_path, opt<const string> name, opt<const string> model, opt<vector<unsigned int>> indices);
string lookup();
@ -44,12 +44,14 @@ private:
static vector<string> find_files(const string &path, const vector<unsigned int> &indices);
static string filename(int index);
static vector<string> find_hwmons_by_model(const string &path, const string &model, unsigned char depth);
static vector<string> find_hwmons_by_name(const string &path, const string &name, unsigned char depth);
static vector<string> find_hwmons_by_indices(const string &path, const vector<unsigned int> &indices, unsigned char depth);
protected:
opt<const string> base_path_;
opt<const string> name_;
opt<const string> model_;
opt<vector<unsigned int>> indices_;
vector<string> found_paths_;
opt<vector<string>::const_iterator> paths_it_;

View File

@ -135,7 +135,7 @@ void SensorDriver::skip_io_error(const ExpectedError &e)
HwmonSensorDriver::HwmonSensorDriver(const string &path, bool optional)
: HwmonSensorDriver(
std::make_shared<HwmonInterface<SensorDriver>>(path, nullopt, nullopt),
std::make_shared<HwmonInterface<SensorDriver>>(path, nullopt, nullopt, nullopt),
optional,
nullopt,
0

View File

@ -92,6 +92,7 @@ The syntax for identifying each type of sensors looks as follows:
\f[CB]sensors:
\f[CB] \- hwmon: \f[CI]hwmon-path\f[CR] # A path to a sysfs/hwmon sensor
\f[CB] name: \f[CI]hwmon-name\f[CR] # Optional entry
\f[CB] model: \f[CI]hwmon-model\f[CR] # Optional entry for nvme
\f[CB] indices: \f[CI]index-list\f[CR] # Optional entry
\f[CB] \- chip: \f[CI]chip-name\f[CR] # An lm_sensors/libsensors chip...
@ -225,6 +226,14 @@ This method of specifying sensors is particularly useful if the full path to a
particular hwmon keeps changing between bootups, e.g. due to changing load order
of the driver modules.
.TP
.I hwmon-model
The model of a device in a hwmon interface usually found for NVME devices in a
file under \*(lqdevice\*(rq called \*(lqmodel\*(rq.
For example, you might have an NVME \*(lq/sys/class/hwmon/hwmon3/device/model\*(rq
and you might have an external NVME over USB or Thunderbolt that you don't want
to monitor or you might have two NVME's.
.TP
.I index-list
A YAML list

View File

@ -102,11 +102,12 @@ bool convert_driver<vector<wtf_ptr<HwmonSensorDriver>>>(
opt<vector<int>> correction = decode_opt<vector<int>>(node[kw_correction]);
opt<const string> name = decode_opt<string>(node[kw_name]);
opt<const string> model = decode_opt<string>(node[kw_model]);
bool optional = node[kw_optional] ? node[kw_optional].as<bool>() : false;
opt<unsigned int> max_errors = decode_opt<unsigned int>(node[kw_max_errors]);
opt<vector<unsigned int>> indices = decode_opt<vector<unsigned int>>(node[kw_indices]);
auto hwmon_iface = std::make_shared<HwmonInterface<SensorDriver>>(path, name, indices);
auto hwmon_iface = std::make_shared<HwmonInterface<SensorDriver>>(path, name, model, indices);
if (indices) {
if (correction && correction->size() != indices->size())
@ -285,12 +286,13 @@ bool convert_driver<vector<wtf_ptr<HwmonFanDriver>>>(const Node &node, vector<wt
string path = node[kw_hwmon].as<string>();
opt<string> name = decode_opt<string>(node[kw_name]);
opt<string> model = decode_opt<string>(node[kw_model]);
bool optional = node[kw_optional] ? node[kw_optional].as<bool>() : false;
opt<vector<unsigned int>> indices = decode_opt<vector<unsigned int>>(node[kw_indices]);
opt<unsigned int> max_errors = decode_opt<unsigned int>(node[kw_max_errors]);
shared_ptr<HwmonInterface<FanDriver>> hwmon_iface = std::make_shared<HwmonInterface<FanDriver>>(
path, name, indices
path, name, model, indices
);
if (!indices && optional)

View File

@ -32,6 +32,7 @@ const string kw_speed("speed");
const string kw_upper("upper_limit");
const string kw_lower("lower_limit");
const string kw_name("name");
const string kw_model("model");
const string kw_indices("indices");
const string kw_correction("correction");
const string kw_optional("optional");