diff options
author | Jan Kara <jack@suse.cz> | 2014-04-03 17:46:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 19:20:54 -0400 |
commit | e3a767b60fd8a9f5e133f42f4970cff77ec43173 (patch) | |
tree | a8399e9f83f246bcaf7be821da534754d0c37692 /fs/ocfs2 | |
parent | 9f985cb6c45bc3f8b7e161c9658d409d051d576f (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.h | 5 | ||||
-rw-r--r-- | fs/ocfs2/quota.h | 2 | ||||
-rw-r--r-- | fs/ocfs2/quota_global.c | 35 | ||||
-rw-r--r-- | fs/ocfs2/super.c | 8 |
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, | |||
110 | int ocfs2_create_local_dquot(struct dquot *dquot); | 111 | int ocfs2_create_local_dquot(struct dquot *dquot); |
111 | int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot); | 112 | int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot); |
112 | int ocfs2_local_write_dquot(struct dquot *dquot); | 113 | int ocfs2_local_write_dquot(struct dquot *dquot); |
114 | void ocfs2_drop_dquot_refs(struct work_struct *work); | ||
113 | 115 | ||
114 | extern const struct dquot_operations ocfs2_quota_operations; | 116 | extern const struct dquot_operations ocfs2_quota_operations; |
115 | extern struct quota_format_type ocfs2_quota_format; | 117 | extern 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 | ||
683 | void 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 | */ | ||
682 | static int ocfs2_release_dquot(struct dquot *dquot) | 704 | static 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); |