aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-04-03 17:46:56 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:20:54 -0400
commite3a767b60fd8a9f5e133f42f4970cff77ec43173 (patch)
treea8399e9f83f246bcaf7be821da534754d0c37692 /fs/ocfs2
parent9f985cb6c45bc3f8b7e161c9658d409d051d576f (diff)
ocfs2: implement delayed dropping of last dquot reference
We cannot drop last dquot reference from downconvert thread as that creates the following deadlock: NODE 1 NODE2 holds dentry lock for 'foo' holds inode lock for GLOBAL_BITMAP_SYSTEM_INODE dquot_initialize(bar) ocfs2_dquot_acquire() ocfs2_inode_lock(USER_QUOTA_SYSTEM_INODE) ... downconvert thread (triggered from another node or a different process from NODE2) ocfs2_dentry_post_unlock() ... iput(foo) ocfs2_evict_inode(foo) ocfs2_clear_inode(foo) dquot_drop(inode) ... ocfs2_dquot_release() ocfs2_inode_lock(USER_QUOTA_SYSTEM_INODE) - blocks finds we need more space in quota file ... ocfs2_extend_no_holes() ocfs2_inode_lock(GLOBAL_BITMAP_SYSTEM_INODE) - deadlocks waiting for downconvert thread We solve the problem by postponing dropping of the last dquot reference to a workqueue if it happens from the downconvert thread. Signed-off-by: Jan Kara <jack@suse.cz> Reviewed-by: Mark Fasheh <mfasheh@suse.de> Reviewed-by: Srinivas Eeda <srinivas.eeda@oracle.com> Cc: Joel Becker <jlbec@evilplan.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r--fs/ocfs2/ocfs2.h5
-rw-r--r--fs/ocfs2/quota.h2
-rw-r--r--fs/ocfs2/quota_global.c35
-rw-r--r--fs/ocfs2/super.c8
4 files changed, 50 insertions, 0 deletions
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 553f53cc73ae..64c02239ba46 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -30,6 +30,7 @@
30#include <linux/sched.h> 30#include <linux/sched.h>
31#include <linux/wait.h> 31#include <linux/wait.h>
32#include <linux/list.h> 32#include <linux/list.h>
33#include <linux/llist.h>
33#include <linux/rbtree.h> 34#include <linux/rbtree.h>
34#include <linux/workqueue.h> 35#include <linux/workqueue.h>
35#include <linux/kref.h> 36#include <linux/kref.h>
@@ -419,6 +420,10 @@ struct ocfs2_super
419 struct ocfs2_dentry_lock *dentry_lock_list; 420 struct ocfs2_dentry_lock *dentry_lock_list;
420 struct work_struct dentry_lock_work; 421 struct work_struct dentry_lock_work;
421 422
423 /* List of dquot structures to drop last reference to */
424 struct llist_head dquot_drop_list;
425 struct work_struct dquot_drop_work;
426
422 wait_queue_head_t osb_mount_event; 427 wait_queue_head_t osb_mount_event;
423 428
424 /* Truncate log info */ 429 /* Truncate log info */
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
index d5ab56cbe5c5..f266d67df3c6 100644
--- a/fs/ocfs2/quota.h
+++ b/fs/ocfs2/quota.h
@@ -28,6 +28,7 @@ struct ocfs2_dquot {
28 unsigned int dq_use_count; /* Number of nodes having reference to this entry in global quota file */ 28 unsigned int dq_use_count; /* Number of nodes having reference to this entry in global quota file */
29 s64 dq_origspace; /* Last globally synced space usage */ 29 s64 dq_origspace; /* Last globally synced space usage */
30 s64 dq_originodes; /* Last globally synced inode usage */ 30 s64 dq_originodes; /* Last globally synced inode usage */
31 struct llist_node list; /* Member of list of dquots to drop */
31}; 32};
32 33
33/* Description of one chunk to recover in memory */ 34/* Description of one chunk to recover in memory */
@@ -110,6 +111,7 @@ int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block,
110int ocfs2_create_local_dquot(struct dquot *dquot); 111int ocfs2_create_local_dquot(struct dquot *dquot);
111int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot); 112int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot);
112int ocfs2_local_write_dquot(struct dquot *dquot); 113int ocfs2_local_write_dquot(struct dquot *dquot);
114void ocfs2_drop_dquot_refs(struct work_struct *work);
113 115
114extern const struct dquot_operations ocfs2_quota_operations; 116extern const struct dquot_operations ocfs2_quota_operations;
115extern struct quota_format_type ocfs2_quota_format; 117extern struct quota_format_type ocfs2_quota_format;
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index d7b5108789e2..b990a62cff50 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -10,6 +10,7 @@
10#include <linux/jiffies.h> 10#include <linux/jiffies.h>
11#include <linux/writeback.h> 11#include <linux/writeback.h>
12#include <linux/workqueue.h> 12#include <linux/workqueue.h>
13#include <linux/llist.h>
13 14
14#include <cluster/masklog.h> 15#include <cluster/masklog.h>
15 16
@@ -679,6 +680,27 @@ static int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
679 OCFS2_INODE_UPDATE_CREDITS; 680 OCFS2_INODE_UPDATE_CREDITS;
680} 681}
681 682
683void ocfs2_drop_dquot_refs(struct work_struct *work)
684{
685 struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
686 dquot_drop_work);
687 struct llist_node *list;
688 struct ocfs2_dquot *odquot, *next_odquot;
689
690 list = llist_del_all(&osb->dquot_drop_list);
691 llist_for_each_entry_safe(odquot, next_odquot, list, list) {
692 /* Drop the reference we acquired in ocfs2_dquot_release() */
693 dqput(&odquot->dq_dquot);
694 }
695}
696
697/*
698 * Called when the last reference to dquot is dropped. If we are called from
699 * downconvert thread, we cannot do all the handling here because grabbing
700 * quota lock could deadlock (the node holding the quota lock could need some
701 * other cluster lock to proceed but with blocked downconvert thread we cannot
702 * release any lock).
703 */
682static int ocfs2_release_dquot(struct dquot *dquot) 704static int ocfs2_release_dquot(struct dquot *dquot)
683{ 705{
684 handle_t *handle; 706 handle_t *handle;
@@ -694,6 +716,19 @@ static int ocfs2_release_dquot(struct dquot *dquot)
694 /* Check whether we are not racing with some other dqget() */ 716 /* Check whether we are not racing with some other dqget() */
695 if (atomic_read(&dquot->dq_count) > 1) 717 if (atomic_read(&dquot->dq_count) > 1)
696 goto out; 718 goto out;
719 /* Running from downconvert thread? Postpone quota processing to wq */
720 if (current == osb->dc_task) {
721 /*
722 * Grab our own reference to dquot and queue it for delayed
723 * dropping. Quota code rechecks after calling
724 * ->release_dquot() and won't free dquot structure.
725 */
726 dqgrab(dquot);
727 /* First entry on list -> queue work */
728 if (llist_add(&OCFS2_DQUOT(dquot)->list, &osb->dquot_drop_list))
729 queue_work(ocfs2_wq, &osb->dquot_drop_work);
730 goto out;
731 }
697 status = ocfs2_lock_global_qf(oinfo, 1); 732 status = ocfs2_lock_global_qf(oinfo, 1);
698 if (status < 0) 733 if (status < 0)
699 goto out; 734 goto out;
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 9fef73da1ca5..b800a1f78d78 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1941,6 +1941,11 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
1941 1941
1942 ocfs2_disable_quotas(osb); 1942 ocfs2_disable_quotas(osb);
1943 1943
1944 /* All dquots should be freed by now */
1945 WARN_ON(!llist_empty(&osb->dquot_drop_list));
1946 /* Wait for worker to be done with the work structure in osb */
1947 cancel_work_sync(&osb->dquot_drop_work);
1948
1944 ocfs2_shutdown_local_alloc(osb); 1949 ocfs2_shutdown_local_alloc(osb);
1945 1950
1946 /* This will disable recovery and flush any recovery work. */ 1951 /* This will disable recovery and flush any recovery work. */
@@ -2276,6 +2281,9 @@ static int ocfs2_initialize_super(struct super_block *sb,
2276 INIT_WORK(&osb->dentry_lock_work, ocfs2_drop_dl_inodes); 2281 INIT_WORK(&osb->dentry_lock_work, ocfs2_drop_dl_inodes);
2277 osb->dentry_lock_list = NULL; 2282 osb->dentry_lock_list = NULL;
2278 2283
2284 INIT_WORK(&osb->dquot_drop_work, ocfs2_drop_dquot_refs);
2285 init_llist_head(&osb->dquot_drop_list);
2286
2279 /* get some pseudo constants for clustersize bits */ 2287 /* get some pseudo constants for clustersize bits */
2280 osb->s_clustersize_bits = 2288 osb->s_clustersize_bits =
2281 le32_to_cpu(di->id2.i_super.s_clustersize_bits); 2289 le32_to_cpu(di->id2.i_super.s_clustersize_bits);