aboutsummaryrefslogtreecommitdiffstats
path: root/fs/gfs2/glock.c
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2010-11-03 16:01:07 -0400
committerSteven Whitehouse <swhiteho@redhat.com>2010-11-15 07:44:42 -0500
commit044b9414c7caf9a26192c73a5b88fa1a8a32a1c1 (patch)
tree9596bb669a68b04eebc40864c3b3fd71d3d1e273 /fs/gfs2/glock.c
parent0143832cc96d0bf78486297aad5c8fb2c2ead02a (diff)
GFS2: Fix inode deallocation race
This area of the code has always been a bit delicate due to the subtleties of lock ordering. The problem is that for "normal" alloc/dealloc, we always grab the inode locks first and the rgrp lock later. In order to ensure no races in looking up the unlinked, but still allocated inodes, we need to hold the rgrp lock when we do the lookup, which means that we can't take the inode glock. The solution is to borrow the technique already used by NFS to solve what is essentially the same problem (given an inode number, look up the inode carefully, checking that it really is in the expected state). We cannot do that directly from the allocation code (lock ordering again) so we give the job to the pre-existing delete workqueue and carry on with the allocation as normal. If we find there is no space, we do a journal flush (required anyway if space from a deallocation is to be released) which should block against the pending deallocations, so we should always get the space back. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2/glock.c')
-rw-r--r--fs/gfs2/glock.c21
1 files changed, 10 insertions, 11 deletions
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 87778857f099..f92c17704169 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -686,21 +686,20 @@ static void delete_work_func(struct work_struct *work)
686{ 686{
687 struct gfs2_glock *gl = container_of(work, struct gfs2_glock, gl_delete); 687 struct gfs2_glock *gl = container_of(work, struct gfs2_glock, gl_delete);
688 struct gfs2_sbd *sdp = gl->gl_sbd; 688 struct gfs2_sbd *sdp = gl->gl_sbd;
689 struct gfs2_inode *ip = NULL; 689 struct gfs2_inode *ip;
690 struct inode *inode; 690 struct inode *inode;
691 u64 no_addr = 0; 691 u64 no_addr = gl->gl_name.ln_number;
692
693 ip = gl->gl_object;
694 /* Note: Unsafe to dereference ip as we don't hold right refs/locks */
692 695
693 spin_lock(&gl->gl_spin);
694 ip = (struct gfs2_inode *)gl->gl_object;
695 if (ip) 696 if (ip)
696 no_addr = ip->i_no_addr;
697 spin_unlock(&gl->gl_spin);
698 if (ip) {
699 inode = gfs2_ilookup(sdp->sd_vfs, no_addr); 697 inode = gfs2_ilookup(sdp->sd_vfs, no_addr);
700 if (inode) { 698 else
701 d_prune_aliases(inode); 699 inode = gfs2_lookup_by_inum(sdp, no_addr, NULL, GFS2_BLKST_UNLINKED);
702 iput(inode); 700 if (inode && !IS_ERR(inode)) {
703 } 701 d_prune_aliases(inode);
702 iput(inode);
704 } 703 }
705 gfs2_glock_put(gl); 704 gfs2_glock_put(gl);
706} 705}