Commit 50d0f78f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'afs-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull afs/fscache fixes from David Howells:

 - Fix the default return of fscache_maybe_release_page() when a cache
   isn't in use - it prevents a filesystem from releasing pages. This
   can cause a system to OOM.

 - Fix a potential uninitialised variable in AFS.

 - Fix AFS unlink's handling of the nlink count. It needs to use the
   nlink manipulation functions so that inode structs of deleted inodes
   actually get scheduled for destruction.

 - Fix error handling in afs_write_end() so that the page gets unlocked
   and put if we can't fill the unwritten portion.

* 'afs-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix missing error handling in afs_write_end()
  afs: Fix unlink
  afs: Potential uninitialized variable in afs_extract_data()
  fscache: Fix the default for fscache_maybe_release_page()
parents b4df268d afae457d
...@@ -895,20 +895,38 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -895,20 +895,38 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
* However, if we didn't have a callback promise outstanding, or it was * However, if we didn't have a callback promise outstanding, or it was
* outstanding on a different server, then it won't break it either... * outstanding on a different server, then it won't break it either...
*/ */
static int afs_dir_remove_link(struct dentry *dentry, struct key *key) static int afs_dir_remove_link(struct dentry *dentry, struct key *key,
unsigned long d_version_before,
unsigned long d_version_after)
{ {
bool dir_valid;
int ret = 0; int ret = 0;
/* There were no intervening changes on the server if the version
* number we got back was incremented by exactly 1.
*/
dir_valid = (d_version_after == d_version_before + 1);
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) if (dir_valid) {
kdebug("AFS_VNODE_DELETED"); drop_nlink(&vnode->vfs_inode);
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); if (vnode->vfs_inode.i_nlink == 0) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
ret = afs_validate(vnode, key); clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
if (ret == -ESTALE) }
ret = 0; ret = 0;
} else {
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
kdebug("AFS_VNODE_DELETED");
ret = afs_validate(vnode, key);
if (ret == -ESTALE)
ret = 0;
}
_debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
} }
...@@ -923,6 +941,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -923,6 +941,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
struct key *key; struct key *key;
unsigned long d_version = (unsigned long)dentry->d_fsdata;
int ret; int ret;
_enter("{%x:%u},{%pd}", _enter("{%x:%u},{%pd}",
...@@ -955,7 +974,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -955,7 +974,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
afs_vnode_commit_status(&fc, dvnode, fc.cb_break); afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret == 0) if (ret == 0)
ret = afs_dir_remove_link(dentry, key); ret = afs_dir_remove_link(
dentry, key, d_version,
(unsigned long)dvnode->status.data_version);
} }
error_key: error_key:
......
...@@ -377,6 +377,10 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) ...@@ -377,6 +377,10 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
} }
read_sequnlock_excl(&vnode->cb_lock); read_sequnlock_excl(&vnode->cb_lock);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
clear_nlink(&vnode->vfs_inode);
if (valid) if (valid)
goto valid; goto valid;
......
...@@ -885,7 +885,7 @@ int afs_extract_data(struct afs_call *call, void *buf, size_t count, ...@@ -885,7 +885,7 @@ int afs_extract_data(struct afs_call *call, void *buf, size_t count,
{ {
struct afs_net *net = call->net; struct afs_net *net = call->net;
enum afs_call_state state; enum afs_call_state state;
u32 remote_abort; u32 remote_abort = 0;
int ret; int ret;
_enter("{%s,%zu},,%zu,%d", _enter("{%s,%zu},,%zu,%d",
......
...@@ -198,7 +198,7 @@ int afs_write_end(struct file *file, struct address_space *mapping, ...@@ -198,7 +198,7 @@ int afs_write_end(struct file *file, struct address_space *mapping,
ret = afs_fill_page(vnode, key, pos + copied, ret = afs_fill_page(vnode, key, pos + copied,
len - copied, page); len - copied, page);
if (ret < 0) if (ret < 0)
return ret; goto out;
} }
SetPageUptodate(page); SetPageUptodate(page);
} }
...@@ -206,10 +206,12 @@ int afs_write_end(struct file *file, struct address_space *mapping, ...@@ -206,10 +206,12 @@ int afs_write_end(struct file *file, struct address_space *mapping,
set_page_dirty(page); set_page_dirty(page);
if (PageDirty(page)) if (PageDirty(page))
_debug("dirtied"); _debug("dirtied");
ret = copied;
out:
unlock_page(page); unlock_page(page);
put_page(page); put_page(page);
return ret;
return copied;
} }
/* /*
......
...@@ -755,7 +755,7 @@ bool fscache_maybe_release_page(struct fscache_cookie *cookie, ...@@ -755,7 +755,7 @@ bool fscache_maybe_release_page(struct fscache_cookie *cookie,
{ {
if (fscache_cookie_valid(cookie) && PageFsCache(page)) if (fscache_cookie_valid(cookie) && PageFsCache(page))
return __fscache_maybe_release_page(cookie, page, gfp); return __fscache_maybe_release_page(cookie, page, gfp);
return false; return true;
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment