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 | |
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>
-rw-r--r-- | fs/proc/generic.c | 83 | ||||
-rw-r--r-- | fs/proc/inode.c | 248 | ||||
-rw-r--r-- | fs/proc/internal.h | 7 | ||||
-rw-r--r-- | include/linux/proc_fs.h | 11 |
4 files changed, 135 insertions, 214 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index a6a1cb5d589d..bec58323629c 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -39,7 +39,7 @@ static int proc_match(unsigned int len, const char *name, struct proc_dir_entry | |||
39 | /* buffer size is one page but our output routines use some slack for overruns */ | 39 | /* buffer size is one page but our output routines use some slack for overruns */ |
40 | #define PROC_BLOCK_SIZE (PAGE_SIZE - 1024) | 40 | #define PROC_BLOCK_SIZE (PAGE_SIZE - 1024) |
41 | 41 | ||
42 | static ssize_t | 42 | ssize_t |
43 | __proc_file_read(struct file *file, char __user *buf, size_t nbytes, | 43 | __proc_file_read(struct file *file, char __user *buf, size_t nbytes, |
44 | loff_t *ppos) | 44 | loff_t *ppos) |
45 | { | 45 | { |
@@ -171,48 +171,6 @@ __proc_file_read(struct file *file, char __user *buf, size_t nbytes, | |||
171 | return retval; | 171 | return retval; |
172 | } | 172 | } |
173 | 173 | ||
174 | static ssize_t | ||
175 | proc_file_read(struct file *file, char __user *buf, size_t nbytes, | ||
176 | loff_t *ppos) | ||
177 | { | ||
178 | struct proc_dir_entry *pde = PDE(file_inode(file)); | ||
179 | ssize_t rv = -EIO; | ||
180 | |||
181 | spin_lock(&pde->pde_unload_lock); | ||
182 | if (!pde->proc_fops) { | ||
183 | spin_unlock(&pde->pde_unload_lock); | ||
184 | return rv; | ||
185 | } | ||
186 | pde->pde_users++; | ||
187 | spin_unlock(&pde->pde_unload_lock); | ||
188 | |||
189 | rv = __proc_file_read(file, buf, nbytes, ppos); | ||
190 | |||
191 | pde_users_dec(pde); | ||
192 | return rv; | ||
193 | } | ||
194 | |||
195 | static loff_t | ||
196 | proc_file_lseek(struct file *file, loff_t offset, int orig) | ||
197 | { | ||
198 | loff_t retval = -EINVAL; | ||
199 | switch (orig) { | ||
200 | case 1: | ||
201 | offset += file->f_pos; | ||
202 | /* fallthrough */ | ||
203 | case 0: | ||
204 | if (offset < 0 || offset > MAX_NON_LFS) | ||
205 | break; | ||
206 | file->f_pos = retval = offset; | ||
207 | } | ||
208 | return retval; | ||
209 | } | ||
210 | |||
211 | static const struct file_operations proc_file_operations = { | ||
212 | .llseek = proc_file_lseek, | ||
213 | .read = proc_file_read, | ||
214 | }; | ||
215 | |||
216 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) | 174 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) |
217 | { | 175 | { |
218 | struct inode *inode = dentry->d_inode; | 176 | struct inode *inode = dentry->d_inode; |
@@ -722,41 +680,6 @@ void pde_put(struct proc_dir_entry *pde) | |||
722 | free_proc_entry(pde); | 680 | free_proc_entry(pde); |
723 | } | 681 | } |
724 | 682 | ||
725 | static void entry_rundown(struct proc_dir_entry *de) | ||
726 | { | ||
727 | spin_lock(&de->pde_unload_lock); | ||
728 | /* | ||
729 | * Stop accepting new callers into module. If you're | ||
730 | * dynamically allocating ->proc_fops, save a pointer somewhere. | ||
731 | */ | ||
732 | de->proc_fops = NULL; | ||
733 | /* Wait until all existing callers into module are done. */ | ||
734 | if (de->pde_users > 0) { | ||
735 | DECLARE_COMPLETION_ONSTACK(c); | ||
736 | |||
737 | if (!de->pde_unload_completion) | ||
738 | de->pde_unload_completion = &c; | ||
739 | |||
740 | spin_unlock(&de->pde_unload_lock); | ||
741 | |||
742 | wait_for_completion(de->pde_unload_completion); | ||
743 | |||
744 | spin_lock(&de->pde_unload_lock); | ||
745 | } | ||
746 | |||
747 | while (!list_empty(&de->pde_openers)) { | ||
748 | struct pde_opener *pdeo; | ||
749 | |||
750 | pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); | ||
751 | list_del(&pdeo->lh); | ||
752 | spin_unlock(&de->pde_unload_lock); | ||
753 | pdeo->release(pdeo->inode, pdeo->file); | ||
754 | kfree(pdeo); | ||
755 | spin_lock(&de->pde_unload_lock); | ||
756 | } | ||
757 | spin_unlock(&de->pde_unload_lock); | ||
758 | } | ||
759 | |||
760 | /* | 683 | /* |
761 | * Remove a /proc entry and free it if it's not currently in use. | 684 | * Remove a /proc entry and free it if it's not currently in use. |
762 | */ | 685 | */ |
@@ -788,7 +711,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
788 | return; | 711 | return; |
789 | } | 712 | } |
790 | 713 | ||
791 | entry_rundown(de); | 714 | proc_entry_rundown(de); |
792 | 715 | ||
793 | if (S_ISDIR(de->mode)) | 716 | if (S_ISDIR(de->mode)) |
794 | parent->nlink--; | 717 | parent->nlink--; |
@@ -837,7 +760,7 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | |||
837 | } | 760 | } |
838 | spin_unlock(&proc_subdir_lock); | 761 | spin_unlock(&proc_subdir_lock); |
839 | 762 | ||
840 | entry_rundown(de); | 763 | proc_entry_rundown(de); |
841 | next = de->parent; | 764 | next = de->parent; |
842 | if (S_ISDIR(de->mode)) | 765 | if (S_ISDIR(de->mode)) |
843 | next->nlink--; | 766 | next->nlink--; |
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 | ||
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 9c93a53f371d..c43d536f93b9 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -151,12 +151,13 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent, | |||
151 | filldir_t filldir); | 151 | filldir_t filldir); |
152 | 152 | ||
153 | struct pde_opener { | 153 | struct pde_opener { |
154 | struct inode *inode; | ||
155 | struct file *file; | 154 | struct file *file; |
156 | int (*release)(struct inode *, struct file *); | ||
157 | struct list_head lh; | 155 | struct list_head lh; |
158 | }; | 156 | }; |
159 | void pde_users_dec(struct proc_dir_entry *pde); | 157 | |
158 | ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *); | ||
159 | extern const struct file_operations proc_file_operations; | ||
160 | void proc_entry_rundown(struct proc_dir_entry *); | ||
160 | 161 | ||
161 | extern spinlock_t proc_subdir_lock; | 162 | extern spinlock_t proc_subdir_lock; |
162 | 163 | ||
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index bcc0e10ef1df..947ae7eb63ef 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h | |||
@@ -60,20 +60,13 @@ struct proc_dir_entry { | |||
60 | kgid_t gid; | 60 | kgid_t gid; |
61 | loff_t size; | 61 | loff_t size; |
62 | const struct inode_operations *proc_iops; | 62 | const struct inode_operations *proc_iops; |
63 | /* | ||
64 | * NULL ->proc_fops means "PDE is going away RSN" or | ||
65 | * "PDE is just created". In either case, e.g. ->read_proc won't be | ||
66 | * called because it's too late or too early, respectively. | ||
67 | * | ||
68 | * If you're allocating ->proc_fops dynamically, save a pointer | ||
69 | * somewhere. | ||
70 | */ | ||
71 | const struct file_operations *proc_fops; | 63 | const struct file_operations *proc_fops; |
72 | struct proc_dir_entry *next, *parent, *subdir; | 64 | struct proc_dir_entry *next, *parent, *subdir; |
73 | void *data; | 65 | void *data; |
74 | read_proc_t *read_proc; | 66 | read_proc_t *read_proc; |
75 | atomic_t count; /* use count */ | 67 | atomic_t count; /* use count */ |
76 | int pde_users; /* number of callers into module in progress */ | 68 | int pde_users; /* number of callers into module in progress; */ |
69 | /* negative -> it's going away RSN */ | ||
77 | struct completion *pde_unload_completion; | 70 | struct completion *pde_unload_completion; |
78 | struct list_head pde_openers; /* who did ->open, but not ->release */ | 71 | struct list_head pde_openers; /* who did ->open, but not ->release */ |
79 | spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ | 72 | spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ |