Skip to content

Commit

Permalink
UBIFS: introduce list sorting debugging checks
Browse files Browse the repository at this point in the history
The UBIFS bug in the GC list sorting comparison functions inspired
me to write internal debugging check functions which verify that
the list of nodes is sorted properly.

So, this patch implements 2 new debugging functions:
 o 'dbg_check_data_nodes_order()' - check order of data nodes list
 o 'dbg_check_nondata_nodes_order()' - check order of non-data nodes list

The debugging functions are executed only if general UBIFS debugging checks are
enabled. And they are compiled out if UBIFS debugging is disabled.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
  • Loading branch information
Artem Bityutskiy authored and Artem Bityutskiy committed Aug 30, 2010
1 parent 1a9476a commit 3bb66b4
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 2 deletions.
156 changes: 156 additions & 0 deletions fs/ubifs/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -2239,6 +2239,162 @@ int dbg_check_filesystem(struct ubifs_info *c)
return err;
}

/**
* dbg_check_data_nodes_order - check that list of data nodes is sorted.
* @c: UBIFS file-system description object
* @head: the list of nodes ('struct ubifs_scan_node' objects)
*
* This function returns zero if the list of data nodes is sorted correctly,
* and %-EINVAL if not.
*/
int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
{
struct list_head *cur;
struct ubifs_scan_node *sa, *sb;

if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
return 0;

for (cur = head->next; cur->next != head; cur = cur->next) {
ino_t inuma, inumb;
uint32_t blka, blkb;

cond_resched();
sa = container_of(cur, struct ubifs_scan_node, list);
sb = container_of(cur->next, struct ubifs_scan_node, list);

if (sa->type != UBIFS_DATA_NODE) {
ubifs_err("bad node type %d", sa->type);
dbg_dump_node(c, sa->node);
return -EINVAL;
}
if (sb->type != UBIFS_DATA_NODE) {
ubifs_err("bad node type %d", sb->type);
dbg_dump_node(c, sb->node);
return -EINVAL;
}

inuma = key_inum(c, &sa->key);
inumb = key_inum(c, &sb->key);

if (inuma < inumb)
continue;
if (inuma > inumb) {
ubifs_err("larger inum %lu goes before inum %lu",
(unsigned long)inuma, (unsigned long)inumb);
goto error_dump;
}

blka = key_block(c, &sa->key);
blkb = key_block(c, &sb->key);

if (blka > blkb) {
ubifs_err("larger block %u goes before %u", blka, blkb);
goto error_dump;
}
if (blka == blkb) {
ubifs_err("two data nodes for the same block");
goto error_dump;
}
}

return 0;

error_dump:
dbg_dump_node(c, sa->node);
dbg_dump_node(c, sb->node);
return -EINVAL;
}

/**
* dbg_check_nondata_nodes_order - check that list of data nodes is sorted.
* @c: UBIFS file-system description object
* @head: the list of nodes ('struct ubifs_scan_node' objects)
*
* This function returns zero if the list of non-data nodes is sorted correctly,
* and %-EINVAL if not.
*/
int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
{
struct list_head *cur;
struct ubifs_scan_node *sa, *sb;

if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
return 0;

for (cur = head->next; cur->next != head; cur = cur->next) {
ino_t inuma, inumb;
uint32_t hasha, hashb;

cond_resched();
sa = container_of(cur, struct ubifs_scan_node, list);
sb = container_of(cur->next, struct ubifs_scan_node, list);

if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE &&
sa->type != UBIFS_XENT_NODE) {
ubifs_err("bad node type %d", sa->type);
dbg_dump_node(c, sa->node);
return -EINVAL;
}
if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE &&
sa->type != UBIFS_XENT_NODE) {
ubifs_err("bad node type %d", sb->type);
dbg_dump_node(c, sb->node);
return -EINVAL;
}

if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
ubifs_err("non-inode node goes before inode node");
goto error_dump;
}

if (sa->type == UBIFS_INO_NODE && sb->type != UBIFS_INO_NODE)
continue;

if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
/* Inode nodes are sorted in descending size order */
if (sa->len < sb->len) {
ubifs_err("smaller inode node goes first");
goto error_dump;
}
continue;
}

/*
* This is either a dentry or xentry, which should be sorted in
* ascending (parent ino, hash) order.
*/
inuma = key_inum(c, &sa->key);
inumb = key_inum(c, &sb->key);

if (inuma < inumb)
continue;
if (inuma > inumb) {
ubifs_err("larger inum %lu goes before inum %lu",
(unsigned long)inuma, (unsigned long)inumb);
goto error_dump;
}

hasha = key_block(c, &sa->key);
hashb = key_block(c, &sb->key);

if (hasha > hashb) {
ubifs_err("larger hash %u goes before %u", hasha, hashb);
goto error_dump;
}
}

return 0;

error_dump:
ubifs_msg("dumping first node");
dbg_dump_node(c, sa->node);
ubifs_msg("dumping second node");
dbg_dump_node(c, sb->node);
return -EINVAL;
return 0;
}

static int invocation_cnt;

int dbg_force_in_the_gaps(void)
Expand Down
4 changes: 4 additions & 0 deletions fs/ubifs/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
int row, int col);
int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
loff_t size);
int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head);
int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);

/* Force the use of in-the-gaps method for testing */

Expand Down Expand Up @@ -465,6 +467,8 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c);
#define dbg_check_lprops(c) 0
#define dbg_check_lpt_nodes(c, cnode, row, col) 0
#define dbg_check_inode_size(c, inode, size) 0
#define dbg_check_data_nodes_order(c, head) 0
#define dbg_check_nondata_nodes_order(c, head) 0
#define dbg_force_in_the_gaps_enabled 0
#define dbg_force_in_the_gaps() 0
#define dbg_failure_mode 0
Expand Down
10 changes: 8 additions & 2 deletions fs/ubifs/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,13 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b)
static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
struct list_head *nondata, int *min)
{
int err;
struct ubifs_scan_node *snod, *tmp;

*min = INT_MAX;

/* Separate data nodes and non-data nodes */
list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
int err;

ubifs_assert(snod->type == UBIFS_INO_NODE ||
snod->type == UBIFS_DATA_NODE ||
snod->type == UBIFS_DENT_NODE ||
Expand Down Expand Up @@ -293,6 +292,13 @@ static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
/* Sort data and non-data nodes */
list_sort(c, &sleb->nodes, &data_nodes_cmp);
list_sort(c, nondata, &nondata_nodes_cmp);

err = dbg_check_data_nodes_order(c, &sleb->nodes);
if (err)
return err;
err = dbg_check_nondata_nodes_order(c, nondata);
if (err)
return err;
return 0;
}

Expand Down

0 comments on commit 3bb66b4

Please sign in to comment.