From: "H. Peter Anvin" <hpa@transmeta.com>

Remove the limit of 2048 pty's - allocate them on demand up to the 12:20
dev_t limit: a million.



---

 25-akpm/drivers/char/Kconfig       |   53 +++++++------
 25-akpm/drivers/char/pty.c         |   69 ++++++++++++++---
 25-akpm/drivers/char/tty_io.c      |  149 +++++++++++++++++++++++++------------
 25-akpm/fs/Kconfig                 |   28 ------
 25-akpm/fs/devpts/Makefile         |    4 
 25-akpm/fs/devpts/inode.c          |   36 +++++++-
 25-akpm/include/linux/devpts_fs.h  |   23 ++---
 25-akpm/include/linux/sysctl.h     |    8 +
 25-akpm/include/linux/tty.h        |   24 -----
 25-akpm/include/linux/tty_driver.h |   11 +-
 25-akpm/kernel/sysctl.c            |   11 ++
 11 files changed, 271 insertions(+), 145 deletions(-)

diff -puN drivers/char/Kconfig~dynamic-pty-allocation drivers/char/Kconfig
--- 25/drivers/char/Kconfig~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/drivers/char/Kconfig	Fri Feb 13 18:04:19 2004
@@ -445,7 +445,8 @@ config A2232
 source "drivers/serial/Kconfig"
 
 config UNIX98_PTYS
-	bool "Unix98 PTY support"
+	bool "Unix98 PTY support" if EMBEDDED
+	default y
 	---help---
 	  A pseudo terminal (PTY) is a software device consisting of two
 	  halves: a master and a slave. The slave device behaves identical to
@@ -463,28 +464,38 @@ config UNIX98_PTYS
 	  terminal slave can be accessed as /dev/pts/<number>. What was
 	  traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
 
-	  The entries in /dev/pts/ are created on the fly by a virtual
-	  file system; therefore, if you say Y here you should say Y to
-	  "/dev/pts file system for Unix98 PTYs" as well.
-
-	  If you want to say Y here, you need to have the C library glibc 2.1
-	  or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*").
-	  Read the instructions in <file:Documentation/Changes> pertaining to
-	  pseudo terminals. It's safe to say N.
-
-config UNIX98_PTY_COUNT
-	int "Maximum number of Unix98 PTYs in use (0-2048)"
-	depends on UNIX98_PTYS
+	  All modern Linux systems use the Unix98 ptys.  Say Y unless
+	  you're on an embedded system and want to conserve memory.
+
+config LEGACY_PTYS
+	bool "Legacy (BSD) PTY support"
+	default y
+	---help---
+	  A pseudo terminal (PTY) is a software device consisting of two
+	  halves: a master and a slave. The slave device behaves identical to
+	  a physical terminal; the master device is used by a process to
+	  read data from and write data to the slave, thereby emulating a
+	  terminal. Typical programs for the master side are telnet servers
+	  and xterms.
+
+	  Linux has traditionally used the BSD-like names /dev/ptyxx
+	  for masters and /dev/ttyxx for slaves of pseudo
+	  terminals. This scheme has a number of problems, including
+	  security.  This option enables these legacy devices; on most
+	  systems, it is safe to say N.
+
+
+config LEGACY_PTY_COUNT
+	int "Maximum number of legacy PTY in use"
+	depends on LEGACY_PTYS
 	default "256"
-	help
-	  The maximum number of Unix98 PTYs that can be used at any one time.
-	  The default is 256, and should be enough for desktop systems. Server
-	  machines which support incoming telnet/rlogin/ssh connections and/or
-	  serve several X terminals may want to increase this: every incoming
-	  connection and every xterm uses up one PTY.
+	---help---
+	  The maximum number of legacy PTYs that can be used at any one time.
+	  The default is 256, and should be more than enough.  Embedded
+	  systems may want to reduce this to save memory.
 
-	  When not in use, each additional set of 256 PTYs occupy
-	  approximately 8 KB of kernel memory on 32-bit architectures.
+	  When not in use, each legacy PTY occupies 12 bytes on 32-bit
+	  architectures and 24 bytes on 64-bit architectures.
 
 config PRINTER
 	tristate "Parallel printer support"
diff -puN drivers/char/pty.c~dynamic-pty-allocation drivers/char/pty.c
--- 25/drivers/char/pty.c~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/drivers/char/pty.c	Fri Feb 13 18:04:19 2004
@@ -25,16 +25,21 @@
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/sysctl.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <linux/devpts_fs.h>
 
+#if defined(CONFIG_LEGACY_PTYS) || defined(CONFIG_UNIX98_PTYS)
+
+#ifdef CONFIG_LEGACY_PTYS
 static struct tty_driver *pty_driver, *pty_slave_driver;
+#endif
 
-#ifdef CONFIG_UNIX98_PTYS
 /* These are global because they are accessed in tty_io.c */
+#ifdef CONFIG_UNIX98_PTYS
 struct tty_driver *ptm_driver;
 struct tty_driver *pts_driver;
 #endif
@@ -226,8 +231,9 @@ static int pty_set_lock(struct tty_struc
 	return 0;
 }
 
+#ifdef CONFIG_LEGACY_PTYS
 static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
-			unsigned int cmd, unsigned long arg)
+			 unsigned int cmd, unsigned long arg)
 {
 	if (!tty) {
 		printk("pty_ioctl called with NULL tty!\n");
@@ -239,6 +245,7 @@ static int pty_bsd_ioctl(struct tty_stru
 	}
 	return -ENOIOCTLCMD;
 }
+#endif
 
 #ifdef CONFIG_UNIX98_PTYS
 static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
@@ -249,11 +256,13 @@ static int pty_unix98_ioctl(struct tty_s
 		return -EIO;
 	}
 	switch(cmd) {
+	case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
+		return pty_set_lock(tty, (int *)arg);
 	case TIOCGPTN: /* Get PT Number */
 		return pty_get_device_number(tty, (unsigned int *)arg);
 	}
 
-	return pty_bsd_ioctl(tty,file,cmd,arg);
+	return -ENOIOCTLCMD;
 }
 #endif
 
@@ -313,8 +322,41 @@ static struct tty_operations pty_ops = {
 	.set_termios = pty_set_termios,
 };
 
+/* sysctl support for setting limits on the number of Unix98 ptys allocated.
+   Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. */
+#ifdef CONFIG_UNIX98_PTYS
+int pty_limit = NR_UNIX98_PTY_DEFAULT;
+static int pty_limit_min = 0;
+static int pty_limit_max = NR_UNIX98_PTY_MAX;
+
+ctl_table pty_table[] = {
+	{
+		.ctl_name	= PTY_MAX,
+		.procname	= "max",
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.data		= &pty_limit,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &pty_limit_min,
+		.extra2		= &pty_limit_max,
+	}, {
+		.ctl_name	= PTY_NR,
+		.procname	= "nr",
+		.maxlen		= sizeof(int),
+		.mode		= 0444,
+		.proc_handler	= &proc_dointvec,
+	}, {
+		.ctl_name	= 0
+	}
+};
+#endif
+
+/* Initialization */
+
 static int __init pty_init(void)
 {
+#ifdef CONFIG_LEGACY_PTYS
 	/* Traditional BSD devices */
 
 	pty_driver = alloc_tty_driver(NR_PTYS);
@@ -363,15 +405,15 @@ static int __init pty_init(void)
 	if (tty_register_driver(pty_slave_driver))
 		panic("Couldn't register pty slave driver");
 
+#endif /* CONFIG_LEGACY_PTYS */
 
-	/* Unix98 devices */
 #ifdef CONFIG_UNIX98_PTYS
+	/* Unix98 devices */
 	devfs_mk_dir("pts");
-	printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS);
-	ptm_driver = alloc_tty_driver(UNIX98_NR_MAJORS * NR_PTYS);
+	ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
 	if (!ptm_driver)
 		panic("Couldn't allocate Unix98 ptm driver");
-	pts_driver = alloc_tty_driver(UNIX98_NR_MAJORS * NR_PTYS);
+	pts_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
 	if (!pts_driver)
 		panic("Couldn't allocate Unix98 pts driver");
 
@@ -388,7 +430,7 @@ static int __init pty_init(void)
 	ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
 	ptm_driver->init_termios.c_lflag = 0;
 	ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
