V4L/DVB: v4l2-dev: remove unnecessary lock around atomic clear_bit
[safe/jmp/linux-2.6] / drivers / net / pppol2tp.c
index 185b1df..449a982 100644 (file)
@@ -90,7 +90,9 @@
 #include <linux/hash.h>
 #include <linux/sort.h>
 #include <linux/proc_fs.h>
+#include <linux/nsproxy.h>
 #include <net/net_namespace.h>
+#include <net/netns/generic.h>
 #include <net/dst.h>
 #include <net/ip.h>
 #include <net/udp.h>
@@ -204,6 +206,7 @@ struct pppol2tp_tunnel
        struct sock             *sock;          /* Parent socket */
        struct list_head        list;           /* Keep a list of all open
                                                 * prepared sockets */
+       struct net              *pppol2tp_net;  /* the net we belong to */
 
        atomic_t                ref_count;
 };
@@ -226,9 +229,21 @@ static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel);
 static atomic_t pppol2tp_tunnel_count;
 static atomic_t pppol2tp_session_count;
 static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };
-static struct proto_ops pppol2tp_ops;
-static LIST_HEAD(pppol2tp_tunnel_list);
-static DEFINE_RWLOCK(pppol2tp_tunnel_list_lock);
+static const struct proto_ops pppol2tp_ops;
+
+/* per-net private data for this module */
+static int pppol2tp_net_id __read_mostly;
+struct pppol2tp_net {
+       struct list_head pppol2tp_tunnel_list;
+       rwlock_t pppol2tp_tunnel_list_lock;
+};
+
+static inline struct pppol2tp_net *pppol2tp_pernet(struct net *net)
+{
+       BUG_ON(!net);
+
+       return net_generic(net, pppol2tp_net_id);
+}
 
 /* Helpers to obtain tunnel/session contexts from sockets.
  */
@@ -321,18 +336,19 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id)
 
 /* Lookup a tunnel by id
  */
-static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id)
+static struct pppol2tp_tunnel *pppol2tp_tunnel_find(struct net *net, u16 tunnel_id)
 {
-       struct pppol2tp_tunnel *tunnel = NULL;
+       struct pppol2tp_tunnel *tunnel;
+       struct pppol2tp_net *pn = pppol2tp_pernet(net);
 
-       read_lock_bh(&pppol2tp_tunnel_list_lock);
-       list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) {
+       read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
+       list_for_each_entry(tunnel, &pn->pppol2tp_tunnel_list, list) {
                if (tunnel->stats.tunnel_id == tunnel_id) {
-                       read_unlock_bh(&pppol2tp_tunnel_list_lock);
+                       read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
                        return tunnel;
                }
        }
-       read_unlock_bh(&pppol2tp_tunnel_list_lock);
+       read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
 
        return NULL;
 }
@@ -417,8 +433,7 @@ static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct s
                 *   to the inner packet either
                 */
                secpath_reset(skb);
-               dst_release(skb->dst);
-               skb->dst = NULL;
+               skb_dst_drop(skb);
                nf_reset(skb);
 
                po = pppox_sk(session_sock);
@@ -489,6 +504,30 @@ out:
        spin_unlock_bh(&session->reorder_q.lock);
 }
 
+static inline int pppol2tp_verify_udp_checksum(struct sock *sk,
+                                              struct sk_buff *skb)
+{
+       struct udphdr *uh = udp_hdr(skb);
+       u16 ulen = ntohs(uh->len);
+       struct inet_sock *inet;
+       __wsum psum;
+
+       if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check)
+               return 0;
+
+       inet = inet_sk(sk);
+       psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen,
+                                 IPPROTO_UDP, 0);
+
+       if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
+           !csum_fold(csum_add(psum, skb->csum)))
+               return 0;
+
+       skb->csum = psum;
+
+       return __skb_checksum_complete(skb);
+}
+
 /* Internal receive frame. Do the real work of receiving an L2TP data frame
  * here. The skb is not on a list when we get here.
  * Returns 0 if the packet was a data packet and was successfully passed on.
@@ -509,6 +548,9 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
        if (tunnel == NULL)
                goto no_tunnel;
 
+       if (tunnel->sock && pppol2tp_verify_udp_checksum(tunnel->sock, skb))
+               goto discard_bad_csum;
+
        /* UDP always verifies the packet length. */
        __skb_pull(skb, sizeof(struct udphdr));
 
