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
This commit is contained in:
commit
0fdfe5ad28
6 changed files with 39 additions and 23 deletions
|
@ -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
|
||||||
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,10 +623,10 @@ 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)
|
||||||
ctx = get_nfs_open_context(pos);
|
continue;
|
||||||
break;
|
ctx = get_nfs_open_context(pos);
|
||||||
}
|
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)
|
||||||
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
|
||||||
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)
|
||||||
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)
|
||||||
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
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
.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,
|
||||||
(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,
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
.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,
|
||||||
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 {
|
||||||
#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 */
|
||||||
|
|
Loading…
Reference in a new issue