diff -u'rNF^function' linux-2.6.17.13/include/linux/pkt_sched.h linux-2.6.17.13_tcn/include/linux/pkt_sched.h
--- linux-2.6.17.13/include/linux/pkt_sched.h	2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13_tcn/include/linux/pkt_sched.h	2006-09-20 21:57:06.000000000 +0200
@@ -430,6 +430,8 @@
 	TCA_NETEM_DELAY_DIST,
 	TCA_NETEM_REORDER,
 	TCA_NETEM_CORRUPT,
+	TCA_NETEM_TRACE,
+	TCA_NETEM_STATS,
 	__TCA_NETEM_MAX,
 };
 
@@ -445,6 +447,35 @@
 	__u32	jitter;		/* random jitter in latency (us) */
 };
 
+struct tc_netem_stats
+{
+	int packetcount;
+	int packetok;
+	int normaldelay;
+	int drops;
+	int dupl;
+	int corrupt;
+	int novaliddata;
+	int uninitialized;
+	int bufferunderrun;
+	int bufferinuseempty;
+	int noemptybuffer;
+	int readbehindbuffer;
+	int buffer1_reloads;
+	int buffer2_reloads;
+	int tobuffer1_switch;
+	int tobuffer2_switch;
+	int switch_to_emptybuffer1;
+	int switch_to_emptybuffer2;				   		
+};	
+
+struct tc_netem_trace
+{
+	__u32   fid;             /*flowid */
+	__u32   def;          	 /* default action 0 = no delay, 1 = drop*/
+	__u32   ticks;	         /* number of ticks corresponding to 1ms */
+};
+
 struct tc_netem_corr
 {
 	__u32	delay_corr;	/* delay correlation */
diff -u'rNF^function' linux-2.6.17.13/include/net/flowseed.h linux-2.6.17.13_tcn/include/net/flowseed.h
--- linux-2.6.17.13/include/net/flowseed.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17.13_tcn/include/net/flowseed.h	2006-09-23 23:26:28.000000000 +0200
@@ -0,0 +1,57 @@
+/* flowseed.h     header file for the netem trace enhancement
+ */
+
+#ifndef _FLOWSEED_H
+#define _FLOWSEED_H
+#include <net/sch_generic.h>
+
+/* must be divisible by 4 (=#pkts)*/
+#define DATA_PACKAGE 4000
+#define DATA_PACKAGE_ID 4008
+
+/* maximal amount of parallel flows */
+#define MAX_FLOWS 8
+
+/* struct per flow - kernel */
+struct tcn_control
+{
+        void * buffer1;
+        void * buffer2;
+        void * buffer_in_use;   /* buffer that is used by consumer */
+        void * offsetpos;       /* pointer to actual pos in the buffer in use */
+        void * buffer1_empty;   /* *buffer1 if buffer is empty, NULL else */
+        void * buffer2_empty;   /* *buffer2 if buffer is empty, NULL else */
+        int flowid;             /* netem flowid */
+        int validdataB1;        /* 1 if Data in buffer1 is valid, 0 if tracefile reached end and rubish is in B1 */
+        int validdataB2;        /* 1 if Data in buffer2 is valid, 0 if tracefile reached end and rubish is in B2 */
+};
+
+struct pktdelay
+{
+  	u8 head;
+  	int delay;
+};
+
+struct tcn_statistic
+{
+  	int packetcount;
+  	int packetok;
+	int normaldelay;
+	int drops;
+	int dupl;
+	int corrupt;
+	int novaliddata;
+	int uninitialized;
+	int bufferunderrun;
+	int bufferinuseempty;
+	int noemptybuffer;
+	int readbehindbuffer;
+	int buffer1_reloads;
+	int buffer2_reloads;
+	int tobuffer1_switch;
+	int tobuffer2_switch;
+  	int switch_to_emptybuffer1;
+	int switch_to_emptybuffer2;
+};
+
+#endif
diff -u'rNF^function' linux-2.6.17.13/net/sched/sch_netem.c linux-2.6.17.13_tcn/net/sched/sch_netem.c
--- linux-2.6.17.13/net/sched/sch_netem.c	2006-09-09 05:23:25.000000000 +0200
+++ linux-2.6.17.13_tcn/net/sched/sch_netem.c	2006-09-20 23:54:51.000000000 +0200
@@ -11,6 +11,9 @@
  *
  * Authors:	Stephen Hemminger <shemminger@osdl.org>
  *		Catalin(ux aka Dino) BOIE <catab at umbrella dot ro>
+ *              netem trace enhancement: Ariane Keller <arkeller@ee.ethz.ch> ETH Zurich
+ *                                       Rainer Baumann <baumann@hypert.net> ETH Zurich
+ *                                       Ulrich Fiedler <fiedler@tik.ee.ethz.ch> ETH Zurich
  */
 
 #include <linux/config.h>
@@ -22,10 +25,16 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/configfs.h>
+#include <linux/vmalloc.h>
 
 #include <net/pkt_sched.h>
 
-#define VERSION "1.2"
+#include "net/flowseed.h"
+
+#define VERSION "1.3"
 
 /*	Network Emulation Queuing algorithm.
 	====================================
@@ -51,6 +60,11 @@
 
 	 The simulator is limited by the Linux timer resolution
 	 and will create packet bursts on the HZ boundary (1ms).
+
+	 The trace option allows us to read the values for packet delay,
+	 duplication, loss and corruption from a tracefile. This permits
+	 the modulation of statistical properties such as long-range 
+	 dependences. See http://tcn.hypert.net.
 */
 
 struct netem_sched_data {
@@ -66,6 +80,11 @@
 	u32 duplicate;
 	u32 reorder;
 	u32 corrupt;
+	u32 tcnstop;
+	u32 trace;
+	u32 ticks;
+	u32 def;
+	u32 newdataneeded;
 
 	struct crndstate {
 		unsigned long last;
@@ -73,9 +92,13 @@
 	} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
 
 	struct disttable {
-		u32  size;
+		u32 size;
 		s16 table[0];
 	} *delay_dist;
+
+	struct tcn_statistic * statistic;
+	struct tcn_control * flowbuffer;
+	wait_queue_head_t my_event;
 };
 
 /* Time stamp put into socket buffer control block */
@@ -83,6 +106,16 @@
 	psched_time_t	time_to_send;
 };
 
