#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/timer.h>
+#include <linux/list.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <asm/div64.h> /* do_div */
#include <asm/timex.h>
-#define VERSION "pktgen v2.64: Packet Generator for packet performance testing.\n"
+#define VERSION "pktgen v2.66: Packet Generator for packet performance testing.\n"
/* #define PG_DEBUG(a) a */
#define PG_DEBUG(a)
#define T_REMDEVALL (1<<3) /* Remove all devs */
#define T_REMDEV (1<<4) /* Remove one dev */
-/* Locks */
-#define thread_lock() down(&pktgen_sem)
-#define thread_unlock() up(&pktgen_sem)
-
/* If lock -- can be removed after some work */
#define if_lock(t) spin_lock(&(t->if_lock));
#define if_unlock(t) spin_unlock(&(t->if_lock));
char result[512];
struct pktgen_thread *pg_thread; /* the owner */
- struct pktgen_dev *next; /* Used for chaining in the thread's run-queue */
+ struct list_head list; /* Used for chaining in the thread's run-queue */
int running; /* if this changes to false, the test will stop */
struct pktgen_thread {
spinlock_t if_lock;
- struct pktgen_dev *if_list; /* All device here */
- struct pktgen_thread *next;
+ struct list_head if_list; /* All device here */
+ struct list_head th_list;
+ int removed;
char name[32];
char result[512];
u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
static int pg_clone_skb_d;
static int debug;
-static DECLARE_MUTEX(pktgen_sem);
-static struct pktgen_thread *pktgen_threads = NULL;
+static DEFINE_MUTEX(pktgen_thread_lock);
+static LIST_HEAD(pktgen_threads);
static struct notifier_block pktgen_notifier_block = {
.notifier_call = pktgen_device_event,
static int pktgen_thread_show(struct seq_file *seq, void *v)
{
struct pktgen_thread *t = seq->private;
- struct pktgen_dev *pkt_dev = NULL;
+ struct pktgen_dev *pkt_dev;
BUG_ON(!t);
seq_printf(seq, "Running: ");
if_lock(t);
- for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next)
+ list_for_each_entry(pkt_dev, &t->if_list, list)
if (pkt_dev->running)
seq_printf(seq, "%s ", pkt_dev->ifname);
seq_printf(seq, "\nStopped: ");
- for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next)
+ list_for_each_entry(pkt_dev, &t->if_list, list)
if (!pkt_dev->running)
seq_printf(seq, "%s ", pkt_dev->ifname);
if (copy_from_user(f, &user_buffer[i], len))
return -EFAULT;
i += len;
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
pktgen_add_device(t, f);
- thread_unlock();
+ mutex_unlock(&pktgen_thread_lock);
ret = count;
sprintf(pg_result, "OK: add_device=%s", f);
goto out;
}
if (!strcmp(name, "rem_device_all")) {
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
t->control |= T_REMDEVALL;
- thread_unlock();
+ mutex_unlock(&pktgen_thread_lock);
schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
ret = count;
sprintf(pg_result, "OK: rem_device_all");
if (!strcmp(name, "max_before_softirq")) {
len = num_arg(&user_buffer[i], 10, &value);
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
t->max_before_softirq = value;
- thread_unlock();
+ mutex_unlock(&pktgen_thread_lock);
ret = count;
sprintf(pg_result, "OK: max_before_softirq=%lu", value);
goto out;
struct pktgen_thread *t;
struct pktgen_dev *pkt_dev = NULL;
- t = pktgen_threads;
-
- while (t) {
+ list_for_each_entry(t, &pktgen_threads, th_list) {
pkt_dev = pktgen_find_dev(t, ifname);
if (pkt_dev) {
if (remove) {
}
break;
}
- t = t->next;
}
return pkt_dev;
}
int i = 0;
int ret = 0;
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
PG_DEBUG(printk("pktgen: pktgen_mark_device marking %s for removal\n",
ifname));
if (pkt_dev == NULL)
break; /* success */
- thread_unlock();
+ mutex_unlock(&pktgen_thread_lock);
PG_DEBUG(printk("pktgen: pktgen_mark_device waiting for %s "
"to disappear....\n", ifname));
schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
if (++i >= max_tries) {
printk("pktgen_mark_device: timed out after waiting "
}
- thread_unlock();
+ mutex_unlock(&pktgen_thread_lock);
return ret;
}
static void pktgen_run(struct pktgen_thread *t)
{
- struct pktgen_dev *pkt_dev = NULL;
+ struct pktgen_dev *pkt_dev;
int started = 0;
PG_DEBUG(printk("pktgen: entering pktgen_run. %p\n", t));
if_lock(t);
- for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next) {
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
/*
* setup odev and create initial packet.
static void pktgen_stop_all_threads_ifs(void)
{
- struct pktgen_thread *t = pktgen_threads;
+ struct pktgen_thread *t;
PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n"));
- thread_lock();
- while (t) {
+ mutex_lock(&pktgen_thread_lock);
+
+ list_for_each_entry(t, &pktgen_threads, th_list)
t->control |= T_STOP;
- t = t->next;
- }
- thread_unlock();
+
+ mutex_unlock(&pktgen_thread_lock);
}
static int thread_is_running(struct pktgen_thread *t)
{
- struct pktgen_dev *next;
+ struct pktgen_dev *pkt_dev;
int res = 0;
- for (next = t->if_list; next; next = next->next) {
- if (next->running) {
+ list_for_each_entry(pkt_dev, &t->if_list, list)
+ if (pkt_dev->running) {
res = 1;
break;
}
- }
return res;
}
static int pktgen_wait_all_threads_run(void)
{
- struct pktgen_thread *t = pktgen_threads;
+ struct pktgen_thread *t;
int sig = 1;
- while (t) {
+ mutex_lock(&pktgen_thread_lock);
+
+ list_for_each_entry(t, &pktgen_threads, th_list) {
sig = pktgen_wait_thread_run(t);
if (sig == 0)
break;
- thread_lock();
- t = t->next;
- thread_unlock();
}
- if (sig == 0) {
- thread_lock();
- while (t) {
+
+ if (sig == 0)
+ list_for_each_entry(t, &pktgen_threads, th_list)
t->control |= (T_STOP);
- t = t->next;
- }
- thread_unlock();
- }
+
+ mutex_unlock(&pktgen_thread_lock);
return sig;
}
static void pktgen_run_all_threads(void)
{
- struct pktgen_thread *t = pktgen_threads;
+ struct pktgen_thread *t;
PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n"));
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
- while (t) {
+ list_for_each_entry(t, &pktgen_threads, th_list)
t->control |= (T_RUN);
- t = t->next;
- }
- thread_unlock();
+
+ mutex_unlock(&pktgen_thread_lock);
schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
{
- struct pktgen_dev *next, *best = NULL;
+ struct pktgen_dev *pkt_dev, *best = NULL;
if_lock(t);
- for (next = t->if_list; next; next = next->next) {
- if (!next->running)
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
+ if (!pkt_dev->running)
continue;
if (best == NULL)
- best = next;
- else if (next->next_tx_us < best->next_tx_us)
- best = next;
+ best = pkt_dev;
+ else if (pkt_dev->next_tx_us < best->next_tx_us)
+ best = pkt_dev;
}
if_unlock(t);
return best;
static void pktgen_stop(struct pktgen_thread *t)
{
- struct pktgen_dev *next = NULL;
+ struct pktgen_dev *pkt_dev;
PG_DEBUG(printk("pktgen: entering pktgen_stop\n"));
if_lock(t);
- for (next = t->if_list; next; next = next->next) {
- pktgen_stop_device(next);
- if (next->skb)
- kfree_skb(next->skb);
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
+ pktgen_stop_device(pkt_dev);
+ if (pkt_dev->skb)
+ kfree_skb(pkt_dev->skb);
- next->skb = NULL;
+ pkt_dev->skb = NULL;
}
if_unlock(t);
*/
static void pktgen_rem_one_if(struct pktgen_thread *t)
{
- struct pktgen_dev *cur, *next = NULL;
+ struct list_head *q, *n;
+ struct pktgen_dev *cur;
PG_DEBUG(printk("pktgen: entering pktgen_rem_one_if\n"));
if_lock(t);
- for (cur = t->if_list; cur; cur = next) {
- next = cur->next;
+ list_for_each_safe(q, n, &t->if_list) {
+ cur = list_entry(q, struct pktgen_dev, list);
if (!cur->removal_mark)
continue;
static void pktgen_rem_all_ifs(struct pktgen_thread *t)
{
- struct pktgen_dev *cur, *next = NULL;
+ struct list_head *q, *n;
+ struct pktgen_dev *cur;
/* Remove all devices, free mem */
PG_DEBUG(printk("pktgen: entering pktgen_rem_all_ifs\n"));
if_lock(t);
- for (cur = t->if_list; cur; cur = next) {
- next = cur->next;
+ list_for_each_safe(q, n, &t->if_list) {
+ cur = list_entry(q, struct pktgen_dev, list);
if (cur->skb)
kfree_skb(cur->skb);
{
/* Remove from the thread list */
- struct pktgen_thread *tmp = pktgen_threads;
-
remove_proc_entry(t->name, pg_proc_dir);
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
- if (tmp == t)
- pktgen_threads = tmp->next;
- else {
- while (tmp) {
- if (tmp->next == t) {
- tmp->next = t->next;
- t->next = NULL;
- break;
- }
- tmp = tmp->next;
- }
- }
- thread_unlock();
+ list_del(&t->th_list);
+
+ mutex_unlock(&pktgen_thread_lock);
}
static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name));
pktgen_rem_thread(t);
+
+ t->removed = 1;
}
static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
const char *ifname)
{
- struct pktgen_dev *pkt_dev = NULL;
+ struct pktgen_dev *p, *pkt_dev = NULL;
if_lock(t);
- for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next) {
- if (strncmp(pkt_dev->ifname, ifname, IFNAMSIZ) == 0) {
+ list_for_each_entry(p, &t->if_list, list)
+ if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) {
+ pkt_dev = p;
break;
}
- }
if_unlock(t);
PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev));
rv = -EBUSY;
goto out;
}
- pkt_dev->next = t->if_list;
- t->if_list = pkt_dev;
+
+ list_add(&pkt_dev->list, &t->if_list);
pkt_dev->pg_thread = t;
pkt_dev->running = 0;
static struct pktgen_thread *__init pktgen_find_thread(const char *name)
{
- struct pktgen_thread *t = NULL;
+ struct pktgen_thread *t;
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
- t = pktgen_threads;
- while (t) {
- if (strcmp(t->name, name) == 0)
- break;
+ list_for_each_entry(t, &pktgen_threads, th_list)
+ if (strcmp(t->name, name) == 0) {
+ mutex_unlock(&pktgen_thread_lock);
+ return t;
+ }
- t = t->next;
- }
- thread_unlock();
- return t;
+ mutex_unlock(&pktgen_thread_lock);
+ return NULL;
}
static int __init pktgen_create_thread(const char *name, int cpu)
{
+ int err;
struct pktgen_thread *t = NULL;
struct proc_dir_entry *pe;
pe->proc_fops = &pktgen_thread_fops;
pe->data = t;
- t->next = pktgen_threads;
- pktgen_threads = t;
+ INIT_LIST_HEAD(&t->if_list);
+
+ list_add_tail(&t->th_list, &pktgen_threads);
- if (kernel_thread((void *)pktgen_thread_worker, (void *)t,
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND) < 0)
+ t->removed = 0;
+
+ err = kernel_thread((void *)pktgen_thread_worker, (void *)t,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (err < 0) {
printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu);
+ remove_proc_entry(t->name, pg_proc_dir);
+ list_del(&t->th_list);
+ kfree(t);
+ return err;
+ }
return 0;
}
static void _rem_dev_from_if_list(struct pktgen_thread *t,
struct pktgen_dev *pkt_dev)
{
- struct pktgen_dev *i, *prev = NULL;
-
- i = t->if_list;
+ struct list_head *q, *n;
+ struct pktgen_dev *p;
- while (i) {
- if (i == pkt_dev) {
- if (prev)
- prev->next = i->next;
- else
- t->if_list = NULL;
- break;
- }
- prev = i;
- i = i->next;
+ list_for_each_safe(q, n, &t->if_list) {
+ p = list_entry(q, struct pktgen_dev, list);
+ if (p == pkt_dev)
+ list_del(&p->list);
}
}
register_netdevice_notifier(&pktgen_notifier_block);
for_each_online_cpu(cpu) {
+ int err;
char buf[30];
sprintf(buf, "kpktgend_%i", cpu);
- pktgen_create_thread(buf, cpu);
+ err = pktgen_create_thread(buf, cpu);
+ if (err)
+ printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n",
+ cpu, err);
}
+
+ if (list_empty(&pktgen_threads)) {
+ printk("pktgen: ERROR: Initialization failed for all threads\n");
+ unregister_netdevice_notifier(&pktgen_notifier_block);
+ remove_proc_entry(PGCTRL, pg_proc_dir);
+ proc_net_remove(PG_PROC_DIR);
+ return -ENODEV;
+ }
+
return 0;
}
static void __exit pg_cleanup(void)
{
+ struct pktgen_thread *t;
+ struct list_head *q, *n;
wait_queue_head_t queue;
init_waitqueue_head(&queue);
/* Stop all interfaces & threads */
- while (pktgen_threads) {
- struct pktgen_thread *t = pktgen_threads;
- pktgen_threads->control |= (T_TERMINATE);
+ list_for_each_safe(q, n, &pktgen_threads) {
+ t = list_entry(q, struct pktgen_thread, th_list);
+ t->control |= (T_TERMINATE);
- wait_event_interruptible_timeout(queue, (t != pktgen_threads),
- HZ);
+ wait_event_interruptible_timeout(queue, (t->removed == 1), HZ);
}
/* Un-register us from receiving netdevice events */