]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/sctp/socket.c
[PATCH] POLLRDHUP/EPOLLRDHUP handling for half-closed devices notifications
[linux-2.6-omap-h63xx.git] / net / sctp / socket.c
index adb5ee62c2a6f63c21b145745a566a474309072c..b6e4b89539b3178216037c53350f5591dde8a1b6 100644 (file)
@@ -63,6 +63,7 @@
 #include <linux/wait.h>
 #include <linux/time.h>
 #include <linux/ip.h>
+#include <linux/capability.h>
 #include <linux/fcntl.h>
 #include <linux/poll.h>
 #include <linux/init.h>
@@ -860,7 +861,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
                return -EFAULT;
 
        /* Alloc space for the address array in kernel memory.  */
-       kaddrs = (struct sockaddr *)kmalloc(addrs_size, GFP_KERNEL);
+       kaddrs = kmalloc(addrs_size, GFP_KERNEL);
        if (unlikely(!kaddrs))
                return -ENOMEM;
 
@@ -1150,7 +1151,7 @@ SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
                return -EFAULT;
 
        /* Alloc space for the address array in kernel memory.  */
-       kaddrs = (struct sockaddr *)kmalloc(addrs_size, GFP_KERNEL);
+       kaddrs = kmalloc(addrs_size, GFP_KERNEL);
        if (unlikely(!kaddrs))
                return -ENOMEM;
 
@@ -2214,6 +2215,109 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
        return 0;
 }
 
