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 /fs/nilfs2 | |
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>
Diffstat (limited to 'fs/nilfs2')
-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: |