diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/include/linux/sockios.h linux-2.6.15.4-tcpldc/include/linux/sockios.h
--- linux-2.6.15.4/include/linux/sockios.h	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/include/linux/sockios.h	2006-03-30 10:21:54.000000000 +0200
@@ -83,6 +83,8 @@
 
 #define SIOCWANDEV	0x894A		/* get/set netdev parameters	*/
 
+#define SIOCTCPLDCNTF   0x894B          /* notify a waiting tcp-ldc socket */
+
 /* ARP cache control calls. */
 		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
 #define SIOCDARP	0x8953		/* delete ARP table entry	*/
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/include/linux/tcp.h linux-2.6.15.4-tcpldc/include/linux/tcp.h
--- linux-2.6.15.4/include/linux/tcp.h	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/include/linux/tcp.h	2006-03-16 15:03:38.000000000 +0100
@@ -110,6 +110,7 @@ enum { 
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_LDC			14	/* Late data choice */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/include/net/sock.h linux-2.6.15.4-tcpldc/include/net/sock.h
--- linux-2.6.15.4/include/net/sock.h	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/include/net/sock.h	2006-04-27 13:58:56.000000000 +0200
@@ -54,6 +54,7 @@
 #include <asm/atomic.h>
 #include <net/dst.h>
 #include <net/checksum.h>
+#include <net/tcp_ldc.h>       /* TCP Late Data Choice stuff */
 
 /*
  * This structure really needs to be cleaned up.
@@ -251,6 +252,7 @@ struct sock {
   	int			(*sk_backlog_rcv)(struct sock *sk,
 						  struct sk_buff *skb);  
 	void                    (*sk_destruct)(struct sock *sk);
+	struct ldc_packet_ring  *ldc_ring;
 };
 
 /*
@@ -385,6 +387,7 @@ enum sock_flags {
 	SOCK_NO_LARGESEND, /* whether to sent large segments or not */
 	SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */
 	SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */
+	SOCK_LDC, /* Late Data Choice */
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/include/net/tcp_ldc.h linux-2.6.15.4-tcpldc/include/net/tcp_ldc.h
--- linux-2.6.15.4/include/net/tcp_ldc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15.4-tcpldc/include/net/tcp_ldc.h	2006-05-13 15:51:40.000000000 +0200
@@ -0,0 +1,59 @@
+/*
+ * Definitions for the TCP_LDC module.
+ */
+
+#ifndef _TCP_LDC_H
+#define _TCP_LDC_H
+
+
+struct ldc_packet {
+    int user_seq;
+    unsigned int kern_flag : 2;
+    unsigned int user_flag : 2;
+    int size;                   //Packet size
+    char *data;  /* Pointer to the data in USER SPACE */
+    char *kdata; /* Pointer to the data in KERNEL SPACE */
+    int offset;  /* Data between data and (data + offset) has been put
+		  * in skb(s). Data between (data + offset) and (data +
+		  * size) has not 
+		  */
+};
+
+struct ldc_ring_head {
+    int kern_i;
+    int kern_flag;
+
+    int umod_i;
+    int user_i;
+    int ring_size; /* How many packets can fit in the buffer */
+    int packet_size; /* How big can a packet be */
+};
+
+
+struct ldc_packet_ring {
+    struct ldc_ring_head *ring_head;
+    struct ldc_packet    *packet_ring;
+
+    void                 *ldc_pkt_buffer_uaddr;  /* Buffer addr in user space */
+    void                 *ldc_pkt_buffer;
+    int                   ldc_pkt_size;    /* aligned size */
+};
+
+
+/* Macros */
+
+#define LDC_NEXT(rh, p) ((rh->p + 1) % rh->ring_size)
+#define LDC_PREV(rh, p) ((rh->p + rh->ring_size - 1) % rh->ring_size)
+
+#define LDC_INCP(rh, p) (rh->p = LDC_NEXT(rh, p))
+#define LDC_DECP(rh, p) (rh->p = LDC_PREV(rh, p))
+
+#define LDC_MOVE_BACKWARDS(rh, p, l) rh->p = ((rh->p + rh->ring_size - l) % rh->ring_size)
+#define LDC_MOVE_FORWARD(rh, p, l) rh->p = ((rh->p + l) % rh->ring_size)
+
+#define LDC_BUFFER_NOT_FULL(rh) (LDC_NEXT(rh, user_i) != rh->kern_i)
+#define LDC_BUFFER_NOT_EMPTY(rh) (rh->kern_i != rh->umod_i)
+
+#define LDC_READY_IN_BUFFER(rh) ((rh->umod_i >= rh->kern_i) ? (rh->umod_i - rh->kern_i) : (rh->ring_size + rh->umod_i - rh->kern_i))
+
+#endif /* _TCP_LDC_H */
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/kernel/fork.c linux-2.6.15.4-tcpldc/kernel/fork.c
--- linux-2.6.15.4/kernel/fork.c	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/kernel/fork.c	2006-05-01 01:38:02.000000000 +0200
@@ -96,6 +96,7 @@ kmem_cache_t *fs_cachep;
 
 /* SLAB cache for vm_area_struct structures */
 kmem_cache_t *vm_area_cachep;
+EXPORT_SYMBOL(vm_area_cachep); /* Need export since TCP_LDC is a module */
 
 /* SLAB cache for mm_struct structures (tsk->mm) */
 static kmem_cache_t *mm_cachep;
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/mm/mmap.c linux-2.6.15.4-tcpldc/mm/mmap.c
--- linux-2.6.15.4/mm/mmap.c	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/mm/mmap.c	2006-05-01 01:35:18.000000000 +0200
@@ -861,6 +861,7 @@ void vm_stat_account(struct mm_struct *m
 	if (flags & (VM_RESERVED|VM_IO))
 		mm->reserved_vm += pages;
 }
+EXPORT_SYMBOL(vm_stat_account); /* Need export since TCP_LDC is a module */
 #endif /* CONFIG_PROC_FS */
 
 /*
@@ -1990,6 +1991,7 @@ int insert_vm_struct(struct mm_struct * 
 	vma_link(mm, vma, prev, rb_link, rb_parent);
 	return 0;
 }
+EXPORT_SYMBOL(insert_vm_struct); /* Need export since TCP_LDC is a module */
 
 /*
  * Copy the vma structure to a new location in the same mm,
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/net/ipv4/Makefile linux-2.6.15.4-tcpldc/net/ipv4/Makefile
--- linux-2.6.15.4/net/ipv4/Makefile	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/net/ipv4/Makefile	2006-04-27 12:23:53.000000000 +0200
@@ -43,3 +43,6 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_s
 
 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
 		      xfrm4_output.o
+
+# TCP Late data choice
+obj-m += tcp_ldc.o
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/net/ipv4/tcp.c linux-2.6.15.4-tcpldc/net/ipv4/tcp.c
--- linux-2.6.15.4/net/ipv4/tcp.c	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/net/ipv4/tcp.c	2006-05-10 17:02:45.000000000 +0200
@@ -401,6 +401,9 @@ unsigned int tcp_poll(struct file *file,
 	return mask;
 }
 
+
+int (*ldc_notify)(struct sock *sk);
+EXPORT_SYMBOL(ldc_notify);
 int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
@@ -440,6 +443,9 @@ int tcp_ioctl(struct sock *sk, int cmd, 
 		else
 			answ = tp->write_seq - tp->snd_una;
 		break;
+	case SIOCTCPLDCNTF:
+		answ = (*ldc_notify)(sk);
+		break;
 	default:
 		return -ENOIOCTLCMD;
 	};
@@ -452,11 +458,13 @@ static inline void tcp_mark_push(struct 
 	TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
 	tp->pushed_seq = tp->write_seq;
 }
+EXPORT_SYMBOL(tcp_mark_push);
 
 static inline int forced_push(struct tcp_sock *tp)
 {
 	return after(tp->write_seq, tp->pushed_seq + (tp->max_window >> 1));
 }
+EXPORT_SYMBOL(forced_push);
 
 static inline void skb_entail(struct sock *sk, struct tcp_sock *tp,
 			      struct sk_buff *skb)
@@ -474,6 +482,7 @@ static inline void skb_entail(struct soc
 	if (tp->nonagle & TCP_NAGLE_PUSH)
 		tp->nonagle &= ~TCP_NAGLE_PUSH; 
 }
+EXPORT_SYMBOL(skb_entail);
 
 static inline void tcp_mark_urg(struct tcp_sock *tp, int flags,
 				struct sk_buff *skb)
@@ -497,6 +506,7 @@ static inline void tcp_push(struct sock 
 					  (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle);
 	}
 }
+EXPORT_SYMBOL(tcp_push);
 
 static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset,
 			 size_t psize, int flags)
@@ -658,10 +668,14 @@ static inline int select_size(struct soc
 
 	return tmp;
 }
+EXPORT_SYMBOL(select_size);
 
 int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 		size_t size)
 {
+    if(sock_flag(sk, SOCK_LDC)) 
+	return (*ldc_notify)(sk);
+    
 	struct iovec *iov;
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct sk_buff *skb;
@@ -1684,6 +1698,14 @@ int tcp_disconnect(struct sock *sk, int 
 	return err;
 }
 
+
+
+/* This will be hijacked by the
+ * tcp_ldc module.
+ */
+int (*tcp_set_ldc)(struct sock *sk, char __user *optval, int optlen);
+EXPORT_SYMBOL(tcp_set_ldc);
+
 /*
  *	Socket option code for TCP.
  */
@@ -1699,6 +1721,19 @@ int tcp_setsockopt(struct sock *sk, int 
 		return tp->af_specific->setsockopt(sk, level, optname,
 						   optval, optlen);
 
+	/* TCP Late data choice */
+	if (optname == TCP_LDC) {
+		/* Test of automatic loading of late data choice module */
+		request_module("tcp_ldc");
+
+		lock_sock(sk);
+		err = (*tcp_set_ldc)(sk, optval, optlen);
+		if(!err)
+		    sock_set_flag(sk, SOCK_LDC);
+		release_sock(sk);
+		return err;
+	}
+
 	/* This is a string value all the others are int's */
 	if (optname == TCP_CONGESTION) {
 		char name[TCP_CA_NAME_MAX];
@@ -2014,6 +2049,13 @@ int tcp_getsockopt(struct sock *sk, int 
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+	case TCP_LDC:
+		len = sizeof(void *);
+		if (put_user(len, optlen))
+			return -EFAULT;
+		if (copy_to_user(optval, &sk->ldc_ring->ldc_pkt_buffer_uaddr, len))
+			return -EFAULT;
+		return 0;
 	default:
 		return -ENOPROTOOPT;
 	};
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/net/ipv4/tcp_ldc.c linux-2.6.15.4-tcpldc/net/ipv4/tcp_ldc.c
--- linux-2.6.15.4/net/ipv4/tcp_ldc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15.4-tcpldc/net/ipv4/tcp_ldc.c	2006-05-30 22:34:02.000000000 +0200
@@ -0,0 +1,1013 @@
+/*
+ * TCP Late data choice 
+ *
+ *	Erlend Birkedal:	TCP Late data choice support
+ */
+
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/tcp.h>
+#include <linux/vmalloc.h>
+#include <net/sock.h> 
+#include <net/tcp.h>
+#include <net/tcp_ldc.h>
+
+/* The two following lines are copied from net/ipv4/tcp.c */
+#define TCP_PAGE(sk)    (sk->sk_sndmsg_page)
+#define TCP_OFF(sk)     (sk->sk_sndmsg_off)
+
+
+//#define LDC_DEBUG 1
+#ifdef LDC_DEBUG
+#define DPRINT(a) printk a
+#else
+#define DPRINT(a)
+#endif
+
+#define DDPRINT(a) printk a
+
+extern int (*tcp_set_ldc)(struct sock *sk, char __user *optval, int optlen);
+extern int (*ldc_notify)(struct sock *sk);
+extern struct sk_buff *(*ldc_update_send_head)(struct sock *sk, struct tcp_sock *tp,
+					       struct sk_buff *skb);
+extern inline int select_size(struct sock *sk, struct tcp_sock *tp);
+extern inline void skb_entail(struct sock *sk, struct tcp_sock *tp,
+			      struct sk_buff *skb);
+extern inline void tcp_mark_push(struct tcp_sock *tp, struct sk_buff *skb);
+extern inline int forced_push(struct tcp_sock *tp);
+extern inline void tcp_push(struct sock *sk, struct tcp_sock *tp, int flags,
+			    int mss_now, int nonagle);
+
+int ldc_map_user_pages(struct ldc_packet_ring *lpr, unsigned long uaddr);
+int ldc_unmap_user_pages(struct sock *sk);
+int ldc_sendmsg(struct sock *sk);
+int ldc_next_pkt(struct sock *sk);
+struct sk_buff *ldc_make_new_skb(struct sock *sk, int pkt);
+static int ldc_pkt_buffer_alloc(struct ldc_packet_ring *lpr, int rsize, int psize);
+static int ldc_free_pkt_buffer(struct sock *sk);
+static void * ldc_rvmalloc(unsigned long size);
+static void ldc_rvfree(void *mem, unsigned long size);
+static int ldc_remap_buffer(struct vm_area_struct *vma, unsigned long buf,
+			    unsigned long addr, unsigned long size);
+static inline int ldc_skb_add_data(struct sk_buff *skb,
+				   char __user *from, int copy);
+static inline int ldc_skb_copy_to_page(struct sock *sk, char __user *from,
+				       struct sk_buff *skb, struct page *page,
+				       int off, int copy);
+
+/* This function is called by tcp_setsockopt() to init
+ * the LDC support for the socket.
+ */
+int _tcp_set_ldc(struct sock *sk, char __user *optval, int optlen)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** _tcp_set_ldc(struct sock *sk = 0x%lx, char __user *optval = 0x%lx, int optlen = %d)\n", 
+		jiffies,
+		(long unsigned int)sk,
+		(long unsigned int)optval,
+		optlen));
+	int ret;
+	int usr_vaules[2];
+
+	copy_from_user(usr_vaules, optval, optlen);
+    
+	if (usr_vaules[0] < 0 || usr_vaules[1] < 0)
+		return -EINVAL;
+
+	if ((sk->ldc_ring = kmalloc(sizeof(struct ldc_packet_ring), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+        
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] Got ring_size: %d and packet_size: %d from app\n",
+		jiffies,
+		usr_vaules[0],
+		usr_vaules[1]));
+        
+	ret = ldc_pkt_buffer_alloc(sk->ldc_ring, usr_vaules[0], usr_vaules[1]);
+	if(ret){
+		DPRINT((KERN_EMERG "TCP_LDC: [%lu] Got %d from ldc_pkt_buffer_alloc()\n",
+			jiffies,
+			ret));
+		return ret;
+	}
+
+	sock_set_flag(sk, SOCK_LDC);
+
+	return 0;
+}
+
+/* This function get called when the user wants to
+ * notify the kernel about new packets in the ring
+ */
+int _ldc_notify(struct sock *sk)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** _ldc_notify(struct sock *sk = %lx)\n", jiffies, (long unsigned int)sk));
+	/* Clearing the flag */
+	sk->ldc_ring->ring_head->kern_flag = 0;
+    
+	return ldc_sendmsg(sk);
+}
+
+
+/* This function finds the next packet to
+ * send. Returns -1 if the buffer is empty.
+ */
+int ldc_next_pkt(struct sock *sk)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** _ldc_next_pkt(struct sock *sk = %lx)\n", jiffies, (long unsigned int)sk));
+	int ret = -1;
+	struct ldc_packet_ring *lpr = sk->ldc_ring;
+	int pkt = lpr->ring_head->kern_i;
+    
+	/* Skiping deleted packets */
+	while(LDC_BUFFER_NOT_EMPTY(lpr->ring_head) && lpr->packet_ring[pkt].user_flag){
+		DPRINT((KERN_EMERG "TCP_LDC:          Deleting packet: user_seq = %d, idx = %d\n", lpr->packet_ring[pkt].user_seq, pkt));
+		LDC_INCP(lpr->ring_head, kern_i);
+		pkt = lpr->ring_head->kern_i;
+	}
+    
+	if(LDC_BUFFER_NOT_EMPTY(lpr->ring_head)){
+		/* Yes, we have packet(s) to send! 
+		 * Inccrementing the pointer so we are 
+		 * ready for the next round.
+		 */ 
+		ret = pkt;
+		DPRINT((KERN_EMERG "TCP_LDC:          Sending packet: user_seq = %d, idx = %d\n",lpr->packet_ring[pkt].user_seq, pkt));
+		LDC_INCP(lpr->ring_head, kern_i);
+	} else {
+		/* No packet(s) to send.
+		 * Setting the kern_flag.
+		 */
+		DPRINT((KERN_EMERG "TCP_LDC: [%lu] *  B U F F E R  E M P T Y !  *  Setting the kern_flag     *\n", jiffies));
+		lpr->ring_head->kern_flag = 1;
+	}
+
+	return ret;
+}
+
+
+/* This function is called by update_send_head() if the current socket
+ * is a LDC-socket.
+ */
+struct sk_buff *_ldc_update_send_head(struct sock *sk, struct tcp_sock *tp,
+				      struct sk_buff *skb)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** _ldc_update_send_head(struct sock *sk = %lx, struct tcp_sock *tp = %lx, struct sk_buff *skb = %lx)\n", 
+		jiffies,
+		(long unsigned int)sk,
+		(long unsigned int)tp,
+		(long unsigned int)skb));
+
+	/* The regular update_send_head first we check if we have a pakcet 
+	 * at skb->next. For LDC sockets packets will end up the if the
+	 * regular write/send etc is used to send packets or if we have a 
+	 * packet with size > mss and the packet was spilt into several skbs.
+	 * If there was no packet at skb->next and the socket is a TCP LDC
+	 * socket, this funtion will be called.
+	 */
+    
+	struct sk_buff * new_skb;
+	int pkt = ldc_next_pkt(sk);
+	
+	if(pkt < 0) {
+		/* No packets in the LDC packet ring. */
+		new_skb = NULL;
+	} else {
+		/* We have packets in the LDC packet ring. Make skb and
+		 * insert it into the send buffer (return to update_send_head).
+		 */
+		new_skb = ldc_make_new_skb(sk, pkt);
+		if(!new_skb) {
+			/* Something failed. Perhaps memory? 
+			 * Decrementing kern_i and setting kern_flag 
+			 */
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu] ldc_make_new_skb() FAILED! DECR packet pointer and setting the kernel flag.\n", jiffies));
+			LDC_DECP(sk->ldc_ring->ring_head, kern_i);
+			sk->ldc_ring->ring_head->kern_flag = 1;
+		}		
+	}
+	
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu]   ldc_update_send_head() returns %lx\n", jiffies, skb));
+	return new_skb;
+}
+
+
+/* This function i used to make a skb of the packet at idx
+ * and insert it into the tcp sending queue.
+ */
+struct sk_buff *ldc_make_new_skb(struct sock *sk, int idx)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** ldc_make_new_skb(struct sock *sk = %lx, int idx = %d)\n", jiffies, (long unsigned int)sk, idx));
+// This is a modified tcp_sendmsg
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct sk_buff *skb = NULL; //skb is set to NULL to be 110% sure that we do not return garbage.
+
+	/* Need these two in case the size is > mss */
+	struct sk_buff *first_skb = NULL;
+	int multiple_skbs = 0;
+
+	int flags;
+	int mss_now, size_goal;
+	int err, copied;
+	long timeo;
+	
+
+	if(in_interrupt()) {
+		DPRINT((KERN_EMERG "TCP_LDC: [%lu] O H  N O ! We're gonne die!\n", jiffies));
+	}
+
+	// Modification of net/socket.c:720
+	flags = !(sk->sk_socket->file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+
+	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+
+
+	/* This should be in poll */
+	clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+	mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
+	size_goal = tp->xmit_size_goal;
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu]   mss_now = %d, size_goal = %d, seglen = %d\n", 
+		jiffies, 
+		mss_now,
+		size_goal,
+		sk->ldc_ring->packet_ring[idx].size));
+
+	/* Ok commence sending. */
+	//iovlen = msg->msg_iovlen;
+	//iov = msg->msg_iov;
+	copied = 0;
+
+	err = -EPIPE;
+
+	int seglen = sk->ldc_ring->packet_ring[idx].size - sk->ldc_ring->packet_ring[idx].offset;
+	void *from = sk->ldc_ring->packet_ring[idx].kdata + sk->ldc_ring->packet_ring[idx].offset;
+	
+	while (seglen > 0) {
+		int copy;
+		skb = sk->sk_write_queue.prev;
+		DPRINT((KERN_EMERG "TCP_LDC: [%lu]   skb = sk->sk_write_queue.prev (%lx)\n", jiffies, (long unsigned int)skb));
+
+		/* Since we don't have unsent packets in sk->sk_write_queue
+		 * an this is update_send_head() we can't use the skb at
+		 * sk->sk_write_queue.prev. That skb is probably sent already.
+		 * Make a new one!
+		 */
+		if(TCP_SKB_CB(skb)->when) {
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   We can't use skb (0x%lx) since it was sent at %lu jiffies. Making a new one...\n", 
+				jiffies, 
+				(long unsigned int)skb,
+				(long unsigned int)TCP_SKB_CB(skb)->when));
+			goto new_segment;
+		}
+
+		if (!sk->sk_send_head ||
+		    (copy = size_goal - skb->len) <= 0) {
+
+		new_segment:
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   Allocate new segment.\n", jiffies));
+			/* Allocate new segment. If the interface is SG,
+			 * allocate skb fitting to single page.
+			 */
+			if (!sk_stream_memory_free(sk)){
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   !sk_stream_memory_free(sk); goto wait_for_sndbuf\n", jiffies));
+				goto wait_for_sndbuf;
+			}
+
+			skb = sk_stream_alloc_pskb(sk, select_size(sk, tp),
+						   0, sk->sk_allocation);
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   skb (%lx) = sk_stream_alloc_pskb(sk, select_size(sk, tp), 0, sk->sk_allocation)\n",
+				jiffies,
+				(long unsigned int)skb));
+			if (!skb){
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   goto wait_for_memory\n", jiffies));
+				goto wait_for_memory;
+			}
+
+			/*
+			 * Check whether we can use HW checksum.
+			 */
+			if (sk->sk_route_caps &
+			    (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM |
+			     NETIF_F_HW_CSUM))
+				skb->ip_summed = CHECKSUM_HW;
+
+			skb_entail(sk, tp, skb);
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   skb_entail(sk = %lx, tp = %lx, skb = %lx)\n",
+				jiffies,
+				(long unsigned int)sk,
+				(long unsigned int)tp,
+				(long unsigned int)skb));
+			copy = size_goal;
+		}
+
+		/* Try to append data to the end of skb. */
+		if (copy > seglen)
+			copy = seglen;
+
+		/* Where to copy to? */
+		if (skb_tailroom(skb) > 0) {
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   We have some space in skb head. Superb!\n", jiffies));
+			/* We have some space in skb head. Superb! */
+			if (copy > skb_tailroom(skb))
+				copy = skb_tailroom(skb);
+		
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   ldc_skb_add_data(skb = %lx, from = %lx, copy = %d)\n",
+				jiffies,
+				(long unsigned int)skb,
+				(long unsigned int)from,
+				copy));
+
+			if ((err = ldc_skb_add_data(skb, from, copy)) != 0){
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   ldc_skb_add_data returned %d\n", jiffies, err));
+				dump_stack();
+				goto do_fault;
+			}
+		} else {
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   No space in skb head.\n", jiffies));
+			int merge = 0;
+			int i = skb_shinfo(skb)->nr_frags;
+			struct page *page = TCP_PAGE(sk);
+			int off = TCP_OFF(sk);
+
+			if (skb_can_coalesce(skb, i, page, off) &&
+			    off != PAGE_SIZE) {
+				/* We can extend the last page
+				 * fragment. */
+				merge = 1;
+			} else if (i == MAX_SKB_FRAGS ||
+				   (!i &&
+				    !(sk->sk_route_caps & NETIF_F_SG))) {
+				/* Need to add new fragment and cannot
+				 * do this because interface is non-SG,
+				 * or because all the page slots are
+				 * busy. */
+				tcp_mark_push(tp, skb);
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   goto new_segment\n", jiffies));
+				goto new_segment;
+			} else if (page) {
+				if (off == PAGE_SIZE) {
+					put_page(page);
+					TCP_PAGE(sk) = page = NULL;
+					off = 0;
+				}
+			} else
+				off = 0;
+
+			if (copy > PAGE_SIZE - off)
+				copy = PAGE_SIZE - off;
+
+			if (!sk_stream_wmem_schedule(sk, copy)){  /* IS THIS HARMLESS? */
+				DPRINT((KERN_EMERG " goto wait_for_memory (*2*)\n"));
+				goto wait_for_memory;
+			}
+
+			if (!page) {
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   Allocate new cache page\n", jiffies));
+				/* Allocate new cache page. */
+				if (!(page = sk_stream_alloc_page(sk))){
+					DPRINT((KERN_EMERG "TCP_LDC: [%lu]  goto wait_for_memory (3)\n", jiffies));
+					goto wait_for_memory;
+				}
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   page (%lx) = sk_stream_alloc_page(sk)\n", jiffies, (long unsigned int)page));
+			}
+
+			/* Time to copy data. We are close to
+			 * the end! */
+			err = ldc_skb_copy_to_page(sk, from, skb, page,
+						   off, copy);
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   ldc_skb_copy_to_page(sk = %lx, from = %lx, skb = %lx, page = %lx, off = %d, copy = %d) returned %d\n", 
+				jiffies, 
+				(long unsigned int)sk,
+				(long unsigned int)from,
+				(long unsigned int)skb,
+				(long unsigned int)page,
+				off,
+				copy,
+				err));
+			if (err) {
+				/* If this page was new, give it to the
+				 * socket so it does not get leaked.
+				 */
+				if (!TCP_PAGE(sk)) {
+					TCP_PAGE(sk) = page;
+					TCP_OFF(sk) = 0;
+				}
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   goto do_error (ldc_skb_copy_to_page)\n", 
+					jiffies));
+				//goto do_error;
+				goto do_fault;
+			}
+
+			/* Update the skb. */
+			if (merge) {
+				skb_shinfo(skb)->frags[i - 1].size +=
+					copy;
+			} else {
+				skb_fill_page_desc(skb, i, page, off, copy);
+				if (TCP_PAGE(sk)) {
+					get_page(page);
+				} else if (off + copy < PAGE_SIZE) {
+					get_page(page);
+					TCP_PAGE(sk) = page;
+				}
+			}
+
+			TCP_OFF(sk) = off + copy;
+		}
+
+		if (!copied)
+			TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH;
+
+		tp->write_seq += copy;
+		TCP_SKB_CB(skb)->end_seq += copy;
+		skb_shinfo(skb)->tso_segs = 0;
+
+		from += copy;
+		copied += copy;
+		sk->ldc_ring->packet_ring[idx].offset += copy;
+		if ((seglen -= copy) == 0){
+			//DPRINT((KERN_EMERG "TCP_LDC: [%lu]   goto out\n", jiffies));
+			goto out;
+		}
+
+		if (skb->len < mss_now || (flags & MSG_OOB)){
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   continue (1)\n", jiffies));
+			continue;
+		}
+
+		DPRINT((KERN_EMERG "TCP_LDC: [%lu]  SIZE > MSS \n", jiffies));
+	    
+
+		/* We can't push stuff now, because we're allready puching.
+		 * We just have to continue making skbs while there is data
+		 * in the LDC ring.
+		 *
+		 * NB! We must return the first skb we made and let 
+		 * update_send handle the rest.
+		 */
+
+		if(!multiple_skbs)
+			first_skb = skb;
+
+		multiple_skbs++;
+
+		continue;
+
+
+	wait_for_sndbuf:
+		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+	wait_for_memory:
+		/* if (copied) */
+		/* tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); */
+
+		//if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
+		goto do_error;
+
+		mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
+		size_goal = tp->xmit_size_goal;
+	}
+	
+
+ out:
+	if (copied) {
+		//tcp_push(sk, tp, flags, mss_now, tp->nonagle); 
+	}
+	//release_sock(sk);
+	/* return copied; */
+	if(skb) {
+		DPRINT((KERN_EMERG "TCP_LDC: [%lu]   return skb (%lx)\n", jiffies, (long unsigned int)skb));
+		if(multiple_skbs)
+			return first_skb;
+		return skb;
+	}
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu]   return NULL (WE SHOULD NOT HAVE BEEN HERE)\n", jiffies));
+	return NULL;
+    
+ do_fault:
+	if (!skb->len) {
+		if (sk->sk_send_head == skb)
+			sk->sk_send_head = NULL;
+		__skb_unlink(skb, &sk->sk_write_queue);
+		sk_stream_free_skb(sk, skb);
+	}
+
+ do_error:
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu]   return NULL (2)\n", jiffies));
+	return NULL;
+}
+
+
+int ldc_sendmsg(struct sock *sk) 
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** ldc_sendmsg(struct sock *sk = %lx)\n", jiffies, (long unsigned int)sk));
+	/* TCP LDC SEND INIT */
+	int pkt = ldc_next_pkt(sk);
+
+	if(pkt < 0)
+		return -EAGAIN;
+
+	
+// This is a modified tcp_sendmsg
+
+	//struct iovec *iov;
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct sk_buff *skb;
+	int flags;
+	int mss_now, size_goal;
+	int err, copied;
+	long timeo;
+
+	lock_sock(sk);
+	TCP_CHECK_TIMER(sk);
+
+	//flags = msg->msg_flags;
+	// Modification of net/socket.c:720
+	flags = !(sk->sk_socket->file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+
+	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+
+	/* Wait for a connection to finish. */
+	if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
+		if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
+			goto out_err;
+
+	/* This should be in poll */
+	clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+	mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
+	size_goal = tp->xmit_size_goal;
+
+	/* Ok commence sending. */
+	//iovlen = msg->msg_iovlen;
+	//iov = msg->msg_iov;
+	copied = 0;
+
+	err = -EPIPE;
+	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+		goto do_error;
+
+	int seglen = sk->ldc_ring->packet_ring[pkt].size - sk->ldc_ring->packet_ring[pkt].offset;
+	void *from = sk->ldc_ring->packet_ring[pkt].kdata + sk->ldc_ring->packet_ring[pkt].offset;
+
+
+	while (seglen > 0) {
+		int copy;
+
+		skb = sk->sk_write_queue.prev;
+
+		if (!sk->sk_send_head ||
+		    (copy = size_goal - skb->len) <= 0) {
+
+		new_segment:
+			/* Allocate new segment. If the interface is SG,
+			 * allocate skb fitting to single page.
+			 */
+			if (!sk_stream_memory_free(sk))
+				goto wait_for_sndbuf;
+
+			skb = sk_stream_alloc_pskb(sk, select_size(sk, tp),
+						   0, sk->sk_allocation);
+			if (!skb)
+				goto wait_for_memory;
+
+			/*
+			 * Check whether we can use HW checksum.
+			 */
+			if (sk->sk_route_caps &
+			    (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM |
+			     NETIF_F_HW_CSUM))
+				skb->ip_summed = CHECKSUM_HW;
+
+			skb_entail(sk, tp, skb);
+			copy = size_goal;
+		}
+
+		/* Try to append data to the end of skb. */
+		if (copy > seglen)
+			copy = seglen;
+
+		/* Where to copy to? */
+		if (skb_tailroom(skb) > 0) {
+			/* We have some space in skb head. Superb! */
+			if (copy > skb_tailroom(skb))
+				copy = skb_tailroom(skb);
+	
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   ldc_skb_add_data(skb = %lx, from = %lx, copy = %d) <=======*\n",
+				jiffies,
+				(long unsigned int)skb,
+				(long unsigned int)from,
+				copy));
+
+			if ((err = ldc_skb_add_data(skb, from, copy)) != 0){
+				DPRINT((KERN_EMERG "TCP_LDC: [%lu]   lsc_skb_add_data returned %d\n", jiffies, err));
+				goto do_fault;
+			}
+		} else {
+			int merge = 0;
+			int i = skb_shinfo(skb)->nr_frags;
+			struct page *page = TCP_PAGE(sk);
+			int off = TCP_OFF(sk);
+
+			if (skb_can_coalesce(skb, i, page, off) &&
+			    off != PAGE_SIZE) {
+				/* We can extend the last page
+				 * fragment. */
+				merge = 1;
+			} else if (i == MAX_SKB_FRAGS ||
+				   (!i &&
+				    !(sk->sk_route_caps & NETIF_F_SG))) {
+				/* Need to add new fragment and cannot
+				 * do this because interface is non-SG,
+				 * or because all the page slots are
+				 * busy. */
+				tcp_mark_push(tp, skb);
+				goto new_segment;
+			} else if (page) {
+				if (off == PAGE_SIZE) {
+					put_page(page);
+					TCP_PAGE(sk) = page = NULL;
+					off = 0;
+				}
+			} else
+				off = 0;
+
+			if (copy > PAGE_SIZE - off)
+				copy = PAGE_SIZE - off;
+
+			if (!sk_stream_wmem_schedule(sk, copy))
+				goto wait_for_memory;
+
+			if (!page) {
+				/* Allocate new cache page. */
+				if (!(page = sk_stream_alloc_page(sk)))
+					goto wait_for_memory;
+			}
+
+			/* Time to copy data. We are close to
+			 * the end! */
+			err = ldc_skb_copy_to_page(sk, from, skb, page,
+						   off, copy);
+			DPRINT((KERN_EMERG "TCP_LDC: [%lu]   ldc_skb_copy_to_page(sk = %lx, from = %lx, skb = %lx, page = %lx, off = %d, copy = %d) returned %d\n", 
+				jiffies, 
+				(long unsigned int)sk,
+				(long unsigned int)from,
+				(long unsigned int)skb,
+				(long unsigned int)page,
+				off,
+				copy,
+				err));
+			if (err) {
+				/* If this page was new, give it to the
+				 * socket so it does not get leaked.
+				 */
+				if (!TCP_PAGE(sk)) {
+					TCP_PAGE(sk) = page;
+					TCP_OFF(sk) = 0;
+				}
+				goto do_error;
+			}
+
+			/* Update the skb. */
+			if (merge) {
+				skb_shinfo(skb)->frags[i - 1].size +=
+					copy;
+			} else {
+				skb_fill_page_desc(skb, i, page, off, copy);
+				if (TCP_PAGE(sk)) {
+					get_page(page);
+				} else if (off + copy < PAGE_SIZE) {
+					get_page(page);
+					TCP_PAGE(sk) = page;
+				}
+			}
+
+			TCP_OFF(sk) = off + copy;
+		}
+
+		if (!copied)
+			TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH;
+
+		tp->write_seq += copy;
+		TCP_SKB_CB(skb)->end_seq += copy;
+		skb_shinfo(skb)->tso_segs = 0;
+
+		from += copy;
+		copied += copy;
+		sk->ldc_ring->packet_ring[pkt].offset += copy;
+		if ((seglen -= copy) == 0)
+			goto out;
+
+		if (skb->len < mss_now || (flags & MSG_OOB))
+			continue;
+
+		if (forced_push(tp)) {
+			tcp_mark_push(tp, skb);
+			__tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_PUSH);
+		} else if (skb == sk->sk_send_head)
+			tcp_push_one(sk, mss_now);
+		continue;
+
+	wait_for_sndbuf:
+		DPRINT(("TCP_LDC: [%lu] wait_for_sndbuf\n", jiffies));
+		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+	wait_for_memory:
+		DPRINT(("TCP_LDC: [%lu] wait_for_memory\n", jiffies));			
+		if (copied)
+			tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+
+		if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
+			goto do_error;
+
+		mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
+		size_goal = tp->xmit_size_goal;
+	}
+		
+
+ out:
+	DPRINT(("TCP_LDC: [%lu] out\n", jiffies));
+	if (copied)
+		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
+	TCP_CHECK_TIMER(sk);
+	release_sock(sk);
+	return copied;
+
+ do_fault:
+	DPRINT(("TCP_LDC: [%lu] do_fault\n", jiffies));
+	if (!skb->len) {
+		if (sk->sk_send_head == skb)
+			sk->sk_send_head = NULL;
+		__skb_unlink(skb, &sk->sk_write_queue);
+		sk_stream_free_skb(sk, skb);
+	}
+
+ do_error:
+	DPRINT(("TCP_LDC: [%lu] do_error\n", jiffies));
+	if (copied)
+		goto out;
+ out_err:
+	DPRINT(("TCP_LDC: [%lu] out_error\n", jiffies));
+	err = sk_stream_error(sk, flags, err);
+	TCP_CHECK_TIMER(sk);
+	release_sock(sk);
+	return err;
+}
+
+
+
+
+
+/* The follwing function is based on arch/ia64/kernel/perfmon.c and
+ * http://www.ussg.iu.edu/hypermail/linux/kernel/0512.0/1038.html
+ *
+ * This function will allocate a ldc packet buffer and remap it into 
+ * the user address space of the task
+ */
+static int ldc_pkt_buffer_alloc(struct ldc_packet_ring *lpr, int rsize, int psize)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma = NULL;
+	unsigned long size;
+	void *pkt_buf;
+	int buffer_size, i;
+
+
+	/*
+	 * the fixed header + requested size and align to page boundary
+	 */
+	buffer_size = sizeof(struct ldc_ring_head) + (rsize * sizeof(struct ldc_packet)) + (rsize * psize);
+	size = PAGE_ALIGN(buffer_size);
+
+	DPRINT(("packet buffer rsize=%d size=%lu bytes\n", buffer_size, size));
+
+	/*
+	 * We do the easy to undo allocations first.
+	 *
+	 * ldc_rvmalloc(), clears the buffer, so there is no leak
+	 */
+	pkt_buf = ldc_rvmalloc(size);
+	if (pkt_buf == NULL) {
+		DDPRINT(("Can't allocate packet buffer\n"));
+		return -ENOMEM;
+	}
+
+	DPRINT(("pkt_buf @%p\n", pkt_buf));
+
+	/* allocate vma */
+	vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!vma) {
+		DDPRINT(("Cannot allocate vma\n"));
+		goto error_kmem;
+	}
+	memset(vma, 0, sizeof(*vma));
+
+	/*
+	 * partially initialize the vma for the packet buffer
+	 */
+	vma->vm_mm        = mm;
+	vma->vm_flags     = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; 
+	vma->vm_page_prot = PAGE_SHARED;
+
+	/*
+	 * Now we have everything we need and we can initialize
+	 * and connect all the data structures
+	 */
+
+	lpr->ldc_pkt_buffer = pkt_buf;
+	lpr->ldc_pkt_size = size; /* aligned size */
+	lpr->ring_head = pkt_buf;
+	lpr->packet_ring = pkt_buf + sizeof(struct ldc_ring_head);
+	/* Set the buffer size (in packets) */
+	lpr->ring_head->ring_size = rsize;
+	lpr->ring_head->packet_size = psize;
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] Init the ldc_ring_head\n", jiffies));
+	lpr->ring_head->kern_i = 0;
+	lpr->ring_head->kern_flag = 1;
+	lpr->ring_head->umod_i = 0;
+	lpr->ring_head->user_i = 0;
+
+	/*
+	 * Let's do the difficult operations next.
+	 *
+	 * now we atomically find some area in the address space and
+	 * remap the buffer in it.
+	 */
+	down_write(&current->mm->mmap_sem);
+
+	/* find some free area in address space, must have mmap sem held */
+	vma->vm_start = get_unmapped_area(NULL, 0, size, 0, MAP_PRIVATE|MAP_ANONYMOUS);  /* Do we need MAP_SHARED and not MAP_PRIVATE ? */
+	if (vma->vm_start == 0UL) {
+		DPRINT(("Cannot find unmapped area for size %ld\n", size));
+		up_write(&current->mm->mmap_sem);
+		goto error;
+	}
+	vma->vm_end = vma->vm_start + size;
+	vma->vm_pgoff = vma->vm_start >> PAGE_SHIFT;
+
+	DPRINT(("aligned size=%ld, hdr=%p mapped @0x%lx\n", size, pkt_buf, vma->vm_start));
+
+	/* can only be applied to current task, need to have the mm semaphore held when called */
+	if (ldc_remap_buffer(vma, (unsigned long)pkt_buf, vma->vm_start, size)) {
+		DPRINT(("Can't remap buffer\n"));
+		up_write(&current->mm->mmap_sem);
+		goto error;
+	}
+
+	/* Init the packets */
+	for(i = 0; i < rsize; i++){
+		lpr->packet_ring[i].data = (void *)vma->vm_start + sizeof(struct ldc_ring_head) + (rsize * sizeof(struct ldc_ring_head)) + (psize * i);
+		lpr->packet_ring[i].kdata = pkt_buf + sizeof(struct ldc_ring_head) + (rsize * sizeof(struct ldc_ring_head)) + (psize * i);
+	}
+
+	/*
+	 * now insert the vma in the vm list for the process, must be
+	 * done with mmap lock held
+	 */
+
+	insert_vm_struct(mm, vma);
+	mm->total_vm  += size >> PAGE_SHIFT;
+	vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file,
+			vma_pages(vma));
+	up_write(&current->mm->mmap_sem);
+
+	/*
+	 * keep track of user level virtual address
+	 */
+	lpr->ldc_pkt_buffer_uaddr = (void *)vma->vm_start;
+
+	return 0;
+
+ error:
+	kmem_cache_free(vm_area_cachep, vma);
+ error_kmem:
+	ldc_rvfree(pkt_buf, size);
+
+	return -ENOMEM;
+}
+
+/* FIX ME: Call this when a socket closes */
+static int ldc_free_pkt_buffer(struct sock *sk)
+{
+	ldc_rvfree(sk->ldc_ring->ldc_pkt_buffer, sk->ldc_ring->ldc_pkt_size);
+	kfree(sk->ldc_ring);
+	return 0;
+}
+
+static void * ldc_rvmalloc(unsigned long size)
+{
+	void *mem;
+
+	size = PAGE_ALIGN(size);
+	mem  = vmalloc_32(size);
+	if (mem) {
+		DPRINT(("CPU%d ldc_rvmalloc(%ld)=%p\n", smp_processor_id(), size, mem));
+		memset(mem, 0, size);
+	}
+	return mem;
+}
+
+static void ldc_rvfree(void *mem, unsigned long size)
+{
+	if (mem) {
+		DPRINT(("freeing physical buffer @%p size=%lu\n", mem, size));
+		vfree(mem);
+	}
+	return;
+}
+
+static int ldc_remap_buffer(struct vm_area_struct *vma, unsigned long buf, unsigned long addr, unsigned long size)
+{
+	DPRINT(("CPU%d buf=0x%lx addr=0x%lx size=%ld\n", smp_processor_id(), buf, addr, size));
+
+	while (size > 0) {
+		if (vm_insert_page(vma, addr, page))
+			return -ENOMEM;
+
+		addr  += PAGE_SIZE;
+		buf   += PAGE_SIZE;
+		size  -= PAGE_SIZE;
+	}
+	return 0;
+}
+
+/* These two funtions copy to skb from kernel space */
+
+static inline int ldc_skb_add_data(struct sk_buff *skb,
+				   char __user *from, int copy)
+{
+	const int off = skb->len;
+
+	if (skb->ip_summed == CHECKSUM_NONE) {
+		int err = 0;
+		unsigned int csum = csum_partial_copy_generic(from,
+							      skb_put(skb, copy),
+							      0, copy, &err, NULL);
+		if (!err) {
+			skb->csum = csum_block_add(skb->csum, csum, off);
+			return 0;
+		}
+	} else {
+		memcpy(skb_put(skb, copy), from, copy);
+		return 0;
+	}
+ 
+	__skb_trim(skb, off);
+	return -EFAULT;
+}
+
+
+static inline int ldc_skb_copy_to_page(struct sock *sk, char __user *from,
+				       struct sk_buff *skb, struct page *page,
+				       int off, int copy)
+{
+	if (skb->ip_summed == CHECKSUM_NONE) {
+		int err = 0;
+		unsigned int csum = csum_partial_copy_generic(from,
+							      page_address(page) + off,
+							      0, copy, &err, NULL);
+		if (err)
+			return err;
+		skb->csum = csum_block_add(skb->csum, csum, skb->len);
+	} else 
+		memcpy(page_address(page) + off, from, copy);
+	
+ 
+	skb->len             += copy;
+	skb->data_len        += copy;
+	skb->truesize        += copy;
+	sk->sk_wmem_queued   += copy;
+	sk->sk_forward_alloc -= copy;
+	return 0;
+}
+
+int __init init_tcp_ldc(void)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** init_tcp_ldc(void)\n", jiffies));
+	tcp_set_ldc = _tcp_set_ldc;
+	ldc_notify = _ldc_notify;
+	ldc_update_send_head = _ldc_update_send_head;
+    
+	printk("TCP_LDC: [%lu] Module loaded.\n", jiffies);
+	return 0;
+}
+
+void __exit cleanup_tcp_ldc(void)
+{
+	DPRINT((KERN_EMERG "TCP_LDC: [%lu] ** cleanup_tcp_ldc(void)\n", jiffies));
+	tcp_set_ldc = NULL;
+	ldc_notify = NULL;
+	ldc_update_send_head = NULL;
+	printk("TCP_LDC: [%lu] Module removed.\n", jiffies);
+	return;
+}
+
+
+module_init(init_tcp_ldc);
+module_exit(cleanup_tcp_ldc);
+
+MODULE_AUTHOR("Erlend Birkedal <erlendbi@ifi.uio.no>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TCP Late data choice");
diff -uprN -X linux-2.6.15.4/Documentation/dontdiff linux-2.6.15.4/net/ipv4/tcp_output.c linux-2.6.15.4-tcpldc/net/ipv4/tcp_output.c
--- linux-2.6.15.4/net/ipv4/tcp_output.c	2006-02-10 08:22:48.000000000 +0100
+++ linux-2.6.15.4-tcpldc/net/ipv4/tcp_output.c	2006-05-13 16:02:43.000000000 +0200
@@ -51,12 +51,18 @@ int sysctl_tcp_retrans_collapse = 1;
  */
 int sysctl_tcp_tso_win_divisor = 3;
 
