Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ee74baa7 authored by David S. Miller's avatar David S. Miller
Browse files

[PKTGEN]: Convert to kthread API.



Based upon a suggestion from Christoph Hellwig.

This fixes various races in module load/unload handling
too.

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3136dcb3
Loading
Loading
Loading
Loading
+48 −108
Original line number Original line Diff line number Diff line
@@ -148,6 +148,7 @@
#include <linux/seq_file.h>
#include <linux/seq_file.h>
#include <linux/wait.h>
#include <linux/wait.h>
#include <linux/etherdevice.h>
#include <linux/etherdevice.h>
#include <linux/kthread.h>
#include <net/checksum.h>
#include <net/checksum.h>
#include <net/ipv6.h>
#include <net/ipv6.h>
#include <net/addrconf.h>
#include <net/addrconf.h>
@@ -360,8 +361,7 @@ struct pktgen_thread {
	spinlock_t if_lock;
	spinlock_t if_lock;
	struct list_head if_list;	/* All device here */
	struct list_head if_list;	/* All device here */
	struct list_head th_list;
	struct list_head th_list;
	int removed;
	struct task_struct *tsk;
	char name[32];
	char result[512];
	char result[512];
	u32 max_before_softirq;	/* We'll call do_softirq to prevent starvation. */
	u32 max_before_softirq;	/* We'll call do_softirq to prevent starvation. */


@@ -1689,7 +1689,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
	BUG_ON(!t);
	BUG_ON(!t);


	seq_printf(seq, "Name: %s  max_before_softirq: %d\n",
	seq_printf(seq, "Name: %s  max_before_softirq: %d\n",
		   t->name, t->max_before_softirq);
		   t->tsk->comm, t->max_before_softirq);


	seq_printf(seq, "Running: ");
	seq_printf(seq, "Running: ");


@@ -3112,7 +3112,7 @@ static void pktgen_rem_thread(struct pktgen_thread *t)
{
{
	/* Remove from the thread list */
	/* Remove from the thread list */


	remove_proc_entry(t->name, pg_proc_dir);
	remove_proc_entry(t->tsk->comm, pg_proc_dir);


	mutex_lock(&pktgen_thread_lock);
	mutex_lock(&pktgen_thread_lock);


@@ -3260,58 +3260,40 @@ out:;
 * Main loop of the thread goes here
 * Main loop of the thread goes here
 */
 */


static void pktgen_thread_worker(struct pktgen_thread *t)
static int pktgen_thread_worker(void *arg)
{
{
	DEFINE_WAIT(wait);
	DEFINE_WAIT(wait);
	struct pktgen_thread *t = arg;
	struct pktgen_dev *pkt_dev = NULL;
	struct pktgen_dev *pkt_dev = NULL;
	int cpu = t->cpu;
	int cpu = t->cpu;
	sigset_t tmpsig;
	u32 max_before_softirq;
	u32 max_before_softirq;
	u32 tx_since_softirq = 0;
	u32 tx_since_softirq = 0;


	daemonize("pktgen/%d", cpu);
	BUG_ON(smp_processor_id() != cpu);

	/* Block all signals except SIGKILL, SIGSTOP and SIGTERM */

	spin_lock_irq(&current->sighand->siglock);
	tmpsig = current->blocked;
	siginitsetinv(&current->blocked,
		      sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGTERM));

	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	/* Migrate to the right CPU */
	set_cpus_allowed(current, cpumask_of_cpu(cpu));
	if (smp_processor_id() != cpu)
		BUG();


	init_waitqueue_head(&t->queue);
	init_waitqueue_head(&t->queue);


	t->control &= ~(T_TERMINATE);
	t->control &= ~(T_RUN);
	t->control &= ~(T_STOP);
	t->control &= ~(T_REMDEVALL);
	t->control &= ~(T_REMDEV);

	t->pid = current->pid;
	t->pid = current->pid;


	PG_DEBUG(printk("pktgen: starting pktgen/%d:  pid=%d\n", cpu, current->pid));
	PG_DEBUG(printk("pktgen: starting pktgen/%d:  pid=%d\n", cpu, current->pid));


	max_before_softirq = t->max_before_softirq;
	max_before_softirq = t->max_before_softirq;


	__set_current_state(TASK_INTERRUPTIBLE);
	set_current_state(TASK_INTERRUPTIBLE);
	mb();


	while (1) {
	while (!kthread_should_stop()) {

		pkt_dev = next_to_run(t);
		__set_current_state(TASK_RUNNING);


		/*
		if (!pkt_dev &&
		 * Get next dev to xmit -- if any.
		    (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV))
		 */
		    == 0) {
			prepare_to_wait(&(t->queue), &wait,
					TASK_INTERRUPTIBLE);
			schedule_timeout(HZ / 10);
			finish_wait(&(t->queue), &wait);
		}


		pkt_dev = next_to_run(t);
		__set_current_state(TASK_RUNNING);


		if (pkt_dev) {
		if (pkt_dev) {


@@ -3329,21 +3311,8 @@ static void pktgen_thread_worker(struct pktgen_thread *t)
					do_softirq();
					do_softirq();
				tx_since_softirq = 0;
				tx_since_softirq = 0;
			}
			}
		} else {
			prepare_to_wait(&(t->queue), &wait, TASK_INTERRUPTIBLE);
			schedule_timeout(HZ / 10);
			finish_wait(&(t->queue), &wait);
		}
		}


		/*
		 * Back from sleep, either due to the timeout or signal.
		 * We check if we have any "posted" work for us.
		 */

		if (t->control & T_TERMINATE || signal_pending(current))
			/* we received a request to terminate ourself */
			break;

		if (t->control & T_STOP) {
		if (t->control & T_STOP) {
			pktgen_stop(t);
			pktgen_stop(t);
			t->control &= ~(T_STOP);
			t->control &= ~(T_STOP);
@@ -3364,20 +3333,19 @@ static void pktgen_thread_worker(struct pktgen_thread *t)
			t->control &= ~(T_REMDEV);
			t->control &= ~(T_REMDEV);
		}
		}


		if (need_resched())
		set_current_state(TASK_INTERRUPTIBLE);
			schedule();
	}
	}


	PG_DEBUG(printk("pktgen: %s stopping all device\n", t->name));
	PG_DEBUG(printk("pktgen: %s stopping all device\n", t->tsk->comm));
	pktgen_stop(t);
	pktgen_stop(t);


	PG_DEBUG(printk("pktgen: %s removing all device\n", t->name));
	PG_DEBUG(printk("pktgen: %s removing all device\n", t->tsk->comm));
	pktgen_rem_all_ifs(t);
	pktgen_rem_all_ifs(t);


	PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name));
	PG_DEBUG(printk("pktgen: %s removing thread.\n", t->tsk->comm));
	pktgen_rem_thread(t);
	pktgen_rem_thread(t);


	t->removed = 1;
	return 0;
}
}