@@ -714,6 +756,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
 
        /* Try to dequeue as many skbs from reorder_q as we can. */
        pppol2tp_recv_dequeue(session);
+       sock_put(sock);
 
        return 0;
 
@@ -725,6 +768,15 @@ discard:
 
        return 0;
 
+discard_bad_csum:
+       LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
+       UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0);
+       tunnel->stats.rx_errors++;
+       kfree_skb(skb);
+       sock_put(sock);
+
+       return 0;
+
 error:
        /* Put UDP header back */
        __skb_push(skb, sizeof(struct udphdr));
@@ -851,7 +903,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        static const unsigned char ppph[2] = { 0xff, 0x03 };
        struct sock *sk = sock->sk;
        struct inet_sock *inet;
-       __wsum csum = 0;
+       __wsum csum;
        struct sk_buff *skb;
        int error;
        int hdr_len;
@@ -859,6 +911,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        struct pppol2tp_tunnel *tunnel;
        struct udphdr *uh;
        unsigned int len;
+       struct sock *sk_tun;
+       u16 udp_len;
 
        error = -ENOTCONN;
        if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
@@ -870,7 +924,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        if (session == NULL)
                goto error;
 
-       tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
+       sk_tun = session->tunnel_sock;
+       tunnel = pppol2tp_sock_to_tunnel(sk_tun);
        if (tunnel == NULL)
                goto error_put_sess;
 
@@ -893,11 +948,12 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        skb_reset_transport_header(skb);
 
        /* Build UDP header */
-       inet = inet_sk(session->tunnel_sock);
+       inet = inet_sk(sk_tun);
+       udp_len = hdr_len + sizeof(ppph) + total_len;
        uh = (struct udphdr *) skb->data;
-       uh->source = inet->sport;
-       uh->dest = inet->dport;
-       uh->len = htons(hdr_len + sizeof(ppph) + total_len);
+       uh->source = inet->inet_sport;
+       uh->dest = inet->inet_dport;
+       uh->len = htons(udp_len);
        uh->check = 0;
        skb_put(skb, sizeof(struct udphdr));
 
@@ -919,8 +975,24 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        skb_put(skb, total_len);
 
        /* Calculate UDP checksum if configured to do so */
-       if (session->tunnel_sock->sk_no_check != UDP_CSUM_NOXMIT)
-               csum = udp_csum_outgoing(sk, skb);
+       if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
+               skb->ip_summed = CHECKSUM_NONE;
+       else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               csum = skb_checksum(skb, 0, udp_len, 0);
+               uh->check = csum_tcpudp_magic(inet->inet_saddr,
+                                             inet->inet_daddr,
+                                             udp_len, IPPROTO_UDP, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+       } else {
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
+                                              inet->inet_daddr,
+                                              udp_len, IPPROTO_UDP, 0);
+       }
 
        /* Debug */
        if (session->send_seq)
@@ -1008,13 +1080,14 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        struct sock *sk = (struct sock *) chan->private;
        struct sock *sk_tun;
        int hdr_len;
+       u16 udp_len;
        struct pppol2tp_session *session;
        struct pppol2tp_tunnel *tunnel;
        int rc;
        int headroom;
        int data_len = skb->len;
        struct inet_sock *inet;
-       __wsum csum = 0;
+       __wsum csum;
        struct udphdr *uh;
        unsigned int len;
        int old_headroom;
@@ -1060,20 +1133,18 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        /* Setup L2TP header */
        pppol2tp_build_l2tp_header(session, __skb_push(skb, hdr_len));
 
+       udp_len = sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len;
+
        /* Setup UDP header */
        inet = inet_sk(sk_tun);
        __skb_push(skb, sizeof(*uh));
        skb_reset_transport_header(skb);
        uh = udp_hdr(skb);
-       uh->source = inet->sport;
-       uh->dest = inet->dport;
-       uh->len = htons(sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len);
+       uh->source = inet->inet_sport;
+       uh->dest = inet->inet_dport;
+       uh->len = htons(udp_len);
        uh->check = 0;
 