-				TTY_DRIVER_NO_DEVFS;
+		TTY_DRIVER_NO_DEVFS | TTY_DRIVER_DEVPTS_MEM;
 	ptm_driver->other = pts_driver;
 	tty_set_operations(ptm_driver, &pty_ops);
 	ptm_driver->ioctl = pty_unix98_ioctl;
@@ -402,8 +444,8 @@ static int __init pty_init(void)
 	pts_driver->subtype = PTY_TYPE_SLAVE;
 	pts_driver->init_termios = tty_std_termios;
 	pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
-	pts_driver->flags = TTY_DRIVER_RESET_TERMIOS |
-			TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+	pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
+		TTY_DRIVER_NO_DEVFS | TTY_DRIVER_DEVPTS_MEM;
 	pts_driver->other = ptm_driver;
 	tty_set_operations(pts_driver, &pty_ops);
 	
@@ -411,7 +453,12 @@ static int __init pty_init(void)
 		panic("Couldn't register Unix98 ptm driver");
 	if (tty_register_driver(pts_driver))
 		panic("Couldn't register Unix98 pts driver");
-#endif
+
+	pty_table[1].data = &ptm_driver->refcount;
+#endif /* CONFIG_UNIX98_PTYS */
+
 	return 0;
 }
 module_init(pty_init);
+
+#endif /* CONFIG_LEGACY_PTYS || CONFIG_UNIX98_PTYS */
diff -puN drivers/char/tty_io.c~dynamic-pty-allocation drivers/char/tty_io.c
--- 25/drivers/char/tty_io.c~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/drivers/char/tty_io.c	Fri Feb 13 18:04:19 2004
@@ -124,7 +124,7 @@ struct tty_ldisc ldiscs[NR_LDISCS];	/* l
 
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;	/* Unix98 pty masters; for /dev/ptmx */
-extern struct tty_driver *pts_driver;	/* Unix98 pty slaves;  for /dev/ptmx */
+extern int pty_limit;		/* Config limit on Unix98 ptys */
 #endif
 
 extern void disable_early_printk(void);
@@ -799,7 +799,13 @@ static int init_dev(struct tty_driver *d
 	down_tty_sem(idx);
 
 	/* check whether we're reopening an existing tty */
-	tty = driver->ttys[idx];
+	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
+		tty = devpts_get_tty(idx);
+		if (tty && driver->subtype == PTY_TYPE_MASTER)
+			tty = tty->link;
+	} else {
+		tty = driver->ttys[idx];
+	}
 	if (tty) goto fast_track;
 
 	/*
@@ -827,7 +833,14 @@ static int init_dev(struct tty_driver *d
 	tty->index = idx;
 	tty_line_name(driver, idx, tty->name);
 
-	tp_loc = &driver->termios[idx];
+	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
+		tp_loc = &tty->termios;
+		ltp_loc = &tty->termios_locked;
+	} else {
+		tp_loc = &driver->termios[idx];
+		ltp_loc = &driver->termios_locked[idx];
+	}
+
 	if (!*tp_loc) {
 		tp = (struct termios *) kmalloc(sizeof(struct termios),
 						GFP_KERNEL);
@@ -836,7 +849,6 @@ static int init_dev(struct tty_driver *d
 		*tp = driver->init_termios;
 	}
 
-	ltp_loc = &driver->termios_locked[idx];
 	if (!*ltp_loc) {
 		ltp = (struct termios *) kmalloc(sizeof(struct termios),
 						 GFP_KERNEL);
@@ -854,7 +866,14 @@ static int init_dev(struct tty_driver *d
 		o_tty->index = idx;
 		tty_line_name(driver->other, idx, o_tty->name);
 
-		o_tp_loc  = &driver->other->termios[idx];
+		if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
+			o_tp_loc = &o_tty->termios;
+			o_ltp_loc = &o_tty->termios_locked;
+		} else {
+			o_tp_loc = &driver->other->termios[idx];
+			o_ltp_loc = &driver->other->termios_locked[idx];
+		}
+
 		if (!*o_tp_loc) {
 			o_tp = (struct termios *)
 				kmalloc(sizeof(struct termios), GFP_KERNEL);
@@ -863,7 +882,6 @@ static int init_dev(struct tty_driver *d
 			*o_tp = driver->other->init_termios;
 		}
 
-		o_ltp_loc = &driver->other->termios_locked[idx];
 		if (!*o_ltp_loc) {
 			o_ltp = (struct termios *)
 				kmalloc(sizeof(struct termios), GFP_KERNEL);
@@ -875,7 +893,9 @@ static int init_dev(struct tty_driver *d
 		/*
 		 * Everything allocated ... set up the o_tty structure.
 		 */
