Commit 1ae88b2e authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

NFS: Fix an O_DIRECT Oops...

We can't call nfs_readdata_release()/nfs_writedata_release() without
first initialising and referencing args.context. Doing so inside
nfs_direct_read_schedule_segment()/nfs_direct_write_schedule_segment()
causes an Oops.

We should rather be calling nfs_readdata_free()/nfs_writedata_free() in
those cases.

Looking at the O_DIRECT code, the "struct nfs_direct_req" is already
referencing the nfs_open_context for us. Since the readdata and writedata
structures carry a reference to that, we can simplify things by getting rid
of the extra nfs_open_context references, so that we can replace all
instances of nfs_readdata_release()/nfs_writedata_release().
Reported-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Tested-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Cc: stable@kernel.org
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7cb7beb3
...@@ -255,7 +255,7 @@ static void nfs_direct_read_release(void *calldata) ...@@ -255,7 +255,7 @@ static void nfs_direct_read_release(void *calldata)
if (put_dreq(dreq)) if (put_dreq(dreq))
nfs_direct_complete(dreq); nfs_direct_complete(dreq);
nfs_readdata_release(calldata); nfs_readdata_free(data);
} }
static const struct rpc_call_ops nfs_read_direct_ops = { static const struct rpc_call_ops nfs_read_direct_ops = {
...@@ -314,14 +314,14 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq, ...@@ -314,14 +314,14 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
data->npages, 1, 0, data->pagevec, NULL); data->npages, 1, 0, data->pagevec, NULL);
up_read(&current->mm->mmap_sem); up_read(&current->mm->mmap_sem);
if (result < 0) { if (result < 0) {
nfs_readdata_release(data); nfs_readdata_free(data);
break; break;
} }
if ((unsigned)result < data->npages) { if ((unsigned)result < data->npages) {
bytes = result * PAGE_SIZE; bytes = result * PAGE_SIZE;
if (bytes <= pgbase) { if (bytes <= pgbase) {
nfs_direct_release_pages(data->pagevec, result); nfs_direct_release_pages(data->pagevec, result);
nfs_readdata_release(data); nfs_readdata_free(data);
break; break;
} }
bytes -= pgbase; bytes -= pgbase;
...@@ -334,7 +334,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq, ...@@ -334,7 +334,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
data->inode = inode; data->inode = inode;
data->cred = msg.rpc_cred; data->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
data->args.context = get_nfs_open_context(ctx); data->args.context = ctx;
data->args.offset = pos; data->args.offset = pos;
data->args.pgbase = pgbase; data->args.pgbase = pgbase;
data->args.pages = data->pagevec; data->args.pages = data->pagevec;
...@@ -441,7 +441,7 @@ static void nfs_direct_free_writedata(struct nfs_direct_req *dreq) ...@@ -441,7 +441,7 @@ static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages); struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages);
list_del(&data->pages); list_del(&data->pages);
nfs_direct_release_pages(data->pagevec, data->npages); nfs_direct_release_pages(data->pagevec, data->npages);
nfs_writedata_release(data); nfs_writedata_free(data);
} }
} }
...@@ -534,7 +534,7 @@ static void nfs_direct_commit_release(void *calldata) ...@@ -534,7 +534,7 @@ static void nfs_direct_commit_release(void *calldata)
dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status); dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status);
nfs_direct_write_complete(dreq, data->inode); nfs_direct_write_complete(dreq, data->inode);
nfs_commitdata_release(calldata); nfs_commit_free(data);
} }
static const struct rpc_call_ops nfs_commit_direct_ops = { static const struct rpc_call_ops nfs_commit_direct_ops = {
...@@ -570,7 +570,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) ...@@ -570,7 +570,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
data->args.fh = NFS_FH(data->inode); data->args.fh = NFS_FH(data->inode);
data->args.offset = 0; data->args.offset = 0;
data->args.count = 0; data->args.count = 0;
data->args.context = get_nfs_open_context(dreq->ctx); data->args.context = dreq->ctx;
data->res.count = 0; data->res.count = 0;
data->res.fattr = &data->fattr; data->res.fattr = &data->fattr;
data->res.verf = &data->verf; data->res.verf = &data->verf;
...@@ -734,14 +734,14 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, ...@@ -734,14 +734,14 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->npages, 0, 0, data->pagevec, NULL); data->npages, 0, 0, data->pagevec, NULL);
up_read(&current->mm->mmap_sem); up_read(&current->mm->mmap_sem);
if (result < 0) { if (result < 0) {
nfs_writedata_release(data); nfs_writedata_free(data);
break; break;
} }
if ((unsigned)result < data->npages) { if ((unsigned)result < data->npages) {
bytes = result * PAGE_SIZE; bytes = result * PAGE_SIZE;
if (bytes <= pgbase) { if (bytes <= pgbase) {
nfs_direct_release_pages(data->pagevec, result); nfs_direct_release_pages(data->pagevec, result);
nfs_writedata_release(data); nfs_writedata_free(data);
break; break;
} }
bytes -= pgbase; bytes -= pgbase;
...@@ -756,7 +756,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, ...@@ -756,7 +756,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->inode = inode; data->inode = inode;
data->cred = msg.rpc_cred; data->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
data->args.context = get_nfs_open_context(ctx); data->args.context = ctx;
data->args.offset = pos; data->args.offset = pos;
data->args.pgbase = pgbase; data->args.pgbase = pgbase;
data->args.pages = data->pagevec; data->args.pages = data->pagevec;
......
...@@ -60,17 +60,15 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) ...@@ -60,17 +60,15 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
return p; return p;
} }
static void nfs_readdata_free(struct nfs_read_data *p) void nfs_readdata_free(struct nfs_read_data *p)
{ {
if (p && (p->pagevec != &p->page_array[0])) if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec); kfree(p->pagevec);
mempool_free(p, nfs_rdata_mempool); mempool_free(p, nfs_rdata_mempool);
} }
void nfs_readdata_release(void *data) static void nfs_readdata_release(struct nfs_read_data *rdata)
{ {
struct nfs_read_data *rdata = data;
put_nfs_open_context(rdata->args.context); put_nfs_open_context(rdata->args.context);
nfs_readdata_free(rdata); nfs_readdata_free(rdata);
} }
......
...@@ -87,17 +87,15 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) ...@@ -87,17 +87,15 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
return p; return p;
} }
static void nfs_writedata_free(struct nfs_write_data *p) void nfs_writedata_free(struct nfs_write_data *p)
{ {
if (p && (p->pagevec != &p->page_array[0])) if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec); kfree(p->pagevec);
mempool_free(p, nfs_wdata_mempool); mempool_free(p, nfs_wdata_mempool);
} }
void nfs_writedata_release(void *data) static void nfs_writedata_release(struct nfs_write_data *wdata)
{ {
struct nfs_write_data *wdata = data;
put_nfs_open_context(wdata->args.context); put_nfs_open_context(wdata->args.context);
nfs_writedata_free(wdata); nfs_writedata_free(wdata);
} }
......
...@@ -473,7 +473,6 @@ extern int nfs_writepages(struct address_space *, struct writeback_control *); ...@@ -473,7 +473,6 @@ extern int nfs_writepages(struct address_space *, struct writeback_control *);
extern int nfs_flush_incompatible(struct file *file, struct page *page); extern int nfs_flush_incompatible(struct file *file, struct page *page);
extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int); extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int);
extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *);
extern void nfs_writedata_release(void *);
/* /*
* Try to write back everything synchronously (but check the * Try to write back everything synchronously (but check the
...@@ -488,7 +487,6 @@ extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); ...@@ -488,7 +487,6 @@ extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
extern int nfs_commit_inode(struct inode *, int); extern int nfs_commit_inode(struct inode *, int);
extern struct nfs_write_data *nfs_commitdata_alloc(void); extern struct nfs_write_data *nfs_commitdata_alloc(void);
extern void nfs_commit_free(struct nfs_write_data *wdata); extern void nfs_commit_free(struct nfs_write_data *wdata);
extern void nfs_commitdata_release(void *wdata);
#else #else
static inline int static inline int
nfs_commit_inode(struct inode *inode, int how) nfs_commit_inode(struct inode *inode, int how)
...@@ -507,6 +505,7 @@ nfs_have_writebacks(struct inode *inode) ...@@ -507,6 +505,7 @@ nfs_have_writebacks(struct inode *inode)
* Allocate nfs_write_data structures * Allocate nfs_write_data structures
*/ */
extern struct nfs_write_data *nfs_writedata_alloc(unsigned int npages); extern struct nfs_write_data *nfs_writedata_alloc(unsigned int npages);
extern void nfs_writedata_free(struct nfs_write_data *);
/* /*
* linux/fs/nfs/read.c * linux/fs/nfs/read.c
...@@ -515,7 +514,6 @@ extern int nfs_readpage(struct file *, struct page *); ...@@ -515,7 +514,6 @@ extern int nfs_readpage(struct file *, struct page *);
extern int nfs_readpages(struct file *, struct address_space *, extern int nfs_readpages(struct file *, struct address_space *,
struct list_head *, unsigned); struct list_head *, unsigned);
extern int nfs_readpage_result(struct rpc_task *, struct nfs_read_data *); extern int nfs_readpage_result(struct rpc_task *, struct nfs_read_data *);
extern void nfs_readdata_release(void *data);
extern int nfs_readpage_async(struct nfs_open_context *, struct inode *, extern int nfs_readpage_async(struct nfs_open_context *, struct inode *,
struct page *); struct page *);
...@@ -523,6 +521,7 @@ extern int nfs_readpage_async(struct nfs_open_context *, struct inode *, ...@@ -523,6 +521,7 @@ extern int nfs_readpage_async(struct nfs_open_context *, struct inode *,
* Allocate nfs_read_data structures * Allocate nfs_read_data structures
*/ */
extern struct nfs_read_data *nfs_readdata_alloc(unsigned int npages); extern struct nfs_read_data *nfs_readdata_alloc(unsigned int npages);
extern void nfs_readdata_free(struct nfs_read_data *);
/* /*
* linux/fs/nfs3proc.c * linux/fs/nfs3proc.c
......
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