-       /* *BROKEN* Calculate UDP checksum if configured to do so */
-       if (sk_tun->sk_no_check != UDP_CSUM_NOXMIT)
-               csum = udp_csum_outgoing(sk_tun, skb);
-
        /* Debug */
        if (session->send_seq)
                PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
@@ -1104,10 +1175,31 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        nf_reset(skb);
 
        /* Get routing info from the tunnel socket */
-       dst_release(skb->dst);
-       skb->dst = dst_clone(__sk_dst_get(sk_tun));
+       skb_dst_drop(skb);
+       skb_dst_set(skb, dst_clone(__sk_dst_get(sk_tun)));
        pppol2tp_skb_set_owner_w(skb, sk_tun);
 
+       /* Calculate UDP checksum if configured to do so */
+       if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
+               skb->ip_summed = CHECKSUM_NONE;
+       else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
+                (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               csum = skb_checksum(skb, 0, udp_len, 0);
+               uh->check = csum_tcpudp_magic(inet->inet_saddr,
+                                             inet->inet_daddr,
+                                             udp_len, IPPROTO_UDP, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+       } else {
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
+                                              inet->inet_daddr,
+                                              udp_len, IPPROTO_UDP, 0);
+       }
+
        /* Queue the packet to IP for output */
        len = skb->len;
        rc = ip_queue_xmit(skb, 1);
@@ -1152,8 +1244,7 @@ static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel)
        struct pppol2tp_session *session;
        struct sock *sk;
 
-       if (tunnel == NULL)
-               BUG();
+       BUG_ON(tunnel == NULL);
 
        PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
               "%s: closing all sessions...\n", tunnel->name);
@@ -1217,10 +1308,12 @@ again:
  */
 static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel)
 {
+       struct pppol2tp_net *pn = pppol2tp_pernet(tunnel->pppol2tp_net);
+
        /* Remove from socket list */
-       write_lock_bh(&pppol2tp_tunnel_list_lock);
+       write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
        list_del_init(&tunnel->list);
-       write_unlock_bh(&pppol2tp_tunnel_list_lock);
+       write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
 
        atomic_dec(&pppol2tp_tunnel_count);
        kfree(tunnel);
@@ -1353,6 +1446,7 @@ static int pppol2tp_release(struct socket *sock)
                        kfree_skb(skb);
                        sock_put(sk);
                }
+               sock_put(sk);
        }
 
        release_sock(sk);
@@ -1373,13 +1467,14 @@ error:
 /* Internal function to prepare a tunnel (UDP) socket to have PPPoX
  * sockets attached to it.
  */