+
+struct confdata {
+	int fid;
+	struct netem_sched_data * sched_data;
+};
+
+static struct confdata map[MAX_FLOWS];
+static int mask_head = -536870912;
+static int mask_delay = 536870911;
+
 /* init_crandom - initialize correlated random number generator
  * Use entropy source for initial seed.
  */
@@ -140,6 +173,126 @@
 	return  x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu;
 }
 
+/* don't call this function directly. It is called after 
+ *a packet has been taken out of a buffer and it was the last. */
+static int reload_flowbuffer (struct netem_sched_data *q)
+{
+	if (q->flowbuffer->buffer_in_use == q->flowbuffer->buffer1) {
+		q->flowbuffer->buffer1_empty = q->flowbuffer->buffer1;
+		if (q->flowbuffer->buffer2_empty != NULL) {
+			q->statistic->switch_to_emptybuffer2++;
+			return -EFAULT;
+		} else {
+			q->statistic->tobuffer2_switch++;
+		}
+
+		q->flowbuffer->buffer_in_use = q->flowbuffer->buffer2;
+		q->flowbuffer->offsetpos = q->flowbuffer->buffer2;
+
+	} else {
+		q->flowbuffer->buffer2_empty = q->flowbuffer->buffer2;
+
+		if (q->flowbuffer->buffer1_empty != NULL) {
+		 	q->statistic->switch_to_emptybuffer1++;
+			return -EFAULT;
+		} else {
+			q->statistic->tobuffer1_switch++;
+		}
+
+		q->flowbuffer->buffer_in_use = q->flowbuffer->buffer1;
+		q->flowbuffer->offsetpos = q->flowbuffer->buffer1;
+
+	}
+	/*the flowseed process can send more data*/
+	q->tcnstop = 0;
+	q->newdataneeded = 1;
+	wake_up(&q->my_event);
+	return 0;
+}
+
+/* return pktdelay with delay and drop/dupl/corrupt option */
+static struct pktdelay get_next_delay(struct netem_sched_data *q)
+{
+	struct pktdelay retval;
+	memset(&retval, 0, sizeof(retval));
+	int variout;
+
+	/*choose whether to drop or 0 delay packets on default*/
+	retval.head = q->def;
+	retval.delay = 0;
+
+	if (q->flowbuffer == NULL) {
+		printk(KERN_ERR "netem: read from an uninitialized flow.\n");
+		q->statistic->uninitialized++;
+		return retval;
+	}
+
+	q->statistic->packetcount++;
+
+	/* check if we have to reload a buffer */
+	if (q->flowbuffer->offsetpos - q->flowbuffer->buffer_in_use ==
+	    DATA_PACKAGE) {
+		reload_flowbuffer(q);
+	}
+
+	/* sanity checks */
+	if ((q->flowbuffer->buffer_in_use ==  q->flowbuffer->buffer1 &&
+	     q->flowbuffer->validdataB1) || ( q->flowbuffer->buffer_in_use ==
+	     q->flowbuffer->buffer2 && q->flowbuffer->validdataB2)) {
+
+		if ((q->flowbuffer->buffer1_empty != NULL) &&
+		    (q->flowbuffer->buffer2_empty != NULL)) {
+			q->statistic->bufferunderrun++;
+			return retval;
+		}
+
+		if (q->flowbuffer->buffer1_empty == q->flowbuffer->buffer_in_use ||
+		    q->flowbuffer->buffer2_empty == q->flowbuffer->buffer_in_use) {
+			q->statistic->bufferinuseempty++;
+			return retval;
+		}
+		if (q->flowbuffer->offsetpos - q->flowbuffer->buffer_in_use >=
+		    DATA_PACKAGE) {
+			q->statistic->readbehindbuffer++;
+			return retval;
+		}
+	/*end of tracefile reached*/	
+	} else {
+		q->statistic->novaliddata++;
+		return retval;
+	}
+	/* now it's safe to read */
+	memcpy(&variout, q->flowbuffer->offsetpos, 4);
+	q->flowbuffer->offsetpos += 4;
+
+	retval.delay = variout & mask_delay;
+	retval.head = (variout & mask_head) >> 29;
+
+	/* head 00 (0) -> normal delay
+	 *	01 (1) -> drop packet
+	 *	10 (2) -> duplicate
+	 *	11 (3) -> currupt
+	 */
+
+	switch (retval.head) {
+	case 0:
+		q->statistic->normaldelay++;
+		break;
+	case 1:
+		q->statistic->drops++;
+		break;
+	case 2:
+		q->statistic->dupl++;
+		break;
+	case 3:
+		q->statistic->corrupt++;
+	        break;
+	}
+
+	q->statistic->packetok++;
+	return retval;
+}
+
 /*
  * Insert one skb into qdisc.
  * Note: parent depends on return value to account for queue length.
@@ -156,15 +309,24 @@
 
 	pr_debug("netem_enqueue skb=%p\n", skb);
 
-	/* Random duplication */
-	if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
+	struct pktdelay tcndelay;
+	tcndelay.delay = 0;
+	tcndelay.head = 0;
+
+	if (q->trace) {
+		tcndelay = get_next_delay(q);
+	}
+
+ 	/* Random duplication */
+	if (tcndelay.head == 2 || (q->duplicate && q->duplicate >=
+				   get_crandom(&q->dup_cor)))
 		++count;
 
 	/* Random packet drop 0 => none, ~0 => all */
