nilfs2: fix lock order reversal in chcp operation

Will fix the following lock order reversal lockdep detected:

=======================================================
[ INFO: possible circular locking dependency detected ]
2.6.32-rc6 #7
-------------------------------------------------------
chcp/30157 is trying to acquire lock:
 (&nilfs->ns_mount_mutex){+.+.+.}, at: [<fed7cfcc>] nilfs_cpfile_change_cpmode+0x46/0x752 [nilfs2]

but task is already holding lock:
 (&nilfs->ns_segctor_sem){++++.+}, at: [<fed7ca32>] nilfs_transaction_begin+0xba/0x110 [nilfs2]

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #2 (&nilfs->ns_segctor_sem){++++.+}:
       [<c105799c>] __lock_acquire+0x109c/0x139d
       [<c1057d26>] lock_acquire+0x89/0xa0
       [<c14151e2>] down_read+0x31/0x45
       [<fed6d77b>] nilfs_attach_checkpoint+0x8f/0x16b [nilfs2]
       [<fed6e393>] nilfs_get_sb+0x3e7/0x653 [nilfs2]
       [<c10c0ccb>] vfs_kern_mount+0x8b/0x124
       [<c10c0db2>] do_kern_mount+0x37/0xc3
       [<c10d7517>] do_mount+0x64d/0x69d
       [<c10d75cd>] sys_mount+0x66/0x95
       [<c1002a14>] sysenter_do_call+0x12/0x32

-> #1 (&type->s_umount_key#31/1){+.+.+.}:
       [<c105799c>] __lock_acquire+0x109c/0x139d
       [<c1057d26>] lock_acquire+0x89/0xa0
       [<c104c0f3>] down_write_nested+0x34/0x52
       [<c10c08fe>] sget+0x22e/0x389
       [<fed6e133>] nilfs_get_sb+0x187/0x653 [nilfs2]
       [<c10c0ccb>] vfs_kern_mount+0x8b/0x124
       [<c10c0db2>] do_kern_mount+0x37/0xc3
       [<c10d7517>] do_mount+0x64d/0x69d
       [<c10d75cd>] sys_mount+0x66/0x95
       [<c1002a14>] sysenter_do_call+0x12/0x32

-> #0 (&nilfs->ns_mount_mutex){+.+.+.}:
       [<c1057727>] __lock_acquire+0xe27/0x139d
       [<c1057d26>] lock_acquire+0x89/0xa0
       [<c1414d63>] mutex_lock_nested+0x41/0x23e
       [<fed7cfcc>] nilfs_cpfile_change_cpmode+0x46/0x752 [nilfs2]
       [<fed801b2>] nilfs_ioctl+0x11a/0x7da [nilfs2]
       [<c10cca12>] vfs_ioctl+0x27/0x6e
       [<c10ccf93>] do_vfs_ioctl+0x491/0x4db
       [<c10cd022>] sys_ioctl+0x45/0x5f
       [<c1002a14>] sysenter_do_call+0x12/0x32

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
This commit is contained in:
Ryusuke Konishi 2009-11-12 00:13:32 +09:00
parent 156171c71a
commit c1ea985c71
2 changed files with 5 additions and 3 deletions

View file

@ -871,7 +871,6 @@ int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
* exclusive with a new mount job. Though it doesn't cover * exclusive with a new mount job. Though it doesn't cover
* umount, it's enough for the purpose. * umount, it's enough for the purpose.
*/ */
mutex_lock(&nilfs->ns_mount_mutex);
if (nilfs_checkpoint_is_mounted(nilfs, cno, 1)) { if (nilfs_checkpoint_is_mounted(nilfs, cno, 1)) {
/* Current implementation does not have to protect /* Current implementation does not have to protect
plain read-only mounts since they are exclusive plain read-only mounts since they are exclusive
@ -880,7 +879,6 @@ int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
ret = -EBUSY; ret = -EBUSY;
} else } else
ret = nilfs_cpfile_clear_snapshot(cpfile, cno); ret = nilfs_cpfile_clear_snapshot(cpfile, cno);
mutex_unlock(&nilfs->ns_mount_mutex);
return ret; return ret;
case NILFS_SNAPSHOT: case NILFS_SNAPSHOT:
return nilfs_cpfile_set_snapshot(cpfile, cno); return nilfs_cpfile_set_snapshot(cpfile, cno);

View file

@ -99,7 +99,8 @@ static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
unsigned int cmd, void __user *argp) unsigned int cmd, void __user *argp)
{ {
struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile; struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
struct inode *cpfile = nilfs->ns_cpfile;
struct nilfs_transaction_info ti; struct nilfs_transaction_info ti;
struct nilfs_cpmode cpmode; struct nilfs_cpmode cpmode;
int ret; int ret;
@ -109,14 +110,17 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
if (copy_from_user(&cpmode, argp, sizeof(cpmode))) if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
return -EFAULT; return -EFAULT;
mutex_lock(&nilfs->ns_mount_mutex);
nilfs_transaction_begin(inode->i_sb, &ti, 0); nilfs_transaction_begin(inode->i_sb, &ti, 0);
ret = nilfs_cpfile_change_cpmode( ret = nilfs_cpfile_change_cpmode(
cpfile, cpmode.cm_cno, cpmode.cm_mode); cpfile, cpmode.cm_cno, cpmode.cm_mode);
if (unlikely(ret < 0)) { if (unlikely(ret < 0)) {
nilfs_transaction_abort(inode->i_sb); nilfs_transaction_abort(inode->i_sb);
mutex_unlock(&nilfs->ns_mount_mutex);
return ret; return ret;
} }
nilfs_transaction_commit(inode->i_sb); /* never fails */ nilfs_transaction_commit(inode->i_sb); /* never fails */
mutex_unlock(&nilfs->ns_mount_mutex);
return ret; return ret;
} }