[PATCH 3/4] Initial implementation for ubihealthd.
Richard Weinberger
richard at nod.at
Thu Nov 5 15:00:02 PST 2015
From: Daniel Walter <dwalter at sigma-star.at>
ubihealthd will read / scrub all PEBs
of a given ubi device over a given amount
of time. This should detect and fix common
errors on various mtds.
Signed-off-by: Daniel Walter <dwalter at sigma-star.at>
Signed-off-by: Richard Weinberger <richard at nod.at>
---
Makefile | 3 +-
ubi-utils/.gitignore | 1 +
ubi-utils/ubihealthd.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 689 insertions(+), 1 deletion(-)
create mode 100644 ubi-utils/ubihealthd.c
diff --git a/Makefile b/Makefile
index 3ce8587..9556da9 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,8 @@ MTD_BINS = \
sumtool jffs2reader
UBI_BINS = \
ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
- ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock
+ ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock \
+ ubihealthd
BINS = $(MTD_BINS)
BINS += mkfs.ubifs/mkfs.ubifs
diff --git a/ubi-utils/.gitignore b/ubi-utils/.gitignore
index 19653a8..80edc23 100644
--- a/ubi-utils/.gitignore
+++ b/ubi-utils/.gitignore
@@ -11,3 +11,4 @@
/ubirsvol
/ubiblock
/mtdinfo
+/ubihealthd
diff --git a/ubi-utils/ubihealthd.c b/ubi-utils/ubihealthd.c
new file mode 100644
index 0000000..97ffbac
--- /dev/null
+++ b/ubi-utils/ubihealthd.c
@@ -0,0 +1,686 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <mtd/ubi-user.h>
+#include <sys/signalfd.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/timerfd.h>
+#include <inttypes.h>
+
+#include <getopt.h>
+#include <libubi.h>
+
+#include "list.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#ifndef NDEBUG
+#define _log(lvl, M, ...) __log(lvl, "[%s:%d] " M, __FILE__, __LINE__, ##__VA_ARGS__);
+#else
+#define _log(lvl, M, ...) __log(lvl, M, ##__VA_ARGS__);
+#endif
+#define log(M, ...) _log(2, M, ##__VA_ARGS__);
+#define log_fatal(M, ...) _log(0, "[FATAL]" M, ##__VA_ARGS__);
+#define log_err(M, ...) _log(1, "[ERR]" M, ##__VA_ARGS__);
+#define log_warn(M, ...) _log(2, "[WARN]" M, ##__VA_ARGS__);
+#define log_info(M, ...) _log(3, "[INFO]" M, ##__VA_ARGS__);
+#define log_debug(M, ...) _log(4, "[DEBUG]" M, ##__VA_ARGS__);
+
+
+int log_level;
+
+static void __log(int level, const char *fmt, ...)
+{
+ va_list ap;
+ if (level > log_level)
+ return;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static const uint64_t UBIHEALTHD_MAGIC_VERSION = 0x00000001LLU;
+
+/*
+ * Basic algorithm:
+ * - get number of PEBs and identify sleep times between scheduling
+ * - read stats to identify hotspots (schedule full block read if identified as such)
+ * - read PEB and remove from list (move to tail ?)
+ */
+
+static const char opt_string[] = "d:f:hr:s:x:v:";
+static const struct option options[] = {
+ {
+ .name = "device",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'd'
+ },
+ {
+ .name = "file",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'f'
+ },
+ {
+ .name = "read_complete",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'r'
+ },
+ {
+ .name = "scrub_complete",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 's'
+ },
+ {
+ .name = "read_threshold",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'x'
+ },
+ {
+ .name = "help",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'h'
+ },
+ {
+ .name = "verbosity",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'v'
+ }
+};
+
+struct peb_info {
+ int64_t peb_num;
+ uint64_t err_cnt;
+ uint64_t read_cnt;
+ uint64_t prev_read_cnt;
+ time_t last_stat_update;
+ time_t last_read;
+ time_t last_err;
+} __attribute__((packed));
+
+typedef enum {
+ SCHED_READ,
+ SCHED_SCRUB
+} sched_type;
+
+struct sched_peb {
+ struct peb_info *peb;
+ sched_type type;
+ struct list_head list;
+};
+
+struct peb_list {
+ struct peb_info *peb;
+ struct list_head list;
+};
+
+static const char *help_str = \
+"[OPTIONS]\n" \
+" -h, --help\t\tShow this message and exit\n" \
+" -d, --device\t\tDevice to be monitored (default: /dev/ubi0)\n" \
+" -f, --file\t\tPath to statistics save file\n" \
+" -r, --read_complete\tTimeframe for reading all PEBs in seconds\n" \
+" -s, --scrub_complete\tTimeframe for scrubbing all PEBs in seconds\n" \
+" -x, --read_threshold\tNumber of reads between two stats updates\n" \
+" \twhich will trigger a PEB read\n" \
+" -v, --verbosity\t\tlog level (0-4)\n";
+
+static void usage(const char* progname)
+{
+ printf("usage: %s [OPTIONS]", progname);
+ printf("\n%s\n", help_str);
+ _exit(1);
+}
+
+static int64_t get_num_pebs(const char *ubi_dev)
+{
+ libubi_t libubi = libubi_open();
+ struct ubi_dev_info dev_info;
+ int err;
+ err = ubi_get_dev_info(libubi, ubi_dev, &dev_info);
+ if (err) {
+ log_err("Could not get ubi info for device %s", ubi_dev);
+ return -1;
+ }
+ libubi_close(libubi);
+ return dev_info.total_lebs;
+}
+
+static int write_stats_file(const char *filename, struct peb_list *peb_head, struct sched_peb *sched_read_head, struct sched_peb *sched_scrub_head, int pnum)
+{
+ int64_t next_read_peb = 0;
+ int64_t next_scrub_peb = 0;
+ struct peb_info *peb = NULL;
+ struct peb_list *tmpp = NULL;
+ struct sched_peb *p = NULL;
+ FILE *file = fopen(filename, "wb");
+ if (file == NULL)
+ return -1;
+ p = list_first_entry_or_null(&sched_read_head->list, struct sched_peb, list);
+ if (p)
+ next_read_peb = p->peb->peb_num;
+ p = list_first_entry_or_null(&sched_scrub_head->list, struct sched_peb, list);
+ if (p)
+ next_scrub_peb = p->peb->peb_num;
+ fwrite(&UBIHEALTHD_MAGIC_VERSION, sizeof(UBIHEALTHD_MAGIC_VERSION), 1, file);
+ fwrite(&pnum, sizeof(pnum), 1, file);
+ fwrite(&next_read_peb, sizeof(next_read_peb), 1, file);
+ fwrite(&next_scrub_peb, sizeof(next_scrub_peb), 1, file);
+ list_for_each_entry(tmpp, &peb_head->list, list) {
+ peb = tmpp->peb;
+ fwrite(peb, sizeof(struct peb_info), 1, file);
+ }
+ fclose(file);
+ return 0;
+}
+
+
+static int init_stats(int fd, struct list_head *head, int pnum)
+{
+ int i, err = 0;
+ size_t req_size = pnum * sizeof(struct ubi_stats_entry);
+ struct ubi_stats_req *req = malloc(sizeof(struct ubi_stats_req) + req_size);
+ if (!req) {
+ log_err("Could not alloc ubi_stats_req: %s", strerror(errno));
+ return -1;
+ }
+ req->req_len = req_size + sizeof(struct ubi_stats_req);
+ req->req_pnum = -1;
+ err = ioctl(fd, UBI_IOCSTATS, req);
+ if (err < 0) {
+ log_err("Could not init stats via ioctl: %s", strerror(errno));
+ free(req);
+ return -1;
+ }
+ log_info("Kernel reported stats for %d PEBs", err);
+ struct peb_info *peb = NULL;
+ struct peb_list *p = NULL;
+ time_t now = time(NULL);
+ for (i = 0; i < err; i++) {
+ struct ubi_stats_entry *s = &req->stats[i];
+ peb = malloc(sizeof(struct peb_info));
+ if (!peb) {
+ log_err("Could not alloc peb_info");
+ free(req);
+ return -1;
+ }
+ peb->peb_num = s->pnum;
+ peb->err_cnt = s->ec;
+ peb->read_cnt = s->rc;
+ peb->prev_read_cnt = s->rc;
+ peb->last_stat_update = now;
+ p = malloc(sizeof(struct peb_list));
+ if (!p) {
+ log_err("Could not alloc peb_list element");
+ free(req);
+ return -1;
+ }
+ p->peb = peb;
+ list_add_tail(&p->list, head);
+ }
+ free(req);
+ return 0;
+}
+
+static void free_list(struct peb_list *head)
+{
+ if (list_empty(&head->list))
+ return;
+ struct peb_list *p = NULL;
+ struct peb_list *tmp = NULL;
+ list_for_each_entry_safe(p, tmp, &head->list, list) {
+ list_del(&p->list);
+ free(p->peb);
+ free(p);
+ }
+}
+
+static int update_stats(int fd, struct peb_list *head, int pnum)
+{
+ if (list_empty(&head->list)) {
+ log_fatal("PEB list not initialized");
+ return -1;
+ }
+ int i, err = 0;
+ size_t req_size = pnum * sizeof(struct ubi_stats_entry);
+ struct ubi_stats_req *req = malloc(sizeof(struct ubi_stats_req) + req_size);
+ if (!req) {
+ log_err("Could not alloc ubi_stats_req: %s", strerror(errno));
+ return -1;
+ }
+ memset(req, 0, sizeof(struct ubi_stats_req));
+ req->req_len = req_size + sizeof(struct ubi_stats_req);
+ req->req_pnum = -1;
+ log_debug("req_len: %d, req_pnum: %d", req->req_len, req->req_pnum);
+ err = ioctl(fd, UBI_IOCSTATS, req);
+ if (err < 0) {
+ log_err("Could not get stats for PEBs, [%d] %s", errno, strerror(errno));
+ free(req);
+ return -1;
+ }
+ log_debug("Kernel reported stats for %d PEBs", err);
+ time_t now = time(NULL);
+ for (i = 0; i < err; i++) {
+ struct ubi_stats_entry *s = &req->stats[i];
+ struct peb_list *p = NULL;
+ struct peb_info *peb = NULL;
+ list_for_each_entry(p, &head->list, list) {
+ if (p->peb && (p->peb->peb_num == s->pnum)) {
+ peb = p->peb;
+ break;
+ }
+ }
+ if (!peb) {
+ log_warn("Could not get stats for PEB %d", i);
+ continue;
+ }
+ /* TODO(sahne): check for overflow ! */
+ peb->err_cnt = s->ec;
+ peb->prev_read_cnt = peb->read_cnt;
+ peb->read_cnt = s->rc;
+ /* check if peb was erased (read_cnt would be reset to 0 if it was) */
+ if (peb->read_cnt < peb->prev_read_cnt)
+ peb->prev_read_cnt = peb->read_cnt;
+ peb->last_stat_update = now;
+ }
+ free(req);
+ return 0;
+}
+
+static int read_peb(int fd, struct peb_info *peb)
+{
+ time_t now = time(NULL);
+ log_debug("Reading PEB %"PRIu64 , peb->peb_num);
+ int err = ioctl(fd, UBI_IOCRPEB, &peb->peb_num);
+ if (err < 0) {
+ log_err("Error while reading PEB %" PRIu64, peb->peb_num);
+ return -1;
+ }
+ peb->last_read = now;
+ return 0;
+}
+
+static int scrub_peb(int fd, struct peb_info *peb)
+{
+ time_t now = time(NULL);
+ log_debug("Scrubbing PEB %"PRIu64, peb->peb_num);
+ int err = ioctl (fd, UBI_IOCSPEB, &peb->peb_num);
+ if (err < 0) {
+ log_err("Error while scrubbing PEB %" PRIu64, peb->peb_num);
+ return -1;
+ }
+ peb->last_read = now;
+ return 0;
+}
+
+static int schedule_peb(struct list_head *sched_list, struct peb_info *peb, sched_type type)
+{
+ struct sched_peb *s = malloc(sizeof(struct sched_peb));
+ if (!s) {
+ log_err("Could not allocate memory");
+ return -1;
+ }
+ s->peb = peb;
+ s->type = type;
+ list_add_tail(&s->list, sched_list);
+ return 0;
+}
+
+static int work(struct sched_peb *sched_list, int fd)
+{
+ if (list_empty(&sched_list->list))
+ return 0;
+ struct sched_peb *sched = list_first_entry(&sched_list->list, struct sched_peb, list);
+ struct peb_info *peb = sched->peb;
+ if (peb == NULL) {
+ log_warn("invalid peb");
+ return -1;
+ }
+ /* delete entry from list, we will add it if needed */
+ list_del(&sched->list);
+ switch(sched->type) {
+ case SCHED_READ:
+ read_peb(fd, peb);
+ break;
+ case SCHED_SCRUB:
+ scrub_peb(fd, peb);
+ break;
+ default:
+ log_warn("Unknown work type: %d", sched->type);
+ free(sched);
+ return -1;
+ }
+ /* reschedule PEB */
+ /* TODO(sahne): check error read/scrub in case PEB went bad (so we don't reschedule it) */
+ schedule_peb(&sched_list->list, peb, sched->type);
+ free(sched);
+ return 1;
+}
+
+static int create_and_arm_timer(int seconds)
+{
+ int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (tfd < 0) {
+ log_err("Could not create timer");
+ return -1;
+ }
+ struct itimerspec tspec = {
+ .it_interval = {
+ .tv_sec = seconds,
+ .tv_nsec = 0,
+ },
+ .it_value = {
+ .tv_sec = 0,
+ .tv_nsec = 1,
+ },
+ };
+ if (timerfd_settime(tfd, 0, &tspec, NULL) < 0) {
+ log_err("Could not arm timer");
+ close(tfd);
+ return -1;
+ }
+
+ return tfd;
+}
+
+static int read_stats_file(const char *filename, struct peb_list *peb_head, struct sched_peb *sched_read_head, struct sched_peb *sched_scrub_head)
+{
+ int num_pebs = 0;
+ int64_t next_read_peb;
+ int64_t next_scrub_peb;
+ uint64_t magic_version;
+ FILE *file = fopen(filename, "rb");
+ ssize_t i;
+ if (file == NULL)
+ return -1;
+ fread(&magic_version, sizeof(magic_version), 1, file);
+ if (magic_version != UBIHEALTHD_MAGIC_VERSION) {
+ log_warn("Magic mismatching, aborting reading from stats file");
+ fclose(file);
+ return -1;
+ }
+ fread(&num_pebs, sizeof(num_pebs), 1, file);
+ fread(&next_read_peb, sizeof(next_read_peb), 1, file);
+ fread(&next_scrub_peb, sizeof(next_scrub_peb), 1, file);
+ for (i = 0; i < num_pebs; i++) {
+ struct peb_info *peb = malloc(sizeof(struct peb_info));
+ if (!peb) {
+ log_err("Could not allocate peb_info");
+ return -1;
+ }
+ struct peb_list *p = NULL;
+ fread(peb, sizeof(struct peb_info), 1, file);
+ list_for_each_entry(p, &peb_head->list, list) {
+ if (p->peb && (p->peb->peb_num == peb->peb_num)) {
+ free(p->peb);
+ p->peb = peb;
+ }
+ }
+ }
+ /* init read and scrub lists */
+ struct peb_list *p = NULL;
+ list_for_each_entry(p, &peb_head->list, list) {
+ if (p->peb->peb_num >= next_read_peb)
+ schedule_peb(&sched_read_head->list, p->peb, SCHED_READ);
+ if (p->peb->peb_num >= next_scrub_peb)
+ schedule_peb(&sched_scrub_head->list, p->peb, SCHED_SCRUB);
+ }
+ p = NULL;
+ list_for_each_entry(p, &peb_head->list, list) {
+ if (p->peb->peb_num < next_read_peb)
+ schedule_peb(&sched_read_head->list, p->peb, SCHED_READ);
+ if (p->peb->peb_num < next_scrub_peb)
+ schedule_peb(&sched_scrub_head->list, p->peb, SCHED_SCRUB);
+ }
+
+ return 0;
+}
+
+static int init_sigfd()
+{
+ int sigfd;
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGQUIT);
+ sigaddset(&mask, SIGHUP);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
+ log_warn("Could not init sigprocmask");
+ return -1;
+ }
+ sigfd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
+ if (sigfd < 0) {
+ log_warn("Could not init signal handling");
+ return -1;
+ }
+ return sigfd;
+}
+
+int main(int argc, char **argv)
+{
+ int c, i;
+ int64_t num_pebs;
+ time_t read_completion = 100000;
+ time_t scrub_completion = 1000000;
+ uint64_t read_threshold = 10000;
+ struct sched_peb *sched_read_head;
+ struct sched_peb *sched_scrub_head;
+ struct peb_list *peb_head;
+ const char *stats_file = "/tmp/ubihealth_stats";
+ const char *ubi_dev = "/dev/ubi0";
+ log_level = 4;
+
+ while ((c = getopt_long(argc, argv, opt_string, options, &i)) != -1) {
+ switch(c) {
+ case 'd':
+ ubi_dev = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'f':
+ stats_file = optarg;
+ break;
+ case 'r':
+ read_completion = atoi(optarg);
+ break;
+ case 's':
+ scrub_completion = atoi(optarg);
+ break;
+ case 'x':
+ read_threshold = atoi(optarg);
+ break;
+ case 'v':
+ log_level = atoi(optarg);
+ if (log_level < 0)
+ log_level = 0;
+ else if (log_level > 4)
+ log_level = 4;
+ case '?':
+ default:
+ break;
+
+ }
+ }
+ /* signal handling */
+ struct signalfd_siginfo fdsi;
+ int sigfd = init_sigfd();
+ if (sigfd < 0) {
+ log_fatal("Could not init signal handling, aborting");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* init sched_list */
+ peb_head = malloc(sizeof(struct peb_list));
+ if (!peb_head) {
+ log_fatal("Could not allocate peb_list");
+ _exit(EXIT_FAILURE);
+ }
+ peb_head->peb = NULL;
+ INIT_LIST_HEAD(&peb_head->list);
+ sched_read_head = malloc(sizeof(struct sched_peb));
+ if (!sched_read_head) {
+ log_fatal("Could not allocate read scheduler");
+ _exit(EXIT_FAILURE);
+ }
+ INIT_LIST_HEAD(&sched_read_head->list);
+ sched_read_head->peb = NULL;
+ sched_scrub_head = malloc(sizeof(struct sched_peb));
+ if (!sched_read_head) {
+ log_fatal("Could not allocate scrub scheduler");
+ _exit(EXIT_FAILURE);
+ }
+ INIT_LIST_HEAD(&sched_scrub_head->list);
+ sched_scrub_head->peb = NULL;
+ int fd = open(ubi_dev, O_RDONLY);
+ if (fd < 0) {
+ log_fatal("Could not open device %s", ubi_dev);
+ return 1;
+ }
+
+ /* get peb info */
+ num_pebs = get_num_pebs(ubi_dev);
+ if (num_pebs < 1) {
+ log_err("Invalid number of PEBs");
+ return 1;
+ }
+ if (init_stats(fd, &peb_head->list, num_pebs) < 0) {
+ log_fatal("Could not init statistics, aborting");
+ _exit(EXIT_FAILURE);
+ }
+ /* init peb list */
+ log_debug("Number of PEBs: %" PRIu64, num_pebs);
+
+
+ if (read_stats_file(stats_file, peb_head, sched_read_head, sched_scrub_head) < 0) {
+ log_warn("Could not init stats from file %s", stats_file);
+ /* init read and scrub lists */
+ struct peb_list *p = NULL;
+ list_for_each_entry(p, &peb_head->list, list) {
+ schedule_peb(&sched_read_head->list, p->peb, SCHED_READ);
+ schedule_peb(&sched_scrub_head->list, p->peb, SCHED_SCRUB);
+ }
+ }
+
+ int shutdown = 0;
+ int stats_timer = create_and_arm_timer(60);
+ if (stats_timer < 0) {
+ log_fatal("Could not create stats timer, aborting");
+ _exit(1);
+ }
+ int read_peb_timer = create_and_arm_timer(read_completion / num_pebs);
+ if (read_peb_timer < 0) {
+ log_fatal("Could not create read timer, aborting");
+ _exit(1);
+ }
+ int scrub_peb_timer = create_and_arm_timer(scrub_completion / num_pebs);
+ if (scrub_peb_timer < 0) {
+ log_fatal("Could not create scrubbing timer, aborting");
+ _exit(1);
+ }
+ struct pollfd pfd[4];
+ pfd[0].fd = sigfd;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = stats_timer;
+ pfd[1].events = POLLIN;
+ pfd[2].fd = read_peb_timer;
+ pfd[2].events = POLLIN;
+ pfd[3].fd = scrub_peb_timer;
+ pfd[3].events = POLLIN;
+ while (!shutdown) {
+ int n = poll(pfd, ARRAY_SIZE(pfd), -1);
+ if (n == -1) {
+ log_err("poll error: %s", strerror(errno));
+ shutdown = 1;
+ }
+ if (n == 0) {
+ continue;
+ }
+ /* signalfd */
+ if (pfd[0].revents & POLLIN) {
+ ssize_t s = read(sigfd, &fdsi, sizeof(fdsi));
+ if (s != sizeof(fdsi)) {
+ log_warn("Could not read from signal fd");
+ continue;
+ }
+ switch(fdsi.ssi_signo) {
+ case SIGUSR1:
+ /* write back stats to disk */
+ write_stats_file(stats_file, peb_head, sched_read_head, sched_scrub_head, num_pebs);
+ break;
+ default:
+ shutdown = 1;
+ break;
+ }
+ }
+ /* stats timer */
+ if (pfd[1].revents & POLLIN) {
+ uint64_t tmp;
+ read(stats_timer, &tmp, sizeof(tmp));
+ /* update stats */
+ if (update_stats(fd, peb_head, num_pebs) < 0) {
+ log_warn("Could not update stats");
+ continue;
+ }
+
+ struct peb_list *p = NULL;
+ /* check if we need to act on any block */
+ list_for_each_entry(p, &peb_head->list, list) {
+ struct peb_info *peb = p->peb;
+ if (!peb)
+ continue;
+ uint64_t read_stats = peb->read_cnt - peb->prev_read_cnt;
+ /* read whole PEB if number of reads since last check is above threshold */
+ if (read_stats >= read_threshold) {
+ log_info("Too many reads for PEB %" PRIu64 " between stats updates, scheduling READ", peb->peb_num);
+ read_peb(fd, peb);
+ }
+ }
+ }
+
+ /* read_peb_timer */
+ if (pfd[2].revents & POLLIN) {
+ uint64_t tmp;
+ read(pfd[2].fd, &tmp, sizeof(tmp));
+ /* do next peb read */
+ if (work(sched_read_head, fd) < 0) {
+ log_err("Error while reading PEB");
+ }
+ }
+
+ /* scrub pebs */
+ if (pfd[3].revents & POLLIN) {
+ uint64_t tmp;
+ read(pfd[3].fd, &tmp, sizeof(tmp));
+ /* do next peb scrub */
+ if (work(sched_scrub_head, fd) < 0) {
+ log_err("Error while scrubbing PEB");
+ }
+ }
+
+ }
+ log_info("Shutting down");
+ write_stats_file(stats_file, peb_head, sched_read_head, sched_scrub_head, num_pebs);
+ close(fd);
+ free_list(peb_head);
+
+ return 0;
+}
--
2.5.0
More information about the linux-mtd
mailing list