diff options
author | Konstantin Khlebnikov <khlebnikov@openvz.org> | 2012-01-05 04:06:11 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-24 17:01:01 -0500 |
commit | e9aba5158a80098447ff207a452a3418ae7ee386 (patch) | |
tree | fb6dd19824f908746d597128d34093a301c005e7 /fs/devpts | |
parent | a4834c102f4a46808630cad1a545cb0706b3b0a2 (diff) |
tty: rework pty count limiting
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 <khlebnikov@openvz.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/devpts')
-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); |