aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2006-03-23 06:00:17 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-23 10:38:10 -0500
commit6362e4d4eda61efb04ac1cdae32e48ac6d90b701 (patch)
treebe13a8b673d117ec3d186f2911f4ebae3da857dd /fs
parent804f1594cc3deb161e531a43d90c501f0db2635a (diff)
[PATCH] Fix oops in invalidate_dquots()
When quota is being turned off we assumed that all the references to dquots were already dropped. That need not be true as inodes being deleted are not on superblock's inodes list and hence we need not reach it when removing quota references from inodes. So invalidate_dquots() has to wait for all the users of dquots (as quota is already marked as turned off, no new references can be acquired and so this is bound to happen rather early). When we do this, we can also remove the iprune_sem locking as it was protecting us against exactly the same problem when freeing inodes icache memory. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/dquot.c52
1 files changed, 39 insertions, 13 deletions
diff --git a/fs/dquot.c b/fs/dquot.c
index 1966c890b48d..9376a4378988 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -118,8 +118,7 @@
118 * spinlock to internal buffers before writing. 118 * spinlock to internal buffers before writing.
119 * 119 *
120 * Lock ordering (including related VFS locks) is the following: 120 * Lock ordering (including related VFS locks) is the following:
121 * i_mutex > dqonoff_sem > iprune_sem > journal_lock > dqptr_sem > 121 * i_mutex > dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem
122 * > dquot->dq_lock > dqio_sem
123 * i_mutex on quota files is special (it's below dqio_sem) 122 * i_mutex on quota files is special (it's below dqio_sem)
124 */ 123 */
125 124
@@ -407,23 +406,49 @@ out_dqlock:
407 406
408/* Invalidate all dquots on the list. Note that this function is called after 407/* Invalidate all dquots on the list. Note that this function is called after
409 * quota is disabled and pointers from inodes removed so there cannot be new 408 * quota is disabled and pointers from inodes removed so there cannot be new
410 * quota users. Also because we hold dqonoff_sem there can be no quota users 409 * quota users. There can still be some users of quotas due to inodes being
411 * for this sb+type at all. */ 410 * just deleted or pruned by prune_icache() (those are not attached to any
411 * list). We have to wait for such users.
412 */
412static void invalidate_dquots(struct super_block *sb, int type) 413static void invalidate_dquots(struct super_block *sb, int type)
413{ 414{
414 struct dquot *dquot, *tmp; 415 struct dquot *dquot, *tmp;
415 416
417restart:
416 spin_lock(&dq_list_lock); 418 spin_lock(&dq_list_lock);
417 list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) { 419 list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
418 if (dquot->dq_sb != sb) 420 if (dquot->dq_sb != sb)
419 continue; 421 continue;
420 if (dquot->dq_type != type) 422 if (dquot->dq_type != type)
421 continue; 423 continue;
422#ifdef __DQUOT_PARANOIA 424 /* Wait for dquot users */
423 if (atomic_read(&dquot->dq_count)) 425 if (atomic_read(&dquot->dq_count)) {
424 BUG(); 426 DEFINE_WAIT(wait);
425#endif 427
426 /* Quota now has no users and it has been written on last dqput() */ 428 atomic_inc(&dquot->dq_count);
429 prepare_to_wait(&dquot->dq_wait_unused, &wait,
430 TASK_UNINTERRUPTIBLE);
431 spin_unlock(&dq_list_lock);
432 /* Once dqput() wakes us up, we know it's time to free
433 * the dquot.
434 * IMPORTANT: we rely on the fact that there is always
435 * at most one process waiting for dquot to free.
436 * Otherwise dq_count would be > 1 and we would never
437 * wake up.
438 */
439 if (atomic_read(&dquot->dq_count) > 1)
440 schedule();
441 finish_wait(&dquot->dq_wait_unused, &wait);
442 dqput(dquot);
443 /* At this moment dquot() need not exist (it could be
444 * reclaimed by prune_dqcache(). Hence we must
445 * restart. */
446 goto restart;
447 }
448 /*
449 * Quota now has no users and it has been written on last
450 * dqput()
451 */
427 remove_dquot_hash(dquot); 452 remove_dquot_hash(dquot);
428 remove_free_dquot(dquot); 453 remove_free_dquot(dquot);
429 remove_inuse(dquot); 454 remove_inuse(dquot);
@@ -540,6 +565,10 @@ we_slept:
540 if (atomic_read(&dquot->dq_count) > 1) { 565 if (atomic_read(&dquot->dq_count) > 1) {
541 /* We have more than one user... nothing to do */ 566 /* We have more than one user... nothing to do */
542 atomic_dec(&dquot->dq_count); 567 atomic_dec(&dquot->dq_count);
568 /* Releasing dquot during quotaoff phase? */
569 if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) &&
570 atomic_read(&dquot->dq_count) == 1)
571 wake_up(&dquot->dq_wait_unused);
543 spin_unlock(&dq_list_lock); 572 spin_unlock(&dq_list_lock);
544 return; 573 return;
545 } 574 }
@@ -581,6 +610,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
581 INIT_LIST_HEAD(&dquot->dq_inuse); 610 INIT_LIST_HEAD(&dquot->dq_inuse);
582 INIT_HLIST_NODE(&dquot->dq_hash); 611 INIT_HLIST_NODE(&dquot->dq_hash);
583 INIT_LIST_HEAD(&dquot->dq_dirty); 612 INIT_LIST_HEAD(&dquot->dq_dirty);
613 init_waitqueue_head(&dquot->dq_wait_unused);
584 dquot->dq_sb = sb; 614 dquot->dq_sb = sb;
585 dquot->dq_type = type; 615 dquot->dq_type = type;
586 atomic_set(&dquot->dq_count, 1); 616 atomic_set(&dquot->dq_count, 1);
@@ -732,13 +762,9 @@ static void drop_dquot_ref(struct super_block *sb, int type)
732{ 762{
733 LIST_HEAD(tofree_head); 763 LIST_HEAD(tofree_head);
734 764
735 /* We need to be guarded against prune_icache to reach all the
736 * inodes - otherwise some can be on the local list of prune_icache */
737 down(&iprune_sem);
738 down_write(&sb_dqopt(sb)->dqptr_sem); 765 down_write(&sb_dqopt(sb)->dqptr_sem);
739 remove_dquot_ref(sb, type, &tofree_head); 766 remove_dquot_ref(sb, type, &tofree_head);
740 up_write(&sb_dqopt(sb)->dqptr_sem); 767 up_write(&sb_dqopt(sb)->dqptr_sem);
741 up(&iprune_sem);
742 put_dquot_list(&tofree_head); 768 put_dquot_list(&tofree_head);
743} 769}
744 770