Implemented support for Adaptec RAID 5805 controller and possibly other

Adaptec models as well.
This commit is contained in:
Alexander Shaduri
2012-02-19 10:28:52 +00:00
parent 5ba7add5b5
commit fd9fce25fc
3 changed files with 198 additions and 21 deletions
+6 -3
View File
@@ -50,8 +50,8 @@ Remove mandatory first parameter of gsmartcontrol-root, detect it
See whether we should prefer intel matrix or sda, filter duplicates accordingly.
Add warnings for SSD lifetime attributes
http://sourceforge.net/apps/trac/smartmontools/wiki/FAQ#TheSSD_Life_LeftAttributeofmynewSandForcebasedSSDreportszero
Add an option to display the device and/or the S/N in the main icon list
so that the 12x RAID list view looks a bit less generic.
3ware detection:
@@ -64,7 +64,10 @@ Don't issue warnings for attributes whose raw values are not numbers completely
(e.g. they are space-separated numbers in some format)
investiage the new "-l ssd" and "-l devstat" options (since 5.42)
Add warnings for SSD lifetime attributes
http://sourceforge.net/apps/trac/smartmontools/wiki/FAQ#TheSSD_Life_LeftAttributeofmynewSandForcebasedSSDreportszero
Investiage the new "-l ssd" and "-l devstat" options (since 5.42)
Add menu item - "update drive database" (not sure about linux, should
@@ -191,8 +191,10 @@ inline std::string read_proc_devices_file(std::vector<std::string>& lines)
// Read /proc/scsi/scsi file. Return error message on error.
inline std::string read_proc_scsi_scsi_file(std::vector<std::string>& lines)
/// Read /proc/scsi/scsi file. Return error message on error.
/// \c vendors_models is filled with (scsi host #, trimmed vendors line) pairs.
/// Note that scsi host # is not unique.
inline std::string read_proc_scsi_scsi_file(std::vector< std::pair<int, std::string> >& vendors_models)
{
std::string path;
if (!rconfig::get_data("system/linux_proc_scsi_scsi_path", path) || path.empty()) {
@@ -201,6 +203,8 @@ inline std::string read_proc_scsi_scsi_file(std::vector<std::string>& lines)
}
hz::File file(path);
std::vector<std::string> lines;
if (!read_proc_file(file, lines)) { // this outputs to debug too
std::string error_msg = file.get_error_utf8(); // save before calling other file functions
if (!file.exists()) {
@@ -211,6 +215,72 @@ inline std::string read_proc_scsi_scsi_file(std::vector<std::string>& lines)
return error_msg;
}
int last_scsi_host = -1;
pcrecpp::RE host_re = app_pcre_re("^Host: scsi([0-9]+)");
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string trimmed = hz::string_trim_copy(lines[i]);
// The format of this file is scsi host number on one line, vendor on another,
// some other info on the third.
// debug_out_error("app", "SCSI Line: " << hz::string_trim_copy(lines[i]) << "\n");
std::string scsi_host_str;
if (host_re.PartialMatch(trimmed, &scsi_host_str)) {
// debug_out_dump("app", "SCSI Host " << scsi_host_str << " found.\n");
hz::string_is_numeric(scsi_host_str, last_scsi_host);
} else if (last_scsi_host != -1 && app_pcre_match("/Vendor: /i", trimmed)) {
vendors_models.push_back(std::make_pair(last_scsi_host, trimmed));
}
}
return std::string();
}
/// host chan id lun type opens qdepth busy online
/// Read /proc/scsi/sg/devices file. Return error message on error.
/// \c sg_entries is filled with lines parsed as ints.
/// Each line index corresponds to N in /dev/sgN.
inline std::string read_proc_scsi_sg_devices_file(std::vector< std::vector<int> >& sg_entries)
{
std::string path;
if (!rconfig::get_data("system/linux_proc_scsi_sg_devices_path", path) || path.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Sg devices file path is not set.\n");
return "Sg devices path is not set.";
}
hz::File file(path);
std::vector<std::string> lines;
if (!read_proc_file(file, lines)) { // this outputs to debug too
std::string error_msg = file.get_error_utf8(); // save before calling other file functions
if (!file.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Sg devices file doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Sg devices file exists but cannot be read.\n");
}
return error_msg;
}
pcrecpp::RE parse_re = app_pcre_re("^([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)");
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string trimmed = hz::string_trim_copy(lines[i]);
std::vector<std::string> line(9);
if (parse_re.PartialMatch(trimmed, &line[0], &line[1], &line[2], &line[3], &line[4], &line[5], &line[6], &line[7], &line[8])) {
std::vector<int> line_num(line.size(), -1);
for (std::size_t j = 0; j < line_num.size(); ++j) {
hz::string_is_numeric(line[j], line_num[j]);
}
sg_entries.resize(i+1); // maintain the line indices
sg_entries[i] = line_num;
} else {
debug_out_warn("app", DBG_FUNC_MSG << "Sg devices line offset " << i << " has invalid format.\n");
}
}
return std::string();
}
@@ -401,7 +471,7 @@ inline std::string detect_drives_linux_3ware(std::vector<StorageDeviceRefPtr>& d
// Check /proc/devices for twa or twe
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string dev;
if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+(tw[ae])/", hz::string_trim_copy(lines[i]), &dev)) {
if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+(tw[ae])(?:[ \\t]*|$)/", hz::string_trim_copy(lines[i]), &dev)) {
debug_out_dump("app", DBG_FUNC_MSG << "Found " << dev << " entry in devices file.\n");
if (dev == "twa") {
twa_found = true;
@@ -418,36 +488,28 @@ inline std::string detect_drives_linux_3ware(std::vector<StorageDeviceRefPtr>& d
}
lines.clear();
error_msg = read_proc_scsi_scsi_file(lines);
std::vector< std::pair<int, std::string> > vendors_models;
error_msg = read_proc_scsi_scsi_file(vendors_models);
if (!error_msg.empty()) {
return error_msg;
}
int num_controllers = 0;
int last_scsi_host = 0;
pcrecpp::RE host_re = app_pcre_re("^Host: scsi([0-9]+)");
for (std::size_t i = 0; i < lines.size(); ++i) {
// The format of this file is scsi host number on one line, vendor on another.
// debug_out_error("app", "SCSI Line: " << hz::string_trim_copy(lines[i]) << "\n");
std::string scsi_host_str;
if (host_re.PartialMatch(hz::string_trim_copy(lines[i]), &scsi_host_str)) {
// debug_out_dump("app", "SCSI Host " << scsi_host_str << " found.\n");
hz::string_is_numeric(scsi_host_str, last_scsi_host);
}
if (!app_pcre_match("/Vendor: (AMCC)|(3ware) /i", hz::string_trim_copy(lines[i]))) {
for (std::size_t i = 0; i < vendors_models.size(); ++i) {
if (!app_pcre_match("/Vendor: (AMCC)|(3ware) /i", vendors_models[i].second)) {
continue; // not a supported controller
}
debug_out_dump("app", "Found AMCC/3ware controller in SCSI file, SCSI host " << last_scsi_host << ".\n");
debug_out_dump("app", "Found AMCC/3ware controller in SCSI file, SCSI host " << vendors_models[i].first << ".\n");
// We can't handle both twa and twe in one system, so assume twa by default.
// We can't map twaX to scsiY, so lets assume the relative order is the same.
std::string dev = std::string("/dev/") + (twa_found ? "twa" : "twe") + hz::number_to_string(num_controllers);
error_msg = tw_cli_get_drives(dev, last_scsi_host, drives, ex_factory, false);
error_msg = tw_cli_get_drives(dev, vendors_models[i].first, drives, ex_factory, false);
if (!error_msg.empty()) { // no tw_cli
int max_ports = rconfig::get_data<int32_t>("system/linux_max_scan_ports");
max_ports = std::max(max_ports, 23); // 128 smartctl calls are too much (it's too slow). Settle for 24.
@@ -469,6 +531,112 @@ inline std::string detect_drives_linux_3ware(std::vector<StorageDeviceRefPtr>& d
}
/**
Detect drives behind Adaptec RAID controller.
Tested using Adaptec RAID 5805 (SAS / SATA controller).
Adaptec Linux detection strategy:
Check /proc/devices for "aac"; if it's there, continue.
Check /proc/scsi/scsi for "Vendor: Adaptec"; get its host (e.g. scsi6).
Check which lines correspond to e.g. host 6 in /proc/scsi/sg/devices; the
line order (e.g. fourth and fifth lines) correspond to /dev/sg* device (e.g.
/dev/sg3 and /dev/sg4).
Note: This will get us at least 2 unusable devices - /dev/sdc (the volume)
and /dev/sg3 (the controller). I think the controller can be filtered out
using "id > 0" requirement (the third column of /proc/scsi/sg/devices.
Try "-d sat" by default. If it fails ("Device Read Identity Failed:" ? not
sure how to detect the failure), fall back to "-d scsi".
*/
inline std::string detect_drives_linux_adaptec(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<std::string> lines;
std::string error_msg = read_proc_devices_file(lines);
if (!error_msg.empty()) {
return error_msg;
}
bool aac_found = false;
// Check /proc/devices for twa or twe
for (std::size_t i = 0; i < lines.size(); ++i) {
if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+aac(?:[ \\t]*|$)/", hz::string_trim_copy(lines[i]))) {
debug_out_dump("app", DBG_FUNC_MSG << "Found aac entry in devices file.\n");
aac_found = true;
}
}
if (!aac_found) {
return std::string(); // no controllers
}
lines.clear();
std::vector< std::pair<int, std::string> > vendors_models;
error_msg = read_proc_scsi_scsi_file(vendors_models);
if (!error_msg.empty()) {
return error_msg;
}
std::vector< std::vector<int> > sg_entries;
error_msg = read_proc_scsi_sg_devices_file(sg_entries);
if (!error_msg.empty()) {
return error_msg;
}
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
int num_controllers = 0;
for (std::size_t i = 0; i < vendors_models.size(); ++i) {
if (!app_pcre_match("/Vendor: Adaptec /i", vendors_models[i].second)) {
continue; // not a supported controller
}
int host_num = vendors_models[i].first;
debug_out_dump("app", "Found Adaptec controller in SCSI file, SCSI host " << host_num << ".\n");
for (std::size_t sg_num = 0; sg_num < sg_entries.size(); ++sg_num) {
if (sg_entries[sg_num].size() < 3) {
// We need at least 3 columns in that file
continue;
}
if (sg_entries[sg_num][0] != host_num || sg_entries[sg_num][2] <= 0) {
// Different scsi host, or scsi id is 0 (the controller, probably)
continue;
}
std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num);
StorageDeviceRefPtr drive(new StorageDevice(dev, std::string("sat")));
std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
std::string output = drive->get_info_output();
// Note: Not sure about this, have to check with real SAS drives
if (app_pcre_match("/Device Read Identity Failed/mi", output)) {
// "-d sat" didn't work, default back to smartctl's "-d scsi"
drive->clear_fetched();
drive->set_type_argument("");
local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
}
if (!local_error_msg.empty()) {
debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n");
} else {
drives.push_back(drive);
}
}
++num_controllers;
}
if (num_controllers == 0) {
debug_out_warn("app", DBG_FUNC_MSG << "Adaptec entry found in devices file, but SCSI file contains no known entries.\n");
}
return error_msg;
}
} // anon ns
@@ -495,6 +663,11 @@ std::string detect_drives_linux(std::vector<StorageDeviceRefPtr>& drives, Execut
error_msgs.push_back(error_msg);
}
error_msg = detect_drives_linux_adaptec(drives, ex_factory);
if (!error_msg.empty()) {
error_msgs.push_back(error_msg);
}
return hz::string_join(error_msgs, "\n");
}
+1
View File
@@ -50,6 +50,7 @@ inline void init_default_settings()
rconfig::set_default_data("system/linux_proc_partitions_path", "/proc/partitions"); // file in linux /proc/partitions format
rconfig::set_default_data("system/linux_proc_devices_path", "/proc/devices"); // file in linux /proc/devices format
rconfig::set_default_data("system/linux_proc_scsi_scsi_path", "/proc/scsi/scsi"); // file in linux /proc/scsi/scsi format
rconfig::set_default_data("system/linux_proc_scsi_sg_devices_path", "/proc/scsi/sg/devices"); // file in linux /proc/scsi/sg/devices format
rconfig::set_default_data("system/linux_max_scan_ports", int32_t(23)); // maximum number of RAID ports to scan if no other method is available
rconfig::set_default_data("system/solaris_dev_path", "/dev/rdsk"); // path to /dev/rdsk for solaris.
rconfig::set_default_data("system/unix_sdev_path", "/dev"); // path to /dev. used by other unices