Dynamically tunable maxusers, maxuprc and max_pt_cnt
Subsystems (5)
RFC PATCH 1/1 Dynamically tunable maxusers, maxuprc and max_pt_cnt
Hi,
Below is the patch(RFC) for dynamically tuning the:
1. maximum number of user using the system at any point of time (maxusers)
2. maximum number of processes a user is running at any point of time (maxuprc)
3. maximum number of pseudo terminals that can be open at any point of time (max_pt_cnt).
Please give your comments/suggestion.
cheers,
Arvind
P.S. Please get back if the patch is not readable. I'm using evolution to send this mail and made sure that it is configured to work properly.
drivers/char/pty.c +24 -1
@@ -50,6 +50,10 @@static struct termios *ttyp_termios_locked[NR_PTYS ];static struct pty_struct pty_state[NR_PTYS ]; +static spinlock_t pt_count_lock=SPIN_LOCK_UNLOCKED ; +static int pt_count=0 ; + +#ifdef CONFIG_UNIX98_PTYS /* These are global because they are accessed in tty_io.c */ struct tty_driver ptm_driver;
@@ -62,6 +66,10 @@static struct termios *pts_termios[UNIX98_NR_MAJORS *NR_PTYS ];static struct termios *pts_termios_locked[UNIX98_NR_MAJORS *NR_PTYS ];static struct pty_struct ptm_state[UNIX98_NR_MAJORS *NR_PTYS ]; +int max_pt_cnt =2304 ; +#else +int max_pt_cnt =256 ; +#endif static void pty_close (struct tty_struct * tty,struct file * filp)
@@ -71,6 +79,12 @@if (tty->driver->subtype ==PTY_TYPE_MASTER ) {if (tty->count >1 )printk ("master pty_close: count = %d!!\n" , tty->count); +if (test_bit (TTY_THROTTLED ,&tty->flags)) { +spin_lock (&pt_count_lock); + pt_count--; +spin_unlock (&pt_count_lock); + } + }else {if (tty->count >2 )return ;
@@ -318,7 +332,16 @@ goto out;if (tty->link->count !=1 ) goto out; - +spin_lock (&pt_count_lock); +if (!test_bit (TTY_THROTTLED ,&tty->flags) && + tty->driver->subtype ==PTY_TYPE_MASTER ) { +if (pt_count >=max_pt_cnt) { +spin_unlock (&pt_count_lock); + goto out; + } + pt_count++; +spin_unlock (&pt_count_lock); + }clear_bit (TTY_OTHER_CLOSED , &tty->link->flags);wake_up_interruptible (&pty->open_wait);set_bit (TTY_THROTTLED , &tty->flags);
include/linux/sched.h +1 -1
@@ -528,7 +528,7 @@/* per-UID process charging. */ extern struct user_struct *alloc_uid (uid_t );extern void free_uid (struct user_struct *); -extern void switch_uid (struct user_struct *); +extern int switch_uid (struct user_struct *);#include <asm/current.h>
include/linux/sysctl.h +4 -0
@@ -130,6 +130,10 @@KERN_PIDMAX =55 ,/* int: PID # limit */ KERN_CORE_PATTERN =56 ,/* string: pattern for core-file names */ KERN_PANIC_ON_OOPS =57 ,/* int: whether we will panic on an oops */ +KERN_MAXUSERS =58 ,/* int: limit on maximum number of users using the system currently*/ +KERN_MAXUPRC =59 ,/* int: limit on maximum number of user processes per user */ + KERN_MAX_PT_CNT=60, /* int: limit on maximum number of pseudo terminal currently open*/ + };
kernel/exit.c +7 -2
@@ -30,6 +30,8 @@extern void sem_exit (void );extern struct task_struct *child_reaper; +extern spinlock_t maxuprc_lock; +int getrusage (struct task_struct *,int ,struct rusage *);static void __unhash_process (struct task_struct *p)
@@ -53,8 +55,11 @@struct dentry *proc_dentry;BUG_ON (p->state <TASK_ZOMBIE ); - -atomic_dec (&p->user->processes); + +spin_lock (&maxuprc_lock); +atomic_dec (&p->user->processes); +spin_unlock (&maxuprc_lock); +spin_lock (&p->proc_lock); proc_dentry =proc_pid_unhash (p);write_lock_irq (&tasklist_lock);
kernel/fork.c +6 -1
@@ -48,6 +48,9 @@*/ int nr_threads; +spinlock_t maxuprc_lock=SPIN_LOCK_UNLOCKED ; +int maxuprc=0x7fffffff ; +int max_threads;unsigned long total_forks;/* Handle normal Linux uptimes. */
@@ -784,7 +787,8 @@ goto fork_out; retval = -EAGAIN ; -if (atomic_read (&p->user->processes) >= p->rlim[RLIMIT_NPROC ].rlim_cur) { +spin_lock (&maxuprc_lock); +if (atomic_read (&p->user->processes) >= maxuprc ||atomic_read (&p->user->processes) >= p->rlim[RLIMIT_NPROC ].rlim_cur) {if (!capable (CAP_SYS_ADMIN ) && !capable (CAP_SYS_RESOURCE )) goto bad_fork_free; }
@@ -1010,6 +1014,7 @@ retval =0 ; fork_out: +spin_unlock (maxuprc_lock);if (retval)return ERR_PTR (retval);return p;
kernel/sys.c +1 -1
@@ -595,7 +595,7 @@ new_user =alloc_uid (new_ruid);if (!new_user)return -EAGAIN ; -switch_uid (new_user); +if (switch_uid (new_user)<0 )return -EAGAIN ;if (dumpclear) {
kernel/sysctl.c +22 -6
@@ -58,6 +58,10 @@extern int pid_max;extern int sysctl_lower_zone_protection; +extern int maxusers; +extern int maxuprc; +extern int max_pt_cnt; +/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ static int maxolduid =65535 ;static int minolduid;
@@ -146,6 +150,17 @@static void unregister_proc_table (ctl_table *,struct proc_dir_entry *);#endif +/* Constants for minimum and maximum testing +* We use these as one-element integer vectors. */ +static int zero =0 ; +static int one =1 ; +static int one_hundred =100 ; +static int maxint=0x7fffffff ; +static int lim_pt_cnt=2304 ; + + + +/* The default sysctl tables: */ static ctl_table root_table[] = {
@@ -265,15 +280,15 @@ 0600, NULL, &proc_dointvec}, {KERN_PANIC_ON_OOPS,"panic_on_oops", &panic_on_oops,sizeof(int),0644,NULL,&proc_dointvec}, + {KERN_MAXUSERS,"maxusers", + &maxusers,sizeof(int),0644,NULL,&proc_dointvec_minmax,NULL,NULL,&zero,NULL}, + {KERN_MAXUPRC,"maxuprc", + &maxuprc,sizeof(int),0644,NULL,&proc_dointvec_minmax,NULL,NULL,&zero,NULL}, + {KERN_MAX_PT_CNT,"max_pt_cnt", + &max_pt_cnt,sizeof(int),0644,NULL,&proc_dointvec_minmax,NULL,NULL,&zero,&lim_pt_cnt}, {0} }; -/* Constants for minimum and maximum testing in vm_table. - We use these as one-element integer vectors. */ -static int zero = 0; -static int one = 1; -static int one_hundred = 100; - static ctl_table vm_table[] = { {VM_OVERCOMMIT_MEMORY, "overcommit_memory", &sysctl_overcommit_memory,
@@ -368,6 +383,7 @@void __init sysctl_init (void ) { + lim_pt_cnt = max_pt_cnt;#ifdef CONFIG_PROC_FS register_proc_table (root_table, proc_sys_root);init_irq_proc ();
kernel/user.c +20 -1
@@ -27,6 +27,11 @@static struct list_head uidhash_table[UIDHASH_SZ ];static spinlock_t uidhash_lock =SPIN_LOCK_UNLOCKED ; +static unsigned int users_count=0 ; +int maxusers =0x7fffffff ;/*MAXINT*/ +extern spinlock_t user_processes_lock; +extern int maxuprc; +struct user_struct root_user = { .__count =ATOMIC_INIT (1 ), .processes =ATOMIC_INIT (1 ),
@@ -73,6 +78,7 @@ {if (up &&atomic_dec_and_lock (&up->__count, &uidhash_lock)) {uid_hash_remove (up); + users_count--;kmem_cache_free (uid_cachep, up);spin_unlock (&uidhash_lock); }
@@ -107,7 +113,13 @@if (up) {kmem_cache_free (uid_cachep, new); }else { +if (users_count>=maxusers){ +spin_unlock (&uidhash_lock); +return NULL ; + } +uid_hash_insert (new, hashent); + users_count++; up = new; }spin_unlock (&uidhash_lock);
@@ -116,7 +128,7 @@return up; } -void switch_uid (struct user_struct *new_user) +int switch_uid (struct user_struct *new_user) {struct user_struct *old_user;
@@ -126,11 +138,18 @@* we should be checking for it. -DaveM */ old_user = current->user; +spin_lock (&user_processes_lock); +if (new_user !=INIT_USER && (atomic_read (&new_user->processes) >= maxuprc ||atomic_read (&new_user->processes) >= current->rlim[RLIMIT_NPROC ].rlim_cur)) { +spin_unlock (&user_processes_lock); +return -1 ; + }atomic_inc (&new_user->__count);atomic_inc (&new_user->processes);atomic_dec (&old_user->processes); +spin_unlock (&user_processes_lock); current->user = new_user;free_uid (old_user); +return 1 ; }