|
@@ -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[] = {
|
|
@@ -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),
|
|
@@ -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
|
|
|
};
|
|
|
|
|
@@ -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}
|
|
|
};
|
|
@@ -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)
|
|
@@ -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");
|
|
@@ -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;
|
|
@@ -483,6 +503,12 @@ retry:
|
|
|
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);
|
|
@@ -491,10 +517,10 @@ retry:
|
|
|
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);
|