Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / net / pppol2tp.c
index e0b072d..f929882 100644 (file)
@@ -240,12 +240,15 @@ static inline struct pppol2tp_session *pppol2tp_sock_to_session(struct sock *sk)
        if (sk == NULL)
                return NULL;
 
+       sock_hold(sk);
        session = (struct pppol2tp_session *)(sk->sk_user_data);
-       if (session == NULL)
-               return NULL;
+       if (session == NULL) {
+               sock_put(sk);
+               goto out;
+       }
 
        BUG_ON(session->magic != L2TP_SESSION_MAGIC);
-
+out:
        return session;
 }
 
@@ -256,12 +259,15 @@ static inline struct pppol2tp_tunnel *pppol2tp_sock_to_tunnel(struct sock *sk)
        if (sk == NULL)
                return NULL;
 
+       sock_hold(sk);
        tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data);
-       if (tunnel == NULL)
-               return NULL;
+       if (tunnel == NULL) {
+               sock_put(sk);
+               goto out;
+       }
 
        BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
-
+out:
        return tunnel;
 }
 
@@ -302,14 +308,14 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id)
        struct pppol2tp_session *session;
        struct hlist_node *walk;
 
-       read_lock(&tunnel->hlist_lock);
+       read_lock_bh(&tunnel->hlist_lock);
        hlist_for_each_entry(session, walk, session_list, hlist) {
                if (session->tunnel_addr.s_session == session_id) {
-                       read_unlock(&tunnel->hlist_lock);
+                       read_unlock_bh(&tunnel->hlist_lock);
                        return session;
                }
        }
-       read_unlock(&tunnel->hlist_lock);
+       read_unlock_bh(&tunnel->hlist_lock);
 
        return NULL;
 }
@@ -320,14 +326,14 @@ static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id)
 {
        struct pppol2tp_tunnel *tunnel = NULL;
 
-       read_lock(&pppol2tp_tunnel_list_lock);
+       read_lock_bh(&pppol2tp_tunnel_list_lock);
        list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) {
                if (tunnel->stats.tunnel_id == tunnel_id) {
-                       read_unlock(&pppol2tp_tunnel_list_lock);
+                       read_unlock_bh(&pppol2tp_tunnel_list_lock);
                        return tunnel;
                }
        }
-       read_unlock(&pppol2tp_tunnel_list_lock);
+       read_unlock_bh(&pppol2tp_tunnel_list_lock);
 
        return NULL;
 }
@@ -342,10 +348,11 @@ static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id)
 static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_buff *skb)
 {
        struct sk_buff *skbp;
+       struct sk_buff *tmp;
        u16 ns = PPPOL2TP_SKB_CB(skb)->ns;
 
-       spin_lock(&session->reorder_q.lock);
-       skb_queue_walk(&session->reorder_q, skbp) {
+       spin_lock_bh(&session->reorder_q.lock);
+       skb_queue_walk_safe(&session->reorder_q, skbp, tmp) {
                if (PPPOL2TP_SKB_CB(skbp)->ns > ns) {
                        __skb_insert(skb, skbp->prev, skbp, &session->reorder_q);
                        PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
@@ -360,7 +367,7 @@ static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_
        __skb_queue_tail(&session->reorder_q, skb);
 
 out:
-       spin_unlock(&session->reorder_q.lock);
+       spin_unlock_bh(&session->reorder_q.lock);
 }
 
 /* Dequeue a single skb.
@@ -371,10 +378,9 @@ static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct s
        int length = PPPOL2TP_SKB_CB(skb)->length;
        struct sock *session_sock = NULL;
 
-       /* We're about to requeue the skb, so unlink it and return resources
+       /* We're about to requeue the skb, so return resources
         * to its current owner (a socket receive buffer).
         */
-       skb_unlink(skb, &session->reorder_q);
        skb_orphan(skb);
 
        tunnel->stats.rx_packets++;
@@ -442,7 +448,7 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
         * expect to send up next, dequeue it and any other
         * in-sequence packets behind it.
         */
-       spin_lock(&session->reorder_q.lock);
+       spin_lock_bh(&session->reorder_q.lock);
        skb_queue_walk_safe(&session->reorder_q, skb, tmp) {
                if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) {
                        session->stats.rx_seq_discards++;
@@ -455,6 +461,7 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
                               skb_queue_len(&session->reorder_q));
                        __skb_unlink(skb, &session->reorder_q);
                        kfree_skb(skb);
+                       sock_put(session->sock);
                        continue;
                }
 
@@ -469,13 +476,18 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
                                goto out;
                        }
                }
