]> err.no Git - linux-2.6/blobdiff - drivers/net/pppoe.c
Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[linux-2.6] / drivers / net / pppoe.c
index f761a9aae4c81fc10dd17852275c2c68c0036223..6f98834e6ace29bc892b54f63a03aa6ed799fd70 100644 (file)
@@ -218,17 +218,6 @@ static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp)
        return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex);
 }
 
-static inline int set_item(struct pppox_sock *po)
-{
-       int i;
-
-       write_lock_bh(&pppoe_hash_lock);
-       i = __set_item(po);
-       write_unlock_bh(&pppoe_hash_lock);
-
-       return i;
-}
-
 static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex)
 {
        struct pppox_sock *ret;
@@ -252,54 +241,53 @@ static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int
 static void pppoe_flush_dev(struct net_device *dev)
 {
        int hash;
-
        BUG_ON(dev == NULL);
 
-       read_lock_bh(&pppoe_hash_lock);
+       write_lock_bh(&pppoe_hash_lock);
        for (hash = 0; hash < PPPOE_HASH_SIZE; hash++) {
                struct pppox_sock *po = item_hash_table[hash];
 
                while (po != NULL) {
-                       if (po->pppoe_dev == dev) {
-                               struct sock *sk = sk_pppox(po);
-
-                               sock_hold(sk);
-                               po->pppoe_dev = NULL;
+                       struct sock *sk = sk_pppox(po);
+                       if (po->pppoe_dev != dev) {
+                               po = po->next;
+                               continue;
+                       }
+                       po->pppoe_dev = NULL;
+                       dev_put(dev);
 
-                               /* We hold a reference to SK, now drop the
-                                * hash table lock so that we may attempt
-                                * to lock the socket (which can sleep).
-                                */
-                               read_unlock_bh(&pppoe_hash_lock);
 
-                               lock_sock(sk);
+                       /* We always grab the socket lock, followed by the
+                        * pppoe_hash_lock, in that order.  Since we should
+                        * hold the sock lock while doing any unbinding,
+                        * we need to release the lock we're holding.
+                        * Hold a reference to the sock so it doesn't disappear
+                        * as we're jumping between locks.
+                        */
 
-                               if (sk->sk_state &
-                                   (PPPOX_CONNECTED | PPPOX_BOUND)) {
-                                       pppox_unbind_sock(sk);
-                                       dev_put(dev);
-                                       sk->sk_state = PPPOX_ZOMBIE;
-                                       sk->sk_state_change(sk);
-                               }
+                       sock_hold(sk);
 
-                               release_sock(sk);
+                       write_unlock_bh(&pppoe_hash_lock);
+                       lock_sock(sk);
 
-                               sock_put(sk);
+                       if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+                               pppox_unbind_sock(sk);
+                               sk->sk_state = PPPOX_ZOMBIE;
+                               sk->sk_state_change(sk);
+                       }
 
-                               read_lock_bh(&pppoe_hash_lock);
+                       release_sock(sk);
+                       sock_put(sk);
 
-                               /* Now restart from the beginning of this
-                                * hash chain.  We always NULL out pppoe_dev
-                                * so we are guaranteed to make forward
-                                * progress.
-                                */
-                               po = item_hash_table[hash];
-                               continue;
-                       }
-                       po = po->next;
+                       /* Restart scan at the beginning of this hash chain.
+                        * While the lock was dropped the chain contents may
+                        * have changed.
+                        */
+                       write_lock_bh(&pppoe_hash_lock);
+                       po = item_hash_table[hash];
                }
        }
-       read_unlock_bh(&pppoe_hash_lock);
+       write_unlock_bh(&pppoe_hash_lock);
 }
 
 static int pppoe_device_event(struct notifier_block *this,
@@ -515,28 +503,42 @@ static int pppoe_release(struct socket *sock)
        if (!sk)
                return 0;
 
-       if (sock_flag(sk, SOCK_DEAD))
+       lock_sock(sk);
+       if (sock_flag(sk, SOCK_DEAD)){
+               release_sock(sk);
                return -EBADF;
+       }
 
        pppox_unbind_sock(sk);
 
        /* Signal the death of the socket. */
        sk->sk_state = PPPOX_DEAD;
 
+
+       /* Write lock on hash lock protects the entire "po" struct from
+        * concurrent updates via pppoe_flush_dev. The "po" struct should
+        * be considered part of the hash table contents, thus protected
+        * by the hash table lock */
+       write_lock_bh(&pppoe_hash_lock);
+
        po = pppox_sk(sk);
        if (po->pppoe_pa.sid) {
-               delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote, po->pppoe_ifindex);
+               __delete_item(po->pppoe_pa.sid,
+                             po->pppoe_pa.remote, po->pppoe_ifindex);
        }
 
-       if (po->pppoe_dev)
+       if (po->pppoe_dev) {
                dev_put(po->pppoe_dev);
+               po->pppoe_dev = NULL;
+       }
 
-       po->pppoe_dev = NULL;
+       write_unlock_bh(&pppoe_hash_lock);
 
        sock_orphan(sk);
        sock->sk = NULL;
 
        skb_queue_purge(&sk->sk_receive_queue);
+       release_sock(sk);
        sock_put(sk);
 
        return 0;
@@ -595,14 +597,18 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
                po->pppoe_dev = dev;
                po->pppoe_ifindex = dev->ifindex;
 
-               if (!(dev->flags & IFF_UP))
+               write_lock_bh(&pppoe_hash_lock);
+               if (!(dev->flags & IFF_UP)){
+                       write_unlock_bh(&pppoe_hash_lock);
                        goto err_put;
+               }
 
                memcpy(&po->pppoe_pa,
                       &sp->sa_addr.pppoe,
                       sizeof(struct pppoe_addr));
 
-               error = set_item(po);
+               error = __set_item(po);
+               write_unlock_bh(&pppoe_hash_lock);
                if (error < 0)
                        goto err_put;