-static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
-                                                  int *error)
+static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net,
+                                       int fd, u16 tunnel_id, int *error)
 {
        int err;
        struct socket *sock = NULL;
        struct sock *sk;
        struct pppol2tp_tunnel *tunnel;
+       struct pppol2tp_net *pn;
        struct sock *ret = NULL;
 
        /* Get the tunnel UDP socket from the fd, which was opened by
@@ -1445,7 +1540,7 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
         * if the tunnel socket goes away.
         */
        tunnel->old_sk_destruct = sk->sk_destruct;
-       sk->sk_destruct = &pppol2tp_tunnel_destruct;
+       sk->sk_destruct = pppol2tp_tunnel_destruct;
 
        tunnel->sock = sk;
        sk->sk_allocation = GFP_ATOMIC;
@@ -1453,11 +1548,15 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
        /* Misc init */
        rwlock_init(&tunnel->hlist_lock);
 
+       /* The net we belong to */
+       tunnel->pppol2tp_net = net;
+       pn = pppol2tp_pernet(net);
+
        /* Add tunnel to our list */
        INIT_LIST_HEAD(&tunnel->list);
-       write_lock_bh(&pppol2tp_tunnel_list_lock);
-       list_add(&tunnel->list, &pppol2tp_tunnel_list);
-       write_unlock_bh(&pppol2tp_tunnel_list_lock);
+       write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
+       list_add(&tunnel->list, &pn->pppol2tp_tunnel_list);
+       write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
        atomic_inc(&pppol2tp_tunnel_count);
 
        /* Bump the reference count. The tunnel context is deleted
@@ -1558,15 +1657,17 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
         * tunnel id.
         */
        if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) {
-               tunnel_sock = pppol2tp_prepare_tunnel_socket(sp->pppol2tp.fd,
+               tunnel_sock = pppol2tp_prepare_tunnel_socket(sock_net(sk),
+                                                            sp->pppol2tp.fd,
                                                             sp->pppol2tp.s_tunnel,
                                                             &error);
                if (tunnel_sock == NULL)
                        goto end;
 
+               sock_hold(tunnel_sock);
                tunnel = tunnel_sock->sk_user_data;
        } else {
-               tunnel = pppol2tp_tunnel_find(sp->pppol2tp.s_tunnel);
+               tunnel = pppol2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel);
 
                /* Error if we can't find the tunnel */
                error = -ENOENT;
@@ -1654,7 +1755,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
        po->chan.ops     = &pppol2tp_chan_ops;
        po->chan.mtu     = session->mtu;
 
-       error = ppp_register_channel(&po->chan);
+       error = ppp_register_net_channel(sock_net(sk), &po->chan);
        if (error)
                goto end_put_tun;
 
@@ -2086,7 +2187,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk,
  * session or the special tunnel type.
  */
 static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
-                              char __user *optval, int optlen)
+                              char __user *optval, unsigned int optlen)
 {
        struct sock *sk = sock->sk;
        struct pppol2tp_session *session = sk->sk_user_data;
@@ -2276,8 +2377,9 @@ end:
 #include <linux/seq_file.h>
 
 struct pppol2tp_seq_data {
-       struct pppol2tp_tunnel *tunnel; /* current tunnel */
-       struct pppol2tp_session *session; /* NULL means get first session in tunnel */
+       struct seq_net_private p;
+       struct pppol2tp_tunnel *tunnel;         /* current tunnel */
+       struct pppol2tp_session *session;       /* NULL means get first session in tunnel */
 };
 
 static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, struct pppol2tp_session *curr)
@@ -2313,17 +2415,18 @@ out:
        return session;
 }
 
-static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr)
+static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_net *pn,
+                                          struct pppol2tp_tunnel *curr)
 {
        struct pppol2tp_tunnel *tunnel = NULL;
 
-       read_lock_bh(&pppol2tp_tunnel_list_lock);
-       if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) {
+       read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
+       if (list_is_last(&curr->list, &pn->pppol2tp_tunnel_list)) {
                goto out;
        }
        tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list);
 out:
-       read_unlock_bh(&pppol2tp_tunnel_list_lock);
+       read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
 
        return tunnel;
 }
@@ -2331,6 +2434,7 @@ out:
 static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs)
 {
        struct pppol2tp_seq_data *pd = SEQ_START_TOKEN;
+       struct pppol2tp_net *pn;
        loff_t pos = *offs;
 
        if (!pos)
@@ -2338,14 +2442,15 @@ static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs)
 
        BUG_ON(m->private == NULL);
        pd = m->private;
+       pn = pppol2tp_pernet(seq_file_net(m));
 
        if (pd->tunnel == NULL) {
-               if (!list_empty(&pppol2tp_tunnel_list))
-                       pd->tunnel = list_entry(pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list);
+               if (!list_empty(&pn->pppol2tp_tunnel_list))
+                       pd->tunnel = list_entry(pn->pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list);
        } else {
                pd->session = next_session(pd->tunnel, pd->session);
                if (pd->session == NULL) {
-                       pd->tunnel = next_tunnel(pd->tunnel);
+                       pd->tunnel = next_tunnel(pn, pd->tunnel);
                }
        }
 
@@ -2446,7 +2551,7 @@ out:
        return 0;
 }
 