+struct sk_buff *(*ldc_update_send_head)(struct sock *sk, struct tcp_sock *tp,
+				    struct sk_buff *skb);
+EXPORT_SYMBOL(ldc_update_send_head);
 static inline void update_send_head(struct sock *sk, struct tcp_sock *tp,
 				    struct sk_buff *skb)
 {
 	sk->sk_send_head = skb->next;
 	if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue)
-		sk->sk_send_head = NULL;
+		if(sock_flag(sk, SOCK_LDC))
+			sk->sk_send_head = (*ldc_update_send_head)(sk, tp, skb);
+		else
+ 			sk->sk_send_head = NULL;
 	tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
 	tcp_packets_out_inc(sk, tp, skb);
 }
@@ -720,6 +726,7 @@ unsigned int tcp_current_mss(struct sock
 
 	return mss_now;
 }
+EXPORT_SYMBOL(tcp_current_mss);
 
 /* Congestion window validation. (RFC2861) */
 
@@ -1086,6 +1093,7 @@ void __tcp_push_pending_frames(struct so
 			tcp_check_probe_timer(sk, tp);
 	}
 }
+EXPORT_SYMBOL(__tcp_push_pending_frames);
 
 /* Send _single_ skb sitting at the send head. This function requires
  * true push pending frames to setup probe timer etc.
@@ -1133,6 +1141,7 @@ void tcp_push_one(struct sock *sk, unsig
 		}
 	}
 }
+EXPORT_SYMBOL(tcp_push_one);
 
 /* This function returns the amount that we can raise the
  * usable window based on the following constraints

