SUNRPC: Fix a potential race in rpc_wake_up_task()

Use RCU to ensure that we can safely call rpc_finish_wakeup after we've
called __rpc_do_wake_up_task. If not, there is a theoretical race, in which
the rpc_task finishes executing, and gets freed first.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Trond Myklebust
2006-11-13 16:23:44 -05:00
parent e6b3c4db6f
commit 8aca67f0ae
5 changed files with 47 additions and 20 deletions

View File

@@ -102,13 +102,19 @@ struct nfs_write_data *nfs_commit_alloc(void)
return p;
}
void nfs_commit_free(struct nfs_write_data *p)
void nfs_commit_rcu_free(struct rcu_head *head)
{
struct nfs_write_data *p = container_of(head, struct nfs_write_data, task.u.tk_rcu);
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_commit_mempool);
}
void nfs_commit_free(struct nfs_write_data *wdata)
{
call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free);
}
struct nfs_write_data *nfs_writedata_alloc(size_t len)
{
unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -131,13 +137,19 @@ struct nfs_write_data *nfs_writedata_alloc(size_t len)
return p;
}
static void nfs_writedata_free(struct nfs_write_data *p)
static void nfs_writedata_rcu_free(struct rcu_head *head)
{
struct nfs_write_data *p = container_of(head, struct nfs_write_data, task.u.tk_rcu);
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_wdata_mempool);
}
static void nfs_writedata_free(struct nfs_write_data *wdata)
{
call_rcu_bh(&wdata->task.u.tk_rcu, nfs_writedata_rcu_free);
}
void nfs_writedata_release(void *wdata)
{
nfs_writedata_free(wdata);
@@ -258,7 +270,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
io_error:
nfs_end_data_update(inode);
end_page_writeback(page);
nfs_writedata_free(wdata);
nfs_writedata_release(wdata);
return written ? written : result;
}
@@ -1043,7 +1055,7 @@ out_bad:
while (!list_empty(&list)) {
data = list_entry(list.next, struct nfs_write_data, pages);
list_del(&data->pages);
nfs_writedata_free(data);
nfs_writedata_release(data);
}
nfs_mark_request_dirty(req);
nfs_clear_page_writeback(req);