Skip to content

Commit 67245ff

Browse files
committed
devpts: clean up interface to pty drivers
This gets rid of the horrible notion of having that struct inode *ptmx_inode be the linchpin of the interface between the pty code and devpts. By de-emphasizing the ptmx inode, a lot of things actually get cleaner, and we will have a much saner way forward. In particular, this will allow us to associate with any particular devpts instance at open-time, and not be artificially tied to one particular ptmx inode. The patch itself is actually fairly straightforward, and apart from some locking and return path cleanups it's pretty mechanical: - the interfaces that devpts exposes all take "struct pts_fs_info *" instead of "struct inode *ptmx_inode" now. NOTE! The "struct pts_fs_info" thing is a completely opaque structure as far as the pty driver is concerned: it's still declared entirely internally to devpts. So the pty code can't actually access it in any way, just pass it as a "cookie" to the devpts code. - the "look up the pts fs info" is now a single clear operation, that also does the reference count increment on the pts superblock. So "devpts_add/del_ref()" is gone, and replaced by a "lookup and get ref" operation (devpts_get_ref(inode)), along with a "put ref" op (devpts_put_ref()). - the pty master "tty->driver_data" field now contains the pts_fs_info, not the ptmx inode. - because we don't care about the ptmx inode any more as some kind of base index, the ref counting can now drop the inode games - it just gets the ref on the superblock. - the pts_fs_info now has a back-pointer to the super_block. That's so that we can easily look up the information we actually need. Although quite often, the pts fs info was actually all we wanted, and not having to look it up based on some magical inode makes things more straightforward. In particular, now that "devpts_get_ref(inode)" operation should really be the *only* place we need to look up what devpts instance we're associated with, and we do it exactly once, at ptmx_open() time. The other side of this is that one ptmx node could now be associated with multiple different devpts instances - you could have a single /dev/ptmx node, and then have multiple mount namespaces with their own instances of devpts mounted on /dev/pts/. And that's all perfectly sane in a model where we just look up the pts instance at open time. This will eventually allow us to get rid of our odd single-vs-multiple pts instance model, but this patch in itself changes no semantics, only an internal binding model. Cc: Eric Biederman <[email protected]> Cc: Peter Anvin <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Al Viro <[email protected]> Cc: Peter Hurley <[email protected]> Cc: Serge Hallyn <[email protected]> Cc: Willy Tarreau <[email protected]> Cc: Aurelien Jarno <[email protected]> Cc: Alan Cox <[email protected]> Cc: Jann Horn <[email protected]> Cc: Greg KH <[email protected]> Cc: Jiri Slaby <[email protected]> Cc: Florian Weimer <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2e57259 commit 67245ff

File tree

3 files changed

+64
-82
lines changed

3 files changed

+64
-82
lines changed

drivers/tty/pty.c

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -663,14 +663,14 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
663663
/* this is called once with whichever end is closed last */
664664
static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
665665
{
666-
struct inode *ptmx_inode;
666+
struct pts_fs_info *fsi;
667667

668668
if (tty->driver->subtype == PTY_TYPE_MASTER)
669-
ptmx_inode = tty->driver_data;
669+
fsi = tty->driver_data;
670670
else
671-
ptmx_inode = tty->link->driver_data;
672-
devpts_kill_index(ptmx_inode, tty->index);
673-
devpts_del_ref(ptmx_inode);
671+
fsi = tty->link->driver_data;
672+
devpts_kill_index(fsi, tty->index);
673+
devpts_put_ref(fsi);
674674
}
675675