-               spin_unlock(&session->reorder_q.lock);
+               __skb_unlink(skb, &session->reorder_q);
+
+               /* Process the skb. We release the queue lock while we
+                * do so to let other contexts process the queue.
+                */
+               spin_unlock_bh(&session->reorder_q.lock);
                pppol2tp_recv_dequeue_skb(session, skb);
-               spin_lock(&session->reorder_q.lock);
+               spin_lock_bh(&session->reorder_q.lock);
        }
 
 out:
-       spin_unlock(&session->reorder_q.lock);
+       spin_unlock_bh(&session->reorder_q.lock);
 }
 
 /* Internal receive frame. Do the real work of receiving an L2TP data frame
@@ -710,12 +722,14 @@ discard:
        session->stats.rx_errors++;
        kfree_skb(skb);
        sock_put(session->sock);
+       sock_put(sock);
 
        return 0;
 
 error:
        /* Put UDP header back */
        __skb_push(skb, sizeof(struct udphdr));
+       sock_put(sock);
 
 no_tunnel:
        return 1;
@@ -739,10 +753,13 @@ static int pppol2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
               "%s: received %d bytes\n", tunnel->name, skb->len);
 
        if (pppol2tp_recv_core(sk, skb))
-               goto pass_up;
+               goto pass_up_put;
 
+       sock_put(sk);
        return 0;
 
+pass_up_put:
+       sock_put(sk);
 pass_up:
        return 1;
 }
@@ -766,14 +783,18 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
        err = 0;
        skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
                                flags & MSG_DONTWAIT, &err);
-       if (skb) {
-               err = memcpy_toiovec(msg->msg_iov, (unsigned char *) skb->data,
-                                    skb->len);
-               if (err < 0)
-                       goto do_skb_free;
-               err = skb->len;
-       }
-do_skb_free:
+       if (!skb)
+               goto end;
+
+       if (len > skb->len)
+               len = skb->len;
+       else if (len < skb->len)
+               msg->msg_flags |= MSG_TRUNC;
+
+       err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
+       if (likely(err == 0))
+               err = len;
+
        kfree_skb(skb);
 end:
        return err;
@@ -852,7 +873,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
 
        tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
        if (tunnel == NULL)
-               goto error;
+               goto error_put_sess;
 
        /* What header length is configured for this session? */
        hdr_len = pppol2tp_l2tp_header_len(session);
@@ -864,7 +885,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
                           sizeof(ppph) + total_len,
                           0, GFP_KERNEL);
        if (!skb)
-               goto error;
+               goto error_put_sess_tun;
 
        /* Reserve space for headers. */
        skb_reserve(skb, NET_SKB_PAD);
@@ -894,7 +915,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        error = memcpy_fromiovec(skb->data, m->msg_iov, total_len);
        if (error < 0) {
                kfree_skb(skb);
-               goto error;
+               goto error_put_sess_tun;
        }
        skb_put(skb, total_len);
 
@@ -941,10 +962,33 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
                session->stats.tx_errors++;
        }
 
+       return error;
+
+error_put_sess_tun:
+       sock_put(session->tunnel_sock);
+error_put_sess:
+       sock_put(sk);
 error:
        return error;
 }
 
