diff options
| -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 | ||
