diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-01-01 23:51:53 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-01-03 21:38:09 -0500 |
commit | ee74baa7d83e9e0c2fdaff8122ee9cefd06cddc5 (patch) | |
tree | 3fb7f4331b3b60b933f1255bd43043cc83a20971 /net | |
parent | 3136dcb3cd6e5b4ed4bd34d422f8cdeec4da6836 (diff) |
[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: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/pktgen.c | 156 |
1 files changed, 48 insertions, 108 deletions
diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 1897a3a385d8..04d4b93c68eb 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c | |||
@@ -148,6 +148,7 @@ | |||
148 | #include <linux/seq_file.h> | 148 | #include <linux/seq_file.h> |
149 | #include <linux/wait.h> | 149 | #include <linux/wait.h> |
150 | #include <linux/etherdevice.h> | 150 | #include <linux/etherdevice.h> |
151 | #include <linux/kthread.h> | ||
151 | #include <net/checksum.h> | 152 | #include <net/checksum.h> |
152 | #include <net/ipv6.h> | 153 | #include <net/ipv6.h> |
153 | #include <net/addrconf.h> | 154 | #include <net/addrconf.h> |
@@ -360,8 +361,7 @@ struct pktgen_thread { | |||
360 | spinlock_t if_lock; | 361 | spinlock_t if_lock; |
361 | struct list_head if_list; /* All device here */ | 362 | struct list_head if_list; /* All device here */ |
362 | struct list_head th_list; | 363 | struct list_head th_list; |
363 | int removed; | 364 | struct task_struct *tsk; |
364 | char name[32]; | ||
365 | char result[512]; | 365 | char result[512]; |
366 | u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */ | 366 | u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */ |
367 | 367 | ||
@@ -1689,7 +1689,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) | |||
1689 | BUG_ON(!t); | 1689 | BUG_ON(!t); |
1690 | 1690 | ||
1691 | seq_printf(seq, "Name: %s max_before_softirq: %d\n", | 1691 | seq_printf(seq, "Name: %s max_before_softirq: %d\n", |
1692 | t->name, t->max_before_softirq); | 1692 | t->tsk->comm, t->max_before_softirq); |
1693 | 1693 | ||
1694 | seq_printf(seq, "Running: "); | 1694 | seq_printf(seq, "Running: "); |
1695 | 1695 | ||
@@ -3112,7 +3112,7 @@ static void pktgen_rem_thread(struct pktgen_thread *t) | |||
3112 | { | 3112 | { |
3113 | /* Remove from the thread list */ | 3113 | /* Remove from the thread list */ |
3114 | 3114 | ||
3115 | remove_proc_entry(t->name, pg_proc_dir); | 3115 | remove_proc_entry(t->tsk->comm, pg_proc_dir); |
3116 | 3116 | ||
3117 | mutex_lock(&pktgen_thread_lock); | 3117 | mutex_lock(&pktgen_thread_lock); |
3118 | 3118 | ||
@@ -3260,58 +3260,40 @@ out:; | |||
3260 | * Main loop of the thread goes here | 3260 | * Main loop of the thread goes here |
3261 | */ | 3261 | */ |
3262 | 3262 | ||
3263 | static void pktgen_thread_worker(struct pktgen_thread *t) | 3263 | static int pktgen_thread_worker(void *arg) |
3264 | { | 3264 | { |
3265 | DEFINE_WAIT(wait); | 3265 | DEFINE_WAIT(wait); |
3266 | struct pktgen_thread *t = arg; | ||
3266 | struct pktgen_dev *pkt_dev = NULL; | 3267 | struct pktgen_dev *pkt_dev = NULL; |
3267 | int cpu = t->cpu; | 3268 | int cpu = t->cpu; |
3268 | sigset_t tmpsig; | ||
3269 | u32 max_before_softirq; | 3269 | u32 max_before_softirq; |
3270 | u32 tx_since_softirq = 0; | 3270 | u32 tx_since_softirq = 0; |
3271 | 3271 | ||
3272 | daemonize("pktgen/%d", cpu); | 3272 | BUG_ON(smp_processor_id() != cpu); |
3273 | |||
3274 | /* Block all signals except SIGKILL, SIGSTOP and SIGTERM */ | ||
3275 | |||
3276 | spin_lock_irq(¤t->sighand->siglock); | ||
3277 | tmpsig = current->blocked; | ||
3278 | siginitsetinv(¤t->blocked, | ||
3279 | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGTERM)); | ||
3280 | |||
3281 | recalc_sigpending(); | ||
3282 | spin_unlock_irq(¤t->sighand->siglock); | ||
3283 | |||
3284 | /* Migrate to the right CPU */ | ||
3285 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | ||
3286 | if (smp_processor_id() != cpu) | ||
3287 | BUG(); | ||
3288 | 3273 | ||
3289 | init_waitqueue_head(&t->queue); | 3274 | init_waitqueue_head(&t->queue); |
3290 | 3275 | ||
3291 | t->control &= ~(T_TERMINATE); | ||
3292 | t->control &= ~(T_RUN); | ||
3293 | t->control &= ~(T_STOP); | ||
3294 | t->control &= ~(T_REMDEVALL); | ||
3295 | t->control &= ~(T_REMDEV); | ||
3296 | |||
3297 | t->pid = current->pid; | 3276 | t->pid = current->pid; |
3298 | 3277 | ||
3299 | PG_DEBUG(printk("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid)); | 3278 | PG_DEBUG(printk("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid)); |
3300 | 3279 | ||
3301 | max_before_softirq = t->max_before_softirq; | 3280 | max_before_softirq = t->max_before_softirq; |
3302 | 3281 | ||
3303 | __set_current_state(TASK_INTERRUPTIBLE); | 3282 | set_current_state(TASK_INTERRUPTIBLE); |
3304 | mb(); | ||
3305 | 3283 | ||
3306 | while (1) { | 3284 | while (!kthread_should_stop()) { |
3307 | 3285 | pkt_dev = next_to_run(t); | |
3308 | __set_current_state(TASK_RUNNING); | ||
3309 | 3286 | ||
3310 | /* | 3287 | if (!pkt_dev && |
3311 | * Get next dev to xmit -- if any. | 3288 | (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV)) |
3312 | */ | 3289 | == 0) { |
3290 | prepare_to_wait(&(t->queue), &wait, | ||
3291 | TASK_INTERRUPTIBLE); | ||
3292 | schedule_timeout(HZ / 10); | ||
3293 | finish_wait(&(t->queue), &wait); | ||
3294 | } | ||
3313 | 3295 | ||
3314 | pkt_dev = next_to_run(t); | 3296 | __set_current_state(TASK_RUNNING); |
3315 | 3297 | ||
3316 | if (pkt_dev) { | 3298 | if (pkt_dev) { |
3317 | 3299 | ||
@@ -3329,21 +3311,8 @@ static void pktgen_thread_worker(struct pktgen_thread *t) | |||
3329 | do_softirq(); | 3311 | do_softirq(); |
3330 | tx_since_softirq = 0; | 3312 | tx_since_softirq = 0; |
3331 | } | 3313 | } |
3332 | } else { | ||
3333 | prepare_to_wait(&(t->queue), &wait, TASK_INTERRUPTIBLE); | ||
3334 | schedule_timeout(HZ / 10); | ||
3335 | finish_wait(&(t->queue), &wait); | ||
3336 | } | 3314 | } |
3337 | 3315 | ||
3338 | /* | ||
3339 | * Back from sleep, either due to the timeout or signal. | ||
3340 | * We check if we have any "posted" work for us. | ||
3341 | */ | ||
3342 | |||
3343 | if (t->control & T_TERMINATE || signal_pending(current)) | ||
3344 | /* we received a request to terminate ourself */ | ||
3345 | break; | ||
3346 | |||
3347 | if (t->control & T_STOP) { | 3316 | if (t->control & T_STOP) { |
3348 | pktgen_stop(t); | 3317 | pktgen_stop(t); |
3349 | t->control &= ~(T_STOP); | 3318 | t->control &= ~(T_STOP); |
@@ -3364,20 +3333,19 @@ static void pktgen_thread_worker(struct pktgen_thread *t) | |||
3364 | t->control &= ~(T_REMDEV); | 3333 | t->control &= ~(T_REMDEV); |
3365 | } | 3334 | } |
3366 | 3335 | ||
3367 | if (need_resched()) | 3336 | set_current_state(TASK_INTERRUPTIBLE); |
3368 | schedule(); | ||
3369 | } | 3337 | } |
3370 | 3338 | ||
3371 | PG_DEBUG(printk("pktgen: %s stopping all device\n", t->name)); | 3339 | PG_DEBUG(printk("pktgen: %s stopping all device\n", t->tsk->comm)); |
3372 | pktgen_stop(t); | 3340 | pktgen_stop(t); |
3373 | 3341 | ||
3374 | PG_DEBUG(printk("pktgen: %s removing all device\n", t->name)); | 3342 | PG_DEBUG(printk("pktgen: %s removing all device\n", t->tsk->comm)); |
3375 | pktgen_rem_all_ifs(t); | 3343 | pktgen_rem_all_ifs(t); |
3376 | 3344 | ||
3377 | PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name)); | 3345 | PG_DEBUG(printk("pktgen: %s removing thread.\n", t->tsk->comm)); |
3378 | pktgen_rem_thread(t); | 3346 | pktgen_rem_thread(t); |
3379 | 3347 | ||
3380 | t->removed = 1; | 3348 | return 0; |
3381 | } | 3349 | } |
3382 | 3350 | ||
3383 | static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, | 3351 | 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) | |||
3495 | return add_dev_to_thread(t, pkt_dev); | 3463 | return add_dev_to_thread(t, pkt_dev); |
3496 | } | 3464 | } |
3497 | 3465 | ||
3498 | static struct pktgen_thread *__init pktgen_find_thread(const char *name) | 3466 | static int __init pktgen_create_thread(int cpu) |
3499 | { | 3467 | { |
3500 | struct pktgen_thread *t; | 3468 | struct pktgen_thread *t; |
3501 | |||
3502 | mutex_lock(&pktgen_thread_lock); | ||
3503 | |||
3504 | list_for_each_entry(t, &pktgen_threads, th_list) | ||
3505 | if (strcmp(t->name, name) == 0) { | ||
3506 | mutex_unlock(&pktgen_thread_lock); | ||
3507 | return t; | ||
3508 | } | ||
3509 | |||
3510 | mutex_unlock(&pktgen_thread_lock); | ||
3511 | return NULL; | ||
3512 | } | ||
3513 | |||
3514 | static int __init pktgen_create_thread(const char *name, int cpu) | ||
3515 | { | ||
3516 | int err; | ||
3517 | struct pktgen_thread *t = NULL; | ||
3518 | struct proc_dir_entry *pe; | 3469 | struct proc_dir_entry *pe; |
3519 | 3470 | struct task_struct *p; | |
3520 | if (strlen(name) > 31) { | ||
3521 | printk("pktgen: ERROR: Thread name cannot be more than 31 characters.\n"); | ||
3522 | return -EINVAL; | ||
3523 | } | ||
3524 | |||
3525 | if (pktgen_find_thread(name)) { | ||
3526 | printk("pktgen: ERROR: thread: %s already exists\n", name); | ||
3527 | return -EINVAL; | ||
3528 | } | ||
3529 | 3471 | ||
3530 | t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL); | 3472 | t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL); |
3531 | if (!t) { | 3473 | if (!t) { |
@@ -3533,14 +3475,29 @@ static int __init pktgen_create_thread(const char *name, int cpu) | |||
3533 | return -ENOMEM; | 3475 | return -ENOMEM; |
3534 | } | 3476 | } |
3535 | 3477 | ||
3536 | strcpy(t->name, name); | ||
3537 | spin_lock_init(&t->if_lock); | 3478 | spin_lock_init(&t->if_lock); |
3538 | t->cpu = cpu; | 3479 | t->cpu = cpu; |
3539 | 3480 | ||
3540 | pe = create_proc_entry(t->name, 0600, pg_proc_dir); | 3481 | INIT_LIST_HEAD(&t->if_list); |
3482 | |||
3483 | list_add_tail(&t->th_list, &pktgen_threads); | ||
3484 | |||
3485 | p = kthread_create(pktgen_thread_worker, t, "kpktgend_%d", cpu); | ||
3486 | if (IS_ERR(p)) { | ||
3487 | printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu); | ||
3488 | list_del(&t->th_list); | ||
3489 | kfree(t); | ||
3490 | return PTR_ERR(p); | ||
3491 | } | ||
3492 | kthread_bind(p, cpu); | ||
3493 | t->tsk = p; | ||
3494 | |||
3495 | pe = create_proc_entry(t->tsk->comm, 0600, pg_proc_dir); | ||
3541 | if (!pe) { | 3496 | if (!pe) { |
3542 | printk("pktgen: cannot create %s/%s procfs entry.\n", | 3497 | printk("pktgen: cannot create %s/%s procfs entry.\n", |
3543 | PG_PROC_DIR, t->name); | 3498 | PG_PROC_DIR, t->tsk->comm); |
3499 | kthread_stop(p); | ||
3500 | list_del(&t->th_list); | ||
3544 | kfree(t); | 3501 | kfree(t); |
3545 | return -EINVAL; | 3502 | return -EINVAL; |
3546 | } | 3503 | } |
@@ -3548,21 +3505,7 @@ static int __init pktgen_create_thread(const char *name, int cpu) | |||
3548 | pe->proc_fops = &pktgen_thread_fops; | 3505 | pe->proc_fops = &pktgen_thread_fops; |
3549 | pe->data = t; | 3506 | pe->data = t; |
3550 | 3507 | ||
3551 | INIT_LIST_HEAD(&t->if_list); | 3508 | wake_up_process(p); |
3552 | |||
3553 | list_add_tail(&t->th_list, &pktgen_threads); | ||
3554 | |||
3555 | t->removed = 0; | ||
3556 | |||
3557 | err = kernel_thread((void *)pktgen_thread_worker, (void *)t, | ||
3558 | CLONE_FS | CLONE_FILES | CLONE_SIGHAND); | ||
3559 | if (err < 0) { | ||
3560 | printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu); | ||
3561 | remove_proc_entry(t->name, pg_proc_dir); | ||
3562 | list_del(&t->th_list); | ||
3563 | kfree(t); | ||
3564 | return err; | ||
3565 | } | ||
3566 | 3509 | ||
3567 | return 0; | 3510 | return 0; |
3568 | } | 3511 | } |
@@ -3643,10 +3586,8 @@ static int __init pg_init(void) | |||
3643 | 3586 | ||
3644 | for_each_online_cpu(cpu) { | 3587 | for_each_online_cpu(cpu) { |
3645 | int err; | 3588 | int err; |
3646 | char buf[30]; | ||
3647 | 3589 | ||
3648 | sprintf(buf, "kpktgend_%i", cpu); | 3590 | err = pktgen_create_thread(cpu); |
3649 | err = pktgen_create_thread(buf, cpu); | ||
3650 | if (err) | 3591 | if (err) |
3651 | printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n", | 3592 | printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n", |
3652 | cpu, err); | 3593 | cpu, err); |
@@ -3674,9 +3615,8 @@ static void __exit pg_cleanup(void) | |||
3674 | 3615 | ||
3675 | list_for_each_safe(q, n, &pktgen_threads) { | 3616 | list_for_each_safe(q, n, &pktgen_threads) { |
3676 | t = list_entry(q, struct pktgen_thread, th_list); | 3617 | t = list_entry(q, struct pktgen_thread, th_list); |
3677 | t->control |= (T_TERMINATE); | 3618 | kthread_stop(t->tsk); |
3678 | 3619 | kfree(t); | |
3679 | wait_event_interruptible_timeout(queue, (t->removed == 1), HZ); | ||
3680 | } | 3620 | } |
3681 | 3621 | ||
3682 | /* Un-register us from receiving netdevice events */ | 3622 | /* Un-register us from receiving netdevice events */ |