+/* Automatically called when the skb is freed.
+ */
+static void pppol2tp_sock_wfree(struct sk_buff *skb)
+{
+       sock_put(skb->sk);
+}
+
+/* For data skbs that we transmit, we associate with the tunnel socket
+ * but don't do accounting.
+ */
+static inline void pppol2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
+{
+       sock_hold(sk);
+       skb->sk = sk;
+       skb->destructor = pppol2tp_sock_wfree;
+}
+
 /* Transmit function called by generic PPP driver.  Sends PPP frame
  * over PPPoL2TP socket.
  *
@@ -974,6 +1018,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        __wsum csum = 0;
        struct udphdr *uh;
        unsigned int len;
+       int old_headroom;
+       int new_headroom;
 
        if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
                goto abort;
@@ -985,25 +1031,27 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
 
        sk_tun = session->tunnel_sock;
        if (sk_tun == NULL)
-               goto abort;
+               goto abort_put_sess;
        tunnel = pppol2tp_sock_to_tunnel(sk_tun);
        if (tunnel == NULL)
-               goto abort;
+               goto abort_put_sess;
 
        /* What header length is configured for this session? */
        hdr_len = pppol2tp_l2tp_header_len(session);
 
        /* Check that there's enough headroom in the skb to insert IP,
         * UDP and L2TP and PPP headers. If not enough, expand it to
-        * make room. Note that a new skb (or a clone) is
-        * allocated. If we return an error from this point on, make
-        * sure we free the new skb but do not free the original skb
-        * since that is done by the caller for the error case.
+        * make room. Adjust truesize.
         */
        headroom = NET_SKB_PAD + sizeof(struct iphdr) +
                sizeof(struct udphdr) + hdr_len + sizeof(ppph);
+       old_headroom = skb_headroom(skb);
        if (skb_cow_head(skb, headroom))
-               goto abort;
+               goto abort_put_sess_tun;
+
+       new_headroom = skb_headroom(skb);
+       skb_orphan(skb);
+       skb->truesize += new_headroom - old_headroom;
 
        /* Setup PPP header */
        __skb_push(skb, sizeof(ppph));
@@ -1058,9 +1106,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
 
        /* Get routing info from the tunnel socket */
        dst_release(skb->dst);
-       skb->dst = sk_dst_get(sk_tun);
-       skb_orphan(skb);
-       skb->sk = sk_tun;
+       skb->dst = dst_clone(__sk_dst_get(sk_tun));
+       pppol2tp_skb_set_owner_w(skb, sk_tun);
 
        /* Queue the packet to IP for output */
        len = skb->len;
@@ -1077,8 +1124,14 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
                session->stats.tx_errors++;
        }
 
+       sock_put(sk_tun);
+       sock_put(sk);
        return 1;
 
+abort_put_sess_tun:
+       sock_put(sk_tun);
+abort_put_sess:
+       sock_put(sk);
 abort:
        /* Free the original skb */
        kfree_skb(skb);
@@ -1106,10 +1159,12 @@ static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel)
        PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
               "%s: closing all sessions...\n", tunnel->name);
 
-       write_lock(&tunnel->hlist_lock);
+       write_lock_bh(&tunnel->hlist_lock);
        for (hash = 0; hash < PPPOL2TP_HASH_SIZE; hash++) {
 again:
                hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
+                       struct sk_buff *skb;
+
                        session = hlist_entry(walk, struct pppol2tp_session, hlist);
 
                        sk = session->sock;
@@ -1126,7 +1181,7 @@ again:
                         * disappear as we're jumping between locks.
                         */
                        sock_hold(sk);
-                       write_unlock(&tunnel->hlist_lock);
+                       write_unlock_bh(&tunnel->hlist_lock);
                        lock_sock(sk);
 
                        if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
@@ -1138,7 +1193,10 @@ again:
                        /* Purge any queued data */
                        skb_queue_purge(&sk->sk_receive_queue);
                        skb_queue_purge(&sk->sk_write_queue);
-                       skb_queue_purge(&session->reorder_q);
+                       while ((skb = skb_dequeue(&session->reorder_q))) {
+                               kfree_skb(skb);
+                               sock_put(sk);
+                       }
 
                        release_sock(sk);
                        sock_put(sk);
@@ -1148,11 +1206,11 @@ again:
                         * list so we are guaranteed to make forward
                         * progress.
                         */
-                       write_lock(&tunnel->hlist_lock);
+                       write_lock_bh(&tunnel->hlist_lock);
                        goto again;
                }
        }
-       write_unlock(&tunnel->hlist_lock);
+       write_unlock_bh(&tunnel->hlist_lock);
 }
 
 /* Really kill the tunnel.
@@ -1161,9 +1219,9 @@ again:
 static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel)
 {
        /* Remove from socket list */
-       write_lock(&pppol2tp_tunnel_list_lock);
+       write_lock_bh(&pppol2tp_tunnel_list_lock);
        list_del_init(&tunnel->list);
