diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-05-30 12:58:00 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-05-30 16:26:01 -0400 |
commit | b4946ffb1860597b187d78d61ac6504177eb0ff8 (patch) | |
tree | 56e5df1992aa478b806e5a768eda668543a5bb89 | |
parent | 7a74fc4925067c2102175baef73f9b07ab519b71 (diff) |
NFS: Fix a refcount leakage in O_DIRECT
The current code is leaking a reference to dreq->kref when the calls to
nfs_direct_read_schedule() and nfs_direct_write_schedule() return an
error.
This patch moves the call to kref_put() from nfs_direct_wait() back into
nfs_direct_read() and nfs_direct_write() (which are the functions that
actually took the reference in the first place) fixing the leak.
Thanks to Denis V. Lunev for spotting the bug and proposing the original
fix.
Acked-by: Denis V. Lunev <dlunev@gmail.com>
Acked-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/direct.c | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 0c542ec92d5b..00eee87510fe 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
@@ -168,7 +168,7 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void) | |||
168 | return dreq; | 168 | return dreq; |
169 | } | 169 | } |
170 | 170 | ||
171 | static void nfs_direct_req_release(struct kref *kref) | 171 | static void nfs_direct_req_free(struct kref *kref) |
172 | { | 172 | { |
173 | struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref); | 173 | struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref); |
174 | 174 | ||
@@ -177,6 +177,11 @@ static void nfs_direct_req_release(struct kref *kref) | |||
177 | kmem_cache_free(nfs_direct_cachep, dreq); | 177 | kmem_cache_free(nfs_direct_cachep, dreq); |
178 | } | 178 | } |
179 | 179 | ||
180 | static void nfs_direct_req_release(struct nfs_direct_req *dreq) | ||
181 | { | ||
182 | kref_put(&dreq->kref, nfs_direct_req_free); | ||
183 | } | ||
184 | |||
180 | /* | 185 | /* |
181 | * Collects and returns the final error value/byte-count. | 186 | * Collects and returns the final error value/byte-count. |
182 | */ | 187 | */ |
@@ -196,7 +201,6 @@ static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq) | |||
196 | result = dreq->count; | 201 | result = dreq->count; |
197 | 202 | ||
198 | out: | 203 | out: |
199 | kref_put(&dreq->kref, nfs_direct_req_release); | ||
200 | return (ssize_t) result; | 204 | return (ssize_t) result; |
201 | } | 205 | } |
202 | 206 | ||
@@ -214,7 +218,7 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq) | |||
214 | } | 218 | } |
215 | complete_all(&dreq->completion); | 219 | complete_all(&dreq->completion); |
216 | 220 | ||
217 | kref_put(&dreq->kref, nfs_direct_req_release); | 221 | nfs_direct_req_release(dreq); |
218 | } | 222 | } |
219 | 223 | ||
220 | /* | 224 | /* |
@@ -369,6 +373,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size | |||
369 | if (!result) | 373 | if (!result) |
370 | result = nfs_direct_wait(dreq); | 374 | result = nfs_direct_wait(dreq); |
371 | rpc_clnt_sigunmask(clnt, &oldset); | 375 | rpc_clnt_sigunmask(clnt, &oldset); |
376 | nfs_direct_req_release(dreq); | ||
372 | 377 | ||
373 | return result; | 378 | return result; |
374 | } | 379 | } |
@@ -716,6 +721,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz | |||
716 | if (!result) | 721 | if (!result) |
717 | result = nfs_direct_wait(dreq); | 722 | result = nfs_direct_wait(dreq); |
718 | rpc_clnt_sigunmask(clnt, &oldset); | 723 | rpc_clnt_sigunmask(clnt, &oldset); |
724 | nfs_direct_req_release(dreq); | ||
719 | 725 | ||
720 | return result; | 726 | return result; |
721 | } | 727 | } |