Skip to content

Commit

Permalink
tty: rework pty count limiting
Browse files Browse the repository at this point in the history
After adding devpts multiple-insrances sysctl kernel.pty.max limit pty count for
each devpts instance independently, while kernel.pty.nr shows total pty count.

This patch restores sysctl kernel.pty.max as global limit (4096 by default),
adds pty reseve for main devpts (mounted without "newinstance" argument),
and new sysctl to tune it: kernel.pty.reserve (1024 by default)

Also it adds devpts mount option "max=%d" to limit pty count for each devpts
instance independently. (by default NR_UNIX98_PTY_MAX == 2^20)

Thus devpts instances in containers cannot eat up all available pty even if we didn't
set any limits, while with "max" argument we can adjust limits more precisely.

Plus, now open("/dev/ptmx") return -ENOSPC in case lack of pty indexes,
this is more informative than -EIO.

Signed-off-by: Konstantin Khlebnikov <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
koct9i authored and gregkh committed Jan 24, 2012
1 parent a4834c1 commit e9aba51
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 4 deletions.
34 changes: 30 additions & 4 deletions fs/devpts/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@
* Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly.
*/
static int pty_limit = NR_UNIX98_PTY_DEFAULT;
static int pty_reserve = NR_UNIX98_PTY_RESERVE;
static int pty_limit_min;
static int pty_limit_max = NR_UNIX98_PTY_MAX;
static int pty_limit_max = INT_MAX;
static int pty_count;

static struct ctl_table pty_table[] = {
Expand All @@ -54,6 +55,14 @@ static struct ctl_table pty_table[] = {
.proc_handler = proc_dointvec_minmax,
.extra1 = &pty_limit_min,
.extra2 = &pty_limit_max,
}, {
.procname = "reserve",
.maxlen = sizeof(int),
.mode = 0644,
.data = &pty_reserve,
.proc_handler = proc_dointvec_minmax,
.extra1 = &pty_limit_min,
.extra2 = &pty_limit_max,
}, {
.procname = "nr",
.maxlen = sizeof(int),
Expand Down Expand Up @@ -94,10 +103,11 @@ struct pts_mount_opts {
umode_t mode;
umode_t ptmxmode;
int newinstance;
int max;
};

enum {
Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance,
Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, Opt_max,
Opt_err
};

Expand All @@ -108,6 +118,7 @@ static const match_table_t tokens = {
#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
{Opt_ptmxmode, "ptmxmode=%o"},
{Opt_newinstance, "newinstance"},
{Opt_max, "max=%d"},
#endif
{Opt_err, NULL}
};
Expand Down Expand Up @@ -154,6 +165,7 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
opts->gid = 0;
opts->mode = DEVPTS_DEFAULT_MODE;
opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
opts->max = NR_UNIX98_PTY_MAX;

/* newinstance makes sense only on initial mount */
if (op == PARSE_MOUNT)
Expand Down Expand Up @@ -197,6 +209,12 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
if (op == PARSE_MOUNT)
opts->newinstance = 1;
break;
case Opt_max:
if (match_int(&args[0], &option) ||
option < 0 || option > NR_UNIX98_PTY_MAX)
return -EINVAL;
opts->max = option;
break;
#endif
default:
printk(KERN_ERR "devpts: called with bogus options\n");
Expand Down Expand Up @@ -303,6 +321,8 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",mode=%03o", opts->mode);
#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode);
if (opts->max < NR_UNIX98_PTY_MAX)
seq_printf(seq, ",max=%d", opts->max);
#endif

return 0;
Expand Down Expand Up @@ -483,6 +503,12 @@ int devpts_new_index(struct inode *ptmx_inode)
return -ENOMEM;

mutex_lock(&allocated_ptys_lock);
if (pty_count >= pty_limit -
(fsi->mount_opts.newinstance ? pty_reserve : 0)) {
mutex_unlock(&allocated_ptys_lock);
return -ENOSPC;
}

ida_ret = ida_get_new(&fsi->allocated_ptys, &index);
if (ida_ret < 0) {
mutex_unlock(&allocated_ptys_lock);
Expand All @@ -491,10 +517,10 @@ int devpts_new_index(struct inode *ptmx_inode)
return -EIO;
}

if (index >= pty_limit) {
if (index >= fsi->mount_opts.max) {
ida_remove(&fsi->allocated_ptys, index);
mutex_unlock(&allocated_ptys_lock);
return -EIO;
return -ENOSPC;
}
pty_count++;
mutex_unlock(&allocated_ptys_lock);
Expand Down
1 change: 1 addition & 0 deletions include/linux/tty.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
* hardcoded at present.)
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_RESERVE 1024 /* Default reserve for main devpts */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */

/*
Expand Down

0 comments on commit e9aba51

Please sign in to comment.