-       write_unlock(&pppol2tp_tunnel_list_lock);
+       write_unlock_bh(&pppol2tp_tunnel_list_lock);
 
        atomic_dec(&pppol2tp_tunnel_count);
        kfree(tunnel);
@@ -1177,7 +1235,7 @@ static void pppol2tp_tunnel_destruct(struct sock *sk)
 {
        struct pppol2tp_tunnel *tunnel;
 
-       tunnel = pppol2tp_sock_to_tunnel(sk);
+       tunnel = sk->sk_user_data;
        if (tunnel == NULL)
                goto end;
 
@@ -1216,10 +1274,12 @@ static void pppol2tp_session_destruct(struct sock *sk)
        if (sk->sk_user_data != NULL) {
                struct pppol2tp_tunnel *tunnel;
 
-               session = pppol2tp_sock_to_session(sk);
+               session = sk->sk_user_data;
                if (session == NULL)
                        goto out;
 
+               BUG_ON(session->magic != L2TP_SESSION_MAGIC);
+
                /* Don't use pppol2tp_sock_to_tunnel() here to
                 * get the tunnel context because the tunnel
                 * socket might have already been closed (its
@@ -1239,9 +1299,9 @@ static void pppol2tp_session_destruct(struct sock *sk)
                                /* Delete the session socket from the
                                 * hash
                                 */
-                               write_lock(&tunnel->hlist_lock);
+                               write_lock_bh(&tunnel->hlist_lock);
                                hlist_del_init(&session->hlist);
-                               write_unlock(&tunnel->hlist_lock);
+                               write_unlock_bh(&tunnel->hlist_lock);
 
                                atomic_dec(&pppol2tp_session_count);
                        }
@@ -1265,6 +1325,7 @@ out:
 static int pppol2tp_release(struct socket *sock)
 {
        struct sock *sk = sock->sk;
+       struct pppol2tp_session *session;
        int error;
 
        if (!sk)
@@ -1282,9 +1343,18 @@ static int pppol2tp_release(struct socket *sock)
        sock_orphan(sk);
        sock->sk = NULL;
 
+       session = pppol2tp_sock_to_session(sk);
+
        /* Purge any queued data */
        skb_queue_purge(&sk->sk_receive_queue);
        skb_queue_purge(&sk->sk_write_queue);
+       if (session != NULL) {
+               struct sk_buff *skb;
+               while ((skb = skb_dequeue(&session->reorder_q))) {
+                       kfree_skb(skb);
+                       sock_put(sk);
+               }
+       }
 
        release_sock(sk);
 
@@ -1386,9 +1456,9 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
 
        /* Add tunnel to our list */
        INIT_LIST_HEAD(&tunnel->list);
-       write_lock(&pppol2tp_tunnel_list_lock);
+       write_lock_bh(&pppol2tp_tunnel_list_lock);
        list_add(&tunnel->list, &pppol2tp_tunnel_list);
-       write_unlock(&pppol2tp_tunnel_list_lock);
+       write_unlock_bh(&pppol2tp_tunnel_list_lock);
        atomic_inc(&pppol2tp_tunnel_count);
 
        /* Bump the reference count. The tunnel context is deleted
@@ -1587,17 +1657,17 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 
        error = ppp_register_channel(&po->chan);
        if (error)
-               goto end;
+               goto end_put_tun;
 
        /* This is how we get the session context from the socket. */
        sk->sk_user_data = session;
 
        /* Add session to the tunnel's hash list */
-       write_lock(&tunnel->hlist_lock);
+       write_lock_bh(&tunnel->hlist_lock);
        hlist_add_head(&session->hlist,
                       pppol2tp_session_id_hash(tunnel,
                                                session->tunnel_addr.s_session));
-       write_unlock(&tunnel->hlist_lock);
+       write_unlock_bh(&tunnel->hlist_lock);
 
        atomic_inc(&pppol2tp_session_count);
 
@@ -1607,12 +1677,21 @@ out_no_ppp:
        PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
               "%s: created\n", session->name);
 
+end_put_tun:
+       sock_put(tunnel_sock);
 end:
        release_sock(sk);
 
-       if (error != 0)
-               PRINTK(session ? session->debug : -1, PPPOL2TP_MSG_CONTROL, KERN_WARNING,
-                      "%s: connect failed: %d\n", session->name, error);
+       if (error != 0) {
+               if (session)
+                       PRINTK(session->debug,
+                               PPPOL2TP_MSG_CONTROL, KERN_WARNING,
+                               "%s: connect failed: %d\n",
+                               session->name, error);
+               else
+                       PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_WARNING,
+                               "connect failed: %d\n", error);
+       }
 
        return error;
 }
