diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-04-19 19:36:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-04-19 19:36:18 -0400 |
commit | 9a0e3eea25d3ab267aff9d4eaed83fbe46d989d0 (patch) | |
tree | ecd334957a0c62bcd4fe2fe9feea53ba9a6905cf | |
parent | 12566cc35d0e68308bde7aad615743d560cb097b (diff) | |
parent | 67245ff332064c01b760afa7a384ccda024bfd24 (diff) |
Merge branch 'ptmx-cleanup'
Merge the ptmx internal interface cleanup branch.
This doesn't change semantics, but it should be a sane basis for
eventually getting the multi-instance devpts code into some sane shape
where we can get rid of the kernel config option. Which we can
hopefully get done next merge window..
* ptmx-cleanup:
devpts: clean up interface to pty drivers
-rw-r--r-- | drivers/tty/pty.c | 63 | ||||
-rw-r--r-- | fs/devpts/inode.c | 49 | ||||
-rw-r--r-- | include/linux/devpts_fs.h | 34 |
3 files changed, 64 insertions, 82 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index e16a49b507ef..0058d9fbf931 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c | |||
@@ -663,14 +663,14 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) | |||
663 | /* this is called once with whichever end is closed last */ | 663 | /* this is called once with whichever end is closed last */ |
664 | static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) | 664 | static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) |
665 | { | 665 | { |
666 | struct inode *ptmx_inode; | 666 | struct pts_fs_info *fsi; |
667 | 667 | ||
668 | if (tty->driver->subtype == PTY_TYPE_MASTER) | 668 | if (tty->driver->subtype == PTY_TYPE_MASTER) |
669 | ptmx_inode = tty->driver_data; | 669 | fsi = tty->driver_data; |
670 | else | 670 | else |
671 | ptmx_inode = tty->link->driver_data; | 671 | fsi = tty->link->driver_data; |
672 | devpts_kill_index(ptmx_inode, tty->index); | 672 | devpts_kill_index(fsi, tty->index); |
673 | devpts_del_ref(ptmx_inode); | 673 | devpts_put_ref(fsi); |
674 | } | 674 | } |
675 | 675 | ||
676 | static const struct tty_operations ptm_unix98_ops = { | 676 | static const struct tty_operations ptm_unix98_ops = { |
@@ -720,6 +720,7 @@ static const struct tty_operations pty_unix98_ops = { | |||
720 | 720 | ||
721 | static int ptmx_open(struct inode *inode, struct file *filp) | 721 | static int ptmx_open(struct inode *inode, struct file *filp) |
722 | { | 722 | { |
723 | struct pts_fs_info *fsi; | ||
723 | struct tty_struct *tty; | 724 | struct tty_struct *tty; |
724 | struct inode *slave_inode; | 725 | struct inode *slave_inode; |
725 | int retval; | 726 | int retval; |
@@ -734,47 +735,41 @@ static int ptmx_open(struct inode *inode, struct file *filp) | |||
734 | if (retval) | 735 | if (retval) |
735 | return retval; | 736 | return retval; |
736 | 737 | ||
738 | fsi = devpts_get_ref(inode, filp); | ||
739 | retval = -ENODEV; | ||
740 | if (!fsi) | ||
741 | goto out_free_file; | ||
742 | |||
737 | /* find a device that is not in use. */ | 743 | /* find a device that is not in use. */ |
738 | mutex_lock(&devpts_mutex); | 744 | mutex_lock(&devpts_mutex); |
739 | index = devpts_new_index(inode); | 745 | index = devpts_new_index(fsi); |
740 | if (index < 0) { | ||
741 | retval = index; | ||
742 | mutex_unlock(&devpts_mutex); | ||
743 | goto err_file; | ||
744 | } | ||
745 | |||
746 | mutex_unlock(&devpts_mutex); | 746 | mutex_unlock(&devpts_mutex); |
747 | 747 | ||
748 | mutex_lock(&tty_mutex); | 748 | retval = index; |
749 | tty = tty_init_dev(ptm_driver, index); | 749 | if (index < 0) |
750 | goto out_put_ref; | ||
750 | 751 | ||
751 | if (IS_ERR(tty)) { | ||
752 | retval = PTR_ERR(tty); | ||
753 | goto out; | ||
754 | } | ||
755 | 752 | ||
753 | mutex_lock(&tty_mutex); | ||
754 | tty = tty_init_dev(ptm_driver, index); | ||
756 | /* The tty returned here is locked so we can safely | 755 | /* The tty returned here is locked so we can safely |
757 | drop the mutex */ | 756 | drop the mutex */ |
758 | mutex_unlock(&tty_mutex); | 757 | mutex_unlock(&tty_mutex); |
759 | 758 | ||
760 | set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ | 759 | retval = PTR_ERR(tty); |
761 | tty->driver_data = inode; | 760 | if (IS_ERR(tty)) |
761 | goto out; | ||
762 | 762 | ||
763 | /* | 763 | /* |
764 | * In the case where all references to ptmx inode are dropped and we | 764 | * From here on out, the tty is "live", and the index and |
765 | * still have /dev/tty opened pointing to the master/slave pair (ptmx | 765 | * fsi will be killed/put by the tty_release() |
766 | * is closed/released before /dev/tty), we must make sure that the inode | ||
767 | * is still valid when we call the final pty_unix98_shutdown, thus we | ||
768 | * hold an additional reference to the ptmx inode. For the same /dev/tty | ||
769 | * last close case, we also need to make sure the super_block isn't | ||
770 | * destroyed (devpts instance unmounted), before /dev/tty is closed and | ||
771 | * on its release devpts_kill_index is called. | ||
772 | */ | 766 | */ |
773 | devpts_add_ref(inode); | 767 | set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ |
768 | tty->driver_data = fsi; | ||
774 | 769 | ||
775 | tty_add_file(tty, filp); | 770 | tty_add_file(tty, filp); |
776 | 771 | ||
777 | slave_inode = devpts_pty_new(inode, | 772 | slave_inode = devpts_pty_new(fsi, |
778 | MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, | 773 | MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, |
779 | tty->link); | 774 | tty->link); |
780 | if (IS_ERR(slave_inode)) { | 775 | if (IS_ERR(slave_inode)) { |
@@ -793,12 +788,14 @@ static int ptmx_open(struct inode *inode, struct file *filp) | |||
793 | return 0; | 788 | return 0; |
794 | err_release: | 789 | err_release: |
795 | tty_unlock(tty); | 790 | tty_unlock(tty); |
791 | // This will also put-ref the fsi | ||
796 | tty_release(inode, filp); | 792 | tty_release(inode, filp); |
797 | return retval; | 793 | return retval; |
798 | out: | 794 | out: |
799 | mutex_unlock(&tty_mutex); | 795 | devpts_kill_index(fsi, index); |
800 | devpts_kill_index(inode, index); | 796 | out_put_ref: |
801 | err_file: | 797 | devpts_put_ref(fsi); |
798 | out_free_file: | ||
802 | tty_free_file(filp); | 799 | tty_free_file(filp); |
803 | return retval; | 800 | return retval; |
804 | } | 801 | } |
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 655f21f99160..0af8e7d70d27 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c | |||
@@ -128,6 +128,7 @@ static const match_table_t tokens = { | |||
128 | struct pts_fs_info { | 128 | struct pts_fs_info { |
129 | struct ida allocated_ptys; | 129 | struct ida allocated_ptys; |
130 | struct pts_mount_opts mount_opts; | 130 | struct pts_mount_opts mount_opts; |
131 | struct super_block *sb; | ||
131 | struct dentry *ptmx_dentry; | 132 | struct dentry *ptmx_dentry; |
132 | }; | 133 | }; |
133 | 134 | ||
@@ -358,7 +359,7 @@ static const struct super_operations devpts_sops = { | |||
358 | .show_options = devpts_show_options, | 359 | .show_options = devpts_show_options, |
359 | }; | 360 | }; |
360 | 361 | ||
361 | static void *new_pts_fs_info(void) | 362 | static void *new_pts_fs_info(struct super_block *sb) |
362 | { | 363 | { |
363 | struct pts_fs_info *fsi; | 364 | struct pts_fs_info *fsi; |
364 | 365 | ||
@@ -369,6 +370,7 @@ static void *new_pts_fs_info(void) | |||
369 | ida_init(&fsi->allocated_ptys); | 370 | ida_init(&fsi->allocated_ptys); |
370 | fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE; | 371 | fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE; |
371 | fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; | 372 | fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; |
373 | fsi->sb = sb; | ||
372 | 374 | ||
373 | return fsi; | 375 | return fsi; |
374 | } | 376 | } |
@@ -384,7 +386,7 @@ devpts_fill_super(struct super_block *s, void *data, int silent) | |||
384 | s->s_op = &devpts_sops; | 386 | s->s_op = &devpts_sops; |
385 | s->s_time_gran = 1; | 387 | s->s_time_gran = 1; |
386 | 388 | ||
387 | s->s_fs_info = new_pts_fs_info(); | 389 | s->s_fs_info = new_pts_fs_info(s); |
388 | if (!s->s_fs_info) | 390 | if (!s->s_fs_info) |
389 | goto fail; | 391 | goto fail; |
390 | 392 | ||
@@ -524,17 +526,14 @@ static struct file_system_type devpts_fs_type = { | |||
524 | * to the System V naming convention | 526 | * to the System V naming convention |
525 | */ | 527 | */ |
526 | 528 | ||
527 | int devpts_new_index(struct inode *ptmx_inode) | 529 | int devpts_new_index(struct pts_fs_info *fsi) |
528 | { | 530 | { |
529 | struct super_block *sb = pts_sb_from_inode(ptmx_inode); | ||
530 | struct pts_fs_info *fsi; | ||
531 | int index; | 531 | int index; |
532 | int ida_ret; | 532 | int ida_ret; |
533 | 533 | ||
534 | if (!sb) | 534 | if (!fsi) |
535 | return -ENODEV; | 535 | return -ENODEV; |
536 | 536 | ||
537 | fsi = DEVPTS_SB(sb); | ||
538 | retry: | 537 | retry: |
539 | if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) | 538 | if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) |
540 | return -ENOMEM; | 539 | return -ENOMEM; |
@@ -564,11 +563,8 @@ retry: | |||
564 | return index; | 563 | return index; |
565 | } | 564 | } |
566 | 565 | ||
567 | void devpts_kill_index(struct inode *ptmx_inode, int idx) | 566 | void devpts_kill_index(struct pts_fs_info *fsi, int idx) |
568 | { | 567 | { |
569 | struct super_block *sb = pts_sb_from_inode(ptmx_inode); | ||
570 | struct pts_fs_info *fsi = DEVPTS_SB(sb); | ||
571 | |||
572 | mutex_lock(&allocated_ptys_lock); | 568 | mutex_lock(&allocated_ptys_lock); |
573 | ida_remove(&fsi->allocated_ptys, idx); | 569 | ida_remove(&fsi->allocated_ptys, idx); |
574 | pty_count--; | 570 | pty_count--; |
@@ -578,21 +574,25 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) | |||
578 | /* | 574 | /* |
579 | * pty code needs to hold extra references in case of last /dev/tty close | 575 | * pty code needs to hold extra references in case of last /dev/tty close |
580 | */ | 576 | */ |
581 | 577 | struct pts_fs_info *devpts_get_ref(struct inode *ptmx_inode, struct file *file) | |
582 | void devpts_add_ref(struct inode *ptmx_inode) | ||
583 | { | 578 | { |
584 | struct super_block *sb = pts_sb_from_inode(ptmx_inode); | 579 | struct super_block *sb; |
580 | struct pts_fs_info *fsi; | ||
581 | |||
582 | sb = pts_sb_from_inode(ptmx_inode); | ||
583 | if (!sb) | ||
584 | return NULL; | ||
585 | fsi = DEVPTS_SB(sb); | ||
586 | if (!fsi) | ||
587 | return NULL; | ||
585 | 588 | ||
586 | atomic_inc(&sb->s_active); | 589 | atomic_inc(&sb->s_active); |
587 | ihold(ptmx_inode); | 590 | return fsi; |
588 | } | 591 | } |
589 | 592 | ||
590 | void devpts_del_ref(struct inode *ptmx_inode) | 593 | void devpts_put_ref(struct pts_fs_info *fsi) |
591 | { | 594 | { |
592 | struct super_block *sb = pts_sb_from_inode(ptmx_inode); | 595 | deactivate_super(fsi->sb); |
593 | |||
594 | iput(ptmx_inode); | ||
595 | deactivate_super(sb); | ||
596 | } | 596 | } |
597 | 597 | ||
598 | /** | 598 | /** |
@@ -604,22 +604,21 @@ void devpts_del_ref(struct inode *ptmx_inode) | |||
604 | * | 604 | * |
605 | * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill. | 605 | * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill. |
606 | */ | 606 | */ |
607 | struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, | 607 | struct inode *devpts_pty_new(struct pts_fs_info *fsi, dev_t device, int index, |
608 | void *priv) | 608 | void *priv) |
609 | { | 609 | { |
610 | struct dentry *dentry; | 610 | struct dentry *dentry; |
611 | struct super_block *sb = pts_sb_from_inode(ptmx_inode); | 611 | struct super_block *sb; |
612 | struct inode *inode; | 612 | struct inode *inode; |
613 | struct dentry *root; | 613 | struct dentry *root; |
614 | struct pts_fs_info *fsi; | ||
615 | struct pts_mount_opts *opts; | 614 | struct pts_mount_opts *opts; |
616 | char s[12]; | 615 | char s[12]; |
617 | 616 | ||
618 | if (!sb) | 617 | if (!fsi) |
619 | return ERR_PTR(-ENODEV); | 618 | return ERR_PTR(-ENODEV); |
620 | 619 | ||
620 | sb = fsi->sb; | ||
621 | root = sb->s_root; | 621 | root = sb->s_root; |
622 | fsi = DEVPTS_SB(sb); | ||
623 | opts = &fsi->mount_opts; | 622 | opts = &fsi->mount_opts; |
624 | 623 | ||
625 | inode = new_inode(sb); | 624 | inode = new_inode(sb); |
diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index e0ee0b3000b2..358a4db72a27 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h | |||
@@ -15,38 +15,24 @@ | |||
15 | 15 | ||
16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
17 | 17 | ||
18 | struct pts_fs_info; | ||
19 | |||
18 | #ifdef CONFIG_UNIX98_PTYS | 20 | #ifdef CONFIG_UNIX98_PTYS |
19 | 21 | ||
20 | int devpts_new_index(struct inode *ptmx_inode); | 22 | /* Look up a pts fs info and get a ref to it */ |
21 | void devpts_kill_index(struct inode *ptmx_inode, int idx); | 23 | struct pts_fs_info *devpts_get_ref(struct inode *, struct file *); |
22 | void devpts_add_ref(struct inode *ptmx_inode); | 24 | void devpts_put_ref(struct pts_fs_info *); |
23 | void devpts_del_ref(struct inode *ptmx_inode); | 25 | |
26 | int devpts_new_index(struct pts_fs_info *); | ||
27 | void devpts_kill_index(struct pts_fs_info *, int); | ||
28 | |||
24 | /* mknod in devpts */ | 29 | /* mknod in devpts */ |
25 | struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, | 30 | struct inode *devpts_pty_new(struct pts_fs_info *, dev_t, int, void *); |
26 | void *priv); | ||
27 | /* get private structure */ | 31 | /* get private structure */ |
28 | void *devpts_get_priv(struct inode *pts_inode); | 32 | void *devpts_get_priv(struct inode *pts_inode); |
29 | /* unlink */ | 33 | /* unlink */ |
30 | void devpts_pty_kill(struct inode *inode); | 34 | void devpts_pty_kill(struct inode *inode); |
31 | 35 | ||
32 | #else | ||
33 | |||
34 | /* Dummy stubs in the no-pty case */ | ||
35 | static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } | ||
36 | static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } | ||
37 | static inline void devpts_add_ref(struct inode *ptmx_inode) { } | ||
38 | static inline void devpts_del_ref(struct inode *ptmx_inode) { } | ||
39 | static inline struct inode *devpts_pty_new(struct inode *ptmx_inode, | ||
40 | dev_t device, int index, void *priv) | ||
41 | { | ||
42 | return ERR_PTR(-EINVAL); | ||
43 | } | ||
44 | static inline void *devpts_get_priv(struct inode *pts_inode) | ||
45 | { | ||
46 | return NULL; | ||
47 | } | ||
48 | static inline void devpts_pty_kill(struct inode *inode) { } | ||
49 | |||
50 | #endif | 36 | #endif |
51 | 37 | ||
52 | 38 | ||