-		driver->other->ttys[idx] = o_tty;
+		if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {
+			driver->other->ttys[idx] = o_tty;
+		}
 		if (!*o_tp_loc)
 			*o_tp_loc = o_tp;
 		if (!*o_ltp_loc)
@@ -896,7 +916,9 @@ static int init_dev(struct tty_driver *d
 	 * Failures after this point use release_mem to clean up, so 
 	 * there's no need to null out the local pointers.
 	 */
-	driver->ttys[idx] = tty;
+	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+		driver->ttys[idx] = tty;
+	}
 	
 	if (!*tp_loc)
 		*tp_loc = tp;
@@ -994,12 +1016,20 @@ static void release_mem(struct tty_struc
 {
 	struct tty_struct *o_tty;
 	struct termios *tp;
+	int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
 
 	if ((o_tty = tty->link) != NULL) {
-		o_tty->driver->ttys[idx] = NULL;
+		if (!devpts)
+			o_tty->driver->ttys[idx] = NULL;
 		if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-			tp = o_tty->driver->termios[idx];
-			o_tty->driver->termios[idx] = NULL;
+			tp = o_tty->termios;
+			if (!devpts)
+				o_tty->driver->termios[idx] = NULL;
+			kfree(tp);
+
+			tp = o_tty->termios_locked;
+			if (!devpts)
+				o_tty->driver->termios_locked[idx] = NULL;
 			kfree(tp);
 		}
 		o_tty->magic = 0;
@@ -1010,12 +1040,20 @@ static void release_mem(struct tty_struc
 		free_tty_struct(o_tty);
 	}
 
-	tty->driver->ttys[idx] = NULL;
+	if (!devpts)
+		tty->driver->ttys[idx] = NULL;
 	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-		tp = tty->driver->termios[idx];
-		tty->driver->termios[idx] = NULL;
+		tp = tty->termios;
+		if (!devpts)
+			tty->driver->termios[idx] = NULL;
+		kfree(tp);
+
+		tp = tty->termios_locked;
+		if (!devpts)
+			tty->driver->termios_locked[idx] = NULL;
 		kfree(tp);
 	}
+
 	tty->magic = 0;
 	tty->driver->refcount--;
 	file_list_lock();
@@ -1059,22 +1097,24 @@ static void release_dev(struct file * fi
 				  "free (%s)\n", tty->name);
 		return;
 	}
-	if (tty != tty->driver->ttys[idx]) {
-		printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
-				  "for (%s)\n", idx, tty->name);
-		return;
-	}
-	if (tty->termios != tty->driver->termios[idx]) {
-		printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
-		       "for (%s)\n",
-		       idx, tty->name);
-		return;
-	}
-	if (tty->termios_locked != tty->driver->termios_locked[idx]) {
-		printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
-		       "termios_locked for (%s)\n",
-		       idx, tty->name);
-		return;
+	if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+		if (tty != tty->driver->ttys[idx]) {
+			printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
+			       "for (%s)\n", idx, tty->name);
+			return;
+		}
+		if (tty->termios != tty->driver->termios[idx]) {
+			printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
+			       "for (%s)\n",
+			       idx, tty->name);
+			return;
+		}
+		if (tty->termios_locked != tty->driver->termios_locked[idx]) {
+			printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
+			       "termios_locked for (%s)\n",
+			       idx, tty->name);
+			return;
+		}
 	}
 #endif
 
