summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/pppoe.c30
-rw-r--r--drivers/net/pppox.c47
-rw-r--r--include/linux/if_pppox.h14
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 {