diff options
| -rw-r--r-- | drivers/net/pppoe.c | 30 | ||||
| -rw-r--r-- | drivers/net/pppox.c | 47 | ||||
| -rw-r--r-- | include/linux/if_pppox.h | 14 |
3 files changed, 67 insertions, 24 deletions
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 17f18f1f8f90..ba2ba6d5e179 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -471,16 +471,15 @@ struct packet_type pppoed_ptype = { /*********************************************************************** * - * Really kill the socket. (Called from sock_put if refcnt == 0.) + * Really kill the socket. (Called from pppox_sk_free if refcnt == 0.) * **********************************************************************/ -void pppoe_sock_destruct(struct sock *sk) +static void pppoe_sk_free(struct sock *sk) { struct pppox_opt *po = pppox_sk(sk); if (po) kfree(po); - MOD_DEC_USE_COUNT; } @@ -495,26 +494,16 @@ static int pppoe_create(struct socket *sock) struct sock *sk; struct pppox_opt *po; - MOD_INC_USE_COUNT; - - sk = sk_alloc(PF_PPPOX, GFP_KERNEL, 1, NULL); + sk = pppox_sk_alloc(sock, PX_PROTO_OE, GFP_KERNEL, 1, NULL); if (!sk) - goto decmod; - - sock_init_data(sock, sk); + goto out; sock->state = SS_UNCONNECTED; sock->ops = &pppoe_ops; - sk->protocol = PX_PROTO_OE; - sk->family = PF_PPPOX; - sk->backlog_rcv = pppoe_rcv_core; - sk->next = NULL; - sk->pprev = NULL; sk->state = PPPOX_NONE; sk->type = SOCK_STREAM; - sk->destruct = pppoe_sock_destruct; po = pppox_sk(sk) = kmalloc(sizeof(*po), GFP_KERNEL); if (!po) @@ -522,10 +511,8 @@ static int pppoe_create(struct socket *sock) memset(po, 0, sizeof(*po)); po->sk = sk; error = 0; - sock->sk = sk; out: return error; frees: sk_free(sk); -decmod: MOD_DEC_USE_COUNT; goto out; } @@ -1075,16 +1062,16 @@ static struct file_operations pppoe_seq_fops = { }; #endif /* CONFIG_PROC_FS */ +/* ->release and ->ioctl are set at pppox_create */ + struct proto_ops pppoe_ops = { .family = AF_PPPOX, - .release = pppoe_release, .bind = sock_no_bind, .connect = pppoe_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = pppoe_getname, .poll = datagram_poll, - .ioctl = pppoe_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, @@ -1096,7 +1083,10 @@ struct proto_ops pppoe_ops = { struct pppox_proto pppoe_proto = { .create = pppoe_create, - .ioctl = pppoe_ioctl + .ioctl = pppoe_ioctl, + .release = pppoe_release, + .sk_free = pppoe_sk_free, + .owner = THIS_MODULE, }; diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c index 31b7442ceeda..3bd707c67238 100644 --- a/drivers/net/pppox.c +++ b/drivers/net/pppox.c @@ -64,9 +64,45 @@ void pppox_unbind_sock(struct sock *sk) } } +static int pppox_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + int rc = pppox_protos[sk->protocol]->release(sock); + + module_put(pppox_protos[sk->protocol]->owner); + return rc; +} + +static void pppox_sk_free(struct sock *sk) +{ + pppox_protos[sk->protocol]->sk_free(sk); + module_put(pppox_protos[sk->protocol]->owner); +} + +struct sock *pppox_sk_alloc(struct socket *sock, int protocol, int priority, + int zero_it, kmem_cache_t *slab) +{ + struct sock *sk = NULL; + + if (!try_module_get(pppox_protos[protocol]->owner)) + goto out; + + sk = sk_alloc(PF_PPPOX, priority, zero_it, slab); + if (sk) { + sock_init_data(sock, sk); + sk->family = PF_PPPOX; + sk->protocol = protocol; + sk->destruct = pppox_sk_free; + } else + module_put(pppox_protos[protocol]->owner); +out: + return sk; +} + EXPORT_SYMBOL(register_pppox_proto); EXPORT_SYMBOL(unregister_pppox_proto); EXPORT_SYMBOL(pppox_unbind_sock); +EXPORT_SYMBOL(pppox_sk_alloc); static int pppox_ioctl(struct socket* sock, unsigned int cmd, unsigned long arg) @@ -116,11 +152,19 @@ static int pppox_create(struct socket *sock, int protocol) if (!pppox_protos[protocol]) goto out; + rc = -EBUSY; + if (!try_module_get(pppox_protos[protocol]->owner)) + goto out; + rc = pppox_protos[protocol]->create(sock); - if (!rc) + if (!rc) { /* We get to set the ioctl handler. */ + /* And the release handler, for module refcounting */ /* For everything else, pppox is just a shell. */ sock->ops->ioctl = pppox_ioctl; + sock->ops->release = pppox_release; + } else + module_put(pppox_protos[protocol]->owner); out: return rc; } @@ -128,6 +172,7 @@ out: static struct net_proto_family pppox_proto_family = { .family = PF_PPPOX, .create = pppox_create, + .owner = THIS_MODULE, }; static int __init pppox_init(void) diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 948826892b82..117357b14483 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -134,10 +134,15 @@ struct pppox_opt { #define pppox_sk(__sk) ((struct pppox_opt *)(__sk)->protinfo) +struct module; + struct pppox_proto { - int (*create)(struct socket *sock); - int (*ioctl)(struct socket *sock, unsigned int cmd, - unsigned long arg); + int (*create)(struct socket *sock); + int (*ioctl)(struct socket *sock, unsigned int cmd, + unsigned long arg); + int (*release)(struct socket *sock); + void (*sk_free)(struct sock *sk); + struct module *owner; }; extern int register_pppox_proto(int proto_num, struct pppox_proto *pp); @@ -145,6 +150,9 @@ extern void unregister_pppox_proto(int proto_num); extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */ extern int pppox_channel_ioctl(struct ppp_channel *pc, unsigned int cmd, unsigned long arg); +extern struct sock *pppox_sk_alloc(struct socket *sock, int protocol, + int priority, int zero_it, + kmem_cache_t *slab); /* PPPoX socket states */ enum { |
