aboutsummaryrefslogtreecommitdiffstats
path: root/fs/devpts
diff options
context:
space:
mode:
authorKonstantin Khlebnikov <khlebnikov@openvz.org>2012-01-05 04:06:11 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2012-01-24 17:01:01 -0500
commite9aba5158a80098447ff207a452a3418ae7ee386 (patch)
treefb6dd19824f908746d597128d34093a301c005e7 /fs/devpts
parenta4834c102f4a46808630cad1a545cb0706b3b0a2 (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.c34
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 */
43static int pty_limit = NR_UNIX98_PTY_DEFAULT; 43static int pty_limit = NR_UNIX98_PTY_DEFAULT;
44static int pty_reserve = NR_UNIX98_PTY_RESERVE;
44static int pty_limit_min; 45static int pty_limit_min;
45static int pty_limit_max = NR_UNIX98_PTY_MAX; 46static int pty_limit_max = INT_MAX;
46static int pty_count; 47static int pty_count;
47 48
48static struct ctl_table pty_table[] = { 49static 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
99enum { 109enum {
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);