mmc plugin: initial checkin (#3887)

Signed-off-by: Florian Eckert <fe@dev.tdt.de>
This commit is contained in:
Florian Eckert
2021-09-13 15:00:46 +02:00
committed by GitHub
parent 478dd8e2ee
commit f76de1408c
7 changed files with 306 additions and 0 deletions

View File

@@ -183,6 +183,9 @@ Flavio Stanchina <flavio at stanchina.net>
Franck Lombardi
- UNIX socket code for the memcached plugin.
Florian Eckert
- MMC plugin
Gergely Nagy <algernon at madhouse-project.org>
- Write-Riemann plugin.

View File

@@ -1472,6 +1472,13 @@ mqtt_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMOSQUITTO_LDFLAGS)
mqtt_la_LIBADD = $(BUILD_WITH_LIBMOSQUITTO_LIBS)
endif
if BUILD_PLUGIN_MMC
pkglib_LTLIBRARIES += mmc.la
mmc_la_SOURCES = src/mmc.c
mmc_la_LDFLAGS = $(PLUGIN_LDFLAGS)
mmc_la_LIBADD = libignorelist.la
endif
if BUILD_PLUGIN_MULTIMETER
pkglib_LTLIBRARIES += multimeter.la
multimeter_la_SOURCES = src/multimeter.c

5
README
View File

@@ -265,6 +265,11 @@ Features
Collects CPU usage, memory usage, temperatures and power consumption from
Intel Many Integrated Core (MIC) CPUs.
- mmc
Reads the percentage of bad blocks on mmc device. The additional values
for block erases and power cycles are also read. This Plugin in does only
work for the swissbit mmc Cards. MANFID=0x5D OEMID=0x5342
- modbus
Reads values from Modbus/TCP enabled devices. Supports reading values
from multiple "slaves" so gateway devices can be used.

View File

@@ -6640,6 +6640,7 @@ plugin_log_logstash="no"
plugin_mcelog="no"
plugin_mdevents="no"
plugin_memory="no"
plugin_mmc="no"
plugin_multimeter="no"
plugin_netstat_udp="no"
plugin_nfs="no"
@@ -7125,6 +7126,7 @@ AC_PLUGIN([memory], [$plugin_memory], [Memory usage])
AC_PLUGIN([mic], [$with_mic], [Intel Many Integrated Core stats])
AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin])
AC_PLUGIN([mqtt], [$with_libmosquitto], [MQTT output plugin])
AC_PLUGIN([mmc], [$plugin_mmc], [MMC statistics])
AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values])
AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics])
AC_PLUGIN([netapp], [$with_libnetapp], [NetApp plugin])

View File

@@ -1044,6 +1044,11 @@
# ValuesPercentage false
#</Plugin>
#<Plugin mmc>
# Device "mmc0"
# IgnoreSelected false
#</Plugin>
#<Plugin modbus>
# <Data "data_name">
# RegisterBase 1234

281
src/mmc.c Normal file
View File

