From 359c10f69def89c8617f62db29d9ebcc2900637d Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Tue, 17 May 2011 20:18:40 +0200 Subject: [PATCH 27/84] lsbd: add /proc/lsbdstat statistics The LSBD now reports statistics for all devices in /proc/lsbdstat. This is especially useful when mirroring is used. --- drivers/block/lsbd.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 1 deletion(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index a071c0c..57de7fe 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -90,6 +92,14 @@ struct lsbd { unsigned int clean_block; int mirrored; + /* counters */ + unsigned long read_errors; + unsigned long uncorrectable; + unsigned long write_errors; + + unsigned long sectors_written; + unsigned long blocks_written; + lsbd_lcache_t *lcache; unsigned int cur_lcache; @@ -970,6 +980,13 @@ static int lsbd_mount(struct lsbd *p) blksize = blksize_size[MAJOR(p->dev)][MINOR(p->dev)]; if (!blksize) blksize = 1024; + + p->read_errors = 0; + p->uncorrectable = 0; + p->write_errors = 0; + + p->sectors_written = 0; + p->blocks_written = 0; /* * Currently only 4kB sectors are supported. The minimal sector size @@ -1404,6 +1421,7 @@ static int lsbd_make_request(request_queue_t *q, int rw, if (buffer_uptodate(bh)) return 0; + p->read_errors++; /* try to switch this sector to mirror and retry */ lsbd_switch_mirror(p, lsector, sector); sector = p->lcache[lsector]; @@ -1419,6 +1437,10 @@ static int lsbd_make_request(request_queue_t *q, int rw, if (buffer_uptodate(bh)) return 0; + lsbd_info(p, "uncorrectable error sector %d (physical %d " + "and %d)\n", lsector, + sector ^ p->sectors_per_block, sector); + p->uncorrectable++; buffer_IO_error(bh); return 0; } @@ -1441,8 +1463,11 @@ static int lsbd_make_request(request_queue_t *q, int rw, if (current->rt_priority) queue = LSBD_QUEUE_HIGH; - if (lsbd_queue_bh(p, bh, lsector, queue)) + if (lsbd_queue_bh(p, bh, lsector, queue)) { buffer_IO_error(bh); + } else { + p->sectors_written++; + } return 0; } buffer_IO_error(bh); @@ -1800,13 +1825,25 @@ static int lsbd_write_block(struct lsbd *p) generic_make_request(WRITE, bh2[i]); } } + p->blocks_written++; lsbd_debug(p, "waiting for write\n"); for (i = 0; i < p->sectors_per_block; i++) { + int ok = 1; + wait_on_buffer(bh[i]); + if (!buffer_uptodate(bh[i])) { + ok = 0; + lsbd_info(p, "write error block %d\n", p->cur_block); + } if (mirrored) { wait_on_buffer(bh2[i]); + if (!buffer_uptodate(bh2[i])) { + lsbd_info(p, "write error block %d\n", + p->cur_block); + ok = 0; + } brelse(bh2[i]); __lsbd_put_buffer(p, bh2[i]); } @@ -1815,6 +1852,10 @@ static int lsbd_write_block(struct lsbd *p) if (wbh[i] != NULL) wbh[i]->b_end_io(wbh[i], 1); + + if (!ok) { + p->write_errors++; + } } lsbd_debug(p, "done\n"); kfree(wbh); @@ -1923,6 +1964,127 @@ static int lsbd_thread(void *data) return 0; } +static const char *lsbd_status(struct lsbd *p) +{ + /* + * if any uncorrectable read failure occur device should + * be replaced as soon as possible. + */ + if (p->uncorrectable) + return "scrap"; + + if (p->read_errors || p->write_errors) + return "failing"; + + return "healthy"; +} + +static void *lsbd_seq_start(struct seq_file *seq, loff_t *pos) +{ + loff_t l = *pos; + unsigned int i; + + if (l >= 0x10000) + return NULL; + + if (!l--) + /* header */ + return (void *)1; + + for (i = 0; i < LSBD_MAX; i++) { + if (!l--) { + return &lsbd_dev[i]; + } + } + + if (!l--) + return (void *)2;/* tail */ + return NULL; +} + +static void *lsbd_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct lsbd *tmp; + struct lsbd *p = v; + void *next; + + ++*pos; + if (v == (void *)2) + return NULL; + + if (v == (void *)1) + tmp = &lsbd_dev[0]; + else + tmp = p + 1; + + if (tmp != &lsbd_dev[LSBD_MAX]) + next = tmp; + else { + next = (void *)2; + *pos = 0x10000; + } + + return next; +} + +static void lsbd_seq_stop(struct seq_file *seq, void *v) +{ +} + +static int lsbd_seq_show(struct seq_file *seq, void *v) +{ + struct lsbd *p = v; + + if (v == (void *)1) { + seq_printf(seq, "LSBD 0.0\n\n"); + return 0; + } + if (v == (void *)2) { + return 0; + } + + seq_printf(seq, "lsbd%d : %sactive%s\n", p->id, p->stop ? "in" : "", + p->mirrored ? " (mirrored)" : ""); + seq_printf(seq, " geometry %d KiB * %d * %d = %Ld KiB\n", + p->sector_size >> 10, p->sectors_per_block, + p->blocks, (long long) p->psectors + * (p->sector_size >> 10)); + seq_printf(seq, " logical size: %Ld KiB\n", + (long long) p->lsectors * (p->sector_size >> 10)); + seq_printf(seq, " write: %d %d %Ld %ld %ld\n", p->cur_block, + p->clean_block, p->epoch, + p->sectors_written, p->blocks_written); + seq_printf(seq, " status: %s ( %ld %ld %ld )\n", + lsbd_status(p), p->read_errors, + p->uncorrectable, p->write_errors); + + seq_printf(seq, "\n"); + + return 0; +} + +static struct seq_operations lsbd_seq_ops = { + .start = lsbd_seq_start, + .next = lsbd_seq_next, + .stop = lsbd_seq_stop, + .show = lsbd_seq_show, +}; + +static int lsbd_seq_open(struct inode *inode, struct file *file) +{ + int error; + + error = seq_open(file, &lsbd_seq_ops); + return error; +} + +static struct file_operations lsbd_seq_fops = { + .open = lsbd_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + int __init lsbd_init_module(void) { unsigned int i; @@ -1984,6 +2146,16 @@ int __init lsbd_init_module(void) register_disk(&lsbd_gendisk, MKDEV(MAJOR_NR, i << PART_BITS), 1 << PART_BITS, &lsbd_fops, 0); } + +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry *p; + + p = create_proc_entry("lsbdstat", S_IRUGO, NULL); + if (p) + p->proc_fops = &lsbd_seq_fops; + } +#endif return 0; } -- 1.8.4.652.g0d6e0ce