diff options
author | Sage Weil <sage@newdream.net> | 2011-11-05 22:06:31 -0700 |
---|---|---|
committer | Sage Weil <sage@newdream.net> | 2011-11-05 22:06:31 -0700 |
commit | 15a2015fbc692e1c97d7ce12d96e077f5ae7ea6d (patch) | |
tree | f902cc2b7cab475c8b220147bb786dc3a772888e | |
parent | 0c6d4b4e22a513f8563a2e00c5ab08e9f8796272 (diff) | |
download | linux-3.10-15a2015fbc692e1c97d7ce12d96e077f5ae7ea6d.tar.gz linux-3.10-15a2015fbc692e1c97d7ce12d96e077f5ae7ea6d.tar.bz2 linux-3.10-15a2015fbc692e1c97d7ce12d96e077f5ae7ea6d.zip |
ceph: fix iput race when queueing inode work
If we queue a work item that calls iput(), make sure we ihold() before
attempting to queue work. Otherwise our queued work might miraculously run
before we notice the queue_work() succeeded and call ihold(), allowing the
inode to be destroyed.
That is, instead of
if (queue_work(...))
ihold();
we need to do
ihold();
if (!queue_work(...))
iput();
Reported-by: Amon Ott <a.ott@m-privacy.de>
Signed-off-by: Sage Weil <sage@newdream.net>
-rw-r--r-- | fs/ceph/inode.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index e392bfce84a..116f36502f1 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1328,12 +1328,13 @@ int ceph_inode_set_size(struct inode *inode, loff_t size) */ void ceph_queue_writeback(struct inode *inode) { + ihold(inode); if (queue_work(ceph_inode_to_client(inode)->wb_wq, &ceph_inode(inode)->i_wb_work)) { dout("ceph_queue_writeback %p\n", inode); - ihold(inode); } else { dout("ceph_queue_writeback %p failed\n", inode); + iput(inode); } } @@ -1353,12 +1354,13 @@ static void ceph_writeback_work(struct work_struct *work) */ void ceph_queue_invalidate(struct inode *inode) { + ihold(inode); if (queue_work(ceph_inode_to_client(inode)->pg_inv_wq, &ceph_inode(inode)->i_pg_inv_work)) { dout("ceph_queue_invalidate %p\n", inode); - ihold(inode); } else { dout("ceph_queue_invalidate %p failed\n", inode); + iput(inode); } } @@ -1434,13 +1436,14 @@ void ceph_queue_vmtruncate(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); + ihold(inode); if (queue_work(ceph_sb_to_client(inode->i_sb)->trunc_wq, &ci->i_vmtruncate_work)) { dout("ceph_queue_vmtruncate %p\n", inode); - ihold(inode); } else { dout("ceph_queue_vmtruncate %p failed, pending=%d\n", inode, ci->i_truncate_pending); + iput(inode); } } |