diff options
author | Diego Ismirlian <dismirlian@gmail.com> | 2021-08-16 21:38:02 -0300 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2021-09-06 08:21:00 +0200 |
commit | f0b32c7ba5136151a961c3a43c8ff430b33d7c33 (patch) | |
tree | 18ed3ec66cdb3933e330c3de8788c626002f70f2 | |
parent | 42ea7cd48d2b3c306d59bb6c530d79f8c25bf9f5 (diff) |
Add ubiscan utility
ubiscan will scan the PEBs in a specific MTD device and print a summary of
the PEB erase counters and (optionally) details about each PEB's status.
Example output:
# ./ubiscan /dev/mtd6
Summary
=========================================================
mtd : 6
type : nand
size : 110362624 bytes (105.2 MiB)
PEBs : 842
min I/O: 2048 bytes
PEB erase counters
=========================================================
valid : 834
empty : 0
corrupted: 0
alien : 0
bad : 8
Histogram
=========================================================
from to count min avg max
---------------------------------------------------------
0 .. 9: 55 1 4 9
10 .. 99: 174 10 48 99
100 .. 999: 514 103 287 987
1000 .. 9999: 91 1004 1880 2251
10000 .. 99999: 0 0 0 0
100000 .. inf: 0 0 0 0
---------------------------------------------------------
Total : 834 1 392 2251
If the --verbose switch is given, ubiscan will print PEB details:
# ./ubiscan --verbose /dev/mtd6
[... same output as before ...]
Details
=========================================================
PEB 0: 253
PEB 1: 1489
PEB 2: 1
PEB 3: 1
PEB 4: 1
PEB 5: 1
PEB 6: 1
PEB 7: 1
PEB 8: 1
PEB 9: 1
PEB 10: 1
...
PEB 832: 1225
PEB 833: 252
PEB 834: 111
PEB 835: 298
PEB 836: 1264
PEB 837: 11
PEB 838: EB_BAD
PEB 839: EB_BAD
PEB 840: EB_BAD
PEB 841: EB_BAD
Signed-off-by: Diego Ismirlian <dismirlian@gmail.com>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r-- | ubi-utils/Makemodule.am | 5 | ||||
-rw-r--r-- | ubi-utils/ubiscan.c | 318 |
2 files changed, 322 insertions, 1 deletions
diff --git a/ubi-utils/Makemodule.am b/ubi-utils/Makemodule.am index 7491a8a..7183ec3 100644 --- a/ubi-utils/Makemodule.am +++ b/ubi-utils/Makemodule.am @@ -25,6 +25,9 @@ ubinize_LDADD = libubi.a libubigen.a libmtd.a libiniparser.a ubiformat_SOURCES = ubi-utils/ubiformat.c include/mtd_swab.h ubiformat_LDADD = libubi.a libubigen.a libmtd.a libscan.a +ubiscan_SOURCES = ubi-utils/ubiscan.c include/mtd_swab.h +ubiscan_LDADD = libubi.a libubigen.a libscan.a libmtd.a + ubirename_SOURCES = ubi-utils/ubirename.c ubirename_LDADD = libmtd.a libubi.a @@ -44,7 +47,7 @@ endif sbin_PROGRAMS += \ ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ - ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock + ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock ubiscan if WITH_GETRANDOM sbin_PROGRAMS += ubihealthd diff --git a/ubi-utils/ubiscan.c b/ubi-utils/ubiscan.c new file mode 100644 index 0000000..e040bab --- /dev/null +++ b/ubi-utils/ubiscan.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2021 Diego Ismirlian + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to scan MTD devices. + * + * Author: Diego Ismirlian dismirlian (at) google's mail + */ + +#define PROGRAM_NAME "ubiscan" + +#include <sys/stat.h> +#include <unistd.h> +#include <stdint.h> +#include <stdlib.h> +#include <getopt.h> +#include <fcntl.h> +#include <limits.h> + +#include <mtd/ubi-media.h> +#include <libubi.h> +#include <libmtd.h> +#include <libscan.h> +#include "common.h" + +#define MAX_BINS 50 + +/* The variables below are set by command line arguments */ +struct args { + int verbose; + const char *node; + int node_fd; + int bin_thresholds[MAX_BINS - 1]; + int nbins; +}; + +static struct args args = { + .verbose = 0, + .nbins = 6, + .bin_thresholds = { + 10, + 100, + 1000, + 10000, + 100000, + }, +}; + +static const char doc[] = PROGRAM_NAME " version " VERSION + " - a tool to scan MTD devices"; + +static const char optionsstr[] = +"-h, -?, --help print help message\n" +"-H, --histrogram=<list> comma-separated list of bin thresholds\n" +"-v, --verbose be verbose\n" +"-V, --version print program version\n"; + +static const char usage[] = +"Usage: " PROGRAM_NAME " <MTD device node file name> " +"\t\t\t[--help] [--version] [--verbose] [--histogram=<list>]"; + +static const struct option long_options[] = { + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "histogram", .has_arg = 1, .flag = NULL, .val = 'H' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + int last_bin = 0; + while (1) { + int key; + + key = getopt_long(argc, argv, "h?VvH:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'v': + args.verbose = 1; + break; + case 'H': { + args.nbins = 1; + char *token = strtok(optarg, ","); + while (token) { + if (args.nbins == MAX_BINS) + return errmsg("too many bins"); + int th = atoi(token); + if (th <= last_bin) + return errmsg("bad bin threshold list"); + args.bin_thresholds[args.nbins - 1] = th; + last_bin = th; + args.nbins++; + token = strtok(NULL, ","); + } + } break; + case 'V': + common_print_version(); + exit(EXIT_SUCCESS); + case 'h': + printf("%s\n\n", doc); + printf("%s\n\n", usage); + printf("%s\n", optionsstr); + exit(EXIT_SUCCESS); + case '?': + printf("%s\n\n", doc); + printf("%s\n\n", usage); + printf("%s\n", optionsstr); + return -1; + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("MTD device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one MTD device specified (use -h for help)"); + + args.node = argv[optind]; + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libmtd_t libmtd; + struct mtd_info mtd_info; + struct mtd_dev_info mtd; + struct ubi_scan_info *si; + int max, min; + + struct { + int min; + int max; + int cnt; + uint64_t mean; + } bins[MAX_BINS]; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libmtd = libmtd_open(); + if (!libmtd) + return errmsg("MTD subsystem is not present"); + + err = mtd_get_info(libmtd, &mtd_info); + if (err) { + sys_errmsg("cannot get MTD information"); + goto out_close_mtd; + } + + err = mtd_get_dev_info(libmtd, args.node, &mtd); + if (err) { + sys_errmsg("cannot get information about \"%s\"", args.node); + goto out_close_mtd; + } + + args.node_fd = open(args.node, O_RDONLY); + if (args.node_fd == -1) { + sys_errmsg("cannot open \"%s\"", args.node); + goto out_close_mtd; + } + + printf("Summary\n"); + printf("=========================================================\n"); + printf("mtd : %d\n", mtd.mtd_num); + printf("type : %s\n", mtd.type_str); + printf("size : "); + util_print_bytes(mtd.size, 1); + printf("\n"); + printf("PEBs : %d\n", mtd.eb_cnt); + printf("min I/O: %d bytes\n", mtd.min_io_size); + + printf("\n"); + printf("PEB erase counters\n"); + printf("=========================================================\n"); + err = ubi_scan(&mtd, args.node_fd, &si, 0); + if (err) { + errmsg("failed to scan mtd%d (%s)", mtd.mtd_num, args.node); + goto out_close; + } + + memset(bins, 0, sizeof(bins)); + + for (int j = 0; j < args.nbins; j++) + bins[j].min = INT_MAX; + + min = INT_MAX; + max = 0; + + for (int eb = 0; eb < mtd.eb_cnt; eb++) { + uint32_t ec = si->ec[eb]; + switch (ec) { + case EB_EMPTY: + case EB_CORRUPTED: + case EB_ALIEN: + case EB_BAD: + case EC_MAX: + break; + default: { + int bin = 0; + + if (ec > max) + max = ec; + if (ec < min) + min = ec; + + for (int j = 0; j < args.nbins - 1 && ec >= args.bin_thresholds[j]; j++, bin++); + + bins[bin].cnt++; + bins[bin].mean += ec; + if (ec < bins[bin].min) + bins[bin].min = ec; + if (ec > bins[bin].max) + bins[bin].max = ec; + + } break; + } + } + + printf("valid : %d\n", si->ok_cnt); + printf("empty : %d\n", si->empty_cnt); + printf("corrupted: %d\n", si->corrupted_cnt); + printf("alien : %d\n", si->alien_cnt); + printf("bad : %d\n", si->bad_cnt); + + if (si->ok_cnt == 0) + min = 0; + + printf("\n"); + printf("Histogram\n"); + printf("=========================================================\n"); + printf("from to count min avg max\n"); + printf("---------------------------------------------------------\n"); + for (int j = 0; j < args.nbins; j++) { + if (bins[j].cnt) + bins[j].mean /= bins[j].cnt; + else + bins[j].min = 0; + + int from = (j == 0) ? 0 : args.bin_thresholds[j - 1]; + if (j == args.nbins - 1) + printf("%-8d .. inf: %8d %8d %8llu %8d\n", + from, bins[j].cnt, bins[j].min, bins[j].mean, bins[j].max); + else + printf("%-8d .. %8d: %8d %8d %8llu %8d\n", + from, args.bin_thresholds[j] - 1, + bins[j].cnt, bins[j].min, bins[j].mean, bins[j].max); + } + printf("---------------------------------------------------------\n"); + printf("Total : %8d %8d %8llu %8d\n", si->ok_cnt, min, si->mean_ec, max); + + if (args.verbose) { + printf("\n"); + printf("Details\n"); + printf("=========================================================\n"); + for (int eb = 0; eb < mtd.eb_cnt; eb++) { + printf("PEB %8d: ", eb); + uint32_t ec = si->ec[eb]; + switch (ec) { + case EB_EMPTY: + printf("EB_EMPTY\n"); + break; + case EB_CORRUPTED: + printf("EB_CORRUPTED\n"); + break; + case EB_ALIEN: + printf("EB_ALIEN\n"); + break; + case EB_BAD: + printf("EB_BAD\n"); + break; + case EC_MAX: + printf("EC_MAX\n"); + break; + default: + printf("%u\n", ec); + break; + } + } + } + + ubi_scan_free(si); + close(args.node_fd); + libmtd_close(libmtd); + return 0; + +out_close: + close(args.node_fd); +out_close_mtd: + libmtd_close(libmtd); + return -1; +} + |