tty: rework pty count limiting
[deliverable/linux.git] / fs / devpts / inode.c
index c2c7317d56872efcdc71fe9588b2598c07d2fc01..1c6f908e38caa8283d8b7dddaa9596de7329cbae 100644 (file)
@@ -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);
This page took 0.025462 seconds and 5 git commands to generate.