Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 944fdef5 authored by Artem Bityutskiy's avatar Artem Bityutskiy
Browse files

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: default avatarHans J. Koch <hjk@linutronix.de>
Tested-by: default avatarJohn Ogness <john.ogness@linutronix.de>
Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
parent 18d1d7fb
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -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: