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

Commit c72e0575 authored by Frederic Weisbecker's avatar Frederic Weisbecker
Browse files

kill-the-bkl/reiserfs: acquire the inode mutex safely



While searching a pathname, an inode mutex can be acquired
in do_lookup() which calls reiserfs_lookup() which in turn
acquires the write lock.

On the other side reiserfs_fill_super() can acquire the write_lock
and then call reiserfs_lookup_privroot() which can acquire an
inode mutex (the root of the mount point).

So we theoretically risk an AB - BA lock inversion that could lead
to a deadlock.

As for other lock dependencies found since the bkl to mutex
conversion, the fix is to use reiserfs_mutex_lock_safe() which
drops the lock dependency to the write lock.

[ Impact: fix a possible deadlock with reiserfs ]

Cc: Jeff Mahoney <jeffm@suse.com>
Cc: Chris Mason <chris.mason@oracle.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Alexander Beregalov <a.beregalov@gmail.com>
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
parent 2ac62695
Loading
Loading
Loading
Loading
+0 −34
Original line number Diff line number Diff line
@@ -537,40 +537,6 @@ static inline void insert_journal_hash(struct reiserfs_journal_cnode **table,
	journal_hash(table, cn->sb, cn->blocknr) = cn;
}

/*
 * Several mutexes depend on the write lock.
 * However sometimes we want to relax the write lock while we hold
 * these mutexes, according to the release/reacquire on schedule()
 * properties of the Bkl that were used.
 * Reiserfs performances and locking were based on this scheme.
 * Now that the write lock is a mutex and not the bkl anymore, doing so
 * may result in a deadlock:
 *
 * A acquire write_lock
 * A acquire j_commit_mutex
 * A release write_lock and wait for something
 * B acquire write_lock
 * B can't acquire j_commit_mutex and sleep
 * A can't acquire write lock anymore
 * deadlock
 *
 * What we do here is avoiding such deadlock by playing the same game
 * than the Bkl: if we can't acquire a mutex that depends on the write lock,
 * we release the write lock, wait a bit and then retry.
 *
 * The mutexes concerned by this hack are:
 * - The commit mutex of a journal list
 * - The flush mutex
 * - The journal lock
 */
static inline void reiserfs_mutex_lock_safe(struct mutex *m,
			       struct super_block *s)
{
	reiserfs_write_unlock(s);
	mutex_lock(m);
	reiserfs_write_lock(s);
}

/* lock the current transaction */
static inline void lock_journal(struct super_block *sb)
{
+2 −2
Original line number Diff line number Diff line
@@ -975,7 +975,7 @@ int reiserfs_lookup_privroot(struct super_block *s)
	int err = 0;

	/* If we don't have the privroot located yet - go find it */
	mutex_lock(&s->s_root->d_inode->i_mutex);
	reiserfs_mutex_lock_safe(&s->s_root->d_inode->i_mutex, s);
	dentry = lookup_one_len(PRIVROOT_NAME, s->s_root,
				strlen(PRIVROOT_NAME));
	if (!IS_ERR(dentry)) {
@@ -1011,7 +1011,7 @@ int reiserfs_xattr_init(struct super_block *s, int mount_flags)

	if (privroot->d_inode) {
		s->s_xattr = reiserfs_xattr_handlers;
		mutex_lock(&privroot->d_inode->i_mutex);
		reiserfs_mutex_lock_safe(&privroot->d_inode->i_mutex, s);
		if (!REISERFS_SB(s)->xattr_root) {
			struct dentry *dentry;
			dentry = lookup_one_len(XAROOT_NAME, privroot,
+35 −0
Original line number Diff line number Diff line
@@ -62,6 +62,41 @@ void reiserfs_write_unlock(struct super_block *s);
int reiserfs_write_lock_once(struct super_block *s);
void reiserfs_write_unlock_once(struct super_block *s, int lock_depth);

/*
 * Several mutexes depend on the write lock.
 * However sometimes we want to relax the write lock while we hold
 * these mutexes, according to the release/reacquire on schedule()
 * properties of the Bkl that were used.
 * Reiserfs performances and locking were based on this scheme.
 * Now that the write lock is a mutex and not the bkl anymore, doing so
 * may result in a deadlock:
 *
 * A acquire write_lock
 * A acquire j_commit_mutex
 * A release write_lock and wait for something
 * B acquire write_lock
 * B can't acquire j_commit_mutex and sleep
 * A can't acquire write lock anymore
 * deadlock
 *
 * What we do here is avoiding such deadlock by playing the same game
 * than the Bkl: if we can't acquire a mutex that depends on the write lock,
 * we release the write lock, wait a bit and then retry.
 *
 * The mutexes concerned by this hack are:
 * - The commit mutex of a journal list
 * - The flush mutex
 * - The journal lock
 * - The inode mutex
 */
static inline void reiserfs_mutex_lock_safe(struct mutex *m,
			       struct super_block *s)
{
	reiserfs_write_unlock(s);
	mutex_lock(m);
	reiserfs_write_lock(s);
}

/*
 * When we schedule, we usually want to also release the write lock,
 * according to the previous bkl based locking scheme of reiserfs.