aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc/inode.c
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 /fs/proc/inode.c
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>
Diffstat (limited to 'fs/proc/inode.c')
-rw-r--r--fs/proc/inode.c248
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
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