static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
@@ -3495,37 +3463,11 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
	return add_dev_to_thread(t, pkt_dev);
	return add_dev_to_thread(t, pkt_dev);
}
}


static struct pktgen_thread *__init pktgen_find_thread(const char *name)
static int __init pktgen_create_thread(int cpu)
{
{
	struct pktgen_thread *t;
	struct pktgen_thread *t;

	mutex_lock(&pktgen_thread_lock);

	list_for_each_entry(t, &pktgen_threads, th_list)
		if (strcmp(t->name, name) == 0) {
			mutex_unlock(&pktgen_thread_lock);
			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;
	struct proc_dir_entry *pe;

	struct task_struct *p;
	if (strlen(name) > 31) {
		printk("pktgen: ERROR:  Thread name cannot be more than 31 characters.\n");
		return -EINVAL;
	}

	if (pktgen_find_thread(name)) {
		printk("pktgen: ERROR: thread: %s already exists\n", name);
		return -EINVAL;
	}


	t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
	t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
	if (!t) {
	if (!t) {
@@ -3533,14 +3475,29 @@ static int __init pktgen_create_thread(const char *name, int cpu)
		return -ENOMEM;
		return -ENOMEM;
	}
	}


	strcpy(t->name, name);
	spin_lock_init(&t->if_lock);
	spin_lock_init(&t->if_lock);
	t->cpu = cpu;
	t->cpu = cpu;


	pe = create_proc_entry(t->name, 0600, pg_proc_dir);
	INIT_LIST_HEAD(&t->if_list);

	list_add_tail(&t->th_list, &pktgen_threads);

	p = kthread_create(pktgen_thread_worker, t, "kpktgend_%d", cpu);
	if (IS_ERR(p)) {
		printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu);
		list_del(&t->th_list);
		kfree(t);
		return PTR_ERR(p);
	}
	kthread_bind(p, cpu);
	t->tsk = p;

	pe = create_proc_entry(t->tsk->comm, 0600, pg_proc_dir);
	if (!pe) {
	if (!pe) {
		printk("pktgen: cannot create %s/%s procfs entry.\n",
		printk("pktgen: cannot create %s/%s procfs entry.\n",
		       PG_PROC_DIR, t->name);
		       PG_PROC_DIR, t->tsk->comm);
		kthread_stop(p);
		list_del(&t->th_list);
		kfree(t);
		kfree(t);
		return -EINVAL;
		return -EINVAL;
	}
	}
@@ -3548,21 +3505,7 @@ static int __init pktgen_create_thread(const char *name, int cpu)
	pe->proc_fops = &pktgen_thread_fops;
	pe->proc_fops = &pktgen_thread_fops;
	pe->data = t;
	pe->data = t;


	INIT_LIST_HEAD(&t->if_list);
	wake_up_process(p);

	list_add_tail(&t->th_list, &pktgen_threads);

	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;
	return 0;
}
}
@@ -3643,10 +3586,8 @@ static int __init pg_init(void)


	for_each_online_cpu(cpu) {
	for_each_online_cpu(cpu) {
		int err;
		int err;
		char buf[30];


		sprintf(buf, "kpktgend_%i", cpu);
		err = pktgen_create_thread(cpu);
		err = pktgen_create_thread(buf, cpu);
		if (err)
		if (err)
			printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n",
			printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n",
					cpu, err);
					cpu, err);
@@ -3674,9 +3615,8 @@ static void __exit pg_cleanup(void)


	list_for_each_safe(q, n, &pktgen_threads) {
	list_for_each_safe(q, n, &pktgen_threads) {
		t = list_entry(q, struct pktgen_thread, th_list);
		t = list_entry(q, struct pktgen_thread, th_list);
		t->control |= (T_TERMINATE);
		kthread_stop(t->tsk);

		kfree(t);
		wait_event_interruptible_timeout(queue, (t->removed == 1), HZ);
	}
	}


	/* Un-register us from receiving netdevice events */
	/* Un-register us from receiving netdevice events */