[SCTP]: Rewrite of sctp buffer management code
[linux-2.6.git] / net / sctp / socket.c
index 9f1a908..b995242 100644 (file)
@@ -108,24 +108,41 @@ static void sctp_sock_migrate(struct sock *, struct sock *,
 static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
 
 extern struct kmem_cache *sctp_bucket_cachep;
+extern int sysctl_sctp_mem[3];
+extern int sysctl_sctp_rmem[3];
+extern int sysctl_sctp_wmem[3];
+
+int sctp_memory_pressure;
+atomic_t sctp_memory_allocated;
+atomic_t sctp_sockets_allocated;
+
+static void sctp_enter_memory_pressure(void)
+{
+       sctp_memory_pressure = 1;
+}
+
 
 /* Get the sndbuf space available at the time on the association.  */
 static inline int sctp_wspace(struct sctp_association *asoc)
 {
-       struct sock *sk = asoc->base.sk;
-       int amt = 0;
+       int amt;
 
-       if (asoc->ep->sndbuf_policy) {
-               /* make sure that no association uses more than sk_sndbuf */
-               amt = sk->sk_sndbuf - asoc->sndbuf_used;
+       if (asoc->ep->sndbuf_policy)
+               amt = asoc->sndbuf_used;
+       else
+               amt = atomic_read(&asoc->base.sk->sk_wmem_alloc);
+
+       if (amt >= asoc->base.sk->sk_sndbuf) {
+               if (asoc->base.sk->sk_userlocks & SOCK_SNDBUF_LOCK)
+                       amt = 0;
+               else {
+                       amt = sk_stream_wspace(asoc->base.sk);
+                       if (amt < 0)
+                               amt = 0;
+               }
        } else {
-               /* do socket level accounting */
-               amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
+               amt = asoc->base.sk->sk_sndbuf - amt;
        }
-
-       if (amt < 0)
-               amt = 0;
-
        return amt;
 }
 
@@ -157,6 +174,7 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
                                sizeof(struct sctp_chunk);
 
        atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
+       sk_charge_skb(sk, chunk->skb);
 }
 
 /* Verify that this is a valid address. */
@@ -333,12 +351,19 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        if (!sp->pf->bind_verify(sp, addr))
                return -EADDRNOTAVAIL;
 
-       /* We must either be unbound, or bind to the same port.  */
-       if (bp->port && (snum != bp->port)) {
-               SCTP_DEBUG_PRINTK("sctp_do_bind:"
+       /* We must either be unbound, or bind to the same port.
+        * It's OK to allow 0 ports if we are already bound.
+        * We'll just inhert an already bound port in this case
+        */
+       if (bp->port) {
+               if (!snum)
+                       snum = bp->port;
+               else if (snum != bp->port) {
+                       SCTP_DEBUG_PRINTK("sctp_do_bind:"
                                  " New port %d does not match existing port "
                                  "%d.\n", snum, bp->port);
-               return -EINVAL;
+                       return -EINVAL;
+               }
        }
 
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
@@ -348,6 +373,7 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
         * The function sctp_get_port_local() does duplicate address
         * detection.
         */
+       addr->v4.sin_port = htons(snum);
        if ((ret = sctp_get_port_local(sk, addr))) {
                if (ret == (long) sk) {
                        /* This endpoint has a conflicting address. */
@@ -361,14 +387,10 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        if (!bp->port)
                bp->port = inet_sk(sk)->num;
 
-       /* Add the address to the bind address list.  */
-       sctp_local_bh_disable();
-       sctp_write_lock(&ep->base.addr_lock);
-
-       /* Use GFP_ATOMIC since BHs are disabled.  */
+       /* Add the address to the bind address list.
+        * Use GFP_ATOMIC since BHs will be disabled.
+        */
        ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC);
-       sctp_write_unlock(&ep->base.addr_lock);
-       sctp_local_bh_enable();
 
        /* Copy back into socket for getsockname() use. */
        if (!ret) {
@@ -426,7 +448,7 @@ out:
  *
  * Only sctp_setsockopt_bindx() is supposed to call this function.
  */
-int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
+static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
 {
        int cnt;
        int retval = 0;
@@ -538,15 +560,12 @@ static int sctp_send_asconf_add_ip(struct sock            *sk,
                if (i < addrcnt)
                        continue;
 
-               /* Use the first address in bind addr list of association as
-                * Address Parameter of ASCONF CHUNK.
+               /* Use the first valid address in bind addr list of
+                * association as Address Parameter of ASCONF CHUNK.
                 */
-               sctp_read_lock(&asoc->base.addr_lock);
                bp = &asoc->base.bind_addr;
                p = bp->address_list.next;
                laddr = list_entry(p, struct sctp_sockaddr_entry, list);
-               sctp_read_unlock(&asoc->base.addr_lock);
-
                chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
                                                   addrcnt, SCTP_PARAM_ADD_IP);
                if (!chunk) {
@@ -561,8 +580,6 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                /* Add the new addresses to the bind address list with
                 * use_as_src set to 0.
                 */
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
                        addr = (union sctp_addr *)addr_buf;
@@ -572,8 +589,6 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                                                    GFP_ATOMIC);
                        addr_buf += af->sockaddr_len;
                }
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
        }
 
 out:
@@ -595,7 +610,7 @@ out:
  *
  * Only sctp_setsockopt_bindx() is supposed to call this function.
  */
-int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
+static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_endpoint *ep = sp->ep;
@@ -645,13 +660,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                 * socket routing and failover schemes. Refer to comments in
                 * sctp_do_bind(). -daisy
                 */
-               sctp_local_bh_disable();
-               sctp_write_lock(&ep->base.addr_lock);
-
-               retval = sctp_del_bind_addr(bp, sa_addr);
-
-               sctp_write_unlock(&ep->base.addr_lock);
-               sctp_local_bh_enable();
+               retval = sctp_del_bind_addr(bp, sa_addr, call_rcu);
 
                addr_buf += af->sockaddr_len;
 err_bindx_rem:
@@ -742,14 +751,16 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                 * make sure that we do not delete all the addresses in the
                 * association.
                 */
-               sctp_read_lock(&asoc->base.addr_lock);
                bp = &asoc->base.bind_addr;
                laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
                                               addrcnt, sp);
-               sctp_read_unlock(&asoc->base.addr_lock);
                if (!laddr)
                        continue;
 
+               /* We do not need RCU protection throughout this loop
+                * because this is done under a socket lock from the
+                * setsockopt call.
+                */
                chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
                                                   SCTP_PARAM_DEL_IP);
                if (!chunk) {
@@ -760,23 +771,16 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                /* Reset use_as_src flag for the addresses in the bind address
                 * list that are to be deleted.
                 */
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
                        laddr = (union sctp_addr *)addr_buf;
                        af = sctp_get_af_specific(laddr->v4.sin_family);
-                       list_for_each(pos1, &bp->address_list) {
-                               saddr = list_entry(pos1,
-                                                  struct sctp_sockaddr_entry,
-                                                  list);
+                       list_for_each_entry(saddr, &bp->address_list, list) {
                                if (sctp_cmp_addr_exact(&saddr->a, laddr))
                                        saddr->use_as_src = 0;
                        }
                        addr_buf += af->sockaddr_len;
                }
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
 
                /* Update the route and saddr entries for all the transports
                 * as some of the addresses in the bind address list are
@@ -970,9 +974,10 @@ static int __sctp_connect(struct sock* sk,
        int err = 0;
        int addrcnt = 0;
        int walk_size = 0;
-       union sctp_addr *sa_addr;
+       union sctp_addr *sa_addr = NULL;
        void *addr_buf;
        unsigned short port;
+       unsigned int f_flags = 0;
 
        sp = sctp_sk(sk);
        ep = sp->ep;
@@ -1003,7 +1008,10 @@ static int __sctp_connect(struct sock* sk,
                        goto out_free;
                }
 
-               err = sctp_verify_addr(sk, sa_addr, af->sockaddr_len);
+               /* Save current address so we can work with it */
+               memcpy(&to, sa_addr, af->sockaddr_len);
+
+               err = sctp_verify_addr(sk, &to, af->sockaddr_len);
                if (err)
                        goto out_free;
 
@@ -1013,12 +1021,11 @@ static int __sctp_connect(struct sock* sk,
                if (asoc && asoc->peer.port && asoc->peer.port != port)
                        goto out_free;
 
-               memcpy(&to, sa_addr, af->sockaddr_len);
 
                /* Check if there already is a matching association on the
                 * endpoint (other than the one created here).
                 */
-               asoc2 = sctp_endpoint_lookup_assoc(ep, sa_addr, &transport);
+               asoc2 = sctp_endpoint_lookup_assoc(ep, &to, &transport);
                if (asoc2 && asoc2 != asoc) {
                        if (asoc2->state >= SCTP_STATE_ESTABLISHED)
                                err = -EISCONN;
@@ -1031,7 +1038,7 @@ static int __sctp_connect(struct sock* sk,
                 * make sure that there is no peeled-off association matching
                 * the peer address even on another socket.
                 */
-               if (sctp_endpoint_is_peeled_off(ep, sa_addr)) {
+               if (sctp_endpoint_is_peeled_off(ep, &to)) {
                        err = -EADDRNOTAVAIL;
                        goto out_free;
                }
@@ -1062,7 +1069,7 @@ static int __sctp_connect(struct sock* sk,
                                }
                        }
 
-                       scope = sctp_scope(sa_addr);
+                       scope = sctp_scope(&to);
                        asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
                        if (!asoc) {
                                err = -ENOMEM;
@@ -1071,7 +1078,7 @@ static int __sctp_connect(struct sock* sk,
                }
 
                /* Prime the peer's transport structures.  */
-               transport = sctp_assoc_add_peer(asoc, sa_addr, GFP_KERNEL,
+               transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL,
                                                SCTP_UNKNOWN);
                if (!transport) {
                        err = -ENOMEM;
@@ -1095,11 +1102,18 @@ static int __sctp_connect(struct sock* sk,
 
        /* Initialize sk's dport and daddr for getpeername() */
        inet_sk(sk)->dport = htons(asoc->peer.port);
-       af = sctp_get_af_specific(to.sa.sa_family);
-       af->to_sk_daddr(&to, sk);
+       af = sctp_get_af_specific(sa_addr->sa.sa_family);
+       af->to_sk_daddr(sa_addr, sk);
        sk->sk_err = 0;
 
-       timeo = sock_sndtimeo(sk, sk->sk_socket->file->f_flags & O_NONBLOCK);
+       /* in-kernel sockets don't generally have a file allocated to them
+        * if all they do is call sock_create_kern().
+        */
+       if (sk->sk_socket->file)
+               f_flags = sk->sk_socket->file->f_flags;
+
+       timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
+
        err = sctp_wait_for_connect(asoc, &timeo);
 
        /* Don't free association on exit. */
@@ -1516,7 +1530,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                        goto out_unlock;
                }
                if (sinfo_flags & SCTP_ABORT) {
-                       struct sctp_chunk *chunk;
 
                        chunk = sctp_make_abort_user(asoc, msg, msg_len);
                        if (!chunk) {
@@ -1655,6 +1668,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                goto out_free;
        }
 
+       if (asoc->pmtu_pending)
+               sctp_assoc_pending_pmtu(asoc);
+
        /* If fragmentation is disabled and the message length exceeds the
         * association fragmentation point, return EMSGSIZE.  The I-D
         * does not specify what this error is, but this looks like
@@ -2586,7 +2602,7 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int opt
  *
  * 7.1.2 SCTP_ASSOCINFO
  *
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
  * of the association.
  * Returns an error if the new association retransmission value is
  * greater than the sum of the retransmission value  of the peer.
@@ -3297,6 +3313,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->hmac = NULL;
 
        SCTP_DBG_OBJCNT_INC(sock);
+       atomic_inc(&sctp_sockets_allocated);
        return 0;
 }
 
@@ -3310,7 +3327,7 @@ SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
        /* Release our hold on the endpoint. */
        ep = sctp_sk(sk)->ep;
        sctp_endpoint_free(ep);
-
+       atomic_dec(&sctp_sockets_allocated);
        return 0;
 }
 
@@ -3365,12 +3382,13 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
        sctp_assoc_t associd;
        int retval = 0;
 
-       if (len != sizeof(status)) {
+       if (len < sizeof(status)) {
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&status, optval, sizeof(status))) {
+       len = sizeof(status);
+       if (copy_from_user(&status, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
@@ -3442,12 +3460,13 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
        struct sctp_transport *transport;
        int retval = 0;
 
-       if (len != sizeof(pinfo)) {
+       if (len < sizeof(pinfo)) {
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+       len = sizeof(pinfo);
+       if (copy_from_user(&pinfo, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
@@ -3513,8 +3532,11 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
-       if (len != sizeof(struct sctp_event_subscribe))
+       if (len < sizeof(struct sctp_event_subscribe))
                return -EINVAL;
+       len = sizeof(struct sctp_event_subscribe);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
                return -EFAULT;
        return 0;
@@ -3536,9 +3558,12 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
        /* Applicable to UDP-style socket only */
        if (sctp_style(sk, TCP))
                return -EOPNOTSUPP;
-       if (len != sizeof(int))
+       if (len < sizeof(int))
                return -EINVAL;
-       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+       len = sizeof(int);
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
                return -EFAULT;
        return 0;
 }
@@ -3550,6 +3575,7 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        struct sock *sk = asoc->base.sk;
        struct socket *sock;
        struct inet_sock *inetsk;
+       struct sctp_af *af;
        int err = 0;
 
        /* An association cannot be branched off from an already peeled-off
@@ -3571,8 +3597,9 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        /* Make peeled-off sockets more like 1-1 accepted sockets.
         * Set the daddr and initialize id to something more random
         */
+       af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
+       af->to_sk_daddr(&asoc->peer.primary_addr, sk);
        inetsk = inet_sk(sock->sk);
-       inetsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
        inetsk->id = asoc->next_tsn ^ jiffies;
 
        *sockp = sock;
@@ -3587,8 +3614,9 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
        int retval = 0;
        struct sctp_association *asoc;
 
-       if (len != sizeof(sctp_peeloff_arg_t))
+       if (len < sizeof(sctp_peeloff_arg_t))
                return -EINVAL;
+       len = sizeof(sctp_peeloff_arg_t);
        if (copy_from_user(&peeloff, optval, len))
                return -EFAULT;
 
@@ -3616,6 +3644,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
 
        /* Return the fd mapped to the new socket.  */
        peeloff.sd = retval;
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &peeloff, len))
                retval = -EFAULT;
 
@@ -3724,9 +3754,9 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_paddrparams))
+       if (len < sizeof(struct sctp_paddrparams))
                return -EINVAL;
-
+       len = sizeof(struct sctp_paddrparams);
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3825,9 +3855,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return - EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3876,8 +3908,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
  */
 static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
 {
-       if (len != sizeof(struct sctp_initmsg))
+       if (len < sizeof(struct sctp_initmsg))
                return -EINVAL;
+       len = sizeof(struct sctp_initmsg);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
                return -EFAULT;
        return 0;
@@ -3892,7 +3927,7 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -3928,10 +3963,12 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3954,7 +3991,9 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
                if (cnt >= getaddrs.addr_num) break;
        }
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &getaddrs, len))
                return -EFAULT;
 
        return 0;
@@ -3987,8 +4026,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
                return -EINVAL;
 
        to = optval + offsetof(struct sctp_getaddrs,addrs);
-       space_left = len - sizeof(struct sctp_getaddrs) -
-                       offsetof(struct sctp_getaddrs,addrs);
+       space_left = len - offsetof(struct sctp_getaddrs,addrs);
 
        list_for_each(pos, &asoc->peer.transport_addr_list) {
                from = list_entry(pos, struct sctp_transport, transports);
@@ -4020,12 +4058,10 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
        sctp_assoc_t id;
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos, *temp;
        struct sctp_sockaddr_entry *addr;
-       rwlock_t *addr_lock;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -4039,17 +4075,13 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
         */
        if (0 == id) {
                bp = &sctp_sk(sk)->ep->base.bind_addr;
-               addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
        } else {
                asoc = sctp_id2assoc(sk, id);
                if (!asoc)
                        return -EINVAL;
                bp = &asoc->base.bind_addr;
-               addr_lock = &asoc->base.addr_lock;
        }
 
-       sctp_read_lock(addr_lock);
-
        /* If the endpoint is bound to 0.0.0.0 or ::0, count the valid
         * addresses from the global local address list.
         */
@@ -4057,27 +4089,33 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
                addr = list_entry(bp->address_list.next,
                                  struct sctp_sockaddr_entry, list);
                if (sctp_is_any(&addr->a)) {
-                       list_for_each_safe(pos, temp, &sctp_local_addr_list) {
-                               addr = list_entry(pos,
-                                                 struct sctp_sockaddr_entry,
-                                                 list);
+                       rcu_read_lock();
+                       list_for_each_entry_rcu(addr,
+                                               &sctp_local_addr_list, list) {
+                               if (!addr->valid)
+                                       continue;
+
                                if ((PF_INET == sk->sk_family) &&
                                    (AF_INET6 == addr->a.sa.sa_family))
                                        continue;
+
                                cnt++;
                        }
+                       rcu_read_unlock();
                } else {
                        cnt = 1;
                }
                goto done;
        }
 
-       list_for_each(pos, &bp->address_list) {
+       /* Protection on the bound address list is not needed,
+        * since in the socket option context we hold the socket lock,
+        * so there is no way that the bound address list can change.
+        */
+       list_for_each_entry(addr, &bp->address_list, list) {
                cnt ++;
        }
-
 done:
-       sctp_read_unlock(addr_lock);
        return cnt;
 }
 
@@ -4088,14 +4126,16 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
                                        int max_addrs, void *to,
                                        int *bytes_copied)
 {
-       struct list_head *pos, *next;
        struct sctp_sockaddr_entry *addr;
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
 
-       list_for_each_safe(pos, next, &sctp_local_addr_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+       rcu_read_lock();
+       list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
+               if (!addr->valid)
+                       continue;
+
                if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
@@ -4110,6 +4150,7 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
                cnt ++;
                if (cnt >= max_addrs) break;
        }
+       rcu_read_unlock();
 
        return cnt;
 }
@@ -4117,14 +4158,16 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
 static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
                            size_t space_left, int *bytes_copied)
 {
-       struct list_head *pos, *next;
        struct sctp_sockaddr_entry *addr;
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
 
-       list_for_each_safe(pos, next, &sctp_local_addr_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+       rcu_read_lock();
+       list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
+               if (!addr->valid)
+                       continue;
+
                if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
@@ -4132,15 +4175,18 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
                                                                &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               if (space_left < addrlen)
-                       return -ENOMEM;
+               if (space_left < addrlen) {
+                       cnt =  -ENOMEM;
+                       break;
+               }
                memcpy(to, &temp, addrlen);
 
                to += addrlen;
                cnt ++;
                space_left -= addrlen;
-               bytes_copied += addrlen;
+               *bytes_copied += addrlen;
        }
+       rcu_read_unlock();
 
        return cnt;
 }
@@ -4153,7 +4199,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
 {
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos;
        int cnt = 0;
        struct sctp_getaddrs_old getaddrs;
        struct sctp_sockaddr_entry *addr;
@@ -4161,15 +4206,16 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
        union sctp_addr temp;
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
-       rwlock_t *addr_lock;
        int err = 0;
        void *addrs;
+       void *buf;
        int bytes_copied = 0;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -4181,13 +4227,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
         */
        if (0 == getaddrs.assoc_id) {
                bp = &sctp_sk(sk)->ep->base.bind_addr;
-               addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
        } else {
                asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
                if (!asoc)
                        return -EINVAL;
                bp = &asoc->base.bind_addr;
-               addr_lock = &asoc->base.addr_lock;
        }
 
        to = getaddrs.addrs;
@@ -4201,8 +4245,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
        if (!addrs)
                return -ENOMEM;
 
-       sctp_read_lock(addr_lock);
-
        /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
         * addresses from the global local address list.
         */
@@ -4217,21 +4259,23 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
                }
        }
 
-       list_for_each(pos, &bp->address_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+       buf = addrs;
+       /* Protection on the bound address list is not needed since
+        * in the socket option context we hold a socket lock and
+        * thus the bound address list can't change.
+        */
+       list_for_each_entry(addr, &bp->address_list, list) {
                memcpy(&temp, &addr->a, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               memcpy(addrs, &temp, addrlen);
-               to += addrlen;
+               memcpy(buf, &temp, addrlen);
+               buf += addrlen;
                bytes_copied += addrlen;
                cnt ++;
                if (cnt >= getaddrs.addr_num) break;
        }
 
 copy_getaddrs:
-       sctp_read_unlock(addr_lock);
-
        /* copy the entire address list into the user provided space */
        if (copy_to_user(to, addrs, bytes_copied)) {
                err = -EFAULT;
@@ -4240,7 +4284,7 @@ copy_getaddrs:
 
        /* copy the leading structure back to user */
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (copy_to_user(optval, &getaddrs, len))
                err = -EFAULT;
 
 error:
@@ -4253,7 +4297,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
 {
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos;
        int cnt = 0;
        struct sctp_getaddrs getaddrs;
        struct sctp_sockaddr_entry *addr;
@@ -4261,13 +4304,13 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
        union sctp_addr temp;
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
-       rwlock_t *addr_lock;
        int err = 0;
        size_t space_left;
        int bytes_copied = 0;
        void *addrs;
+       void *buf;
 
-       if (len <= sizeof(struct sctp_getaddrs))
+       if (len < sizeof(struct sctp_getaddrs))
                return -EINVAL;
 
        if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
@@ -4281,24 +4324,20 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
         */
        if (0 == getaddrs.assoc_id) {
                bp = &sctp_sk(sk)->ep->base.bind_addr;
-               addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
        } else {
                asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
                if (!asoc)
                        return -EINVAL;
                bp = &asoc->base.bind_addr;
-               addr_lock = &asoc->base.addr_lock;
        }
 
        to = optval + offsetof(struct sctp_getaddrs,addrs);
-       space_left = len - sizeof(struct sctp_getaddrs) -
-                        offsetof(struct sctp_getaddrs,addrs);
+       space_left = len - offsetof(struct sctp_getaddrs,addrs);
+
        addrs = kmalloc(space_left, GFP_KERNEL);
        if (!addrs)
                return -ENOMEM;
 
-       sctp_read_lock(addr_lock);
-
        /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
         * addresses from the global local address list.
         */
@@ -4310,41 +4349,44 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
                                                space_left, &bytes_copied);
                        if (cnt < 0) {
                                err = cnt;
-                               goto error;
+                               goto out;
                        }
                        goto copy_getaddrs;
                }
        }
 
-       list_for_each(pos, &bp->address_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+       buf = addrs;
+       /* Protection on the bound address list is not needed since
+        * in the socket option context we hold a socket lock and
+        * thus the bound address list can't change.
+        */
+       list_for_each_entry(addr, &bp->address_list, list) {
                memcpy(&temp, &addr->a, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
                if (space_left < addrlen) {
                        err =  -ENOMEM; /*fixme: right error?*/
-                       goto error;
+                       goto out;
                }
-               memcpy(addrs, &temp, addrlen);
-               to += addrlen;
+               memcpy(buf, &temp, addrlen);
+               buf += addrlen;
                bytes_copied += addrlen;
                cnt ++;
                space_left -= addrlen;
        }
 
 copy_getaddrs:
-       sctp_read_unlock(addr_lock);
-
        if (copy_to_user(to, addrs, bytes_copied)) {
                err = -EFAULT;
-               goto error;
+               goto out;
+       }
+       if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
+               err = -EFAULT;
+               goto out;
        }
-       if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
-               return -EFAULT;
        if (put_user(bytes_copied, optlen))
-               return -EFAULT;
-
-error:
+               err = -EFAULT;
+out:
        kfree(addrs);
        return err;
 }
@@ -4362,10 +4404,12 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_prim))
+       if (len < sizeof(struct sctp_prim))
                return -EINVAL;
 
-       if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
+       len = sizeof(struct sctp_prim);
+
+       if (copy_from_user(&prim, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
@@ -4381,7 +4425,9 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
                        (union sctp_addr *)&prim.ssp_addr);
 
-       if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &prim, len))
                return -EFAULT;
 
        return 0;
@@ -4398,10 +4444,15 @@ static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,
 {
        struct sctp_setadaptation adaptation;
 
-       if (len != sizeof(struct sctp_setadaptation))
+       if (len < sizeof(struct sctp_setadaptation))
                return -EINVAL;
 
+       len = sizeof(struct sctp_setadaptation);
+
        adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &adaptation, len))
                return -EFAULT;
 
@@ -4435,9 +4486,12 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_sndrcvinfo))
+       if (len < sizeof(struct sctp_sndrcvinfo))
                return -EINVAL;
-       if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo)))
+
+       len = sizeof(struct sctp_sndrcvinfo);
+
+       if (copy_from_user(&info, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
@@ -4458,7 +4512,9 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
                info.sinfo_timetolive = sp->default_timetolive;
        }
 
-       if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &info, len))
                return -EFAULT;
 
        return 0;
@@ -4509,10 +4565,12 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
        struct sctp_rtoinfo rtoinfo;
        struct sctp_association *asoc;
 
-       if (len != sizeof (struct sctp_rtoinfo))
+       if (len < sizeof (struct sctp_rtoinfo))
                return -EINVAL;
 
-       if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo)))
+       len = sizeof(struct sctp_rtoinfo);
+
+       if (copy_from_user(&rtoinfo, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
@@ -4547,7 +4605,7 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
  *
  * 7.1.2 SCTP_ASSOCINFO
  *
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
  * of the association.
  * Returns an error if the new association retransmission value is
  * greater than the sum of the retransmission value  of the peer.
@@ -4564,11 +4622,12 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof (struct sctp_assocparams))
+       if (len < sizeof (struct sctp_assocparams))
                return -EINVAL;
 
-       if (copy_from_user(&assocparams, optval,
-                       sizeof (struct sctp_assocparams)))
+       len = sizeof(struct sctp_assocparams);
+
+       if (copy_from_user(&assocparams, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
@@ -4654,9 +4713,11 @@ static int sctp_getsockopt_context(struct sock *sk, int len,
        struct sctp_sock *sp;
        struct sctp_association *asoc;
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return -EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -4739,7 +4800,7 @@ static int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len,
                                                  char __user *optval,
                                                  int __user *optlen)
 {
-        u32 val;
+       u32 val;
 
        if (len < sizeof(u32))
                return -EINVAL;
@@ -4763,7 +4824,7 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len,
                                    char __user *optval,
                                    int __user *optlen)
 {
-        int val;
+       int val;
 
        if (len < sizeof(int))
                return -EINVAL;
@@ -5133,6 +5194,7 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
 
                sctp_unhash_endpoint(ep);
                sk->sk_state = SCTP_SS_CLOSED;
+               return 0;
        }
 
        /* Return if we are already listening. */
@@ -5180,6 +5242,7 @@ SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
 
                sctp_unhash_endpoint(ep);
                sk->sk_state = SCTP_SS_CLOSED;
+               return 0;
        }
 
        if (sctp_sstate(sk, LISTENING))
@@ -5227,7 +5290,12 @@ int sctp_inet_listen(struct socket *sock, int backlog)
        /* Allocate HMAC for generating cookie. */
        if (sctp_hmac_alg) {
                tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
-               if (!tfm) {
+               if (IS_ERR(tfm)) {
+                       if (net_ratelimit()) {
+                               printk(KERN_INFO
+                                      "SCTP: failed to load transform for %s: %ld\n",
+                                       sctp_hmac_alg, PTR_ERR(tfm));
+                       }
                        err = -ENOSYS;
                        goto out;
                }
@@ -5673,6 +5741,12 @@ static void sctp_wfree(struct sk_buff *skb)
 
        atomic_sub(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
 
+       /*
+        * This undoes what is done via sk_charge_skb
+        */
+       sk->sk_wmem_queued   -= skb->truesize;
+       sk->sk_forward_alloc += skb->truesize;
+
        sock_wfree(skb);
        __sctp_write_space(asoc);
 
@@ -5690,6 +5764,11 @@ void sctp_sock_rfree(struct sk_buff *skb)
        struct sctp_ulpevent *event = sctp_skb2event(skb);
 
        atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);
+
+       /*
+        * Mimic the behavior of sk_stream_rfree
+        */
+       sk->sk_forward_alloc += event->rmem_len;
 }
 
 
@@ -5895,7 +5974,7 @@ static int sctp_wait_for_accept(struct sock *sk, long timeo)
        return err;
 }
 
-void sctp_wait_for_close(struct sock *sk, long timeout)
+static void sctp_wait_for_close(struct sock *sk, long timeout)
 {
        DEFINE_WAIT(wait);
 
@@ -6062,8 +6141,11 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
         * queued to the backlog.  This prevents a potential race between
         * backlog processing on the old socket and new-packet processing
         * on the new socket.
+        *
+        * The caller has just allocated newsk so we can guarantee that other
+        * paths won't try to lock it and then oldsk.
         */
-       sctp_lock_sock(newsk);
+       lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
        sctp_assoc_migrate(assoc, newsk);
 
        /* If the association on the newsk is already closed before accept()
@@ -6076,6 +6158,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        sctp_release_sock(newsk);
 }
 
+
 /* This proto struct describes the ULP interface for SCTP.  */
 struct proto sctp_prot = {
        .name        =  "SCTP",
@@ -6098,6 +6181,12 @@ struct proto sctp_prot = {
        .unhash      =  sctp_unhash,
        .get_port    =  sctp_get_port,
        .obj_size    =  sizeof(struct sctp_sock),
+       .sysctl_mem  =  sysctl_sctp_mem,
+       .sysctl_rmem =  sysctl_sctp_rmem,
+       .sysctl_wmem =  sysctl_sctp_wmem,
+       .memory_pressure = &sctp_memory_pressure,
+       .enter_memory_pressure = sctp_enter_memory_pressure,
+       .memory_allocated = &sctp_memory_allocated,
 };
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -6122,5 +6211,11 @@ struct proto sctpv6_prot = {
        .unhash         = sctp_unhash,
        .get_port       = sctp_get_port,
        .obj_size       = sizeof(struct sctp6_sock),
+       .sysctl_mem     = sysctl_sctp_mem,
+       .sysctl_rmem    = sysctl_sctp_rmem,
+       .sysctl_wmem    = sysctl_sctp_wmem,
+       .memory_pressure = &sctp_memory_pressure,
+       .enter_memory_pressure = sctp_enter_memory_pressure,
+       .memory_allocated = &sctp_memory_allocated,
 };
 #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */