>From 15633a356bbb32431adcd5788977185cf4f8f5be Mon Sep 17 00:00:00 2001 From: Bron Gondwana Date: Sat, 24 Jan 2009 23:32:41 +1100 Subject: [PATCH] epoll: add /proc/sys/fs/epoll/limits interface This is a 4 value vector containing max_user_instances and max_user_watches constants as well as the current highest value for any user of these items. --- fs/eventpoll.c | 33 +++++++++++++++++++++++---------- include/linux/eventpoll.h | 13 +++++++++++++ kernel/user.c | 26 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 16eb817..f9cba5b 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -234,10 +234,7 @@ struct ep_pqueue { /* * Configuration options available inside /proc/sys/fs/epoll/ */ -/* Maximum number of epoll devices, per user */ -static int max_user_instances __read_mostly; -/* Maximum number of epoll watched descriptors, per user */ -static int max_user_watches __read_mostly; +struct epoll_limits_struct epoll_limits; /* * This mutex is used to serialize ep_free() and eventpoll_release_file(). @@ -259,10 +256,18 @@ static struct kmem_cache *pwq_cache __read_mostly; static int zero; +static int epoll_user_instances(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + user_epoll_maximums(&epoll_limits.num_user_instances, + &epoll_limits.num_user_watches); + return proc_dointvec(table, write, filp, buffer, lenp, ppos); +} + ctl_table epoll_table[] = { { .procname = "max_user_instances", - .data = &max_user_instances, + .data = &epoll_limits.max_user_instances, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_minmax, @@ -270,12 +275,20 @@ ctl_table epoll_table[] = { }, { .procname = "max_user_watches", - .data = &max_user_watches, + .data = &epoll_limits.max_user_watches, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_minmax, .extra1 = &zero, }, + { + .procname = "limits", + .data = &epoll_limits, + .maxlen = 4*sizeof(int), + .mode = 0444, + .proc_handler = &epoll_user_instances, + .extra1 = &zero, + }, { .ctl_name = 0 } }; #endif /* CONFIG_SYSCTL */ @@ -583,7 +596,7 @@ static int ep_alloc(struct eventpoll **pep) user = get_current_user(); error = -EMFILE; if (unlikely(atomic_read(&user->epoll_devs) >= - max_user_instances)) + epoll_limits.max_user_instances)) goto free_uid; error = -ENOMEM; ep = kzalloc(sizeof(*ep), GFP_KERNEL); @@ -762,7 +775,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, struct ep_pqueue epq; if (unlikely(atomic_read(&ep->user->epoll_watches) >= - max_user_watches)) + epoll_limits.max_user_watches)) return -ENOSPC; if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL))) return -ENOMEM; @@ -1366,8 +1379,8 @@ static int __init eventpoll_init(void) struct sysinfo si; si_meminfo(&si); - max_user_instances = 1024; - max_user_watches = (((si.totalram - si.totalhigh) / 32) << PAGE_SHIFT) / + epoll_limits.max_user_instances = 1024; + epoll_limits.max_user_watches = (((si.totalram - si.totalhigh) / 32) << PAGE_SHIFT) / EP_ITEM_COST; /* Initialize the structure used to perform safe poll wait head wake ups */ diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index f1e1d3c..38bec62 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -57,6 +57,16 @@ struct file; #ifdef CONFIG_EPOLL +/* + * Configuration options available inside /proc/sys/fs/epoll/ + */ +struct epoll_limits_struct { + int num_user_instances; /* read only */ + int num_user_watches; /* read only */ + int max_user_instances; /* tunable */ + int max_user_watches; /* tunable */ +}; + /* Used to initialize the epoll bits inside the "struct file" */ static inline void eventpoll_init_file(struct file *file) { @@ -96,6 +106,9 @@ static inline void eventpoll_release(struct file *file) eventpoll_release_file(file); } +extern struct epoll_limits_struct epoll_limits; +extern void user_epoll_maximums(int *num_devs, int *num_watches); + #else static inline void eventpoll_init_file(struct file *file) {} diff --git a/kernel/user.c b/kernel/user.c index 477b666..4d03a95 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -381,6 +381,32 @@ struct user_struct *find_user(uid_t uid) return ret; } +#ifdef CONFIG_EPOLL +void user_epoll_maximums(int *max_devs, int *max_watches) +{ + unsigned long flags; + struct user_struct *user; + struct hlist_node *h; + int n; + + *max_devs = 0; + *max_watches = 0; + + spin_lock_irqsave(&uidhash_lock, flags); + + for(n = 0; n < UIDHASH_SZ; ++n) { + hlist_for_each_entry(user, h, init_user_ns.uidhash_table + n, uidhash_node) { + if (user->epoll_devs.counter > *max_devs) + *max_devs = user->epoll_devs.counter; + if (user->epoll_watches.counter > *max_watches) + *max_watches = user->epoll_watches.counter; + } + } + + spin_unlock_irqrestore(&uidhash_lock, flags); +} +#endif /* CONFIG_EPOLL */ + void free_uid(struct user_struct *up) { unsigned long flags; -- 1.5.6.3