Commit 3931ca0a authored by H. Peter Anvin's avatar H. Peter Anvin Committed by Linus Torvalds

[PATCH] Use first-fit for pty allocation

(With Andrew Morton).

The current dynamic pty allocation scheme has a few problems:

- pty numbers grow to be very large, causing wtmp file bloat.

- Seems to break libc5 and some old applications

So change it to do first-fit.  An IDR tree is used to provide a
logarithmic-time search.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5c4ad014
...@@ -91,6 +91,7 @@ ...@@ -91,6 +91,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/idr.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -128,6 +129,8 @@ DECLARE_MUTEX(tty_sem); ...@@ -128,6 +129,8 @@ DECLARE_MUTEX(tty_sem);
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
extern int pty_limit; /* Config limit on Unix98 ptys */ extern int pty_limit; /* Config limit on Unix98 ptys */
static DEFINE_IDR(allocated_ptys);
static DECLARE_MUTEX(allocated_ptys_lock);
#endif #endif
extern void disable_early_printk(void); extern void disable_early_printk(void);
...@@ -1065,6 +1068,7 @@ static void release_dev(struct file * filp) ...@@ -1065,6 +1068,7 @@ static void release_dev(struct file * filp)
{ {
struct tty_struct *tty, *o_tty; struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep; int pty_master, tty_closing, o_tty_closing, do_sleep;
int devpts_master;
int idx; int idx;
char buf[64]; char buf[64];
...@@ -1079,6 +1083,7 @@ static void release_dev(struct file * filp) ...@@ -1079,6 +1083,7 @@ static void release_dev(struct file * filp)
idx = tty->index; idx = tty->index;
pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER); tty->driver->subtype == PTY_TYPE_MASTER);
devpts_master = pty_master && (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM);
o_tty = tty->link; o_tty = tty->link;
#ifdef TTY_PARANOIA_CHECK #ifdef TTY_PARANOIA_CHECK
...@@ -1295,11 +1300,21 @@ static void release_dev(struct file * filp) ...@@ -1295,11 +1300,21 @@ static void release_dev(struct file * filp)
o_tty->ldisc = ldiscs[N_TTY]; o_tty->ldisc = ldiscs[N_TTY];
} }
/* /*
* The release_mem function takes care of the details of clearing * The release_mem function takes care of the details of clearing
* the slots and preserving the termios structure. * the slots and preserving the termios structure.
*/ */
release_mem(tty, idx); release_mem(tty, idx);
#ifdef CONFIG_UNIX98_PTYS
/* Make this pty number available for reallocation */
if (devpts_master) {
down(&allocated_ptys_lock);
idr_remove(&allocated_ptys, idx);
up(&allocated_ptys_lock);
}
#endif
} }
/* /*
...@@ -1322,8 +1337,12 @@ static int tty_open(struct inode * inode, struct file * filp) ...@@ -1322,8 +1337,12 @@ static int tty_open(struct inode * inode, struct file * filp)
int index; int index;
dev_t device = inode->i_rdev; dev_t device = inode->i_rdev;
unsigned short saved_flags = filp->f_flags; unsigned short saved_flags = filp->f_flags;
retry_open: retry_open:
noctty = filp->f_flags & O_NOCTTY; noctty = filp->f_flags & O_NOCTTY;
index = -1;
retval = 0;
if (device == MKDEV(TTYAUX_MAJOR,0)) { if (device == MKDEV(TTYAUX_MAJOR,0)) {
if (!current->signal->tty) if (!current->signal->tty)
return -ENXIO; return -ENXIO;
...@@ -1361,24 +1380,40 @@ static int tty_open(struct inode * inode, struct file * filp) ...@@ -1361,24 +1380,40 @@ static int tty_open(struct inode * inode, struct file * filp)
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
if (device == MKDEV(TTYAUX_MAJOR,2)) { if (device == MKDEV(TTYAUX_MAJOR,2)) {
int idr_ret;
/* find a device that is not in use. */ /* find a device that is not in use. */
static int next_ptmx_dev = 0; down(&allocated_ptys_lock);
retval = -1; if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
up(&allocated_ptys_lock);
return -ENOMEM;
}
idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
if (idr_ret < 0) {
up(&allocated_ptys_lock);
if (idr_ret == -EAGAIN)
return -ENOMEM;
return -EIO;
}
if (index >= pty_limit) {
idr_remove(&allocated_ptys, index);
up(&allocated_ptys_lock);
return -EIO;
}
up(&allocated_ptys_lock);
driver = ptm_driver; driver = ptm_driver;
while (driver->refcount < pty_limit) { retval = init_dev(driver, index, &tty);
index = next_ptmx_dev; if (retval) {
next_ptmx_dev = (next_ptmx_dev+1) % driver->num; down(&allocated_ptys_lock);
if (!init_dev(driver, index, &tty)) idr_remove(&allocated_ptys, index);
goto ptmx_found; /* ok! */ up(&allocated_ptys_lock);
return retval;
} }
return -EIO; /* no free ptys */
ptmx_found:
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
if (devpts_pty_new(tty->link)) { if (devpts_pty_new(tty->link))
/* BADNESS - need to destroy both ptm and pts! */ retval = -ENOMEM;
return -ENOMEM;
}
noctty = 1;
} else } else
#endif #endif
{ {
...@@ -1400,10 +1435,12 @@ static int tty_open(struct inode * inode, struct file * filp) ...@@ -1400,10 +1435,12 @@ static int tty_open(struct inode * inode, struct file * filp)
#ifdef TTY_DEBUG_HANGUP #ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "opening %s...", tty->name); printk(KERN_DEBUG "opening %s...", tty->name);
#endif #endif
if (tty->driver->open) if (!retval) {
retval = tty->driver->open(tty, filp); if (tty->driver->open)
else retval = tty->driver->open(tty, filp);
retval = -ENODEV; else
retval = -ENODEV;
}
filp->f_flags = saved_flags; filp->f_flags = saved_flags;
if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
...@@ -1415,6 +1452,14 @@ static int tty_open(struct inode * inode, struct file * filp) ...@@ -1415,6 +1452,14 @@ static int tty_open(struct inode * inode, struct file * filp)
tty->name); tty->name);
#endif #endif
#ifdef CONFIG_UNIX98_PTYS
if (index != -1) {
down(&allocated_ptys_lock);
idr_remove(&allocated_ptys, index);
up(&allocated_ptys_lock);
}
#endif
release_dev(filp); release_dev(filp);
if (retval != -ERESTARTSYS) if (retval != -ERESTARTSYS)
return retval; return retval;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment