]> err.no Git - linux-2.6/blobdiff - net/dccp/proto.c
[IPV6]: Don't redo xfrm_lookup for cached dst entries
[linux-2.6] / net / dccp / proto.c
index 8b613c3017c5e4e2a56e4a51e7d1226c97877f7e..18a0e69c9dc75f709e15be01e7ef6def17de3eb5 100644 (file)
@@ -39,7 +39,7 @@
 #include "ccid.h"
 #include "dccp.h"
 
-DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics);
+DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
 
 atomic_t dccp_orphan_count = ATOMIC_INIT(0);
 
@@ -140,6 +140,62 @@ int dccp_disconnect(struct sock *sk, int flags)
        return err;
 }
 
+/*
+ *     Wait for a DCCP event.
+ *
+ *     Note that we don't need to lock the socket, as the upper poll layers
+ *     take care of normal races (between the test and the event) and we don't
+ *     go look at any of the socket buffers directly.
+ */
+static unsigned int dccp_poll(struct file *file, struct socket *sock,
+                             poll_table *wait)
+{
+       unsigned int mask;
+       struct sock *sk = sock->sk;
+
+       poll_wait(file, sk->sk_sleep, wait);
+       if (sk->sk_state == DCCP_LISTEN)
+               return inet_csk_listen_poll(sk);
+
+       /* Socket is not locked. We are protected from async events
+          by poll logic and correct handling of state changes
+          made by another threads is impossible in any case.
+        */
+
+       mask = 0;
+       if (sk->sk_err)
+               mask = POLLERR;
+
+       if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
+               mask |= POLLHUP;
+       if (sk->sk_shutdown & RCV_SHUTDOWN)
+               mask |= POLLIN | POLLRDNORM;
+
+       /* Connected? */
+       if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
+               if (atomic_read(&sk->sk_rmem_alloc) > 0)
+                       mask |= POLLIN | POLLRDNORM;
+
+               if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
+                       if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
+                               mask |= POLLOUT | POLLWRNORM;
+                       } else {  /* send SIGIO later */
+                               set_bit(SOCK_ASYNC_NOSPACE,
+                                       &sk->sk_socket->flags);
+                               set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+
+                               /* Race breaker. If space is freed after
+                                * wspace test but before the flags are set,
+                                * IO signal will be lost.
+                                */
+                               if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
+                                       mask |= POLLOUT | POLLWRNORM;
+                       }
+               }
+       }
+       return mask;
+}
+
 int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
        dccp_pr_debug("entry\n");
@@ -149,23 +205,67 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 int dccp_setsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int optlen)
 {
-       dccp_pr_debug("entry\n");
+       struct dccp_sock *dp;
+       int err;
+       int val;
 
        if (level != SOL_DCCP)
                return ip_setsockopt(sk, level, optname, optval, optlen);
 
-       return -EOPNOTSUPP;
+       if (optlen < sizeof(int))
+               return -EINVAL;
+
+       if (get_user(val, (int __user *)optval))
+               return -EFAULT;
+
+       lock_sock(sk);
+
+       dp = dccp_sk(sk);
+       err = 0;
+
+       switch (optname) {
+       case DCCP_SOCKOPT_PACKET_SIZE:
+               dp->dccps_packet_size = val;
+               break;
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+       
+       release_sock(sk);
+       return err;
 }
 
 int dccp_getsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int __user *optlen)
 {
-       dccp_pr_debug("entry\n");
+       struct dccp_sock *dp;
+       int val, len;
 
        if (level != SOL_DCCP)
                return ip_getsockopt(sk, level, optname, optval, optlen);
 
-       return -EOPNOTSUPP;
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       len = min_t(unsigned int, len, sizeof(int));
+       if (len < 0)
+               return -EINVAL;
+
+       dp = dccp_sk(sk);
+
+       switch (optname) {
+       case DCCP_SOCKOPT_PACKET_SIZE:
+               val = dp->dccps_packet_size;
+               break;
+       default:
+               return -ENOPROTOOPT;
+       }
+
+       if (put_user(len, optlen) || copy_to_user(optval, &val, len))
+               return -EFAULT;
+
+       return 0;
 }
 
 int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
@@ -205,7 +305,19 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (rc != 0)
                goto out_discard;
 
-       rc = dccp_write_xmit(sk, skb, len);
+       rc = dccp_write_xmit(sk, skb, &timeo);
+       /*
+        * XXX we don't use sk_write_queue, so just discard the packet.
+        *     Current plan however is to _use_ sk_write_queue with
+        *     an algorith similar to tcp_sendmsg, where the main difference
+        *     is that in DCCP we have to respect packet boundaries, so
+        *     no coalescing of skbs.
+        *
+        *     This bug was _quickly_ found & fixed by just looking at an OSTRA
+        *     generated callgraph 8) -acme
+        */
+       if (rc != 0)
+               goto out_discard;
 out_release:
        release_sock(sk);
        return rc ? : len;
@@ -402,12 +514,15 @@ void dccp_close(struct sock *sk, long timeout)
                /* Check zero linger _after_ checking for unread data. */
                sk->sk_prot->disconnect(sk, 0);
        } else if (dccp_close_state(sk)) {
-               dccp_send_close(sk);
+               dccp_send_close(sk, 1);
        }
 
        sk_stream_wait_close(sk, timeout);
 
 adjudge_to_death:
+       /*
+        * It is the last release_sock in its life. It will remove backlog.
+        */
        release_sock(sk);
        /*
         * Now socket is owned by kernel and we acquire BH lock
@@ -419,11 +534,26 @@ adjudge_to_death:
 
        sock_hold(sk);
        sock_orphan(sk);
-                                               
-       if (sk->sk_state != DCCP_CLOSED)
+
+       /*
+        * The last release_sock may have processed the CLOSE or RESET
+        * packet moving sock to CLOSED state, if not we have to fire
+        * the CLOSE/CLOSEREQ retransmission timer, see "8.3. Termination"
+        * in draft-ietf-dccp-spec-11. -acme
+        */
+       if (sk->sk_state == DCCP_CLOSING) {
+               /* FIXME: should start at 2 * RTT */
+               /* Timer for repeating the CLOSE/CLOSEREQ until an answer. */
+               inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
+                                         inet_csk(sk)->icsk_rto,
+                                         DCCP_RTO_MAX);
+#if 0
+               /* Yeah, we should use sk->sk_prot->orphan_count, etc */
                dccp_set_state(sk, DCCP_CLOSED);
+#endif
+       }
 
-       atomic_inc(&dccp_orphan_count);
+       atomic_inc(sk->sk_prot->orphan_count);
        if (sk->sk_state == DCCP_CLOSED)
                inet_csk_destroy_sock(sk);
 
@@ -448,7 +578,8 @@ static struct proto_ops inet_dccp_ops = {
        .socketpair     = sock_no_socketpair,
        .accept         = inet_accept,
        .getname        = inet_getname,
-       .poll           = sock_no_poll,
+       /* FIXME: work on tcp_poll to rename it to inet_csk_poll */
+       .poll           = dccp_poll,
        .ioctl          = inet_ioctl,
        /* FIXME: work on inet_listen to rename it to sock_common_listen */
        .listen         = inet_dccp_listen,