diff options
Diffstat (limited to 'fs/devpts/inode.c')
-rw-r--r-- | fs/devpts/inode.c | 34 |
1 files changed, 30 insertions, 4 deletions
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index c2c7317d5687..1c6f908e38ca 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c | |||
@@ -41,8 +41,9 @@ | |||
41 | * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. | 41 | * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. |
42 | */ | 42 | */ |
43 | static int pty_limit = NR_UNIX98_PTY_DEFAULT; | 43 | static int pty_limit = NR_UNIX98_PTY_DEFAULT; |
44 | static int pty_reserve = NR_UNIX98_PTY_RESERVE; | ||
44 | static int pty_limit_min; | 45 | static int pty_limit_min; |
45 | static int pty_limit_max = NR_UNIX98_PTY_MAX; | 46 | static int pty_limit_max = INT_MAX; |
46 | static int pty_count; | 47 | static int pty_count; |
47 | 48 | ||
48 | static struct ctl_table pty_table[] = { | 49 | static struct ctl_table pty_table[] = { |
@@ -55,6 +56,14 @@ static struct ctl_table pty_table[] = { | |||
55 | .extra1 = &pty_limit_min, | 56 | .extra1 = &pty_limit_min, |
56 | .extra2 = &pty_limit_max, | 57 | .extra2 = &pty_limit_max, |
57 | }, { | 58 | }, { |
59 | .procname = "reserve", | ||
60 | .maxlen = sizeof(int), | ||
61 | .mode = 0644, | ||
62 | .data = &pty_reserve, | ||
63 | .proc_handler = proc_dointvec_minmax, | ||
64 | .extra1 = &pty_limit_min, | ||
65 | .extra2 = &pty_limit_max, | ||
66 | }, { | ||
58 | .procname = "nr", | 67 | .procname = "nr", |
59 | .maxlen = sizeof(int), | 68 | .maxlen = sizeof(int), |
60 | .mode = 0444, | 69 | .mode = 0444, |
@@ -94,10 +103,11 @@ struct pts_mount_opts { | |||
94 | umode_t mode; | 103 | umode_t mode; |
95 | umode_t ptmxmode; | 104 | umode_t ptmxmode; |
96 | int newinstance; | 105 | int newinstance; |
106 | int max; | ||
97 | }; | 107 | }; |
98 | 108 | ||
99 | enum { | 109 | enum { |
100 | Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, | 110 | Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, Opt_max, |
101 | Opt_err | 111 | Opt_err |
102 | }; | 112 | }; |
103 | 113 | ||
@@ -108,6 +118,7 @@ static const match_table_t tokens = { | |||
108 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | 118 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES |
109 | {Opt_ptmxmode, "ptmxmode=%o"}, | 119 | {Opt_ptmxmode, "ptmxmode=%o"}, |
110 | {Opt_newinstance, "newinstance"}, | 120 | {Opt_newinstance, "newinstance"}, |
121 | {Opt_max, "max=%d"}, | ||
111 | #endif | 122 | #endif |
112 | {Opt_err, NULL} | 123 | {Opt_err, NULL} |
113 | }; | 124 | }; |
@@ -154,6 +165,7 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) | |||
154 | opts->gid = 0; | 165 | opts->gid = 0; |
155 | opts->mode = DEVPTS_DEFAULT_MODE; | 166 | opts->mode = DEVPTS_DEFAULT_MODE; |
156 | opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; | 167 | opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; |
168 | opts->max = NR_UNIX98_PTY_MAX; | ||
157 | 169 | ||
158 | /* newinstance makes sense only on initial mount */ | 170 | /* newinstance makes sense only on initial mount */ |
159 | if (op == PARSE_MOUNT) | 171 | if (op == PARSE_MOUNT) |
@@ -197,6 +209,12 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) | |||
197 | if (op == PARSE_MOUNT) | 209 | if (op == PARSE_MOUNT) |
198 | opts->newinstance = 1; | 210 | opts->newinstance = 1; |
199 | break; | 211 | break; |
212 | case Opt_max: | ||
213 | if (match_int(&args[0], &option) || | ||
214 | option < 0 || option > NR_UNIX98_PTY_MAX) | ||
215 | return -EINVAL; | ||
216 | opts->max = option; | ||
217 | break; | ||
200 | #endif | 218 | #endif |
201 | default: | 219 | default: |
202 | printk(KERN_ERR "devpts: called with bogus options\n"); | 220 | 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) | |||
303 | seq_printf(seq, ",mode=%03o", opts->mode); | 321 | seq_printf(seq, ",mode=%03o", opts->mode); |
304 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | 322 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES |
305 | seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); | 323 | seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); |
324 | if (opts->max < NR_UNIX98_PTY_MAX) | ||
325 | seq_printf(seq, ",max=%d", opts->max); | ||
306 | #endif | 326 | #endif |
307 | 327 | ||
308 | return 0; | 328 | return 0; |
@@ -483,6 +503,12 @@ retry: | |||
483 | return -ENOMEM; | 503 | return -ENOMEM; |
484 | 504 | ||
485 | mutex_lock(&allocated_ptys_lock); | 505 | mutex_lock(&allocated_ptys_lock); |
506 | if (pty_count >= pty_limit - | ||
507 | (fsi->mount_opts.newinstance ? pty_reserve : 0)) { | ||
508 | mutex_unlock(&allocated_ptys_lock); | ||
509 | return -ENOSPC; | ||
510 | } | ||
511 | |||
486 | ida_ret = ida_get_new(&fsi->allocated_ptys, &index); | 512 | ida_ret = ida_get_new(&fsi->allocated_ptys, &index); |
487 | if (ida_ret < 0) { | 513 | if (ida_ret < 0) { |
488 | mutex_unlock(&allocated_ptys_lock); | 514 | mutex_unlock(&allocated_ptys_lock); |
@@ -491,10 +517,10 @@ retry: | |||
491 | return -EIO; | 517 | return -EIO; |
492 | } | 518 | } |
493 | 519 | ||
494 | if (index >= pty_limit) { | 520 | if (index >= fsi->mount_opts.max) { |
495 | ida_remove(&fsi->allocated_ptys, index); | 521 | ida_remove(&fsi->allocated_ptys, index); |
496 | mutex_unlock(&allocated_ptys_lock); | 522 | mutex_unlock(&allocated_ptys_lock); |
497 | return -EIO; | 523 | return -ENOSPC; |
498 | } | 524 | } |
499 | pty_count++; | 525 | pty_count++; |
500 | mutex_unlock(&allocated_ptys_lock); | 526 | mutex_unlock(&allocated_ptys_lock); |