From fa8a2288abe4b4df8e3daebca458731bc5eb0085 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Mon, 1 Aug 2011 19:37:32 +0200 Subject: [PATCH 55/84] lsbd: add initial support for data checksumming This patch adds data checksumming support. The sectors checksums are generated and the checksums of last written sectors are checked to catch partial writes. However currently such sectors are not fixed. --- drivers/block/lsbd.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index 2a8249a..6efa961 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -486,6 +486,30 @@ static u32 lsbd_checksum(const void *buf, size_t count) } /** + * lsbd_copy_and_cksum - copy data and compute LSBD checksum + * @dst: destination buffer + * @src: source buffer + * @count: number of bytes to checksum, must be a multiple of 4 + * + * This checksum is used mostly for detecting corruptions caused + * by power failures. + */ +static u32 lsbd_copy_and_cksum(void *dst, const void *src, size_t count) +{ + u32 *d = dst; + const u32 *s = src; + size_t i; + u32 csum = 0x12345678; + + BUG_ON(count & 3); + for (i = 0; i < count / 4; i++) { + d[i] = s[i]; + csum += s[i]; + } + return csum; +} + +/** * lsbd_block_verify_ok - verify LSBD block header checksum * @p: LSBD device * @b: LSBD block to check @@ -642,6 +666,113 @@ static int lsbd_psector_valid(struct lsbd *p, unsigned int psector) } /** + * lsbd_check_sector - check sector checksum + * @p: LSBD device + * @block: block number + * @i: sector in block to check + * @valid_chsum: valid checksum + */ +static int lsbd_check_sector(struct lsbd *p, unsigned int block, + unsigned int i, u32 valid_cksum) +{ + struct buffer_head *s_bh; + void *s; + u32 cksum; + int ret = -EIO; + + s_bh = lsbd_sread(p, block, i + 1); + if (s_bh == NULL) { + lsbd_error(p, "block %d.%d: I/O error\n", block, i + 1); + goto out; + } + + s = (void *) s_bh->b_data; + cksum = lsbd_checksum(s, p->sector_size); + if (cksum != valid_cksum) { + lsbd_error(p, "block %d.%d: checksum error\n", block, i + 1); + goto out; + } + ret = 0; + +out: + brelse(s_bh); + lsbd_put_buffer(p, s_bh); + return ret; +} + +/** + * lsbd_check_sectors - check recently written sectors + * @p: LSBD device + */ +int lsbd_check_sectors(struct lsbd *p) +{ + struct buffer_head *bh; + struct lsbd_block *b; + unsigned int block; + unsigned int i = 0; + struct lsbd_sect *sects; + unsigned int sectors; + unsigned int sectors_max; + unsigned int readed_blocks = 0; + u32 ptab_checksum; + sectors_max = (p->sector_size - sizeof(*b)) / sizeof(sects[0]); + + block = p->cur_block; + do { + readed_blocks++; + bh = lsbd_sread(p, block, 0); + if (bh == NULL) + continue; + b = (void *) bh->b_data; + + if (!lsbd_block_verify_ok(p, b)) { + brelse(bh); + lsbd_put_buffer(p, bh); + continue; + } + + /* + * Process sector -> logical sector mapping firts because + * they are never than lcache. + */ + sects = (void *) (bh->b_data + be32_to_cpu(b->ptab_offset)); + sectors = be32_to_cpu(b->sectors_per_block) - 1; + if (sectors > sectors_max) + sectors = sectors_max; + + ptab_checksum = lsbd_checksum(bh->b_data + + be32_to_cpu(b->ptab_offset), + (p->sectors_per_block - 1) + * sizeof(struct lsbd_sect)); + + if (be32_to_cpu(b->ptab_checksum) == ptab_checksum) { + for (i = 0; i < sectors; i++) { + u32 cksum; + unsigned int sector_id; + + sector_id = be32_to_cpu(sects[i].id); + if (sector_id == LSBD_SECT_INVALID) + continue; + + cksum = be32_to_cpu(sects[i].data_checksum); + lsbd_check_sector(p, block, i, cksum); + } + } else { + lsbd_info(p, "block %d: ptab checksum error\n", + block); + } + + brelse(bh); + lsbd_put_buffer(p, bh); + } while ((readed_blocks < 4) + && ((block = block_prev(p, block)) != p->cur_block)); + + lsbd_info(p, "sector checking finished\n"); + return 0; +} + + +/** * lsbd_read_lcache - create lcache mapping * @p: LSBD device * @@ -1084,6 +1215,7 @@ static int lsbd_mount(struct lsbd *p) ret = lsbd_load_params(p); if (ret) return ret; + lsbd_check_sectors(p); lsbd_read_lcache(p); MOD_INC_USE_COUNT; @@ -2023,6 +2155,7 @@ static int lsbd_write_block(struct lsbd *p) r = lsbd_request_dequeue(p, throttle); if (r != NULL) { int move; + u32 cksum; BUG_ON(r->sector >= p->lsectors); sects[i].id = cpu_to_be32(r->sector); @@ -2041,8 +2174,10 @@ static int lsbd_write_block(struct lsbd *p) BUG_ON(p->lcache[r->sector] >= p->psectors); write_unlock(&p->lcache_lock); - memcpy(bh[i + 1]->b_data, r->bh->b_data, - p->sector_size); + cksum = lsbd_copy_and_cksum(bh[i + 1]->b_data, + r->bh->b_data, p->sector_size); + sects[i].data_checksum = cpu_to_be32(cksum); + move = r->move; if (move) { unlock_buffer(r->bh); -- 1.8.4.652.g0d6e0ce