summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSage Weil <sage@newdream.net>2010-04-19 10:15:44 -0700
committerSage Weil <sage@newdream.net>2010-05-03 10:49:23 -0700
commit91dee39eebcfb47085c4d457a584b0e9723b6ca0 (patch)
tree52bdeb4795c3b60b1d23460cada8c813c94fb7a9
parentc10f5e12bafde7f7a2f9b75d76f7a68d62154e91 (diff)
downloadlinux-3.10-91dee39eebcfb47085c4d457a584b0e9723b6ca0.tar.gz
linux-3.10-91dee39eebcfb47085c4d457a584b0e9723b6ca0.tar.bz2
linux-3.10-91dee39eebcfb47085c4d457a584b0e9723b6ca0.zip
ceph: fix snap realm splits
The snap realm split was checking i_snap_realm, not the list_head, to determine if an inode belonged in the new realm. The check always failed, which meant we always moved the inode, corrupting the old realm's list and causing various crashes. Also wait to release old realm reference to avoid possibility of use after free. Signed-off-by: Sage Weil <sage@newdream.net>
-rw-r--r--fs/ceph/snap.c24
1 files changed, 14 insertions, 10 deletions
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
index 2b881262ef6..d5114db7045 100644
--- a/fs/ceph/snap.c
+++ b/fs/ceph/snap.c
@@ -869,16 +869,20 @@ skip_inode:
continue;
ci = ceph_inode(inode);
spin_lock(&inode->i_lock);
- if (!ci->i_snap_realm)
- goto split_skip_inode;
- ceph_put_snap_realm(mdsc, ci->i_snap_realm);
- spin_lock(&realm->inodes_with_caps_lock);
- list_add(&ci->i_snap_realm_item,
- &realm->inodes_with_caps);
- ci->i_snap_realm = realm;
- spin_unlock(&realm->inodes_with_caps_lock);
- ceph_get_snap_realm(mdsc, realm);
-split_skip_inode:
+ if (list_empty(&ci->i_snap_realm_item)) {
+ struct ceph_snap_realm *oldrealm =
+ ci->i_snap_realm;
+
+ dout(" moving %p to split realm %llx %p\n",
+ inode, realm->ino, realm);
+ spin_lock(&realm->inodes_with_caps_lock);
+ list_add(&ci->i_snap_realm_item,
+ &realm->inodes_with_caps);
+ ci->i_snap_realm = realm;
+ spin_unlock(&realm->inodes_with_caps_lock);
+ ceph_get_snap_realm(mdsc, realm);
+ ceph_put_snap_realm(mdsc, oldrealm);
+ }
spin_unlock(&inode->i_lock);
iput(inode);
}