From 7e48dec552e42a76a508c2f4d78c64d6a51f1bfb Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 2 May 2008 10:40:46 -0700 Subject: [PATCH] kclient: more truncate tomfoolery --- src/kernel/file.c | 4 ++++ src/kernel/inode.c | 48 ++++++++++++++++++++++++++++++++++++---------- src/kernel/super.c | 2 ++ src/kernel/super.h | 3 ++- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/kernel/file.c b/src/kernel/file.c index 509b302afbe6a..03641da6331a5 100644 --- a/src/kernel/file.c +++ b/src/kernel/file.c @@ -249,6 +249,8 @@ ssize_t ceph_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) ssize_t ret; int got = 0; + __ceph_do_pending_vmtruncate(inode); + dout(10, "read %llx %llu~%u trying to get caps on %p\n", ceph_ino(inode), *ppos, (unsigned)len, inode); ret = wait_event_interruptible(ci->i_cap_wq, @@ -285,6 +287,8 @@ ssize_t ceph_write(struct file *filp, const char __user *buf, int check = 0; loff_t endoff = *ppos + len; + __ceph_do_pending_vmtruncate(inode); + /* do we need to explicitly request a larger max_size? */ spin_lock(&inode->i_lock); if ((endoff >= ci->i_max_size || diff --git a/src/kernel/inode.c b/src/kernel/inode.c index f9e9fa1575cd3..c8cc5b1b3dd72 100644 --- a/src/kernel/inode.c +++ b/src/kernel/inode.c @@ -1086,35 +1086,60 @@ void ceph_vmtruncate_work(struct work_struct *work) struct ceph_inode_info *ci = container_of(work, struct ceph_inode_info, i_vmtruncate_work); struct inode *inode = &ci->vfs_inode; - + dout(10, "vmtruncate_work %p\n", inode); mutex_lock(&inode->i_mutex); - if (inode->i_size < ci->i_vmtruncate_from) - vmtruncate(inode, inode->i_size); + __ceph_do_pending_vmtruncate(inode); mutex_unlock(&inode->i_mutex); } +void __ceph_do_pending_vmtruncate(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + loff_t to; + + spin_lock(&inode->i_lock); + to = ci->i_vmtruncate_to; + ci->i_vmtruncate_to = -1; + spin_unlock(&inode->i_lock); + + if (to >= 0) { + dout(10, "__do_pending_vmtruncate %p to %lld\n", inode, to); + vmtruncate(inode, to); + } else + dout(10, "__do_pending_vmtruncate %p nothing to do\n", inode); +} + static void apply_cap_truncate(struct inode *inode, loff_t size) { struct ceph_client *client = ceph_client(inode->i_sb); struct ceph_inode_info *ci = ceph_inode(inode); - int queue = 0; + int queue_trunc = 0; + spin_lock(&inode->i_lock); /* * vmtruncate lazily; we can't block on i_mutex in the message * handler path, or we deadlock against osd op replies needed * to complete the writes holding the lock... + * + * if its an expansion, and there is no truncate pending, we + * don't need to truncate. */ - spin_lock(&inode->i_lock); - if (size < inode->i_size) { - ci->i_vmtruncate_from = inode->i_size; - queue = 1; + if (ci->i_vmtruncate_to < 0 && size > inode->i_size) + dout(10, "clean fwd truncate, no vmtruncate needed\n"); + else if (ci->i_vmtruncate_to >= 0 && size >= ci->i_vmtruncate_to) + dout(10, "trunc to %lld < %lld already queued\n", + ci->i_vmtruncate_to, size); + else { + /* we need to trunc even smaller */ + dout(10, "queueing trunc %lld -> %lld\n", inode->i_size, size); + ci->i_vmtruncate_to = size; + queue_trunc = 1; } i_size_write(inode, size); ci->i_reported_size = size; spin_unlock(&inode->i_lock); - - if (queue) + if (queue_trunc) queue_work(client->trunc_wq, &ci->i_vmtruncate_work); } @@ -1437,6 +1462,7 @@ static int ceph_setattr_size(struct dentry *dentry, struct iattr *attr) err = ceph_mdsc_do_request(mdsc, req); ceph_mdsc_put_request(req); dout(10, "truncate result %d\n", err); + __ceph_do_pending_vmtruncate(inode); if (err) return err; @@ -1450,6 +1476,8 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) const unsigned int ia_valid = attr->ia_valid; int err; + __ceph_do_pending_vmtruncate(inode); + err = inode_change_ok(inode, attr); if (err != 0) return err; diff --git a/src/kernel/super.c b/src/kernel/super.c index b57d47ed54bf2..d56a99494ca3f 100644 --- a/src/kernel/super.c +++ b/src/kernel/super.c @@ -177,6 +177,8 @@ static struct inode *ceph_alloc_inode(struct super_block *sb) ci->i_hashval = 0; INIT_WORK(&ci->i_wb_work, ceph_inode_writeback); + + ci->i_vmtruncate_to = -1; INIT_WORK(&ci->i_vmtruncate_work, ceph_vmtruncate_work); return &ci->vfs_inode; diff --git a/src/kernel/super.h b/src/kernel/super.h index 05afc0d359567..2f599f30c1dc6 100644 --- a/src/kernel/super.h +++ b/src/kernel/super.h @@ -199,7 +199,7 @@ struct ceph_inode_info { struct work_struct i_wb_work; /* writeback work */ - loff_t i_vmtruncate_from; + loff_t i_vmtruncate_to; struct work_struct i_vmtruncate_work; struct inode vfs_inode; /* at end */ @@ -394,6 +394,7 @@ extern void ceph_check_caps(struct ceph_inode_info *ci, int is_delayed); extern void ceph_inode_set_size(struct inode *inode, loff_t size); extern void ceph_inode_writeback(struct work_struct *work); extern void ceph_vmtruncate_work(struct work_struct *work); +extern void __ceph_do_pending_vmtruncate(struct inode *inode); extern int ceph_setattr(struct dentry *dentry, struct iattr *attr); extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, -- 2.39.5