-	if (q->loss && q->loss >= get_crandom(&q->loss_cor))
+	if (!q->trace && q->loss && q->loss >= get_crandom(&q->loss_cor))
 		--count;
 
-	if (count == 0) {
+	if (count == 0||(tcndelay.head == 1)) {
 		sch->qstats.drops++;
 		kfree_skb(skb);
 		return NET_XMIT_BYPASS;
@@ -190,7 +352,8 @@
 	 * If packet is going to be hardware checksummed, then
 	 * do it now in software before we mangle it.
 	 */
-	if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
+	if ((!q->trace && q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) ||
+	    tcndelay.head == 3) {
 		if (!(skb = skb_unshare(skb, GFP_ATOMIC))
 		    || (skb->ip_summed == CHECKSUM_HW
 			&& skb_checksum_help(skb, 0))) {
@@ -207,9 +370,14 @@
 		psched_time_t now;
 		psched_tdiff_t delay;
 
-		delay = tabledist(q->latency, q->jitter,
-				  &q->delay_cor, q->delay_dist);
-
+		if (q->trace) {
+			delay = tcndelay.delay;
+			delay = delay * q->ticks;
+			delay = delay / 1000;
+		} else {
+			delay = tabledist(q->latency, q->jitter,
+					  &q->delay_cor, q->delay_dist);
+		}
 		PSCHED_GET_TIME(now);
 		PSCHED_TADD2(now, delay, cb->time_to_send);
 		++q->counter;
@@ -342,6 +510,61 @@
 	return ret;
 }
 
+static void reset_stats(struct netem_sched_data * q)
+{
+	memset(q->statistic, 0, sizeof(*(q->statistic)));
+	return;
+}
+
+static void free_flowbuffer(struct netem_sched_data * q)
+{
+	if (q->flowbuffer != NULL) {
+		q->tcnstop = 1;
+		q->newdataneeded = 1;
+		wake_up(&q->my_event);
+
+		if (q->flowbuffer->buffer1 != NULL) {
+			kfree(q->flowbuffer->buffer1);
+		}
+		if (q->flowbuffer->buffer2 != NULL) {
+			kfree(q->flowbuffer->buffer2);
+		}
+		kfree(q->flowbuffer);
+		kfree(q->statistic);
+		q->flowbuffer = NULL;
+		q->statistic = NULL;
+	}
+}
+
+static int init_flowbuffer(unsigned int fid, struct netem_sched_data * q)
+{
+	q->statistic = kmalloc(sizeof(*(q->statistic)), GFP_ATOMIC);
+	reset_stats(q);
+	init_waitqueue_head(&q->my_event);
+	int i, flowid = -1;
+	for(i = 0; i < MAX_FLOWS; i++) {
+		if(map[i].fid == 0) {
+			flowid = i;
+			map[i].fid = fid;
+			map[i].sched_data = q;
+			break;
+		}
+	}
+	if(flowid!=-1) {
+		q->flowbuffer = kmalloc(sizeof(*(q->flowbuffer)), GFP_ATOMIC);
+		q->flowbuffer->buffer1 = kmalloc(DATA_PACKAGE, GFP_ATOMIC);
+		q->flowbuffer->buffer2 = kmalloc(DATA_PACKAGE, GFP_ATOMIC);
+		q->flowbuffer->buffer_in_use = q->flowbuffer->buffer1;
+		q->flowbuffer->offsetpos = q->flowbuffer->buffer1;
+		q->flowbuffer->buffer1_empty = q->flowbuffer->buffer1;
+		q->flowbuffer->buffer2_empty = q->flowbuffer->buffer2;
+		q->flowbuffer->flowid = flowid; 
+		q->flowbuffer->validdataB1 = 0;
+		q->flowbuffer->validdataB2 = 0;
+	}
+	return flowid;
+}
+
 /*
  * Distribution data is a variable size payload containing
  * signed 16 bit values.
@@ -413,6 +636,32 @@
 	return 0;
 }
 
+static int get_trace(struct Qdisc *sch, const struct rtattr *attr)
+{
+	struct netem_sched_data *q = qdisc_priv(sch);
+	const struct tc_netem_trace *traceopt = RTA_DATA(attr);
+
+	if (RTA_PAYLOAD(attr) != sizeof(*traceopt))
+		return -EINVAL;
+
+	if (traceopt->fid) {
+		/*correction us -> ticks*/
+		q->ticks = traceopt->ticks;
+		int ind;
+		ind = init_flowbuffer(traceopt->fid, q);
+		if(ind < 0) {
+			printk("netem: maximum number of traces:%d"
+			       " change in net/flowseedprocfs.h\n", MAX_FLOWS);
+			return -EINVAL;
+		}
+		q->trace = ind + 1;
+
+	} else
+		q->trace = 0;
+	q->def = traceopt->def;
+	return 0;
+}
+
 /* Parse netlink message to set options */
 static int netem_change(struct Qdisc *sch, struct rtattr *opt)
 {
@@ -430,6 +679,14 @@
 		return ret;
 	}
 	
+	if (q->trace) {
+		int temp = q->trace - 1;
+		q->trace = 0;
+		map[temp].fid = 0;
+		reset_stats(q);
+		free_flowbuffer(q);
+	}
+
 	q->latency = qopt->latency;
 	q->jitter = qopt->jitter;
 	q->limit = qopt->limit;
@@ -476,6 +733,11 @@
 			if (ret)
 				return ret;
 		}
