Gang He
2015-Aug-21 07:43 UTC
[Ocfs2-devel] [PATCH 0/4] ocfs2: add file extent block online check
Besides inode block online check, add file extent block online check this time, we will add more kinds of meta block online check/repair in the future. Gang He (4): ocfs2: update filecheck copyright ocfs2: add errno and macro definitions ocfs2: filecheck validate_extent_block function ocfs2: add file extent block check fs/ocfs2/alloc.c | 116 +++++++++++++++++++++++ fs/ocfs2/alloc.h | 4 + fs/ocfs2/filecheck.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/ocfs2/filecheck.h | 3 +- fs/ocfs2/journal.h | 3 + fs/ocfs2/ocfs2_trace.h | 24 +++++ 6 files changed, 393 insertions(+), 10 deletions(-) -- 2.1.2
Update filecheck copyright to the right time/contributor Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/filecheck.c | 2 +- fs/ocfs2/filecheck.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c index a492e55..4b5a673 100644 --- a/fs/ocfs2/filecheck.c +++ b/fs/ocfs2/filecheck.c @@ -5,7 +5,7 @@ * * Code which implements online file check. * - * Copyright (C) 2007, 2009 Oracle. All rights reserved. + * Copyright (C) 2015 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h index c65fee9..5ec331b 100644 --- a/fs/ocfs2/filecheck.h +++ b/fs/ocfs2/filecheck.h @@ -5,7 +5,7 @@ * * Online file check. * - * Copyright (C) 2007 Oracle. All rights reserved. + * Copyright (C) 2015 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public -- 2.1.2
Gang He
2015-Aug-21 07:43 UTC
[Ocfs2-devel] [PATCH 2/4] ocfs2: add errno and macro definitions
Add new errno, macro definitions and header file inclusion, which will be used for file extent block online check. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/filecheck.c | 4 ++++ fs/ocfs2/filecheck.h | 1 + fs/ocfs2/journal.h | 3 +++ 3 files changed, 8 insertions(+) diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c index 4b5a673..e8bc0cf 100644 --- a/fs/ocfs2/filecheck.c +++ b/fs/ocfs2/filecheck.c @@ -31,7 +31,10 @@ #include "ocfs2.h" #include "ocfs2_fs.h" #include "stackglue.h" +#include "dlmglue.h" #include "inode.h" +#include "alloc.h" +#include "journal.h" #include "filecheck.h" @@ -45,6 +48,7 @@ static const char * const ocfs2_filecheck_errs[] = { "INPROGRESS", "READONLY", "INVALIDINO", + "INVALIDEXT", "BLOCKECC", "BLOCKNO", "VALIDFLAG", diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h index 5ec331b..e9c3fe6 100644 --- a/fs/ocfs2/filecheck.h +++ b/fs/ocfs2/filecheck.h @@ -32,6 +32,7 @@ enum { OCFS2_FILECHECK_ERR_INPROGRESS, /* In progress */ OCFS2_FILECHECK_ERR_READONLY, /* Read only */ OCFS2_FILECHECK_ERR_INVALIDINO, /* Invalid ino */ + OCFS2_FILECHECK_ERR_INVALIDEXT, /* Invalid extent block */ OCFS2_FILECHECK_ERR_BLOCKECC, /* Block ecc */ OCFS2_FILECHECK_ERR_BLOCKNO, /* Block number */ OCFS2_FILECHECK_ERR_VALIDFLAG, /* Inode valid flag */ diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index f4cd3c3..8ab70cd 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -350,6 +350,9 @@ void ocfs2_journal_dirty(handle_t *handle, struct buffer_head *bh); /* simple file updates like chmod, etc. */ #define OCFS2_INODE_UPDATE_CREDITS 1 +/* extent block update */ +#define OCFS2_EXTENT_BLOCK_UPDATE_CREDITS 1 + /* extended attribute block update */ #define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1 -- 2.1.2
Gang He
2015-Aug-21 07:43 UTC
[Ocfs2-devel] [PATCH 3/4] ocfs2: filecheck validate_extent_block function
Add validate_extent_block/repair_extent_block functions for online filecheck. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/alloc.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/alloc.h | 4 ++ fs/ocfs2/ocfs2_trace.h | 24 ++++++++++ 3 files changed, 144 insertions(+) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 0afb4cb..35fbbf1 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -51,6 +51,7 @@ #include "xattr.h" #include "refcounttree.h" #include "ocfs2_trace.h" +#include "filecheck.h" #include "buffer_head_io.h" @@ -934,6 +935,121 @@ bail: return rc; } +static int ocfs2_filecheck_validate_extent_block(struct super_block *sb, + struct buffer_head *bh) +{ + int rc; + struct ocfs2_extent_block *eb + (struct ocfs2_extent_block *)bh->b_data; + + trace_ocfs2_filecheck_validate_extent_block( + (unsigned long long)bh->b_blocknr); + + BUG_ON(!buffer_uptodate(bh)); + + if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { + mlog(ML_ERROR, + "Filecheck: extent block #%llu has bad signature %.*s\n", + (unsigned long long)bh->b_blocknr, + 7, eb->h_signature); + return -OCFS2_FILECHECK_ERR_INVALIDEXT; + } + + rc = ocfs2_validate_meta_ecc(sb, bh->b_data, &eb->h_check); + if (rc) { + mlog(ML_ERROR, "Filecheck: checksum failed for extent " + "block %llu\n", + (unsigned long long)bh->b_blocknr); + return -OCFS2_FILECHECK_ERR_BLOCKECC; + } + + if (le64_to_cpu(eb->h_blkno) != bh->b_blocknr) { + mlog(ML_ERROR, + "Filecheck: extent block #%llu has an invalid h_blkno " + "of %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(eb->h_blkno)); + return -OCFS2_FILECHECK_ERR_BLOCKNO; + } + + if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) { + mlog(ML_ERROR, + "Filecheck: extent block #%llu has an invalid " + "h_fs_generation of #%u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(eb->h_fs_generation)); + return -OCFS2_FILECHECK_ERR_GENERATION; + } + + return 0; +} + +static int ocfs2_filecheck_repair_extent_block(struct super_block *sb, + struct buffer_head *bh) +{ + int rc; + int changed = 0; + struct ocfs2_extent_block *eb + (struct ocfs2_extent_block *)bh->b_data; + + rc = ocfs2_filecheck_validate_extent_block(sb, bh); + /* Can't fix invalid extent block */ + if (!rc || rc == -OCFS2_FILECHECK_ERR_INVALIDEXT) + return rc; + + trace_ocfs2_filecheck_repair_extent_block( + (unsigned long long)bh->b_blocknr); + + if (le64_to_cpu(eb->h_blkno) != bh->b_blocknr) { + eb->h_blkno = cpu_to_le64(bh->b_blocknr); + changed = 1; + mlog(ML_ERROR, + "Filecheck: reset extent block #%llu: h_blkno to %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(eb->h_blkno)); + } + + if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) { + eb->h_fs_generation = cpu_to_le32(OCFS2_SB(sb)->fs_generation); + changed = 1; + mlog(ML_ERROR, + "Filecheck: reset extent block #%llu: " + "h_fs_generation to %u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(eb->h_fs_generation)); + } + + if (changed || ocfs2_validate_meta_ecc(sb, bh->b_data, &eb->h_check)) { + ocfs2_compute_meta_ecc(sb, bh->b_data, &eb->h_check); + mark_buffer_dirty(bh); + mlog(ML_ERROR, + "Filecheck: reset extent block #%llu: compute meta ecc\n", + (unsigned long long)bh->b_blocknr); + } + + return 0; +} + +int +ocfs2_filecheck_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno, + struct buffer_head **bh, unsigned int flags) +{ + int rc; + struct buffer_head *tmp = *bh; + + if (!flags) /* check extent block */ + rc = ocfs2_read_block(ci, eb_blkno, &tmp, + ocfs2_filecheck_validate_extent_block); + else /* repair extent block */ + rc = ocfs2_read_block(ci, eb_blkno, &tmp, + ocfs2_filecheck_repair_extent_block); + + if (!rc && !*bh) + *bh = tmp; + + return rc; +} + int ocfs2_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno, struct buffer_head **bh) { diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index fb09b97..c882d52 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h @@ -92,6 +92,10 @@ void ocfs2_init_refcount_extent_tree(struct ocfs2_extent_tree *et, int ocfs2_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno, struct buffer_head **bh); +int +ocfs2_filecheck_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno, + struct buffer_head **bh, unsigned int flags); + struct ocfs2_alloc_context; int ocfs2_insert_extent(handle_t *handle, struct ocfs2_extent_tree *et, diff --git a/fs/ocfs2/ocfs2_trace.h b/fs/ocfs2/ocfs2_trace.h index d9205e0..4788748 100644 --- a/fs/ocfs2/ocfs2_trace.h +++ b/fs/ocfs2/ocfs2_trace.h @@ -557,6 +557,30 @@ TRACE_EVENT(ocfs2_validate_extent_block, TP_printk("%llu ", __entry->blkno) ); +TRACE_EVENT(ocfs2_filecheck_validate_extent_block, + TP_PROTO(unsigned long long blkno), + TP_ARGS(blkno), + TP_STRUCT__entry( + __field(unsigned long long, blkno) + ), + TP_fast_assign( + __entry->blkno = blkno; + ), + TP_printk("%llu ", __entry->blkno) +); + +TRACE_EVENT(ocfs2_filecheck_repair_extent_block, + TP_PROTO(unsigned long long blkno), + TP_ARGS(blkno), + TP_STRUCT__entry( + __field(unsigned long long, blkno) + ), + TP_fast_assign( + __entry->blkno = blkno; + ), + TP_printk("%llu ", __entry->blkno) +); + TRACE_EVENT(ocfs2_rotate_leaf, TP_PROTO(unsigned int insert_cpos, int insert_index, int has_empty, int next_free, -- 2.1.2
Gang He
2015-Aug-21 07:43 UTC
[Ocfs2-devel] [PATCH 4/4] ocfs2: add file extent block check
Add file extent block online check, besides inode block online check, we will add more kinds of meta block online check/repair. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/filecheck.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 239 insertions(+), 8 deletions(-) diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c index e8bc0cf..6aedb9b 100644 --- a/fs/ocfs2/filecheck.c +++ b/fs/ocfs2/filecheck.c @@ -462,38 +462,269 @@ ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent, spin_unlock(&ent->fs_fcheck->fc_lock); } +static int +ocfs2_filecheck_inode_block(struct super_block *sb, unsigned long ino, + unsigned int flags, struct inode **ret) +{ + int rc = 0; + struct inode *inode; + + if (flags == OCFS2_FILECHECK_TYPE_CHK) + flags = OCFS2_FI_FLAG_FILECHECK_CHK; + else + flags = OCFS2_FI_FLAG_FILECHECK_FIX; + + inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0); + if (IS_ERR(inode)) + rc = (int)(long)inode; + else + *ret = inode; + + return rc; +} + +static int +ocfs2_filecheck_extent_list(handle_t *handle, struct inode *inode, + unsigned int flags, struct ocfs2_extent_list *el) +{ + int i, rc = 0; + u64 blkno; + struct buffer_head *bh = NULL; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *ell; + + if (!el || !el->l_tree_depth) + return 0; + + mlog(ML_NOTICE, "ocfs2_filecheck_extent_list: inode %llu tree_depth " + "%u count %u next_free_rec %u\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + le16_to_cpu(el->l_tree_depth), + le16_to_cpu(el->l_count), + le16_to_cpu(el->l_next_free_rec)); + + if (le16_to_cpu(el->l_next_free_rec) == 0) { + mlog(ML_ERROR, + "Inode %llu has empty extent list at depth %u\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + le16_to_cpu(el->l_tree_depth)); + rc = -EROFS; + goto out; + } + + if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) { + mlog(ML_ERROR, + "Inode %llu has bad count in extent list " + "at depth %u (next free=%u, count=%u)\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + le16_to_cpu(el->l_tree_depth), + le16_to_cpu(el->l_next_free_rec), + le16_to_cpu(el->l_count)); + rc = -EROFS; + goto out; + } + + for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { + blkno = le64_to_cpu(el->l_recs[i].e_blkno); + if (blkno == 0) { + mlog(ML_ERROR, + "Inode %llu has bad blkno in extent list " + "at depth %u (index %d)\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + le16_to_cpu(el->l_tree_depth), i); + rc = -EROFS; + goto out; + } + + mlog(ML_NOTICE, "ocfs2_filecheck_extent_list: inode %llu " + "rec[%d]: cpos %u clusters %u blkno %llu\n ", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + i, + le32_to_cpu(el->l_recs[i].e_cpos), + le32_to_cpu(el->l_recs[i].e_int_clusters), + le64_to_cpu(el->l_recs[i].e_blkno)); + + brelse(bh); + bh = NULL; + + rc = ocfs2_filecheck_read_extent_block(INODE_CACHE(inode), + blkno, &bh, flags); + if (rc) { + mlog_errno(rc); + goto out; + } + + if (flags && buffer_dirty(bh)) { + /* Dirty buffer means that filecheck just repaired + * this buffer during reading it from disk. + */ + clear_buffer_dirty(bh); + + rc = ocfs2_extend_trans(handle, + OCFS2_EXTENT_BLOCK_UPDATE_CREDITS); + if (rc) { + mlog_errno(rc); + goto out; + } + + rc = ocfs2_journal_access_eb(handle, + INODE_CACHE(inode), bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (rc < 0) { + mlog_errno(rc); + goto out; + } + + ocfs2_journal_dirty(handle, bh); + } + + eb = (struct ocfs2_extent_block *)bh->b_data; + ell = &eb->h_list; + rc = ocfs2_filecheck_extent_list(handle, inode, flags, ell); + if (rc) + goto out; + } + +out: + brelse(bh); + return rc; +} + +static int +ocfs2_filecheck_extent_block(struct inode *inode, struct buffer_head *di_bh, + unsigned int flags) +{ + int rc = 0; + handle_t *handle = NULL; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_dinode *di; + struct ocfs2_extent_list *el; + + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) + return 0; + + if (flags) { + handle = ocfs2_start_trans(osb, + OCFS2_EXTENT_BLOCK_UPDATE_CREDITS); + if (IS_ERR(handle)) { + rc = PTR_ERR(handle); + mlog_errno(rc); + goto out; + } + } + + di = (struct ocfs2_dinode *)di_bh->b_data; + el = &di->id2.i_list; + rc = ocfs2_filecheck_extent_list(handle, inode, flags, el); + + if (flags) + ocfs2_commit_trans(osb, handle); + +out: + return rc; +} + +static int +ocfs2_filecheck_file_block(struct inode *inode, unsigned int flags) +{ + int rc; + struct buffer_head *di_bh = NULL; + + mutex_lock(&inode->i_mutex); + + rc = ocfs2_rw_lock(inode, 1); + if (rc) { + mlog_errno(rc); + goto out; + } + + rc = ocfs2_inode_lock(inode, &di_bh, 1); + if (rc) { + mlog_errno(rc); + goto out_rw_unlock; + } + + do { + down_write(&OCFS2_I(inode)->ip_alloc_sem); + rc = ocfs2_filecheck_extent_block(inode, di_bh, flags); + up_write(&OCFS2_I(inode)->ip_alloc_sem); + if (rc) + break; + + /* TODO: Add check/fix for xattr/refcount blocks */ + + } while (0); + + brelse(di_bh); + ocfs2_inode_unlock(inode, 1); +out_rw_unlock: + ocfs2_rw_unlock(inode, 1); +out: + mutex_unlock(&inode->i_mutex); + + return rc; +} + +static int +ocfs2_filecheck_other_block(struct inode *inode, unsigned int flags) +{ + int rc = 0; + + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + rc = ocfs2_filecheck_file_block(inode, flags); + break; + case S_IFDIR: + /* TODO: Add check/fix for directory blocks */ + break; + default: + break; + } + + return rc; +} + static unsigned short ocfs2_filecheck_handle(struct super_block *sb, unsigned long ino, unsigned int flags) { + int rc; unsigned short ret = OCFS2_FILECHECK_ERR_SUCCESS; struct inode *inode = NULL; - int rc; - inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0); - if (IS_ERR(inode)) { - rc = (int)(-(long)inode); + do { + rc = ocfs2_filecheck_inode_block(sb, ino, flags, &inode); + if (rc) + break; + + rc = ocfs2_filecheck_other_block(inode, flags); + } while (0); + + if (rc) { + rc = (-rc); if (rc >= OCFS2_FILECHECK_ERR_START && rc < OCFS2_FILECHECK_ERR_END) ret = rc; else ret = OCFS2_FILECHECK_ERR_FAILED; - } else + } + + if (inode) iput(inode); return ret; } -static void +static inline void ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent, struct ocfs2_filecheck_entry *entry) { if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK) entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, - entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK); + entry->fe_ino, OCFS2_FILECHECK_TYPE_CHK); else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX) entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, - entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX); + entry->fe_ino, OCFS2_FILECHECK_TYPE_FIX); else entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED; -- 2.1.2