]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/dccp/proto.c
Merge master.kernel.org:/home/rmk/linux-2.6-serial
[linux-2.6-omap-h63xx.git] / net / dccp / proto.c
index 46dd489f66cf2f4de32a422943314c3be199d273..65b11ea90d858818db105a4855b2a285aacf7ab2 100644 (file)
@@ -24,7 +24,7 @@
 #include <net/checksum.h>
 
 #include <net/inet_common.h>
-#include <net/ip.h>
+#include <net/inet_sock.h>
 #include <net/protocol.h>
 #include <net/sock.h>
 #include <net/xfrm.h>
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include <linux/poll.h>
-#include <linux/dccp.h>
 
 #include "ccid.h"
 #include "dccp.h"
 
-DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics);
+DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
+
+EXPORT_SYMBOL_GPL(dccp_statistics);
 
 atomic_t dccp_orphan_count = ATOMIC_INIT(0);
 
+EXPORT_SYMBOL_GPL(dccp_orphan_count);
+
 static struct net_protocol dccp_protocol = {
        .handler        = dccp_v4_rcv,
        .err_handler    = dccp_v4_err,
+       .no_policy      = 1,
 };
 
 const char *dccp_packet_name(const int type)
@@ -94,7 +98,15 @@ EXPORT_SYMBOL_GPL(dccp_state_name);
 
 static inline int dccp_listen_start(struct sock *sk)
 {
-       dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN;
+       struct dccp_sock *dp = dccp_sk(sk);
+
+       dp->dccps_role = DCCP_ROLE_LISTEN;
+       /*
+        * Apps need to use setsockopt(DCCP_SOCKOPT_SERVICE)
+        * before calling listen()
+        */
+       if (dccp_service_not_initialized(sk))
+               return -EPROTO;
        return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE);
 }
 
@@ -140,34 +152,224 @@ int dccp_disconnect(struct sock *sk, int flags)
        return err;
 }
 
+EXPORT_SYMBOL_GPL(dccp_disconnect);
+
+/*
+ *     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.
+ */
+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;
+}
+
+EXPORT_SYMBOL_GPL(dccp_poll);
+
 int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
        dccp_pr_debug("entry\n");
        return -ENOIOCTLCMD;
 }
 
+EXPORT_SYMBOL_GPL(dccp_ioctl);
+
+static int dccp_setsockopt_service(struct sock *sk, const u32 service,
+                                  char __user *optval, int optlen)
+{
+       struct dccp_sock *dp = dccp_sk(sk);
+       struct dccp_service_list *sl = NULL;
+
+       if (service == DCCP_SERVICE_INVALID_VALUE || 
+           optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
+               return -EINVAL;
+
+       if (optlen > sizeof(service)) {
+               sl = kmalloc(optlen, GFP_KERNEL);
+               if (sl == NULL)
+                       return -ENOMEM;
+
+               sl->dccpsl_nr = optlen / sizeof(u32) - 1;
+               if (copy_from_user(sl->dccpsl_list,
+                                  optval + sizeof(service),
+                                  optlen - sizeof(service)) ||
+                   dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
+                       kfree(sl);
+                       return -EFAULT;
+               }
+       }
+
+       lock_sock(sk);
+       dp->dccps_service = service;
+
+       kfree(dp->dccps_service_list);
+
+       dp->dccps_service_list = sl;
+       release_sock(sk);
+       return 0;
+}
+
 int dccp_setsockopt(struct sock *sk, int level, int optname,
-                   char *optval, int optlen)
+                   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 inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
+                                                            optname, optval,
+                                                            optlen);
+
+       if (optlen < sizeof(int))
+               return -EINVAL;
+
+       if (get_user(val, (int __user *)optval))
+               return -EFAULT;
+
+       if (optname == DCCP_SOCKOPT_SERVICE)
+               return dccp_setsockopt_service(sk, val, optval, optlen);
+
+       lock_sock(sk);
+       dp = dccp_sk(sk);
+       err = 0;
 
