diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-03 19:07:30 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-09 15:16:51 -0400 |
commit | 866ad9a747bbf5461739fcae6d0a41c8971bbe1d (patch) | |
tree | 6a94d3434bbb9034f9aeed68a59e799abf7f8ff2 /fs/proc/inode.c | |
parent | ad147d011f4e9d4e4309f7974fd19c7f875ccb14 (diff) |
procfs: preparations for remove_proc_entry() race fixes
* leave ->proc_fops alone; make ->pde_users negative instead
* trim pde_opener
* move relevant code in fs/proc/inode.c
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/proc/inode.c')
-rw-r--r-- | fs/proc/inode.c | 248 |
1 files changed, 126 insertions, 122 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index a4aaaeee3342..0cd9d80f28e8 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -129,96 +129,138 @@ static const struct super_operations proc_sops = { | |||
129 | .show_options = proc_show_options, | 129 | .show_options = proc_show_options, |
130 | }; | 130 | }; |
131 | 131 | ||
132 | enum {BIAS = -1U<<31}; | ||
133 | |||
134 | static inline int use_pde(struct proc_dir_entry *pde) | ||
135 | { | ||
136 | int res = 1; | ||
137 | spin_lock(&pde->pde_unload_lock); | ||
138 | if (unlikely(pde->pde_users < 0)) | ||
139 | res = 0; | ||
140 | else | ||
141 | pde->pde_users++; | ||
142 | spin_unlock(&pde->pde_unload_lock); | ||
143 | return res; | ||
144 | } | ||
145 | |||
132 | static void __pde_users_dec(struct proc_dir_entry *pde) | 146 | static void __pde_users_dec(struct proc_dir_entry *pde) |
133 | { | 147 | { |
134 | pde->pde_users--; | 148 | if (--pde->pde_users == BIAS) |
135 | if (pde->pde_unload_completion && pde->pde_users == 0) | ||
136 | complete(pde->pde_unload_completion); | 149 | complete(pde->pde_unload_completion); |
137 | } | 150 | } |
138 | 151 | ||
139 | void pde_users_dec(struct proc_dir_entry *pde) | 152 | static void unuse_pde(struct proc_dir_entry *pde) |
140 | { | 153 | { |
141 | spin_lock(&pde->pde_unload_lock); | 154 | spin_lock(&pde->pde_unload_lock); |
142 | __pde_users_dec(pde); | 155 | __pde_users_dec(pde); |
143 | spin_unlock(&pde->pde_unload_lock); | 156 | spin_unlock(&pde->pde_unload_lock); |
144 | } | 157 | } |
145 | 158 | ||
146 | static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) | 159 | void proc_entry_rundown(struct proc_dir_entry *de) |
147 | { | 160 | { |
148 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 161 | spin_lock(&de->pde_unload_lock); |
149 | loff_t rv = -EINVAL; | 162 | de->pde_users += BIAS; |
150 | loff_t (*llseek)(struct file *, loff_t, int); | 163 | /* Wait until all existing callers into module are done. */ |
164 | if (de->pde_users != BIAS) { | ||
165 | DECLARE_COMPLETION_ONSTACK(c); | ||
166 | de->pde_unload_completion = &c; | ||
167 | spin_unlock(&de->pde_unload_lock); | ||
151 | 168 | ||
152 | spin_lock(&pde->pde_unload_lock); | 169 | wait_for_completion(de->pde_unload_completion); |
153 | /* | 170 | |
154 | * remove_proc_entry() is going to delete PDE (as part of module | 171 | spin_lock(&de->pde_unload_lock); |
155 | * cleanup sequence). No new callers into module allowed. | ||
156 | */ | ||
157 | if (!pde->proc_fops) { | ||
158 | spin_unlock(&pde->pde_unload_lock); | ||
159 | return rv; | ||
160 | } | 172 | } |
161 | /* | ||
162 | * Bump refcount so that remove_proc_entry will wail for ->llseek to | ||
163 | * complete. | ||
164 | */ | ||
165 | pde->pde_users++; | ||
166 | /* | ||
167 | * Save function pointer under lock, to protect against ->proc_fops | ||
168 | * NULL'ifying right after ->pde_unload_lock is dropped. | ||
169 | */ | ||
170 | llseek = pde->proc_fops->llseek; | ||
171 | spin_unlock(&pde->pde_unload_lock); | ||
172 | 173 | ||
173 | if (!llseek) | 174 | while (!list_empty(&de->pde_openers)) { |
174 | llseek = default_llseek; | 175 | struct pde_opener *pdeo; |
175 | rv = llseek(file, offset, whence); | 176 | struct file *file; |
176 | 177 | ||
177 | pde_users_dec(pde); | 178 | pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); |
178 | return rv; | 179 | list_del(&pdeo->lh); |
180 | spin_unlock(&de->pde_unload_lock); | ||
181 | file = pdeo->file; | ||
182 | de->proc_fops->release(file_inode(file), file); | ||
183 | kfree(pdeo); | ||
184 | spin_lock(&de->pde_unload_lock); | ||
185 | } | ||
186 | spin_unlock(&de->pde_unload_lock); | ||
179 | } | 187 | } |
180 | 188 | ||
181 | static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 189 | /* ->read_proc() users - legacy crap */ |
190 | static ssize_t | ||
191 | proc_file_read(struct file *file, char __user *buf, size_t nbytes, | ||
192 | loff_t *ppos) | ||
182 | { | 193 | { |
183 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 194 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
184 | ssize_t rv = -EIO; | 195 | ssize_t rv = -EIO; |
185 | ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); | 196 | if (use_pde(pde)) { |
197 | rv = __proc_file_read(file, buf, nbytes, ppos); | ||
198 | unuse_pde(pde); | ||
199 | } | ||
200 | return rv; | ||
201 | } | ||
186 | 202 | ||
187 | spin_lock(&pde->pde_unload_lock); | 203 | static loff_t |
188 | if (!pde->proc_fops) { | 204 | proc_file_lseek(struct file *file, loff_t offset, int orig) |
189 | spin_unlock(&pde->pde_unload_lock); | 205 | { |
190 | return rv; | 206 | loff_t retval = -EINVAL; |
207 | switch (orig) { | ||
208 | case 1: | ||
209 | offset += file->f_pos; | ||
210 | /* fallthrough */ | ||
211 | case 0: | ||
212 | if (offset < 0 || offset > MAX_NON_LFS) | ||
213 | break; | ||
214 | file->f_pos = retval = offset; | ||
191 | } | 215 | } |
192 | pde->pde_users++; | 216 | return retval; |
193 | read = pde->proc_fops->read; | 217 | } |
194 | spin_unlock(&pde->pde_unload_lock); | ||
195 | 218 | ||
196 | if (read) | 219 | const struct file_operations proc_file_operations = { |
197 | rv = read(file, buf, count, ppos); | 220 | .llseek = proc_file_lseek, |
221 | .read = proc_file_read, | ||
222 | }; | ||
198 | 223 | ||
199 | pde_users_dec(pde); | 224 | static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) |
225 | { | ||
226 | struct proc_dir_entry *pde = PDE(file_inode(file)); | ||
227 | loff_t rv = -EINVAL; | ||
228 | if (use_pde(pde)) { | ||
229 | loff_t (*llseek)(struct file *, loff_t, int); | ||
230 | llseek = pde->proc_fops->llseek; | ||
231 | if (!llseek) | ||
232 | llseek = default_llseek; | ||
233 | rv = llseek(file, offset, whence); | ||
234 | unuse_pde(pde); | ||
235 | } | ||
200 | return rv; | 236 | return rv; |
201 | } | 237 | } |
202 | 238 | ||
203 | static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | 239 | static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
204 | { | 240 | { |
241 | ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); | ||
205 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 242 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
206 | ssize_t rv = -EIO; | 243 | ssize_t rv = -EIO; |
207 | ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); | 244 | if (use_pde(pde)) { |
208 | 245 | read = pde->proc_fops->read; | |
209 | spin_lock(&pde->pde_unload_lock); | 246 | if (read) |
210 | if (!pde->proc_fops) { | 247 | rv = read(file, buf, count, ppos); |
211 | spin_unlock(&pde->pde_unload_lock); | 248 | unuse_pde(pde); |
212 | return rv; | ||
213 | } | 249 | } |
214 | pde->pde_users++; | 250 | return rv; |
215 | write = pde->proc_fops->write; | 251 | } |
216 | spin_unlock(&pde->pde_unload_lock); | ||
217 | |||
218 | if (write) | ||
219 | rv = write(file, buf, count, ppos); | ||
220 | 252 | ||
221 | pde_users_dec(pde); | 253 | static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) |
254 | { | ||
255 | ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); | ||
256 | struct proc_dir_entry *pde = PDE(file_inode(file)); | ||
257 | ssize_t rv = -EIO; | ||
258 | if (use_pde(pde)) { | ||
259 | write = pde->proc_fops->write; | ||
260 | if (write) | ||
261 | rv = write(file, buf, count, ppos); | ||
262 | unuse_pde(pde); | ||
263 | } | ||
222 | return rv; | 264 | return rv; |
223 | } | 265 | } |
224 | 266 | ||
@@ -227,20 +269,12 @@ static unsigned int proc_reg_poll(struct file *file, struct poll_table_struct *p | |||
227 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 269 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
228 | unsigned int rv = DEFAULT_POLLMASK; | 270 | unsigned int rv = DEFAULT_POLLMASK; |
229 | unsigned int (*poll)(struct file *, struct poll_table_struct *); | 271 | unsigned int (*poll)(struct file *, struct poll_table_struct *); |
230 | 272 | if (use_pde(pde)) { | |
231 | spin_lock(&pde->pde_unload_lock); | 273 | poll = pde->proc_fops->poll; |
232 | if (!pde->proc_fops) { | 274 | if (poll) |
233 | spin_unlock(&pde->pde_unload_lock); | 275 | rv = poll(file, pts); |
234 | return rv; | 276 | unuse_pde(pde); |
235 | } | 277 | } |
236 | pde->pde_users++; | ||
237 | poll = pde->proc_fops->poll; | ||
238 | spin_unlock(&pde->pde_unload_lock); | ||
239 | |||
240 | if (poll) | ||
241 | rv = poll(file, pts); | ||
242 | |||
243 | pde_users_dec(pde); | ||
244 | return rv; | 278 | return rv; |
245 | } | 279 | } |
246 | 280 | ||
@@ -249,20 +283,12 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne | |||
249 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 283 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
250 | long rv = -ENOTTY; | 284 | long rv = -ENOTTY; |
251 | long (*ioctl)(struct file *, unsigned int, unsigned long); | 285 | long (*ioctl)(struct file *, unsigned int, unsigned long); |
252 | 286 | if (use_pde(pde)) { | |
253 | spin_lock(&pde->pde_unload_lock); | 287 | ioctl = pde->proc_fops->unlocked_ioctl; |
254 | if (!pde->proc_fops) { | 288 | if (ioctl) |
255 | spin_unlock(&pde->pde_unload_lock); | 289 | rv = ioctl(file, cmd, arg); |
256 | return rv; | 290 | unuse_pde(pde); |
257 | } | 291 | } |
258 | pde->pde_users++; | ||
259 | ioctl = pde->proc_fops->unlocked_ioctl; | ||
260 | spin_unlock(&pde->pde_unload_lock); | ||
261 | |||
262 | if (ioctl) | ||
263 | rv = ioctl(file, cmd, arg); | ||
264 | |||
265 | pde_users_dec(pde); | ||
266 | return rv; | 292 | return rv; |
267 | } | 293 | } |
268 | 294 | ||
@@ -272,20 +298,12 @@ static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned | |||
272 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 298 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
273 | long rv = -ENOTTY; | 299 | long rv = -ENOTTY; |
274 | long (*compat_ioctl)(struct file *, unsigned int, unsigned long); | 300 | long (*compat_ioctl)(struct file *, unsigned int, unsigned long); |
275 | 301 | if (use_pde(pde)) { | |
276 | spin_lock(&pde->pde_unload_lock); | 302 | compat_ioctl = pde->proc_fops->compat_ioctl; |
277 | if (!pde->proc_fops) { | 303 | if (compat_ioctl) |
278 | spin_unlock(&pde->pde_unload_lock); | 304 | rv = compat_ioctl(file, cmd, arg); |
279 | return rv; | 305 | unuse_pde(pde); |
280 | } | 306 | } |
281 | pde->pde_users++; | ||
282 | compat_ioctl = pde->proc_fops->compat_ioctl; | ||
283 | spin_unlock(&pde->pde_unload_lock); | ||
284 | |||
285 | if (compat_ioctl) | ||
286 | rv = compat_ioctl(file, cmd, arg); | ||
287 | |||
288 | pde_users_dec(pde); | ||
289 | return rv; | 307 | return rv; |
290 | } | 308 | } |
291 | #endif | 309 | #endif |
@@ -295,20 +313,12 @@ static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) | |||
295 | struct proc_dir_entry *pde = PDE(file_inode(file)); | 313 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
296 | int rv = -EIO; | 314 | int rv = -EIO; |
297 | int (*mmap)(struct file *, struct vm_area_struct *); | 315 | int (*mmap)(struct file *, struct vm_area_struct *); |
298 | 316 | if (use_pde(pde)) { | |
299 | spin_lock(&pde->pde_unload_lock); | 317 | mmap = pde->proc_fops->mmap; |
300 | if (!pde->proc_fops) { | 318 | if (mmap) |
301 | spin_unlock(&pde->pde_unload_lock); | 319 | rv = mmap(file, vma); |
302 | return rv; | 320 | unuse_pde(pde); |
303 | } | 321 | } |
304 | pde->pde_users++; | ||
305 | mmap = pde->proc_fops->mmap; | ||
306 | spin_unlock(&pde->pde_unload_lock); | ||
307 | |||
308 | if (mmap) | ||
309 | rv = mmap(file, vma); | ||
310 | |||
311 | pde_users_dec(pde); | ||
312 | return rv; | 322 | return rv; |
313 | } | 323 | } |
314 | 324 | ||
@@ -334,16 +344,12 @@ static int proc_reg_open(struct inode *inode, struct file *file) | |||
334 | if (!pdeo) | 344 | if (!pdeo) |
335 | return -ENOMEM; | 345 | return -ENOMEM; |
336 | 346 | ||
337 | spin_lock(&pde->pde_unload_lock); | 347 | if (!use_pde(pde)) { |
338 | if (!pde->proc_fops) { | ||
339 | spin_unlock(&pde->pde_unload_lock); | ||
340 | kfree(pdeo); | 348 | kfree(pdeo); |
341 | return -ENOENT; | 349 | return -ENOENT; |
342 | } | 350 | } |
343 | pde->pde_users++; | ||
344 | open = pde->proc_fops->open; | 351 | open = pde->proc_fops->open; |
345 | release = pde->proc_fops->release; | 352 | release = pde->proc_fops->release; |
346 | spin_unlock(&pde->pde_unload_lock); | ||
347 | 353 | ||
348 | if (open) | 354 | if (open) |
349 | rv = open(inode, file); | 355 | rv = open(inode, file); |
@@ -351,10 +357,8 @@ static int proc_reg_open(struct inode *inode, struct file *file) | |||
351 | spin_lock(&pde->pde_unload_lock); | 357 | spin_lock(&pde->pde_unload_lock); |
352 | if (rv == 0 && release) { | 358 | if (rv == 0 && release) { |
353 | /* To know what to release. */ | 359 | /* To know what to release. */ |
354 | pdeo->inode = inode; | ||
355 | pdeo->file = file; | 360 | pdeo->file = file; |
356 | /* Strictly for "too late" ->release in proc_reg_release(). */ | 361 | /* Strictly for "too late" ->release in proc_reg_release(). */ |
357 | pdeo->release = release; | ||
358 | list_add(&pdeo->lh, &pde->pde_openers); | 362 | list_add(&pdeo->lh, &pde->pde_openers); |
359 | } else | 363 | } else |
360 | kfree(pdeo); | 364 | kfree(pdeo); |
@@ -364,12 +368,12 @@ static int proc_reg_open(struct inode *inode, struct file *file) | |||
364 | } | 368 | } |
365 | 369 | ||
366 | static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, | 370 | static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, |
367 | struct inode *inode, struct file *file) | 371 | struct file *file) |
368 | { | 372 | { |
369 | struct pde_opener *pdeo; | 373 | struct pde_opener *pdeo; |
370 | 374 | ||
371 | list_for_each_entry(pdeo, &pde->pde_openers, lh) { | 375 | list_for_each_entry(pdeo, &pde->pde_openers, lh) { |
372 | if (pdeo->inode == inode && pdeo->file == file) | 376 | if (pdeo->file == file) |
373 | return pdeo; | 377 | return pdeo; |
374 | } | 378 | } |
375 | return NULL; | 379 | return NULL; |
@@ -383,8 +387,8 @@ static int proc_reg_release(struct inode *inode, struct file *file) | |||
383 | struct pde_opener *pdeo; | 387 | struct pde_opener *pdeo; |
384 | 388 | ||
385 | spin_lock(&pde->pde_unload_lock); | 389 | spin_lock(&pde->pde_unload_lock); |
386 | pdeo = find_pde_opener(pde, inode, file); | 390 | pdeo = find_pde_opener(pde, file); |
387 | if (!pde->proc_fops) { | 391 | if (pde->pde_users < 0) { |
388 | /* | 392 | /* |
389 | * Can't simply exit, __fput() will think that everything is OK, | 393 | * Can't simply exit, __fput() will think that everything is OK, |
390 | * and move on to freeing struct file. remove_proc_entry() will | 394 | * and move on to freeing struct file. remove_proc_entry() will |
@@ -396,7 +400,7 @@ static int proc_reg_release(struct inode *inode, struct file *file) | |||
396 | if (pdeo) { | 400 | if (pdeo) { |
397 | list_del(&pdeo->lh); | 401 | list_del(&pdeo->lh); |
398 | spin_unlock(&pde->pde_unload_lock); | 402 | spin_unlock(&pde->pde_unload_lock); |
399 | rv = pdeo->release(inode, file); | 403 | rv = pde->proc_fops->release(inode, file); |
400 | kfree(pdeo); | 404 | kfree(pdeo); |
401 | } else | 405 | } else |
402 | spin_unlock(&pde->pde_unload_lock); | 406 | spin_unlock(&pde->pde_unload_lock); |
@@ -413,7 +417,7 @@ static int proc_reg_release(struct inode *inode, struct file *file) | |||
413 | if (release) | 417 | if (release) |
414 | rv = release(inode, file); | 418 | rv = release(inode, file); |
415 | 419 | ||
416 | pde_users_dec(pde); | 420 | unuse_pde(pde); |
417 | return rv; | 421 | return rv; |
418 | } | 422 | } |
419 | 423 | ||