2015-08-21 07:43:51

by Gang He

[permalink] [raw]
Subject: [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


2015-08-21 07:43:52

by Gang He

[permalink] [raw]
Subject: [PATCH 1/4] ocfs2: update filecheck copyright

Update filecheck copyright to the right time/contributor

Signed-off-by: Gang He <[email protected]>
---
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

2015-08-21 07:44:59

by Gang He

[permalink] [raw]
Subject: [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 <[email protected]>
---
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

2015-08-21 07:44:44

by Gang He

[permalink] [raw]
Subject: [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 <[email protected]>
---
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

2015-08-21 07:43:53

by Gang He

[permalink] [raw]
Subject: [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 <[email protected]>
---
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