Commit 420eb6d7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-4.5-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 "Stable bugfixes:
   - Fix nfs_size_to_loff_t
   - NFSv4: Fix a dentry leak on alias use

  Other bugfixes:
   - Don't schedule a layoutreturn if the layout segment can be freed
     immediately.
   - Always set NFS_LAYOUT_RETURN_REQUESTED with lo->plh_return_iomode
   - rpcrdma_bc_receive_call() should init rq_private_buf.len
   - fix stateid handling for the NFS v4.2 operations
   - pnfs/blocklayout: fix a memeory leak when using,vmalloc_to_page
   - fix panic in gss_pipe_downcall() in fips mode
   - Fix a race between layoutget and pnfs_destroy_layout
   - Fix a race between layoutget and bulk recalls"

* tag 'nfs-for-4.5-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFSv4.x/pnfs: Fix a race between layoutget and bulk recalls
  NFSv4.x/pnfs: Fix a race between layoutget and pnfs_destroy_layout
  auth_gss: fix panic in gss_pipe_downcall() in fips mode
  pnfs/blocklayout: fix a memeory leak when using,vmalloc_to_page
  nfs4: fix stateid handling for the NFS v4.2 operations
  NFSv4: Fix a dentry leak on alias use
  xprtrdma: rpcrdma_bc_receive_call() should init rq_private_buf.len
  pNFS: Always set NFS_LAYOUT_RETURN_REQUESTED with lo->plh_return_iomode
  pNFS: Fix pnfs_mark_matching_lsegs_return()
  nfs: fix nfs_size_to_loff_t