@@ -1084,7 +1124,8 @@ static void release_dev(struct file * fi
 #endif
 
 #ifdef TTY_PARANOIA_CHECK
-	if (tty->driver->other) {
+	if (tty->driver->other &&
+	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
 		if (o_tty != tty->driver->other->ttys[idx]) {
 			printk(KERN_DEBUG "release_dev: other->table[%d] "
 					  "not o_tty for (%s)\n",
@@ -1328,23 +1369,29 @@ retry_open:
 		return -ENODEV;
 	}
 
-	if (device == MKDEV(TTYAUX_MAJOR,2)) {
 #ifdef CONFIG_UNIX98_PTYS
+	if (device == MKDEV(TTYAUX_MAJOR,2)) {
 		/* find a device that is not in use. */
+		static int next_ptmx_dev = 0;
 		retval = -1;
 		driver = ptm_driver;
-		for (index = 0; index < driver->num ; index++)
+		while (driver->refcount < pty_limit) {
+			index = next_ptmx_dev;
+			next_ptmx_dev = (next_ptmx_dev+1) % driver->num;
 			if (!init_dev(driver, index, &tty))
 				goto ptmx_found; /* ok! */
+		}
 		return -EIO; /* no free ptys */
 	ptmx_found:
 		set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
-		devpts_pty_new(index, MKDEV(pts_driver->major, pts_driver->minor_start) + index);
+		if (devpts_pty_new(tty->link)) {
+			/* BADNESS - need to destroy both ptm and pts! */
+			return -ENOMEM;
+		}
 		noctty = 1;
-#else
-		return -ENODEV;
-#endif  /* CONFIG_UNIX_98_PTYS */
-	} else {
+	} else
+#endif
+	{
 		driver = get_tty_driver(device, &index);
 		if (!driver)
 			return -ENODEV;
@@ -2190,15 +2237,17 @@ int tty_register_driver(struct tty_drive
         int i;
 	dev_t dev;
 	char *s;
-	void **p;
+	void **p = NULL;
 
 	if (driver->flags & TTY_DRIVER_INSTALLED)
 		return 0;
 
-	p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
-	if (!p)
-		return -ENOMEM;
-	memset(p, 0, driver->num * 3 * sizeof(void *));
+	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+		p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
+		if (!p)
+			return -ENOMEM;
+		memset(p, 0, driver->num * 3 * sizeof(void *));
+	}
 
 	if (!driver->major) {
 		error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
@@ -2217,9 +2266,15 @@ int tty_register_driver(struct tty_drive
 		return error;
 	}
 
-	driver->ttys = (struct tty_struct **)p;
-	driver->termios = (struct termios **)(p + driver->num);
-	driver->termios_locked = (struct termios **)(p + driver->num * 2);
+	if (p) {
+		driver->ttys = (struct tty_struct **)p;
+		driver->termios = (struct termios **)(p + driver->num);
+		driver->termios_locked = (struct termios **)(p + driver->num * 2);
+	} else {
+		driver->ttys = NULL;
+		driver->termios = NULL;
+		driver->termios_locked = NULL;
+	}
 
 	driver->cdev.kobj.parent = &tty_kobj;
 	strcpy(driver->cdev.kobj.name, driver->name);
@@ -2388,7 +2443,7 @@ static int __init tty_init(void)
 	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");
 	class_simple_device_add(tty_class, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
 #endif
-	
+
 #ifdef CONFIG_VT
 	strcpy(vc0_cdev.kobj.name, "dev.vc0");
 	cdev_init(&vc0_cdev, &console_fops);
diff -puN fs/devpts/inode.c~dynamic-pty-allocation fs/devpts/inode.c
--- 25/fs/devpts/inode.c~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/fs/devpts/inode.c	Fri Feb 13 18:04:19 2004
@@ -2,7 +2,7 @@
  *
  * linux/fs/devpts/inode.c
  *
- *  Copyright 1998 H. Peter Anvin -- All Rights Reserved
+ *  Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
  *
  * This file is part of the Linux kernel and is made available under
  * the terms of the GNU General Public License, version 2, or at your
@@ -16,6 +16,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/tty.h>
+#include <linux/devpts_fs.h>
 #include "xattr.h"
 
 #define DEVPTS_SUPER_MAGIC 0x1cd1
@@ -126,7 +128,7 @@ static struct file_system_type devpts_fs
 
 static struct dentry *get_node(int num)
 {
-	char s[10];
+	char s[12];
 	struct dentry *root = devpts_root;
 	down(&root->d_inode->i_sem);
 	return lookup_one_len(s, root, sprintf(s, "%d", num));
@@ -139,12 +141,21 @@ static struct inode_operations devpts_fi
 	.removexattr	= devpts_removexattr,
 };
 
-void devpts_pty_new(int number, dev_t device)
+int devpts_pty_new(struct tty_struct *tty)
 {
+	int number = tty->index;
+	struct tty_driver *driver = tty->driver;
+	dev_t device = MKDEV(driver->major, driver->minor_start+number);
 	struct dentry *dentry;
 	struct inode *inode = new_inode(devpts_mnt->mnt_sb);
+
+	/* We're supposed to be given the slave end of a pty */
+	BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY);
+	BUG_ON(driver->subtype != PTY_TYPE_SLAVE);
+
 	if (!inode)
-		return;
+		return -ENOMEM;
+
 	inode->i_ino = number+2;
 	inode->i_blksize = 1024;
 	inode->i_uid = config.setuid ? config.uid : current->fsuid;
@@ -152,11 +163,28 @@ void devpts_pty_new(int number, dev_t de
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 	init_special_inode(inode, S_IFCHR|config.mode, device);
 	inode->i_op = &devpts_file_inode_operations;
+	inode->u.generic_ip = tty;
 
 	dentry = get_node(number);
 	if (!IS_ERR(dentry) && !dentry->d_inode)
 		d_instantiate(dentry, inode);
+
 	up(&devpts_root->d_inode->i_sem);
+
+	return 0;
+}
+
+struct tty_struct *devpts_get_tty(int number)
+{
+	struct dentry *dentry = get_node(number);
+	struct tty_struct *tty;
+
+	tty = (IS_ERR(dentry) || !dentry->d_inode) ? NULL :
+			dentry->d_inode->u.generic_ip;
+
+	up(&devpts_root->d_inode->i_sem);
+
+	return tty;
 }
 
 void devpts_pty_kill(int number)
diff -puN fs/devpts/Makefile~dynamic-pty-allocation fs/devpts/Makefile
--- 25/fs/devpts/Makefile~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/fs/devpts/Makefile	Fri Feb 13 18:04:19 2004
@@ -2,8 +2,8 @@
 # Makefile for the Linux /dev/pts virtual filesystem.
 #
 
-obj-$(CONFIG_DEVPTS_FS) += devpts.o
+obj-$(CONFIG_UNIX98_PTYS)		+= devpts.o
 
-devpts-y := inode.o
+devpts-$(CONFIG_UNIX98_PTYS)		:= inode.o
 devpts-$(CONFIG_DEVPTS_FS_XATTR)	+= xattr.o 
 devpts-$(CONFIG_DEVPTS_FS_SECURITY)	+= xattr_security.o
diff -puN fs/Kconfig~dynamic-pty-allocation fs/Kconfig
--- 25/fs/Kconfig~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/fs/Kconfig	Fri Feb 13 18:04:19 2004
@@ -821,8 +821,7 @@ config DEVFS_FS
 	  the file README there.
 
 	  Note that devfs no longer manages /dev/pts!  If you are using UNIX98
-	  ptys, you will also need to enable (and mount) the /dev/pts
-	  filesystem (CONFIG_DEVPTS_FS).
+	  ptys, you will also need to mount the /dev/pts filesystem (devpts).
 
 	  Note that devfs has been obsoleted by udev,
 	  <http://www.kernel.org/pub/linux/utils/kernel/hotplug/>.
@@ -855,32 +854,9 @@ config DEVFS_DEBUG
 
 	  If unsure, say N.
 
-config DEVPTS_FS
-# It compiles as a module for testing only.  It should not be used
-# as a module in general.  If we make this "tristate", a bunch of people
-# who don't know what they are doing turn it on and complain when it
-# breaks.
-	bool "/dev/pts file system for Unix98 PTYs"
-	depends on UNIX98_PTYS
-	---help---
-	  You should say Y here if you said Y to "Unix98 PTY support" above.
-	  You'll then get a virtual file system which can be mounted on
-	  /dev/pts with "mount -t devpts". This, together with the pseudo
-	  terminal master multiplexer /dev/ptmx, is used for pseudo terminal
-	  support as described in The Open Group's Unix98 standard: in order
-	  to acquire a pseudo terminal, a process opens /dev/ptmx; the number
-	  of the pseudo terminal is then made available to the process and the
-	  pseudo terminal slave can be accessed as /dev/pts/<number>. What was
-	  traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
-
-	  The GNU C library glibc 2.1 contains the requisite support for this
-	  mode of operation; you also need client programs that use the Unix98
-	  API. Please read <file:Documentation/Changes> for more information
-	  about the Unix98 pty devices.
-
 config DEVPTS_FS_XATTR
 	bool "/dev/pts Extended Attributes"
-	depends on DEVPTS_FS
+	depends on UNIX98_PTYS
 	help
 	  Extended attributes are name:value pairs associated with inodes by
 	  the kernel or by users (see the attr(5) manual page, or visit
diff -puN include/linux/devpts_fs.h~dynamic-pty-allocation include/linux/devpts_fs.h
--- 25/include/linux/devpts_fs.h~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/include/linux/devpts_fs.h	Fri Feb 13 18:04:19 2004
@@ -2,7 +2,7 @@
  *
  * linux/include/linux/devpts_fs.h
  *
- *  Copyright 1998 H. Peter Anvin -- All Rights Reserved
+ *  Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
  *
  * This file is part of the Linux kernel and is made available under
  * the terms of the GNU General Public License, version 2, or at your
@@ -13,21 +13,22 @@
 #ifndef _LINUX_DEVPTS_FS_H
 #define _LINUX_DEVPTS_FS_H 1
 
-#ifdef CONFIG_DEVPTS_FS
+#include <linux/errno.h>
 
-void devpts_pty_new(int, dev_t);	/* mknod in devpts */
-void devpts_pty_kill(int);		/* unlink */
+#if CONFIG_UNIX98_PTYS
+
+int devpts_pty_new(struct tty_struct *); /* mknod in devpts */
+struct tty_struct *devpts_get_tty(int);	 /* get tty structure */
+void devpts_pty_kill(int);		 /* unlink */
 
 #else
 
-static inline void devpts_pty_new(int line, dev_t device)
-{
-}
-
-static inline void devpts_pty_kill(int line)
-{
-}
+/* Dummy stubs in the no-pty case */
+static inline int devpts_pty_new(struct tty_struct *) { return -EINVAL; }
+static inline struct tty_struct *devpts_get_tty(int)  { return NULL; }
+static inline void devpts_pty_kill(int) { }
 
 #endif
 
+
 #endif /* _LINUX_DEVPTS_FS_H */
diff -puN include/linux/sysctl.h~dynamic-pty-allocation include/linux/sysctl.h
--- 25/include/linux/sysctl.h~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/include/linux/sysctl.h	Fri Feb 13 18:04:19 2004
@@ -129,6 +129,7 @@ enum
 	KERN_HPPA_UNALIGNED=59,	/* int: hppa unaligned-trap enable */
 	KERN_PRINTK_RATELIMIT=60, /* int: tune printk ratelimiting */
 	KERN_PRINTK_RATELIMIT_BURST=61,	/* int: tune printk ratelimiting */
+	KERN_PTY=62,		/* dir: pty driver */
 };
 
 
@@ -194,6 +195,13 @@ enum
 	RANDOM_UUID=6
 };
 
+/* /proc/sys/kernel/pty */
+enum
+{
+	PTY_MAX=1,
+	PTY_NR=2
+};
+
 /* /proc/sys/bus/isa */
 enum
 {
diff -puN include/linux/tty_driver.h~dynamic-pty-allocation include/linux/tty_driver.h
--- 25/include/linux/tty_driver.h~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/include/linux/tty_driver.h	Fri Feb 13 18:04:19 2004
@@ -160,9 +160,10 @@ struct tty_driver {
 	const char	*devfs_name;
 	const char	*name;
 	int	name_base;	/* offset of printed name */
-	short	major;		/* major device number */
-	short	minor_start;	/* start of minor device number*/
-	short	num;		/* number of devices */
+	int	major;		/* major device number */
+	int	minor_start;	/* start of minor device number */
+	int	minor_num;	/* number of *possible* devices */
+	int	num;		/* number of devices allocated */
 	short	type;		/* type of tty driver */
 	short	subtype;	/* subtype of tty driver */
 	struct termios init_termios; /* Initial termios */
@@ -244,11 +245,15 @@ void tty_set_operations(struct tty_drive
  * TTY_DRIVER_NO_DEVFS --- if set, do not create devfs entries. This
  *	is only used by tty_register_driver().
  *
+ * TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead
+ *	use dynamic memory keyed through the devpts filesystem.  This
+ *	is only applicable to the pty driver.
  */
 #define TTY_DRIVER_INSTALLED		0x0001
 #define TTY_DRIVER_RESET_TERMIOS	0x0002
 #define TTY_DRIVER_REAL_RAW		0x0004
 #define TTY_DRIVER_NO_DEVFS		0x0008
+#define TTY_DRIVER_DEVPTS_MEM		0x0010
 
 /* tty driver types */
 #define TTY_DRIVER_TYPE_SYSTEM		0x0001
diff -puN include/linux/tty.h~dynamic-pty-allocation include/linux/tty.h
--- 25/include/linux/tty.h~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/include/linux/tty.h	Fri Feb 13 18:04:19 2004
@@ -28,29 +28,13 @@
 
 
 /*
- * Note: don't mess with NR_PTYS until you understand the tty minor 
- * number allocation game...
  * (Note: the *_driver.minor_start values 1, 64, 128, 192 are
  * hardcoded at present.)
  */
-#define NR_PTYS		256	/* ptys/major */
-#define NR_LDISCS	16
-
-/*
- * Unix98 PTY's can be defined as any multiple of NR_PTYS up to
- * UNIX98_PTY_MAJOR_COUNT; this section defines what we need from the
- * config options
- */
-#ifdef CONFIG_UNIX98_PTYS
-# define UNIX98_NR_MAJORS ((CONFIG_UNIX98_PTY_COUNT+NR_PTYS-1)/NR_PTYS)
-# if UNIX98_NR_MAJORS <= 0
-#  undef CONFIG_UNIX98_PTYS
-# elif UNIX98_NR_MAJORS > UNIX98_PTY_MAJOR_COUNT
-#  error  Too many Unix98 ptys defined
-#  undef  UNIX98_NR_MAJORS
-#  define UNIX98_NR_MAJORS UNIX98_PTY_MAJOR_COUNT
-# endif
-#endif
+#define NR_PTYS	CONFIG_LEGACY_PTY_COUNT   /* Number of legacy ptys */
+#define NR_UNIX98_PTY_DEFAULT	4096      /* Default maximum for Unix98 ptys */
+#define NR_UNIX98_PTY_MAX	(1 << MINORBITS) /* Absolute limit */
+#define NR_LDISCS		16
 
 /*
  * These are set up by the setup-routine at boot-time:
diff -puN kernel/sysctl.c~dynamic-pty-allocation kernel/sysctl.c
--- 25/kernel/sysctl.c~dynamic-pty-allocation	Fri Feb 13 18:04:19 2004
+++ 25-akpm/kernel/sysctl.c	Fri Feb 13 18:04:19 2004
@@ -133,6 +133,9 @@ static ctl_table fs_table[];
 static ctl_table debug_table[];
 static ctl_table dev_table[];
 extern ctl_table random_table[];
+#ifdef CONFIG_UNIX98_PTYS
+extern ctl_table pty_table[];
+#endif
 
 /* /proc declarations: */
 
@@ -518,6 +521,14 @@ static ctl_table kern_table[] = {
 		.mode		= 0555,
 		.child		= random_table,
 	},
+#ifdef CONFIG_UNIX98_PTYS
+	{
+		.ctl_name	= KERN_PTY,
+		.procname	= "pty",
+		.mode		= 0555,
+		.child		= pty_table,
+	},
+#endif
 	{
 		.ctl_name	= KERN_OVERFLOWUID,
 		.procname	= "overflowuid",

_