From 777b01b17f93eaf92ba4d8df9ce1ed4d04b9c043 Mon Sep 17 00:00:00 2001 From: Erez Zadok Date: Mon, 26 Nov 2007 19:54:06 -0500 Subject: [PATCH] Unionfs: avoid a deadlock during branch-management on a pivot_root'ed union Signed-off-by: Erez Zadok --- fs/unionfs/union.h | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h index 18c430d8271..fb57dc17f5b 100644 --- a/fs/unionfs/union.h +++ b/fs/unionfs/union.h @@ -142,15 +142,22 @@ struct unionfs_sb_info { * This rwsem is used to make sure that a branch management * operation... * 1) will not begin before all currently in-flight operations - * complete + * complete. * 2) any new operations do not execute until the currently - * running branch management operation completes + * running branch management operation completes. + * + * The write_lock_owner records the PID of the task which grabbed + * the rw_sem for writing. If the same task also tries to grab the + * read lock, we allow it. This prevents a self-deadlock when + * branch-management is used on a pivot_root'ed union, because we + * have to ->lookup paths which belong to the same union. */ #ifdef CONFIG_PREEMPT_RT struct compat_rw_semaphore rwsem; #else /* not CONFIG_PREEMPT_RT */ struct rw_semaphore rwsem; #endif /* not CONFIG_PREEMPT_RT */ + pid_t write_lock_owner; /* PID of rw_sem owner (write lock) */ int high_branch_id; /* last unique branch ID given */ struct unionfs_data *data; }; @@ -238,10 +245,30 @@ static inline off_t rdstate2offset(struct unionfs_dir_state *buf) return tmp; } -#define unionfs_read_lock(sb) down_read(&UNIONFS_SB(sb)->rwsem) -#define unionfs_read_unlock(sb) up_read(&UNIONFS_SB(sb)->rwsem) -#define unionfs_write_lock(sb) down_write(&UNIONFS_SB(sb)->rwsem) -#define unionfs_write_unlock(sb) up_write(&UNIONFS_SB(sb)->rwsem) +static inline void unionfs_read_lock(struct super_block *sb) +{ + if (UNIONFS_SB(sb)->write_lock_owner && + UNIONFS_SB(sb)->write_lock_owner == current->pid) + return; + down_read(&UNIONFS_SB(sb)->rwsem); +} +static inline void unionfs_read_unlock(struct super_block *sb) +{ + if (UNIONFS_SB(sb)->write_lock_owner && + UNIONFS_SB(sb)->write_lock_owner == current->pid) + return; + up_read(&UNIONFS_SB(sb)->rwsem); +} +static inline void unionfs_write_lock(struct super_block *sb) +{ + down_write(&UNIONFS_SB(sb)->rwsem); + UNIONFS_SB(sb)->write_lock_owner = current->pid; +} +static inline void unionfs_write_unlock(struct super_block *sb) +{ + up_write(&UNIONFS_SB(sb)->rwsem); + UNIONFS_SB(sb)->write_lock_owner = 0; +} static inline void unionfs_double_lock_dentry(struct dentry *d1, struct dentry *d2) -- 2.43.0