diff options
Diffstat (limited to 'net/core/sock.c')
-rw-r--r-- | net/core/sock.c | 219 |
1 files changed, 177 insertions, 42 deletions
diff --git a/net/core/sock.c b/net/core/sock.c index 18365fb8853f9..29e8a34d83edb 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -99,6 +99,8 @@ #include <linux/kernel.h> #include <linux/major.h> #include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/string.h> @@ -613,48 +615,36 @@ lenout: return 0; } -static kmem_cache_t *sk_cachep; - /** * sk_alloc - All socket objects are allocated here * @family - protocol family * @priority - for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc) - * @zero_it - zeroes the allocated sock - * @slab - alternate slab - * - * All socket objects are allocated here. If @zero_it is non-zero - * it should have the size of the are to be zeroed, because the - * private slabcaches have different sizes of the generic struct sock. - * 1 has been kept as a way to say sizeof(struct sock). + * @prot - struct proto associated with this new sock instance + * @zero_it - if we should zero the newly allocated sock */ -struct sock *sk_alloc(int family, int priority, int zero_it, kmem_cache_t *slab) +struct sock *sk_alloc(int family, int priority, struct proto *prot, int zero_it) { struct sock *sk = NULL; - - /* - * Transitional, this test will be removed when sk_cachep is killed - */ - if (slab == NULL && zero_it == 1) - slab = sk_cachep; + kmem_cache_t *slab = prot->slab; if (slab != NULL) sk = kmem_cache_alloc(slab, priority); else - sk = kmalloc(zero_it, priority); + sk = kmalloc(prot->obj_size, priority); if (sk) { if (zero_it) { - memset(sk, 0, - zero_it == 1 ? sizeof(struct sock) : zero_it); + memset(sk, 0, prot->obj_size); sk->sk_family = family; + sk->sk_prot = prot; sock_lock_init(sk); } - sk->sk_slab = slab; if (security_sk_alloc(sk, family, priority)) { kmem_cache_free(slab, sk); sk = NULL; - } + } else + __module_get(prot->owner); } return sk; } @@ -662,7 +652,7 @@ struct sock *sk_alloc(int family, int priority, int zero_it, kmem_cache_t *slab) void sk_free(struct sock *sk) { struct sk_filter *filter; - struct module *owner = sk->sk_owner; + struct module *owner = sk->sk_prot->owner; if (sk->sk_destruct) sk->sk_destruct(sk); @@ -680,8 +670,8 @@ void sk_free(struct sock *sk) __FUNCTION__, atomic_read(&sk->sk_omem_alloc)); security_sk_free(sk); - if (sk->sk_slab != NULL) - kmem_cache_free(sk->sk_slab, sk); + if (sk->sk_prot->slab != NULL) + kmem_cache_free(sk->sk_prot->slab, sk); else kfree(sk); module_put(owner); @@ -689,11 +679,6 @@ void sk_free(struct sock *sk) void __init sk_init(void) { - sk_cachep = kmem_cache_create("sock", sizeof(struct sock), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!sk_cachep) - printk(KERN_CRIT "sk_init: Cannot create sock SLAB cache!"); - if (num_physpages <= 4096) { sysctl_wmem_max = 32767; sysctl_rmem_max = 32767; @@ -1227,7 +1212,6 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_rcvlowat = 1; sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; - sk->sk_owner = NULL; sk->sk_stamp.tv_sec = -1L; sk->sk_stamp.tv_usec = -1L; @@ -1368,32 +1352,183 @@ void sk_common_release(struct sock *sk) EXPORT_SYMBOL(sk_common_release); -int sk_alloc_slab(struct proto *prot, char *name) +static rwlock_t proto_list_lock; +static LIST_HEAD(proto_list); + +int proto_register(struct proto *prot, int alloc_slab) { - prot->slab = kmem_cache_create(name, - prot->slab_obj_size, 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + int rc = -ENOBUFS; - if (prot->slab == NULL) { - printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n", - prot->name); - return -ENOBUFS; + write_lock(&proto_list_lock); + + if (alloc_slab) { + prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (prot->slab == NULL) { + printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n", + prot->name); + goto out_unlock; + } } - return 0; + list_add(&prot->node, &proto_list); + rc = 0; +out_unlock: + write_unlock(&proto_list_lock); + return rc; } -EXPORT_SYMBOL(sk_alloc_slab); +EXPORT_SYMBOL(proto_register); -void sk_free_slab(struct proto *prot) +void proto_unregister(struct proto *prot) { + write_lock(&proto_list_lock); + if (prot->slab != NULL) { kmem_cache_destroy(prot->slab); prot->slab = NULL; } + + list_del(&prot->node); + write_unlock(&proto_list_lock); +} + +EXPORT_SYMBOL(proto_unregister); + +#ifdef CONFIG_PROC_FS +static inline struct proto *__proto_head(void) +{ + return list_entry(proto_list.next, struct proto, node); +} + +static inline struct proto *proto_head(void) +{ + return list_empty(&proto_list) ? NULL : __proto_head(); +} + +static inline struct proto *proto_next(struct proto *proto) +{ + return proto->node.next == &proto_list ? NULL : + list_entry(proto->node.next, struct proto, node); +} + +static inline struct proto *proto_get_idx(loff_t pos) +{ + struct proto *proto; + loff_t i = 0; + + list_for_each_entry(proto, &proto_list, node) + if (i++ == pos) + goto out; + + proto = NULL; +out: + return proto; } -EXPORT_SYMBOL(sk_free_slab); +static void *proto_seq_start(struct seq_file *seq, loff_t *pos) +{ + read_lock(&proto_list_lock); + return *pos ? proto_get_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *proto_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return v == SEQ_START_TOKEN ? proto_head() : proto_next(v); +} + +static void proto_seq_stop(struct seq_file *seq, void *v) +{ + read_unlock(&proto_list_lock); +} + +static char proto_method_implemented(const void *method) +{ + return method == NULL ? 'n' : 'y'; +} + +static void proto_seq_printf(struct seq_file *seq, struct proto *proto) +{ + seq_printf(seq, "%-9s %4u %6d %6d %-3s %6u %-3s %-10s " + "%2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c\n", + proto->name, + proto->obj_size, + proto->sockets_allocated != NULL ? atomic_read(proto->sockets_allocated) : -1, + proto->memory_allocated != NULL ? atomic_read(proto->memory_allocated) : -1, + proto->memory_pressure != NULL ? *proto->memory_pressure ? "yes" : "no" : "NI", + proto->max_header, + proto->slab == NULL ? "no" : "yes", + module_name(proto->owner), + proto_method_implemented(proto->close), + proto_method_implemented(proto->connect), + proto_method_implemented(proto->disconnect), + proto_method_implemented(proto->accept), + proto_method_implemented(proto->ioctl), + proto_method_implemented(proto->init), + proto_method_implemented(proto->destroy), + proto_method_implemented(proto->shutdown), + proto_method_implemented(proto->setsockopt), + proto_method_implemented(proto->getsockopt), + proto_method_implemented(proto->sendmsg), + proto_method_implemented(proto->recvmsg), + proto_method_implemented(proto->sendpage), + proto_method_implemented(proto->bind), + proto_method_implemented(proto->backlog_rcv), + proto_method_implemented(proto->hash), + proto_method_implemented(proto->unhash), + proto_method_implemented(proto->get_port), + proto_method_implemented(proto->enter_memory_pressure)); +} + +static int proto_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "%-9s %-4s %-8s %-6s %-5s %-7s %-4s %-10s %s", + "protocol", + "size", + "sockets", + "memory", + "press", + "maxhdr", + "slab", + "module", + "cl co di ac io in de sh ss gs se re sp bi br ha uh gp em\n"); + else + proto_seq_printf(seq, v); + return 0; +} + +static struct seq_operations proto_seq_ops = { + .start = proto_seq_start, + .next = proto_seq_next, + .stop = proto_seq_stop, + .show = proto_seq_show, +}; + +static int proto_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &proto_seq_ops); +} + +static struct file_operations proto_seq_fops = { + .owner = THIS_MODULE, + .open = proto_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init proto_init(void) +{ + /* register /proc/net/protocols */ + return proc_net_fops_create("protocols", S_IRUGO, &proto_seq_fops) == NULL ? -ENOBUFS : 0; +} + +subsys_initcall(proto_init); + +#endif /* PROC_FS */ EXPORT_SYMBOL(sk_alloc); EXPORT_SYMBOL(sk_free); |