UBIFS: do not start the commit if there is nothing to commit
This patch fixes suboptimal UBIFS 'sync_fs()' implementation which causes flash I/O even if the file-system is synchronized. E.g., a 'printk()' in the MTD erasure function (e.g., 'nand_erase_nand()') can show that for every 'sync' shell command UBIFS erases at least one eraseblock. So '$ while true; do sync; done' will cause huge amount of flash I/O. The reason for this is that UBIFS commits in 'sync_fs()', and starts the commit even if there is nothing to commit, e.g., it anyway changes the log. This patch adds a check in the 'do_commit()' UBIFS functions which prevents the commit if there is nothing to commit. Reported-by: Hans J. Koch <hjk@linutronix.de> Tested-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
This commit is contained in:
parent
18d1d7fbcc
commit
944fdef52c
1 changed files with 57 additions and 1 deletions
|
@ -48,6 +48,56 @@
|
|||
#include <linux/slab.h>
|
||||
#include "ubifs.h"
|
||||
|
||||
/*
|
||||
* nothing_to_commit - check if there is nothing to commit.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This is a helper function which checks if there is anything to commit. It is
|
||||
* used as an optimization to avoid starting the commit if it is not really
|
||||
* necessary. Indeed, the commit operation always assumes flash I/O (e.g.,
|
||||
* writing the commit start node to the log), and it is better to avoid doing
|
||||
* this unnecessarily. E.g., 'ubifs_sync_fs()' runs the commit, but if there is
|
||||
* nothing to commit, it is more optimal to avoid any flash I/O.
|
||||
*
|
||||
* This function has to be called with @c->commit_sem locked for writing -
|
||||
* this function does not take LPT/TNC locks because the @c->commit_sem
|
||||
* guarantees that we have exclusive access to the TNC and LPT data structures.
|
||||
*
|
||||
* This function returns %1 if there is nothing to commit and %0 otherwise.
|
||||
*/
|
||||
static int nothing_to_commit(struct ubifs_info *c)
|
||||
{
|
||||
/*
|
||||
* During mounting or remounting from R/O mode to R/W mode we may
|
||||
* commit for various recovery-related reasons.
|
||||
*/
|
||||
if (c->mounting || c->remounting_rw)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the root TNC node is dirty, we definitely have something to
|
||||
* commit.
|
||||
*/
|
||||
if (c->zroot.znode && test_bit(DIRTY_ZNODE, &c->zroot.znode->flags))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Even though the TNC is clean, the LPT tree may have dirty nodes. For
|
||||
* example, this may happen if the budgeting subsystem invoked GC to
|
||||
* make some free space, and the GC found an LEB with only dirty and
|
||||
* free space. In this case GC would just change the lprops of this
|
||||
* LEB (by turning all space into free space) and unmap it.
|
||||
*/
|
||||
if (c->nroot && test_bit(DIRTY_CNODE, &c->nroot->flags))
|
||||
return 0;
|
||||
|
||||
ubifs_assert(atomic_long_read(&c->dirty_zn_cnt) == 0);
|
||||
ubifs_assert(c->dirty_pn_cnt == 0);
|
||||
ubifs_assert(c->dirty_nn_cnt == 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_commit - commit the journal.
|
||||
* @c: UBIFS file-system description object
|
||||
|
@ -70,6 +120,12 @@ static int do_commit(struct ubifs_info *c)
|
|||
goto out_up;
|
||||
}
|
||||
|
||||
if (nothing_to_commit(c)) {
|
||||
up_write(&c->commit_sem);
|
||||
err = 0;
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
/* Sync all write buffers (necessary for recovery) */
|
||||
for (i = 0; i < c->jhead_cnt; i++) {
|
||||
err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
|
||||
|
@ -162,12 +218,12 @@ static int do_commit(struct ubifs_info *c)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
out_cancel:
|
||||
spin_lock(&c->cs_lock);
|
||||
c->cmt_state = COMMIT_RESTING;
|
||||
wake_up(&c->cmt_wq);
|
||||
dbg_cmt("commit end");
|
||||
spin_unlock(&c->cs_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
out_up:
|
||||
|
|
Loading…
Reference in a new issue