-       return -EOPNOTSUPP;
+       switch (optname) {
+       case DCCP_SOCKOPT_PACKET_SIZE:
+               dp->dccps_packet_size = val;
+               break;
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+       
+       release_sock(sk);
+       return err;
+}
+
+EXPORT_SYMBOL_GPL(dccp_setsockopt);
+
+static int dccp_getsockopt_service(struct sock *sk, int len,
+                                  u32 __user *optval,
+                                  int __user *optlen)
+{
+       const struct dccp_sock *dp = dccp_sk(sk);
+       const struct dccp_service_list *sl;
+       int err = -ENOENT, slen = 0, total_len = sizeof(u32);
+
+       lock_sock(sk);
+       if (dccp_service_not_initialized(sk))
+               goto out;
+
+       if ((sl = dp->dccps_service_list) != NULL) {
+               slen = sl->dccpsl_nr * sizeof(u32);
+               total_len += slen;
+       }
+
+       err = -EINVAL;
+       if (total_len > len)
+               goto out;
+
+       err = 0;
+       if (put_user(total_len, optlen) ||
+           put_user(dp->dccps_service, optval) ||
+           (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
+               err = -EFAULT;
+out:
+       release_sock(sk);
+       return err;
 }
 
 int dccp_getsockopt(struct sock *sk, int level, int optname,
-                   char *optval, int *optlen)
+                   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 inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
+                                                            optname, optval,
+                                                            optlen);
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       if (len < sizeof(int))
+               return -EINVAL;
+
+       dp = dccp_sk(sk);
+
+       switch (optname) {
+       case DCCP_SOCKOPT_PACKET_SIZE:
+               val = dp->dccps_packet_size;
+               len = sizeof(dp->dccps_packet_size);
+               break;
+       case DCCP_SOCKOPT_SERVICE:
+               return dccp_getsockopt_service(sk, len,
+                                              (u32 __user *)optval, optlen);
+       case 128 ... 191:
+               return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
+                                            len, (u32 __user *)optval, optlen);
+       case 192 ... 255:
+               return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
+                                            len, (u32 __user *)optval, optlen);
+       default:
+               return -ENOPROTOOPT;
+       }
+
+       if (put_user(len, optlen) || copy_to_user(optval, &val, len))
+               return -EFAULT;
 
-       return -EOPNOTSUPP;
+       return 0;
 }
 
+EXPORT_SYMBOL_GPL(dccp_getsockopt);
+
 int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                 size_t len)
 {
@@ -205,7 +407,17 @@ 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
+        */
 out_release:
        release_sock(sk);
        return rc ? : len;
@@ -214,200 +426,108 @@ out_discard:
        goto out_release;
 }
 
