diff -u'rNF^function' patchdir/linux-2.6.16.19/include/linux/pkt_sched.h original/linux-2.6.16.19/include/linux/pkt_sched.h --- patchdir/linux-2.6.16.19/include/linux/pkt_sched.h 2006-05-31 02:31:44.000000000 +0200 +++ original/linux-2.6.16.19/include/linux/pkt_sched.h 2006-07-17 16:41:53.000000000 +0200 @@ -443,6 +443,9 @@ __u32 gap; /* re-ordering gap (0 for none) */ __u32 duplicate; /* random packet dup (0=none ~0=100%) */ __u32 jitter; /* random jitter in latency (us) */ + __u32 trace; /* handle of trace*/ + __u32 index; /* interface index*/ + __u32 ticks; /* number of ticks corresponding to 1us*/ }; struct tc_netem_corr diff -u'rNF^function' patchdir/linux-2.6.16.19/include/net/flowseedprocfs.h original/linux-2.6.16.19/include/net/flowseedprocfs.h --- patchdir/linux-2.6.16.19/include/net/flowseedprocfs.h 1970-01-01 01:00:00.000000000 +0100 +++ original/linux-2.6.16.19/include/net/flowseedprocfs.h 2006-07-17 16:41:02.000000000 +0200 @@ -0,0 +1,79 @@ +/* flowseedprocfs.h header file for the netem trace enhancement + */ + +#ifndef _FLOWSEEDPROCFS_H +#define _FLOWSEEDPROCFS_H + +/* must be divisible by 4 (=#pkts)*/ +#define DATA_PACKAGE 40000 +#define DATA_PACKAGE_ID DATA_PACKAGE+sizeof(unsigned int)+sizeof(int) + +/* maximal amount of parallel flows */ +#define MAX_FLOWS 4 + +/* struct per flow - kernel */ +typedef struct _flowbuffer { + char * buffer1; + char * buffer2; + char * buffer_in_use; // buffer that is used by consumer + char * offsetpos; // pointer to actual pos in the buffer in use + char * buffer1_empty; // *buffer1 if buffer is empty, NULL else + char * buffer2_empty; // *buffer2 if buffer is empty, NULL else + int flowid; // NIST Net flow id [array index] + int upid; // pid of the user process corresponding to this flowbuffer + 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 +} flowbuffer; + +typedef struct _strdelay { + u_int8_t head; + int delay; +} strdelay; + +struct _proc_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; +}; + + +static strdelay get_next_delay(flowbuffer *myrbuf,unsigned int index); + +static int init_flowbuffer(unsigned int pid); + +static void free_flowbuffer(flowbuffer *victim); + +static int proc_write_input(struct file *file, + const char *buffer, + unsigned long count, + void *data); + +static int proc_read_stats(char *page, char **start, + off_t off, int count, + int *eof, void *data); + +static int proc_write_stats(struct file *file, + const char *buffer, + unsigned long count, + void *data); + +static void reset_stats(int i); +static int init_procfs(void); +static void cleanup_procfs(void); + +#endif diff -u'rNF^function' patchdir/linux-2.6.16.19/net/sched/Makefile original/linux-2.6.16.19/net/sched/Makefile --- patchdir/linux-2.6.16.19/net/sched/Makefile 2006-05-31 02:31:44.000000000 +0200 +++ original/linux-2.6.16.19/net/sched/Makefile 2006-07-17 16:40:08.000000000 +0200 @@ -27,7 +27,7 @@ obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o -obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o +obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o obj-$(CONFIG_NET_CLS_U32) += cls_u32.o obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o obj-$(CONFIG_NET_CLS_FW) += cls_fw.o diff -u'rNF^function' patchdir/linux-2.6.16.19/net/sched/sch_netem.c original/linux-2.6.16.19/net/sched/sch_netem.c --- patchdir/linux-2.6.16.19/net/sched/sch_netem.c 2006-05-31 02:31:44.000000000 +0200 +++ original/linux-2.6.16.19/net/sched/sch_netem.c 2006-07-18 10:05:48.000000000 +0200 @@ -11,6 +11,9 @@ * * Authors: Stephen Hemminger * Catalin(ux aka Dino) BOIE + * netem trace enhancement: Ariane Keller ETH Zurich + * Rainer Baumann ETH Zurich + * Ulrich Fiedler ETH Zurich */ #include @@ -22,10 +25,16 @@ #include #include #include - +#include +#include #include -#define VERSION "1.2" +#include "net/flowseedprocfs.h" + +#define VERSION "1.3" +#define bzero(addr, len) memset(addr, 0, len) + +//----------------------------------------- /* 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 tcn.hypert.net. */ struct netem_sched_data { @@ -66,6 +80,9 @@ u32 duplicate; u32 reorder; u32 corrupt; + u32 trace; + u32 index; + u32 ticks; struct crndstate { unsigned long last; @@ -83,6 +100,20 @@ psched_time_t time_to_send; }; + +/*trace extension*/ +int mask_head = -536870912; // 11100000000000000000000000000000 +int mask_delay = 536870911; // 00011111111111111111111111111111 + +/* the buffer procfs is writing into after copy_from_user */ +char * procbuf = NULL; +static struct proc_dir_entry *netem_dir, *netem_input_file, *netem_stats_file; +flowbuffer *flowbufferptr[MAX_FLOWS]; +unsigned int map[MAX_FLOWS]; +struct _proc_stats procstats[MAX_FLOWS]; +/*end trace extension*/ + + /* init_crandom - initialize correlated random number generator * Use entropy source for initial seed. */ @@ -153,18 +184,26 @@ struct sk_buff *skb2; int ret; int count = 1; - + flowbuffer *mybuf; + strdelay mydelay; + mydelay.delay=0; //inizialize to 0 delay and no duplication + mydelay.head=0; pr_debug("netem_enqueue skb=%p\n", skb); + if(q->trace){ + mybuf=flowbufferptr[(q->trace)-1]; + mydelay=get_next_delay(mybuf,q->index); + } + /* Random duplication */ - if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) + if (mydelay.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||(mydelay.head==1)) { sch->qstats.drops++; kfree_skb(skb); return NET_XMIT_DROP; @@ -175,13 +214,14 @@ * qdisc tree, since parent queuer expects that only one * skb will be queued. */ - if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { - struct Qdisc *rootq = sch->dev->qdisc; - u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ - q->duplicate = 0; - rootq->enqueue(skb2, rootq); - q->duplicate = dupsave; + if ((count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL)) { + struct Qdisc *rootq = sch->dev->qdisc; + u32 dupsave = q->duplicate; /* prevent duplicating a dup...*/ + q->duplicate = 0; + rootq->enqueue(skb2, rootq); + q->duplicate = dupsave; + } /* @@ -190,7 +230,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))||mydelay.head==3) { if (!(skb = skb_unshare(skb, GFP_ATOMIC)) || (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb, 0))) { @@ -201,17 +242,23 @@ skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8); } - if (q->gap == 0 /* not doing reordering */ + if ((q->gap == 0 /* not doing reordering */ || q->counter < q->gap /* inside last reordering gap */ - || q->reorder < get_crandom(&q->reorder_cor)) { + || q->reorder < get_crandom(&q->reorder_cor))) { psched_time_t now; psched_tdiff_t delay; - - delay = tabledist(q->latency, q->jitter, + + if(q->trace){ + delay=mydelay.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); + PSCHED_TADD2(now, delay, cb->time_to_send); //add delay to packet ++q->counter; ret = q->qdisc->enqueue(skb, q->qdisc); } else { @@ -233,6 +280,7 @@ pr_debug("netem: enqueue ret %d\n", ret); return ret; + } /* Requeue packets but don't change time stamp */ @@ -268,6 +316,8 @@ skb = q->qdisc->dequeue(q->qdisc); if (skb) { + + const struct netem_skb_cb *cb = (const struct netem_skb_cb *)skb->cb; psched_time_t now; @@ -282,7 +332,6 @@ return skb; } else { psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now); - if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { sch->qstats.drops++; @@ -312,8 +361,7 @@ static void netem_reset(struct Qdisc *sch) { - struct netem_sched_data *q = qdisc_priv(sch); - + struct netem_sched_data *q = qdisc_priv(sch); qdisc_reset(q->qdisc); sch->q.qlen = 0; sch->flags &= ~TCQ_F_THROTTLED; @@ -416,7 +464,7 @@ /* Parse netlink message to set options */ static int netem_change(struct Qdisc *sch, struct rtattr *opt) { - struct netem_sched_data *q = qdisc_priv(sch); + struct netem_sched_data *q = qdisc_priv(sch); struct tc_netem_qopt *qopt; int ret; @@ -438,6 +486,30 @@ q->loss = qopt->loss; q->duplicate = qopt->duplicate; + if(q->trace){ //there is already a flowseed process running -> kill actual one + int temp=q->trace-1; + q->trace=0; + reset_stats(temp); + free_flowbuffer(flowbufferptr[temp]); + } + if(qopt->trace){// the new one is a trace + //correction us -> ticks + q->ticks=qopt->ticks; + int ind; + ind=init_flowbuffer(qopt->trace); + if(ind<0){ //there is no more space + printk("netem: maximum number of traces:%d change in net/flowseedprocfs.h\n",MAX_FLOWS); + kill_proc(qopt->trace,SIGKILL,1); //kill flowseed process + kill_proc(qopt->trace,SIGCONT,1); //wake him up to be killed + return -EINVAL; + } + q->trace=ind+1; + + } + else + q->trace = 0; + q->index=qopt->index; + /* for compatiablity with earlier versions. * if gap is set, need to assume 100% probablity */ @@ -452,7 +524,7 @@ RTA_DATA(opt) + sizeof(*qopt), RTA_PAYLOAD(opt) - sizeof(*qopt))) return -EINVAL; - + if (tb[TCA_NETEM_CORR-1]) { ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]); if (ret) @@ -589,6 +661,12 @@ { struct netem_sched_data *q = qdisc_priv(sch); + if(q->trace){ + int temp=q->trace-1; + q->trace=0; //first: stop reading values form buffer + free_flowbuffer(flowbufferptr[temp]); //second: delete buffer + } + del_timer_sync(&q->timer); qdisc_destroy(q->qdisc); kfree(q->delay_dist); @@ -610,6 +688,11 @@ qopt.loss = q->loss; qopt.gap = q->gap; qopt.duplicate = q->duplicate; + + qopt.trace = q->trace; + qopt.index = q->index; + qopt.ticks = q->ticks; + RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt); cor.delay_corr = q->delay_cor.rho; @@ -708,6 +791,458 @@ return NULL; } +/*functions of the trace enhancement*/ + +/* 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 (flowbuffer *myrbuf) { + if (myrbuf->buffer_in_use == myrbuf->buffer1) { + myrbuf->buffer1_empty = myrbuf->buffer1; + + if (myrbuf->buffer2_empty!=NULL) { + procstats[myrbuf->flowid].switch_to_emptybuffer2++; + return -EFAULT; + }else{ + procstats[myrbuf->flowid].tobuffer2_switch++; + } + + myrbuf->buffer_in_use = myrbuf->buffer2; + myrbuf->offsetpos =myrbuf->buffer2; + + }else { + myrbuf->buffer2_empty = myrbuf->buffer2; + + if (myrbuf->buffer1_empty!=NULL) { + procstats[myrbuf->flowid].switch_to_emptybuffer1++; + return -EFAULT; + }else{ + procstats[myrbuf->flowid].tobuffer1_switch++; + } + + myrbuf->buffer_in_use = myrbuf->buffer1; + myrbuf->offsetpos = myrbuf->buffer1; + + } + /*the flowseed process can send more data*/ + kill_proc(myrbuf->upid,SIGCONT,1); + + return 0; +} + +/* return delay struct with delay and drop/dupl/corrupt option */ +static strdelay get_next_delay(flowbuffer *myrbuf, unsigned int index) { + + strdelay retval; + bzero(&retval, sizeof(retval)); + + int variout; + + /*choose whether to drop or 0 delay packets on default (on error or on end off tracefile) */ + retval.head = index; + retval.delay=0; + /* sanity check... does our flow exist? */ + if (myrbuf == NULL) { + printk("netem: read from an uninitialized flow.\n"); + procstats[myrbuf->flowid].uninitialized++; + return retval; + } + + procstats[myrbuf->flowid].packetcount++; + + /* check if we have to reload a buffer */ + + if (myrbuf->offsetpos - myrbuf->buffer_in_use == DATA_PACKAGE) { + + reload_flowbuffer(myrbuf); + } + + /*do checks only if the Data is valid (e.g. not on end of Tracefile)*/ + if((myrbuf->buffer_in_use == myrbuf->buffer1&&myrbuf->validDataB1)|| + ( myrbuf->buffer_in_use == myrbuf->buffer2&&myrbuf->validDataB2)){ + + /* sanity check... empty buffers? */ + if ((myrbuf->buffer1_empty != NULL) && (myrbuf->buffer2_empty != NULL)) { + procstats[myrbuf->flowid].bufferunderrun++; + return retval; + } + + /* sanity check ... active buffer empty? */ + if (myrbuf->buffer1_empty == myrbuf->buffer_in_use || + myrbuf->buffer2_empty == myrbuf->buffer_in_use) { + procstats[myrbuf->flowid].bufferinuseempty++; + return retval; + } + + /* offset check */ + if (myrbuf->offsetpos - myrbuf->buffer_in_use >= DATA_PACKAGE) { + procstats[myrbuf->flowid].readbehindbuffer++; + return retval; + } + }else{ //end of tracefile reached + procstats[myrbuf->flowid].noValidData++; + return retval; + } + /* now it's safe to read */ + memcpy(&variout, myrbuf->offsetpos, 4); + myrbuf->offsetpos+=4; + + /* printout what we've read */ + 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: + procstats[myrbuf->flowid].normaldelay++; + break; + case 1: + procstats[myrbuf->flowid].drops++; + break; + case 2: + procstats[myrbuf->flowid].dupl++; + break; + case 3: + procstats[myrbuf->flowid].corrupt++; + break; + } + + procstats[myrbuf->flowid].packetok++; + + return retval; +} + + +static void free_flowbuffer(flowbuffer *victim) { + + /* flowseed delete section */ + int flowid=0, upid=0; + + + /* if the bufferptr ist NULL, there isn't a corresponding process and tracefile + * to this flow and nothing has to be deleted here. + */ + + if (victim != NULL) { + + upid = victim->upid; + if (upid > 0) { + kill_proc(upid,SIGKILL,1); + kill_proc(upid,SIGCONT,1); + } + + flowid=victim->flowid; + map[flowid]=0; + flowbufferptr[flowid]=NULL; + + if(victim->buffer1!=NULL){ + kfree(victim->buffer1); + } + if(victim->buffer2!=NULL) + kfree(victim->buffer2); + + kfree(victim); + + /* set the bufferptr to zero or bad things will happen... */ + victim=NULL; + + } else{ + printk("netem: can't free given flowbuffer, nullpointer\n"); + } +} + +static int init_flowbuffer(unsigned int pid) { + int i,flowid=-1; + flowbuffer *mybufB; + + for(i=0;ibuffer1 = kmalloc(DATA_PACKAGE,GFP_ATOMIC); + mybufB->buffer2 = kmalloc(DATA_PACKAGE,GFP_ATOMIC); + mybufB->buffer_in_use = mybufB->buffer1; + mybufB->offsetpos = mybufB->buffer1; + mybufB->buffer1_empty = mybufB->buffer1; + mybufB->buffer2_empty = mybufB->buffer2; + mybufB->flowid=flowid; + mybufB->upid=pid; + mybufB->validDataB1=0; + mybufB->validDataB2=0; + } + + return flowid; /*-1 if no more space exists, index in flowbufferptr otherwise*/ + +} + +static int proc_write_input(struct file *file, + const char *buffer, + unsigned long count, + void *data) +{ + int len; + file=NULL; + data=NULL; + flowbuffer *mybufA; + if(count != DATA_PACKAGE_ID) { + printk("netem: Unexpected data received. Size too big.... exit.\n"); + + return -EFAULT; + } + else + len = count; + + if(copy_from_user(procbuf, buffer, len)) { + return -EFAULT; + } + + int upid,i,validData=0; + int flowid=-1; + + memcpy(&upid, procbuf+DATA_PACKAGE, sizeof(int)); + memcpy(&validData,procbuf+DATA_PACKAGE+sizeof(int),sizeof(int)); + + + /*check whether this process is allowed to send data*/ + for(i=0;ibuffer1_empty != NULL) { + memcpy(mybufA->buffer1, procbuf, DATA_PACKAGE); + mybufA->buffer1_empty = NULL; + mybufA->validDataB1=validData; + procstats[flowid].buffer1_reloads++; + + } else if (mybufA->buffer2_empty != NULL) { + memcpy(mybufA->buffer2, procbuf, DATA_PACKAGE); + mybufA->buffer2_empty = NULL; + mybufA->validDataB2=validData; + procstats[flowid].buffer2_reloads++; + + } else { + printk("netem: flow %d: no empty buffer. data loss. exit.\n",flowid); + procstats[flowid].noemptybuffer++; + } + + if(validData){ + /* send stop signal to process if no more empty buffers exist */ + kill_proc(upid,SIGSTOP,1); + + /* if buffers are loaded the first time, only buffer1 gets data. the + * following call sends a start for the process to send again data for buffer2 */ + if (mybufA->buffer2_empty != NULL) { + kill_proc(upid,SIGCONT,1); + } + + } + + return DATA_PACKAGE_ID; +} + +static int proc_read_stats(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + start=NULL; + off=0; + count=0; + data=NULL; + eof=NULL; + + char * message; + message = kmalloc(1024,GFP_ATOMIC); + + int i, offset=0; + bzero(message, 1024); + + for (i=0; i < MAX_FLOWS; i++) { + + if (procstats[i].packetcount <= 0) continue; + + bzero(message, 1024); + + sprintf(message, "Statistics for Flow %2d\n" + "----------------------\n" + "Packet count: %d\n" + "Packets ok: %d\n" + "Packets with normal Delay: %d\n" + "Duplicated Packets: %d\n" + "Drops on Request: %d\n" + "Corrupted Packets: %d\n" + "No valid data available: %d\n" + "Uninitialized access: %d\n" + "Bufferunderruns: %d\n" + "Use of empty Buffer: %d\n" + "No empty Buffer: %d\n" + "Read behind Buffer: %d\n" + "Buffer1 reloads: %d\n" + "Buffer2 reloads: %d\n" + "Switches to Buffer1: %d\n" + "Switches to Buffer2: %d\n" + "Switches to empty Buffer1: %d\n" + "Switches to empty Buffer2: %d\n\n", + i, + procstats[i].packetcount, procstats[i].packetok, + procstats[i].normaldelay, procstats[i].dupl, + procstats[i].drops, procstats[i].corrupt,procstats[i].noValidData, + procstats[i].uninitialized, procstats[i].bufferunderrun, + procstats[i].bufferinuseempty, procstats[i].noemptybuffer, + procstats[i].readbehindbuffer, procstats[i].buffer1_reloads, + procstats[i].buffer2_reloads, procstats[i].tobuffer1_switch, + procstats[i].tobuffer2_switch, + procstats[i].switch_to_emptybuffer1, + procstats[i].switch_to_emptybuffer2); + + memcpy(page+offset, message, strlen(message)); + + offset+=strlen(message); + } + + + kfree(message); + return offset; +} + +/* a write to /proc/netem/stats resets statistic*/ +static int proc_write_stats(struct file *file, + const char *buffer, + unsigned long count, + void *data) +{ + file=NULL; + data=NULL; + + /* clean up */ + bzero(&procstats, sizeof(procstats)); + + return count; +} + +static void reset_stats(int i) +{ + procstats[i].packetcount=0; + procstats[i].packetok=0; + procstats[i].normaldelay=0; + procstats[i].dupl=0; + procstats[i].drops=0; + procstats[i].corrupt=0; + procstats[i].noValidData=0; + procstats[i].uninitialized=0; + procstats[i].bufferunderrun=0; + procstats[i].bufferinuseempty=0; + procstats[i].noemptybuffer=0; + procstats[i].readbehindbuffer=0; + procstats[i].buffer1_reloads=0; + procstats[i].buffer2_reloads=0; + procstats[i].tobuffer1_switch=0; + procstats[i].tobuffer2_switch=0; + procstats[i].switch_to_emptybuffer1=0; + procstats[i].switch_to_emptybuffer2=0; + return; +} + +static int init_procfs(void) +{ + int rv = 0; + bzero(&procstats, sizeof(procstats)); + + /* allocate buffer for data from procfs */ + procbuf = vmalloc(DATA_PACKAGE_ID); + + /* initialize flowbufferpointers all to NULL */ + int i; + for (i = 0 ; i < MAX_FLOWS ; i++){ + flowbufferptr[i] = NULL; + map[i]=0; + } + + /* create directory */ + netem_dir = proc_mkdir("netem", NULL); + if(netem_dir == NULL) { + rv = -ENOMEM; + goto out; + } + + netem_dir->owner = THIS_MODULE; + + /* create files */ + netem_input_file = create_proc_entry("input", 0644, netem_dir); + if(netem_input_file == NULL) { + rv = -ENOMEM; + goto no_input; + } + + netem_stats_file = create_proc_entry("stats", 0644, netem_dir); + if(netem_stats_file == NULL) { + rv = -ENOMEM; + goto no_stats; + } + + netem_input_file->write_proc = proc_write_input; + netem_input_file->owner = THIS_MODULE; + + netem_stats_file->read_proc = proc_read_stats; + netem_stats_file->write_proc = proc_write_stats; + netem_stats_file->owner = THIS_MODULE; + + + /* everything OK */ + return 0; + +no_stats: + remove_proc_entry("stats", netem_dir); +no_input: + remove_proc_entry("input", netem_dir); + remove_proc_entry("netem", NULL); +out: + return rv; +} + + +static void cleanup_procfs(void) +{ + remove_proc_entry("input", netem_dir); + remove_proc_entry("stats", netem_dir); + remove_proc_entry("netem", NULL); + vfree(procbuf); + + /* free allocated mem for flowbufferpointers */ + int i; + for (i = 0 ; i < MAX_FLOWS ; i++) { + if (flowbufferptr[i] != NULL) { + kfree(flowbufferptr[i]->buffer1); + kfree(flowbufferptr[i]->buffer2); + } + } +} +/*end functions of trace enhancement*/ + static struct Qdisc_class_ops netem_class_ops = { .graft = netem_graft, .leaf = netem_leaf, @@ -740,12 +1275,17 @@ static int __init netem_module_init(void) { pr_info("netem: version " VERSION "\n"); + init_procfs(); return register_qdisc(&netem_qdisc_ops); } static void __exit netem_module_exit(void) { unregister_qdisc(&netem_qdisc_ops); + cleanup_procfs(); } module_init(netem_module_init) module_exit(netem_module_exit) MODULE_LICENSE("GPL"); + + +