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
This commit is contained in:
Linus Torvalds 2016-04-19 16:36:18 -07:00
Родитель 12566cc35d 67245ff332
Коммит 9a0e3eea25
3 изменённых файлов: 64 добавлений и 82 удалений

Просмотреть файл

@ -663,14 +663,14 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
/* this is called once with whichever end is closed last */ /* this is called once with whichever end is closed last */
static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
{ {
struct inode *ptmx_inode; struct pts_fs_info *fsi;
if (tty->driver->subtype == PTY_TYPE_MASTER) if (tty->driver->subtype == PTY_TYPE_MASTER)
ptmx_inode = tty->driver_data; fsi = tty->driver_data;
else else
ptmx_inode = tty->link->driver_data; fsi = tty->link->driver_data;
devpts_kill_index(ptmx_inode, tty->index); devpts_kill_index(fsi, tty->index);
devpts_del_ref(ptmx_inode); devpts_put_ref(fsi);
} }
static const struct tty_operations ptm_unix98_ops = { static const struct tty_operations ptm_unix98_ops = {
@ -720,6 +720,7 @@ static const struct tty_operations pty_unix98_ops = {
static int ptmx_open(struct inode *inode, struct file *filp) static int ptmx_open(struct inode *inode, struct file *filp)
{ {
struct pts_fs_info *fsi;
struct tty_struct *tty; struct tty_struct *tty;
struct inode *slave_inode; struct inode *slave_inode;
int retval; int retval;
@ -734,47 +735,41 @@ static int ptmx_open(struct inode *inode, struct file *filp)
if (retval) if (retval)
return retval; return retval;
fsi = devpts_get_ref(inode, filp);
retval = -ENODEV;
if (!fsi)
goto out_free_file;
/* find a device that is not in use. */ /* find a device that is not in use. */
mutex_lock(&devpts_mutex); mutex_lock(&devpts_mutex);
index = devpts_new_index(inode); index = devpts_new_index(fsi);
if (index < 0) {
retval = index;
mutex_unlock(&devpts_mutex);
goto err_file;
}
mutex_unlock(&devpts_mutex); mutex_unlock(&devpts_mutex);
retval = index;
if (index < 0)
goto out_put_ref;
mutex_lock(&tty_mutex); mutex_lock(&tty_mutex);
tty = tty_init_dev(ptm_driver, index); tty = tty_init_dev(ptm_driver, index);
if (IS_ERR(tty)) {
retval = PTR_ERR(tty);
goto out;
}
/* The tty returned here is locked so we can safely /* The tty returned here is locked so we can safely
drop the mutex */ drop the mutex */
mutex_unlock(&tty_mutex); mutex_unlock(&tty_mutex);
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ retval = PTR_ERR(tty);
tty->driver_data = inode; if (IS_ERR(tty))
goto out;
/* /*
* In the case where all references to ptmx inode are dropped and we * From here on out, the tty is "live", and the index and
* still have /dev/tty opened pointing to the master/slave pair (ptmx * fsi will be killed/put by the tty_release()
* is closed/released before /dev/tty), we must make sure that the inode
* is still valid when we call the final pty_unix98_shutdown, thus we
* hold an additional reference to the ptmx inode. For the same /dev/tty
* last close case, we also need to make sure the super_block isn't
* destroyed (devpts instance unmounted), before /dev/tty is closed and
* on its release devpts_kill_index is called.
*/ */
devpts_add_ref(inode); set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
tty->driver_data = fsi;
tty_add_file(tty, filp); tty_add_file(tty, filp);
slave_inode = devpts_pty_new(inode, slave_inode = devpts_pty_new(fsi,
MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index,
tty->link); tty->link);
if (IS_ERR(slave_inode)) { if (IS_ERR(slave_inode)) {
@ -793,12 +788,14 @@ static int ptmx_open(struct inode *inode, struct file *filp)
return 0; return 0;
err_release: err_release:
tty_unlock(tty); tty_unlock(tty);
// This will also put-ref the fsi
tty_release(inode, filp); tty_release(inode, filp);
return retval; return retval;
out: out:
mutex_unlock(&tty_mutex); devpts_kill_index(fsi, index);
devpts_kill_index(inode, index); out_put_ref:
err_file: devpts_put_ref(fsi);
out_free_file:
tty_free_file(filp); tty_free_file(filp);
return retval; return retval;
} }

Просмотреть файл

@ -128,6 +128,7 @@ static const match_table_t tokens = {
struct pts_fs_info { struct pts_fs_info {
struct ida allocated_ptys; struct ida allocated_ptys;
struct pts_mount_opts mount_opts; struct pts_mount_opts mount_opts;
struct super_block *sb;
struct dentry *ptmx_dentry; struct dentry *ptmx_dentry;
}; };
@ -358,7 +359,7 @@ static const struct super_operations devpts_sops = {
.show_options = devpts_show_options, .show_options = devpts_show_options,
}; };
static void *new_pts_fs_info(void) static void *new_pts_fs_info(struct super_block *sb)
{ {
struct pts_fs_info *fsi; struct pts_fs_info *fsi;
@ -369,6 +370,7 @@ static void *new_pts_fs_info(void)
ida_init(&fsi->allocated_ptys); ida_init(&fsi->allocated_ptys);
fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE; fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
fsi->sb = sb;
return fsi; return fsi;
} }
@ -384,7 +386,7 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
s->s_op = &devpts_sops; s->s_op = &devpts_sops;
s->s_time_gran = 1; s->s_time_gran = 1;
s->s_fs_info = new_pts_fs_info(); s->s_fs_info = new_pts_fs_info(s);
if (!s->s_fs_info) if (!s->s_fs_info)
goto fail; goto fail;
@ -524,17 +526,14 @@ static struct file_system_type devpts_fs_type = {
* to the System V naming convention * to the System V naming convention
*/ */
int devpts_new_index(struct inode *ptmx_inode) int devpts_new_index(struct pts_fs_info *fsi)
{ {
struct super_block *sb = pts_sb_from_inode(ptmx_inode);
struct pts_fs_info *fsi;
int index; int index;
int ida_ret; int ida_ret;
if (!sb) if (!fsi)
return -ENODEV; return -ENODEV;
fsi = DEVPTS_SB(sb);
retry: retry:
if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
@ -564,11 +563,8 @@ retry:
return index; return index;
} }
void devpts_kill_index(struct inode *ptmx_inode, int idx) void devpts_kill_index(struct pts_fs_info *fsi, int idx)
{ {
struct super_block *sb = pts_sb_from_inode(ptmx_inode);
struct pts_fs_info *fsi = DEVPTS_SB(sb);
mutex_lock(&allocated_ptys_lock); mutex_lock(&allocated_ptys_lock);
ida_remove(&fsi->allocated_ptys, idx); ida_remove(&fsi->allocated_ptys, idx);
pty_count--; pty_count--;
@ -578,21 +574,25 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx)
/* /*
* pty code needs to hold extra references in case of last /dev/tty close * pty code needs to hold extra references in case of last /dev/tty close
*/ */
struct pts_fs_info *devpts_get_ref(struct inode *ptmx_inode, struct file *file)
void devpts_add_ref(struct inode *ptmx_inode)
{ {
struct super_block *sb = pts_sb_from_inode(ptmx_inode); struct super_block *sb;
struct pts_fs_info *fsi;
sb = pts_sb_from_inode(ptmx_inode);
if (!sb)
return NULL;
fsi = DEVPTS_SB(sb);
if (!fsi)
return NULL;
atomic_inc(&sb->s_active); atomic_inc(&sb->s_active);
ihold(ptmx_inode); return fsi;
} }
void devpts_del_ref(struct inode *ptmx_inode) void devpts_put_ref(struct pts_fs_info *fsi)
{ {
struct super_block *sb = pts_sb_from_inode(ptmx_inode); deactivate_super(fsi->sb);
iput(ptmx_inode);
deactivate_super(sb);
} }
/** /**
@ -604,22 +604,21 @@ void devpts_del_ref(struct inode *ptmx_inode)
* *
* The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill. * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill.
*/ */
struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, struct inode *devpts_pty_new(struct pts_fs_info *fsi, dev_t device, int index,
void *priv) void *priv)
{ {
struct dentry *dentry; struct dentry *dentry;
struct super_block *sb = pts_sb_from_inode(ptmx_inode); struct super_block *sb;
struct inode *inode; struct inode *inode;
struct dentry *root; struct dentry *root;
struct pts_fs_info *fsi;
struct pts_mount_opts *opts; struct pts_mount_opts *opts;
char s[12]; char s[12];
if (!sb) if (!fsi)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
sb = fsi->sb;
root = sb->s_root; root = sb->s_root;
fsi = DEVPTS_SB(sb);
opts = &fsi->mount_opts; opts = &fsi->mount_opts;
inode = new_inode(sb); inode = new_inode(sb);

Просмотреть файл

@ -15,38 +15,24 @@
#include <linux/errno.h> #include <linux/errno.h>
struct pts_fs_info;
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
int devpts_new_index(struct inode *ptmx_inode); /* Look up a pts fs info and get a ref to it */
void devpts_kill_index(struct inode *ptmx_inode, int idx); struct pts_fs_info *devpts_get_ref(struct inode *, struct file *);
void devpts_add_ref(struct inode *ptmx_inode); void devpts_put_ref(struct pts_fs_info *);
void devpts_del_ref(struct inode *ptmx_inode);
int devpts_new_index(struct pts_fs_info *);
void devpts_kill_index(struct pts_fs_info *, int);
/* mknod in devpts */ /* mknod in devpts */
struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, struct inode *devpts_pty_new(struct pts_fs_info *, dev_t, int, void *);
void *priv);
/* get private structure */ /* get private structure */
void *devpts_get_priv(struct inode *pts_inode); void *devpts_get_priv(struct inode *pts_inode);
/* unlink */ /* unlink */
void devpts_pty_kill(struct inode *inode); void devpts_pty_kill(struct inode *inode);
#else
/* Dummy stubs in the no-pty case */
static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; }
static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { }
static inline void devpts_add_ref(struct inode *ptmx_inode) { }
static inline void devpts_del_ref(struct inode *ptmx_inode) { }
static inline struct inode *devpts_pty_new(struct inode *ptmx_inode,
dev_t device, int index, void *priv)
{
return ERR_PTR(-EINVAL);
}
static inline void *devpts_get_priv(struct inode *pts_inode)
{
return NULL;
}
static inline void devpts_pty_kill(struct inode *inode) { }
#endif #endif