+/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+ *
+ *   This options will get or set the delayed ack timer.  The time is set
+ *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
+ *   endpoints default delayed ack timer value.  If the assoc_id field is
+ *   non-zero, then the set or get effects the specified association.
+ *
+ *   struct sctp_assoc_value {
+ *       sctp_assoc_t            assoc_id;
+ *       uint32_t                assoc_value;
+ *   };
+ *
+ *     assoc_id    - This parameter, indicates which association the
+ *                   user is preforming an action upon. Note that if
+ *                   this field's value is zero then the endpoints
+ *                   default value is changed (effecting future
+ *                   associations only).
+ *
+ *     assoc_value - This parameter contains the number of milliseconds
+ *                   that the user is requesting the delayed ACK timer
+ *                   be set to. Note that this value is defined in
+ *                   the standard to be between 200 and 500 milliseconds.
+ *
+ *                   Note: a value of zero will leave the value alone,
+ *                   but disable SACK delay. A non-zero value will also
+ *                   enable SACK delay.
+ */
+
+static int sctp_setsockopt_delayed_ack_time(struct sock *sk,
+                                           char __user *optval, int optlen)
+{
+       struct sctp_assoc_value  params;
+       struct sctp_transport   *trans = NULL;
+       struct sctp_association *asoc = NULL;
+       struct sctp_sock        *sp = sctp_sk(sk);
+
+       if (optlen != sizeof(struct sctp_assoc_value))
+               return - EINVAL;
+
+       if (copy_from_user(&params, optval, optlen))
+               return -EFAULT;
+
+       /* Validate value parameter. */
+       if (params.assoc_value > 500)
+               return -EINVAL;
+
+       /* Get association, if assoc_id != 0 and the socket is a one
+        * to many style socket, and an association was not found, then
+        * the id was invalid.
+        */
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (params.assoc_value) {
+               if (asoc) {
+                       asoc->sackdelay =
+                               msecs_to_jiffies(params.assoc_value);
+                       asoc->param_flags = 
+                               (asoc->param_flags & ~SPP_SACKDELAY) |
+                               SPP_SACKDELAY_ENABLE;
+               } else {
+                       sp->sackdelay = params.assoc_value;
+                       sp->param_flags = 
+                               (sp->param_flags & ~SPP_SACKDELAY) |
+                               SPP_SACKDELAY_ENABLE;
+               }
+       } else {
+               if (asoc) {
+                       asoc->param_flags = 
+                               (asoc->param_flags & ~SPP_SACKDELAY) |
+                               SPP_SACKDELAY_DISABLE;
+               } else {
+                       sp->param_flags = 
+                               (sp->param_flags & ~SPP_SACKDELAY) |
+                               SPP_SACKDELAY_DISABLE;
+               }
+       }
+
+       /* If change is for association, also apply to each transport. */
+       if (asoc) {
+               struct list_head *pos;
+
+               list_for_each(pos, &asoc->peer.transport_addr_list) {
+                       trans = list_entry(pos, struct sctp_transport,
+                                          transports);
+                       if (params.assoc_value) {
+                               trans->sackdelay =
+                                       msecs_to_jiffies(params.assoc_value);
+                               trans->param_flags = 
+                                       (trans->param_flags & ~SPP_SACKDELAY) |
+                                       SPP_SACKDELAY_ENABLE;
+                       } else {
+                               trans->param_flags = 
+                                       (trans->param_flags & ~SPP_SACKDELAY) |
+                                       SPP_SACKDELAY_DISABLE;
+                       }
+               }
+       }
+       return 0;
+}
+
 /* 7.1.3 Initialization Parameters (SCTP_INITMSG)
  *
  * Applications can specify protocol parameters for the default association
@@ -2660,6 +2764,10 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
                retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
                break;
 
+       case SCTP_DELAYED_ACK_TIME:
+               retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen);
+               break;
+
        case SCTP_INITMSG:
                retval = sctp_setsockopt_initmsg(sk, optval, optlen);
                break;
@@ -2887,7 +2995,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->hbinterval  = jiffies_to_msecs(sctp_hb_interval);
        sp->pathmaxrxt  = sctp_max_retrans_path;
        sp->pathmtu     = 0; // allow default discovery
-       sp->sackdelay   = sctp_sack_timeout;
+       sp->sackdelay   = jiffies_to_msecs(sctp_sack_timeout);
        sp->param_flags = SPP_HB_ENABLE |
                          SPP_PMTUD_ENABLE |
                          SPP_SACKDELAY_ENABLE;
@@ -3417,6 +3525,79 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
        return 0;
 }
 
+/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+ *
+ *   This options will get or set the delayed ack timer.  The time is set
+ *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
+ *   endpoints default delayed ack timer value.  If the assoc_id field is
+ *   non-zero, then the set or get effects the specified association.
+ *
+ *   struct sctp_assoc_value {
+ *       sctp_assoc_t            assoc_id;
+ *       uint32_t                assoc_value;
+ *   };
+ *
+ *     assoc_id    - This parameter, indicates which association the
+ *                   user is preforming an action upon. Note that if
+ *                   this field's value is zero then the endpoints
+ *                   default value is changed (effecting future
+ *                   associations only).
+ *
+ *     assoc_value - This parameter contains the number of milliseconds
+ *                   that the user is requesting the delayed ACK timer
+ *                   be set to. Note that this value is defined in
+ *                   the standard to be between 200 and 500 milliseconds.
+ *
+ *                   Note: a value of zero will leave the value alone,
+ *                   but disable SACK delay. A non-zero value will also
+ *                   enable SACK delay.
+ */
+static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
+                                           char __user *optval,
+                                           int __user *optlen)
+{
+       struct sctp_assoc_value  params;
+       struct sctp_association *asoc = NULL;
+       struct sctp_sock        *sp = sctp_sk(sk);
+
+       if (len != sizeof(struct sctp_assoc_value))
+               return - EINVAL;
+
+       if (copy_from_user(&params, optval, len))
+               return -EFAULT;
+
+       /* Get association, if assoc_id != 0 and the socket is a one
+        * to many style socket, and an association was not found, then
+        * the id was invalid.
+        */
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (asoc) {
+               /* Fetch association values. */
+               if (asoc->param_flags & SPP_SACKDELAY_ENABLE)
+                       params.assoc_value = jiffies_to_msecs(
+                               asoc->sackdelay);
+               else
+                       params.assoc_value = 0;
+       } else {
+               /* Fetch socket values. */
+               if (sp->param_flags & SPP_SACKDELAY_ENABLE)
+                       params.assoc_value  = sp->sackdelay;
+               else
+                       params.assoc_value  = 0;
+       }
+
+       if (copy_to_user(optval, &params, len))
+               return -EFAULT;
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       return 0;
+}
+
 /* 7.1.3 Initialization Parameters (SCTP_INITMSG)
  *
  * Applications can specify protocol parameters for the default association
@@ -4274,6 +4455,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
                retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
                                                          optlen);
                break;
+       case SCTP_DELAYED_ACK_TIME:
+               retval = sctp_getsockopt_delayed_ack_time(sk, len, optval,
+                                                         optlen);
+               break;
        case SCTP_INITMSG:
                retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
                break;
@@ -4709,6 +4894,8 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
        /* Is there any exceptional events?  */
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
                mask |= POLLERR;
+       if (sk->sk_shutdown & RCV_SHUTDOWN)
+               mask |= POLLRDHUP;
        if (sk->sk_shutdown == SHUTDOWN_MASK)
                mask |= POLLHUP;
 
@@ -5241,7 +5428,7 @@ out:
        return err;
 
 do_error:
-       if (asoc->init_err_counter + 1 >= asoc->max_init_attempts)
+       if (asoc->init_err_counter + 1 > asoc->max_init_attempts)
                err = -ETIMEDOUT;
        else
                err = -ECONNREFUSED;
@@ -5417,8 +5604,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
         */
        newsp->type = type;
 
+       spin_lock_bh(&oldsk->sk_lock.slock);
+       /* Migrate the backlog from oldsk to newsk. */
+       sctp_backlog_migrate(assoc, oldsk, newsk);
        /* Migrate the association to the new socket. */
        sctp_assoc_migrate(assoc, newsk);
+       spin_unlock_bh(&oldsk->sk_lock.slock);
 
        /* If the association on the newsk is already closed before accept()
         * is called, set RCV_SHUTDOWN flag.