]> err.no Git - linux-2.6/blobdiff - net/dccp/proto.c
[PATCH] blk: Use blk_queue_xxx functions to set parameters
[linux-2.6] / net / dccp / proto.c
index a3f8a8095f81fd835c73a30c290d4f5440c9c0cf..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;
@@ -466,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,