-EXPORT_SYMBOL(dccp_sendmsg);
+EXPORT_SYMBOL_GPL(dccp_sendmsg);
 
 int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                 size_t len, int nonblock, int flags, int *addr_len)
 {
        const struct dccp_hdr *dh;
-       int copied = 0;
-       unsigned long used;
-       int err;
-       int target;             /* Read at least this many bytes */
        long timeo;
 
        lock_sock(sk);
 
-       err = -ENOTCONN;
-       if (sk->sk_state == DCCP_LISTEN)
+       if (sk->sk_state == DCCP_LISTEN) {
+               len = -ENOTCONN;
                goto out;
-
-       timeo = sock_rcvtimeo(sk, nonblock);
-
-       /* Urgent data needs to be handled specially. */
-       if (flags & MSG_OOB)
-               goto recv_urg;
-
-       /* FIXME */
-#if 0
-       seq = &tp->copied_seq;
-       if (flags & MSG_PEEK) {
-               peek_seq = tp->copied_seq;
-               seq = &peek_seq;
        }
-#endif
 
-       target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
+       timeo = sock_rcvtimeo(sk, nonblock);
 
        do {
-               struct sk_buff *skb;
-               u32 offset;
+               struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
 
-       /* FIXME */
-#if 0
-               /*
-                * Are we at urgent data? Stop if we have read anything or
-                * have SIGURG pending.
-                */
-               if (tp->urg_data && tp->urg_seq == *seq) {
-                       if (copied)
-                               break;
-                       if (signal_pending(current)) {
-                               copied = timeo ? sock_intr_errno(timeo) :
-                                                -EAGAIN;
-                               break;
-                       }
-               }
-#endif
-
-               /* Next get a buffer. */
+               if (skb == NULL)
+                       goto verify_sock_status;
 
-               skb = skb_peek(&sk->sk_receive_queue);
-               do {
-                       if (!skb)
-                               break;
+               dh = dccp_hdr(skb);
 
-                       offset = 0;
-                       dh = dccp_hdr(skb);
+               if (dh->dccph_type == DCCP_PKT_DATA ||
+                   dh->dccph_type == DCCP_PKT_DATAACK)
+                       goto found_ok_skb;
 
-                       if (dh->dccph_type == DCCP_PKT_DATA ||
-                           dh->dccph_type == DCCP_PKT_DATAACK)
-                               goto found_ok_skb;
-
-                       if (dh->dccph_type == DCCP_PKT_RESET ||
-                           dh->dccph_type == DCCP_PKT_CLOSE) {
-                               dccp_pr_debug("found fin ok!\n");
-                               goto found_fin_ok;
-                       }
-                       dccp_pr_debug("packet_type=%s\n",
-                                     dccp_packet_name(dh->dccph_type));
-                       BUG_TRAP(flags & MSG_PEEK);
-                       skb = skb->next;
-               } while (skb != (struct sk_buff *)&sk->sk_receive_queue);
-
-               /* Well, if we have backlog, try to process it now yet. */
-               if (copied >= target && !sk->sk_backlog.tail)
+               if (dh->dccph_type == DCCP_PKT_RESET ||
+                   dh->dccph_type == DCCP_PKT_CLOSE) {
+                       dccp_pr_debug("found fin ok!\n");
+                       len = 0;
+                       goto found_fin_ok;
+               }
+               dccp_pr_debug("packet_type=%s\n",
+                             dccp_packet_name(dh->dccph_type));
+               sk_eat_skb(sk, skb);
+verify_sock_status:
+               if (sock_flag(sk, SOCK_DONE)) {
+                       len = 0;
                        break;
+               }
 
-               if (copied) {
-                       if (sk->sk_err ||
-                           sk->sk_state == DCCP_CLOSED ||
-                           (sk->sk_shutdown & RCV_SHUTDOWN) ||
-                           !timeo ||
-                           signal_pending(current) ||
-                           (flags & MSG_PEEK))
-                               break;
-               } else {
-                       if (sock_flag(sk, SOCK_DONE))
-                               break;
-
-                       if (sk->sk_err) {
-                               copied = sock_error(sk);
-                               break;
-                       }
-
-                       if (sk->sk_shutdown & RCV_SHUTDOWN)
-                               break;
-
-                       if (sk->sk_state == DCCP_CLOSED) {
-                               if (!sock_flag(sk, SOCK_DONE)) {
-                                       /* This occurs when user tries to read
-                                        * from never connected socket.
-                                        */
-                                       copied = -ENOTCONN;
-                                       break;
-                               }
-                               break;
-                       }
+               if (sk->sk_err) {
+                       len = sock_error(sk);
+                       break;
+               }
 
-                       if (!timeo) {
-                               copied = -EAGAIN;
-                               break;
-                       }
+               if (sk->sk_shutdown & RCV_SHUTDOWN) {
+                       len = 0;
+                       break;
+               }
 
-                       if (signal_pending(current)) {
-                               copied = sock_intr_errno(timeo);
+               if (sk->sk_state == DCCP_CLOSED) {
+                       if (!sock_flag(sk, SOCK_DONE)) {
+                               /* This occurs when user tries to read
+                                * from never connected socket.
+                                */
+                               len = -ENOTCONN;
                                break;
                        }
+                       len = 0;
+                       break;
                }
 
-               /* FIXME: cleanup_rbuf(sk, copied); */
+               if (!timeo) {
+                       len = -EAGAIN;
+                       break;
+               }
 
-               if (copied >= target) {
-                       /* Do not sleep, just process backlog. */
-                       release_sock(sk);
-                       lock_sock(sk);
-               } else
-                       sk_wait_data(sk, &timeo);
+               if (signal_pending(current)) {
+                       len = sock_intr_errno(timeo);
+                       break;
+               }
 
+               sk_wait_data(sk, &timeo);
                continue;
-
        found_ok_skb:
-               /* Ok so how much can we use? */
-               used = skb->len - offset;
-               if (len < used)
-                       used = len;
-
-               if (!(flags & MSG_TRUNC)) {
-                       err = skb_copy_datagram_iovec(skb, offset,
-                                                     msg->msg_iov, used);
-                       if (err) {
-                               /* Exception. Bailout! */
-                               if (!copied)
-                                       copied = -EFAULT;
-                               break;
-                       }
+               if (len > skb->len)
+                       len = skb->len;
+               else if (len < skb->len)
+                       msg->msg_flags |= MSG_TRUNC;
+
+               if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
+                       /* Exception. Bailout! */
+                       len = -EFAULT;
+                       break;
                }
-
-               copied += used;
-               len -= used;
-
-               /* FIXME: tcp_rcv_space_adjust(sk); */
-
-//skip_copy:
-               if (used + offset < skb->len)
-                       continue;
-
-               if (!(flags & MSG_PEEK))
-                       sk_eat_skb(sk, skb);
-               continue;
        found_fin_ok:
                if (!(flags & MSG_PEEK))
                        sk_eat_skb(sk, skb);
                break;
-               
-       } while (len > 0);
-
-       /* According to UNIX98, msg_name/msg_namelen are ignored
-        * on connected socket. I was just happy when found this 8) --ANK
-        */
-
-       /* Clean up data we have read: This will do ACK frames. */
-       /* FIXME: cleanup_rbuf(sk, copied); */
-
-       release_sock(sk);
-       return copied;
-
+       } while (1);
 out:
        release_sock(sk);
-       return err;
-
-recv_urg:
-       /* FIXME: err = tcp_recv_urg(sk, timeo, msg, len, flags, addr_len); */
-       goto out;
+       return len;
 }
 
-static int inet_dccp_listen(struct socket *sock, int backlog)
+EXPORT_SYMBOL_GPL(dccp_recvmsg);
+
+int inet_dccp_listen(struct socket *sock, int backlog)
 {
        struct sock *sk = sock->sk;
        unsigned char old_state;
@@ -443,6 +563,8 @@ out:
        return err;
 }
 
+EXPORT_SYMBOL_GPL(inet_dccp_listen);
+
 static const unsigned char dccp_new_state[] = {
        /* current state:   new state:      action:     */
        [0]               = DCCP_CLOSED,
@@ -498,12 +620,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
@@ -515,11 +640,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);
 
@@ -530,12 +670,16 @@ adjudge_to_death:
        sock_put(sk);
 }
 
+EXPORT_SYMBOL_GPL(dccp_close);
+
 void dccp_shutdown(struct sock *sk, int how)
 {
        dccp_pr_debug("entry\n");
 }
 
-struct proto_ops inet_dccp_ops = {
+EXPORT_SYMBOL_GPL(dccp_shutdown);
+
+static const struct proto_ops inet_dccp_ops = {
        .family         = PF_INET,
        .owner          = THIS_MODULE,
        .release        = inet_release,
@@ -544,7 +688,8 @@ 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,
@@ -562,11 +707,11 @@ extern struct net_proto_family inet_family_ops;
 static struct inet_protosw dccp_v4_protosw = {
        .type           = SOCK_DCCP,
        .protocol       = IPPROTO_DCCP,
-       .prot           = &dccp_v4_prot,
+       .prot           = &dccp_prot,
        .ops            = &inet_dccp_ops,
        .capability     = -1,
        .no_check       = 0,
-       .flags          = 0,
+       .flags          = INET_PROTOSW_ICSK,
 };
 
 /*
@@ -599,12 +744,18 @@ static int __init dccp_ctl_sock_init(void)
        return rc;
 }
 
-static void __exit dccp_ctl_sock_exit(void)
+#ifdef CONFIG_IP_DCCP_UNLOAD_HACK
+void dccp_ctl_sock_exit(void)
 {
-       if (dccp_ctl_socket != NULL)
+       if (dccp_ctl_socket != NULL) {
                sock_release(dccp_ctl_socket);
+               dccp_ctl_socket = NULL;
+       }
 }
 
+EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit);
+#endif
+
 static int __init init_dccp_v4_mibs(void)
 {
        int rc = -ENOMEM;
@@ -631,15 +782,19 @@ static int thash_entries;
 module_param(thash_entries, int, 0444);
 MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
 
+#ifdef CONFIG_IP_DCCP_DEBUG
 int dccp_debug;
 module_param(dccp_debug, int, 0444);
 MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
 
+EXPORT_SYMBOL_GPL(dccp_debug);
+#endif
+
 static int __init dccp_init(void)
 {
        unsigned long goal;
        int ehash_order, bhash_order, i;
-       int rc = proto_register(&dccp_v4_prot, 1);
+       int rc = proto_register(&dccp_prot, 1);
 
        if (rc)
                goto out;
@@ -742,7 +897,7 @@ out_free_bind_bucket_cachep:
        kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
        dccp_hashinfo.bind_bucket_cachep = NULL;
 out_proto_unregister:
-       proto_unregister(&dccp_v4_prot);
+       proto_unregister(&dccp_prot);
        goto out;
 }
 
@@ -751,19 +906,21 @@ static const char dccp_del_proto_err_msg[] __exitdata =
 
 static void __exit dccp_fini(void)
 {
-       dccp_ctl_sock_exit();
-
        inet_unregister_protosw(&dccp_v4_protosw);
 
        if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0)
                printk(dccp_del_proto_err_msg);
 
-       /* Free the control endpoint.  */
-       sock_release(dccp_ctl_socket);
-
-       proto_unregister(&dccp_v4_prot);
-
+       free_percpu(dccp_statistics[0]);
+       free_percpu(dccp_statistics[1]);
+       free_pages((unsigned long)dccp_hashinfo.bhash,
+                  get_order(dccp_hashinfo.bhash_size *
+                            sizeof(struct inet_bind_hashbucket)));
+       free_pages((unsigned long)dccp_hashinfo.ehash,
+                  get_order(dccp_hashinfo.ehash_size *
+                            sizeof(struct inet_ehash_bucket)));
        kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
+       proto_unregister(&dccp_prot);
 }
 
 module_init(dccp_init);