]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sdp: BUG2141 - fix refcnt bug
authorEldad Zinger <eldadz@mellanox.co.il>
Wed, 6 Oct 2010 09:52:10 +0000 (11:52 +0200)
committerMukesh Kacker <mukesh.kacker@oracle.com>
Tue, 6 Oct 2015 12:05:25 +0000 (05:05 -0700)
This bug is reproducable when sdpnetstat(1) is run while a socket is being
destructed. sdp_proc utilities might try to hold a refcnt for a socket that its
destruction already began. This is possible because sdp_proc utilities uses
sock_list to scan for sockets, but a socket is removed from that list only
after all its resourcs are freed.

Signed-off-by: Eldad Zinger <eldadz@mellanox.co.il>
drivers/infiniband/ulp/sdp/sdp_proc.c

index 99006dd7e7278a0ff426399b6edadda9ed284df0..c00093b8b82792649e06007cc20d0f3d0431fc10 100644 (file)
@@ -69,6 +69,14 @@ static void *sdp_get_idx(struct seq_file *seq, loff_t pos)
        return NULL;
 }
 
+#define sdp_sock_hold_return(sk, msg)                                  \
+       ({                                                              \
+       _sdp_add_to_history(sk, #msg, __func__, __LINE__, HOLD_REF, msg); \
+       sdp_dbg(sk, "%s:%d - %s (%s) ref = %d.\n", __func__, __LINE__, \
+               "sock_hold", #msg, atomic_read(&(sk)->sk_refcnt)); \
+       atomic_inc_return(&(sk)->sk_refcnt);                            \
+       })
+
 static void *sdp_seq_start(struct seq_file *seq, loff_t *pos)
 {
        void *start = NULL;
@@ -81,8 +89,12 @@ static void *sdp_seq_start(struct seq_file *seq, loff_t *pos)
 
        spin_lock_irq(&sock_list_lock);
        start = sdp_get_idx(seq, *pos - 1);
-       if (start)
-               sock_hold((struct sock *)start, SOCK_REF_SEQ);
+       if (!start)
+               goto out;
+
+       if (sdp_sock_hold_return((struct sock *)start, SOCK_REF_SEQ) < 2)
+               start = NULL;
+out:
        spin_unlock_irq(&sock_list_lock);
 
        return start;
@@ -98,10 +110,13 @@ static void *sdp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
                next = sdp_get_idx(seq, 0);
        else
                next = sdp_get_idx(seq, *pos);
-       if (next)
-               sock_hold((struct sock *)next, SOCK_REF_SEQ);
-       spin_unlock_irq(&sock_list_lock);
+       if (!next)
+               goto out;
 
+       if (sdp_sock_hold_return((struct sock *)next, SOCK_REF_SEQ) < 2)
+               next = NULL;
+out:
+       spin_unlock_irq(&sock_list_lock);
        *pos += 1;
        st->num++;