aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify/inotify
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 20:12:04 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:58:16 -0400
commitb7ba83715317007962ee318587de92f14e9c3aaa (patch)
tree8964c554566ddcd0b73c266b95461dab1547aaf0 /fs/notify/inotify
parentfc0f5ac8fe693d1b05f5a928cc48135d1c8b7f2e (diff)
inotify: simplify the inotify idr handling
This patch moves all of the idr editing operations into their own idr functions. It makes it easier to prove locking correctness and to to understand the code flow. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify/inotify')
-rw-r--r--fs/notify/inotify/inotify_user.c194
1 files changed, 139 insertions, 55 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index e46ca685b9be..653c507b1bb3 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -357,6 +357,77 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
357 return error; 357 return error;
358} 358}
359 359
360static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock,
361 int last_wd,
362 struct inotify_inode_mark_entry *ientry)
363{
364 int ret;
365
366 do {
367 if (unlikely(!idr_pre_get(idr, GFP_KERNEL)))
368 return -ENOMEM;
369
370 spin_lock(idr_lock);
371 ret = idr_get_new_above(idr, ientry, last_wd + 1,
372 &ientry->wd);
373 /* we added the mark to the idr, take a reference */
374 if (!ret)
375 fsnotify_get_mark(&ientry->fsn_entry);
376 spin_unlock(idr_lock);
377 } while (ret == -EAGAIN);
378
379 return ret;
380}
381
382static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_group *group,
383 int wd)
384{
385 struct idr *idr = &group->inotify_data.idr;
386 spinlock_t *idr_lock = &group->inotify_data.idr_lock;
387 struct inotify_inode_mark_entry *ientry;
388
389 assert_spin_locked(idr_lock);
390
391 ientry = idr_find(idr, wd);
392 if (ientry) {
393 struct fsnotify_mark_entry *fsn_entry = &ientry->fsn_entry;
394
395 fsnotify_get_mark(fsn_entry);
396 /* One ref for being in the idr, one ref we just took */
397 BUG_ON(atomic_read(&fsn_entry->refcnt) < 2);
398 }
399
400 return ientry;
401}
402
403static struct inotify_inode_mark_entry *inotify_idr_find(struct fsnotify_group *group,
404 int wd)
405{
406 struct inotify_inode_mark_entry *ientry;
407 spinlock_t *idr_lock = &group->inotify_data.idr_lock;
408
409 spin_lock(idr_lock);
410 ientry = inotify_idr_find_locked(group, wd);
411 spin_unlock(idr_lock);
412
413 return ientry;
414}
415
416static void do_inotify_remove_from_idr(struct fsnotify_group *group,
417 struct inotify_inode_mark_entry *ientry)
418{
419 struct idr *idr = &group->inotify_data.idr;
420 spinlock_t *idr_lock = &group->inotify_data.idr_lock;
421 int wd = ientry->wd;
422
423 assert_spin_locked(idr_lock);
424
425 idr_remove(idr, wd);
426
427 /* removed from the idr, drop that ref */
428 fsnotify_put_mark(&ientry->fsn_entry);
429}
430
360/* 431/*
361 * Remove the mark from the idr (if present) and drop the reference 432 * Remove the mark from the idr (if present) and drop the reference
362 * on the mark because it was in the idr. 433 * on the mark because it was in the idr.
@@ -364,42 +435,72 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
364static void inotify_remove_from_idr(struct fsnotify_group *group, 435static void inotify_remove_from_idr(struct fsnotify_group *group,
365 struct inotify_inode_mark_entry *ientry) 436 struct inotify_inode_mark_entry *ientry)
366{ 437{
367 struct idr *idr; 438 spinlock_t *idr_lock = &group->inotify_data.idr_lock;
368 struct fsnotify_mark_entry *entry; 439 struct inotify_inode_mark_entry *found_ientry = NULL;
369 struct inotify_inode_mark_entry *found_ientry;
370 int wd; 440 int wd;
371 441
372 spin_lock(&group->inotify_data.idr_lock); 442 spin_lock(idr_lock);
373 idr = &group->inotify_data.idr;
374 wd = ientry->wd; 443 wd = ientry->wd;
375 444
376 if (wd == -1) 445 /*
446 * does this ientry think it is in the idr? we shouldn't get called
447 * if it wasn't....
448 */
449 if (wd == -1) {
450 printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p"
451 " ientry->inode=%p\n", __func__, ientry, ientry->wd,
452 ientry->fsn_entry.group, ientry->fsn_entry.inode);
453 WARN_ON(1);
377 goto out; 454 goto out;
455 }
378 456
379 entry = idr_find(&group->inotify_data.idr, wd); 457 /* Lets look in the idr to see if we find it */
380 if (unlikely(!entry)) 458 found_ientry = inotify_idr_find_locked(group, wd);
459 if (unlikely(!found_ientry)) {
460 printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p"
461 " ientry->inode=%p\n", __func__, ientry, ientry->wd,
462 ientry->fsn_entry.group, ientry->fsn_entry.inode);
463 WARN_ON(1);
381 goto out; 464 goto out;
465 }
382 466
383 found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); 467 /*
468 * We found an entry in the idr at the right wd, but it's
469 * not the entry we were told to remove. eparis seriously
470 * fucked up somewhere.
471 */
384 if (unlikely(found_ientry != ientry)) { 472 if (unlikely(found_ientry != ientry)) {
385 /* We found an entry in the idr with the right wd, but it's
386 * not the entry we were told to remove. eparis seriously
387 * fucked up somewhere. */
388 WARN_ON(1); 473 WARN_ON(1);
389 ientry->wd = -1; 474 printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p "
475 "entry->inode=%p found_ientry=%p found_ientry->wd=%d "
476 "found_ientry->group=%p found_ientry->inode=%p\n",
477 __func__, ientry, ientry->wd, ientry->fsn_entry.group,
478 ientry->fsn_entry.inode, found_ientry, found_ientry->wd,
479 found_ientry->fsn_entry.group,
480 found_ientry->fsn_entry.inode);
390 goto out; 481 goto out;
391 } 482 }
392 483
393 /* One ref for being in the idr, one ref held by the caller */ 484 /*
394 BUG_ON(atomic_read(&entry->refcnt) < 2); 485 * One ref for being in the idr
395 486 * one ref held by the caller trying to kill us
396 idr_remove(idr, wd); 487 * one ref grabbed by inotify_idr_find
397 ientry->wd = -1; 488 */
489 if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) {
490 printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p"
491 " ientry->inode=%p\n", __func__, ientry, ientry->wd,
492 ientry->fsn_entry.group, ientry->fsn_entry.inode);
493 /* we can't really recover with bad ref cnting.. */
494 BUG();
495 }
398 496
399 /* removed from the idr, drop that ref */ 497 do_inotify_remove_from_idr(group, ientry);
400 fsnotify_put_mark(entry);
401out: 498out:
402 spin_unlock(&group->inotify_data.idr_lock); 499 /* match the ref taken by inotify_idr_find_locked() */
500 if (found_ientry)
501 fsnotify_put_mark(&found_ientry->fsn_entry);
502 ientry->wd = -1;
503 spin_unlock(idr_lock);
403} 504}
404 505
405/* 506/*
@@ -524,6 +625,8 @@ static int inotify_new_watch(struct fsnotify_group *group,
524 struct inotify_inode_mark_entry *tmp_ientry; 625 struct inotify_inode_mark_entry *tmp_ientry;
525 __u32 mask; 626 __u32 mask;
526 int ret; 627 int ret;
628 struct idr *idr = &group->inotify_data.idr;
629 spinlock_t *idr_lock = &group->inotify_data.idr_lock;
527 630
528 /* don't allow invalid bits: we don't want flags set */ 631 /* don't allow invalid bits: we don't want flags set */
529 mask = inotify_arg_to_mask(arg); 632 mask = inotify_arg_to_mask(arg);
@@ -541,28 +644,11 @@ static int inotify_new_watch(struct fsnotify_group *group,
541 ret = -ENOSPC; 644 ret = -ENOSPC;
542 if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) 645 if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches)
543 goto out_err; 646 goto out_err;
544retry:
545 ret = -ENOMEM;
546 if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL)))
547 goto out_err;
548
549 /* we are putting the mark on the idr, take a reference */
550 fsnotify_get_mark(&tmp_ientry->fsn_entry);
551
552 spin_lock(&group->inotify_data.idr_lock);
553 ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry,
554 group->inotify_data.last_wd+1,
555 &tmp_ientry->wd);
556 spin_unlock(&group->inotify_data.idr_lock);
557 if (ret) {
558 /* we didn't get on the idr, drop the idr reference */
559 fsnotify_put_mark(&tmp_ientry->fsn_entry);
560 647
561 /* idr was out of memory allocate and try again */ 648 ret = inotify_add_to_idr(idr, idr_lock, group->inotify_data.last_wd,
562 if (ret == -EAGAIN) 649 tmp_ientry);
563 goto retry; 650 if (ret)
564 goto out_err; 651 goto out_err;
565 }
566 652
567 /* we are on the idr, now get on the inode */ 653 /* we are on the idr, now get on the inode */
568 ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); 654 ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
@@ -726,7 +812,7 @@ fput_and_out:
726SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) 812SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd)
727{ 813{
728 struct fsnotify_group *group; 814 struct fsnotify_group *group;
729 struct fsnotify_mark_entry *entry; 815 struct inotify_inode_mark_entry *ientry;
730 struct file *filp; 816 struct file *filp;
731 int ret = 0, fput_needed; 817 int ret = 0, fput_needed;
732 818
@@ -735,25 +821,23 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd)
735 return -EBADF; 821 return -EBADF;
736 822
737 /* verify that this is indeed an inotify instance */ 823 /* verify that this is indeed an inotify instance */
738 if (unlikely(filp->f_op != &inotify_fops)) { 824 ret = -EINVAL;
739 ret = -EINVAL; 825 if (unlikely(filp->f_op != &inotify_fops))
740 goto out; 826 goto out;
741 }
742 827
743 group = filp->private_data; 828 group = filp->private_data;
744 829
745 spin_lock(&group->inotify_data.idr_lock); 830 ret = -EINVAL;
746 entry = idr_find(&group->inotify_data.idr, wd); 831 ientry = inotify_idr_find(group, wd);
747 if (unlikely(!entry)) { 832 if (unlikely(!ientry))
748 spin_unlock(&group->inotify_data.idr_lock);
749 ret = -EINVAL;
750 goto out; 833 goto out;
751 }
752 fsnotify_get_mark(entry);
753 spin_unlock(&group->inotify_data.idr_lock);
754 834
755 fsnotify_destroy_mark_by_entry(entry); 835 ret = 0;
756 fsnotify_put_mark(entry); 836
837 fsnotify_destroy_mark_by_entry(&ientry->fsn_entry);
838
839 /* match ref taken by inotify_idr_find */
840 fsnotify_put_mark(&ientry->fsn_entry);
757 841
758out: 842out:
759 fput_light(filp, fput_needed); 843 fput_light(filp, fput_needed);