afs: Skip truncation on the server of data we haven't written yet
Don't send a truncation RPC to the server if we're only shortening data that's in the pagecache and is beyond the server's EOF. Also don't automatically force writeback on setattr, but do wait to store RPCs that are in the region to be removed on a shortening truncation. Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: kafs-testing@auristor.com Acked-by: Jeff Layton <jlayton@kernel.org> cc: Marc Dionne <marc.dionne@auristor.com> cc: linux-afs@lists.infradead.org Link: https://lore.kernel.org/r/163819663275.215744.4781075713714590913.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906972600.143852.14237659724463048094.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967177522.1823006.15336589054269480601.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021571880.640689.1837025861707111004.stgit@warthog.procyon.org.uk/ # v4
This commit is contained in:
Родитель
c7f75ef33b
Коммит
0770bd4187
|
@ -848,42 +848,67 @@ static const struct afs_operation_ops afs_setattr_operation = {
|
|||
int afs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
{
|
||||
const unsigned int supported =
|
||||
ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID |
|
||||
ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET | ATTR_TOUCH;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
|
||||
struct inode *inode = &vnode->vfs_inode;
|
||||
loff_t i_size;
|
||||
int ret;
|
||||
|
||||
_enter("{%llx:%llu},{n=%pd},%x",
|
||||
vnode->fid.vid, vnode->fid.vnode, dentry,
|
||||
attr->ia_valid);
|
||||
|
||||
if (!(attr->ia_valid & (ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID |
|
||||
ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET |
|
||||
ATTR_TOUCH))) {
|
||||
if (!(attr->ia_valid & supported)) {
|
||||
_leave(" = 0 [unsupported]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
i_size = i_size_read(inode);
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
if (!S_ISREG(vnode->vfs_inode.i_mode))
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return -EISDIR;
|
||||
|
||||
ret = inode_newsize_ok(&vnode->vfs_inode, attr->ia_size);
|
||||
ret = inode_newsize_ok(inode, attr->ia_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (attr->ia_size == i_size_read(&vnode->vfs_inode))
|
||||
if (attr->ia_size == i_size)
|
||||
attr->ia_valid &= ~ATTR_SIZE;
|
||||
}
|
||||
|
||||
fscache_use_cookie(afs_vnode_cache(vnode), true);
|
||||
|
||||
/* flush any dirty data outstanding on a regular file */
|
||||
if (S_ISREG(vnode->vfs_inode.i_mode))
|
||||
filemap_write_and_wait(vnode->vfs_inode.i_mapping);
|
||||
|
||||
/* Prevent any new writebacks from starting whilst we do this. */
|
||||
down_write(&vnode->validate_lock);
|
||||
|
||||
if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
|
||||
loff_t size = attr->ia_size;
|
||||
|
||||
/* Wait for any outstanding writes to the server to complete */
|
||||
loff_t from = min(size, i_size);
|
||||
loff_t to = max(size, i_size);
|
||||
ret = filemap_fdatawait_range(inode->i_mapping, from, to);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
/* Don't talk to the server if we're just shortening in-memory
|
||||
* writes that haven't gone to the server yet.
|
||||
*/
|
||||
if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) &&
|
||||
attr->ia_size < i_size &&
|
||||
attr->ia_size > vnode->status.size) {
|
||||
truncate_pagecache(inode, attr->ia_size);
|
||||
fscache_resize_cookie(afs_vnode_cache(vnode),
|
||||
attr->ia_size);
|
||||
i_size_write(inode, attr->ia_size);
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
op = afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ?
|
||||
afs_file_key(attr->ia_file) : NULL),
|
||||
vnode->volume);
|
||||
|
|
Загрузка…
Ссылка в новой задаче