+		if (tb[TCA_NETEM_TRACE-1]) {
+			ret = get_trace(sch, tb[TCA_NETEM_TRACE-1]);
+			if (ret)
+				return ret;
+		}
 	}
 
 	return 0;
@@ -571,6 +833,7 @@
 	q->timer.function = netem_watchdog;
 	q->timer.data = (unsigned long) sch;
 
+	q->trace = 0;
 	q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops);
 	if (!q->qdisc) {
 		pr_debug("netem: qdisc create failed\n");
@@ -589,6 +852,12 @@
 {
 	struct netem_sched_data *q = qdisc_priv(sch);
 
+	if (q->trace) {
+		int temp = q->trace - 1;
+		q->trace = 0;
+		map[temp].fid = 0;
+		free_flowbuffer(q);
+	}
 	del_timer_sync(&q->timer);
 	qdisc_destroy(q->qdisc);
 	kfree(q->delay_dist);
@@ -603,6 +872,8 @@
 	struct tc_netem_corr cor;
 	struct tc_netem_reorder reorder;
 	struct tc_netem_corrupt corrupt;
+	struct tc_netem_trace traceopt;
+	struct tc_netem_stats tracestats;
 
 	qopt.latency = q->latency;
 	qopt.jitter = q->jitter;
@@ -625,6 +896,33 @@
 	corrupt.correlation = q->corrupt_cor.rho;
 	RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
 
+	traceopt.fid = q->trace;
+	traceopt.def = q->def;
+	traceopt.ticks = q->ticks;
+	RTA_PUT(skb, TCA_NETEM_TRACE, sizeof(traceopt), &traceopt);
+
+	if(q->trace){
+		tracestats.packetcount = q->statistic->packetcount;
+		tracestats.packetok = q->statistic->packetok;
+		tracestats.normaldelay = q->statistic->normaldelay;
+		tracestats.drops = q->statistic->drops;
+		tracestats.dupl = q->statistic->dupl;
+		tracestats.corrupt = q->statistic->corrupt;
+		tracestats.novaliddata = q->statistic->novaliddata;
+		tracestats.uninitialized = q->statistic->uninitialized;
+		tracestats.bufferunderrun = q->statistic->bufferunderrun;
+		tracestats.bufferinuseempty = q->statistic->bufferinuseempty;
+		tracestats.noemptybuffer = q->statistic->noemptybuffer;
+		tracestats.readbehindbuffer = q->statistic->readbehindbuffer;
+		tracestats.buffer1_reloads = q->statistic->buffer1_reloads;
+		tracestats.buffer2_reloads = q->statistic->buffer2_reloads;
+		tracestats.tobuffer1_switch = q->statistic->tobuffer1_switch;
+		tracestats.tobuffer2_switch = q->statistic->tobuffer2_switch;
+		tracestats.switch_to_emptybuffer1 = q->statistic->switch_to_emptybuffer1;
+		tracestats.switch_to_emptybuffer2 = q->statistic->switch_to_emptybuffer2;
+		RTA_PUT(skb, TCA_NETEM_STATS, sizeof(tracestats), &tracestats);
+	}
+
 	rta->rta_len = skb->tail - b;
 
 	return skb->len;
@@ -708,6 +1006,176 @@
 	return NULL;
 }
 
+/*configfs to read tcn delay values from userspace*/
+struct tcn_flow {
+	struct config_item item;
+};
+
+static struct tcn_flow *to_tcn_flow(struct config_item *item)
+{
+	return item ? container_of(item, struct tcn_flow, item) : NULL;
+}
+
+static struct configfs_attribute tcn_flow_attr_storeme = {
+	.ca_owner = THIS_MODULE,
+	.ca_name = "delayvalue",
+	.ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute *tcn_flow_attrs[] = {
+	&tcn_flow_attr_storeme,
+	NULL,
+};
+
+static ssize_t tcn_flow_attr_store(struct config_item *item,
+				       struct configfs_attribute *attr,
+				       const char *page, size_t count)
+{
+	char *p = (char *)page;
+
+	struct tcn_control *checkbuf;
+	if (count != DATA_PACKAGE_ID) {
+		printk("netem: Unexpected data received. %d\n", count);
+		return -EMSGSIZE;
+	}
+
+	int fid, i, validData = 0;
+	int flowid = -1;
+
+	memcpy(&fid, p + DATA_PACKAGE, sizeof(int));
+	memcpy(&validData, p + DATA_PACKAGE + sizeof(int), sizeof(int));
+
+	/*check whether this flow is registered */
+	for (i = 0; i < MAX_FLOWS; i++) {
+		if (map[i].fid == fid) {
+			flowid = i;
+			break;
+		}
+	}
+	/*exit if flow is not registered */
+	if (flowid < 0) {
+		printk("netem: Invalid FID received. Killing process.\n");
+		return -EINVAL;
+	}
+
+	checkbuf = map[flowid].sched_data->flowbuffer;
+	if (checkbuf == NULL) {
+		printk("netem: no flow registered");
+		return -ENOBUFS;
+	}
+
+	/* check if flowbuffer has empty buffer and copy data into it */
+	if (checkbuf->buffer1_empty != NULL) {
+		memcpy(checkbuf->buffer1, p, DATA_PACKAGE);
+		checkbuf->buffer1_empty = NULL;
+		checkbuf->validdataB1 = validData;
+		map[flowid].sched_data->statistic->buffer1_reloads++;
+
+	} else if (checkbuf->buffer2_empty != NULL) {
+		memcpy(checkbuf->buffer2, p, DATA_PACKAGE);
+		checkbuf->buffer2_empty = NULL;
+		checkbuf->validdataB2 = validData;
+		map[flowid].sched_data->statistic->buffer2_reloads++;
+
+	} else {
+		printk("netem: flow %d: no empty buffer. data loss.\n", flowid);
+		map[flowid].sched_data->statistic->noemptybuffer++;
+	}
+
+	if (validData) {
+		/* on initialization both buffers need data */
+		if (checkbuf->buffer2_empty != NULL) {
+			return DATA_PACKAGE_ID;
+		}
+		/* wait until new data is needed */
+		wait_event(map[flowid].sched_data->my_event,
+			   map[flowid].sched_data->newdataneeded);
+		map[flowid].sched_data->newdataneeded = 0;
+
+	}
+
+	if (map[flowid].sched_data->tcnstop) {
+		return -ECANCELED;
+	}
+
+	return DATA_PACKAGE_ID;
+
+}
+
+static void tcn_flow_release(struct config_item *item)
+{
+	kfree(to_tcn_flow(item));
+
+}
+
+static struct configfs_item_operations tcn_flow_item_ops = {
+	.release = tcn_flow_release,
+	.store_attribute = tcn_flow_attr_store,
+};
+
+static struct config_item_type tcn_flow_type = {
+	.ct_item_ops = &tcn_flow_item_ops,
+	.ct_attrs = tcn_flow_attrs,
+	.ct_owner = THIS_MODULE,
+};
+
+static struct config_item * tcn_make_item(struct config_group *group,
+						     const char *name)
+{
+	struct tcn_flow *tcn_flow;
+
+	tcn_flow = kmalloc(sizeof(struct tcn_flow), GFP_KERNEL);
+	if (!tcn_flow)
+		return NULL;
+
+	memset(tcn_flow, 0, sizeof(struct tcn_flow));
+
+	config_item_init_type_name(&tcn_flow->item, name,
+				   &tcn_flow_type);
+	return &tcn_flow->item;
+}
+
+static struct configfs_group_operations tcn_group_ops = {
+	.make_item = tcn_make_item,
+};
+
+static struct config_item_type tcn_type = {
+	.ct_group_ops = &tcn_group_ops,
+	.ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem tcn_subsys = {
+	.su_group = {
+		     .cg_item = {
+				 .ci_namebuf = "tcn",
+				 .ci_type = &tcn_type,
+				 },
+		     },
+};
+
+static int configfs_init(void)
+{
+	int ret;
+	struct configfs_subsystem *subsys;
+	subsys = &tcn_subsys;
+	config_group_init(&subsys->su_group);
+	init_MUTEX(&subsys->su_sem);
+	ret = configfs_register_subsystem(subsys);
+	if (ret) {
+		printk(KERN_ERR "Error %d while registering subsystem %s\n",
+		       ret, subsys->su_group.cg_item.ci_namebuf);
+		configfs_unregister_subsystem(&tcn_subsys);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void configfs_exit(void)
+{
+	configfs_unregister_subsystem(&tcn_subsys);
+}
+
 static struct Qdisc_class_ops netem_class_ops = {
 	.graft		=	netem_graft,
 	.leaf		=	netem_leaf,
@@ -736,14 +1204,26 @@
 	.owner		=	THIS_MODULE,
 };
 
+static int init_flow(void)
+{
+	int i;
+	for (i = 0; i < MAX_FLOWS; i++) {
+		map[i].fid = 0;
+		map[i].sched_data = NULL;
+	}
+	return 0;
+}
 
 static int __init netem_module_init(void)
 {
 	pr_info("netem: version " VERSION "\n");
+	configfs_init();
+	init_flow();
 	return register_qdisc(&netem_qdisc_ops);
 }
 static void __exit netem_module_exit(void)
 {
+	configfs_exit();
 	unregister_qdisc(&netem_qdisc_ops);
 }
 module_init(netem_module_init)