676676
static const struct tty_operations ptm_unix98_ops = {
@@ -720,6 +720,7 @@ static const struct tty_operations pty_unix98_ops = {
720720

721721
static int ptmx_open(struct inode *inode, struct file *filp)
722722
{
723+
struct pts_fs_info *fsi;
723724
struct tty_struct *tty;
724725
struct inode *slave_inode;
725726
int retval;
@@ -734,47 +735,41 @@ static int ptmx_open(struct inode *inode, struct file *filp)
734735
if (retval)
735736
return retval;
736737

738+
fsi = devpts_get_ref(inode, filp);
739+
retval = -ENODEV;
740+
if (!fsi)
741+
goto out_free_file;
742+
737743
/* find a device that is not in use. */
738744
mutex_lock(&devpts_mutex);
739-
index = devpts_new_index(inode);
740-
if (index < 0) {
741-
retval = index;
742-
mutex_unlock(&devpts_mutex);
743-
goto err_file;
744-
}
745-
745+
index = devpts_new_index(fsi);
746746
mutex_unlock(&devpts_mutex);
747747

748-
mutex_lock(&tty_mutex);
749-
tty = tty_init_dev(ptm_driver, index);
748+
retval = index;
749+
if (index < 0)
750+
goto out_put_ref;
750751

751-
if (IS_ERR(tty)) {
752-
retval = PTR_ERR(tty);
753-
goto out;
754-
}
755752

753+
mutex_lock(&tty_mutex);
754+
tty = tty_init_dev(ptm_driver, index);
756755
/* The tty returned here is locked so we can safely
757756
drop the mutex */
758757
mutex_unlock(&tty_mutex);
759758

760-
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
761-
tty->driver_data = inode;
759+
retval = PTR_ERR(tty);
760+
if (IS_ERR(tty))
761+
goto out;
762762

763763
/*
764-
* In the case where all references to ptmx inode are dropped and we
765-
* still have /dev/tty opened pointing to the master/slave pair (ptmx
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.
764+
* From here on out, the tty is "live", and the index and
765+
* fsi will be killed/put by the tty_release()
772766
*/
773-
devpts_add_ref(inode);
767+
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
768+
tty->driver_data = fsi;
774769

775770
tty_add_file(tty, filp);
776771

777-
slave_inode = devpts_pty_new(inode,
772+
slave_inode = devpts_pty_new(fsi,
778773
MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index,
779774
tty->link);
780775
if (IS_ERR(slave_inode)) {
@@ -793,12 +788,14 @@ static int ptmx_open(struct inode *inode, struct file *filp)
793788
return 0;
794789
err_release:
795790
tty_unlock(tty);
791+
// This will also put-ref the fsi
796792
tty_release(inode, filp);
797793
return retval;
798794
out:
799-
mutex_unlock(&tty_mutex);
800-
devpts_kill_index(inode, index);
801-
err_file:
795+
devpts_kill_index(fsi, index);
796+
out_put_ref:
797+
devpts_put_ref(fsi);
798+
out_free_file:
802799
tty_free_file(filp);
803800
return retval;
804801
}

fs/devpts/inode.c

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ static const match_table_t tokens = {
128128
struct pts_fs_info {
129129
struct ida allocated_ptys;
130130
struct pts_mount_opts mount_opts;
131+
struct super_block *sb;
131132
struct dentry *ptmx_dentry;
132133
};
133134

@@ -358,7 +359,7 @@ static const struct super_operations devpts_sops = {
358359
.show_options = devpts_show_options,
359360
};
360361

361-
static void *new_pts_fs_info(void)
362+
static void *new_pts_fs_info(struct super_block *sb)
362363
{
363364
struct pts_fs_info *fsi;
364365

@@ -369,6 +370,7 @@ static void *new_pts_fs_info(void)
369370
ida_init(&fsi->allocated_ptys);
370371
fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
371372
fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
373+
fsi->sb = sb;
372374

373375
return fsi;
374376
}
@@ -384,7 +386,7 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
384386
s->s_op = &devpts_sops;
385387
s->s_time_gran = 1;
386388

387-
s->s_fs_info = new_pts_fs_info();
389+
s->s_fs_info = new_pts_fs_info(s);
388390
if (!s->s_fs_info)
389391
goto fail;
390392

@@ -524,17 +526,14 @@ static struct file_system_type devpts_fs_type = {
524526
* to the System V naming convention
525527
*/
526528

527-
int devpts_new_index(struct inode *ptmx_inode)
529+
int devpts_new_index(struct pts_fs_info *fsi)
528530
{
529-
struct super_block *sb = pts_sb_from_inode(ptmx_inode);
530-
struct pts_fs_info *fsi;
531531
int index;
532532
int ida_ret;
533533

534-
if (!sb)
534+
if (!fsi)
535535
return -ENODEV;
536536

537-
fsi = DEVPTS_SB(sb);
538537
retry:
539538
if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL))
540539
return -ENOMEM;
@@ -564,11 +563,8 @@ int devpts_new_index(struct inode *ptmx_inode)
564563
return index;
565564
}
566565

567-
void devpts_kill_index(struct inode *ptmx_inode, int idx)
566+
void devpts_kill_index(struct pts_fs_info *fsi, int idx)
568567
{
569-
struct super_block *sb = pts_sb_from_inode(ptmx_inode);
570-
struct pts_fs_info *fsi = DEVPTS_SB(sb);
571-
572568
mutex_lock(&allocated_ptys_lock);
573569
ida_remove(&fsi->allocated_ptys, idx);
574570
pty_count--;
@@ -578,21 +574,25 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx)
578574
/*
579575
* pty code needs to hold extra references in case of last /dev/tty close
580576
*/
581-
582-
void devpts_add_ref(struct inode *ptmx_inode)
577+
struct pts_fs_info *devpts_get_ref(struct inode *ptmx_inode, struct file *file)
583578
{
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;
585588

586589
atomic_inc(&sb->s_active);
587-
ihold(ptmx_inode);
590+
return fsi;
588591
}
589592

590-
void devpts_del_ref(struct inode *ptmx_inode)
593+
void devpts_put_ref(struct pts_fs_info *fsi)
591594
{
592-
struct super_block *sb = pts_sb_from_inode(ptmx_inode);
593-
594-
iput(ptmx_inode);
595-
deactivate_super(sb);
595+
deactivate_super(fsi->sb);
596596
}
597597

598598
/**
@@ -604,22 +604,21 @@ void devpts_del_ref(struct inode *ptmx_inode)
604604
*
605605
* The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill.
606606
*/
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,
608608
void *priv)
609609
{
610610
struct dentry *dentry;
611-
struct super_block *sb = pts_sb_from_inode(ptmx_inode);
611+
struct super_block *sb;
612612
struct inode *inode;
613613
struct dentry *root;
614-
struct pts_fs_info *fsi;
615614
struct pts_mount_opts *opts;
616615
char s[12];
617616

618-
if (!sb)
617+
if (!fsi)
619618
return ERR_PTR(-ENODEV);
620619

620+
sb = fsi->sb;
621621
root = sb->s_root;
622-
fsi = DEVPTS_SB(sb);
623622
opts = &fsi->mount_opts;
624623

625624
inode = new_inode(sb);

include/linux/devpts_fs.h

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,38 +15,24 @@
1515

1616
#include <linux/errno.h>
1717

18+
struct pts_fs_info;
19+
1820
#ifdef CONFIG_UNIX98_PTYS
1921

20-
int devpts_new_index(struct inode *ptmx_inode);
21-
void devpts_kill_index(struct inode *ptmx_inode, int idx);
22-
void devpts_add_ref(struct inode *ptmx_inode);
23-
void devpts_del_ref(struct inode *ptmx_inode);
22+
/* Look up a pts fs info and get a ref to it */
23+
struct pts_fs_info *devpts_get_ref(struct inode *, struct file *);
24+
void devpts_put_ref(struct pts_fs_info *);
25+
26+
int devpts_new_index(struct pts_fs_info *);
27+
void devpts_kill_index(struct pts_fs_info *, int);
28+
2429
/* mknod in devpts */
25-
struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index,
26-
void *priv);
30+
struct inode *devpts_pty_new(struct pts_fs_info *, dev_t, int, void *);
2731
/* get private structure */
2832
void *devpts_get_priv(struct inode *pts_inode);
2933
/* unlink */
3034
void devpts_pty_kill(struct inode *inode);
3135

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-
5036
#endif
5137

5238

0 commit comments

Comments
 (0)