diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-11-13 16:23:44 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-12-06 10:46:26 -0500 |
commit | 8aca67f0ae2d8811165c22326825a645cc8e1b48 (patch) | |
tree | 19e82f4bc7b4f865a9dcf4744e7c224ea517ba10 /fs/nfs/write.c | |
parent | e6b3c4db6fbcd0d33720696f37790d6b8be12313 (diff) |
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>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 883dd4a1c157..29d88209199d 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -102,13 +102,19 @@ struct nfs_write_data *nfs_commit_alloc(void) | |||
102 | return p; | 102 | return p; |
103 | } | 103 | } |
104 | 104 | ||
105 | void nfs_commit_free(struct nfs_write_data *p) | 105 | void nfs_commit_rcu_free(struct rcu_head *head) |
106 | { | 106 | { |
107 | struct nfs_write_data *p = container_of(head, struct nfs_write_data, task.u.tk_rcu); | ||
107 | if (p && (p->pagevec != &p->page_array[0])) | 108 | if (p && (p->pagevec != &p->page_array[0])) |
108 | kfree(p->pagevec); | 109 | kfree(p->pagevec); |
109 | mempool_free(p, nfs_commit_mempool); | 110 | mempool_free(p, nfs_commit_mempool); |
110 | } | 111 | } |
111 | 112 | ||
113 | void nfs_commit_free(struct nfs_write_data *wdata) | ||
114 | { | ||
115 | call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free); | ||
116 | } | ||
117 | |||
112 | struct nfs_write_data *nfs_writedata_alloc(size_t len) | 118 | struct nfs_write_data *nfs_writedata_alloc(size_t len) |
113 | { | 119 | { |
114 | unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 120 | unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
@@ -131,13 +137,19 @@ struct nfs_write_data *nfs_writedata_alloc(size_t len) | |||
131 | return p; | 137 | return p; |
132 | } | 138 | } |
133 | 139 | ||
134 | static void nfs_writedata_free(struct nfs_write_data *p) | 140 | static void nfs_writedata_rcu_free(struct rcu_head *head) |
135 | { | 141 | { |
142 | struct nfs_write_data *p = container_of(head, struct nfs_write_data, task.u.tk_rcu); | ||
136 | if (p && (p->pagevec != &p->page_array[0])) | 143 | if (p && (p->pagevec != &p->page_array[0])) |
137 | kfree(p->pagevec); | 144 | kfree(p->pagevec); |
138 | mempool_free(p, nfs_wdata_mempool); | 145 | mempool_free(p, nfs_wdata_mempool); |
139 | } | 146 | } |
140 | 147 | ||
148 | static void nfs_writedata_free(struct nfs_write_data *wdata) | ||
149 | { | ||
150 | call_rcu_bh(&wdata->task.u.tk_rcu, nfs_writedata_rcu_free); | ||
151 | } | ||
152 | |||
141 | void nfs_writedata_release(void *wdata) | 153 | void nfs_writedata_release(void *wdata) |
142 | { | 154 | { |
143 | nfs_writedata_free(wdata); | 155 | nfs_writedata_free(wdata); |
@@ -258,7 +270,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode, | |||
258 | io_error: | 270 | io_error: |
259 | nfs_end_data_update(inode); | 271 | nfs_end_data_update(inode); |
260 | end_page_writeback(page); | 272 | end_page_writeback(page); |
261 | nfs_writedata_free(wdata); | 273 | nfs_writedata_release(wdata); |
262 | return written ? written : result; | 274 | return written ? written : result; |
263 | } | 275 | } |
264 | 276 | ||
@@ -1043,7 +1055,7 @@ out_bad: | |||
1043 | while (!list_empty(&list)) { | 1055 | while (!list_empty(&list)) { |
1044 | data = list_entry(list.next, struct nfs_write_data, pages); | 1056 | data = list_entry(list.next, struct nfs_write_data, pages); |
1045 | list_del(&data->pages); | 1057 | list_del(&data->pages); |
1046 | nfs_writedata_free(data); | 1058 | nfs_writedata_release(data); |
1047 | } | 1059 | } |
1048 | nfs_mark_request_dirty(req); | 1060 | nfs_mark_request_dirty(req); |
1049 | nfs_clear_page_writeback(req); | 1061 | nfs_clear_page_writeback(req); |