-static struct seq_operations pppol2tp_seq_ops = {
+static const struct seq_operations pppol2tp_seq_ops = {
        .start          = pppol2tp_seq_start,
        .next           = pppol2tp_seq_next,
        .stop           = pppol2tp_seq_stop,
@@ -2459,58 +2564,25 @@ static struct seq_operations pppol2tp_seq_ops = {
  */
 static int pppol2tp_proc_open(struct inode *inode, struct file *file)
 {
-       struct seq_file *m;
-       struct pppol2tp_seq_data *pd;
-       int ret = 0;
-
-       ret = seq_open(file, &pppol2tp_seq_ops);
-       if (ret < 0)
-               goto out;
-
-       m = file->private_data;
-
-       /* Allocate and fill our proc_data for access later */
-       ret = -ENOMEM;
-       m->private = kzalloc(sizeof(struct pppol2tp_seq_data), GFP_KERNEL);
-       if (m->private == NULL)
-               goto out;
-
-       pd = m->private;
-       ret = 0;
-
-out:
-       return ret;
+       return seq_open_net(inode, file, &pppol2tp_seq_ops,
+                           sizeof(struct pppol2tp_seq_data));
 }
 
-/* Called when /proc file access completes.
- */
-static int pppol2tp_proc_release(struct inode *inode, struct file *file)
-{
-       struct seq_file *m = (struct seq_file *)file->private_data;
-
-       kfree(m->private);
-       m->private = NULL;
-
-       return seq_release(inode, file);
-}
-
-static struct file_operations pppol2tp_proc_fops = {
+static const struct file_operations pppol2tp_proc_fops = {
        .owner          = THIS_MODULE,
        .open           = pppol2tp_proc_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = pppol2tp_proc_release,
+       .release        = seq_release_net,
 };
 
-static struct proc_dir_entry *pppol2tp_proc;
-
 #endif /* CONFIG_PROC_FS */
 
 /*****************************************************************************
  * Init and cleanup
  *****************************************************************************/
 
-static struct proto_ops pppol2tp_ops = {
+static const struct proto_ops pppol2tp_ops = {
        .family         = AF_PPPOX,
        .owner          = THIS_MODULE,
        .release        = pppol2tp_release,
@@ -2535,6 +2607,35 @@ static struct pppox_proto pppol2tp_proto = {
        .ioctl          = pppol2tp_ioctl
 };
 
+static __net_init int pppol2tp_init_net(struct net *net)
+{
+       struct pppol2tp_net *pn = pppol2tp_pernet(net);
+       struct proc_dir_entry *pde;
+
+       INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list);
+       rwlock_init(&pn->pppol2tp_tunnel_list_lock);
+
+       pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops);
+#ifdef CONFIG_PROC_FS
+       if (!pde)
+               return -ENOMEM;
+#endif
+
+       return 0;
+}
+
+static __net_exit void pppol2tp_exit_net(struct net *net)
+{
+       proc_net_remove(net, "pppol2tp");
+}
+
+static struct pernet_operations pppol2tp_net_ops = {
+       .init = pppol2tp_init_net,
+       .exit = pppol2tp_exit_net,
+       .id   = &pppol2tp_net_id,
+       .size = sizeof(struct pppol2tp_net),
+};
+
 static int __init pppol2tp_init(void)
 {
        int err;
@@ -2546,23 +2647,17 @@ static int __init pppol2tp_init(void)
        if (err)
                goto out_unregister_pppol2tp_proto;
 
-#ifdef CONFIG_PROC_FS
-       pppol2tp_proc = proc_net_fops_create(&init_net, "pppol2tp", 0,
-                                            &pppol2tp_proc_fops);
-       if (!pppol2tp_proc) {
-               err = -ENOMEM;
+       err = register_pernet_device(&pppol2tp_net_ops);
+       if (err)
                goto out_unregister_pppox_proto;
-       }
-#endif /* CONFIG_PROC_FS */
+
        printk(KERN_INFO "PPPoL2TP kernel driver, %s\n",
               PPPOL2TP_DRV_VERSION);
 
 out:
        return err;
-#ifdef CONFIG_PROC_FS
 out_unregister_pppox_proto:
        unregister_pppox_proto(PX_PROTO_OL2TP);
-#endif
 out_unregister_pppol2tp_proto:
        proto_unregister(&pppol2tp_sk_proto);
        goto out;
@@ -2571,10 +2666,7 @@ out_unregister_pppol2tp_proto:
 static void __exit pppol2tp_exit(void)
 {
        unregister_pppox_proto(PX_PROTO_OL2TP);
-
-#ifdef CONFIG_PROC_FS
-       remove_proc_entry("pppol2tp", init_net.proc_net);
-#endif
+       unregister_pernet_device(&pppol2tp_net_ops);
        proto_unregister(&pppol2tp_sk_proto);
 }