parents de9e478b 9fd4b9fc
...@@ -476,6 +476,7 @@ static void ext_tree_free_commitdata(struct nfs4_layoutcommit_args *arg, ...@@ -476,6 +476,7 @@ static void ext_tree_free_commitdata(struct nfs4_layoutcommit_args *arg,
for (i = 0; i < nr_pages; i++) for (i = 0; i < nr_pages; i++)
put_page(arg->layoutupdate_pages[i]); put_page(arg->layoutupdate_pages[i]);
vfree(arg->start_p);
kfree(arg->layoutupdate_pages); kfree(arg->layoutupdate_pages);
} else { } else {
put_page(arg->layoutupdate_page); put_page(arg->layoutupdate_page);
...@@ -559,10 +560,15 @@ ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg) ...@@ -559,10 +560,15 @@ ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg)
if (unlikely(arg->layoutupdate_pages != &arg->layoutupdate_page)) { if (unlikely(arg->layoutupdate_pages != &arg->layoutupdate_page)) {
void *p = start_p, *end = p + arg->layoutupdate_len; void *p = start_p, *end = p + arg->layoutupdate_len;
struct page *page = NULL;
int i = 0; int i = 0;
for ( ; p < end; p += PAGE_SIZE) arg->start_p = start_p;
arg->layoutupdate_pages[i++] = vmalloc_to_page(p); for ( ; p < end; p += PAGE_SIZE) {
page = vmalloc_to_page(p);
arg->layoutupdate_pages[i++] = page;
get_page(page);
}
} }
dprintk("%s found %zu ranges\n", __func__, count); dprintk("%s found %zu ranges\n", __func__, count);
......
...@@ -16,29 +16,8 @@ ...@@ -16,29 +16,8 @@
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
static int nfs42_set_rw_stateid(nfs4_stateid *dst, struct file *file,
fmode_t fmode)
{
struct nfs_open_context *open;
struct nfs_lock_context *lock;
int ret;
open = get_nfs_open_context(nfs_file_open_context(file));
lock = nfs_get_lock_context(open);
if (IS_ERR(lock)) {
put_nfs_open_context(open);
return PTR_ERR(lock);
}
ret = nfs4_set_rw_stateid(dst, open, lock, fmode);
nfs_put_lock_context(lock);
put_nfs_open_context(open);
return ret;
}
static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
loff_t offset, loff_t len) struct nfs_lock_context *lock, loff_t offset, loff_t len)
{ {
struct inode *inode = file_inode(filep); struct inode *inode = file_inode(filep);
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
...@@ -56,7 +35,8 @@ static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, ...@@ -56,7 +35,8 @@ static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
msg->rpc_argp = &args; msg->rpc_argp = &args;
msg->rpc_resp = &res; msg->rpc_resp = &res;
status = nfs42_set_rw_stateid(&args.falloc_stateid, filep, FMODE_WRITE); status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
lock, FMODE_WRITE);
if (status) if (status)
return status; return status;
...@@ -78,15 +58,26 @@ static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, ...@@ -78,15 +58,26 @@ static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
{ {
struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs_server *server = NFS_SERVER(file_inode(filep));
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
struct nfs_lock_context *lock;
int err; int err;
lock = nfs_get_lock_context(nfs_file_open_context(filep));
if (IS_ERR(lock))
return PTR_ERR(lock);
exception.inode = file_inode(filep);
exception.state = lock->open_context->state;
do { do {
err = _nfs42_proc_fallocate(msg, filep, offset, len); err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
if (err == -ENOTSUPP) if (err == -ENOTSUPP) {
return -EOPNOTSUPP; err = -EOPNOTSUPP;
break;
}
err = nfs4_handle_exception(server, err, &exception); err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
nfs_put_lock_context(lock);
return err; return err;
} }
...@@ -135,7 +126,8 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len) ...@@ -135,7 +126,8 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
return err; return err;
} }
static loff_t _nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) static loff_t _nfs42_proc_llseek(struct file *filep,
struct nfs_lock_context *lock, loff_t offset, int whence)
{ {
struct inode *inode = file_inode(filep); struct inode *inode = file_inode(filep);
struct nfs42_seek_args args = { struct nfs42_seek_args args = {
...@@ -156,7 +148,8 @@ static loff_t _nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) ...@@ -156,7 +148,8 @@ static loff_t _nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
if (!nfs_server_capable(inode, NFS_CAP_SEEK)) if (!nfs_server_capable(inode, NFS_CAP_SEEK))
return -ENOTSUPP; return -ENOTSUPP;
status = nfs42_set_rw_stateid(&args.sa_stateid, filep, FMODE_READ); status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
lock, FMODE_READ);
if (status) if (status)
return status; return status;
...@@ -175,17 +168,28 @@ loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) ...@@ -175,17 +168,28 @@ loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
{ {
struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs_server *server = NFS_SERVER(file_inode(filep));
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
struct nfs_lock_context *lock;
loff_t err; loff_t err;
lock = nfs_get_lock_context(nfs_file_open_context(filep));
if (IS_ERR(lock))
return PTR_ERR(lock);
exception.inode = file_inode(filep);
exception.state = lock->open_context->state;
do { do {
err = _nfs42_proc_llseek(filep, offset, whence); err = _nfs42_proc_llseek(filep, lock, offset, whence);
if (err >= 0) if (err >= 0)
break; break;
if (err == -ENOTSUPP) if (err == -ENOTSUPP) {
return -EOPNOTSUPP; err = -EOPNOTSUPP;
break;
}
err = nfs4_handle_exception(server, err, &exception); err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
nfs_put_lock_context(lock);
return err; return err;
} }
...@@ -298,8 +302,9 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server, ...@@ -298,8 +302,9 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server,
} }
static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
struct file *dst_f, loff_t src_offset, struct file *dst_f, struct nfs_lock_context *src_lock,
loff_t dst_offset, loff_t count) struct nfs_lock_context *dst_lock, loff_t src_offset,
loff_t dst_offset, loff_t count)
{ {
struct inode *src_inode = file_inode(src_f); struct inode *src_inode = file_inode(src_f);
struct inode *dst_inode = file_inode(dst_f); struct inode *dst_inode = file_inode(dst_f);
...@@ -320,11 +325,13 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, ...@@ -320,11 +325,13 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
msg->rpc_argp = &args; msg->rpc_argp = &args;
msg->rpc_resp = &res; msg->rpc_resp = &res;
status = nfs42_set_rw_stateid(&args.src_stateid, src_f, FMODE_READ); status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
src_lock, FMODE_READ);
if (status) if (status)
return status; return status;
status = nfs42_set_rw_stateid(&args.dst_stateid, dst_f, FMODE_WRITE); status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
dst_lock, FMODE_WRITE);
if (status) if (status)
return status; return status;
...@@ -349,22 +356,48 @@ int nfs42_proc_clone(struct file *src_f, struct file *dst_f, ...@@ -349,22 +356,48 @@ int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
}; };
struct inode *inode = file_inode(src_f); struct inode *inode = file_inode(src_f);
struct nfs_server *server = NFS_SERVER(file_inode(src_f)); struct nfs_server *server = NFS_SERVER(file_inode(src_f));
struct nfs4_exception exception = { }; struct nfs_lock_context *src_lock;
int err; struct nfs_lock_context *dst_lock;
struct nfs4_exception src_exception = { };
struct nfs4_exception dst_exception = { };
int err, err2;
if (!nfs_server_capable(inode, NFS_CAP_CLONE)) if (!nfs_server_capable(inode, NFS_CAP_CLONE))
return -EOPNOTSUPP; return -EOPNOTSUPP;
src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
if (IS_ERR(src_lock))
return PTR_ERR(src_lock);
src_exception.inode = file_inode(src_f);
src_exception.state = src_lock->open_context->state;
dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
if (IS_ERR(dst_lock)) {
err = PTR_ERR(dst_lock);
goto out_put_src_lock;
}
dst_exception.inode = file_inode(dst_f);
dst_exception.state = dst_lock->open_context->state;
do { do {
err = _nfs42_proc_clone(&msg, src_f, dst_f, src_offset, err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
dst_offset, count); src_offset, dst_offset, count);
if (err == -ENOTSUPP || err == -EOPNOTSUPP) { if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE; NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
return -EOPNOTSUPP; err = -EOPNOTSUPP;
break;
} }
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err; err2 = nfs4_handle_exception(server, err, &src_exception);
err = nfs4_handle_exception(server, err, &dst_exception);
if (!err)
err = err2;
} while (src_exception.retry || dst_exception.retry);
nfs_put_lock_context(dst_lock);
out_put_src_lock:
nfs_put_lock_context(src_lock);
return err;
} }
...@@ -2466,9 +2466,9 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, ...@@ -2466,9 +2466,9 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
dentry = d_add_unique(dentry, igrab(state->inode)); dentry = d_add_unique(dentry, igrab(state->inode));
if (dentry == NULL) { if (dentry == NULL) {
dentry = opendata->dentry; dentry = opendata->dentry;
} else if (dentry != ctx->dentry) { } else {
dput(ctx->dentry); dput(ctx->dentry);
ctx->dentry = dget(dentry); ctx->dentry = dentry;
} }
nfs_set_verifier(dentry, nfs_set_verifier(dentry,
nfs_save_change_attribute(d_inode(opendata->dir))); nfs_save_change_attribute(d_inode(opendata->dir)));
......
...@@ -252,6 +252,27 @@ pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo) ...@@ -252,6 +252,27 @@ pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo)
} }
} }
/*
* Mark a pnfs_layout_hdr and all associated layout segments as invalid
*
* In order to continue using the pnfs_layout_hdr, a full recovery
* is required.
* Note that caller must hold inode->i_lock.
*/
static int
pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
struct list_head *lseg_list)
{
struct pnfs_layout_range range = {
.iomode = IOMODE_ANY,
.offset = 0,
.length = NFS4_MAX_UINT64,
};
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
return pnfs_mark_matching_lsegs_invalid(lo, lseg_list, &range);
}
static int static int
pnfs_iomode_to_fail_bit(u32 iomode) pnfs_iomode_to_fail_bit(u32 iomode)
{ {
...@@ -554,9 +575,8 @@ pnfs_destroy_layout(struct nfs_inode *nfsi) ...@@ -554,9 +575,8 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
spin_lock(&nfsi->vfs_inode.i_lock); spin_lock(&nfsi->vfs_inode.i_lock);
lo = nfsi->layout; lo = nfsi->layout;
if (lo) { if (lo) {
lo->plh_block_lgets++; /* permanently block new LAYOUTGETs */
pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL);
pnfs_get_layout_hdr(lo); pnfs_get_layout_hdr(lo);
pnfs_mark_layout_stateid_invalid(lo, &tmp_list);
pnfs_layout_clear_fail_bit(lo, NFS_LAYOUT_RO_FAILED); pnfs_layout_clear_fail_bit(lo, NFS_LAYOUT_RO_FAILED);
pnfs_layout_clear_fail_bit(lo, NFS_LAYOUT_RW_FAILED); pnfs_layout_clear_fail_bit(lo, NFS_LAYOUT_RW_FAILED);
spin_unlock(&nfsi->vfs_inode.i_lock); spin_unlock(&nfsi->vfs_inode.i_lock);
...@@ -617,11 +637,6 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list, ...@@ -617,11 +637,6 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list,
{ {
struct pnfs_layout_hdr *lo; struct pnfs_layout_hdr *lo;
struct inode *inode; struct inode *inode;
struct pnfs_layout_range range = {
.iomode = IOMODE_ANY,
.offset = 0,
.length = NFS4_MAX_UINT64,
};
LIST_HEAD(lseg_list); LIST_HEAD(lseg_list);
int ret = 0; int ret = 0;
...@@ -636,11 +651,11 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list, ...@@ -636,11 +651,11 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list,
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
list_del_init(&lo->plh_bulk_destroy); list_del_init(&lo->plh_bulk_destroy);
lo->plh_block_lgets++; /* permanently block new LAYOUTGETs */ if (pnfs_mark_layout_stateid_invalid(lo, &lseg_list)) {
if (is_bulk_recall) if (is_bulk_recall)
set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags); set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
if (pnfs_mark_matching_lsegs_invalid(lo, &lseg_list, &range))
ret = -EAGAIN; ret = -EAGAIN;
}
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&lseg_list); pnfs_free_lseg_list(&lseg_list);
/* Free all lsegs that are attached to commit buckets */ /* Free all lsegs that are attached to commit buckets */
...@@ -1738,8 +1753,19 @@ pnfs_set_plh_return_iomode(struct pnfs_layout_hdr *lo, enum pnfs_iomode iomode) ...@@ -1738,8 +1753,19 @@ pnfs_set_plh_return_iomode(struct pnfs_layout_hdr *lo, enum pnfs_iomode iomode)
if (lo->plh_return_iomode != 0) if (lo->plh_return_iomode != 0)
iomode = IOMODE_ANY; iomode = IOMODE_ANY;
lo->plh_return_iomode = iomode; lo->plh_return_iomode = iomode;
set_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags);
} }
/**
* pnfs_mark_matching_lsegs_return - Free or return matching layout segments
* @lo: pointer to layout header
* @tmp_list: list header to be used with pnfs_free_lseg_list()
* @return_range: describe layout segment ranges to be returned
*
* This function is mainly intended for use by layoutrecall. It attempts
* to free the layout segment immediately, or else to mark it for return
* as soon as its reference count drops to zero.
*/
int int
pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo, pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
struct list_head *tmp_list, struct list_head *tmp_list,
...@@ -1762,12 +1788,11 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo, ...@@ -1762,12 +1788,11 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
lseg, lseg->pls_range.iomode, lseg, lseg->pls_range.iomode,
lseg->pls_range.offset, lseg->pls_range.offset,
lseg->pls_range.length); lseg->pls_range.length);
if (mark_lseg_invalid(lseg, tmp_list))
continue;
remaining++;
set_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags); set_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags);
pnfs_set_plh_return_iomode(lo, return_range->iomode); pnfs_set_plh_return_iomode(lo, return_range->iomode);
if (!mark_lseg_invalid(lseg, tmp_list))
remaining++;
set_bit(NFS_LAYOUT_RETURN_REQUESTED,
&lo->plh_flags);
} }
return remaining; return remaining;
} }
......
...@@ -550,9 +550,7 @@ extern int nfs_readpage_async(struct nfs_open_context *, struct inode *, ...@@ -550,9 +550,7 @@ extern int nfs_readpage_async(struct nfs_open_context *, struct inode *,
static inline loff_t nfs_size_to_loff_t(__u64 size) static inline loff_t nfs_size_to_loff_t(__u64 size)
{ {
if (size > (__u64) OFFSET_MAX - 1) return min_t(u64, size, OFFSET_MAX);
return OFFSET_MAX - 1;
return (loff_t) size;
} }
static inline ino_t static inline ino_t
......
...@@ -275,6 +275,7 @@ struct nfs4_layoutcommit_args { ...@@ -275,6 +275,7 @@ struct nfs4_layoutcommit_args {
size_t layoutupdate_len; size_t layoutupdate_len;
struct page *layoutupdate_page; struct page *layoutupdate_page;
struct page **layoutupdate_pages; struct page **layoutupdate_pages;
__be32 *start_p;
}; };
struct nfs4_layoutcommit_res { struct nfs4_layoutcommit_res {
......
...@@ -740,7 +740,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -740,7 +740,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
default: default:
printk(KERN_CRIT "%s: bad return from " printk(KERN_CRIT "%s: bad return from "
"gss_fill_context: %zd\n", __func__, err); "gss_fill_context: %zd\n", __func__, err);
BUG(); gss_msg->msg.errno = -EIO;
} }
goto err_release_msg; goto err_release_msg;
} }
......
...@@ -341,6 +341,8 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt, ...@@ -341,6 +341,8 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt,
rqst->rq_reply_bytes_recvd = 0; rqst->rq_reply_bytes_recvd = 0;
rqst->rq_bytes_sent = 0; rqst->rq_bytes_sent = 0;
rqst->rq_xid = headerp->rm_xid; rqst->rq_xid = headerp->rm_xid;
rqst->rq_private_buf.len = size;
set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state); set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state);
buf = &rqst->rq_rcv_buf; buf = &rqst->rq_rcv_buf;
......
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