Skip to content

Commit

Permalink
ocfs2: Add statistics for the checksum and ecc operations.
Browse files Browse the repository at this point in the history
It would be nice to know how often we get checksum failures.  Even
better, how many of them we can fix with the single bit ecc.  So, we add
a statistics structure.  The structure can be installed into debugfs
wherever the user wants.

For ocfs2, we'll put it in the superblock-specific debugfs directory and
pass it down from our higher-level functions.  The stats are only
registered with debugfs when the filesystem supports metadata ecc.

Signed-off-by: Joel Becker <joel.becker@oracle.com>
  • Loading branch information
Joel Becker committed Jun 4, 2009
1 parent 15633a2 commit 73be192
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 19 deletions.
184 changes: 176 additions & 8 deletions fs/ocfs2/blockcheck.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include <linux/crc32.h>
#include <linux/buffer_head.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/byteorder.h>

#include <cluster/masklog.h>
Expand Down Expand Up @@ -222,6 +225,155 @@ void ocfs2_hamming_fix_block(void *data, unsigned int blocksize,
ocfs2_hamming_fix(data, blocksize * 8, 0, fix);
}


/*
* Debugfs handling.
*/

#ifdef CONFIG_DEBUG_FS

static int blockcheck_u64_get(void *data, u64 *val)
{
*val = *(u64 *)data;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(blockcheck_fops, blockcheck_u64_get, NULL, "%llu\n");

static struct dentry *blockcheck_debugfs_create(const char *name,
struct dentry *parent,
u64 *value)
{
return debugfs_create_file(name, S_IFREG | S_IRUSR, parent, value,
&blockcheck_fops);
}

static void ocfs2_blockcheck_debug_remove(struct ocfs2_blockcheck_stats *stats)
{
if (stats) {
debugfs_remove(stats->b_debug_check);
stats->b_debug_check = NULL;
debugfs_remove(stats->b_debug_failure);
stats->b_debug_failure = NULL;
debugfs_remove(stats->b_debug_recover);
stats->b_debug_recover = NULL;
debugfs_remove(stats->b_debug_dir);
stats->b_debug_dir = NULL;
}
}

static int ocfs2_blockcheck_debug_install(struct ocfs2_blockcheck_stats *stats,
struct dentry *parent)
{
int rc = -EINVAL;

if (!stats)
goto out;

stats->b_debug_dir = debugfs_create_dir("blockcheck", parent);
if (!stats->b_debug_dir)
goto out;

stats->b_debug_check =
blockcheck_debugfs_create("blocks_checked",
stats->b_debug_dir,
&stats->b_check_count);

stats->b_debug_failure =
blockcheck_debugfs_create("checksums_failed",
stats->b_debug_dir,
&stats->b_failure_count);

stats->b_debug_recover =
blockcheck_debugfs_create("ecc_recoveries",
stats->b_debug_dir,
&stats->b_recover_count);
if (stats->b_debug_check && stats->b_debug_failure &&
stats->b_debug_recover)
rc = 0;

out:
if (rc)
ocfs2_blockcheck_debug_remove(stats);
return rc;
}
#else
static inline int ocfs2_blockcheck_debug_install(struct ocfs2_blockcheck_stats *stats,
struct dentry *parent)
{
return 0;
}

static inline void ocfs2_blockcheck_debug_remove(struct ocfs2_blockcheck_stats *stats)
{
}
#endif /* CONFIG_DEBUG_FS */

/* Always-called wrappers for starting and stopping the debugfs files */
int ocfs2_blockcheck_stats_debugfs_install(struct ocfs2_blockcheck_stats *stats,
struct dentry *parent)
{
return ocfs2_blockcheck_debug_install(stats, parent);
}

void ocfs2_blockcheck_stats_debugfs_remove(struct ocfs2_blockcheck_stats *stats)
{
ocfs2_blockcheck_debug_remove(stats);
}

static void ocfs2_blockcheck_inc_check(struct ocfs2_blockcheck_stats *stats)
{
u64 new_count;

if (!stats)
return;

spin_lock(&stats->b_lock);
stats->b_check_count++;
new_count = stats->b_check_count;
spin_unlock(&stats->b_lock);

if (!new_count)
mlog(ML_NOTICE, "Block check count has wrapped\n");
}

static void ocfs2_blockcheck_inc_failure(struct ocfs2_blockcheck_stats *stats)
{
u64 new_count;

if (!stats)
return;

spin_lock(&stats->b_lock);
stats->b_failure_count++;
new_count = stats->b_failure_count;
spin_unlock(&stats->b_lock);

if (!new_count)
mlog(ML_NOTICE, "Checksum failure count has wrapped\n");
}

static void ocfs2_blockcheck_inc_recover(struct ocfs2_blockcheck_stats *stats)
{
u64 new_count;

if (!stats)
return;

spin_lock(&stats->b_lock);
stats->b_recover_count++;
new_count = stats->b_recover_count;
spin_unlock(&stats->b_lock);

if (!new_count)
mlog(ML_NOTICE, "ECC recovery count has wrapped\n");
}



/*
* These are the low-level APIs for using the ocfs2_block_check structure.
*/

/*
* This function generates check information for a block.
* data is the block to be checked. bc is a pointer to the
Expand Down Expand Up @@ -266,12 +418,15 @@ void ocfs2_block_check_compute(void *data, size_t blocksize,
* Again, the data passed in should be the on-disk endian.
*/
int ocfs2_block_check_validate(void *data, size_t blocksize,
struct ocfs2_block_check *bc)
struct ocfs2_block_check *bc,
struct ocfs2_blockcheck_stats *stats)
{
int rc = 0;
struct ocfs2_block_check check;
u32 crc, ecc;

ocfs2_blockcheck_inc_check(stats);

check.bc_crc32e = le32_to_cpu(bc->bc_crc32e);
check.bc_ecc = le16_to_cpu(bc->bc_ecc);

Expand All @@ -282,6 +437,7 @@ int ocfs2_block_check_validate(void *data, size_t blocksize,
if (crc == check.bc_crc32e)
goto out;

ocfs2_blockcheck_inc_failure(stats);
mlog(ML_ERROR,
"CRC32 failed: stored: %u, computed %u. Applying ECC.\n",
(unsigned int)check.bc_crc32e, (unsigned int)crc);
Expand All @@ -292,8 +448,10 @@ int ocfs2_block_check_validate(void *data, size_t blocksize,

/* And check the crc32 again */
crc = crc32_le(~0, data, blocksize);
if (crc == check.bc_crc32e)
if (crc == check.bc_crc32e) {
ocfs2_blockcheck_inc_recover(stats);
goto out;
}

mlog(ML_ERROR, "Fixed CRC32 failed: stored: %u, computed %u\n",
(unsigned int)check.bc_crc32e, (unsigned int)crc);
Expand Down Expand Up @@ -366,7 +524,8 @@ void ocfs2_block_check_compute_bhs(struct buffer_head **bhs, int nr,
* Again, the data passed in should be the on-disk endian.
*/
int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
struct ocfs2_block_check *bc)
struct ocfs2_block_check *bc,
struct ocfs2_blockcheck_stats *stats)
{
int i, rc = 0;
struct ocfs2_block_check check;
Expand All @@ -377,6 +536,8 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
if (!nr)
return 0;

ocfs2_blockcheck_inc_check(stats);

check.bc_crc32e = le32_to_cpu(bc->bc_crc32e);
check.bc_ecc = le16_to_cpu(bc->bc_ecc);

Expand All @@ -388,6 +549,7 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
if (crc == check.bc_crc32e)
goto out;

ocfs2_blockcheck_inc_failure(stats);
mlog(ML_ERROR,
"CRC32 failed: stored: %u, computed %u. Applying ECC.\n",
(unsigned int)check.bc_crc32e, (unsigned int)crc);
Expand Down Expand Up @@ -416,8 +578,10 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
/* And check the crc32 again */
for (i = 0, crc = ~0; i < nr; i++)
crc = crc32_le(crc, bhs[i]->b_data, bhs[i]->b_size);
if (crc == check.bc_crc32e)
if (crc == check.bc_crc32e) {
ocfs2_blockcheck_inc_recover(stats);
goto out;
}

mlog(ML_ERROR, "Fixed CRC32 failed: stored: %u, computed %u\n",
(unsigned int)check.bc_crc32e, (unsigned int)crc);
Expand Down Expand Up @@ -448,9 +612,11 @@ int ocfs2_validate_meta_ecc(struct super_block *sb, void *data,
struct ocfs2_block_check *bc)
{
int rc = 0;
struct ocfs2_super *osb = OCFS2_SB(sb);

if (ocfs2_meta_ecc(OCFS2_SB(sb)))
rc = ocfs2_block_check_validate(data, sb->s_blocksize, bc);
if (ocfs2_meta_ecc(osb))
rc = ocfs2_block_check_validate(data, sb->s_blocksize, bc,
&osb->osb_ecc_stats);

return rc;
}
Expand All @@ -468,9 +634,11 @@ int ocfs2_validate_meta_ecc_bhs(struct super_block *sb,
struct ocfs2_block_check *bc)
{
int rc = 0;
struct ocfs2_super *osb = OCFS2_SB(sb);

if (ocfs2_meta_ecc(OCFS2_SB(sb)))
rc = ocfs2_block_check_validate_bhs(bhs, nr, bc);
if (ocfs2_meta_ecc(osb))
rc = ocfs2_block_check_validate_bhs(bhs, nr, bc,
&osb->osb_ecc_stats);

return rc;
}
Expand Down
29 changes: 27 additions & 2 deletions fs/ocfs2/blockcheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@
#define OCFS2_BLOCKCHECK_H


/* Count errors and error correction from blockcheck.c */
struct ocfs2_blockcheck_stats {
spinlock_t b_lock;
u64 b_check_count; /* Number of blocks we've checked */
u64 b_failure_count; /* Number of failed checksums */
u64 b_recover_count; /* Number of blocks fixed by ecc */

/*
* debugfs entries, used if this is passed to
* ocfs2_blockcheck_stats_debugfs_install()
*/
struct dentry *b_debug_dir; /* Parent of the debugfs files */
struct dentry *b_debug_check; /* Exposes b_check_count */
struct dentry *b_debug_failure; /* Exposes b_failure_count */
struct dentry *b_debug_recover; /* Exposes b_recover_count */
};


/* High level block API */
void ocfs2_compute_meta_ecc(struct super_block *sb, void *data,
struct ocfs2_block_check *bc);
Expand All @@ -37,11 +55,18 @@ int ocfs2_validate_meta_ecc_bhs(struct super_block *sb,
void ocfs2_block_check_compute(void *data, size_t blocksize,
struct ocfs2_block_check *bc);
int ocfs2_block_check_validate(void *data, size_t blocksize,
struct ocfs2_block_check *bc);
struct ocfs2_block_check *bc,
struct ocfs2_blockcheck_stats *stats);
void ocfs2_block_check_compute_bhs(struct buffer_head **bhs, int nr,
struct ocfs2_block_check *bc);
int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
struct ocfs2_block_check *bc);
struct ocfs2_block_check *bc,
struct ocfs2_blockcheck_stats *stats);

/* Debug Initialization */
int ocfs2_blockcheck_stats_debugfs_install(struct ocfs2_blockcheck_stats *stats,
struct dentry *parent);
void ocfs2_blockcheck_stats_debugfs_remove(struct ocfs2_blockcheck_stats *stats);

/*
* Hamming code functions
Expand Down
4 changes: 4 additions & 0 deletions fs/ocfs2/ocfs2.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
#include "ocfs2_fs.h"
#include "ocfs2_lockid.h"

/* For struct ocfs2_blockcheck_stats */
#include "blockcheck.h"

/* Most user visible OCFS2 inodes will have very few pieces of
* metadata, but larger files (including bitmaps, etc) must be taken
* into account when designing an access scheme. We allow a small
Expand Down Expand Up @@ -305,6 +308,7 @@ struct ocfs2_super
struct ocfs2_dinode *local_alloc_copy;
struct ocfs2_quota_recovery *quota_rec;

struct ocfs2_blockcheck_stats osb_ecc_stats;
struct ocfs2_alloc_stats alloc_stats;
char dev_str[20]; /* "major,minor" of the device */

Expand Down
Loading

0 comments on commit 73be192

Please sign in to comment.