diff options
| author | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-04-29 13:21:00 -0400 |
|---|---|---|
| committer | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-05-10 23:57:46 -0400 |
| commit | 47eb6b9c8fa963c9f49967ad1d9d7ec947d15b68 (patch) | |
| tree | f69916e4135bd4550b11a8faa631eacd78fddded | |
| parent | 843382370ec614768ac13582405f93635cf3637c (diff) | |
nilfs2: fix possible circular locking for get information ioctls
This is one of two patches which are to correct possible circular
locking between mm->mmap_sem and nilfs->ns_segctor_sem.
The problem was detected by lockdep check as follows:
=======================================================
[ INFO: possible circular locking dependency detected ]
2.6.30-rc3-nilfs-00002-g3552613 #6
-------------------------------------------------------
mmap/5418 is trying to acquire lock:
(&nilfs->ns_segctor_sem){++++.+}, at: [<d0d0e852>] nilfs_transaction_begin+0xb6/0x10c [nilfs2]
but task is already holding lock:
(&mm->mmap_sem){++++++}, at: [<c043700a>] do_page_fault+0x1d8/0x30a
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (&mm->mmap_sem){++++++}:
[<c01470a5>] __lock_acquire+0x1066/0x13b0
[<c01474a9>] lock_acquire+0xba/0xdd
[<c01836bc>] might_fault+0x68/0x88
[<c023c730>] copy_to_user+0x2c/0xfc
[<d0d11b4f>] nilfs_ioctl_wrap_copy+0x103/0x160 [nilfs2]
[<d0d11fa9>] nilfs_ioctl+0x30a/0x3b0 [nilfs2]
[<c01a3be7>] vfs_ioctl+0x22/0x69
[<c01a408e>] do_vfs_ioctl+0x460/0x499
[<c01a4107>] sys_ioctl+0x40/0x5a
[<c01031a4>] sysenter_do_call+0x12/0x38
[<ffffffff>] 0xffffffff
-> #0 (&nilfs->ns_segctor_sem){++++.+}:
[<c0146e0b>] __lock_acquire+0xdcc/0x13b0
[<c01474a9>] lock_acquire+0xba/0xdd
[<c0433f1d>] down_read+0x2a/0x3e
[<d0d0e852>] nilfs_transaction_begin+0xb6/0x10c [nilfs2]
[<d0cfe0e5>] nilfs_page_mkwrite+0xe7/0x154 [nilfs2]
[<c0183b0b>] __do_fault+0x165/0x376
[<c01855cd>] handle_mm_fault+0x287/0x5d1
[<c043712d>] do_page_fault+0x2fb/0x30a
[<c0435462>] error_code+0x72/0x78
[<ffffffff>] 0xffffffff
other info that might help us debug this:
1 lock held by mmap/5418:
#0: (&mm->mmap_sem){++++++}, at: [<c043700a>] do_page_fault+0x1d8/0x30a
stack backtrace:
Pid: 5418, comm: mmap Not tainted 2.6.30-rc3-nilfs-00002-g3552613 #6
Call Trace:
[<c0432145>] ? printk+0xf/0x12
[<c0145c48>] print_circular_bug_tail+0xaa/0xb5
[<c0146e0b>] __lock_acquire+0xdcc/0x13b0
[<d0d10149>] ? nilfs_sufile_get_stat+0x1e/0x105 [nilfs2]
[<c013b59a>] ? up_read+0x16/0x2c
[<d0d10225>] ? nilfs_sufile_get_stat+0xfa/0x105 [nilfs2]
[<c01474a9>] lock_acquire+0xba/0xdd
[<d0d0e852>] ? nilfs_transaction_begin+0xb6/0x10c [nilfs2]
[<c0433f1d>] down_read+0x2a/0x3e
[<d0d0e852>] ? nilfs_transaction_begin+0xb6/0x10c [nilfs2]
[<d0d0e852>] nilfs_transaction_begin+0xb6/0x10c [nilfs2]
[<d0cfe0e5>] nilfs_page_mkwrite+0xe7/0x154 [nilfs2]
[<c0183b0b>] __do_fault+0x165/0x376
[<c01855cd>] handle_mm_fault+0x287/0x5d1
[<c043700a>] ? do_page_fault+0x1d8/0x30a
[<c013b54f>] ? down_read_trylock+0x39/0x43
[<c043712d>] do_page_fault+0x2fb/0x30a
[<c0436e32>] ? do_page_fault+0x0/0x30a
[<c0435462>] error_code+0x72/0x78
[<c0436e32>] ? do_page_fault+0x0/0x30a
This makes the lock granularity of nilfs->ns_segctor_sem finer than
that of the mmap semaphore for ioctl commands except
nilfs_clean_segments().
The successive patch ("nilfs2: fix lock order reversal in
nilfs_clean_segments ioctl") is required to fully resolve the problem.
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
| -rw-r--r-- | fs/nilfs2/ioctl.c | 100 |
1 files changed, 38 insertions, 62 deletions
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index be387c6b2d46..e3c693d37d69 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c | |||
| @@ -147,29 +147,12 @@ static ssize_t | |||
| 147 | nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 147 | nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
| 148 | void *buf, size_t size, size_t nmembs) | 148 | void *buf, size_t size, size_t nmembs) |
| 149 | { | 149 | { |
| 150 | return nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, | ||
| 151 | nmembs); | ||
| 152 | } | ||
| 153 | |||
| 154 | static int nilfs_ioctl_get_cpinfo(struct inode *inode, struct file *filp, | ||
| 155 | unsigned int cmd, void __user *argp) | ||
| 156 | { | ||
| 157 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
| 158 | struct nilfs_argv argv; | ||
| 159 | int ret; | 150 | int ret; |
| 160 | 151 | ||
| 161 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
| 162 | return -EFAULT; | ||
| 163 | |||
| 164 | down_read(&nilfs->ns_segctor_sem); | 152 | down_read(&nilfs->ns_segctor_sem); |
| 165 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 153 | ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, |
| 166 | nilfs_ioctl_do_get_cpinfo); | 154 | nmembs); |
| 167 | up_read(&nilfs->ns_segctor_sem); | 155 | up_read(&nilfs->ns_segctor_sem); |
| 168 | if (ret < 0) | ||
| 169 | return ret; | ||
| 170 | |||
| 171 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
| 172 | ret = -EFAULT; | ||
| 173 | return ret; | 156 | return ret; |
| 174 | } | 157 | } |
| 175 | 158 | ||
| @@ -195,28 +178,11 @@ static ssize_t | |||
| 195 | nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 178 | nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
| 196 | void *buf, size_t size, size_t nmembs) | 179 | void *buf, size_t size, size_t nmembs) |
| 197 | { | 180 | { |
| 198 | return nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs); | ||
| 199 | } | ||
| 200 | |||
| 201 | static int nilfs_ioctl_get_suinfo(struct inode *inode, struct file *filp, | ||
| 202 | unsigned int cmd, void __user *argp) | ||
| 203 | { | ||
| 204 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
| 205 | struct nilfs_argv argv; | ||
| 206 | int ret; | 181 | int ret; |
| 207 | 182 | ||
| 208 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
| 209 | return -EFAULT; | ||
| 210 | |||
| 211 | down_read(&nilfs->ns_segctor_sem); | 183 | down_read(&nilfs->ns_segctor_sem); |
| 212 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 184 | ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs); |
| 213 | nilfs_ioctl_do_get_suinfo); | ||
| 214 | up_read(&nilfs->ns_segctor_sem); | 185 | up_read(&nilfs->ns_segctor_sem); |
| 215 | if (ret < 0) | ||
| 216 | return ret; | ||
| 217 | |||
| 218 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
| 219 | ret = -EFAULT; | ||
| 220 | return ret; | 186 | return ret; |
| 221 | } | 187 | } |
| 222 | 188 | ||
| @@ -242,28 +208,11 @@ static ssize_t | |||
| 242 | nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 208 | nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
| 243 | void *buf, size_t size, size_t nmembs) | 209 | void *buf, size_t size, size_t nmembs) |
| 244 | { | 210 | { |
| 245 | return nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs); | ||
| 246 | } | ||
| 247 | |||
| 248 | static int nilfs_ioctl_get_vinfo(struct inode *inode, struct file *filp, | ||
| 249 | unsigned int cmd, void __user *argp) | ||
| 250 | { | ||
| 251 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
| 252 | struct nilfs_argv argv; | ||
| 253 | int ret; | 211 | int ret; |
| 254 | 212 | ||
| 255 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
| 256 | return -EFAULT; | ||
| 257 | |||
| 258 | down_read(&nilfs->ns_segctor_sem); | 213 | down_read(&nilfs->ns_segctor_sem); |
| 259 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 214 | ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs); |
| 260 | nilfs_ioctl_do_get_vinfo); | ||
| 261 | up_read(&nilfs->ns_segctor_sem); | 215 | up_read(&nilfs->ns_segctor_sem); |
| 262 | if (ret < 0) | ||
| 263 | return ret; | ||
| 264 | |||
| 265 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
| 266 | ret = -EFAULT; | ||
| 267 | return ret; | 216 | return ret; |
| 268 | } | 217 | } |
| 269 | 218 | ||
| @@ -276,17 +225,21 @@ nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, | |||
| 276 | struct nilfs_bdesc *bdescs = buf; | 225 | struct nilfs_bdesc *bdescs = buf; |
| 277 | int ret, i; | 226 | int ret, i; |
| 278 | 227 | ||
| 228 | down_read(&nilfs->ns_segctor_sem); | ||
| 279 | for (i = 0; i < nmembs; i++) { | 229 | for (i = 0; i < nmembs; i++) { |
| 280 | ret = nilfs_bmap_lookup_at_level(bmap, | 230 | ret = nilfs_bmap_lookup_at_level(bmap, |
| 281 | bdescs[i].bd_offset, | 231 | bdescs[i].bd_offset, |
| 282 | bdescs[i].bd_level + 1, | 232 | bdescs[i].bd_level + 1, |
| 283 | &bdescs[i].bd_blocknr); | 233 | &bdescs[i].bd_blocknr); |
| 284 | if (ret < 0) { | 234 | if (ret < 0) { |
| 285 | if (ret != -ENOENT) | 235 | if (ret != -ENOENT) { |
| 236 | up_read(&nilfs->ns_segctor_sem); | ||
| 286 | return ret; | 237 | return ret; |
| 238 | } | ||
| 287 | bdescs[i].bd_blocknr = 0; | 239 | bdescs[i].bd_blocknr = 0; |
| 288 | } | 240 | } |
| 289 | } | 241 | } |
| 242 | up_read(&nilfs->ns_segctor_sem); | ||
| 290 | return nmembs; | 243 | return nmembs; |
| 291 | } | 244 | } |
| 292 | 245 | ||
| @@ -300,10 +253,8 @@ static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, | |||
| 300 | if (copy_from_user(&argv, argp, sizeof(argv))) | 253 | if (copy_from_user(&argv, argp, sizeof(argv))) |
| 301 | return -EFAULT; | 254 | return -EFAULT; |
| 302 | 255 | ||
| 303 | down_read(&nilfs->ns_segctor_sem); | ||
| 304 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 256 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), |
| 305 | nilfs_ioctl_do_get_bdescs); | 257 | nilfs_ioctl_do_get_bdescs); |
| 306 | up_read(&nilfs->ns_segctor_sem); | ||
| 307 | if (ret < 0) | 258 | if (ret < 0) |
| 308 | return ret; | 259 | return ret; |
| 309 | 260 | ||
| @@ -623,6 +574,29 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, | |||
| 623 | return 0; | 574 | return 0; |
| 624 | } | 575 | } |
| 625 | 576 | ||
| 577 | static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, | ||
| 578 | unsigned int cmd, void __user *argp, | ||
| 579 | ssize_t (*dofunc)(struct the_nilfs *, | ||
| 580 | __u64 *, int, | ||
| 581 | void *, size_t, size_t)) | ||
| 582 | |||
| 583 | { | ||
| 584 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
| 585 | struct nilfs_argv argv; | ||
| 586 | int ret; | ||
| 587 | |||
| 588 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
| 589 | return -EFAULT; | ||
| 590 | |||
| 591 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); | ||
| 592 | if (ret < 0) | ||
| 593 | return ret; | ||
| 594 | |||
| 595 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
| 596 | ret = -EFAULT; | ||
| 597 | return ret; | ||
| 598 | } | ||
| 599 | |||
| 626 | long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 600 | long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
| 627 | { | 601 | { |
| 628 | struct inode *inode = filp->f_dentry->d_inode; | 602 | struct inode *inode = filp->f_dentry->d_inode; |
| @@ -634,16 +608,18 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 634 | case NILFS_IOCTL_DELETE_CHECKPOINT: | 608 | case NILFS_IOCTL_DELETE_CHECKPOINT: |
| 635 | return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); | 609 | return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); |
| 636 | case NILFS_IOCTL_GET_CPINFO: | 610 | case NILFS_IOCTL_GET_CPINFO: |
| 637 | return nilfs_ioctl_get_cpinfo(inode, filp, cmd, argp); | 611 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
| 612 | nilfs_ioctl_do_get_cpinfo); | ||
| 638 | case NILFS_IOCTL_GET_CPSTAT: | 613 | case NILFS_IOCTL_GET_CPSTAT: |
| 639 | return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); | 614 | return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); |
| 640 | case NILFS_IOCTL_GET_SUINFO: | 615 | case NILFS_IOCTL_GET_SUINFO: |
| 641 | return nilfs_ioctl_get_suinfo(inode, filp, cmd, argp); | 616 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
| 617 | nilfs_ioctl_do_get_suinfo); | ||
| 642 | case NILFS_IOCTL_GET_SUSTAT: | 618 | case NILFS_IOCTL_GET_SUSTAT: |
| 643 | return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); | 619 | return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); |
| 644 | case NILFS_IOCTL_GET_VINFO: | 620 | case NILFS_IOCTL_GET_VINFO: |
| 645 | /* XXX: rename to ??? */ | 621 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
| 646 | return nilfs_ioctl_get_vinfo(inode, filp, cmd, argp); | 622 | nilfs_ioctl_do_get_vinfo); |
| 647 | case NILFS_IOCTL_GET_BDESCS: | 623 | case NILFS_IOCTL_GET_BDESCS: |
| 648 | return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); | 624 | return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); |
| 649 | case NILFS_IOCTL_CLEAN_SEGMENTS: | 625 | case NILFS_IOCTL_CLEAN_SEGMENTS: |