@@ -0,0 +1,281 @@
/*
* collectd - src/mmc.c
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; only version 2 of the License is applicable.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author:
* Florian Eckert <fe@dev.tdt.de>
*
*/
#include "collectd.h"
#include "plugin.h"
#include "utils/common/common.h"
#include "utils/ignorelist/ignorelist.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#endif
#define PLUGIN_NAME "mmc"
#define SYS_PATH "/sys/bus/mmc/devices/"
#define DEVICE_KEY "Device"
#define IGNORE_KEY "IgnoreSelected"
static const char *config_keys[] = {
DEVICE_KEY,
IGNORE_KEY,
};
static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
#define MMC_MANUFACTOR "manfid"
#define MMC_OEM_ID "oemid"
#define MMC_SSR "ssr"
static ignorelist_t *ignorelist = NULL;
static int mmc_config(const char *key, const char *value) {
if (ignorelist == NULL)
ignorelist = ignorelist_create(1);
if (ignorelist == NULL) {
ERROR(PLUGIN_NAME ": Ignorelist_create failed");
return -ENOMEM;
}
if (strcasecmp(key, DEVICE_KEY) == 0) {
if (ignorelist_add(ignorelist, value)) {
ERROR(PLUGIN_NAME ": Cannot add value to ignorelist");
return -1;
}
} else if (strcasecmp(key, IGNORE_KEY) == 0)
ignorelist_set_invert(ignorelist, IS_TRUE(value) ? 0 : 1);
else {
ERROR(PLUGIN_NAME ": Invalid option %s", key);
return -1;
}
return 0;
}
static void mmc_submit(const char *dev_name, const char *type, gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
sstrncpy(vl.plugin, PLUGIN_NAME, sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, dev_name, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
plugin_dispatch_values(&vl);
}
static int mmc_read_dev_attr(const char *dev_name, const char *file_name,
char *buffer, int size) {
FILE *fh;
char str[sizeof(SYS_PATH) + strlen(dev_name) + sizeof("/") +
strlen(file_name) + 1];
int length;
snprintf(str, sizeof(str), SYS_PATH "%s/%s", dev_name, file_name);
fh = fopen(str, "r");
if (fh == NULL) {
ERROR(PLUGIN_NAME "(%s): Cannot open file [%s]", dev_name, str);
return EXIT_FAILURE;
}
DEBUG(PLUGIN_NAME "(%s): try to read [%s]", dev_name, str);
if (fgets(buffer, size, fh) == NULL) {
ERROR(PLUGIN_NAME "(%s): Unable to read file [%s] (%s)", dev_name, str,
STRERRNO);
fclose(fh);
return EXIT_FAILURE;
}
fclose(fh);
/* Remove trailing whitespace for sysfs attr read */
length = strlen(buffer);
DEBUG(PLUGIN_NAME "(%s): Read %d characters [%s]", dev_name, length, str);
if (buffer > 0)
buffer[length - 1] = '\0';
return 0;
}
static int mmc_read_manfid(const char *dev_name, int *value) {
char buffer[4096];
if (mmc_read_dev_attr(dev_name, MMC_MANUFACTOR, buffer, sizeof(buffer)) ==
0) {
*value = (int)strtol(buffer, NULL, 0);
DEBUG(PLUGIN_NAME "(%s): [%s]=%s (%d)", dev_name, MMC_MANUFACTOR, buffer,
*value);
return 0;
}
WARNING(PLUGIN_NAME "(%s): Unable to read manufacturer identifier (manfid)",
dev_name);
return EXIT_FAILURE;
}
static int mmc_read_oemid(const char *dev_name, int *value) {
char buffer[4096];
if (mmc_read_dev_attr(dev_name, MMC_OEM_ID, buffer, sizeof(buffer)) == 0) {
*value = (int)strtol(buffer, NULL, 0);
DEBUG(PLUGIN_NAME "(%s): [%s]=%s (%d)", dev_name, MMC_OEM_ID, buffer,
*value);
return 0;
}
WARNING(
PLUGIN_NAME
"(%s): Unable to read original equipment manufacturer identifier (oemid)",
dev_name);
return EXIT_FAILURE;
}
enum mmc_manfid {
MANUFACTUR_SWISSBIT = 93, // 0x5d
};
enum mmc_oemid_swissbit {
OEMID_SWISSBIT_1 = 21314, // 0x5342
};
#define MMC_POWER_CYCLES "mmc_power_cycles"
#define MMC_BLOCK_ERASES "mmc_block_erases"
#define MMC_BAD_BLOCKS "mmc_bad_blocks"
// Size of string buffer with '\0'
#define SWISSBIT_LENGTH_SPARE_BLOCKS 3
#define SWISSBIT_LENGTH_BLOCK_ERASES 13
#define SWISSBIT_LENGTH_POWER_ON 9
#define SWISSBIT_SSR_START_SPARE_BLOCKS 66
#define SWISSBIT_SSR_START_BLOCK_ERASES 92
#define SWISSBIT_SSR_START_POWER_ON 112
static int mmc_read_ssr_swissbit(const char *dev_name) {
char buffer[4096];
int oemid;
int value;
int length;
char bad_blocks[SWISSBIT_LENGTH_SPARE_BLOCKS];
char block_erases[SWISSBIT_LENGTH_BLOCK_ERASES];
char power_on[SWISSBIT_LENGTH_POWER_ON];
if (mmc_read_oemid(dev_name, &oemid) != 0) {
return EXIT_FAILURE;
}
if (oemid != OEMID_SWISSBIT_1) {
INFO(PLUGIN_NAME
"(%s): The mmc device is not suppored by this plugin (oemid: 0x%x)",
dev_name, oemid);
return EXIT_FAILURE;
}
if (mmc_read_dev_attr(dev_name, MMC_SSR, buffer, sizeof(buffer)) != 0) {
return EXIT_FAILURE;
}
/*
* Since the register is read out as a byte stream, it is 128 bytes long.
* One char represents a half byte (nibble).
*
*/
length = strlen(buffer);
DEBUG(PLUGIN_NAME ": %d byte read from SSR register", length);
if (length < 128) {
INFO(PLUGIN_NAME "(%s): The SSR register is not 128 byte long", dev_name);
return EXIT_FAILURE;
}
DEBUG(PLUGIN_NAME "(%s): [%s]=%s", dev_name, MMC_SSR, buffer);
/* write MMC_BAD_BLOCKS */
sstrncpy(bad_blocks, &buffer[SWISSBIT_SSR_START_SPARE_BLOCKS],
sizeof(bad_blocks) - 1);
bad_blocks[sizeof(bad_blocks) - 1] = '\0';
value = (int)strtol(bad_blocks, NULL, 16);
/* convert to more common bad blocks information */
value = abs(value - 100);
DEBUG(PLUGIN_NAME "(%s): [bad_blocks] str=%s int=%d", dev_name, bad_blocks,
value);
mmc_submit(dev_name, MMC_BAD_BLOCKS, (gauge_t)value);
/* write MMC_BLOCK_ERASES */
sstrncpy(block_erases, &buffer[SWISSBIT_SSR_START_BLOCK_ERASES],
sizeof(block_erases) - 1);
block_erases[sizeof(block_erases) - 1] = '\0';
value = (int)strtol(block_erases, NULL, 16);
DEBUG(PLUGIN_NAME "(%s): [block_erases] str=%s int=%d", dev_name,
block_erases, value);
mmc_submit(dev_name, MMC_BLOCK_ERASES, (gauge_t)value);
/* write MMC_POWER_CYCLES */
sstrncpy(power_on, &buffer[SWISSBIT_SSR_START_POWER_ON],
sizeof(power_on) - 1);
power_on[sizeof(power_on) - 1] = '\0';
value = (int)strtol(power_on, NULL, 16);
DEBUG(PLUGIN_NAME "(%s): [power_on] str=%s int=%d", dev_name, power_on,
value);
mmc_submit(dev_name, MMC_POWER_CYCLES, (gauge_t)value);
return 0;
}
static int mmc_read(void) {
DIR *dir;
struct dirent *dirent;
int manfid;
if ((dir = opendir(SYS_PATH)) == NULL) {
ERROR(PLUGIN_NAME ": Cannot open directory [%s]", SYS_PATH);
return -1;
}
while ((dirent = readdir(dir)) != NULL) {
if (ignorelist_match(ignorelist, dirent->d_name))
continue;
if (mmc_read_manfid(dirent->d_name, &manfid) != 0)
continue;
DEBUG(PLUGIN_NAME "(%s): manfid=%d", dirent->d_name, manfid);
switch (manfid) {
case MANUFACTUR_SWISSBIT:
mmc_read_ssr_swissbit(dirent->d_name);
break;
default:
INFO(PLUGIN_NAME
"(%s): The manufactur id %d is not suppored by this plugin",
dirent->d_name, manfid);
break;
}
}
closedir(dir);
return 0;
}
void module_register(void) {
plugin_register_config(PLUGIN_NAME, mmc_config, config_keys, config_keys_num);
plugin_register_read(PLUGIN_NAME, mmc_read);
} /* void module_register */

View File

@@ -166,6 +166,9 @@ memcached_ops value:DERIVE:0:U
memory value:GAUGE:0:281474976710656
memory_lua value:GAUGE:0:281474976710656
memory_throttle_count value:DERIVE:0:U
mmc_power_cycles value:GAUGE:0:U
mmc_block_erases value:GAUGE:0:U
mmc_bad_blocks value:GAUGE:0:255
multimeter value:GAUGE:U:U
mutex_operations value:DERIVE:0:U
mysql_bpool_bytes value:GAUGE:0:U