Merge branch 'linux-next' of git://git.infradead.org/ubifs-2.6
* 'linux-next' of git://git.infradead.org/ubifs-2.6: (52 commits) UBIFS: switch to dynamic printks UBIFS: fix kernel-doc comments UBIFS: fix extremely rare mount failure UBIFS: simplify LEB recovery function further UBIFS: always cleanup the recovered LEB UBIFS: clean up LEB recovery function UBIFS: fix-up free space on mount if flag is set UBIFS: add the fixup function UBIFS: add a superblock flag for free space fix-up UBIFS: share the next_log_lnum helper UBIFS: expect corruption only in last journal head LEBs UBIFS: synchronize write-buffer before switching to the next bud UBIFS: remove BUG statement UBIFS: change bud replay function conventions UBIFS: substitute the replay tree with a replay list UBIFS: simplify replay UBIFS: store free and dirty space in the bud replay entry UBIFS: remove unnecessary stack variable UBIFS: double check that buds are replied in order UBIFS: make 2 functions static ...wifi-calibration
commit
eb08d8ff47
|
@ -115,28 +115,8 @@ ubi.mtd=0 root=ubi0:rootfs rootfstype=ubifs
|
||||||
Module Parameters for Debugging
|
Module Parameters for Debugging
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
When UBIFS has been compiled with debugging enabled, there are 3 module
|
When UBIFS has been compiled with debugging enabled, there are 2 module
|
||||||
parameters that are available to control aspects of testing and debugging.
|
parameters that are available to control aspects of testing and debugging.
|
||||||
The parameters are unsigned integers where each bit controls an option.
|
|
||||||
The parameters are:
|
|
||||||
|
|
||||||
debug_msgs Selects which debug messages to display, as follows:
|
|
||||||
|
|
||||||
Message Type Flag value
|
|
||||||
|
|
||||||
General messages 1
|
|
||||||
Journal messages 2
|
|
||||||
Mount messages 4
|
|
||||||
Commit messages 8
|
|
||||||
LEB search messages 16
|
|
||||||
Budgeting messages 32
|
|
||||||
Garbage collection messages 64
|
|
||||||
Tree Node Cache (TNC) messages 128
|
|
||||||
LEB properties (lprops) messages 256
|
|
||||||
Input/output messages 512
|
|
||||||
Log messages 1024
|
|
||||||
Scan messages 2048
|
|
||||||
Recovery messages 4096
|
|
||||||
|
|
||||||
debug_chks Selects extra checks that UBIFS can do while running:
|
debug_chks Selects extra checks that UBIFS can do while running:
|
||||||
|
|
||||||
|
@ -154,11 +134,9 @@ debug_tsts Selects a mode of testing, as follows:
|
||||||
|
|
||||||
Test mode Flag value
|
Test mode Flag value
|
||||||
|
|
||||||
Force in-the-gaps method 2
|
|
||||||
Failure mode for recovery testing 4
|
Failure mode for recovery testing 4
|
||||||
|
|
||||||
For example, set debug_msgs to 5 to display General messages and Mount
|
For example, set debug_chks to 3 to enable general and TNC checks.
|
||||||
messages.
|
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
|
|
|
@ -106,7 +106,7 @@ static long long get_liability(struct ubifs_info *c)
|
||||||
long long liab;
|
long long liab;
|
||||||
|
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
liab = c->budg_idx_growth + c->budg_data_growth + c->budg_dd_growth;
|
liab = c->bi.idx_growth + c->bi.data_growth + c->bi.dd_growth;
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
return liab;
|
return liab;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ int ubifs_calc_min_idx_lebs(struct ubifs_info *c)
|
||||||
int idx_lebs;
|
int idx_lebs;
|
||||||
long long idx_size;
|
long long idx_size;
|
||||||
|
|
||||||
idx_size = c->old_idx_sz + c->budg_idx_growth + c->budg_uncommitted_idx;
|
idx_size = c->bi.old_idx_sz + c->bi.idx_growth + c->bi.uncommitted_idx;
|
||||||
/* And make sure we have thrice the index size of space reserved */
|
/* And make sure we have thrice the index size of space reserved */
|
||||||
idx_size += idx_size << 1;
|
idx_size += idx_size << 1;
|
||||||
/*
|
/*
|
||||||
|
@ -292,13 +292,13 @@ static int can_use_rp(struct ubifs_info *c)
|
||||||
* budgeted index space to the size of the current index, multiplies this by 3,
|
* budgeted index space to the size of the current index, multiplies this by 3,
|
||||||
* and makes sure this does not exceed the amount of free LEBs.
|
* and makes sure this does not exceed the amount of free LEBs.
|
||||||
*
|
*
|
||||||
* Notes about @c->min_idx_lebs and @c->lst.idx_lebs variables:
|
* Notes about @c->bi.min_idx_lebs and @c->lst.idx_lebs variables:
|
||||||
* o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might
|
* o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might
|
||||||
* be large, because UBIFS does not do any index consolidation as long as
|
* be large, because UBIFS does not do any index consolidation as long as
|
||||||
* there is free space. IOW, the index may take a lot of LEBs, but the LEBs
|
* there is free space. IOW, the index may take a lot of LEBs, but the LEBs
|
||||||
* will contain a lot of dirt.
|
* will contain a lot of dirt.
|
||||||
* o @c->min_idx_lebs is the number of LEBS the index presumably takes. IOW,
|
* o @c->bi.min_idx_lebs is the number of LEBS the index presumably takes. IOW,
|
||||||
* the index may be consolidated to take up to @c->min_idx_lebs LEBs.
|
* the index may be consolidated to take up to @c->bi.min_idx_lebs LEBs.
|
||||||
*
|
*
|
||||||
* This function returns zero in case of success, and %-ENOSPC in case of
|
* This function returns zero in case of success, and %-ENOSPC in case of
|
||||||
* failure.
|
* failure.
|
||||||
|
@ -343,13 +343,13 @@ static int do_budget_space(struct ubifs_info *c)
|
||||||
c->lst.taken_empty_lebs;
|
c->lst.taken_empty_lebs;
|
||||||
if (unlikely(rsvd_idx_lebs > lebs)) {
|
if (unlikely(rsvd_idx_lebs > lebs)) {
|
||||||
dbg_budg("out of indexing space: min_idx_lebs %d (old %d), "
|
dbg_budg("out of indexing space: min_idx_lebs %d (old %d), "
|
||||||
"rsvd_idx_lebs %d", min_idx_lebs, c->min_idx_lebs,
|
"rsvd_idx_lebs %d", min_idx_lebs, c->bi.min_idx_lebs,
|
||||||
rsvd_idx_lebs);
|
rsvd_idx_lebs);
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
available = ubifs_calc_available(c, min_idx_lebs);
|
available = ubifs_calc_available(c, min_idx_lebs);
|
||||||
outstanding = c->budg_data_growth + c->budg_dd_growth;
|
outstanding = c->bi.data_growth + c->bi.dd_growth;
|
||||||
|
|
||||||
if (unlikely(available < outstanding)) {
|
if (unlikely(available < outstanding)) {
|
||||||
dbg_budg("out of data space: available %lld, outstanding %lld",
|
dbg_budg("out of data space: available %lld, outstanding %lld",
|
||||||
|
@ -360,7 +360,7 @@ static int do_budget_space(struct ubifs_info *c)
|
||||||
if (available - outstanding <= c->rp_size && !can_use_rp(c))
|
if (available - outstanding <= c->rp_size && !can_use_rp(c))
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
|
|
||||||
c->min_idx_lebs = min_idx_lebs;
|
c->bi.min_idx_lebs = min_idx_lebs;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,11 +393,11 @@ static int calc_data_growth(const struct ubifs_info *c,
|
||||||
{
|
{
|
||||||
int data_growth;
|
int data_growth;
|
||||||
|
|
||||||
data_growth = req->new_ino ? c->inode_budget : 0;
|
data_growth = req->new_ino ? c->bi.inode_budget : 0;
|
||||||
if (req->new_page)
|
if (req->new_page)
|
||||||
data_growth += c->page_budget;
|
data_growth += c->bi.page_budget;
|
||||||
if (req->new_dent)
|
if (req->new_dent)
|
||||||
data_growth += c->dent_budget;
|
data_growth += c->bi.dent_budget;
|
||||||
data_growth += req->new_ino_d;
|
data_growth += req->new_ino_d;
|
||||||
return data_growth;
|
return data_growth;
|
||||||
}
|
}
|
||||||
|
@ -413,12 +413,12 @@ static int calc_dd_growth(const struct ubifs_info *c,
|
||||||
{
|
{
|
||||||
int dd_growth;
|
int dd_growth;
|
||||||
|
|
||||||
dd_growth = req->dirtied_page ? c->page_budget : 0;
|
dd_growth = req->dirtied_page ? c->bi.page_budget : 0;
|
||||||
|
|
||||||
if (req->dirtied_ino)
|
if (req->dirtied_ino)
|
||||||
dd_growth += c->inode_budget << (req->dirtied_ino - 1);
|
dd_growth += c->bi.inode_budget << (req->dirtied_ino - 1);
|
||||||
if (req->mod_dent)
|
if (req->mod_dent)
|
||||||
dd_growth += c->dent_budget;
|
dd_growth += c->bi.dent_budget;
|
||||||
dd_growth += req->dirtied_ino_d;
|
dd_growth += req->dirtied_ino_d;
|
||||||
return dd_growth;
|
return dd_growth;
|
||||||
}
|
}
|
||||||
|
@ -460,19 +460,19 @@ int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||||
|
|
||||||
again:
|
again:
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
ubifs_assert(c->budg_idx_growth >= 0);
|
ubifs_assert(c->bi.idx_growth >= 0);
|
||||||
ubifs_assert(c->budg_data_growth >= 0);
|
ubifs_assert(c->bi.data_growth >= 0);
|
||||||
ubifs_assert(c->budg_dd_growth >= 0);
|
ubifs_assert(c->bi.dd_growth >= 0);
|
||||||
|
|
||||||
if (unlikely(c->nospace) && (c->nospace_rp || !can_use_rp(c))) {
|
if (unlikely(c->bi.nospace) && (c->bi.nospace_rp || !can_use_rp(c))) {
|
||||||
dbg_budg("no space");
|
dbg_budg("no space");
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->budg_idx_growth += idx_growth;
|
c->bi.idx_growth += idx_growth;
|
||||||
c->budg_data_growth += data_growth;
|
c->bi.data_growth += data_growth;
|
||||||
c->budg_dd_growth += dd_growth;
|
c->bi.dd_growth += dd_growth;
|
||||||
|
|
||||||
err = do_budget_space(c);
|
err = do_budget_space(c);
|
||||||
if (likely(!err)) {
|
if (likely(!err)) {
|
||||||
|
@ -484,9 +484,9 @@ again:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restore the old values */
|
/* Restore the old values */
|
||||||
c->budg_idx_growth -= idx_growth;
|
c->bi.idx_growth -= idx_growth;
|
||||||
c->budg_data_growth -= data_growth;
|
c->bi.data_growth -= data_growth;
|
||||||
c->budg_dd_growth -= dd_growth;
|
c->bi.dd_growth -= dd_growth;
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
|
|
||||||
if (req->fast) {
|
if (req->fast) {
|
||||||
|
@ -506,9 +506,9 @@ again:
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
dbg_budg("FS is full, -ENOSPC");
|
dbg_budg("FS is full, -ENOSPC");
|
||||||
c->nospace = 1;
|
c->bi.nospace = 1;
|
||||||
if (can_use_rp(c) || c->rp_size == 0)
|
if (can_use_rp(c) || c->rp_size == 0)
|
||||||
c->nospace_rp = 1;
|
c->bi.nospace_rp = 1;
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
} else
|
} else
|
||||||
ubifs_err("cannot budget space, error %d", err);
|
ubifs_err("cannot budget space, error %d", err);
|
||||||
|
@ -523,8 +523,8 @@ again:
|
||||||
* This function releases the space budgeted by 'ubifs_budget_space()'. Note,
|
* This function releases the space budgeted by 'ubifs_budget_space()'. Note,
|
||||||
* since the index changes (which were budgeted for in @req->idx_growth) will
|
* since the index changes (which were budgeted for in @req->idx_growth) will
|
||||||
* only be written to the media on commit, this function moves the index budget
|
* only be written to the media on commit, this function moves the index budget
|
||||||
* from @c->budg_idx_growth to @c->budg_uncommitted_idx. The latter will be
|
* from @c->bi.idx_growth to @c->bi.uncommitted_idx. The latter will be zeroed
|
||||||
* zeroed by the commit operation.
|
* by the commit operation.
|
||||||
*/
|
*/
|
||||||
void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
|
void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||||
{
|
{
|
||||||
|
@ -553,23 +553,23 @@ void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||||
if (!req->data_growth && !req->dd_growth)
|
if (!req->data_growth && !req->dd_growth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
c->nospace = c->nospace_rp = 0;
|
c->bi.nospace = c->bi.nospace_rp = 0;
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
c->budg_idx_growth -= req->idx_growth;
|
c->bi.idx_growth -= req->idx_growth;
|
||||||
c->budg_uncommitted_idx += req->idx_growth;
|
c->bi.uncommitted_idx += req->idx_growth;
|
||||||
c->budg_data_growth -= req->data_growth;
|
c->bi.data_growth -= req->data_growth;
|
||||||
c->budg_dd_growth -= req->dd_growth;
|
c->bi.dd_growth -= req->dd_growth;
|
||||||
c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||||
|
|
||||||
ubifs_assert(c->budg_idx_growth >= 0);
|
ubifs_assert(c->bi.idx_growth >= 0);
|
||||||
ubifs_assert(c->budg_data_growth >= 0);
|
ubifs_assert(c->bi.data_growth >= 0);
|
||||||
ubifs_assert(c->budg_dd_growth >= 0);
|
ubifs_assert(c->bi.dd_growth >= 0);
|
||||||
ubifs_assert(c->min_idx_lebs < c->main_lebs);
|
ubifs_assert(c->bi.min_idx_lebs < c->main_lebs);
|
||||||
ubifs_assert(!(c->budg_idx_growth & 7));
|
ubifs_assert(!(c->bi.idx_growth & 7));
|
||||||
ubifs_assert(!(c->budg_data_growth & 7));
|
ubifs_assert(!(c->bi.data_growth & 7));
|
||||||
ubifs_assert(!(c->budg_dd_growth & 7));
|
ubifs_assert(!(c->bi.dd_growth & 7));
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,13 +586,13 @@ void ubifs_convert_page_budget(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
/* Release the index growth reservation */
|
/* Release the index growth reservation */
|
||||||
c->budg_idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
|
c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
|
||||||
/* Release the data growth reservation */
|
/* Release the data growth reservation */
|
||||||
c->budg_data_growth -= c->page_budget;
|
c->bi.data_growth -= c->bi.page_budget;
|
||||||
/* Increase the dirty data growth reservation instead */
|
/* Increase the dirty data growth reservation instead */
|
||||||
c->budg_dd_growth += c->page_budget;
|
c->bi.dd_growth += c->bi.page_budget;
|
||||||
/* And re-calculate the indexing space reservation */
|
/* And re-calculate the indexing space reservation */
|
||||||
c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +612,7 @@ void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
|
||||||
|
|
||||||
memset(&req, 0, sizeof(struct ubifs_budget_req));
|
memset(&req, 0, sizeof(struct ubifs_budget_req));
|
||||||
/* The "no space" flags will be cleared because dd_growth is > 0 */
|
/* The "no space" flags will be cleared because dd_growth is > 0 */
|
||||||
req.dd_growth = c->inode_budget + ALIGN(ui->data_len, 8);
|
req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8);
|
||||||
ubifs_release_budget(c, &req);
|
ubifs_release_budget(c, &req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,9 +682,9 @@ long long ubifs_get_free_space_nolock(struct ubifs_info *c)
|
||||||
int rsvd_idx_lebs, lebs;
|
int rsvd_idx_lebs, lebs;
|
||||||
long long available, outstanding, free;
|
long long available, outstanding, free;
|
||||||
|
|
||||||
ubifs_assert(c->min_idx_lebs == ubifs_calc_min_idx_lebs(c));
|
ubifs_assert(c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
|
||||||
outstanding = c->budg_data_growth + c->budg_dd_growth;
|
outstanding = c->bi.data_growth + c->bi.dd_growth;
|
||||||
available = ubifs_calc_available(c, c->min_idx_lebs);
|
available = ubifs_calc_available(c, c->bi.min_idx_lebs);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When reporting free space to user-space, UBIFS guarantees that it is
|
* When reporting free space to user-space, UBIFS guarantees that it is
|
||||||
|
@ -697,8 +697,8 @@ long long ubifs_get_free_space_nolock(struct ubifs_info *c)
|
||||||
* Note, the calculations below are similar to what we have in
|
* Note, the calculations below are similar to what we have in
|
||||||
* 'do_budget_space()', so refer there for comments.
|
* 'do_budget_space()', so refer there for comments.
|
||||||
*/
|
*/
|
||||||
if (c->min_idx_lebs > c->lst.idx_lebs)
|
if (c->bi.min_idx_lebs > c->lst.idx_lebs)
|
||||||
rsvd_idx_lebs = c->min_idx_lebs - c->lst.idx_lebs;
|
rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
|
||||||
else
|
else
|
||||||
rsvd_idx_lebs = 0;
|
rsvd_idx_lebs = 0;
|
||||||
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
|
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
|
||||||
|
|
|
@ -182,7 +182,7 @@ static int do_commit(struct ubifs_info *c)
|
||||||
c->mst_node->root_len = cpu_to_le32(zroot.len);
|
c->mst_node->root_len = cpu_to_le32(zroot.len);
|
||||||
c->mst_node->ihead_lnum = cpu_to_le32(c->ihead_lnum);
|
c->mst_node->ihead_lnum = cpu_to_le32(c->ihead_lnum);
|
||||||
c->mst_node->ihead_offs = cpu_to_le32(c->ihead_offs);
|
c->mst_node->ihead_offs = cpu_to_le32(c->ihead_offs);
|
||||||
c->mst_node->index_size = cpu_to_le64(c->old_idx_sz);
|
c->mst_node->index_size = cpu_to_le64(c->bi.old_idx_sz);
|
||||||
c->mst_node->lpt_lnum = cpu_to_le32(c->lpt_lnum);
|
c->mst_node->lpt_lnum = cpu_to_le32(c->lpt_lnum);
|
||||||
c->mst_node->lpt_offs = cpu_to_le32(c->lpt_offs);
|
c->mst_node->lpt_offs = cpu_to_le32(c->lpt_offs);
|
||||||
c->mst_node->nhead_lnum = cpu_to_le32(c->nhead_lnum);
|
c->mst_node->nhead_lnum = cpu_to_le32(c->nhead_lnum);
|
||||||
|
|
167
fs/ubifs/debug.c
167
fs/ubifs/debug.c
|
@ -34,7 +34,6 @@
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_UBIFS_FS_DEBUG
|
#ifdef CONFIG_UBIFS_FS_DEBUG
|
||||||
|
|
||||||
|
@ -43,15 +42,12 @@ DEFINE_SPINLOCK(dbg_lock);
|
||||||
static char dbg_key_buf0[128];
|
static char dbg_key_buf0[128];
|
||||||
static char dbg_key_buf1[128];
|
static char dbg_key_buf1[128];
|
||||||
|
|
||||||
unsigned int ubifs_msg_flags;
|
|
||||||
unsigned int ubifs_chk_flags;
|
unsigned int ubifs_chk_flags;
|
||||||
unsigned int ubifs_tst_flags;
|
unsigned int ubifs_tst_flags;
|
||||||
|
|
||||||
module_param_named(debug_msgs, ubifs_msg_flags, uint, S_IRUGO | S_IWUSR);
|
|
||||||
module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR);
|
module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR);
|
||||||
module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR);
|
module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR);
|
||||||
|
|
||||||
MODULE_PARM_DESC(debug_msgs, "Debug message type flags");
|
|
||||||
MODULE_PARM_DESC(debug_chks, "Debug check flags");
|
MODULE_PARM_DESC(debug_chks, "Debug check flags");
|
||||||
MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
|
MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
|
||||||
|
|
||||||
|
@ -317,6 +313,8 @@ void dbg_dump_node(const struct ubifs_info *c, const void *node)
|
||||||
printk(KERN_DEBUG "\tflags %#x\n", sup_flags);
|
printk(KERN_DEBUG "\tflags %#x\n", sup_flags);
|
||||||
printk(KERN_DEBUG "\t big_lpt %u\n",
|
printk(KERN_DEBUG "\t big_lpt %u\n",
|
||||||
!!(sup_flags & UBIFS_FLG_BIGLPT));
|
!!(sup_flags & UBIFS_FLG_BIGLPT));
|
||||||
|
printk(KERN_DEBUG "\t space_fixup %u\n",
|
||||||
|
!!(sup_flags & UBIFS_FLG_SPACE_FIXUP));
|
||||||
printk(KERN_DEBUG "\tmin_io_size %u\n",
|
printk(KERN_DEBUG "\tmin_io_size %u\n",
|
||||||
le32_to_cpu(sup->min_io_size));
|
le32_to_cpu(sup->min_io_size));
|
||||||
printk(KERN_DEBUG "\tleb_size %u\n",
|
printk(KERN_DEBUG "\tleb_size %u\n",
|
||||||
|
@ -602,7 +600,7 @@ void dbg_dump_lstats(const struct ubifs_lp_stats *lst)
|
||||||
spin_unlock(&dbg_lock);
|
spin_unlock(&dbg_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_dump_budg(struct ubifs_info *c)
|
void dbg_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct rb_node *rb;
|
struct rb_node *rb;
|
||||||
|
@ -610,26 +608,42 @@ void dbg_dump_budg(struct ubifs_info *c)
|
||||||
struct ubifs_gced_idx_leb *idx_gc;
|
struct ubifs_gced_idx_leb *idx_gc;
|
||||||
long long available, outstanding, free;
|
long long available, outstanding, free;
|
||||||
|
|
||||||
ubifs_assert(spin_is_locked(&c->space_lock));
|
spin_lock(&c->space_lock);
|
||||||
spin_lock(&dbg_lock);
|
spin_lock(&dbg_lock);
|
||||||
printk(KERN_DEBUG "(pid %d) Budgeting info: budg_data_growth %lld, "
|
printk(KERN_DEBUG "(pid %d) Budgeting info: data budget sum %lld, "
|
||||||
"budg_dd_growth %lld, budg_idx_growth %lld\n", current->pid,
|
"total budget sum %lld\n", current->pid,
|
||||||
c->budg_data_growth, c->budg_dd_growth, c->budg_idx_growth);
|
bi->data_growth + bi->dd_growth,
|
||||||
printk(KERN_DEBUG "\tdata budget sum %lld, total budget sum %lld, "
|
bi->data_growth + bi->dd_growth + bi->idx_growth);
|
||||||
"freeable_cnt %d\n", c->budg_data_growth + c->budg_dd_growth,
|
printk(KERN_DEBUG "\tbudg_data_growth %lld, budg_dd_growth %lld, "
|
||||||
c->budg_data_growth + c->budg_dd_growth + c->budg_idx_growth,
|
"budg_idx_growth %lld\n", bi->data_growth, bi->dd_growth,
|
||||||
c->freeable_cnt);
|
bi->idx_growth);
|
||||||
printk(KERN_DEBUG "\tmin_idx_lebs %d, old_idx_sz %lld, "
|
printk(KERN_DEBUG "\tmin_idx_lebs %d, old_idx_sz %llu, "
|
||||||
"calc_idx_sz %lld, idx_gc_cnt %d\n", c->min_idx_lebs,
|
"uncommitted_idx %lld\n", bi->min_idx_lebs, bi->old_idx_sz,
|
||||||
c->old_idx_sz, c->calc_idx_sz, c->idx_gc_cnt);
|
bi->uncommitted_idx);
|
||||||
|
printk(KERN_DEBUG "\tpage_budget %d, inode_budget %d, dent_budget %d\n",
|
||||||
|
bi->page_budget, bi->inode_budget, bi->dent_budget);
|
||||||
|
printk(KERN_DEBUG "\tnospace %u, nospace_rp %u\n",
|
||||||
|
bi->nospace, bi->nospace_rp);
|
||||||
|
printk(KERN_DEBUG "\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
|
||||||
|
c->dark_wm, c->dead_wm, c->max_idx_node_sz);
|
||||||
|
|
||||||
|
if (bi != &c->bi)
|
||||||
|
/*
|
||||||
|
* If we are dumping saved budgeting data, do not print
|
||||||
|
* additional information which is about the current state, not
|
||||||
|
* the old one which corresponded to the saved budgeting data.
|
||||||
|
*/
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "\tfreeable_cnt %d, calc_idx_sz %lld, idx_gc_cnt %d\n",
|
||||||
|
c->freeable_cnt, c->calc_idx_sz, c->idx_gc_cnt);
|
||||||
printk(KERN_DEBUG "\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, "
|
printk(KERN_DEBUG "\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, "
|
||||||
"clean_zn_cnt %ld\n", atomic_long_read(&c->dirty_pg_cnt),
|
"clean_zn_cnt %ld\n", atomic_long_read(&c->dirty_pg_cnt),
|
||||||
atomic_long_read(&c->dirty_zn_cnt),
|
atomic_long_read(&c->dirty_zn_cnt),
|
||||||
atomic_long_read(&c->clean_zn_cnt));
|
atomic_long_read(&c->clean_zn_cnt));
|
||||||
printk(KERN_DEBUG "\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
|
|
||||||
c->dark_wm, c->dead_wm, c->max_idx_node_sz);
|
|
||||||
printk(KERN_DEBUG "\tgc_lnum %d, ihead_lnum %d\n",
|
printk(KERN_DEBUG "\tgc_lnum %d, ihead_lnum %d\n",
|
||||||
c->gc_lnum, c->ihead_lnum);
|
c->gc_lnum, c->ihead_lnum);
|
||||||
|
|
||||||
/* If we are in R/O mode, journal heads do not exist */
|
/* If we are in R/O mode, journal heads do not exist */
|
||||||
if (c->jheads)
|
if (c->jheads)
|
||||||
for (i = 0; i < c->jhead_cnt; i++)
|
for (i = 0; i < c->jhead_cnt; i++)
|
||||||
|
@ -648,13 +662,15 @@ void dbg_dump_budg(struct ubifs_info *c)
|
||||||
printk(KERN_DEBUG "\tcommit state %d\n", c->cmt_state);
|
printk(KERN_DEBUG "\tcommit state %d\n", c->cmt_state);
|
||||||
|
|
||||||
/* Print budgeting predictions */
|
/* Print budgeting predictions */
|
||||||
available = ubifs_calc_available(c, c->min_idx_lebs);
|
available = ubifs_calc_available(c, c->bi.min_idx_lebs);
|
||||||
outstanding = c->budg_data_growth + c->budg_dd_growth;
|
outstanding = c->bi.data_growth + c->bi.dd_growth;
|
||||||
free = ubifs_get_free_space_nolock(c);
|
free = ubifs_get_free_space_nolock(c);
|
||||||
printk(KERN_DEBUG "Budgeting predictions:\n");
|
printk(KERN_DEBUG "Budgeting predictions:\n");
|
||||||
printk(KERN_DEBUG "\tavailable: %lld, outstanding %lld, free %lld\n",
|
printk(KERN_DEBUG "\tavailable: %lld, outstanding %lld, free %lld\n",
|
||||||
available, outstanding, free);
|
available, outstanding, free);
|
||||||
|
out_unlock:
|
||||||
spin_unlock(&dbg_lock);
|
spin_unlock(&dbg_lock);
|
||||||
|
spin_unlock(&c->space_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
|
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
|
||||||
|
@ -729,7 +745,13 @@ void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
|
||||||
if (bud->lnum == lp->lnum) {
|
if (bud->lnum == lp->lnum) {
|
||||||
int head = 0;
|
int head = 0;
|
||||||
for (i = 0; i < c->jhead_cnt; i++) {
|
for (i = 0; i < c->jhead_cnt; i++) {
|
||||||
if (lp->lnum == c->jheads[i].wbuf.lnum) {
|
/*
|
||||||
|
* Note, if we are in R/O mode or in the middle
|
||||||
|
* of mounting/re-mounting, the write-buffers do
|
||||||
|
* not exist.
|
||||||
|
*/
|
||||||
|
if (c->jheads &&
|
||||||
|
lp->lnum == c->jheads[i].wbuf.lnum) {
|
||||||
printk(KERN_CONT ", jhead %s",
|
printk(KERN_CONT ", jhead %s",
|
||||||
dbg_jhead(i));
|
dbg_jhead(i));
|
||||||
head = 1;
|
head = 1;
|
||||||
|
@ -976,6 +998,8 @@ void dbg_save_space_info(struct ubifs_info *c)
|
||||||
|
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats));
|
memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats));
|
||||||
|
memcpy(&d->saved_bi, &c->bi, sizeof(struct ubifs_budg_info));
|
||||||
|
d->saved_idx_gc_cnt = c->idx_gc_cnt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We use a dirty hack here and zero out @c->freeable_cnt, because it
|
* We use a dirty hack here and zero out @c->freeable_cnt, because it
|
||||||
|
@ -1042,14 +1066,14 @@ int dbg_check_space_info(struct ubifs_info *c)
|
||||||
out:
|
out:
|
||||||
ubifs_msg("saved lprops statistics dump");
|
ubifs_msg("saved lprops statistics dump");
|
||||||
dbg_dump_lstats(&d->saved_lst);
|
dbg_dump_lstats(&d->saved_lst);
|
||||||
ubifs_get_lp_stats(c, &lst);
|
ubifs_msg("saved budgeting info dump");
|
||||||
|
dbg_dump_budg(c, &d->saved_bi);
|
||||||
|
ubifs_msg("saved idx_gc_cnt %d", d->saved_idx_gc_cnt);
|
||||||
ubifs_msg("current lprops statistics dump");
|
ubifs_msg("current lprops statistics dump");
|
||||||
|
ubifs_get_lp_stats(c, &lst);
|
||||||
dbg_dump_lstats(&lst);
|
dbg_dump_lstats(&lst);
|
||||||
|
ubifs_msg("current budgeting info dump");
|
||||||
spin_lock(&c->space_lock);
|
dbg_dump_budg(c, &c->bi);
|
||||||
dbg_dump_budg(c);
|
|
||||||
spin_unlock(&c->space_lock);
|
|
||||||
dump_stack();
|
dump_stack();
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1793,6 +1817,8 @@ static struct fsck_inode *add_inode(struct ubifs_info *c,
|
||||||
struct rb_node **p, *parent = NULL;
|
struct rb_node **p, *parent = NULL;
|
||||||
struct fsck_inode *fscki;
|
struct fsck_inode *fscki;
|
||||||
ino_t inum = key_inum_flash(c, &ino->key);
|
ino_t inum = key_inum_flash(c, &ino->key);
|
||||||
|
struct inode *inode;
|
||||||
|
struct ubifs_inode *ui;
|
||||||
|
|
||||||
p = &fsckd->inodes.rb_node;
|
p = &fsckd->inodes.rb_node;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
|
@ -1816,19 +1842,46 @@ static struct fsck_inode *add_inode(struct ubifs_info *c,
|
||||||
if (!fscki)
|
if (!fscki)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
inode = ilookup(c->vfs_sb, inum);
|
||||||
|
|
||||||
fscki->inum = inum;
|
fscki->inum = inum;
|
||||||
fscki->nlink = le32_to_cpu(ino->nlink);
|
/*
|
||||||
fscki->size = le64_to_cpu(ino->size);
|
* If the inode is present in the VFS inode cache, use it instead of
|
||||||
fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
|
* the on-flash inode which might be out-of-date. E.g., the size might
|
||||||
fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
|
* be out-of-date. If we do not do this, the following may happen, for
|
||||||
fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
|
* example:
|
||||||
fscki->mode = le32_to_cpu(ino->mode);
|
* 1. A power cut happens
|
||||||
|
* 2. We mount the file-system R/O, the replay process fixes up the
|
||||||
|
* inode size in the VFS cache, but on on-flash.
|
||||||
|
* 3. 'check_leaf()' fails because it hits a data node beyond inode
|
||||||
|
* size.
|
||||||
|
*/
|
||||||
|
if (!inode) {
|
||||||
|
fscki->nlink = le32_to_cpu(ino->nlink);
|
||||||
|
fscki->size = le64_to_cpu(ino->size);
|
||||||
|
fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
|
||||||
|
fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
|
||||||
|
fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
|
||||||
|
fscki->mode = le32_to_cpu(ino->mode);
|
||||||
|
} else {
|
||||||
|
ui = ubifs_inode(inode);
|
||||||
|
fscki->nlink = inode->i_nlink;
|
||||||
|
fscki->size = inode->i_size;
|
||||||
|
fscki->xattr_cnt = ui->xattr_cnt;
|
||||||
|
fscki->xattr_sz = ui->xattr_size;
|
||||||
|
fscki->xattr_nms = ui->xattr_names;
|
||||||
|
fscki->mode = inode->i_mode;
|
||||||
|
iput(inode);
|
||||||
|
}
|
||||||
|
|
||||||
if (S_ISDIR(fscki->mode)) {
|
if (S_ISDIR(fscki->mode)) {
|
||||||
fscki->calc_sz = UBIFS_INO_NODE_SZ;
|
fscki->calc_sz = UBIFS_INO_NODE_SZ;
|
||||||
fscki->calc_cnt = 2;
|
fscki->calc_cnt = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_link_node(&fscki->rb, parent, p);
|
rb_link_node(&fscki->rb, parent, p);
|
||||||
rb_insert_color(&fscki->rb, &fsckd->inodes);
|
rb_insert_color(&fscki->rb, &fsckd->inodes);
|
||||||
|
|
||||||
return fscki;
|
return fscki;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2421,7 +2474,8 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
|
||||||
hashb = key_block(c, &sb->key);
|
hashb = key_block(c, &sb->key);
|
||||||
|
|
||||||
if (hasha > hashb) {
|
if (hasha > hashb) {
|
||||||
ubifs_err("larger hash %u goes before %u", hasha, hashb);
|
ubifs_err("larger hash %u goes before %u",
|
||||||
|
hasha, hashb);
|
||||||
goto error_dump;
|
goto error_dump;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2437,14 +2491,12 @@ error_dump:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int invocation_cnt;
|
|
||||||
|
|
||||||
int dbg_force_in_the_gaps(void)
|
int dbg_force_in_the_gaps(void)
|
||||||
{
|
{
|
||||||
if (!dbg_force_in_the_gaps_enabled)
|
if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
||||||
return 0;
|
return 0;
|
||||||
/* Force in-the-gaps every 8th commit */
|
|
||||||
return !((invocation_cnt++) & 0x7);
|
return !(random32() & 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Failure mode for recovery testing */
|
/* Failure mode for recovery testing */
|
||||||
|
@ -2632,7 +2684,7 @@ int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
||||||
int len, int check)
|
int len, int check)
|
||||||
{
|
{
|
||||||
if (in_failure_mode(desc))
|
if (in_failure_mode(desc))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return ubi_leb_read(desc, lnum, buf, offset, len, check);
|
return ubi_leb_read(desc, lnum, buf, offset, len, check);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2642,7 +2694,7 @@ int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||||
int err, failing;
|
int err, failing;
|
||||||
|
|
||||||
if (in_failure_mode(desc))
|
if (in_failure_mode(desc))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
failing = do_fail(desc, lnum, 1);
|
failing = do_fail(desc, lnum, 1);
|
||||||
if (failing)
|
if (failing)
|
||||||
cut_data(buf, len);
|
cut_data(buf, len);
|
||||||
|
@ -2650,7 +2702,7 @@ int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (failing)
|
if (failing)
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2660,12 +2712,12 @@ int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (do_fail(desc, lnum, 1))
|
if (do_fail(desc, lnum, 1))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
err = ubi_leb_change(desc, lnum, buf, len, dtype);
|
err = ubi_leb_change(desc, lnum, buf, len, dtype);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (do_fail(desc, lnum, 1))
|
if (do_fail(desc, lnum, 1))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2674,12 +2726,12 @@ int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (do_fail(desc, lnum, 0))
|
if (do_fail(desc, lnum, 0))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
err = ubi_leb_erase(desc, lnum);
|
err = ubi_leb_erase(desc, lnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (do_fail(desc, lnum, 0))
|
if (do_fail(desc, lnum, 0))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2688,19 +2740,19 @@ int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (do_fail(desc, lnum, 0))
|
if (do_fail(desc, lnum, 0))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
err = ubi_leb_unmap(desc, lnum);
|
err = ubi_leb_unmap(desc, lnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (do_fail(desc, lnum, 0))
|
if (do_fail(desc, lnum, 0))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
||||||
{
|
{
|
||||||
if (in_failure_mode(desc))
|
if (in_failure_mode(desc))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return ubi_is_mapped(desc, lnum);
|
return ubi_is_mapped(desc, lnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2709,12 +2761,12 @@ int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (do_fail(desc, lnum, 0))
|
if (do_fail(desc, lnum, 0))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
err = ubi_leb_map(desc, lnum, dtype);
|
err = ubi_leb_map(desc, lnum, dtype);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (do_fail(desc, lnum, 0))
|
if (do_fail(desc, lnum, 0))
|
||||||
return -EIO;
|
return -EROFS;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2784,7 +2836,7 @@ void dbg_debugfs_exit(void)
|
||||||
static int open_debugfs_file(struct inode *inode, struct file *file)
|
static int open_debugfs_file(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
file->private_data = inode->i_private;
|
file->private_data = inode->i_private;
|
||||||
return 0;
|
return nonseekable_open(inode, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
|
static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
|
||||||
|
@ -2795,18 +2847,15 @@ static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
|
||||||
|
|
||||||
if (file->f_path.dentry == d->dfs_dump_lprops)
|
if (file->f_path.dentry == d->dfs_dump_lprops)
|
||||||
dbg_dump_lprops(c);
|
dbg_dump_lprops(c);
|
||||||
else if (file->f_path.dentry == d->dfs_dump_budg) {
|
else if (file->f_path.dentry == d->dfs_dump_budg)
|
||||||
spin_lock(&c->space_lock);
|
dbg_dump_budg(c, &c->bi);
|
||||||
dbg_dump_budg(c);
|
else if (file->f_path.dentry == d->dfs_dump_tnc) {
|
||||||
spin_unlock(&c->space_lock);
|
|
||||||
} else if (file->f_path.dentry == d->dfs_dump_tnc) {
|
|
||||||
mutex_lock(&c->tnc_mutex);
|
mutex_lock(&c->tnc_mutex);
|
||||||
dbg_dump_tnc(c);
|
dbg_dump_tnc(c);
|
||||||
mutex_unlock(&c->tnc_mutex);
|
mutex_unlock(&c->tnc_mutex);
|
||||||
} else
|
} else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
*ppos += count;
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2814,7 +2863,7 @@ static const struct file_operations dfs_fops = {
|
||||||
.open = open_debugfs_file,
|
.open = open_debugfs_file,
|
||||||
.write = write_debugfs_file,
|
.write = write_debugfs_file,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = default_llseek,
|
.llseek = no_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
178
fs/ubifs/debug.h
178
fs/ubifs/debug.h
|
@ -31,6 +31,8 @@ typedef int (*dbg_znode_callback)(struct ubifs_info *c,
|
||||||
|
|
||||||
#ifdef CONFIG_UBIFS_FS_DEBUG
|
#ifdef CONFIG_UBIFS_FS_DEBUG
|
||||||
|
|
||||||
|
#include <linux/random.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ubifs_debug_info - per-FS debugging information.
|
* ubifs_debug_info - per-FS debugging information.
|
||||||
* @old_zroot: old index root - used by 'dbg_check_old_index()'
|
* @old_zroot: old index root - used by 'dbg_check_old_index()'
|
||||||
|
@ -50,13 +52,15 @@ typedef int (*dbg_znode_callback)(struct ubifs_info *c,
|
||||||
* @new_ihead_offs: used by debugging to check @c->ihead_offs
|
* @new_ihead_offs: used by debugging to check @c->ihead_offs
|
||||||
*
|
*
|
||||||
* @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
|
* @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
|
||||||
* @saved_free: saved free space (used by 'dbg_save_space_info()')
|
* @saved_bi: saved budgeting information
|
||||||
|
* @saved_free: saved amount of free space
|
||||||
|
* @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt
|
||||||
*
|
*
|
||||||
* dfs_dir_name: name of debugfs directory containing this file-system's files
|
* @dfs_dir_name: name of debugfs directory containing this file-system's files
|
||||||
* dfs_dir: direntry object of the file-system debugfs directory
|
* @dfs_dir: direntry object of the file-system debugfs directory
|
||||||
* dfs_dump_lprops: "dump lprops" debugfs knob
|
* @dfs_dump_lprops: "dump lprops" debugfs knob
|
||||||
* dfs_dump_budg: "dump budgeting information" debugfs knob
|
* @dfs_dump_budg: "dump budgeting information" debugfs knob
|
||||||
* dfs_dump_tnc: "dump TNC" debugfs knob
|
* @dfs_dump_tnc: "dump TNC" debugfs knob
|
||||||
*/
|
*/
|
||||||
struct ubifs_debug_info {
|
struct ubifs_debug_info {
|
||||||
struct ubifs_zbranch old_zroot;
|
struct ubifs_zbranch old_zroot;
|
||||||
|
@ -76,7 +80,9 @@ struct ubifs_debug_info {
|
||||||
int new_ihead_offs;
|
int new_ihead_offs;
|
||||||
|
|
||||||
struct ubifs_lp_stats saved_lst;
|
struct ubifs_lp_stats saved_lst;
|
||||||
|
struct ubifs_budg_info saved_bi;
|
||||||
long long saved_free;
|
long long saved_free;
|
||||||
|
int saved_idx_gc_cnt;
|
||||||
|
|
||||||
char dfs_dir_name[100];
|
char dfs_dir_name[100];
|
||||||
struct dentry *dfs_dir;
|
struct dentry *dfs_dir;
|
||||||
|
@ -101,23 +107,7 @@ struct ubifs_debug_info {
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define dbg_dump_stack() do { \
|
#define dbg_dump_stack() dump_stack()
|
||||||
if (!dbg_failure_mode) \
|
|
||||||
dump_stack(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* Generic debugging messages */
|
|
||||||
#define dbg_msg(fmt, ...) do { \
|
|
||||||
spin_lock(&dbg_lock); \
|
|
||||||
printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", current->pid, \
|
|
||||||
__func__, ##__VA_ARGS__); \
|
|
||||||
spin_unlock(&dbg_lock); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define dbg_do_msg(typ, fmt, ...) do { \
|
|
||||||
if (ubifs_msg_flags & typ) \
|
|
||||||
dbg_msg(fmt, ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define dbg_err(fmt, ...) do { \
|
#define dbg_err(fmt, ...) do { \
|
||||||
spin_lock(&dbg_lock); \
|
spin_lock(&dbg_lock); \
|
||||||
|
@ -137,77 +127,40 @@ const char *dbg_key_str1(const struct ubifs_info *c,
|
||||||
#define DBGKEY(key) dbg_key_str0(c, (key))
|
#define DBGKEY(key) dbg_key_str0(c, (key))
|
||||||
#define DBGKEY1(key) dbg_key_str1(c, (key))
|
#define DBGKEY1(key) dbg_key_str1(c, (key))
|
||||||
|
|
||||||
|
#define ubifs_dbg_msg(type, fmt, ...) do { \
|
||||||
|
spin_lock(&dbg_lock); \
|
||||||
|
pr_debug("UBIFS DBG " type ": " fmt "\n", ##__VA_ARGS__); \
|
||||||
|
spin_unlock(&dbg_lock); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Just a debugging messages not related to any specific UBIFS subsystem */
|
||||||
|
#define dbg_msg(fmt, ...) ubifs_dbg_msg("msg", fmt, ##__VA_ARGS__)
|
||||||
/* General messages */
|
/* General messages */
|
||||||
#define dbg_gen(fmt, ...) dbg_do_msg(UBIFS_MSG_GEN, fmt, ##__VA_ARGS__)
|
#define dbg_gen(fmt, ...) ubifs_dbg_msg("gen", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional journal messages */
|
/* Additional journal messages */
|
||||||
#define dbg_jnl(fmt, ...) dbg_do_msg(UBIFS_MSG_JNL, fmt, ##__VA_ARGS__)
|
#define dbg_jnl(fmt, ...) ubifs_dbg_msg("jnl", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional TNC messages */
|
/* Additional TNC messages */
|
||||||
#define dbg_tnc(fmt, ...) dbg_do_msg(UBIFS_MSG_TNC, fmt, ##__VA_ARGS__)
|
#define dbg_tnc(fmt, ...) ubifs_dbg_msg("tnc", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional lprops messages */
|
/* Additional lprops messages */
|
||||||
#define dbg_lp(fmt, ...) dbg_do_msg(UBIFS_MSG_LP, fmt, ##__VA_ARGS__)
|
#define dbg_lp(fmt, ...) ubifs_dbg_msg("lp", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional LEB find messages */
|
/* Additional LEB find messages */
|
||||||
#define dbg_find(fmt, ...) dbg_do_msg(UBIFS_MSG_FIND, fmt, ##__VA_ARGS__)
|
#define dbg_find(fmt, ...) ubifs_dbg_msg("find", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional mount messages */
|
/* Additional mount messages */
|
||||||
#define dbg_mnt(fmt, ...) dbg_do_msg(UBIFS_MSG_MNT, fmt, ##__VA_ARGS__)
|
#define dbg_mnt(fmt, ...) ubifs_dbg_msg("mnt", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional I/O messages */
|
/* Additional I/O messages */
|
||||||
#define dbg_io(fmt, ...) dbg_do_msg(UBIFS_MSG_IO, fmt, ##__VA_ARGS__)
|
#define dbg_io(fmt, ...) ubifs_dbg_msg("io", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional commit messages */
|
/* Additional commit messages */
|
||||||
#define dbg_cmt(fmt, ...) dbg_do_msg(UBIFS_MSG_CMT, fmt, ##__VA_ARGS__)
|
#define dbg_cmt(fmt, ...) ubifs_dbg_msg("cmt", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional budgeting messages */
|
/* Additional budgeting messages */
|
||||||
#define dbg_budg(fmt, ...) dbg_do_msg(UBIFS_MSG_BUDG, fmt, ##__VA_ARGS__)
|
#define dbg_budg(fmt, ...) ubifs_dbg_msg("budg", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional log messages */
|
/* Additional log messages */
|
||||||
#define dbg_log(fmt, ...) dbg_do_msg(UBIFS_MSG_LOG, fmt, ##__VA_ARGS__)
|
#define dbg_log(fmt, ...) ubifs_dbg_msg("log", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional gc messages */
|
/* Additional gc messages */
|
||||||
#define dbg_gc(fmt, ...) dbg_do_msg(UBIFS_MSG_GC, fmt, ##__VA_ARGS__)
|
#define dbg_gc(fmt, ...) ubifs_dbg_msg("gc", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional scan messages */
|
/* Additional scan messages */
|
||||||
#define dbg_scan(fmt, ...) dbg_do_msg(UBIFS_MSG_SCAN, fmt, ##__VA_ARGS__)
|
#define dbg_scan(fmt, ...) ubifs_dbg_msg("scan", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* Additional recovery messages */
|
/* Additional recovery messages */
|
||||||
#define dbg_rcvry(fmt, ...) dbg_do_msg(UBIFS_MSG_RCVRY, fmt, ##__VA_ARGS__)
|
#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/*
|
|
||||||
* Debugging message type flags.
|
|
||||||
*
|
|
||||||
* UBIFS_MSG_GEN: general messages
|
|
||||||
* UBIFS_MSG_JNL: journal messages
|
|
||||||
* UBIFS_MSG_MNT: mount messages
|
|
||||||
* UBIFS_MSG_CMT: commit messages
|
|
||||||
* UBIFS_MSG_FIND: LEB find messages
|
|
||||||
* UBIFS_MSG_BUDG: budgeting messages
|
|
||||||
* UBIFS_MSG_GC: garbage collection messages
|
|
||||||
* UBIFS_MSG_TNC: TNC messages
|
|
||||||
* UBIFS_MSG_LP: lprops messages
|
|
||||||
* UBIFS_MSG_IO: I/O messages
|
|
||||||
* UBIFS_MSG_LOG: log messages
|
|
||||||
* UBIFS_MSG_SCAN: scan messages
|
|
||||||
* UBIFS_MSG_RCVRY: recovery messages
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
UBIFS_MSG_GEN = 0x1,
|
|
||||||
UBIFS_MSG_JNL = 0x2,
|
|
||||||
UBIFS_MSG_MNT = 0x4,
|
|
||||||
UBIFS_MSG_CMT = 0x8,
|
|
||||||
UBIFS_MSG_FIND = 0x10,
|
|
||||||
UBIFS_MSG_BUDG = 0x20,
|
|
||||||
UBIFS_MSG_GC = 0x40,
|
|
||||||
UBIFS_MSG_TNC = 0x80,
|
|
||||||
UBIFS_MSG_LP = 0x100,
|
|
||||||
UBIFS_MSG_IO = 0x200,
|
|
||||||
UBIFS_MSG_LOG = 0x400,
|
|
||||||
UBIFS_MSG_SCAN = 0x800,
|
|
||||||
UBIFS_MSG_RCVRY = 0x1000,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Debugging check flags.
|
* Debugging check flags.
|
||||||
|
@ -233,11 +186,9 @@ enum {
|
||||||
/*
|
/*
|
||||||
* Special testing flags.
|
* Special testing flags.
|
||||||
*
|
*
|
||||||
* UBIFS_TST_FORCE_IN_THE_GAPS: force the use of in-the-gaps method
|
|
||||||
* UBIFS_TST_RCVRY: failure mode for recovery testing
|
* UBIFS_TST_RCVRY: failure mode for recovery testing
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
UBIFS_TST_FORCE_IN_THE_GAPS = 0x2,
|
|
||||||
UBIFS_TST_RCVRY = 0x4,
|
UBIFS_TST_RCVRY = 0x4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -262,7 +213,7 @@ void dbg_dump_lpt_node(const struct ubifs_info *c, void *node, int lnum,
|
||||||
int offs);
|
int offs);
|
||||||
void dbg_dump_budget_req(const struct ubifs_budget_req *req);
|
void dbg_dump_budget_req(const struct ubifs_budget_req *req);
|
||||||
void dbg_dump_lstats(const struct ubifs_lp_stats *lst);
|
void dbg_dump_lstats(const struct ubifs_lp_stats *lst);
|
||||||
void dbg_dump_budg(struct ubifs_info *c);
|
void dbg_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi);
|
||||||
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp);
|
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp);
|
||||||
void dbg_dump_lprops(struct ubifs_info *c);
|
void dbg_dump_lprops(struct ubifs_info *c);
|
||||||
void dbg_dump_lpt_info(struct ubifs_info *c);
|
void dbg_dump_lpt_info(struct ubifs_info *c);
|
||||||
|
@ -304,18 +255,16 @@ 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);
|
int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);
|
||||||
|
|
||||||
/* Force the use of in-the-gaps method for testing */
|
/* Force the use of in-the-gaps method for testing */
|
||||||
|
static inline int dbg_force_in_the_gaps_enabled(void)
|
||||||
#define dbg_force_in_the_gaps_enabled \
|
{
|
||||||
(ubifs_tst_flags & UBIFS_TST_FORCE_IN_THE_GAPS)
|
return ubifs_chk_flags & UBIFS_CHK_GEN;
|
||||||
|
}
|
||||||
int dbg_force_in_the_gaps(void);
|
int dbg_force_in_the_gaps(void);
|
||||||
|
|
||||||
/* Failure mode for recovery testing */
|
/* Failure mode for recovery testing */
|
||||||
|
|
||||||
#define dbg_failure_mode (ubifs_tst_flags & UBIFS_TST_RCVRY)
|
#define dbg_failure_mode (ubifs_tst_flags & UBIFS_TST_RCVRY)
|
||||||
|
|
||||||
#ifndef UBIFS_DBG_PRESERVE_UBI
|
#ifndef UBIFS_DBG_PRESERVE_UBI
|
||||||
|
|
||||||
#define ubi_leb_read dbg_leb_read
|
#define ubi_leb_read dbg_leb_read
|
||||||
#define ubi_leb_write dbg_leb_write
|
#define ubi_leb_write dbg_leb_write
|
||||||
#define ubi_leb_change dbg_leb_change
|
#define ubi_leb_change dbg_leb_change
|
||||||
|
@ -323,7 +272,6 @@ int dbg_force_in_the_gaps(void);
|
||||||
#define ubi_leb_unmap dbg_leb_unmap
|
#define ubi_leb_unmap dbg_leb_unmap
|
||||||
#define ubi_is_mapped dbg_is_mapped
|
#define ubi_is_mapped dbg_is_mapped
|
||||||
#define ubi_leb_map dbg_leb_map
|
#define ubi_leb_map dbg_leb_map
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
||||||
|
@ -370,33 +318,33 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c);
|
||||||
__func__, __LINE__, current->pid); \
|
__func__, __LINE__, current->pid); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define dbg_err(fmt, ...) do { \
|
#define dbg_err(fmt, ...) do { \
|
||||||
if (0) \
|
if (0) \
|
||||||
ubifs_err(fmt, ##__VA_ARGS__); \
|
ubifs_err(fmt, ##__VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define dbg_msg(fmt, ...) do { \
|
#define ubifs_dbg_msg(fmt, ...) do { \
|
||||||
if (0) \
|
if (0) \
|
||||||
printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", \
|
pr_debug(fmt "\n", ##__VA_ARGS__); \
|
||||||
current->pid, __func__, ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define dbg_dump_stack()
|
#define dbg_dump_stack()
|
||||||
#define ubifs_assert_cmt_locked(c)
|
#define ubifs_assert_cmt_locked(c)
|
||||||
|
|
||||||
#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_msg(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_jnl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_gen(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_tnc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_jnl(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_lp(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_tnc(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_find(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_lp(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_mnt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_find(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_mnt(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_cmt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_io(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_budg(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_cmt(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_log(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_budg(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_gc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_log(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_scan(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_gc(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
#define dbg_rcvry(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
#define dbg_scan(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
|
#define dbg_rcvry(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define DBGKEY(key) ((char *)(key))
|
#define DBGKEY(key) ((char *)(key))
|
||||||
#define DBGKEY1(key) ((char *)(key))
|
#define DBGKEY1(key) ((char *)(key))
|
||||||
|
@ -420,7 +368,9 @@ static inline void
|
||||||
dbg_dump_budget_req(const struct ubifs_budget_req *req) { return; }
|
dbg_dump_budget_req(const struct ubifs_budget_req *req) { return; }
|
||||||
static inline void
|
static inline void
|
||||||
dbg_dump_lstats(const struct ubifs_lp_stats *lst) { return; }
|
dbg_dump_lstats(const struct ubifs_lp_stats *lst) { return; }
|
||||||
static inline void dbg_dump_budg(struct ubifs_info *c) { return; }
|
static inline void
|
||||||
|
dbg_dump_budg(struct ubifs_info *c,
|
||||||
|
const struct ubifs_budg_info *bi) { return; }
|
||||||
static inline void dbg_dump_lprop(const struct ubifs_info *c,
|
static inline void dbg_dump_lprop(const struct ubifs_info *c,
|
||||||
const struct ubifs_lprops *lp) { return; }
|
const struct ubifs_lprops *lp) { return; }
|
||||||
static inline void dbg_dump_lprops(struct ubifs_info *c) { return; }
|
static inline void dbg_dump_lprops(struct ubifs_info *c) { return; }
|
||||||
|
@ -482,8 +432,8 @@ dbg_check_nondata_nodes_order(struct ubifs_info *c,
|
||||||
struct list_head *head) { return 0; }
|
struct list_head *head) { return 0; }
|
||||||
|
|
||||||
static inline int dbg_force_in_the_gaps(void) { return 0; }
|
static inline int dbg_force_in_the_gaps(void) { return 0; }
|
||||||
#define dbg_force_in_the_gaps_enabled 0
|
#define dbg_force_in_the_gaps_enabled() 0
|
||||||
#define dbg_failure_mode 0
|
#define dbg_failure_mode 0
|
||||||
|
|
||||||
static inline int dbg_debugfs_init(void) { return 0; }
|
static inline int dbg_debugfs_init(void) { return 0; }
|
||||||
static inline void dbg_debugfs_exit(void) { return; }
|
static inline void dbg_debugfs_exit(void) { return; }
|
||||||
|
|
|
@ -603,7 +603,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
ubifs_release_budget(c, &req);
|
ubifs_release_budget(c, &req);
|
||||||
else {
|
else {
|
||||||
/* We've deleted something - clean the "no space" flags */
|
/* We've deleted something - clean the "no space" flags */
|
||||||
c->nospace = c->nospace_rp = 0;
|
c->bi.nospace = c->bi.nospace_rp = 0;
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -693,7 +693,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
ubifs_release_budget(c, &req);
|
ubifs_release_budget(c, &req);
|
||||||
else {
|
else {
|
||||||
/* We've deleted something - clean the "no space" flags */
|
/* We've deleted something - clean the "no space" flags */
|
||||||
c->nospace = c->nospace_rp = 0;
|
c->bi.nospace = c->bi.nospace_rp = 0;
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -212,7 +212,7 @@ static void release_new_page_budget(struct ubifs_info *c)
|
||||||
*/
|
*/
|
||||||
static void release_existing_page_budget(struct ubifs_info *c)
|
static void release_existing_page_budget(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
struct ubifs_budget_req req = { .dd_growth = c->page_budget};
|
struct ubifs_budget_req req = { .dd_growth = c->bi.page_budget};
|
||||||
|
|
||||||
ubifs_release_budget(c, &req);
|
ubifs_release_budget(c, &req);
|
||||||
}
|
}
|
||||||
|
@ -971,11 +971,11 @@ static int do_writepage(struct page *page, int len)
|
||||||
* the page locked, and it locks @ui_mutex. However, write-back does take inode
|
* the page locked, and it locks @ui_mutex. However, write-back does take inode
|
||||||
* @i_mutex, which means other VFS operations may be run on this inode at the
|
* @i_mutex, which means other VFS operations may be run on this inode at the
|
||||||
* same time. And the problematic one is truncation to smaller size, from where
|
* same time. And the problematic one is truncation to smaller size, from where
|
||||||
* we have to call 'truncate_setsize()', which first changes @inode->i_size, then
|
* we have to call 'truncate_setsize()', which first changes @inode->i_size,
|
||||||
* drops the truncated pages. And while dropping the pages, it takes the page
|
* then drops the truncated pages. And while dropping the pages, it takes the
|
||||||
* lock. This means that 'do_truncation()' cannot call 'truncate_setsize()' with
|
* page lock. This means that 'do_truncation()' cannot call 'truncate_setsize()'
|
||||||
* @ui_mutex locked, because it would deadlock with 'ubifs_writepage()'. This
|
* with @ui_mutex locked, because it would deadlock with 'ubifs_writepage()'.
|
||||||
* means that @inode->i_size is changed while @ui_mutex is unlocked.
|
* This means that @inode->i_size is changed while @ui_mutex is unlocked.
|
||||||
*
|
*
|
||||||
* XXX(truncate): with the new truncate sequence this is not true anymore,
|
* XXX(truncate): with the new truncate sequence this is not true anymore,
|
||||||
* and the calls to truncate_setsize can be move around freely. They should
|
* and the calls to truncate_setsize can be move around freely. They should
|
||||||
|
@ -1189,7 +1189,7 @@ out_budg:
|
||||||
if (budgeted)
|
if (budgeted)
|
||||||
ubifs_release_budget(c, &req);
|
ubifs_release_budget(c, &req);
|
||||||
else {
|
else {
|
||||||
c->nospace = c->nospace_rp = 0;
|
c->bi.nospace = c->bi.nospace_rp = 0;
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -1312,7 +1312,11 @@ int ubifs_fsync(struct file *file, int datasync)
|
||||||
|
|
||||||
dbg_gen("syncing inode %lu", inode->i_ino);
|
dbg_gen("syncing inode %lu", inode->i_ino);
|
||||||
|
|
||||||
if (inode->i_sb->s_flags & MS_RDONLY)
|
if (c->ro_mount)
|
||||||
|
/*
|
||||||
|
* For some really strange reasons VFS does not filter out
|
||||||
|
* 'fsync()' for R/O mounted file-systems as per 2.6.39.
|
||||||
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1432,10 +1436,11 @@ static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* mmap()d file has taken write protection fault and is being made
|
* mmap()d file has taken write protection fault and is being made writable.
|
||||||
* writable. UBIFS must ensure page is budgeted for.
|
* UBIFS must ensure page is budgeted for.
|
||||||
*/
|
*/
|
||||||
static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma,
|
||||||
|
struct vm_fault *vmf)
|
||||||
{
|
{
|
||||||
struct page *page = vmf->page;
|
struct page *page = vmf->page;
|
||||||
struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
|
struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
|
||||||
|
@ -1536,7 +1541,6 @@ static int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* 'generic_file_mmap()' takes care of NOMMU case */
|
|
||||||
err = generic_file_mmap(file, vma);
|
err = generic_file_mmap(file, vma);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -252,8 +252,8 @@ int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp,
|
||||||
* But if the index takes fewer LEBs than it is reserved for it,
|
* But if the index takes fewer LEBs than it is reserved for it,
|
||||||
* this function must avoid picking those reserved LEBs.
|
* this function must avoid picking those reserved LEBs.
|
||||||
*/
|
*/
|
||||||
if (c->min_idx_lebs >= c->lst.idx_lebs) {
|
if (c->bi.min_idx_lebs >= c->lst.idx_lebs) {
|
||||||
rsvd_idx_lebs = c->min_idx_lebs - c->lst.idx_lebs;
|
rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
|
||||||
exclude_index = 1;
|
exclude_index = 1;
|
||||||
}
|
}
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
|
@ -276,7 +276,7 @@ int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp,
|
||||||
pick_free = 0;
|
pick_free = 0;
|
||||||
} else {
|
} else {
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
exclude_index = (c->min_idx_lebs >= c->lst.idx_lebs);
|
exclude_index = (c->bi.min_idx_lebs >= c->lst.idx_lebs);
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,8 +501,8 @@ int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *offs,
|
||||||
|
|
||||||
/* Check if there are enough empty LEBs for commit */
|
/* Check if there are enough empty LEBs for commit */
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
if (c->min_idx_lebs > c->lst.idx_lebs)
|
if (c->bi.min_idx_lebs > c->lst.idx_lebs)
|
||||||
rsvd_idx_lebs = c->min_idx_lebs - c->lst.idx_lebs;
|
rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
|
||||||
else
|
else
|
||||||
rsvd_idx_lebs = 0;
|
rsvd_idx_lebs = 0;
|
||||||
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
|
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
|
||||||
|
|
|
@ -100,6 +100,10 @@ static int switch_gc_head(struct ubifs_info *c)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
err = ubifs_wbuf_sync_nolock(wbuf);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = ubifs_add_bud_to_log(c, GCHD, gc_lnum, 0);
|
err = ubifs_add_bud_to_log(c, GCHD, gc_lnum, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
@ -118,7 +122,7 @@ static int switch_gc_head(struct ubifs_info *c)
|
||||||
* This function compares data nodes @a and @b. Returns %1 if @a has greater
|
* This function compares data nodes @a and @b. Returns %1 if @a has greater
|
||||||
* inode or block number, and %-1 otherwise.
|
* inode or block number, and %-1 otherwise.
|
||||||
*/
|
*/
|
||||||
int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b)
|
static int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b)
|
||||||
{
|
{
|
||||||
ino_t inuma, inumb;
|
ino_t inuma, inumb;
|
||||||
struct ubifs_info *c = priv;
|
struct ubifs_info *c = priv;
|
||||||
|
@ -161,7 +165,8 @@ int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b)
|
||||||
* first and sorted by length in descending order. Directory entry nodes go
|
* first and sorted by length in descending order. Directory entry nodes go
|
||||||
* after inode nodes and are sorted in ascending hash valuer order.
|
* after inode nodes and are sorted in ascending hash valuer order.
|
||||||
*/
|
*/
|
||||||
int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b)
|
static int nondata_nodes_cmp(void *priv, struct list_head *a,
|
||||||
|
struct list_head *b)
|
||||||
{
|
{
|
||||||
ino_t inuma, inumb;
|
ino_t inuma, inumb;
|
||||||
struct ubifs_info *c = priv;
|
struct ubifs_info *c = priv;
|
||||||
|
@ -473,6 +478,37 @@ int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp)
|
||||||
ubifs_assert(c->gc_lnum != lnum);
|
ubifs_assert(c->gc_lnum != lnum);
|
||||||
ubifs_assert(wbuf->lnum != lnum);
|
ubifs_assert(wbuf->lnum != lnum);
|
||||||
|
|
||||||
|
if (lp->free + lp->dirty == c->leb_size) {
|
||||||
|
/* Special case - a free LEB */
|
||||||
|
dbg_gc("LEB %d is free, return it", lp->lnum);
|
||||||
|
ubifs_assert(!(lp->flags & LPROPS_INDEX));
|
||||||
|
|
||||||
|
if (lp->free != c->leb_size) {
|
||||||
|
/*
|
||||||
|
* Write buffers must be sync'd before unmapping
|
||||||
|
* freeable LEBs, because one of them may contain data
|
||||||
|
* which obsoletes something in 'lp->pnum'.
|
||||||
|
*/
|
||||||
|
err = gc_sync_wbufs(c);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = ubifs_change_one_lp(c, lp->lnum, c->leb_size,
|
||||||
|
0, 0, 0, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = ubifs_leb_unmap(c, lp->lnum);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (c->gc_lnum == -1) {
|
||||||
|
c->gc_lnum = lnum;
|
||||||
|
return LEB_RETAINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LEB_FREED;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We scan the entire LEB even though we only really need to scan up to
|
* We scan the entire LEB even though we only really need to scan up to
|
||||||
* (c->leb_size - lp->free).
|
* (c->leb_size - lp->free).
|
||||||
|
@ -682,37 +718,6 @@ int ubifs_garbage_collect(struct ubifs_info *c, int anyway)
|
||||||
"(min. space %d)", lp.lnum, lp.free, lp.dirty,
|
"(min. space %d)", lp.lnum, lp.free, lp.dirty,
|
||||||
lp.free + lp.dirty, min_space);
|
lp.free + lp.dirty, min_space);
|
||||||
|
|
||||||
if (lp.free + lp.dirty == c->leb_size) {
|
|
||||||
/* An empty LEB was returned */
|
|
||||||
dbg_gc("LEB %d is free, return it", lp.lnum);
|
|
||||||
/*
|
|
||||||
* ubifs_find_dirty_leb() doesn't return freeable index
|
|
||||||
* LEBs.
|
|
||||||
*/
|
|
||||||
ubifs_assert(!(lp.flags & LPROPS_INDEX));
|
|
||||||
if (lp.free != c->leb_size) {
|
|
||||||
/*
|
|
||||||
* Write buffers must be sync'd before
|
|
||||||
* unmapping freeable LEBs, because one of them
|
|
||||||
* may contain data which obsoletes something
|
|
||||||
* in 'lp.pnum'.
|
|
||||||
*/
|
|
||||||
ret = gc_sync_wbufs(c);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
ret = ubifs_change_one_lp(c, lp.lnum,
|
|
||||||
c->leb_size, 0, 0, 0,
|
|
||||||
0);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
ret = ubifs_leb_unmap(c, lp.lnum);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
ret = lp.lnum;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
space_before = c->leb_size - wbuf->offs - wbuf->used;
|
space_before = c->leb_size - wbuf->offs - wbuf->used;
|
||||||
if (wbuf->lnum == -1)
|
if (wbuf->lnum == -1)
|
||||||
space_before = 0;
|
space_before = 0;
|
||||||
|
|
|
@ -393,7 +393,7 @@ int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
|
||||||
ubifs_assert(wbuf->size % c->min_io_size == 0);
|
ubifs_assert(wbuf->size % c->min_io_size == 0);
|
||||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||||
ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size ));
|
ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size));
|
||||||
|
|
||||||
if (c->ro_error)
|
if (c->ro_error)
|
||||||
return -EROFS;
|
return -EROFS;
|
||||||
|
@ -452,8 +452,8 @@ int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
|
||||||
* @dtype: data type
|
* @dtype: data type
|
||||||
*
|
*
|
||||||
* This function targets the write-buffer to logical eraseblock @lnum:@offs.
|
* This function targets the write-buffer to logical eraseblock @lnum:@offs.
|
||||||
* The write-buffer is synchronized if it is not empty. Returns zero in case of
|
* The write-buffer has to be empty. Returns zero in case of success and a
|
||||||
* success and a negative error code in case of failure.
|
* negative error code in case of failure.
|
||||||
*/
|
*/
|
||||||
int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs,
|
int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs,
|
||||||
int dtype)
|
int dtype)
|
||||||
|
@ -465,13 +465,7 @@ int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs,
|
||||||
ubifs_assert(offs >= 0 && offs <= c->leb_size);
|
ubifs_assert(offs >= 0 && offs <= c->leb_size);
|
||||||
ubifs_assert(offs % c->min_io_size == 0 && !(offs & 7));
|
ubifs_assert(offs % c->min_io_size == 0 && !(offs & 7));
|
||||||
ubifs_assert(lnum != wbuf->lnum);
|
ubifs_assert(lnum != wbuf->lnum);
|
||||||
|
ubifs_assert(wbuf->used == 0);
|
||||||
if (wbuf->used > 0) {
|
|
||||||
int err = ubifs_wbuf_sync_nolock(wbuf);
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&wbuf->lock);
|
spin_lock(&wbuf->lock);
|
||||||
wbuf->lnum = lnum;
|
wbuf->lnum = lnum;
|
||||||
|
@ -573,7 +567,7 @@ out_timers:
|
||||||
int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
{
|
{
|
||||||
struct ubifs_info *c = wbuf->c;
|
struct ubifs_info *c = wbuf->c;
|
||||||
int err, written, n, aligned_len = ALIGN(len, 8), offs;
|
int err, written, n, aligned_len = ALIGN(len, 8);
|
||||||
|
|
||||||
dbg_io("%d bytes (%s) to jhead %s wbuf at LEB %d:%d", len,
|
dbg_io("%d bytes (%s) to jhead %s wbuf at LEB %d:%d", len,
|
||||||
dbg_ntype(((struct ubifs_ch *)buf)->node_type),
|
dbg_ntype(((struct ubifs_ch *)buf)->node_type),
|
||||||
|
@ -588,7 +582,7 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
ubifs_assert(mutex_is_locked(&wbuf->io_mutex));
|
ubifs_assert(mutex_is_locked(&wbuf->io_mutex));
|
||||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||||
ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size ));
|
ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size));
|
||||||
|
|
||||||
if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) {
|
if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) {
|
||||||
err = -ENOSPC;
|
err = -ENOSPC;
|
||||||
|
@ -636,7 +630,6 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
offs = wbuf->offs;
|
|
||||||
written = 0;
|
written = 0;
|
||||||
|
|
||||||
if (wbuf->used) {
|
if (wbuf->used) {
|
||||||
|
@ -653,7 +646,7 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
offs += wbuf->size;
|
wbuf->offs += wbuf->size;
|
||||||
len -= wbuf->avail;
|
len -= wbuf->avail;
|
||||||
aligned_len -= wbuf->avail;
|
aligned_len -= wbuf->avail;
|
||||||
written += wbuf->avail;
|
written += wbuf->avail;
|
||||||
|
@ -672,7 +665,7 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
offs += wbuf->size;
|
wbuf->offs += wbuf->size;
|
||||||
len -= wbuf->size;
|
len -= wbuf->size;
|
||||||
aligned_len -= wbuf->size;
|
aligned_len -= wbuf->size;
|
||||||
written += wbuf->size;
|
written += wbuf->size;
|
||||||
|
@ -687,12 +680,13 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
n = aligned_len >> c->max_write_shift;
|
n = aligned_len >> c->max_write_shift;
|
||||||
if (n) {
|
if (n) {
|
||||||
n <<= c->max_write_shift;
|
n <<= c->max_write_shift;
|
||||||
dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum, offs);
|
dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum,
|
||||||
err = ubi_leb_write(c->ubi, wbuf->lnum, buf + written, offs, n,
|
wbuf->offs);
|
||||||
wbuf->dtype);
|
err = ubi_leb_write(c->ubi, wbuf->lnum, buf + written,
|
||||||
|
wbuf->offs, n, wbuf->dtype);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
offs += n;
|
wbuf->offs += n;
|
||||||
aligned_len -= n;
|
aligned_len -= n;
|
||||||
len -= n;
|
len -= n;
|
||||||
written += n;
|
written += n;
|
||||||
|
@ -707,7 +701,6 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||||
*/
|
*/
|
||||||
memcpy(wbuf->buf, buf + written, len);
|
memcpy(wbuf->buf, buf + written, len);
|
||||||
|
|
||||||
wbuf->offs = offs;
|
|
||||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||||
wbuf->size = c->max_write_size;
|
wbuf->size = c->max_write_size;
|
||||||
else
|
else
|
||||||
|
|
|
@ -141,14 +141,8 @@ again:
|
||||||
* LEB with some empty space.
|
* LEB with some empty space.
|
||||||
*/
|
*/
|
||||||
lnum = ubifs_find_free_space(c, len, &offs, squeeze);
|
lnum = ubifs_find_free_space(c, len, &offs, squeeze);
|
||||||
if (lnum >= 0) {
|
if (lnum >= 0)
|
||||||
/* Found an LEB, add it to the journal head */
|
|
||||||
err = ubifs_add_bud_to_log(c, jhead, lnum, offs);
|
|
||||||
if (err)
|
|
||||||
goto out_return;
|
|
||||||
/* A new bud was successfully allocated and added to the log */
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
err = lnum;
|
err = lnum;
|
||||||
if (err != -ENOSPC)
|
if (err != -ENOSPC)
|
||||||
|
@ -203,12 +197,23 @@ again:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ubifs_add_bud_to_log(c, jhead, lnum, 0);
|
|
||||||
if (err)
|
|
||||||
goto out_return;
|
|
||||||
offs = 0;
|
offs = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
/*
|
||||||
|
* Make sure we synchronize the write-buffer before we add the new bud
|
||||||
|
* to the log. Otherwise we may have a power cut after the log
|
||||||
|
* reference node for the last bud (@lnum) is written but before the
|
||||||
|
* write-buffer data are written to the next-to-last bud
|
||||||
|
* (@wbuf->lnum). And the effect would be that the recovery would see
|
||||||
|
* that there is corruption in the next-to-last bud.
|
||||||
|
*/
|
||||||
|
err = ubifs_wbuf_sync_nolock(wbuf);
|
||||||
|
if (err)
|
||||||
|
goto out_return;
|
||||||
|
err = ubifs_add_bud_to_log(c, jhead, lnum, offs);
|
||||||
|
if (err)
|
||||||
|
goto out_return;
|
||||||
err = ubifs_wbuf_seek_nolock(wbuf, lnum, offs, wbuf->dtype);
|
err = ubifs_wbuf_seek_nolock(wbuf, lnum, offs, wbuf->dtype);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
@ -380,10 +385,8 @@ out:
|
||||||
if (err == -ENOSPC) {
|
if (err == -ENOSPC) {
|
||||||
/* This are some budgeting problems, print useful information */
|
/* This are some budgeting problems, print useful information */
|
||||||
down_write(&c->commit_sem);
|
down_write(&c->commit_sem);
|
||||||
spin_lock(&c->space_lock);
|
|
||||||
dbg_dump_stack();
|
dbg_dump_stack();
|
||||||
dbg_dump_budg(c);
|
dbg_dump_budg(c, &c->bi);
|
||||||
spin_unlock(&c->space_lock);
|
|
||||||
dbg_dump_lprops(c);
|
dbg_dump_lprops(c);
|
||||||
cmt_retries = dbg_check_lprops(c);
|
cmt_retries = dbg_check_lprops(c);
|
||||||
up_write(&c->commit_sem);
|
up_write(&c->commit_sem);
|
||||||
|
|
|
@ -99,20 +99,6 @@ struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* next_log_lnum - switch to the next log LEB.
|
|
||||||
* @c: UBIFS file-system description object
|
|
||||||
* @lnum: current log LEB
|
|
||||||
*/
|
|
||||||
static inline int next_log_lnum(const struct ubifs_info *c, int lnum)
|
|
||||||
{
|
|
||||||
lnum += 1;
|
|
||||||
if (lnum > c->log_last)
|
|
||||||
lnum = UBIFS_LOG_LNUM;
|
|
||||||
|
|
||||||
return lnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* empty_log_bytes - calculate amount of empty space in the log.
|
* empty_log_bytes - calculate amount of empty space in the log.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
|
@ -257,7 +243,7 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
|
||||||
ref->jhead = cpu_to_le32(jhead);
|
ref->jhead = cpu_to_le32(jhead);
|
||||||
|
|
||||||
if (c->lhead_offs > c->leb_size - c->ref_node_alsz) {
|
if (c->lhead_offs > c->leb_size - c->ref_node_alsz) {
|
||||||
c->lhead_lnum = next_log_lnum(c, c->lhead_lnum);
|
c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||||
c->lhead_offs = 0;
|
c->lhead_offs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +411,7 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
|
||||||
|
|
||||||
/* Switch to the next log LEB */
|
/* Switch to the next log LEB */
|
||||||
if (c->lhead_offs) {
|
if (c->lhead_offs) {
|
||||||
c->lhead_lnum = next_log_lnum(c, c->lhead_lnum);
|
c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||||
c->lhead_offs = 0;
|
c->lhead_offs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +432,7 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
|
||||||
|
|
||||||
c->lhead_offs += len;
|
c->lhead_offs += len;
|
||||||
if (c->lhead_offs == c->leb_size) {
|
if (c->lhead_offs == c->leb_size) {
|
||||||
c->lhead_lnum = next_log_lnum(c, c->lhead_lnum);
|
c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||||
c->lhead_offs = 0;
|
c->lhead_offs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,7 +519,7 @@ int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
|
||||||
}
|
}
|
||||||
mutex_lock(&c->log_mutex);
|
mutex_lock(&c->log_mutex);
|
||||||
for (lnum = old_ltail_lnum; lnum != c->ltail_lnum;
|
for (lnum = old_ltail_lnum; lnum != c->ltail_lnum;
|
||||||
lnum = next_log_lnum(c, lnum)) {
|
lnum = ubifs_next_log_lnum(c, lnum)) {
|
||||||
dbg_log("unmap log LEB %d", lnum);
|
dbg_log("unmap log LEB %d", lnum);
|
||||||
err = ubifs_leb_unmap(c, lnum);
|
err = ubifs_leb_unmap(c, lnum);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -642,7 +628,7 @@ static int add_node(struct ubifs_info *c, void *buf, int *lnum, int *offs,
|
||||||
err = ubifs_leb_change(c, *lnum, buf, sz, UBI_SHORTTERM);
|
err = ubifs_leb_change(c, *lnum, buf, sz, UBI_SHORTTERM);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
*lnum = next_log_lnum(c, *lnum);
|
*lnum = ubifs_next_log_lnum(c, *lnum);
|
||||||
*offs = 0;
|
*offs = 0;
|
||||||
}
|
}
|
||||||
memcpy(buf + *offs, node, len);
|
memcpy(buf + *offs, node, len);
|
||||||
|
@ -712,7 +698,7 @@ int ubifs_consolidate_log(struct ubifs_info *c)
|
||||||
ubifs_scan_destroy(sleb);
|
ubifs_scan_destroy(sleb);
|
||||||
if (lnum == c->lhead_lnum)
|
if (lnum == c->lhead_lnum)
|
||||||
break;
|
break;
|
||||||
lnum = next_log_lnum(c, lnum);
|
lnum = ubifs_next_log_lnum(c, lnum);
|
||||||
}
|
}
|
||||||
if (offs) {
|
if (offs) {
|
||||||
int sz = ALIGN(offs, c->min_io_size);
|
int sz = ALIGN(offs, c->min_io_size);
|
||||||
|
@ -732,7 +718,7 @@ int ubifs_consolidate_log(struct ubifs_info *c)
|
||||||
/* Unmap remaining LEBs */
|
/* Unmap remaining LEBs */
|
||||||
lnum = write_lnum;
|
lnum = write_lnum;
|
||||||
do {
|
do {
|
||||||
lnum = next_log_lnum(c, lnum);
|
lnum = ubifs_next_log_lnum(c, lnum);
|
||||||
err = ubifs_leb_unmap(c, lnum);
|
err = ubifs_leb_unmap(c, lnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -1006,22 +1006,12 @@ out:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* struct scan_check_data - data provided to scan callback function.
|
|
||||||
* @lst: LEB properties statistics
|
|
||||||
* @err: error code
|
|
||||||
*/
|
|
||||||
struct scan_check_data {
|
|
||||||
struct ubifs_lp_stats lst;
|
|
||||||
int err;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* scan_check_cb - scan callback.
|
* scan_check_cb - scan callback.
|
||||||
* @c: the UBIFS file-system description object
|
* @c: the UBIFS file-system description object
|
||||||
* @lp: LEB properties to scan
|
* @lp: LEB properties to scan
|
||||||
* @in_tree: whether the LEB properties are in main memory
|
* @in_tree: whether the LEB properties are in main memory
|
||||||
* @data: information passed to and from the caller of the scan
|
* @lst: lprops statistics to update
|
||||||
*
|
*
|
||||||
* This function returns a code that indicates whether the scan should continue
|
* This function returns a code that indicates whether the scan should continue
|
||||||
* (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
|
* (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
|
||||||
|
@ -1030,11 +1020,10 @@ struct scan_check_data {
|
||||||
*/
|
*/
|
||||||
static int scan_check_cb(struct ubifs_info *c,
|
static int scan_check_cb(struct ubifs_info *c,
|
||||||
const struct ubifs_lprops *lp, int in_tree,
|
const struct ubifs_lprops *lp, int in_tree,
|
||||||
struct scan_check_data *data)
|
struct ubifs_lp_stats *lst)
|
||||||
{
|
{
|
||||||
struct ubifs_scan_leb *sleb;
|
struct ubifs_scan_leb *sleb;
|
||||||
struct ubifs_scan_node *snod;
|
struct ubifs_scan_node *snod;
|
||||||
struct ubifs_lp_stats *lst = &data->lst;
|
|
||||||
int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
|
int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
|
||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
|
|
||||||
|
@ -1044,7 +1033,7 @@ static int scan_check_cb(struct ubifs_info *c,
|
||||||
if (cat != (lp->flags & LPROPS_CAT_MASK)) {
|
if (cat != (lp->flags & LPROPS_CAT_MASK)) {
|
||||||
ubifs_err("bad LEB category %d expected %d",
|
ubifs_err("bad LEB category %d expected %d",
|
||||||
(lp->flags & LPROPS_CAT_MASK), cat);
|
(lp->flags & LPROPS_CAT_MASK), cat);
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1078,7 +1067,7 @@ static int scan_check_cb(struct ubifs_info *c,
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
ubifs_err("bad LPT list (category %d)", cat);
|
ubifs_err("bad LPT list (category %d)", cat);
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1090,45 +1079,40 @@ static int scan_check_cb(struct ubifs_info *c,
|
||||||
if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
|
if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
|
||||||
lp != heap->arr[lp->hpos]) {
|
lp != heap->arr[lp->hpos]) {
|
||||||
ubifs_err("bad LPT heap (category %d)", cat);
|
ubifs_err("bad LPT heap (category %d)", cat);
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
|
buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
|
||||||
if (!buf) {
|
if (!buf)
|
||||||
ubifs_err("cannot allocate memory to scan LEB %d", lnum);
|
return -ENOMEM;
|
||||||
goto out;
|
|
||||||
|
/*
|
||||||
|
* After an unclean unmount, empty and freeable LEBs
|
||||||
|
* may contain garbage - do not scan them.
|
||||||
|
*/
|
||||||
|
if (lp->free == c->leb_size) {
|
||||||
|
lst->empty_lebs += 1;
|
||||||
|
lst->total_free += c->leb_size;
|
||||||
|
lst->total_dark += ubifs_calc_dark(c, c->leb_size);
|
||||||
|
return LPT_SCAN_CONTINUE;
|
||||||
|
}
|
||||||
|
if (lp->free + lp->dirty == c->leb_size &&
|
||||||
|
!(lp->flags & LPROPS_INDEX)) {
|
||||||
|
lst->total_free += lp->free;
|
||||||
|
lst->total_dirty += lp->dirty;
|
||||||
|
lst->total_dark += ubifs_calc_dark(c, c->leb_size);
|
||||||
|
return LPT_SCAN_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleb = ubifs_scan(c, lnum, 0, buf, 0);
|
sleb = ubifs_scan(c, lnum, 0, buf, 0);
|
||||||
if (IS_ERR(sleb)) {
|
if (IS_ERR(sleb)) {
|
||||||
/*
|
ret = PTR_ERR(sleb);
|
||||||
* After an unclean unmount, empty and freeable LEBs
|
if (ret == -EUCLEAN) {
|
||||||
* may contain garbage.
|
dbg_dump_lprops(c);
|
||||||
*/
|
dbg_dump_budg(c, &c->bi);
|
||||||
if (lp->free == c->leb_size) {
|
|
||||||
ubifs_err("scan errors were in empty LEB "
|
|
||||||
"- continuing checking");
|
|
||||||
lst->empty_lebs += 1;
|
|
||||||
lst->total_free += c->leb_size;
|
|
||||||
lst->total_dark += ubifs_calc_dark(c, c->leb_size);
|
|
||||||
ret = LPT_SCAN_CONTINUE;
|
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
goto out;
|
||||||
if (lp->free + lp->dirty == c->leb_size &&
|
|
||||||
!(lp->flags & LPROPS_INDEX)) {
|
|
||||||
ubifs_err("scan errors were in freeable LEB "
|
|
||||||
"- continuing checking");
|
|
||||||
lst->total_free += lp->free;
|
|
||||||
lst->total_dirty += lp->dirty;
|
|
||||||
lst->total_dark += ubifs_calc_dark(c, c->leb_size);
|
|
||||||
ret = LPT_SCAN_CONTINUE;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
data->err = PTR_ERR(sleb);
|
|
||||||
ret = LPT_SCAN_STOP;
|
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is_idx = -1;
|
is_idx = -1;
|
||||||
|
@ -1246,10 +1230,8 @@ static int scan_check_cb(struct ubifs_info *c,
|
||||||
}
|
}
|
||||||
|
|
||||||
ubifs_scan_destroy(sleb);
|
ubifs_scan_destroy(sleb);
|
||||||
ret = LPT_SCAN_CONTINUE;
|
|
||||||
exit:
|
|
||||||
vfree(buf);
|
vfree(buf);
|
||||||
return ret;
|
return LPT_SCAN_CONTINUE;
|
||||||
|
|
||||||
out_print:
|
out_print:
|
||||||
ubifs_err("bad accounting of LEB %d: free %d, dirty %d flags %#x, "
|
ubifs_err("bad accounting of LEB %d: free %d, dirty %d flags %#x, "
|
||||||
|
@ -1258,10 +1240,10 @@ out_print:
|
||||||
dbg_dump_leb(c, lnum);
|
dbg_dump_leb(c, lnum);
|
||||||
out_destroy:
|
out_destroy:
|
||||||
ubifs_scan_destroy(sleb);
|
ubifs_scan_destroy(sleb);
|
||||||
|
ret = -EINVAL;
|
||||||
out:
|
out:
|
||||||
vfree(buf);
|
vfree(buf);
|
||||||
data->err = -EINVAL;
|
return ret;
|
||||||
return LPT_SCAN_STOP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1278,8 +1260,7 @@ out:
|
||||||
int dbg_check_lprops(struct ubifs_info *c)
|
int dbg_check_lprops(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i, err;
|
||||||
struct scan_check_data data;
|
struct ubifs_lp_stats lst;
|
||||||
struct ubifs_lp_stats *lst = &data.lst;
|
|
||||||
|
|
||||||
if (!(ubifs_chk_flags & UBIFS_CHK_LPROPS))
|
if (!(ubifs_chk_flags & UBIFS_CHK_LPROPS))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1294,29 +1275,23 @@ int dbg_check_lprops(struct ubifs_info *c)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(lst, 0, sizeof(struct ubifs_lp_stats));
|
memset(&lst, 0, sizeof(struct ubifs_lp_stats));
|
||||||
|
|
||||||
data.err = 0;
|
|
||||||
err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
|
err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
|
||||||
(ubifs_lpt_scan_callback)scan_check_cb,
|
(ubifs_lpt_scan_callback)scan_check_cb,
|
||||||
&data);
|
&lst);
|
||||||
if (err && err != -ENOSPC)
|
if (err && err != -ENOSPC)
|
||||||
goto out;
|
goto out;
|
||||||
if (data.err) {
|
|
||||||
err = data.err;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lst->empty_lebs != c->lst.empty_lebs ||
|
if (lst.empty_lebs != c->lst.empty_lebs ||
|
||||||
lst->idx_lebs != c->lst.idx_lebs ||
|
lst.idx_lebs != c->lst.idx_lebs ||
|
||||||
lst->total_free != c->lst.total_free ||
|
lst.total_free != c->lst.total_free ||
|
||||||
lst->total_dirty != c->lst.total_dirty ||
|
lst.total_dirty != c->lst.total_dirty ||
|
||||||
lst->total_used != c->lst.total_used) {
|
lst.total_used != c->lst.total_used) {
|
||||||
ubifs_err("bad overall accounting");
|
ubifs_err("bad overall accounting");
|
||||||
ubifs_err("calculated: empty_lebs %d, idx_lebs %d, "
|
ubifs_err("calculated: empty_lebs %d, idx_lebs %d, "
|
||||||
"total_free %lld, total_dirty %lld, total_used %lld",
|
"total_free %lld, total_dirty %lld, total_used %lld",
|
||||||
lst->empty_lebs, lst->idx_lebs, lst->total_free,
|
lst.empty_lebs, lst.idx_lebs, lst.total_free,
|
||||||
lst->total_dirty, lst->total_used);
|
lst.total_dirty, lst.total_used);
|
||||||
ubifs_err("read from lprops: empty_lebs %d, idx_lebs %d, "
|
ubifs_err("read from lprops: empty_lebs %d, idx_lebs %d, "
|
||||||
"total_free %lld, total_dirty %lld, total_used %lld",
|
"total_free %lld, total_dirty %lld, total_used %lld",
|
||||||
c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
|
c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
|
||||||
|
@ -1325,11 +1300,11 @@ int dbg_check_lprops(struct ubifs_info *c)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lst->total_dead != c->lst.total_dead ||
|
if (lst.total_dead != c->lst.total_dead ||
|
||||||
lst->total_dark != c->lst.total_dark) {
|
lst.total_dark != c->lst.total_dark) {
|
||||||
ubifs_err("bad dead/dark space accounting");
|
ubifs_err("bad dead/dark space accounting");
|
||||||
ubifs_err("calculated: total_dead %lld, total_dark %lld",
|
ubifs_err("calculated: total_dead %lld, total_dark %lld",
|
||||||
lst->total_dead, lst->total_dark);
|
lst.total_dead, lst.total_dark);
|
||||||
ubifs_err("read from lprops: total_dead %lld, total_dark %lld",
|
ubifs_err("read from lprops: total_dead %lld, total_dark %lld",
|
||||||
c->lst.total_dead, c->lst.total_dark);
|
c->lst.total_dead, c->lst.total_dark);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
|
|
@ -29,6 +29,12 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include "ubifs.h"
|
#include "ubifs.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_UBIFS_FS_DEBUG
|
||||||
|
static int dbg_populate_lsave(struct ubifs_info *c);
|
||||||
|
#else
|
||||||
|
#define dbg_populate_lsave(c) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* first_dirty_cnode - find first dirty cnode.
|
* first_dirty_cnode - find first dirty cnode.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
|
@ -586,7 +592,7 @@ static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c,
|
||||||
if (nnode->nbranch[iip].lnum)
|
if (nnode->nbranch[iip].lnum)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (iip >= UBIFS_LPT_FANOUT);
|
} while (iip >= UBIFS_LPT_FANOUT);
|
||||||
|
|
||||||
/* Go right */
|
/* Go right */
|
||||||
nnode = ubifs_get_nnode(c, nnode, iip);
|
nnode = ubifs_get_nnode(c, nnode, iip);
|
||||||
|
@ -815,6 +821,10 @@ static void populate_lsave(struct ubifs_info *c)
|
||||||
c->lpt_drty_flgs |= LSAVE_DIRTY;
|
c->lpt_drty_flgs |= LSAVE_DIRTY;
|
||||||
ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz);
|
ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dbg_populate_lsave(c))
|
||||||
|
return;
|
||||||
|
|
||||||
list_for_each_entry(lprops, &c->empty_list, list) {
|
list_for_each_entry(lprops, &c->empty_list, list) {
|
||||||
c->lsave[cnt++] = lprops->lnum;
|
c->lsave[cnt++] = lprops->lnum;
|
||||||
if (cnt >= c->lsave_cnt)
|
if (cnt >= c->lsave_cnt)
|
||||||
|
@ -1994,4 +2004,47 @@ void dbg_dump_lpt_lebs(const struct ubifs_info *c)
|
||||||
current->pid);
|
current->pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dbg_populate_lsave - debugging version of 'populate_lsave()'
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* This is a debugging version for 'populate_lsave()' which populates lsave
|
||||||
|
* with random LEBs instead of useful LEBs, which is good for test coverage.
|
||||||
|
* Returns zero if lsave has not been populated (this debugging feature is
|
||||||
|
* disabled) an non-zero if lsave has been populated.
|
||||||
|
*/
|
||||||
|
static int dbg_populate_lsave(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
struct ubifs_lprops *lprops;
|
||||||
|
struct ubifs_lpt_heap *heap;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
||||||
|
return 0;
|
||||||
|
if (random32() & 3)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < c->lsave_cnt; i++)
|
||||||
|
c->lsave[i] = c->main_first;
|
||||||
|
|
||||||
|
list_for_each_entry(lprops, &c->empty_list, list)
|
||||||
|
c->lsave[random32() % c->lsave_cnt] = lprops->lnum;
|
||||||
|
list_for_each_entry(lprops, &c->freeable_list, list)
|
||||||
|
c->lsave[random32() % c->lsave_cnt] = lprops->lnum;
|
||||||
|
list_for_each_entry(lprops, &c->frdi_idx_list, list)
|
||||||
|
c->lsave[random32() % c->lsave_cnt] = lprops->lnum;
|
||||||
|
|
||||||
|
heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
|
||||||
|
for (i = 0; i < heap->cnt; i++)
|
||||||
|
c->lsave[random32() % c->lsave_cnt] = heap->arr[i]->lnum;
|
||||||
|
heap = &c->lpt_heap[LPROPS_DIRTY - 1];
|
||||||
|
for (i = 0; i < heap->cnt; i++)
|
||||||
|
c->lsave[random32() % c->lsave_cnt] = heap->arr[i]->lnum;
|
||||||
|
heap = &c->lpt_heap[LPROPS_FREE - 1];
|
||||||
|
for (i = 0; i < heap->cnt; i++)
|
||||||
|
c->lsave[random32() % c->lsave_cnt] = heap->arr[i]->lnum;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_UBIFS_FS_DEBUG */
|
#endif /* CONFIG_UBIFS_FS_DEBUG */
|
||||||
|
|
|
@ -148,7 +148,7 @@ static int validate_master(const struct ubifs_info *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
main_sz = (long long)c->main_lebs * c->leb_size;
|
main_sz = (long long)c->main_lebs * c->leb_size;
|
||||||
if (c->old_idx_sz & 7 || c->old_idx_sz >= main_sz) {
|
if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) {
|
||||||
err = 9;
|
err = 9;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ static int validate_master(const struct ubifs_info *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->lst.total_dead + c->lst.total_dark +
|
if (c->lst.total_dead + c->lst.total_dark +
|
||||||
c->lst.total_used + c->old_idx_sz > main_sz) {
|
c->lst.total_used + c->bi.old_idx_sz > main_sz) {
|
||||||
err = 21;
|
err = 21;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||||
c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum);
|
c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum);
|
||||||
c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum);
|
c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum);
|
||||||
c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs);
|
c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs);
|
||||||
c->old_idx_sz = le64_to_cpu(c->mst_node->index_size);
|
c->bi.old_idx_sz = le64_to_cpu(c->mst_node->index_size);
|
||||||
c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum);
|
c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum);
|
||||||
c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs);
|
c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs);
|
||||||
c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum);
|
c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum);
|
||||||
|
@ -305,7 +305,7 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||||
c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
|
c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
|
||||||
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
|
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
|
||||||
|
|
||||||
c->calc_idx_sz = c->old_idx_sz;
|
c->calc_idx_sz = c->bi.old_idx_sz;
|
||||||
|
|
||||||
if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
|
if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
|
||||||
c->no_orphs = 1;
|
c->no_orphs = 1;
|
||||||
|
|
|
@ -340,4 +340,21 @@ static inline void ubifs_release_lprops(struct ubifs_info *c)
|
||||||
mutex_unlock(&c->lp_mutex);
|
mutex_unlock(&c->lp_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ubifs_next_log_lnum - switch to the next log LEB.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
* @lnum: current log LEB
|
||||||
|
*
|
||||||
|
* This helper function returns the log LEB number which goes next after LEB
|
||||||
|
* 'lnum'.
|
||||||
|
*/
|
||||||
|
static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
|
||||||
|
{
|
||||||
|
lnum += 1;
|
||||||
|
if (lnum > c->log_last)
|
||||||
|
lnum = UBIFS_LOG_LNUM;
|
||||||
|
|
||||||
|
return lnum;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __UBIFS_MISC_H__ */
|
#endif /* __UBIFS_MISC_H__ */
|
||||||
|
|
|
@ -673,7 +673,8 @@ static int kill_orphans(struct ubifs_info *c)
|
||||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
|
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
|
||||||
if (IS_ERR(sleb)) {
|
if (IS_ERR(sleb)) {
|
||||||
if (PTR_ERR(sleb) == -EUCLEAN)
|
if (PTR_ERR(sleb) == -EUCLEAN)
|
||||||
sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, 0);
|
sleb = ubifs_recover_leb(c, lnum, 0,
|
||||||
|
c->sbuf, 0);
|
||||||
if (IS_ERR(sleb)) {
|
if (IS_ERR(sleb)) {
|
||||||
err = PTR_ERR(sleb);
|
err = PTR_ERR(sleb);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -564,13 +564,16 @@ static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* drop_incomplete_group - drop nodes from an incomplete group.
|
* drop_last_node - drop the last node or group of nodes.
|
||||||
* @sleb: scanned LEB information
|
* @sleb: scanned LEB information
|
||||||
* @offs: offset of dropped nodes is returned here
|
* @offs: offset of dropped nodes is returned here
|
||||||
|
* @grouped: non-zero if whole group of nodes have to be dropped
|
||||||
*
|
*
|
||||||
* This function returns %1 if nodes are dropped and %0 otherwise.
|
* This is a helper function for 'ubifs_recover_leb()' which drops the last
|
||||||
|
* node of the scanned LEB or the last group of nodes if @grouped is not zero.
|
||||||
|
* This function returns %1 if a node was dropped and %0 otherwise.
|
||||||
*/
|
*/
|
||||||
static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs)
|
static int drop_last_node(struct ubifs_scan_leb *sleb, int *offs, int grouped)
|
||||||
{
|
{
|
||||||
int dropped = 0;
|
int dropped = 0;
|
||||||
|
|
||||||
|
@ -589,6 +592,8 @@ static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs)
|
||||||
kfree(snod);
|
kfree(snod);
|
||||||
sleb->nodes_cnt -= 1;
|
sleb->nodes_cnt -= 1;
|
||||||
dropped = 1;
|
dropped = 1;
|
||||||
|
if (!grouped)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return dropped;
|
return dropped;
|
||||||
}
|
}
|
||||||
|
@ -609,8 +614,7 @@ static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs)
|
||||||
struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
|
struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
|
||||||
int offs, void *sbuf, int grouped)
|
int offs, void *sbuf, int grouped)
|
||||||
{
|
{
|
||||||
int err, len = c->leb_size - offs, need_clean = 0, quiet = 1;
|
int ret = 0, err, len = c->leb_size - offs, start = offs, min_io_unit;
|
||||||
int empty_chkd = 0, start = offs;
|
|
||||||
struct ubifs_scan_leb *sleb;
|
struct ubifs_scan_leb *sleb;
|
||||||
void *buf = sbuf + offs;
|
void *buf = sbuf + offs;
|
||||||
|
|
||||||
|
@ -620,12 +624,8 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
|
||||||
if (IS_ERR(sleb))
|
if (IS_ERR(sleb))
|
||||||
return sleb;
|
return sleb;
|
||||||
|
|
||||||
if (sleb->ecc)
|
ubifs_assert(len >= 8);
|
||||||
need_clean = 1;
|
|
||||||
|
|
||||||
while (len >= 8) {
|
while (len >= 8) {
|
||||||
int ret;
|
|
||||||
|
|
||||||
dbg_scan("look at LEB %d:%d (%d bytes left)",
|
dbg_scan("look at LEB %d:%d (%d bytes left)",
|
||||||
lnum, offs, len);
|
lnum, offs, len);
|
||||||
|
|
||||||
|
@ -635,8 +635,7 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
|
||||||
* Scan quietly until there is an error from which we cannot
|
* Scan quietly until there is an error from which we cannot
|
||||||
* recover
|
* recover
|
||||||
*/
|
*/
|
||||||
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
|
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
|
||||||
|
|
||||||
if (ret == SCANNED_A_NODE) {
|
if (ret == SCANNED_A_NODE) {
|
||||||
/* A valid node, and not a padding node */
|
/* A valid node, and not a padding node */
|
||||||
struct ubifs_ch *ch = buf;
|
struct ubifs_ch *ch = buf;
|
||||||
|
@ -649,70 +648,32 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
|
||||||
offs += node_len;
|
offs += node_len;
|
||||||
buf += node_len;
|
buf += node_len;
|
||||||
len -= node_len;
|
len -= node_len;
|
||||||
continue;
|
} else if (ret > 0) {
|
||||||
}
|
|
||||||
|
|
||||||
if (ret > 0) {
|
|
||||||
/* Padding bytes or a valid padding node */
|
/* Padding bytes or a valid padding node */
|
||||||
offs += ret;
|
offs += ret;
|
||||||
buf += ret;
|
buf += ret;
|
||||||
len -= ret;
|
len -= ret;
|
||||||
continue;
|
} else if (ret == SCANNED_EMPTY_SPACE ||
|
||||||
}
|
ret == SCANNED_GARBAGE ||
|
||||||
|
ret == SCANNED_A_BAD_PAD_NODE ||
|
||||||
if (ret == SCANNED_EMPTY_SPACE) {
|
ret == SCANNED_A_CORRUPT_NODE) {
|
||||||
if (!is_empty(buf, len)) {
|
dbg_rcvry("found corruption - %d", ret);
|
||||||
if (!is_last_write(c, buf, offs))
|
|
||||||
break;
|
|
||||||
clean_buf(c, &buf, lnum, &offs, &len);
|
|
||||||
need_clean = 1;
|
|
||||||
}
|
|
||||||
empty_chkd = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
} else {
|
||||||
|
dbg_err("unexpected return value %d", ret);
|
||||||
if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE)
|
|
||||||
if (is_last_write(c, buf, offs)) {
|
|
||||||
clean_buf(c, &buf, lnum, &offs, &len);
|
|
||||||
need_clean = 1;
|
|
||||||
empty_chkd = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == SCANNED_A_CORRUPT_NODE)
|
|
||||||
if (no_more_nodes(c, buf, len, lnum, offs)) {
|
|
||||||
clean_buf(c, &buf, lnum, &offs, &len);
|
|
||||||
need_clean = 1;
|
|
||||||
empty_chkd = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quiet) {
|
|
||||||
/* Redo the last scan but noisily */
|
|
||||||
quiet = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ret) {
|
|
||||||
case SCANNED_GARBAGE:
|
|
||||||
dbg_err("garbage");
|
|
||||||
goto corrupted;
|
|
||||||
case SCANNED_A_CORRUPT_NODE:
|
|
||||||
case SCANNED_A_BAD_PAD_NODE:
|
|
||||||
dbg_err("bad node");
|
|
||||||
goto corrupted;
|
|
||||||
default:
|
|
||||||
dbg_err("unknown");
|
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty_chkd && !is_empty(buf, len)) {
|
if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) {
|
||||||
if (is_last_write(c, buf, offs)) {
|
if (!is_last_write(c, buf, offs))
|
||||||
clean_buf(c, &buf, lnum, &offs, &len);
|
goto corrupted_rescan;
|
||||||
need_clean = 1;
|
} else if (ret == SCANNED_A_CORRUPT_NODE) {
|
||||||
} else {
|
if (!no_more_nodes(c, buf, len, lnum, offs))
|
||||||
|
goto corrupted_rescan;
|
||||||
|
} else if (!is_empty(buf, len)) {
|
||||||
|
if (!is_last_write(c, buf, offs)) {
|
||||||
int corruption = first_non_ff(buf, len);
|
int corruption = first_non_ff(buf, len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -728,29 +689,82 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drop nodes from incomplete group */
|
min_io_unit = round_down(offs, c->min_io_size);
|
||||||
if (grouped && drop_incomplete_group(sleb, &offs)) {
|
if (grouped)
|
||||||
buf = sbuf + offs;
|
/*
|
||||||
len = c->leb_size - offs;
|
* If nodes are grouped, always drop the incomplete group at
|
||||||
clean_buf(c, &buf, lnum, &offs, &len);
|
* the end.
|
||||||
need_clean = 1;
|
*/
|
||||||
}
|
drop_last_node(sleb, &offs, 1);
|
||||||
|
|
||||||
if (offs % c->min_io_size) {
|
/*
|
||||||
clean_buf(c, &buf, lnum, &offs, &len);
|
* While we are in the middle of the same min. I/O unit keep dropping
|
||||||
need_clean = 1;
|
* nodes. So basically, what we want is to make sure that the last min.
|
||||||
}
|
* I/O unit where we saw the corruption is dropped completely with all
|
||||||
|
* the uncorrupted node which may possibly sit there.
|
||||||
|
*
|
||||||
|
* In other words, let's name the min. I/O unit where the corruption
|
||||||
|
* starts B, and the previous min. I/O unit A. The below code tries to
|
||||||
|
* deal with a situation when half of B contains valid nodes or the end
|
||||||
|
* of a valid node, and the second half of B contains corrupted data or
|
||||||
|
* garbage. This means that UBIFS had been writing to B just before the
|
||||||
|
* power cut happened. I do not know how realistic is this scenario
|
||||||
|
* that half of the min. I/O unit had been written successfully and the
|
||||||
|
* other half not, but this is possible in our 'failure mode emulation'
|
||||||
|
* infrastructure at least.
|
||||||
|
*
|
||||||
|
* So what is the problem, why we need to drop those nodes? Whey can't
|
||||||
|
* we just clean-up the second half of B by putting a padding node
|
||||||
|
* there? We can, and this works fine with one exception which was
|
||||||
|
* reproduced with power cut emulation testing and happens extremely
|
||||||
|
* rarely. The description follows, but it is worth noting that that is
|
||||||
|
* only about the GC head, so we could do this trick only if the bud
|
||||||
|
* belongs to the GC head, but it does not seem to be worth an
|
||||||
|
* additional "if" statement.
|
||||||
|
*
|
||||||
|
* So, imagine the file-system is full, we run GC which is moving valid
|
||||||
|
* nodes from LEB X to LEB Y (obviously, LEB Y is the current GC head
|
||||||
|
* LEB). The @c->gc_lnum is -1, which means that GC will retain LEB X
|
||||||
|
* and will try to continue. Imagine that LEB X is currently the
|
||||||
|
* dirtiest LEB, and the amount of used space in LEB Y is exactly the
|
||||||
|
* same as amount of free space in LEB X.
|
||||||
|
*
|
||||||
|
* And a power cut happens when nodes are moved from LEB X to LEB Y. We
|
||||||
|
* are here trying to recover LEB Y which is the GC head LEB. We find
|
||||||
|
* the min. I/O unit B as described above. Then we clean-up LEB Y by
|
||||||
|
* padding min. I/O unit. And later 'ubifs_rcvry_gc_commit()' function
|
||||||
|
* fails, because it cannot find a dirty LEB which could be GC'd into
|
||||||
|
* LEB Y! Even LEB X does not match because the amount of valid nodes
|
||||||
|
* there does not fit the free space in LEB Y any more! And this is
|
||||||
|
* because of the padding node which we added to LEB Y. The
|
||||||
|
* user-visible effect of this which I once observed and analysed is
|
||||||
|
* that we cannot mount the file-system with -ENOSPC error.
|
||||||
|
*
|
||||||
|
* So obviously, to make sure that situation does not happen we should
|
||||||
|
* free min. I/O unit B in LEB Y completely and the last used min. I/O
|
||||||
|
* unit in LEB Y should be A. This is basically what the below code
|
||||||
|
* tries to do.
|
||||||
|
*/
|
||||||
|
while (min_io_unit == round_down(offs, c->min_io_size) &&
|
||||||
|
min_io_unit != offs &&
|
||||||
|
drop_last_node(sleb, &offs, grouped));
|
||||||
|
|
||||||
|
buf = sbuf + offs;
|
||||||
|
len = c->leb_size - offs;
|
||||||
|
|
||||||
|
clean_buf(c, &buf, lnum, &offs, &len);
|
||||||
ubifs_end_scan(c, sleb, lnum, offs);
|
ubifs_end_scan(c, sleb, lnum, offs);
|
||||||
|
|
||||||
if (need_clean) {
|
err = fix_unclean_leb(c, sleb, start);
|
||||||
err = fix_unclean_leb(c, sleb, start);
|
if (err)
|
||||||
if (err)
|
goto error;
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sleb;
|
return sleb;
|
||||||
|
|
||||||
|
corrupted_rescan:
|
||||||
|
/* Re-scan the corrupted data with verbose messages */
|
||||||
|
dbg_err("corruptio %d", ret);
|
||||||
|
ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
|
||||||
corrupted:
|
corrupted:
|
||||||
ubifs_scanned_corruption(c, lnum, offs, buf);
|
ubifs_scanned_corruption(c, lnum, offs, buf);
|
||||||
err = -EUCLEAN;
|
err = -EUCLEAN;
|
||||||
|
@ -1069,6 +1083,53 @@ int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* grab_empty_leb - grab an empty LEB to use as GC LEB and run commit.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty
|
||||||
|
* LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns
|
||||||
|
* zero in case of success and a negative error code in case of failure.
|
||||||
|
*/
|
||||||
|
static int grab_empty_leb(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
int lnum, err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note, it is very important to first search for an empty LEB and then
|
||||||
|
* run the commit, not vice-versa. The reason is that there might be
|
||||||
|
* only one empty LEB at the moment, the one which has been the
|
||||||
|
* @c->gc_lnum just before the power cut happened. During the regular
|
||||||
|
* UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no
|
||||||
|
* one but GC can grab it. But at this moment this single empty LEB is
|
||||||
|
* not marked as taken, so if we run commit - what happens? Right, the
|
||||||
|
* commit will grab it and write the index there. Remember that the
|
||||||
|
* index always expands as long as there is free space, and it only
|
||||||
|
* starts consolidating when we run out of space.
|
||||||
|
*
|
||||||
|
* IOW, if we run commit now, we might not be able to find a free LEB
|
||||||
|
* after this.
|
||||||
|
*/
|
||||||
|
lnum = ubifs_find_free_leb_for_idx(c);
|
||||||
|
if (lnum < 0) {
|
||||||
|
dbg_err("could not find an empty LEB");
|
||||||
|
dbg_dump_lprops(c);
|
||||||
|
dbg_dump_budg(c, &c->bi);
|
||||||
|
return lnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the index flag */
|
||||||
|
err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
|
||||||
|
LPROPS_INDEX, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
c->gc_lnum = lnum;
|
||||||
|
dbg_rcvry("found empty LEB %d, run commit", lnum);
|
||||||
|
|
||||||
|
return ubifs_run_commit(c);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit.
|
* ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
|
@ -1091,71 +1152,26 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
|
struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
|
||||||
struct ubifs_lprops lp;
|
struct ubifs_lprops lp;
|
||||||
int lnum, err;
|
int err;
|
||||||
|
|
||||||
|
dbg_rcvry("GC head LEB %d, offs %d", wbuf->lnum, wbuf->offs);
|
||||||
|
|
||||||
c->gc_lnum = -1;
|
c->gc_lnum = -1;
|
||||||
if (wbuf->lnum == -1) {
|
if (wbuf->lnum == -1 || wbuf->offs == c->leb_size)
|
||||||
dbg_rcvry("no GC head LEB");
|
return grab_empty_leb(c);
|
||||||
goto find_free;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* See whether the used space in the dirtiest LEB fits in the GC head
|
|
||||||
* LEB.
|
|
||||||
*/
|
|
||||||
if (wbuf->offs == c->leb_size) {
|
|
||||||
dbg_rcvry("no room in GC head LEB");
|
|
||||||
goto find_free;
|
|
||||||
}
|
|
||||||
err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2);
|
err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2);
|
||||||
if (err) {
|
if (err) {
|
||||||
/*
|
if (err != -ENOSPC)
|
||||||
* There are no dirty or empty LEBs subject to here being
|
return err;
|
||||||
* enough for the index. Try to use
|
|
||||||
* 'ubifs_find_free_leb_for_idx()', which will return any empty
|
dbg_rcvry("could not find a dirty LEB");
|
||||||
* LEBs (ignoring index requirements). If the index then
|
return grab_empty_leb(c);
|
||||||
* doesn't have enough LEBs the recovery commit will fail -
|
|
||||||
* which is the same result anyway i.e. recovery fails. So
|
|
||||||
* there is no problem ignoring index requirements and just
|
|
||||||
* grabbing a free LEB since we have already established there
|
|
||||||
* is not a dirty LEB we could have used instead.
|
|
||||||
*/
|
|
||||||
if (err == -ENOSPC) {
|
|
||||||
dbg_rcvry("could not find a dirty LEB");
|
|
||||||
goto find_free;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ubifs_assert(!(lp.flags & LPROPS_INDEX));
|
ubifs_assert(!(lp.flags & LPROPS_INDEX));
|
||||||
lnum = lp.lnum;
|
ubifs_assert(lp.free + lp.dirty >= wbuf->offs);
|
||||||
if (lp.free + lp.dirty == c->leb_size) {
|
|
||||||
/* An empty LEB was returned */
|
|
||||||
if (lp.free != c->leb_size) {
|
|
||||||
err = ubifs_change_one_lp(c, lnum, c->leb_size,
|
|
||||||
0, 0, 0, 0);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
err = ubifs_leb_unmap(c, lnum);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
c->gc_lnum = lnum;
|
|
||||||
dbg_rcvry("allocated LEB %d for GC", lnum);
|
|
||||||
/* Run the commit */
|
|
||||||
dbg_rcvry("committing");
|
|
||||||
return ubifs_run_commit(c);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* There was no empty LEB so the used space in the dirtiest LEB must fit
|
|
||||||
* in the GC head LEB.
|
|
||||||
*/
|
|
||||||
if (lp.free + lp.dirty < wbuf->offs) {
|
|
||||||
dbg_rcvry("LEB %d doesn't fit in GC head LEB %d:%d",
|
|
||||||
lnum, wbuf->lnum, wbuf->offs);
|
|
||||||
err = ubifs_return_leb(c, lnum);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
goto find_free;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* We run the commit before garbage collection otherwise subsequent
|
* We run the commit before garbage collection otherwise subsequent
|
||||||
* mounts will see the GC and orphan deletion in a different order.
|
* mounts will see the GC and orphan deletion in a different order.
|
||||||
|
@ -1164,11 +1180,8 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
|
||||||
err = ubifs_run_commit(c);
|
err = ubifs_run_commit(c);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
/*
|
|
||||||
* The data in the dirtiest LEB fits in the GC head LEB, so do the GC
|
dbg_rcvry("GC'ing LEB %d", lp.lnum);
|
||||||
* - use locking to keep 'ubifs_assert()' happy.
|
|
||||||
*/
|
|
||||||
dbg_rcvry("GC'ing LEB %d", lnum);
|
|
||||||
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
||||||
err = ubifs_garbage_collect_leb(c, &lp);
|
err = ubifs_garbage_collect_leb(c, &lp);
|
||||||
if (err >= 0) {
|
if (err >= 0) {
|
||||||
|
@ -1184,37 +1197,17 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (err != LEB_RETAINED) {
|
|
||||||
dbg_err("GC returned %d", err);
|
ubifs_assert(err == LEB_RETAINED);
|
||||||
|
if (err != LEB_RETAINED)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
err = ubifs_leb_unmap(c, c->gc_lnum);
|
err = ubifs_leb_unmap(c, c->gc_lnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
dbg_rcvry("allocated LEB %d for GC", lnum);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
find_free:
|
dbg_rcvry("allocated LEB %d for GC", lp.lnum);
|
||||||
/*
|
return 0;
|
||||||
* There is no GC head LEB or the free space in the GC head LEB is too
|
|
||||||
* small, or there are not dirty LEBs. Allocate gc_lnum by calling
|
|
||||||
* 'ubifs_find_free_leb_for_idx()' so GC is not run.
|
|
||||||
*/
|
|
||||||
lnum = ubifs_find_free_leb_for_idx(c);
|
|
||||||
if (lnum < 0) {
|
|
||||||
dbg_err("could not find an empty LEB");
|
|
||||||
return lnum;
|
|
||||||
}
|
|
||||||
/* And reset the index flag */
|
|
||||||
err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
|
|
||||||
LPROPS_INDEX, 0);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
c->gc_lnum = lnum;
|
|
||||||
dbg_rcvry("allocated LEB %d for GC", lnum);
|
|
||||||
/* Run the commit */
|
|
||||||
dbg_rcvry("committing");
|
|
||||||
return ubifs_run_commit(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1456,7 +1449,7 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
|
||||||
err = ubi_leb_change(c->ubi, lnum, c->sbuf, len, UBI_UNKNOWN);
|
err = ubi_leb_change(c->ubi, lnum, c->sbuf, len, UBI_UNKNOWN);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
dbg_rcvry("inode %lu at %d:%d size %lld -> %lld ",
|
dbg_rcvry("inode %lu at %d:%d size %lld -> %lld",
|
||||||
(unsigned long)e->inum, lnum, offs, i_size, e->d_size);
|
(unsigned long)e->inum, lnum, offs, i_size, e->d_size);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1505,20 +1498,27 @@ int ubifs_recover_size(struct ubifs_info *c)
|
||||||
e->i_size = le64_to_cpu(ino->size);
|
e->i_size = le64_to_cpu(ino->size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e->exists && e->i_size < e->d_size) {
|
if (e->exists && e->i_size < e->d_size) {
|
||||||
if (!e->inode && c->ro_mount) {
|
if (c->ro_mount) {
|
||||||
/* Fix the inode size and pin it in memory */
|
/* Fix the inode size and pin it in memory */
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct ubifs_inode *ui;
|
||||||
|
|
||||||
|
ubifs_assert(!e->inode);
|
||||||
|
|
||||||
inode = ubifs_iget(c->vfs_sb, e->inum);
|
inode = ubifs_iget(c->vfs_sb, e->inum);
|
||||||
if (IS_ERR(inode))
|
if (IS_ERR(inode))
|
||||||
return PTR_ERR(inode);
|
return PTR_ERR(inode);
|
||||||
|
|
||||||
|
ui = ubifs_inode(inode);
|
||||||
if (inode->i_size < e->d_size) {
|
if (inode->i_size < e->d_size) {
|
||||||
dbg_rcvry("ino %lu size %lld -> %lld",
|
dbg_rcvry("ino %lu size %lld -> %lld",
|
||||||
(unsigned long)e->inum,
|
(unsigned long)e->inum,
|
||||||
e->d_size, inode->i_size);
|
inode->i_size, e->d_size);
|
||||||
inode->i_size = e->d_size;
|
inode->i_size = e->d_size;
|
||||||
ubifs_inode(inode)->ui_size = e->d_size;
|
ui->ui_size = e->d_size;
|
||||||
|
ui->synced_i_size = e->d_size;
|
||||||
e->inode = inode;
|
e->inode = inode;
|
||||||
this = rb_next(this);
|
this = rb_next(this);
|
||||||
continue;
|
continue;
|
||||||
|
@ -1533,9 +1533,11 @@ int ubifs_recover_size(struct ubifs_info *c)
|
||||||
iput(e->inode);
|
iput(e->inode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this = rb_next(this);
|
this = rb_next(this);
|
||||||
rb_erase(&e->rb, &c->size_tree);
|
rb_erase(&e->rb, &c->size_tree);
|
||||||
kfree(e);
|
kfree(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,44 +33,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ubifs.h"
|
#include "ubifs.h"
|
||||||
|
#include <linux/list_sort.h>
|
||||||
/*
|
|
||||||
* Replay flags.
|
|
||||||
*
|
|
||||||
* REPLAY_DELETION: node was deleted
|
|
||||||
* REPLAY_REF: node is a reference node
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
REPLAY_DELETION = 1,
|
|
||||||
REPLAY_REF = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct replay_entry - replay tree entry.
|
* struct replay_entry - replay list entry.
|
||||||
* @lnum: logical eraseblock number of the node
|
* @lnum: logical eraseblock number of the node
|
||||||
* @offs: node offset
|
* @offs: node offset
|
||||||
* @len: node length
|
* @len: node length
|
||||||
|
* @deletion: non-zero if this entry corresponds to a node deletion
|
||||||
* @sqnum: node sequence number
|
* @sqnum: node sequence number
|
||||||
* @flags: replay flags
|
* @list: links the replay list
|
||||||
* @rb: links the replay tree
|
|
||||||
* @key: node key
|
* @key: node key
|
||||||
* @nm: directory entry name
|
* @nm: directory entry name
|
||||||
* @old_size: truncation old size
|
* @old_size: truncation old size
|
||||||
* @new_size: truncation new size
|
* @new_size: truncation new size
|
||||||
* @free: amount of free space in a bud
|
|
||||||
* @dirty: amount of dirty space in a bud from padding and deletion nodes
|
|
||||||
* @jhead: journal head number of the bud
|
|
||||||
*
|
*
|
||||||
* UBIFS journal replay must compare node sequence numbers, which means it must
|
* The replay process first scans all buds and builds the replay list, then
|
||||||
* build a tree of node information to insert into the TNC.
|
* sorts the replay list in nodes sequence number order, and then inserts all
|
||||||
|
* the replay entries to the TNC.
|
||||||
*/
|
*/
|
||||||
struct replay_entry {
|
struct replay_entry {
|
||||||
int lnum;
|
int lnum;
|
||||||
int offs;
|
int offs;
|
||||||
int len;
|
int len;
|
||||||
|
unsigned int deletion:1;
|
||||||
unsigned long long sqnum;
|
unsigned long long sqnum;
|
||||||
int flags;
|
struct list_head list;
|
||||||
struct rb_node rb;
|
|
||||||
union ubifs_key key;
|
union ubifs_key key;
|
||||||
union {
|
union {
|
||||||
struct qstr nm;
|
struct qstr nm;
|
||||||
|
@ -78,11 +66,6 @@ struct replay_entry {
|
||||||
loff_t old_size;
|
loff_t old_size;
|
||||||
loff_t new_size;
|
loff_t new_size;
|
||||||
};
|
};
|
||||||
struct {
|
|
||||||
int free;
|
|
||||||
int dirty;
|
|
||||||
int jhead;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,57 +73,64 @@ struct replay_entry {
|
||||||
* struct bud_entry - entry in the list of buds to replay.
|
* struct bud_entry - entry in the list of buds to replay.
|
||||||
* @list: next bud in the list
|
* @list: next bud in the list
|
||||||
* @bud: bud description object
|
* @bud: bud description object
|
||||||
* @free: free bytes in the bud
|
|
||||||
* @sqnum: reference node sequence number
|
* @sqnum: reference node sequence number
|
||||||
|
* @free: free bytes in the bud
|
||||||
|
* @dirty: dirty bytes in the bud
|
||||||
*/
|
*/
|
||||||
struct bud_entry {
|
struct bud_entry {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct ubifs_bud *bud;
|
struct ubifs_bud *bud;
|
||||||
int free;
|
|
||||||
unsigned long long sqnum;
|
unsigned long long sqnum;
|
||||||
|
int free;
|
||||||
|
int dirty;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set_bud_lprops - set free and dirty space used by a bud.
|
* set_bud_lprops - set free and dirty space used by a bud.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
* @r: replay entry of bud
|
* @b: bud entry which describes the bud
|
||||||
|
*
|
||||||
|
* This function makes sure the LEB properties of bud @b are set correctly
|
||||||
|
* after the replay. Returns zero in case of success and a negative error code
|
||||||
|
* in case of failure.
|
||||||
*/
|
*/
|
||||||
static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r)
|
static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b)
|
||||||
{
|
{
|
||||||
const struct ubifs_lprops *lp;
|
const struct ubifs_lprops *lp;
|
||||||
int err = 0, dirty;
|
int err = 0, dirty;
|
||||||
|
|
||||||
ubifs_get_lprops(c);
|
ubifs_get_lprops(c);
|
||||||
|
|
||||||
lp = ubifs_lpt_lookup_dirty(c, r->lnum);
|
lp = ubifs_lpt_lookup_dirty(c, b->bud->lnum);
|
||||||
if (IS_ERR(lp)) {
|
if (IS_ERR(lp)) {
|
||||||
err = PTR_ERR(lp);
|
err = PTR_ERR(lp);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
dirty = lp->dirty;
|
dirty = lp->dirty;
|
||||||
if (r->offs == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
|
if (b->bud->start == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
|
||||||
/*
|
/*
|
||||||
* The LEB was added to the journal with a starting offset of
|
* The LEB was added to the journal with a starting offset of
|
||||||
* zero which means the LEB must have been empty. The LEB
|
* zero which means the LEB must have been empty. The LEB
|
||||||
* property values should be lp->free == c->leb_size and
|
* property values should be @lp->free == @c->leb_size and
|
||||||
* lp->dirty == 0, but that is not the case. The reason is that
|
* @lp->dirty == 0, but that is not the case. The reason is that
|
||||||
* the LEB was garbage collected. The garbage collector resets
|
* the LEB had been garbage collected before it became the bud,
|
||||||
* the free and dirty space without recording it anywhere except
|
* and there was not commit inbetween. The garbage collector
|
||||||
* lprops, so if there is not a commit then lprops does not have
|
* resets the free and dirty space without recording it
|
||||||
* that information next time the file system is mounted.
|
* anywhere except lprops, so if there was no commit then
|
||||||
|
* lprops does not have that information.
|
||||||
*
|
*
|
||||||
* We do not need to adjust free space because the scan has told
|
* We do not need to adjust free space because the scan has told
|
||||||
* us the exact value which is recorded in the replay entry as
|
* us the exact value which is recorded in the replay entry as
|
||||||
* r->free.
|
* @b->free.
|
||||||
*
|
*
|
||||||
* However we do need to subtract from the dirty space the
|
* However we do need to subtract from the dirty space the
|
||||||
* amount of space that the garbage collector reclaimed, which
|
* amount of space that the garbage collector reclaimed, which
|
||||||
* is the whole LEB minus the amount of space that was free.
|
* is the whole LEB minus the amount of space that was free.
|
||||||
*/
|
*/
|
||||||
dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum,
|
dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
|
||||||
lp->free, lp->dirty);
|
lp->free, lp->dirty);
|
||||||
dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum,
|
dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
|
||||||
lp->free, lp->dirty);
|
lp->free, lp->dirty);
|
||||||
dirty -= c->leb_size - lp->free;
|
dirty -= c->leb_size - lp->free;
|
||||||
/*
|
/*
|
||||||
|
@ -152,10 +142,10 @@ static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r)
|
||||||
*/
|
*/
|
||||||
if (dirty != 0)
|
if (dirty != 0)
|
||||||
dbg_msg("LEB %d lp: %d free %d dirty "
|
dbg_msg("LEB %d lp: %d free %d dirty "
|
||||||
"replay: %d free %d dirty", r->lnum, lp->free,
|
"replay: %d free %d dirty", b->bud->lnum,
|
||||||
lp->dirty, r->free, r->dirty);
|
lp->free, lp->dirty, b->free, b->dirty);
|
||||||
}
|
}
|
||||||
lp = ubifs_change_lp(c, lp, r->free, dirty + r->dirty,
|
lp = ubifs_change_lp(c, lp, b->free, dirty + b->dirty,
|
||||||
lp->flags | LPROPS_TAKEN, 0);
|
lp->flags | LPROPS_TAKEN, 0);
|
||||||
if (IS_ERR(lp)) {
|
if (IS_ERR(lp)) {
|
||||||
err = PTR_ERR(lp);
|
err = PTR_ERR(lp);
|
||||||
|
@ -163,14 +153,36 @@ static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the journal head points to the latest bud */
|
/* Make sure the journal head points to the latest bud */
|
||||||
err = ubifs_wbuf_seek_nolock(&c->jheads[r->jhead].wbuf, r->lnum,
|
err = ubifs_wbuf_seek_nolock(&c->jheads[b->bud->jhead].wbuf,
|
||||||
c->leb_size - r->free, UBI_SHORTTERM);
|
b->bud->lnum, c->leb_size - b->free,
|
||||||
|
UBI_SHORTTERM);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
ubifs_release_lprops(c);
|
ubifs_release_lprops(c);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set_buds_lprops - set free and dirty space for all replayed buds.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* This function sets LEB properties for all replayed buds. Returns zero in
|
||||||
|
* case of success and a negative error code in case of failure.
|
||||||
|
*/
|
||||||
|
static int set_buds_lprops(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
struct bud_entry *b;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
list_for_each_entry(b, &c->replay_buds, list) {
|
||||||
|
err = set_bud_lprops(c, b);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* trun_remove_range - apply a replay entry for a truncation to the TNC.
|
* trun_remove_range - apply a replay entry for a truncation to the TNC.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
|
@ -207,24 +219,22 @@ static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r)
|
||||||
*/
|
*/
|
||||||
static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
||||||
{
|
{
|
||||||
int err, deletion = ((r->flags & REPLAY_DELETION) != 0);
|
int err;
|
||||||
|
|
||||||
dbg_mnt("LEB %d:%d len %d flgs %d sqnum %llu %s", r->lnum,
|
dbg_mnt("LEB %d:%d len %d deletion %d sqnum %llu %s", r->lnum,
|
||||||
r->offs, r->len, r->flags, r->sqnum, DBGKEY(&r->key));
|
r->offs, r->len, r->deletion, r->sqnum, DBGKEY(&r->key));
|
||||||
|
|
||||||
/* Set c->replay_sqnum to help deal with dangling branches. */
|
/* Set c->replay_sqnum to help deal with dangling branches. */
|
||||||
c->replay_sqnum = r->sqnum;
|
c->replay_sqnum = r->sqnum;
|
||||||
|
|
||||||
if (r->flags & REPLAY_REF)
|
if (is_hash_key(c, &r->key)) {
|
||||||
err = set_bud_lprops(c, r);
|
if (r->deletion)
|
||||||
else if (is_hash_key(c, &r->key)) {
|
|
||||||
if (deletion)
|
|
||||||
err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
|
err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
|
||||||
else
|
else
|
||||||
err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
|
err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
|
||||||
r->len, &r->nm);
|
r->len, &r->nm);
|
||||||
} else {
|
} else {
|
||||||
if (deletion)
|
if (r->deletion)
|
||||||
switch (key_type(c, &r->key)) {
|
switch (key_type(c, &r->key)) {
|
||||||
case UBIFS_INO_KEY:
|
case UBIFS_INO_KEY:
|
||||||
{
|
{
|
||||||
|
@ -247,7 +257,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (c->need_recovery)
|
if (c->need_recovery)
|
||||||
err = ubifs_recover_size_accum(c, &r->key, deletion,
|
err = ubifs_recover_size_accum(c, &r->key, r->deletion,
|
||||||
r->new_size);
|
r->new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,68 +265,77 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* destroy_replay_tree - destroy the replay.
|
* replay_entries_cmp - compare 2 replay entries.
|
||||||
* @c: UBIFS file-system description object
|
* @priv: UBIFS file-system description object
|
||||||
|
* @a: first replay entry
|
||||||
|
* @a: second replay entry
|
||||||
*
|
*
|
||||||
* Destroy the replay tree.
|
* This is a comparios function for 'list_sort()' which compares 2 replay
|
||||||
|
* entries @a and @b by comparing their sequence numer. Returns %1 if @a has
|
||||||
|
* greater sequence number and %-1 otherwise.
|
||||||
*/
|
*/
|
||||||
static void destroy_replay_tree(struct ubifs_info *c)
|
static int replay_entries_cmp(void *priv, struct list_head *a,
|
||||||
|
struct list_head *b)
|
||||||
{
|
{
|
||||||
struct rb_node *this = c->replay_tree.rb_node;
|
struct replay_entry *ra, *rb;
|
||||||
struct replay_entry *r;
|
|
||||||
|
|
||||||
while (this) {
|
cond_resched();
|
||||||
if (this->rb_left) {
|
if (a == b)
|
||||||
this = this->rb_left;
|
return 0;
|
||||||
continue;
|
|
||||||
} else if (this->rb_right) {
|
ra = list_entry(a, struct replay_entry, list);
|
||||||
this = this->rb_right;
|
rb = list_entry(b, struct replay_entry, list);
|
||||||
continue;
|
ubifs_assert(ra->sqnum != rb->sqnum);
|
||||||
}
|
if (ra->sqnum > rb->sqnum)
|
||||||
r = rb_entry(this, struct replay_entry, rb);
|
return 1;
|
||||||
this = rb_parent(this);
|
return -1;
|
||||||
if (this) {
|
|
||||||
if (this->rb_left == &r->rb)
|
|
||||||
this->rb_left = NULL;
|
|
||||||
else
|
|
||||||
this->rb_right = NULL;
|
|
||||||
}
|
|
||||||
if (is_hash_key(c, &r->key))
|
|
||||||
kfree(r->nm.name);
|
|
||||||
kfree(r);
|
|
||||||
}
|
|
||||||
c->replay_tree = RB_ROOT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* apply_replay_tree - apply the replay tree to the TNC.
|
* apply_replay_list - apply the replay list to the TNC.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
*
|
*
|
||||||
* Apply the replay tree.
|
* Apply all entries in the replay list to the TNC. Returns zero in case of
|
||||||
* Returns zero in case of success and a negative error code in case of
|
* success and a negative error code in case of failure.
|
||||||
* failure.
|
|
||||||
*/
|
*/
|
||||||
static int apply_replay_tree(struct ubifs_info *c)
|
static int apply_replay_list(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
struct rb_node *this = rb_first(&c->replay_tree);
|
struct replay_entry *r;
|
||||||
|
int err;
|
||||||
|
|
||||||
while (this) {
|
list_sort(c, &c->replay_list, &replay_entries_cmp);
|
||||||
struct replay_entry *r;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
|
list_for_each_entry(r, &c->replay_list, list) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
|
||||||
r = rb_entry(this, struct replay_entry, rb);
|
|
||||||
err = apply_replay_entry(c, r);
|
err = apply_replay_entry(c, r);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
this = rb_next(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* insert_node - insert a node to the replay tree.
|
* destroy_replay_list - destroy the replay.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* Destroy the replay list.
|
||||||
|
*/
|
||||||
|
static void destroy_replay_list(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
struct replay_entry *r, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(r, tmp, &c->replay_list, list) {
|
||||||
|
if (is_hash_key(c, &r->key))
|
||||||
|
kfree(r->nm.name);
|
||||||
|
list_del(&r->list);
|
||||||
|
kfree(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert_node - insert a node to the replay list
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
* @lnum: node logical eraseblock number
|
* @lnum: node logical eraseblock number
|
||||||
* @offs: node offset
|
* @offs: node offset
|
||||||
|
@ -328,39 +347,25 @@ static int apply_replay_tree(struct ubifs_info *c)
|
||||||
* @old_size: truncation old size
|
* @old_size: truncation old size
|
||||||
* @new_size: truncation new size
|
* @new_size: truncation new size
|
||||||
*
|
*
|
||||||
* This function inserts a scanned non-direntry node to the replay tree. The
|
* This function inserts a scanned non-direntry node to the replay list. The
|
||||||
* replay tree is an RB-tree containing @struct replay_entry elements which are
|
* replay list contains @struct replay_entry elements, and we sort this list in
|
||||||
* indexed by the sequence number. The replay tree is applied at the very end
|
* sequence number order before applying it. The replay list is applied at the
|
||||||
* of the replay process. Since the tree is sorted in sequence number order,
|
* very end of the replay process. Since the list is sorted in sequence number
|
||||||
* the older modifications are applied first. This function returns zero in
|
* order, the older modifications are applied first. This function returns zero
|
||||||
* case of success and a negative error code in case of failure.
|
* in case of success and a negative error code in case of failure.
|
||||||
*/
|
*/
|
||||||
static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
||||||
union ubifs_key *key, unsigned long long sqnum,
|
union ubifs_key *key, unsigned long long sqnum,
|
||||||
int deletion, int *used, loff_t old_size,
|
int deletion, int *used, loff_t old_size,
|
||||||
loff_t new_size)
|
loff_t new_size)
|
||||||
{
|
{
|
||||||
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
|
|
||||||
struct replay_entry *r;
|
struct replay_entry *r;
|
||||||
|
|
||||||
|
dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key));
|
||||||
|
|
||||||
if (key_inum(c, key) >= c->highest_inum)
|
if (key_inum(c, key) >= c->highest_inum)
|
||||||
c->highest_inum = key_inum(c, key);
|
c->highest_inum = key_inum(c, key);
|
||||||
|
|
||||||
dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key));
|
|
||||||
while (*p) {
|
|
||||||
parent = *p;
|
|
||||||
r = rb_entry(parent, struct replay_entry, rb);
|
|
||||||
if (sqnum < r->sqnum) {
|
|
||||||
p = &(*p)->rb_left;
|
|
||||||
continue;
|
|
||||||
} else if (sqnum > r->sqnum) {
|
|
||||||
p = &(*p)->rb_right;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ubifs_err("duplicate sqnum in replay");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
||||||
if (!r)
|
if (!r)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -370,19 +375,18 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
||||||
r->lnum = lnum;
|
r->lnum = lnum;
|
||||||
r->offs = offs;
|
r->offs = offs;
|
||||||
r->len = len;
|
r->len = len;
|
||||||
|
r->deletion = !!deletion;
|
||||||
r->sqnum = sqnum;
|
r->sqnum = sqnum;
|
||||||
r->flags = (deletion ? REPLAY_DELETION : 0);
|
key_copy(c, key, &r->key);
|
||||||
r->old_size = old_size;
|
r->old_size = old_size;
|
||||||
r->new_size = new_size;
|
r->new_size = new_size;
|
||||||
key_copy(c, key, &r->key);
|
|
||||||
|
|
||||||
rb_link_node(&r->rb, parent, p);
|
list_add_tail(&r->list, &c->replay_list);
|
||||||
rb_insert_color(&r->rb, &c->replay_tree);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* insert_dent - insert a directory entry node into the replay tree.
|
* insert_dent - insert a directory entry node into the replay list.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
* @lnum: node logical eraseblock number
|
* @lnum: node logical eraseblock number
|
||||||
* @offs: node offset
|
* @offs: node offset
|
||||||
|
@ -394,43 +398,25 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
||||||
* @deletion: non-zero if this is a deletion
|
* @deletion: non-zero if this is a deletion
|
||||||
* @used: number of bytes in use in a LEB
|
* @used: number of bytes in use in a LEB
|
||||||
*
|
*
|
||||||
* This function inserts a scanned directory entry node to the replay tree.
|
* This function inserts a scanned directory entry node or an extended
|
||||||
* Returns zero in case of success and a negative error code in case of
|
* attribute entry to the replay list. Returns zero in case of success and a
|
||||||
* failure.
|
* negative error code in case of failure.
|
||||||
*
|
|
||||||
* This function is also used for extended attribute entries because they are
|
|
||||||
* implemented as directory entry nodes.
|
|
||||||
*/
|
*/
|
||||||
static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
|
static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
|
||||||
union ubifs_key *key, const char *name, int nlen,
|
union ubifs_key *key, const char *name, int nlen,
|
||||||
unsigned long long sqnum, int deletion, int *used)
|
unsigned long long sqnum, int deletion, int *used)
|
||||||
{
|
{
|
||||||
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
|
|
||||||
struct replay_entry *r;
|
struct replay_entry *r;
|
||||||
char *nbuf;
|
char *nbuf;
|
||||||
|
|
||||||
|
dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key));
|
||||||
if (key_inum(c, key) >= c->highest_inum)
|
if (key_inum(c, key) >= c->highest_inum)
|
||||||
c->highest_inum = key_inum(c, key);
|
c->highest_inum = key_inum(c, key);
|
||||||
|
|
||||||
dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key));
|
|
||||||
while (*p) {
|
|
||||||
parent = *p;
|
|
||||||
r = rb_entry(parent, struct replay_entry, rb);
|
|
||||||
if (sqnum < r->sqnum) {
|
|
||||||
p = &(*p)->rb_left;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sqnum > r->sqnum) {
|
|
||||||
p = &(*p)->rb_right;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ubifs_err("duplicate sqnum in replay");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
||||||
if (!r)
|
if (!r)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
nbuf = kmalloc(nlen + 1, GFP_KERNEL);
|
nbuf = kmalloc(nlen + 1, GFP_KERNEL);
|
||||||
if (!nbuf) {
|
if (!nbuf) {
|
||||||
kfree(r);
|
kfree(r);
|
||||||
|
@ -442,17 +428,15 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
|
||||||
r->lnum = lnum;
|
r->lnum = lnum;
|
||||||
r->offs = offs;
|
r->offs = offs;
|
||||||
r->len = len;
|
r->len = len;
|
||||||
|
r->deletion = !!deletion;
|
||||||
r->sqnum = sqnum;
|
r->sqnum = sqnum;
|
||||||
|
key_copy(c, key, &r->key);
|
||||||
r->nm.len = nlen;
|
r->nm.len = nlen;
|
||||||
memcpy(nbuf, name, nlen);
|
memcpy(nbuf, name, nlen);
|
||||||
nbuf[nlen] = '\0';
|
nbuf[nlen] = '\0';
|
||||||
r->nm.name = nbuf;
|
r->nm.name = nbuf;
|
||||||
r->flags = (deletion ? REPLAY_DELETION : 0);
|
|
||||||
key_copy(c, key, &r->key);
|
|
||||||
|
|
||||||
ubifs_assert(!*p);
|
list_add_tail(&r->list, &c->replay_list);
|
||||||
rb_link_node(&r->rb, parent, p);
|
|
||||||
rb_insert_color(&r->rb, &c->replay_tree);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,30 +472,93 @@ int ubifs_validate_entry(struct ubifs_info *c,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is_last_bud - check if the bud is the last in the journal head.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
* @bud: bud description object
|
||||||
|
*
|
||||||
|
* This function checks if bud @bud is the last bud in its journal head. This
|
||||||
|
* information is then used by 'replay_bud()' to decide whether the bud can
|
||||||
|
* have corruptions or not. Indeed, only last buds can be corrupted by power
|
||||||
|
* cuts. Returns %1 if this is the last bud, and %0 if not.
|
||||||
|
*/
|
||||||
|
static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
|
||||||
|
{
|
||||||
|
struct ubifs_jhead *jh = &c->jheads[bud->jhead];
|
||||||
|
struct ubifs_bud *next;
|
||||||
|
uint32_t data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (list_is_last(&bud->list, &jh->buds_list))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following is a quirk to make sure we work correctly with UBIFS
|
||||||
|
* images used with older UBIFS.
|
||||||
|
*
|
||||||
|
* Normally, the last bud will be the last in the journal head's list
|
||||||
|
* of bud. However, there is one exception if the UBIFS image belongs
|
||||||
|
* to older UBIFS. This is fairly unlikely: one would need to use old
|
||||||
|
* UBIFS, then have a power cut exactly at the right point, and then
|
||||||
|
* try to mount this image with new UBIFS.
|
||||||
|
*
|
||||||
|
* The exception is: it is possible to have 2 buds A and B, A goes
|
||||||
|
* before B, and B is the last, bud B is contains no data, and bud A is
|
||||||
|
* corrupted at the end. The reason is that in older versions when the
|
||||||
|
* journal code switched the next bud (from A to B), it first added a
|
||||||
|
* log reference node for the new bud (B), and only after this it
|
||||||
|
* synchronized the write-buffer of current bud (A). But later this was
|
||||||
|
* changed and UBIFS started to always synchronize the write-buffer of
|
||||||
|
* the bud (A) before writing the log reference for the new bud (B).
|
||||||
|
*
|
||||||
|
* But because older UBIFS always synchronized A's write-buffer before
|
||||||
|
* writing to B, we can recognize this exceptional situation but
|
||||||
|
* checking the contents of bud B - if it is empty, then A can be
|
||||||
|
* treated as the last and we can recover it.
|
||||||
|
*
|
||||||
|
* TODO: remove this piece of code in a couple of years (today it is
|
||||||
|
* 16.05.2011).
|
||||||
|
*/
|
||||||
|
next = list_entry(bud->list.next, struct ubifs_bud, list);
|
||||||
|
if (!list_is_last(&next->list, &jh->buds_list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = ubi_read(c->ubi, next->lnum, (char *)&data,
|
||||||
|
next->start, 4);
|
||||||
|
if (err)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return data == 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* replay_bud - replay a bud logical eraseblock.
|
* replay_bud - replay a bud logical eraseblock.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
* @lnum: bud logical eraseblock number to replay
|
* @b: bud entry which describes the bud
|
||||||
* @offs: bud start offset
|
|
||||||
* @jhead: journal head to which this bud belongs
|
|
||||||
* @free: amount of free space in the bud is returned here
|
|
||||||
* @dirty: amount of dirty space from padding and deletion nodes is returned
|
|
||||||
* here
|
|
||||||
*
|
*
|
||||||
* This function returns zero in case of success and a negative error code in
|
* This function replays bud @bud, recovers it if needed, and adds all nodes
|
||||||
* case of failure.
|
* from this bud to the replay list. Returns zero in case of success and a
|
||||||
|
* negative error code in case of failure.
|
||||||
*/
|
*/
|
||||||
static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
|
static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
|
||||||
int *free, int *dirty)
|
|
||||||
{
|
{
|
||||||
int err = 0, used = 0;
|
int is_last = is_last_bud(c, b->bud);
|
||||||
|
int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
|
||||||
struct ubifs_scan_leb *sleb;
|
struct ubifs_scan_leb *sleb;
|
||||||
struct ubifs_scan_node *snod;
|
struct ubifs_scan_node *snod;
|
||||||
struct ubifs_bud *bud;
|
|
||||||
|
|
||||||
dbg_mnt("replay bud LEB %d, head %d", lnum, jhead);
|
dbg_mnt("replay bud LEB %d, head %d, offs %d, is_last %d",
|
||||||
if (c->need_recovery)
|
lnum, b->bud->jhead, offs, is_last);
|
||||||
sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, jhead != GCHD);
|
|
||||||
|
if (c->need_recovery && is_last)
|
||||||
|
/*
|
||||||
|
* Recover only last LEBs in the journal heads, because power
|
||||||
|
* cuts may cause corruptions only in these LEBs, because only
|
||||||
|
* these LEBs could possibly be written to at the power cut
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf,
|
||||||
|
b->bud->jhead != GCHD);
|
||||||
else
|
else
|
||||||
sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0);
|
sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0);
|
||||||
if (IS_ERR(sleb))
|
if (IS_ERR(sleb))
|
||||||
|
@ -627,15 +674,13 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bud = ubifs_search_bud(c, lnum);
|
ubifs_assert(ubifs_search_bud(c, lnum));
|
||||||
if (!bud)
|
|
||||||
BUG();
|
|
||||||
|
|
||||||
ubifs_assert(sleb->endpt - offs >= used);
|
ubifs_assert(sleb->endpt - offs >= used);
|
||||||
ubifs_assert(sleb->endpt % c->min_io_size == 0);
|
ubifs_assert(sleb->endpt % c->min_io_size == 0);
|
||||||
|
|
||||||
*dirty = sleb->endpt - offs - used;
|
b->dirty = sleb->endpt - offs - used;
|
||||||
*free = c->leb_size - sleb->endpt;
|
b->free = c->leb_size - sleb->endpt;
|
||||||
|
dbg_mnt("bud LEB %d replied: dirty %d, free %d", lnum, b->dirty, b->free);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
ubifs_scan_destroy(sleb);
|
ubifs_scan_destroy(sleb);
|
||||||
|
@ -648,58 +693,6 @@ out_dump:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* insert_ref_node - insert a reference node to the replay tree.
|
|
||||||
* @c: UBIFS file-system description object
|
|
||||||
* @lnum: node logical eraseblock number
|
|
||||||
* @offs: node offset
|
|
||||||
* @sqnum: sequence number
|
|
||||||
* @free: amount of free space in bud
|
|
||||||
* @dirty: amount of dirty space from padding and deletion nodes
|
|
||||||
* @jhead: journal head number for the bud
|
|
||||||
*
|
|
||||||
* This function inserts a reference node to the replay tree and returns zero
|
|
||||||
* in case of success or a negative error code in case of failure.
|
|
||||||
*/
|
|
||||||
static int insert_ref_node(struct ubifs_info *c, int lnum, int offs,
|
|
||||||
unsigned long long sqnum, int free, int dirty,
|
|
||||||
int jhead)
|
|
||||||
{
|
|
||||||
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
|
|
||||||
struct replay_entry *r;
|
|
||||||
|
|
||||||
dbg_mnt("add ref LEB %d:%d", lnum, offs);
|
|
||||||
while (*p) {
|
|
||||||
parent = *p;
|
|
||||||
r = rb_entry(parent, struct replay_entry, rb);
|
|
||||||
if (sqnum < r->sqnum) {
|
|
||||||
p = &(*p)->rb_left;
|
|
||||||
continue;
|
|
||||||
} else if (sqnum > r->sqnum) {
|
|
||||||
p = &(*p)->rb_right;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ubifs_err("duplicate sqnum in replay tree");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
|
||||||
if (!r)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
r->lnum = lnum;
|
|
||||||
r->offs = offs;
|
|
||||||
r->sqnum = sqnum;
|
|
||||||
r->flags = REPLAY_REF;
|
|
||||||
r->free = free;
|
|
||||||
r->dirty = dirty;
|
|
||||||
r->jhead = jhead;
|
|
||||||
|
|
||||||
rb_link_node(&r->rb, parent, p);
|
|
||||||
rb_insert_color(&r->rb, &c->replay_tree);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* replay_buds - replay all buds.
|
* replay_buds - replay all buds.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
|
@ -710,17 +703,16 @@ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs,
|
||||||
static int replay_buds(struct ubifs_info *c)
|
static int replay_buds(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
struct bud_entry *b;
|
struct bud_entry *b;
|
||||||
int err, uninitialized_var(free), uninitialized_var(dirty);
|
int err;
|
||||||
|
unsigned long long prev_sqnum = 0;
|
||||||
|
|
||||||
list_for_each_entry(b, &c->replay_buds, list) {
|
list_for_each_entry(b, &c->replay_buds, list) {
|
||||||
err = replay_bud(c, b->bud->lnum, b->bud->start, b->bud->jhead,
|
err = replay_bud(c, b);
|
||||||
&free, &dirty);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum,
|
|
||||||
free, dirty, b->bud->jhead);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
ubifs_assert(b->sqnum > prev_sqnum);
|
||||||
|
prev_sqnum = b->sqnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1060,25 +1052,29 @@ int ubifs_replay_journal(struct ubifs_info *c)
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err = apply_replay_tree(c);
|
err = apply_replay_list(c);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = set_buds_lprops(c);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UBIFS budgeting calculations use @c->budg_uncommitted_idx variable
|
* UBIFS budgeting calculations use @c->bi.uncommitted_idx variable
|
||||||
* to roughly estimate index growth. Things like @c->min_idx_lebs
|
* to roughly estimate index growth. Things like @c->bi.min_idx_lebs
|
||||||
* depend on it. This means we have to initialize it to make sure
|
* depend on it. This means we have to initialize it to make sure
|
||||||
* budgeting works properly.
|
* budgeting works properly.
|
||||||
*/
|
*/
|
||||||
c->budg_uncommitted_idx = atomic_long_read(&c->dirty_zn_cnt);
|
c->bi.uncommitted_idx = atomic_long_read(&c->dirty_zn_cnt);
|
||||||
c->budg_uncommitted_idx *= c->max_idx_node_sz;
|
c->bi.uncommitted_idx *= c->max_idx_node_sz;
|
||||||
|
|
||||||
ubifs_assert(c->bud_bytes <= c->max_bud_bytes || c->need_recovery);
|
ubifs_assert(c->bud_bytes <= c->max_bud_bytes || c->need_recovery);
|
||||||
dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, "
|
dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, "
|
||||||
"highest_inum %lu", c->lhead_lnum, c->lhead_offs, c->max_sqnum,
|
"highest_inum %lu", c->lhead_lnum, c->lhead_offs, c->max_sqnum,
|
||||||
(unsigned long)c->highest_inum);
|
(unsigned long)c->highest_inum);
|
||||||
out:
|
out:
|
||||||
destroy_replay_tree(c);
|
destroy_replay_list(c);
|
||||||
destroy_bud_list(c);
|
destroy_bud_list(c);
|
||||||
c->replaying = 0;
|
c->replaying = 0;
|
||||||
return err;
|
return err;
|
||||||
|
|
153
fs/ubifs/sb.c
153
fs/ubifs/sb.c
|
@ -475,7 +475,8 @@ failed:
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
*
|
*
|
||||||
* This function returns a pointer to the superblock node or a negative error
|
* This function returns a pointer to the superblock node or a negative error
|
||||||
* code.
|
* code. Note, the user of this function is responsible of kfree()'ing the
|
||||||
|
* returned superblock buffer.
|
||||||
*/
|
*/
|
||||||
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
|
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
|
@ -616,6 +617,7 @@ int ubifs_read_superblock(struct ubifs_info *c)
|
||||||
c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
|
c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
|
||||||
memcpy(&c->uuid, &sup->uuid, 16);
|
memcpy(&c->uuid, &sup->uuid, 16);
|
||||||
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
|
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
|
||||||
|
c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
|
||||||
|
|
||||||
/* Automatically increase file system size to the maximum size */
|
/* Automatically increase file system size to the maximum size */
|
||||||
c->old_leb_cnt = c->leb_cnt;
|
c->old_leb_cnt = c->leb_cnt;
|
||||||
|
@ -650,3 +652,152 @@ out:
|
||||||
kfree(sup);
|
kfree(sup);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fixup_leb - fixup/unmap an LEB containing free space.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
* @lnum: the LEB number to fix up
|
||||||
|
* @len: number of used bytes in LEB (starting at offset 0)
|
||||||
|
*
|
||||||
|
* This function reads the contents of the given LEB number @lnum, then fixes
|
||||||
|
* it up, so that empty min. I/O units in the end of LEB are actually erased on
|
||||||
|
* flash (rather than being just all-0xff real data). If the LEB is completely
|
||||||
|
* empty, it is simply unmapped.
|
||||||
|
*/
|
||||||
|
static int fixup_leb(struct ubifs_info *c, int lnum, int len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ubifs_assert(len >= 0);
|
||||||
|
ubifs_assert(len % c->min_io_size == 0);
|
||||||
|
ubifs_assert(len < c->leb_size);
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
dbg_mnt("unmap empty LEB %d", lnum);
|
||||||
|
return ubi_leb_unmap(c->ubi, lnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg_mnt("fixup LEB %d, data len %d", lnum, len);
|
||||||
|
err = ubi_read(c->ubi, lnum, c->sbuf, 0, len);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ubi_leb_change(c->ubi, lnum, c->sbuf, len, UBI_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fixup_free_space - find & remap all LEBs containing free space.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* This function walks through all LEBs in the filesystem and fiexes up those
|
||||||
|
* containing free/empty space.
|
||||||
|
*/
|
||||||
|
static int fixup_free_space(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
int lnum, err = 0;
|
||||||
|
struct ubifs_lprops *lprops;
|
||||||
|
|
||||||
|
ubifs_get_lprops(c);
|
||||||
|
|
||||||
|
/* Fixup LEBs in the master area */
|
||||||
|
for (lnum = UBIFS_MST_LNUM; lnum < UBIFS_LOG_LNUM; lnum++) {
|
||||||
|
err = fixup_leb(c, lnum, c->mst_offs + c->mst_node_alsz);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unmap unused log LEBs */
|
||||||
|
lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||||
|
while (lnum != c->ltail_lnum) {
|
||||||
|
err = fixup_leb(c, lnum, 0);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
lnum = ubifs_next_log_lnum(c, lnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixup the current log head */
|
||||||
|
err = fixup_leb(c, c->lhead_lnum, c->lhead_offs);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Fixup LEBs in the LPT area */
|
||||||
|
for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
|
||||||
|
int free = c->ltab[lnum - c->lpt_first].free;
|
||||||
|
|
||||||
|
if (free > 0) {
|
||||||
|
err = fixup_leb(c, lnum, c->leb_size - free);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unmap LEBs in the orphans area */
|
||||||
|
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
|
||||||
|
err = fixup_leb(c, lnum, 0);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixup LEBs in the main area */
|
||||||
|
for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
|
||||||
|
lprops = ubifs_lpt_lookup(c, lnum);
|
||||||
|
if (IS_ERR(lprops)) {
|
||||||
|
err = PTR_ERR(lprops);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lprops->free > 0) {
|
||||||
|
err = fixup_leb(c, lnum, c->leb_size - lprops->free);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
ubifs_release_lprops(c);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ubifs_fixup_free_space - find & fix all LEBs with free space.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* This function fixes up LEBs containing free space on first mount, if the
|
||||||
|
* appropriate flag was set when the FS was created. Each LEB with one or more
|
||||||
|
* empty min. I/O unit (i.e. free-space-count > 0) is re-written, to make sure
|
||||||
|
* the free space is actually erased. E.g., this is necessary for some NAND
|
||||||
|
* chips, since the free space may have been programmed like real "0xff" data
|
||||||
|
* (generating a non-0xff ECC), causing future writes to the not-really-erased
|
||||||
|
* NAND pages to behave badly. After the space is fixed up, the superblock flag
|
||||||
|
* is cleared, so that this is skipped for all future mounts.
|
||||||
|
*/
|
||||||
|
int ubifs_fixup_free_space(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct ubifs_sb_node *sup;
|
||||||
|
|
||||||
|
ubifs_assert(c->space_fixup);
|
||||||
|
ubifs_assert(!c->ro_mount);
|
||||||
|
|
||||||
|
ubifs_msg("start fixing up free space");
|
||||||
|
|
||||||
|
err = fixup_free_space(c);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
sup = ubifs_read_sb_node(c);
|
||||||
|
if (IS_ERR(sup))
|
||||||
|
return PTR_ERR(sup);
|
||||||
|
|
||||||
|
/* Free-space fixup is no longer required */
|
||||||
|
c->space_fixup = 0;
|
||||||
|
sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
|
||||||
|
|
||||||
|
err = ubifs_write_sb_node(c, sup);
|
||||||
|
kfree(sup);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ubifs_msg("free space fixup complete");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
|
@ -375,7 +375,7 @@ out:
|
||||||
ubifs_release_dirty_inode_budget(c, ui);
|
ubifs_release_dirty_inode_budget(c, ui);
|
||||||
else {
|
else {
|
||||||
/* We've deleted something - clean the "no space" flags */
|
/* We've deleted something - clean the "no space" flags */
|
||||||
c->nospace = c->nospace_rp = 0;
|
c->bi.nospace = c->bi.nospace_rp = 0;
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
@ -694,11 +694,11 @@ static int init_constants_sb(struct ubifs_info *c)
|
||||||
* be compressed and direntries are of the maximum size.
|
* be compressed and direntries are of the maximum size.
|
||||||
*
|
*
|
||||||
* Note, data, which may be stored in inodes is budgeted separately, so
|
* Note, data, which may be stored in inodes is budgeted separately, so
|
||||||
* it is not included into 'c->inode_budget'.
|
* it is not included into 'c->bi.inode_budget'.
|
||||||
*/
|
*/
|
||||||
c->page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE;
|
c->bi.page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE;
|
||||||
c->inode_budget = UBIFS_INO_NODE_SZ;
|
c->bi.inode_budget = UBIFS_INO_NODE_SZ;
|
||||||
c->dent_budget = UBIFS_MAX_DENT_NODE_SZ;
|
c->bi.dent_budget = UBIFS_MAX_DENT_NODE_SZ;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When the amount of flash space used by buds becomes
|
* When the amount of flash space used by buds becomes
|
||||||
|
@ -742,7 +742,7 @@ static void init_constants_master(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
long long tmp64;
|
long long tmp64;
|
||||||
|
|
||||||
c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||||
c->report_rp_size = ubifs_reported_space(c, c->rp_size);
|
c->report_rp_size = ubifs_reported_space(c, c->rp_size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1144,8 +1144,8 @@ static int check_free_space(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
ubifs_assert(c->dark_wm > 0);
|
ubifs_assert(c->dark_wm > 0);
|
||||||
if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) {
|
if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) {
|
||||||
ubifs_err("insufficient free space to mount in read/write mode");
|
ubifs_err("insufficient free space to mount in R/W mode");
|
||||||
dbg_dump_budg(c);
|
dbg_dump_budg(c, &c->bi);
|
||||||
dbg_dump_lprops(c);
|
dbg_dump_lprops(c);
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
@ -1304,7 +1304,7 @@ static int mount_ubifs(struct ubifs_info *c)
|
||||||
if (err)
|
if (err)
|
||||||
goto out_lpt;
|
goto out_lpt;
|
||||||
|
|
||||||
err = dbg_check_idx_size(c, c->old_idx_sz);
|
err = dbg_check_idx_size(c, c->bi.old_idx_sz);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_lpt;
|
goto out_lpt;
|
||||||
|
|
||||||
|
@ -1313,7 +1313,7 @@ static int mount_ubifs(struct ubifs_info *c)
|
||||||
goto out_journal;
|
goto out_journal;
|
||||||
|
|
||||||
/* Calculate 'min_idx_lebs' after journal replay */
|
/* Calculate 'min_idx_lebs' after journal replay */
|
||||||
c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||||
|
|
||||||
err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
|
err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -1396,6 +1396,12 @@ static int mount_ubifs(struct ubifs_info *c)
|
||||||
} else
|
} else
|
||||||
ubifs_assert(c->lst.taken_empty_lebs > 0);
|
ubifs_assert(c->lst.taken_empty_lebs > 0);
|
||||||
|
|
||||||
|
if (!c->ro_mount && c->space_fixup) {
|
||||||
|
err = ubifs_fixup_free_space(c);
|
||||||
|
if (err)
|
||||||
|
goto out_infos;
|
||||||
|
}
|
||||||
|
|
||||||
err = dbg_check_filesystem(c);
|
err = dbg_check_filesystem(c);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_infos;
|
goto out_infos;
|
||||||
|
@ -1442,7 +1448,8 @@ static int mount_ubifs(struct ubifs_info *c)
|
||||||
c->main_lebs, c->main_first, c->leb_cnt - 1);
|
c->main_lebs, c->main_first, c->leb_cnt - 1);
|
||||||
dbg_msg("index LEBs: %d", c->lst.idx_lebs);
|
dbg_msg("index LEBs: %d", c->lst.idx_lebs);
|
||||||
dbg_msg("total index bytes: %lld (%lld KiB, %lld MiB)",
|
dbg_msg("total index bytes: %lld (%lld KiB, %lld MiB)",
|
||||||
c->old_idx_sz, c->old_idx_sz >> 10, c->old_idx_sz >> 20);
|
c->bi.old_idx_sz, c->bi.old_idx_sz >> 10,
|
||||||
|
c->bi.old_idx_sz >> 20);
|
||||||
dbg_msg("key hash type: %d", c->key_hash_type);
|
dbg_msg("key hash type: %d", c->key_hash_type);
|
||||||
dbg_msg("tree fanout: %d", c->fanout);
|
dbg_msg("tree fanout: %d", c->fanout);
|
||||||
dbg_msg("reserved GC LEB: %d", c->gc_lnum);
|
dbg_msg("reserved GC LEB: %d", c->gc_lnum);
|
||||||
|
@ -1456,7 +1463,7 @@ static int mount_ubifs(struct ubifs_info *c)
|
||||||
dbg_msg("node sizes: ref %zu, cmt. start %zu, orph %zu",
|
dbg_msg("node sizes: ref %zu, cmt. start %zu, orph %zu",
|
||||||
UBIFS_REF_NODE_SZ, UBIFS_CS_NODE_SZ, UBIFS_ORPH_NODE_SZ);
|
UBIFS_REF_NODE_SZ, UBIFS_CS_NODE_SZ, UBIFS_ORPH_NODE_SZ);
|
||||||
dbg_msg("max. node sizes: data %zu, inode %zu dentry %zu, idx %d",
|
dbg_msg("max. node sizes: data %zu, inode %zu dentry %zu, idx %d",
|
||||||
UBIFS_MAX_DATA_NODE_SZ, UBIFS_MAX_INO_NODE_SZ,
|
UBIFS_MAX_DATA_NODE_SZ, UBIFS_MAX_INO_NODE_SZ,
|
||||||
UBIFS_MAX_DENT_NODE_SZ, ubifs_idx_node_sz(c, c->fanout));
|
UBIFS_MAX_DENT_NODE_SZ, ubifs_idx_node_sz(c, c->fanout));
|
||||||
dbg_msg("dead watermark: %d", c->dead_wm);
|
dbg_msg("dead watermark: %d", c->dead_wm);
|
||||||
dbg_msg("dark watermark: %d", c->dark_wm);
|
dbg_msg("dark watermark: %d", c->dark_wm);
|
||||||
|
@ -1584,6 +1591,7 @@ static int ubifs_remount_rw(struct ubifs_info *c)
|
||||||
}
|
}
|
||||||
sup->leb_cnt = cpu_to_le32(c->leb_cnt);
|
sup->leb_cnt = cpu_to_le32(c->leb_cnt);
|
||||||
err = ubifs_write_sb_node(c, sup);
|
err = ubifs_write_sb_node(c, sup);
|
||||||
|
kfree(sup);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -1684,6 +1692,13 @@ static int ubifs_remount_rw(struct ubifs_info *c)
|
||||||
*/
|
*/
|
||||||
err = dbg_check_space_info(c);
|
err = dbg_check_space_info(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c->space_fixup) {
|
||||||
|
err = ubifs_fixup_free_space(c);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&c->umount_mutex);
|
mutex_unlock(&c->umount_mutex);
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1766,10 +1781,9 @@ static void ubifs_put_super(struct super_block *sb)
|
||||||
* to write them back because of I/O errors.
|
* to write them back because of I/O errors.
|
||||||
*/
|
*/
|
||||||
if (!c->ro_error) {
|
if (!c->ro_error) {
|
||||||
ubifs_assert(atomic_long_read(&c->dirty_pg_cnt) == 0);
|
ubifs_assert(c->bi.idx_growth == 0);
|
||||||
ubifs_assert(c->budg_idx_growth == 0);
|
ubifs_assert(c->bi.dd_growth == 0);
|
||||||
ubifs_assert(c->budg_dd_growth == 0);
|
ubifs_assert(c->bi.data_growth == 0);
|
||||||
ubifs_assert(c->budg_data_growth == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -2557,11 +2557,11 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
|
||||||
if (err) {
|
if (err) {
|
||||||
/* Ensure the znode is dirtied */
|
/* Ensure the znode is dirtied */
|
||||||
if (znode->cnext || !ubifs_zn_dirty(znode)) {
|
if (znode->cnext || !ubifs_zn_dirty(znode)) {
|
||||||
znode = dirty_cow_bottom_up(c, znode);
|
znode = dirty_cow_bottom_up(c, znode);
|
||||||
if (IS_ERR(znode)) {
|
if (IS_ERR(znode)) {
|
||||||
err = PTR_ERR(znode);
|
err = PTR_ERR(znode);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = tnc_delete(c, znode, n);
|
err = tnc_delete(c, znode, n);
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,15 +377,13 @@ static int layout_in_gaps(struct ubifs_info *c, int cnt)
|
||||||
c->gap_lebs = NULL;
|
c->gap_lebs = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (!dbg_force_in_the_gaps_enabled) {
|
if (dbg_force_in_the_gaps_enabled()) {
|
||||||
/*
|
/*
|
||||||
* Do not print scary warnings if the debugging
|
* Do not print scary warnings if the debugging
|
||||||
* option which forces in-the-gaps is enabled.
|
* option which forces in-the-gaps is enabled.
|
||||||
*/
|
*/
|
||||||
ubifs_err("out of space");
|
ubifs_warn("out of space");
|
||||||
spin_lock(&c->space_lock);
|
dbg_dump_budg(c, &c->bi);
|
||||||
dbg_dump_budg(c);
|
|
||||||
spin_unlock(&c->space_lock);
|
|
||||||
dbg_dump_lprops(c);
|
dbg_dump_lprops(c);
|
||||||
}
|
}
|
||||||
/* Try to commit anyway */
|
/* Try to commit anyway */
|
||||||
|
@ -796,16 +794,16 @@ int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot)
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
/*
|
/*
|
||||||
* Although we have not finished committing yet, update size of the
|
* Although we have not finished committing yet, update size of the
|
||||||
* committed index ('c->old_idx_sz') and zero out the index growth
|
* committed index ('c->bi.old_idx_sz') and zero out the index growth
|
||||||
* budget. It is OK to do this now, because we've reserved all the
|
* budget. It is OK to do this now, because we've reserved all the
|
||||||
* space which is needed to commit the index, and it is save for the
|
* space which is needed to commit the index, and it is save for the
|
||||||
* budgeting subsystem to assume the index is already committed,
|
* budgeting subsystem to assume the index is already committed,
|
||||||
* even though it is not.
|
* even though it is not.
|
||||||
*/
|
*/
|
||||||
ubifs_assert(c->min_idx_lebs == ubifs_calc_min_idx_lebs(c));
|
ubifs_assert(c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
|
||||||
c->old_idx_sz = c->calc_idx_sz;
|
c->bi.old_idx_sz = c->calc_idx_sz;
|
||||||
c->budg_uncommitted_idx = 0;
|
c->bi.uncommitted_idx = 0;
|
||||||
c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||||
spin_unlock(&c->space_lock);
|
spin_unlock(&c->space_lock);
|
||||||
mutex_unlock(&c->tnc_mutex);
|
mutex_unlock(&c->tnc_mutex);
|
||||||
|
|
||||||
|
|
|
@ -408,9 +408,11 @@ enum {
|
||||||
* Superblock flags.
|
* Superblock flags.
|
||||||
*
|
*
|
||||||
* UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
|
* UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
|
||||||
|
* UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
UBIFS_FLG_BIGLPT = 0x02,
|
UBIFS_FLG_BIGLPT = 0x02,
|
||||||
|
UBIFS_FLG_SPACE_FIXUP = 0x04,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -434,7 +436,7 @@ struct ubifs_ch {
|
||||||
__u8 node_type;
|
__u8 node_type;
|
||||||
__u8 group_type;
|
__u8 group_type;
|
||||||
__u8 padding[2];
|
__u8 padding[2];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* union ubifs_dev_desc - device node descriptor.
|
* union ubifs_dev_desc - device node descriptor.
|
||||||
|
@ -448,7 +450,7 @@ struct ubifs_ch {
|
||||||
union ubifs_dev_desc {
|
union ubifs_dev_desc {
|
||||||
__le32 new;
|
__le32 new;
|
||||||
__le64 huge;
|
__le64 huge;
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_ino_node - inode node.
|
* struct ubifs_ino_node - inode node.
|
||||||
|
@ -509,7 +511,7 @@ struct ubifs_ino_node {
|
||||||
__le16 compr_type;
|
__le16 compr_type;
|
||||||
__u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */
|
__u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */
|
||||||
__u8 data[];
|
__u8 data[];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_dent_node - directory entry node.
|
* struct ubifs_dent_node - directory entry node.
|
||||||
|
@ -534,7 +536,7 @@ struct ubifs_dent_node {
|
||||||
__le16 nlen;
|
__le16 nlen;
|
||||||
__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
|
__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
|
||||||
__u8 name[];
|
__u8 name[];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_data_node - data node.
|
* struct ubifs_data_node - data node.
|
||||||
|
@ -555,7 +557,7 @@ struct ubifs_data_node {
|
||||||
__le16 compr_type;
|
__le16 compr_type;
|
||||||
__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
|
__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
|
||||||
__u8 data[];
|
__u8 data[];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_trun_node - truncation node.
|
* struct ubifs_trun_node - truncation node.
|
||||||
|
@ -575,7 +577,7 @@ struct ubifs_trun_node {
|
||||||
__u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */
|
__u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */
|
||||||
__le64 old_size;
|
__le64 old_size;
|
||||||
__le64 new_size;
|
__le64 new_size;
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_pad_node - padding node.
|
* struct ubifs_pad_node - padding node.
|
||||||
|
@ -586,7 +588,7 @@ struct ubifs_trun_node {
|
||||||
struct ubifs_pad_node {
|
struct ubifs_pad_node {
|
||||||
struct ubifs_ch ch;
|
struct ubifs_ch ch;
|
||||||
__le32 pad_len;
|
__le32 pad_len;
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_sb_node - superblock node.
|
* struct ubifs_sb_node - superblock node.
|
||||||
|
@ -644,7 +646,7 @@ struct ubifs_sb_node {
|
||||||
__u8 uuid[16];
|
__u8 uuid[16];
|
||||||
__le32 ro_compat_version;
|
__le32 ro_compat_version;
|
||||||
__u8 padding2[3968];
|
__u8 padding2[3968];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_mst_node - master node.
|
* struct ubifs_mst_node - master node.
|
||||||
|
@ -711,7 +713,7 @@ struct ubifs_mst_node {
|
||||||
__le32 idx_lebs;
|
__le32 idx_lebs;
|
||||||
__le32 leb_cnt;
|
__le32 leb_cnt;
|
||||||
__u8 padding[344];
|
__u8 padding[344];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_ref_node - logical eraseblock reference node.
|
* struct ubifs_ref_node - logical eraseblock reference node.
|
||||||
|
@ -727,7 +729,7 @@ struct ubifs_ref_node {
|
||||||
__le32 offs;
|
__le32 offs;
|
||||||
__le32 jhead;
|
__le32 jhead;
|
||||||
__u8 padding[28];
|
__u8 padding[28];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_branch - key/reference/length branch
|
* struct ubifs_branch - key/reference/length branch
|
||||||
|
@ -741,7 +743,7 @@ struct ubifs_branch {
|
||||||
__le32 offs;
|
__le32 offs;
|
||||||
__le32 len;
|
__le32 len;
|
||||||
__u8 key[];
|
__u8 key[];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_idx_node - indexing node.
|
* struct ubifs_idx_node - indexing node.
|
||||||
|
@ -755,7 +757,7 @@ struct ubifs_idx_node {
|
||||||
__le16 child_cnt;
|
__le16 child_cnt;
|
||||||
__le16 level;
|
__le16 level;
|
||||||
__u8 branches[];
|
__u8 branches[];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_cs_node - commit start node.
|
* struct ubifs_cs_node - commit start node.
|
||||||
|
@ -765,7 +767,7 @@ struct ubifs_idx_node {
|
||||||
struct ubifs_cs_node {
|
struct ubifs_cs_node {
|
||||||
struct ubifs_ch ch;
|
struct ubifs_ch ch;
|
||||||
__le64 cmt_no;
|
__le64 cmt_no;
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ubifs_orph_node - orphan node.
|
* struct ubifs_orph_node - orphan node.
|
||||||
|
@ -777,6 +779,6 @@ struct ubifs_orph_node {
|
||||||
struct ubifs_ch ch;
|
struct ubifs_ch ch;
|
||||||
__le64 cmt_no;
|
__le64 cmt_no;
|
||||||
__le64 inos[];
|
__le64 inos[];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
#endif /* __UBIFS_MEDIA_H__ */
|
#endif /* __UBIFS_MEDIA_H__ */
|
||||||
|
|
|
@ -389,9 +389,9 @@ struct ubifs_gced_idx_leb {
|
||||||
* The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses
|
* The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses
|
||||||
* @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot
|
* @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot
|
||||||
* make sure @inode->i_size is always changed under @ui_mutex, because it
|
* make sure @inode->i_size is always changed under @ui_mutex, because it
|
||||||
* cannot call 'truncate_setsize()' with @ui_mutex locked, because it would deadlock
|
* cannot call 'truncate_setsize()' with @ui_mutex locked, because it would
|
||||||
* with 'ubifs_writepage()' (see file.c). All the other inode fields are
|
* deadlock with 'ubifs_writepage()' (see file.c). All the other inode fields
|
||||||
* changed under @ui_mutex, so they do not need "shadow" fields. Note, one
|
* are changed under @ui_mutex, so they do not need "shadow" fields. Note, one
|
||||||
* could consider to rework locking and base it on "shadow" fields.
|
* could consider to rework locking and base it on "shadow" fields.
|
||||||
*/
|
*/
|
||||||
struct ubifs_inode {
|
struct ubifs_inode {
|
||||||
|
@ -937,6 +937,40 @@ struct ubifs_mount_opts {
|
||||||
unsigned int compr_type:2;
|
unsigned int compr_type:2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ubifs_budg_info - UBIFS budgeting information.
|
||||||
|
* @idx_growth: amount of bytes budgeted for index growth
|
||||||
|
* @data_growth: amount of bytes budgeted for cached data
|
||||||
|
* @dd_growth: amount of bytes budgeted for cached data that will make
|
||||||
|
* other data dirty
|
||||||
|
* @uncommitted_idx: amount of bytes were budgeted for growth of the index, but
|
||||||
|
* which still have to be taken into account because the index
|
||||||
|
* has not been committed so far
|
||||||
|
* @old_idx_sz: size of index on flash
|
||||||
|
* @min_idx_lebs: minimum number of LEBs required for the index
|
||||||
|
* @nospace: non-zero if the file-system does not have flash space (used as
|
||||||
|
* optimization)
|
||||||
|
* @nospace_rp: the same as @nospace, but additionally means that even reserved
|
||||||
|
* pool is full
|
||||||
|
* @page_budget: budget for a page (constant, nenver changed after mount)
|
||||||
|
* @inode_budget: budget for an inode (constant, nenver changed after mount)
|
||||||
|
* @dent_budget: budget for a directory entry (constant, nenver changed after
|
||||||
|
* mount)
|
||||||
|
*/
|
||||||
|
struct ubifs_budg_info {
|
||||||
|
long long idx_growth;
|
||||||
|
long long data_growth;
|
||||||
|
long long dd_growth;
|
||||||
|
long long uncommitted_idx;
|
||||||
|
unsigned long long old_idx_sz;
|
||||||
|
int min_idx_lebs;
|
||||||
|
unsigned int nospace:1;
|
||||||
|
unsigned int nospace_rp:1;
|
||||||
|
int page_budget;
|
||||||
|
int inode_budget;
|
||||||
|
int dent_budget;
|
||||||
|
};
|
||||||
|
|
||||||
struct ubifs_debug_info;
|
struct ubifs_debug_info;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -980,6 +1014,7 @@ struct ubifs_debug_info;
|
||||||
* @cmt_wq: wait queue to sleep on if the log is full and a commit is running
|
* @cmt_wq: wait queue to sleep on if the log is full and a commit is running
|
||||||
*
|
*
|
||||||
* @big_lpt: flag that LPT is too big to write whole during commit
|
* @big_lpt: flag that LPT is too big to write whole during commit
|
||||||
|
* @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
|
||||||
* @no_chk_data_crc: do not check CRCs when reading data nodes (except during
|
* @no_chk_data_crc: do not check CRCs when reading data nodes (except during
|
||||||
* recovery)
|
* recovery)
|
||||||
* @bulk_read: enable bulk-reads
|
* @bulk_read: enable bulk-reads
|
||||||
|
@ -1057,32 +1092,14 @@ struct ubifs_debug_info;
|
||||||
* @dirty_zn_cnt: number of dirty znodes
|
* @dirty_zn_cnt: number of dirty znodes
|
||||||
* @clean_zn_cnt: number of clean znodes
|
* @clean_zn_cnt: number of clean znodes
|
||||||
*
|
*
|
||||||
* @budg_idx_growth: amount of bytes budgeted for index growth
|
* @space_lock: protects @bi and @lst
|
||||||
* @budg_data_growth: amount of bytes budgeted for cached data
|
* @lst: lprops statistics
|
||||||
* @budg_dd_growth: amount of bytes budgeted for cached data that will make
|
* @bi: budgeting information
|
||||||
* other data dirty
|
|
||||||
* @budg_uncommitted_idx: amount of bytes were budgeted for growth of the index,
|
|
||||||
* but which still have to be taken into account because
|
|
||||||
* the index has not been committed so far
|
|
||||||
* @space_lock: protects @budg_idx_growth, @budg_data_growth, @budg_dd_growth,
|
|
||||||
* @budg_uncommited_idx, @min_idx_lebs, @old_idx_sz, @lst,
|
|
||||||
* @nospace, and @nospace_rp;
|
|
||||||
* @min_idx_lebs: minimum number of LEBs required for the index
|
|
||||||
* @old_idx_sz: size of index on flash
|
|
||||||
* @calc_idx_sz: temporary variable which is used to calculate new index size
|
* @calc_idx_sz: temporary variable which is used to calculate new index size
|
||||||
* (contains accurate new index size at end of TNC commit start)
|
* (contains accurate new index size at end of TNC commit start)
|
||||||
* @lst: lprops statistics
|
|
||||||
* @nospace: non-zero if the file-system does not have flash space (used as
|
|
||||||
* optimization)
|
|
||||||
* @nospace_rp: the same as @nospace, but additionally means that even reserved
|
|
||||||
* pool is full
|
|
||||||
*
|
|
||||||
* @page_budget: budget for a page
|
|
||||||
* @inode_budget: budget for an inode
|
|
||||||
* @dent_budget: budget for a directory entry
|
|
||||||
*
|
*
|
||||||
* @ref_node_alsz: size of the LEB reference node aligned to the min. flash
|
* @ref_node_alsz: size of the LEB reference node aligned to the min. flash
|
||||||
* I/O unit
|
* I/O unit
|
||||||
* @mst_node_alsz: master node aligned size
|
* @mst_node_alsz: master node aligned size
|
||||||
* @min_idx_node_sz: minimum indexing node aligned on 8-bytes boundary
|
* @min_idx_node_sz: minimum indexing node aligned on 8-bytes boundary
|
||||||
* @max_idx_node_sz: maximum indexing node aligned on 8-bytes boundary
|
* @max_idx_node_sz: maximum indexing node aligned on 8-bytes boundary
|
||||||
|
@ -1189,7 +1206,6 @@ struct ubifs_debug_info;
|
||||||
* @replaying: %1 during journal replay
|
* @replaying: %1 during journal replay
|
||||||
* @mounting: %1 while mounting
|
* @mounting: %1 while mounting
|
||||||
* @remounting_rw: %1 while re-mounting from R/O mode to R/W mode
|
* @remounting_rw: %1 while re-mounting from R/O mode to R/W mode
|
||||||
* @replay_tree: temporary tree used during journal replay
|
|
||||||
* @replay_list: temporary list used during journal replay
|
* @replay_list: temporary list used during journal replay
|
||||||
* @replay_buds: list of buds to replay
|
* @replay_buds: list of buds to replay
|
||||||
* @cs_sqnum: sequence number of first node in the log (commit start node)
|
* @cs_sqnum: sequence number of first node in the log (commit start node)
|
||||||
|
@ -1238,6 +1254,7 @@ struct ubifs_info {
|
||||||
wait_queue_head_t cmt_wq;
|
wait_queue_head_t cmt_wq;
|
||||||
|
|
||||||
unsigned int big_lpt:1;
|
unsigned int big_lpt:1;
|
||||||
|
unsigned int space_fixup:1;
|
||||||
unsigned int no_chk_data_crc:1;
|
unsigned int no_chk_data_crc:1;
|
||||||
unsigned int bulk_read:1;
|
unsigned int bulk_read:1;
|
||||||
unsigned int default_compr:2;
|
unsigned int default_compr:2;
|
||||||
|
@ -1308,21 +1325,10 @@ struct ubifs_info {
|
||||||
atomic_long_t dirty_zn_cnt;
|
atomic_long_t dirty_zn_cnt;
|
||||||
atomic_long_t clean_zn_cnt;
|
atomic_long_t clean_zn_cnt;
|
||||||
|
|
||||||
long long budg_idx_growth;
|
|
||||||
long long budg_data_growth;
|
|
||||||
long long budg_dd_growth;
|
|
||||||
long long budg_uncommitted_idx;
|
|
||||||
spinlock_t space_lock;
|
spinlock_t space_lock;
|
||||||
int min_idx_lebs;
|
|
||||||
unsigned long long old_idx_sz;
|
|
||||||
unsigned long long calc_idx_sz;
|
|
||||||
struct ubifs_lp_stats lst;
|
struct ubifs_lp_stats lst;
|
||||||
unsigned int nospace:1;
|
struct ubifs_budg_info bi;
|
||||||
unsigned int nospace_rp:1;
|
unsigned long long calc_idx_sz;
|
||||||
|
|
||||||
int page_budget;
|
|
||||||
int inode_budget;
|
|
||||||
int dent_budget;
|
|
||||||
|
|
||||||
int ref_node_alsz;
|
int ref_node_alsz;
|
||||||
int mst_node_alsz;
|
int mst_node_alsz;
|
||||||
|
@ -1430,7 +1436,6 @@ struct ubifs_info {
|
||||||
unsigned int replaying:1;
|
unsigned int replaying:1;
|
||||||
unsigned int mounting:1;
|
unsigned int mounting:1;
|
||||||
unsigned int remounting_rw:1;
|
unsigned int remounting_rw:1;
|
||||||
struct rb_root replay_tree;
|
|
||||||
struct list_head replay_list;
|
struct list_head replay_list;
|
||||||
struct list_head replay_buds;
|
struct list_head replay_buds;
|
||||||
unsigned long long cs_sqnum;
|
unsigned long long cs_sqnum;
|
||||||
|
@ -1628,6 +1633,7 @@ int ubifs_write_master(struct ubifs_info *c);
|
||||||
int ubifs_read_superblock(struct ubifs_info *c);
|
int ubifs_read_superblock(struct ubifs_info *c);
|
||||||
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c);
|
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c);
|
||||||
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
|
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
|
||||||
|
int ubifs_fixup_free_space(struct ubifs_info *c);
|
||||||
|
|
||||||
/* replay.c */
|
/* replay.c */
|
||||||
int ubifs_validate_entry(struct ubifs_info *c,
|
int ubifs_validate_entry(struct ubifs_info *c,
|
||||||
|
|
|
@ -80,8 +80,8 @@ enum {
|
||||||
SECURITY_XATTR,
|
SECURITY_XATTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct inode_operations none_inode_operations;
|
static const struct inode_operations empty_iops;
|
||||||
static const struct file_operations none_file_operations;
|
static const struct file_operations empty_fops;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create_xattr - create an extended attribute.
|
* create_xattr - create an extended attribute.
|
||||||
|
@ -131,8 +131,8 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
|
||||||
|
|
||||||
/* Re-define all operations to be "nothing" */
|
/* Re-define all operations to be "nothing" */
|
||||||
inode->i_mapping->a_ops = &empty_aops;
|
inode->i_mapping->a_ops = &empty_aops;
|
||||||
inode->i_op = &none_inode_operations;
|
inode->i_op = &empty_iops;
|
||||||
inode->i_fop = &none_file_operations;
|
inode->i_fop = &empty_fops;
|
||||||
|
|
||||||
inode->i_flags |= S_SYNC | S_NOATIME | S_NOCMTIME | S_NOQUOTA;
|
inode->i_flags |= S_SYNC | S_NOATIME | S_NOCMTIME | S_NOQUOTA;
|
||||||
ui = ubifs_inode(inode);
|
ui = ubifs_inode(inode);
|
||||||
|
|
Loading…
Reference in New Issue