diff options
author | Eric Van Hensbergen <ericvh@gmail.com> | 2007-01-26 03:57:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-01-26 16:51:00 -0500 |
commit | da977b2c7eb4d6312f063a7b486f2aad99809710 (patch) | |
tree | bb8a2afc766c16e3349e03dfb8a706dca6408395 /fs/9p/fid.c | |
parent | ff76e1dfc8728278ee231feeb93146f9c57c3ec3 (diff) |
[PATCH] 9p: fix segfault caused by race condition in meta-data operations
Running dbench multithreaded exposed a race condition where fid structures
were removed while in use. This patch adds semaphores to meta-data operations
to protect the fid structure. Some cleanup of error-case handling in the
inode operations is also included.
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/9p/fid.c')
-rw-r--r-- | fs/9p/fid.c | 69 |
1 files changed, 66 insertions, 3 deletions
diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 27507201f9e7..a9b6301a04fc 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/fs.h> | 25 | #include <linux/fs.h> |
26 | #include <linux/sched.h> | 26 | #include <linux/sched.h> |
27 | #include <linux/idr.h> | 27 | #include <linux/idr.h> |
28 | #include <asm/semaphore.h> | ||
28 | 29 | ||
29 | #include "debug.h" | 30 | #include "debug.h" |
30 | #include "v9fs.h" | 31 | #include "v9fs.h" |
@@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) | |||
84 | new->iounit = 0; | 85 | new->iounit = 0; |
85 | new->rdir_pos = 0; | 86 | new->rdir_pos = 0; |
86 | new->rdir_fcall = NULL; | 87 | new->rdir_fcall = NULL; |
88 | init_MUTEX(&new->lock); | ||
87 | INIT_LIST_HEAD(&new->list); | 89 | INIT_LIST_HEAD(&new->list); |
88 | 90 | ||
89 | return new; | 91 | return new; |
@@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid) | |||
102 | } | 104 | } |
103 | 105 | ||
104 | /** | 106 | /** |
105 | * v9fs_fid_lookup - retrieve the right fid from a particular dentry | 107 | * v9fs_fid_lookup - return a locked fid from a dentry |
106 | * @dentry: dentry to look for fid in | 108 | * @dentry: dentry to look for fid in |
107 | * @type: intent of lookup (operation or traversal) | ||
108 | * | 109 | * |
109 | * find a fid in the dentry | 110 | * find a fid in the dentry, obtain its semaphore and return a reference to it. |
111 | * code calling lookup is responsible for releasing lock | ||
110 | * | 112 | * |
111 | * TODO: only match fids that have the same uid as current user | 113 | * TODO: only match fids that have the same uid as current user |
112 | * | 114 | * |
@@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) | |||
124 | 126 | ||
125 | if (!return_fid) { | 127 | if (!return_fid) { |
126 | dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); | 128 | dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); |
129 | return_fid = ERR_PTR(-EBADF); | ||
127 | } | 130 | } |
128 | 131 | ||
132 | if(down_interruptible(&return_fid->lock)) | ||
133 | return ERR_PTR(-EINTR); | ||
134 | |||
129 | return return_fid; | 135 | return return_fid; |
130 | } | 136 | } |
137 | |||
138 | /** | ||
139 | * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it | ||
140 | * @dentry: dentry to look for fid in | ||
141 | * | ||
142 | * find a fid in the dentry and then clone to a new private fid | ||
143 | * | ||
144 | * TODO: only match fids that have the same uid as current user | ||
145 | * | ||
146 | */ | ||
147 | |||
148 | struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) | ||
149 | { | ||
150 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | ||
151 | struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); | ||
152 | struct v9fs_fcall *fcall = NULL; | ||
153 | int fid, err; | ||
154 | |||
155 | base_fid = v9fs_fid_lookup(dentry); | ||
156 | |||
157 | if(IS_ERR(base_fid)) | ||
158 | return base_fid; | ||
159 | |||
160 | if(base_fid) { /* clone fid */ | ||
161 | fid = v9fs_get_idpool(&v9ses->fidpool); | ||
162 | if (fid < 0) { | ||
163 | eprintk(KERN_WARNING, "newfid fails!\n"); | ||
164 | new_fid = ERR_PTR(-ENOSPC); | ||
165 | goto Release_Fid; | ||
166 | } | ||
167 | |||
168 | err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); | ||
169 | if (err < 0) { | ||
170 | dprintk(DEBUG_ERROR, "clone walk didn't work\n"); | ||
171 | v9fs_put_idpool(fid, &v9ses->fidpool); | ||
172 | new_fid = ERR_PTR(err); | ||
173 | goto Free_Fcall; | ||
174 | } | ||
175 | new_fid = v9fs_fid_create(v9ses, fid); | ||
176 | if (new_fid == NULL) { | ||
177 | dprintk(DEBUG_ERROR, "out of memory\n"); | ||
178 | new_fid = ERR_PTR(-ENOMEM); | ||
179 | } | ||
180 | Free_Fcall: | ||
181 | kfree(fcall); | ||
182 | } | ||
183 | |||
184 | Release_Fid: | ||
185 | up(&base_fid->lock); | ||
186 | return new_fid; | ||
187 | } | ||
188 | |||
189 | void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) | ||
190 | { | ||
191 | v9fs_t_clunk(v9ses, fid->fid); | ||
192 | v9fs_fid_destroy(fid); | ||
193 | } | ||