aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-03 19:07:30 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-09 15:16:51 -0400
commit866ad9a747bbf5461739fcae6d0a41c8971bbe1d (patch)
tree6a94d3434bbb9034f9aeed68a59e799abf7f8ff2
parentad147d011f4e9d4e4309f7974fd19c7f875ccb14 (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.c83
-rw-r--r--fs/proc/inode.c248
-rw-r--r--fs/proc/internal.h7
-rw-r--r--include/linux/proc_fs.h11
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
42static ssize_t 42ssize_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
174static ssize_t
175proc_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
195static loff_t
196proc_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
211static const struct file_operations proc_file_operations = {
212 .llseek = proc_file_lseek,
213 .read = proc_file_read,
214};
215
216static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) 174static 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
725static 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
132enum {BIAS = -1U<<31};
133
134static 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
132static void __pde_users_dec(struct proc_dir_entry *pde) 146static 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
139void pde_users_dec(struct proc_dir_entry *pde) 152static 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
146static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) 159void 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
181static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 189/* ->read_proc() users - legacy crap */
190static ssize_t
191proc_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); 203static loff_t
188 if (!pde->proc_fops) { 204proc_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) 219const 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); 224static 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
203static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 239static 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); 253static 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
366static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, 370static 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
153struct pde_opener { 153struct 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};
159void pde_users_dec(struct proc_dir_entry *pde); 157
158ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *);
159extern const struct file_operations proc_file_operations;
160void proc_entry_rundown(struct proc_dir_entry *);
160 161
161extern spinlock_t proc_subdir_lock; 162extern 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 */