You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
736 lines
15 KiB
736 lines
15 KiB
/* pg3.c: Packet Generator for packet performance testing.
|
|
*
|
|
* Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se>
|
|
* Uppsala University, Sweden
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
/*
|
|
|
|
A tool for loading a network with a preconfigurated packets. The tool is
|
|
implemented as a linux module. Parameters as output device IPG interpacket
|
|
packet, number of packets can be configured. pg uses already intalled
|
|
device driver output routine.
|
|
|
|
|
|
Additional hacking by:
|
|
|
|
Jens.Laas@data.slu.se
|
|
Improved by ANK. 010120.
|
|
Improved by ANK even more. 010212.
|
|
MAC address typo fixed. 010417 --ro
|
|
|
|
|
|
TODO:
|
|
* could release kernel lock yet.
|
|
|
|
|
|
HOWTO:
|
|
|
|
1. Compile module pg3.o and install it in the place where modprobe may find it.
|
|
2. Cut script "ipg" (see below).
|
|
3. Edit script to set preferred device and destination IP address.
|
|
4. . ipg
|
|
5. After this two commands are defined:
|
|
A. "pg" to start generator and to get results.
|
|
B. "pgset" to change generator parameters. F.e.
|
|
pgset "pkt_size 9014" sets packet size to 9014
|
|
pgset "frags 5" packet will consist of 5 fragments
|
|
pgset "count 200000" sets number of packets to send
|
|
pgset "ipg 5000" sets artificial gap inserted between packets
|
|
to 5000 nanoseconds
|
|
pgset "dst 10.0.0.1" sets IP destination address
|
|
(BEWARE! This generator is very aggressive!)
|
|
pgset "dstmac 00:00:00:00:00:00" sets MAC destination address
|
|
pgset stop aborts injection
|
|
|
|
Also, ^C aborts generator.
|
|
|
|
---- cut here
|
|
|
|
#! /bin/sh
|
|
|
|
modprobe pg3.o
|
|
|
|
function pgset() {
|
|
local result
|
|
|
|
echo $1 > /proc/net/pg
|
|
|
|
result=`cat /proc/net/pg | fgrep "Result: OK:"`
|
|
if [ "$result" = "" ]; then
|
|
cat /proc/net/pg | fgrep Result:
|
|
fi
|
|
}
|
|
|
|
function pg() {
|
|
echo inject > /proc/net/pg
|
|
cat /proc/net/pg
|
|
}
|
|
|
|
pgset "odev eth0"
|
|
pgset "dst 0.0.0.0"
|
|
|
|
---- cut here
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/malloc.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/inet.h>
|
|
#include <asm/byteorder.h>
|
|
#include <asm/bitops.h>
|
|
#include <asm/io.h>
|
|
#include <asm/dma.h>
|
|
|
|
#include <linux/in.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/udp.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/inetdevice.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/if_arp.h>
|
|
#include <net/checksum.h>
|
|
|
|
static char version[] __initdata =
|
|
"pg3.c: v1.0 010812: Packet Generator for packet performance testing.\n";
|
|
|
|
|
|
|
|
/* Parameters */
|
|
|
|
char pg_outdev[32], pg_dst[32];
|
|
int pkt_size=ETH_ZLEN;
|
|
int nfrags=0;
|
|
__u32 pg_count = 100000; /* Default No packets to send */
|
|
__u32 pg_ipg = 0; /* Default Interpacket gap in nsec */
|
|
|
|
/* Globar vars */
|
|
|
|
int debug;
|
|
int forced_stop;
|
|
int pg_cpu_speed;
|
|
int pg_busy;
|
|
|
|
static __u8 hh[14] = {
|
|
0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
|
|
|
|
/* We fill in SRC address later */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x08, 0x00
|
|
};
|
|
|
|
unsigned char *pg_dstmac = hh;
|
|
char pg_result[512];
|
|
|
|
|
|
static struct net_device *pg_setup_inject(u32 *saddrp)
|
|
{
|
|
int p1, p2;
|
|
struct net_device *odev;
|
|
u32 saddr;
|
|
|
|
rtnl_lock();
|
|
odev = __dev_get_by_name(pg_outdev);
|
|
if (!odev) {
|
|
sprintf(pg_result, "No such netdevice: \"%s\"", pg_outdev);
|
|
goto out_unlock;
|
|
}
|
|
|
|
if (odev->type != ARPHRD_ETHER) {
|
|
sprintf(pg_result, "Not ethernet device: \"%s\"", pg_outdev);
|
|
goto out_unlock;
|
|
}
|
|
|
|
if (!netif_running(odev)) {
|
|
sprintf(pg_result, "Device is down: \"%s\"", pg_outdev);
|
|
goto out_unlock;
|
|
}
|
|
|
|
for(p1=6,p2=0; p1 < odev->addr_len+6;p1++)
|
|
hh[p1]=odev->dev_addr[p2++];
|
|
|
|
saddr = 0;
|
|
if (odev->ip_ptr) {
|
|
struct in_device *in_dev = odev->ip_ptr;
|
|
|
|
if (in_dev->ifa_list)
|
|
saddr = in_dev->ifa_list->ifa_address;
|
|
}
|
|
atomic_inc(&odev->refcnt);
|
|
rtnl_unlock();
|
|
|
|
*saddrp = saddr;
|
|
return odev;
|
|
|
|
out_unlock:
|
|
rtnl_unlock();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
u32 idle_acc_lo, idle_acc_hi;
|
|
|
|
void nanospin(int pg_ipg)
|
|
{
|
|
u32 idle_start, idle;
|
|
|
|
idle_start = get_cycles();
|
|
|
|
for (;;) {
|
|
barrier();
|
|
idle = get_cycles() - idle_start;
|
|
if (idle*1000 >= pg_ipg*pg_cpu_speed)
|
|
break;
|
|
}
|
|
idle_acc_lo += idle;
|
|
if (idle_acc_lo < idle)
|
|
idle_acc_hi++;
|
|
}
|
|
|
|
int calc_mhz(void)
|
|
{
|
|
struct timeval start, stop;
|
|
u32 start_s, elapsed;
|
|
|
|
do_gettimeofday(&start);
|
|
start_s = get_cycles();
|
|
do {
|
|
barrier();
|
|
elapsed = get_cycles() - start_s;
|
|
if (elapsed == 0)
|
|
return 0;
|
|
} while (elapsed < 1000*50000);
|
|
do_gettimeofday(&stop);
|
|
return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
|
|
}
|
|
|
|
static void cycles_calibrate(void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<3; i++) {
|
|
int res = calc_mhz();
|
|
if (res > pg_cpu_speed)
|
|
pg_cpu_speed = res;
|
|
}
|
|
}
|
|
|
|
struct sk_buff *
|
|
fill_packet(struct net_device *odev, __u32 saddr)
|
|
{
|
|
struct sk_buff *skb;
|
|
__u8 *eth;
|
|
struct udphdr *udph;
|
|
int datalen, iplen;
|
|
struct iphdr *iph;
|
|
|
|
skb = alloc_skb(pkt_size+64+16, GFP_ATOMIC);
|
|
if (!skb) {
|
|
sprintf(pg_result, "No memory");
|
|
return NULL;
|
|
}
|
|
|
|
skb_reserve(skb, 16);
|
|
|
|
/* Reserve for ethernet and IP header */
|
|
eth = (__u8 *) skb_push(skb, 14);
|
|
iph = (struct iphdr*)skb_put(skb, sizeof( struct iphdr));
|
|
udph = (struct udphdr*)skb_put(skb, sizeof( struct udphdr));
|
|
|
|
/* Copy the ethernet header */
|
|
memcpy(eth, hh, 14);
|
|
|
|
datalen = pkt_size-14-20-8; /* Eth + IPh + UDPh */
|
|
if (datalen < 0)
|
|
datalen = 0;
|
|
|
|
udph->source= htons(9);
|
|
udph->dest= htons(9);
|
|
udph->len= htons(datalen+8); /* DATA + udphdr */
|
|
udph->check=0; /* No checksum */
|
|
|
|
iph->ihl=5;
|
|
iph->version=4;
|
|
iph->ttl=3;
|
|
iph->tos=0;
|
|
iph->protocol = IPPROTO_UDP; /* UDP */
|
|
iph->saddr = saddr;
|
|
iph->daddr = in_aton(pg_dst);
|
|
iph->frag_off = 0;
|
|
iplen = 20 + 8 + datalen;
|
|
iph->tot_len = htons(iplen);
|
|
iph->check = 0;
|
|
iph->check = ip_fast_csum((void *)iph, iph->ihl);
|
|
skb->protocol = __constant_htons(ETH_P_IP);
|
|
skb->mac.raw = ((u8*)iph) - 14;
|
|
skb->dev = odev;
|
|
skb->pkt_type = PACKET_HOST;
|
|
|
|
if (nfrags<=0) {
|
|
skb_put(skb, datalen);
|
|
} else {
|
|
int frags = nfrags;
|
|
int i;
|
|
|
|
if (frags > MAX_SKB_FRAGS)
|
|
frags = MAX_SKB_FRAGS;
|
|
if (datalen > frags*PAGE_SIZE) {
|
|
skb_put(skb, datalen-frags*PAGE_SIZE);
|
|
datalen = frags*PAGE_SIZE;
|
|
}
|
|
|
|
i = 0;
|
|
while (datalen > 0) {
|
|
struct page *page = alloc_pages(GFP_KERNEL, 0);
|
|
skb_shinfo(skb)->frags[i].page = page;
|
|
skb_shinfo(skb)->frags[i].page_offset = 0;
|
|
skb_shinfo(skb)->frags[i].size = (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
|
|
datalen -= skb_shinfo(skb)->frags[i].size;
|
|
skb->len += skb_shinfo(skb)->frags[i].size;
|
|
skb->data_len += skb_shinfo(skb)->frags[i].size;
|
|
i++;
|
|
skb_shinfo(skb)->nr_frags = i;
|
|
}
|
|
|
|
while (i < frags) {
|
|
int rem;
|
|
|
|
if (i == 0)
|
|
break;
|
|
|
|
rem = skb_shinfo(skb)->frags[i-1].size/2;
|
|
if (rem == 0)
|
|
break;
|
|
|
|
skb_shinfo(skb)->frags[i-1].size -= rem;
|
|
|
|
skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i-1];
|
|
get_page(skb_shinfo(skb)->frags[i].page);
|
|
skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i-1].page;
|
|
skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i-1].size;
|
|
skb_shinfo(skb)->frags[i].size = rem;
|
|
i++;
|
|
skb_shinfo(skb)->nr_frags = i;
|
|
}
|
|
}
|
|
|
|
return skb;
|
|
}
|
|
|
|
|
|
static void pg_inject(void)
|
|
{
|
|
u32 saddr;
|
|
struct net_device *odev;
|
|
struct sk_buff *skb;
|
|
struct timeval start, stop;
|
|
u32 total, idle;
|
|
int pc, lcount;
|
|
|
|
odev = pg_setup_inject(&saddr);
|
|
if (!odev)
|
|
return;
|
|
|
|
skb = fill_packet(odev, saddr);
|
|
if (skb == NULL)
|
|
goto out_reldev;
|
|
|
|
forced_stop = 0;
|
|
idle_acc_hi = 0;
|
|
idle_acc_lo = 0;
|
|
pc = 0;
|
|
lcount = pg_count;
|
|
do_gettimeofday(&start);
|
|
|
|
for(;;) {
|
|
spin_lock_bh(&odev->xmit_lock);
|
|
atomic_inc(&skb->users);
|
|
if (!netif_queue_stopped(odev)) {
|
|
if (odev->hard_start_xmit(skb, odev)) {
|
|
kfree_skb(skb);
|
|
if (net_ratelimit())
|
|
printk(KERN_INFO "Hard xmit error\n");
|
|
}
|
|
pc++;
|
|
} else {
|
|
kfree_skb(skb);
|
|
}
|
|
spin_unlock_bh(&odev->xmit_lock);
|
|
|
|
if (pg_ipg)
|
|
nanospin(pg_ipg);
|
|
if (forced_stop)
|
|
goto out_intr;
|
|
if (signal_pending(current))
|
|
goto out_intr;
|
|
|
|
if (--lcount == 0) {
|
|
if (atomic_read(&skb->users) != 1) {
|
|
u32 idle_start, idle;
|
|
|
|
idle_start = get_cycles();
|
|
while (atomic_read(&skb->users) != 1) {
|
|
if (signal_pending(current))
|
|
goto out_intr;
|
|
schedule();
|
|
}
|
|
idle = get_cycles() - idle_start;
|
|
idle_acc_lo += idle;
|
|
if (idle_acc_lo < idle)
|
|
idle_acc_hi++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (netif_queue_stopped(odev) || current->need_resched) {
|
|
u32 idle_start, idle;
|
|
|
|
idle_start = get_cycles();
|
|
do {
|
|
if (signal_pending(current))
|
|
goto out_intr;
|
|
if (!netif_running(odev))
|
|
goto out_intr;
|
|
if (current->need_resched)
|
|
schedule();
|
|
else
|
|
do_softirq();
|
|
} while (netif_queue_stopped(odev));
|
|
idle = get_cycles() - idle_start;
|
|
idle_acc_lo += idle;
|
|
if (idle_acc_lo < idle)
|
|
idle_acc_hi++;
|
|
}
|
|
}
|
|
|
|
do_gettimeofday(&stop);
|
|
|
|
total = (stop.tv_sec - start.tv_sec)*1000000 +
|
|
stop.tv_usec - start.tv_usec;
|
|
|
|
idle = (((idle_acc_hi<<20)/pg_cpu_speed)<<12)+idle_acc_lo/pg_cpu_speed;
|
|
|
|
if (1) {
|
|
char *p = pg_result;
|
|
|
|
p += sprintf(p, "OK: %u(c%u+d%u) usec, %u (%dbyte,%dfrags) %upps %uMB/sec",
|
|
total, total-idle, idle,
|
|
pc, skb->len, skb_shinfo(skb)->nr_frags,
|
|
((pc*1000)/(total/1000)),
|
|
(((pc*1000)/(total/1000))*pkt_size)/1024/1024
|
|
);
|
|
}
|
|
|
|
out_relskb:
|
|
kfree_skb(skb);
|
|
out_reldev:
|
|
dev_put(odev);
|
|
return;
|
|
|
|
out_intr:
|
|
sprintf(pg_result, "Interrupted");
|
|
goto out_relskb;
|
|
}
|
|
|
|
/* proc/net/pg */
|
|
|
|
static struct proc_dir_entry *pg_proc_ent = 0;
|
|
static struct proc_dir_entry *pg_busy_proc_ent = 0;
|
|
|
|
int proc_pg_busy_read(char *buf , char **start, off_t offset,
|
|
int len, int *eof, void *data)
|
|
{
|
|
char *p;
|
|
|
|
p = buf;
|
|
p += sprintf(p, "%d\n", pg_busy);
|
|
*eof = 1;
|
|
|
|
return p-buf;
|
|
}
|
|
|
|
int proc_pg_read(char *buf , char **start, off_t offset,
|
|
int len, int *eof, void *data)
|
|
{
|
|
char *p;
|
|
int i;
|
|
|
|
p = buf;
|
|
p += sprintf(p, "Params: count=%u pkt_size=%u frags %d ipg %u odev \"%s\" dst %s dstmac ",
|
|
pg_count, pkt_size, nfrags, pg_ipg,
|
|
pg_outdev, pg_dst);
|
|
for(i=0;i<6;i++)
|
|
p += sprintf(p, "%02X%s", pg_dstmac[i], i == 5 ? "\n" : ":");
|
|
|
|
if(pg_result[0])
|
|
p += sprintf(p, "Result: %s\n", pg_result);
|
|
else
|
|
p += sprintf(p, "Result: Idle\n");
|
|
*eof = 1;
|
|
return p-buf;
|
|
}
|
|
|
|
int count_trail_chars(const char *buffer, unsigned int maxlen)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; i<maxlen;i++) {
|
|
switch(buffer[i]) {
|
|
case '\"':
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
case ' ':
|
|
case '=':
|
|
break;
|
|
default:
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
return i;
|
|
}
|
|
|
|
unsigned long num_arg(const char *buffer, unsigned long maxlen,
|
|
unsigned long *num)
|
|
{
|
|
int i=0;
|
|
*num = 0;
|
|
|
|
for(; i<maxlen;i++) {
|
|
if( (buffer[i] >= '0') && (buffer[i] <= '9')) {
|
|
*num *= 10;
|
|
*num += buffer[i] -'0';
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int strn_len(const char *buffer, unsigned int maxlen)
|
|
{
|
|
int i=0;
|
|
|
|
for(; i<maxlen;i++)
|
|
switch(buffer[i]) {
|
|
case '\"':
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
case ' ':
|
|
goto done_str;
|
|
default:
|
|
}
|
|
done_str:
|
|
return i;
|
|
}
|
|
|
|
int proc_pg_write(struct file *file, const char *buffer,
|
|
unsigned long count, void *data)
|
|
{
|
|
int i=0, max, len;
|
|
char name[16], valstr[32];
|
|
unsigned long value = 0;
|
|
|
|
if (count < 1) {
|
|
sprintf(pg_result, "Wrong command format");
|
|
return -EINVAL;
|
|
}
|
|
|
|
max = count -i;
|
|
i += count_trail_chars(&buffer[i], max);
|
|
|
|
/* Read variable name */
|
|
|
|
len = strn_len(&buffer[i], sizeof(name)-1);
|
|
memset(name, 0, sizeof(name));
|
|
strncpy(name, &buffer[i], len);
|
|
i += len;
|
|
|
|
max = count -i;
|
|
len = count_trail_chars(&buffer[i], max);
|
|
i += len;
|
|
|
|
if (debug)
|
|
printk("pg: %s,%lu\n", name, count);
|
|
|
|
/* Only stop is allowed when we are running */
|
|
|
|
if(!strcmp(name, "stop")) {
|
|
forced_stop=1;
|
|
if (pg_busy)
|
|
strcpy(pg_result, "Stopping");
|
|
return count;
|
|
}
|
|
|
|
if (pg_busy) {
|
|
strcpy(pg_result, "Busy");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if(!strcmp(name, "pkt_size")) {
|
|
len = num_arg(&buffer[i], 10, &value);
|
|
i += len;
|
|
if (value < 14+20+8)
|
|
value = 14+20+8;
|
|
pkt_size = value;
|
|
sprintf(pg_result, "OK: pkt_size=%u", pkt_size);
|
|
return count;
|
|
}
|
|
if(!strcmp(name, "frags")) {
|
|
len = num_arg(&buffer[i], 10, &value);
|
|
i += len;
|
|
nfrags = value;
|
|
sprintf(pg_result, "OK: frags=%u", nfrags);
|
|
return count;
|
|
}
|
|
if(!strcmp(name, "ipg")) {
|
|
len = num_arg(&buffer[i], 10, &value);
|
|
i += len;
|
|
pg_ipg = value;
|
|
sprintf(pg_result, "OK: ipg=%u", pg_ipg);
|
|
return count;
|
|
}
|
|
if(!strcmp(name, "count")) {
|
|
len = num_arg(&buffer[i], 10, &value);
|
|
i += len;
|
|
pg_count = value;
|
|
sprintf(pg_result, "OK: count=%u", pg_count);
|
|
return count;
|
|
}
|
|
if(!strcmp(name, "odev")) {
|
|
len = strn_len(&buffer[i], sizeof(pg_outdev)-1);
|
|
memset(pg_outdev, 0, sizeof(pg_outdev));
|
|
strncpy(pg_outdev, &buffer[i], len);
|
|
i += len;
|
|
sprintf(pg_result, "OK: odev=%s", pg_outdev);
|
|
return count;
|
|
}
|
|
if(!strcmp(name, "dst")) {
|
|
len = strn_len(&buffer[i], sizeof(pg_dst)-1);
|
|
memset(pg_dst, 0, sizeof(pg_dst));
|
|
strncpy(pg_dst, &buffer[i], len);
|
|
if(debug)
|
|
printk("pg: dst set to: %s\n", pg_dst);
|
|
i += len;
|
|
sprintf(pg_result, "OK: dst=%s", pg_dst);
|
|
return count;
|
|
}
|
|
if(!strcmp(name, "dstmac")) {
|
|
char *v = valstr;
|
|
unsigned char *m = pg_dstmac;
|
|
|
|
len = strn_len(&buffer[i], sizeof(valstr)-1);
|
|
memset(valstr, 0, sizeof(valstr));
|
|
strncpy(valstr, &buffer[i], len);
|
|
i += len;
|
|
|
|
for(*m = 0;*v && m < pg_dstmac+6;v++) {
|
|
if(*v >= '0' && *v <= '9') {
|
|
*m *= 16;
|
|
*m += *v - '0';
|
|
}
|
|
if(*v >= 'A' && *v <= 'F') {
|
|
*m *= 16;
|
|
*m += *v - 'A' + 10;
|
|
}
|
|
if(*v >= 'a' && *v <= 'f') {
|
|
*m *= 16;
|
|
*m += *v - 'a' + 10;
|
|
}
|
|
if(*v == ':') {
|
|
m++;
|
|
*m = 0;
|
|
}
|
|
}
|
|
sprintf(pg_result, "OK: dstmac");
|
|
return count;
|
|
}
|
|
|
|
if (!strcmp(name, "inject") || !strcmp(name, "start") ) {
|
|
MOD_INC_USE_COUNT;
|
|
pg_busy = 1;
|
|
strcpy(pg_result, "Starting");
|
|
pg_inject();
|
|
pg_busy = 0;
|
|
MOD_DEC_USE_COUNT;
|
|
return count;
|
|
}
|
|
|
|
sprintf(pg_result, "No such parameter \"%s\"", name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int pg_init(void)
|
|
{
|
|
printk(version);
|
|
cycles_calibrate();
|
|
if (pg_cpu_speed == 0) {
|
|
printk("pg3: Error: your machine does not have working cycle counter.\n");
|
|
return -EINVAL;
|
|
}
|
|
if(!pg_proc_ent) {
|
|
pg_proc_ent = create_proc_entry("net/pg", 0600, 0);
|
|
if (pg_proc_ent) {
|
|
pg_proc_ent->read_proc = proc_pg_read;
|
|
pg_proc_ent->write_proc = proc_pg_write;
|
|
pg_proc_ent->data = 0;
|
|
}
|
|
pg_busy_proc_ent = create_proc_entry("net/pg_busy", 0, 0);
|
|
if (pg_busy_proc_ent) {
|
|
pg_busy_proc_ent->read_proc = proc_pg_busy_read;
|
|
pg_busy_proc_ent->data = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void pg_cleanup(void)
|
|
{
|
|
if (pg_proc_ent) {
|
|
remove_proc_entry("net/pg", NULL);
|
|
pg_proc_ent = 0;
|
|
remove_proc_entry("net/pg_busy", NULL);
|
|
pg_busy_proc_ent = 0;
|
|
}
|
|
}
|
|
|
|
module_init(pg_init);
|
|
module_exit(pg_cleanup);
|
|
|
|
|
|
#if LINUX_VERSION_CODE > 0x20118
|
|
MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
|
|
MODULE_DESCRIPTION("Packet Generator tool");
|
|
MODULE_PARM(pg_count, "i");
|
|
MODULE_PARM(pg_ipg, "i");
|
|
MODULE_PARM(pg_cpu_speed, "i");
|
|
#endif
|
|
|
|
/*
|
|
* Local variables:
|
|
* compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c pg3.c"
|
|
* End:
|
|
*/
|