Commit 0fdfe5ad authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6:
  NFSv4: fix delegated locking
  NFS: Ensure that the WRITE and COMMIT RPC calls are always uninterruptible
  NFS: Fix a race with the new commit code
  NFS: Ensure that writeback_single_inode() calls write_inode() when syncing
  NFS: Fix the mode calculation in nfs_find_open_context
  NFSv4: Fall back to ordinary lookup if nfs4_atomic_open() returns EISDIR
parents 44d2d371 0df5dd4a
...@@ -1294,7 +1294,8 @@ static int nfs4_init_server(struct nfs_server *server, ...@@ -1294,7 +1294,8 @@ static int nfs4_init_server(struct nfs_server *server,
/* Initialise the client representation from the mount data */ /* Initialise the client representation from the mount data */
server->flags = data->flags; server->flags = data->flags;
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|
NFS_CAP_POSIX_LOCK;
server->options = data->options; server->options = data->options;
/* Get a client record */ /* Get a client record */
......
...@@ -1025,12 +1025,12 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -1025,12 +1025,12 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
res = NULL; res = NULL;
goto out; goto out;
/* This turned out not to be a regular file */ /* This turned out not to be a regular file */
case -EISDIR:
case -ENOTDIR: case -ENOTDIR:
goto no_open; goto no_open;
case -ELOOP: case -ELOOP:
if (!(nd->intent.open.flags & O_NOFOLLOW)) if (!(nd->intent.open.flags & O_NOFOLLOW))
goto no_open; goto no_open;
/* case -EISDIR: */
/* case -EINVAL: */ /* case -EINVAL: */
default: default:
goto out; goto out;
......
...@@ -623,11 +623,11 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_c ...@@ -623,11 +623,11 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_c
list_for_each_entry(pos, &nfsi->open_files, list) { list_for_each_entry(pos, &nfsi->open_files, list) {
if (cred != NULL && pos->cred != cred) if (cred != NULL && pos->cred != cred)
continue; continue;
if ((pos->mode & mode) == mode) { if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode)
continue;
ctx = get_nfs_open_context(pos); ctx = get_nfs_open_context(pos);
break; break;
} }
}
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
return ctx; return ctx;
} }
......
...@@ -1523,6 +1523,8 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) ...@@ -1523,6 +1523,8 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
nfs_post_op_update_inode(dir, o_res->dir_attr); nfs_post_op_update_inode(dir, o_res->dir_attr);
} else } else
nfs_refresh_inode(dir, o_res->dir_attr); nfs_refresh_inode(dir, o_res->dir_attr);
if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
server->caps &= ~NFS_CAP_POSIX_LOCK;
if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
status = _nfs4_proc_open_confirm(data); status = _nfs4_proc_open_confirm(data);
if (status != 0) if (status != 0)
...@@ -1664,7 +1666,7 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, in ...@@ -1664,7 +1666,7 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, in
status = PTR_ERR(state); status = PTR_ERR(state);
if (IS_ERR(state)) if (IS_ERR(state))
goto err_opendata_put; goto err_opendata_put;
if ((opendata->o_res.rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) != 0) if (server->caps & NFS_CAP_POSIX_LOCK)
set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
nfs4_opendata_put(opendata); nfs4_opendata_put(opendata);
nfs4_put_state_owner(sp); nfs4_put_state_owner(sp);
......
...@@ -201,6 +201,7 @@ static int nfs_set_page_writeback(struct page *page) ...@@ -201,6 +201,7 @@ static int nfs_set_page_writeback(struct page *page)
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct nfs_server *nfss = NFS_SERVER(inode); struct nfs_server *nfss = NFS_SERVER(inode);
page_cache_get(page);
if (atomic_long_inc_return(&nfss->writeback) > if (atomic_long_inc_return(&nfss->writeback) >
NFS_CONGESTION_ON_THRESH) { NFS_CONGESTION_ON_THRESH) {
set_bdi_congested(&nfss->backing_dev_info, set_bdi_congested(&nfss->backing_dev_info,
...@@ -216,6 +217,7 @@ static void nfs_end_page_writeback(struct page *page) ...@@ -216,6 +217,7 @@ static void nfs_end_page_writeback(struct page *page)
struct nfs_server *nfss = NFS_SERVER(inode); struct nfs_server *nfss = NFS_SERVER(inode);
end_page_writeback(page); end_page_writeback(page);
page_cache_release(page);
if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
} }
...@@ -421,6 +423,7 @@ static void ...@@ -421,6 +423,7 @@ static void
nfs_mark_request_dirty(struct nfs_page *req) nfs_mark_request_dirty(struct nfs_page *req)
{ {
__set_page_dirty_nobuffers(req->wb_page); __set_page_dirty_nobuffers(req->wb_page);
__mark_inode_dirty(req->wb_page->mapping->host, I_DIRTY_DATASYNC);
} }
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
...@@ -660,9 +663,11 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, ...@@ -660,9 +663,11 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
req = nfs_setup_write_request(ctx, page, offset, count); req = nfs_setup_write_request(ctx, page, offset, count);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
nfs_mark_request_dirty(req);
/* Update file length */ /* Update file length */
nfs_grow_file(page, offset, count); nfs_grow_file(page, offset, count);
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
nfs_mark_request_dirty(req);
nfs_clear_page_tag_locked(req); nfs_clear_page_tag_locked(req);
return 0; return 0;
} }
...@@ -739,8 +744,6 @@ int nfs_updatepage(struct file *file, struct page *page, ...@@ -739,8 +744,6 @@ int nfs_updatepage(struct file *file, struct page *page,
status = nfs_writepage_setup(ctx, page, offset, count); status = nfs_writepage_setup(ctx, page, offset, count);
if (status < 0) if (status < 0)
nfs_set_pageerror(page); nfs_set_pageerror(page);
else
__set_page_dirty_nobuffers(page);
dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n", dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n",
status, (long long)i_size_read(inode)); status, (long long)i_size_read(inode));
...@@ -749,13 +752,12 @@ int nfs_updatepage(struct file *file, struct page *page, ...@@ -749,13 +752,12 @@ int nfs_updatepage(struct file *file, struct page *page,
static void nfs_writepage_release(struct nfs_page *req) static void nfs_writepage_release(struct nfs_page *req)
{ {
struct page *page = req->wb_page;
if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) { if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req))
nfs_end_page_writeback(req->wb_page);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
} else
nfs_end_page_writeback(req->wb_page);
nfs_clear_page_tag_locked(req); nfs_clear_page_tag_locked(req);
nfs_end_page_writeback(page);
} }
static int flush_task_priority(int how) static int flush_task_priority(int how)
...@@ -779,7 +781,6 @@ static int nfs_write_rpcsetup(struct nfs_page *req, ...@@ -779,7 +781,6 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
int how) int how)
{ {
struct inode *inode = req->wb_context->path.dentry->d_inode; struct inode *inode = req->wb_context->path.dentry->d_inode;
int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
int priority = flush_task_priority(how); int priority = flush_task_priority(how);
struct rpc_task *task; struct rpc_task *task;
struct rpc_message msg = { struct rpc_message msg = {
...@@ -794,9 +795,10 @@ static int nfs_write_rpcsetup(struct nfs_page *req, ...@@ -794,9 +795,10 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
.callback_ops = call_ops, .callback_ops = call_ops,
.callback_data = data, .callback_data = data,
.workqueue = nfsiod_workqueue, .workqueue = nfsiod_workqueue,
.flags = flags, .flags = RPC_TASK_ASYNC,
.priority = priority, .priority = priority,
}; };
int ret = 0;
/* Set up the RPC argument and reply structs /* Set up the RPC argument and reply structs
* NB: take care not to mess about with data->commit et al. */ * NB: take care not to mess about with data->commit et al. */
...@@ -835,10 +837,18 @@ static int nfs_write_rpcsetup(struct nfs_page *req, ...@@ -835,10 +837,18 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
(unsigned long long)data->args.offset); (unsigned long long)data->args.offset);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task)) {
return PTR_ERR(task); ret = PTR_ERR(task);
goto out;
}
if (how & FLUSH_SYNC) {
ret = rpc_wait_for_completion_task(task);
if (ret == 0)
ret = task->tk_status;
}
rpc_put_task(task); rpc_put_task(task);
return 0; out:
return ret;
} }
/* If a nfs_flush_* function fails, it should remove reqs from @head and /* If a nfs_flush_* function fails, it should remove reqs from @head and
...@@ -847,9 +857,11 @@ static int nfs_write_rpcsetup(struct nfs_page *req, ...@@ -847,9 +857,11 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
*/ */
static void nfs_redirty_request(struct nfs_page *req) static void nfs_redirty_request(struct nfs_page *req)
{ {
struct page *page = req->wb_page;
nfs_mark_request_dirty(req); nfs_mark_request_dirty(req);
nfs_end_page_writeback(req->wb_page);
nfs_clear_page_tag_locked(req); nfs_clear_page_tag_locked(req);
nfs_end_page_writeback(page);
} }
/* /*
...@@ -1084,16 +1096,15 @@ static void nfs_writeback_release_full(void *calldata) ...@@ -1084,16 +1096,15 @@ static void nfs_writeback_release_full(void *calldata)
if (nfs_write_need_commit(data)) { if (nfs_write_need_commit(data)) {
memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));
nfs_mark_request_commit(req); nfs_mark_request_commit(req);
nfs_end_page_writeback(page);
dprintk(" marked for commit\n"); dprintk(" marked for commit\n");
goto next; goto next;
} }
dprintk(" OK\n"); dprintk(" OK\n");
remove_request: remove_request:
nfs_end_page_writeback(page);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
next: next:
nfs_clear_page_tag_locked(req); nfs_clear_page_tag_locked(req);
nfs_end_page_writeback(page);
} }
nfs_writedata_release(calldata); nfs_writedata_release(calldata);
} }
...@@ -1207,7 +1218,6 @@ static int nfs_commit_rpcsetup(struct list_head *head, ...@@ -1207,7 +1218,6 @@ static int nfs_commit_rpcsetup(struct list_head *head,
{ {
struct nfs_page *first = nfs_list_entry(head->next); struct nfs_page *first = nfs_list_entry(head->next);
struct inode *inode = first->wb_context->path.dentry->d_inode; struct inode *inode = first->wb_context->path.dentry->d_inode;
int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
int priority = flush_task_priority(how); int priority = flush_task_priority(how);
struct rpc_task *task; struct rpc_task *task;
struct rpc_message msg = { struct rpc_message msg = {
...@@ -1222,7 +1232,7 @@ static int nfs_commit_rpcsetup(struct list_head *head, ...@@ -1222,7 +1232,7 @@ static int nfs_commit_rpcsetup(struct list_head *head,
.callback_ops = &nfs_commit_ops, .callback_ops = &nfs_commit_ops,
.callback_data = data, .callback_data = data,
.workqueue = nfsiod_workqueue, .workqueue = nfsiod_workqueue,
.flags = flags, .flags = RPC_TASK_ASYNC,
.priority = priority, .priority = priority,
}; };
...@@ -1252,6 +1262,8 @@ static int nfs_commit_rpcsetup(struct list_head *head, ...@@ -1252,6 +1262,8 @@ static int nfs_commit_rpcsetup(struct list_head *head,
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_ERR(task); return PTR_ERR(task);
if (how & FLUSH_SYNC)
rpc_wait_for_completion_task(task);
rpc_put_task(task); rpc_put_task(task);
return 0; return 0;
} }
......
...@@ -176,6 +176,7 @@ struct nfs_server { ...@@ -176,6 +176,7 @@ struct nfs_server {
#define NFS_CAP_ATIME (1U << 11) #define NFS_CAP_ATIME (1U << 11)
#define NFS_CAP_CTIME (1U << 12) #define NFS_CAP_CTIME (1U << 12)
#define NFS_CAP_MTIME (1U << 13) #define NFS_CAP_MTIME (1U << 13)
#define NFS_CAP_POSIX_LOCK (1U << 14)
/* maximum number of slots to use */ /* maximum number of slots to use */
......
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