@@ -1647,6 +1726,7 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
        *usockaddr_len = len;
 
        error = 0;
+       sock_put(sock->sk);
 
 end:
        return error;
@@ -1885,14 +1965,17 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
                err = -EBADF;
                tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
                if (tunnel == NULL)
-                       goto end;
+                       goto end_put_sess;
 
                err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg);
-               goto end;
+               sock_put(session->tunnel_sock);
+               goto end_put_sess;
        }
 
        err = pppol2tp_session_ioctl(session, cmd, arg);
 
+end_put_sess:
+       sock_put(sk);
 end:
        return err;
 }
@@ -2038,14 +2121,17 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
                err = -EBADF;
                tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
                if (tunnel == NULL)
-                       goto end;
+                       goto end_put_sess;
 
                err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val);
+               sock_put(session->tunnel_sock);
        } else
                err = pppol2tp_session_setsockopt(sk, session, optname, val);
 
        err = 0;
 
+end_put_sess:
+       sock_put(sk);
 end:
        return err;
 }
@@ -2160,20 +2246,24 @@ static int pppol2tp_getsockopt(struct socket *sock, int level,
                err = -EBADF;
                tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
                if (tunnel == NULL)
-                       goto end;
+                       goto end_put_sess;
 
                err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val);
+               sock_put(session->tunnel_sock);
        } else
                err = pppol2tp_session_getsockopt(sk, session, optname, &val);
 
        err = -EFAULT;
        if (put_user(len, (int __user *) optlen))
-               goto end;
+               goto end_put_sess;
 
        if (copy_to_user((void __user *) optval, &val, len))
-               goto end;
+               goto end_put_sess;
 
        err = 0;
+
+end_put_sess:
+       sock_put(sk);
 end:
        return err;
 }
@@ -2199,7 +2289,7 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str
        int next = 0;
        int i;
 
-       read_lock(&tunnel->hlist_lock);
+       read_lock_bh(&tunnel->hlist_lock);
        for (i = 0; i < PPPOL2TP_HASH_SIZE; i++) {
                hlist_for_each_entry(session, walk, &tunnel->session_hlist[i], hlist) {
                        if (curr == NULL) {
@@ -2217,7 +2307,7 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str
                }
        }
 out:
-       read_unlock(&tunnel->hlist_lock);
+       read_unlock_bh(&tunnel->hlist_lock);
        if (!found)
                session = NULL;
 
@@ -2228,13 +2318,13 @@ static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr)
 {
        struct pppol2tp_tunnel *tunnel = NULL;
 
-       read_lock(&pppol2tp_tunnel_list_lock);
+       read_lock_bh(&pppol2tp_tunnel_list_lock);
        if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) {
                goto out;
        }
        tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list);
 out:
-       read_unlock(&pppol2tp_tunnel_list_lock);
+       read_unlock_bh(&pppol2tp_tunnel_list_lock);
 
        return tunnel;
 }
@@ -2458,12 +2548,12 @@ static int __init pppol2tp_init(void)
                goto out_unregister_pppol2tp_proto;
 
 #ifdef CONFIG_PROC_FS
-       pppol2tp_proc = create_proc_entry("pppol2tp", 0, init_net.proc_net);
+       pppol2tp_proc = proc_net_fops_create(&init_net, "pppol2tp", 0,
+                                            &pppol2tp_proc_fops);
        if (!pppol2tp_proc) {
                err = -ENOMEM;
                goto out_unregister_pppox_proto;
        }
-       pppol2tp_proc->proc_fops = &pppol2tp_proc_fops;
 #endif /* CONFIG_PROC_FS */
        printk(KERN_INFO "PPPoL2TP kernel driver, %s\n",
               PPPOL2TP_DRV_VERSION);