infiniband: Ehca: Use hotplug thread infrastructure
Get rid of the hotplug notifiers and use the generic hotplug thread infrastructure. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> Cc: Rusty Russell <rusty@rustcorp.com.au> Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Namhyung Kim <namhyung@kernel.org> Link: http://lkml.kernel.org/r/20120716103948.775527032@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
62ab707247
commit
81942621bd
2 changed files with 100 additions and 172 deletions
|
@ -42,6 +42,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/smpboot.h>
|
||||||
|
|
||||||
#include "ehca_classes.h"
|
#include "ehca_classes.h"
|
||||||
#include "ehca_irq.h"
|
#include "ehca_irq.h"
|
||||||
|
@ -652,7 +653,7 @@ void ehca_tasklet_eq(unsigned long data)
|
||||||
ehca_process_eq((struct ehca_shca*)data, 1);
|
ehca_process_eq((struct ehca_shca*)data, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
|
static int find_next_online_cpu(struct ehca_comp_pool *pool)
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -662,17 +663,20 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
|
||||||
ehca_dmp(cpu_online_mask, cpumask_size(), "");
|
ehca_dmp(cpu_online_mask, cpumask_size(), "");
|
||||||
|
|
||||||
spin_lock_irqsave(&pool->last_cpu_lock, flags);
|
spin_lock_irqsave(&pool->last_cpu_lock, flags);
|
||||||
cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
|
do {
|
||||||
if (cpu >= nr_cpu_ids)
|
cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
|
||||||
cpu = cpumask_first(cpu_online_mask);
|
if (cpu >= nr_cpu_ids)
|
||||||
pool->last_cpu = cpu;
|
cpu = cpumask_first(cpu_online_mask);
|
||||||
|
pool->last_cpu = cpu;
|
||||||
|
} while (!per_cpu_ptr(pool->cpu_comp_tasks, cpu)->active)
|
||||||
spin_unlock_irqrestore(&pool->last_cpu_lock, flags);
|
spin_unlock_irqrestore(&pool->last_cpu_lock, flags);
|
||||||
|
|
||||||
return cpu;
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __queue_comp_task(struct ehca_cq *__cq,
|
static void __queue_comp_task(struct ehca_cq *__cq,
|
||||||
struct ehca_cpu_comp_task *cct)
|
struct ehca_cpu_comp_task *cct,
|
||||||
|
struct task_struct *thread)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
@ -683,7 +687,7 @@ static void __queue_comp_task(struct ehca_cq *__cq,
|
||||||
__cq->nr_callbacks++;
|
__cq->nr_callbacks++;
|
||||||
list_add_tail(&__cq->entry, &cct->cq_list);
|
list_add_tail(&__cq->entry, &cct->cq_list);
|
||||||
cct->cq_jobs++;
|
cct->cq_jobs++;
|
||||||
wake_up(&cct->wait_queue);
|
wake_up_process(thread);
|
||||||
} else
|
} else
|
||||||
__cq->nr_callbacks++;
|
__cq->nr_callbacks++;
|
||||||
|
|
||||||
|
@ -695,6 +699,7 @@ static void queue_comp_task(struct ehca_cq *__cq)
|
||||||
{
|
{
|
||||||
int cpu_id;
|
int cpu_id;
|
||||||
struct ehca_cpu_comp_task *cct;
|
struct ehca_cpu_comp_task *cct;
|
||||||
|
struct task_struct *thread;
|
||||||
int cq_jobs;
|
int cq_jobs;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
@ -702,7 +707,8 @@ static void queue_comp_task(struct ehca_cq *__cq)
|
||||||
BUG_ON(!cpu_online(cpu_id));
|
BUG_ON(!cpu_online(cpu_id));
|
||||||
|
|
||||||
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
|
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
|
||||||
BUG_ON(!cct);
|
thread = per_cpu_ptr(pool->cpu_comp_threads, cpu_id);
|
||||||
|
BUG_ON(!cct || !thread);
|
||||||
|
|
||||||
spin_lock_irqsave(&cct->task_lock, flags);
|
spin_lock_irqsave(&cct->task_lock, flags);
|
||||||
cq_jobs = cct->cq_jobs;
|
cq_jobs = cct->cq_jobs;
|
||||||
|
@ -710,28 +716,25 @@ static void queue_comp_task(struct ehca_cq *__cq)
|
||||||
if (cq_jobs > 0) {
|
if (cq_jobs > 0) {
|
||||||
cpu_id = find_next_online_cpu(pool);
|
cpu_id = find_next_online_cpu(pool);
|
||||||
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
|
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
|
||||||
BUG_ON(!cct);
|
thread = per_cpu_ptr(pool->cpu_comp_threads, cpu_id);
|
||||||
|
BUG_ON(!cct || !thread);
|
||||||
}
|
}
|
||||||
|
__queue_comp_task(__cq, cct, thread);
|
||||||
__queue_comp_task(__cq, cct);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_comp_task(struct ehca_cpu_comp_task *cct)
|
static void run_comp_task(struct ehca_cpu_comp_task *cct)
|
||||||
{
|
{
|
||||||
struct ehca_cq *cq;
|
struct ehca_cq *cq;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&cct->task_lock, flags);
|
|
||||||
|
|
||||||
while (!list_empty(&cct->cq_list)) {
|
while (!list_empty(&cct->cq_list)) {
|
||||||
cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
|
cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
|
||||||
spin_unlock_irqrestore(&cct->task_lock, flags);
|
spin_unlock_irq(&cct->task_lock);
|
||||||
|
|
||||||
comp_event_callback(cq);
|
comp_event_callback(cq);
|
||||||
if (atomic_dec_and_test(&cq->nr_events))
|
if (atomic_dec_and_test(&cq->nr_events))
|
||||||
wake_up(&cq->wait_completion);
|
wake_up(&cq->wait_completion);
|
||||||
|
|
||||||
spin_lock_irqsave(&cct->task_lock, flags);
|
spin_lock_irq(&cct->task_lock);
|
||||||
spin_lock(&cq->task_lock);
|
spin_lock(&cq->task_lock);
|
||||||
cq->nr_callbacks--;
|
cq->nr_callbacks--;
|
||||||
if (!cq->nr_callbacks) {
|
if (!cq->nr_callbacks) {
|
||||||
|
@ -740,159 +743,76 @@ static void run_comp_task(struct ehca_cpu_comp_task *cct)
|
||||||
}
|
}
|
||||||
spin_unlock(&cq->task_lock);
|
spin_unlock(&cq->task_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&cct->task_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int comp_task(void *__cct)
|
static void comp_task_park(unsigned int cpu)
|
||||||
{
|
|
||||||
struct ehca_cpu_comp_task *cct = __cct;
|
|
||||||
int cql_empty;
|
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
while (!kthread_should_stop()) {
|
|
||||||
add_wait_queue(&cct->wait_queue, &wait);
|
|
||||||
|
|
||||||
spin_lock_irq(&cct->task_lock);
|
|
||||||
cql_empty = list_empty(&cct->cq_list);
|
|
||||||
spin_unlock_irq(&cct->task_lock);
|
|
||||||
if (cql_empty)
|
|
||||||
schedule();
|
|
||||||
else
|
|
||||||
__set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
remove_wait_queue(&cct->wait_queue, &wait);
|
|
||||||
|
|
||||||
spin_lock_irq(&cct->task_lock);
|
|
||||||
cql_empty = list_empty(&cct->cq_list);
|
|
||||||
spin_unlock_irq(&cct->task_lock);
|
|
||||||
if (!cql_empty)
|
|
||||||
run_comp_task(__cct);
|
|
||||||
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
}
|
|
||||||
__set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct task_struct *create_comp_task(struct ehca_comp_pool *pool,
|
|
||||||
int cpu)
|
|
||||||
{
|
|
||||||
struct ehca_cpu_comp_task *cct;
|
|
||||||
|
|
||||||
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
|
||||||
spin_lock_init(&cct->task_lock);
|
|
||||||
INIT_LIST_HEAD(&cct->cq_list);
|
|
||||||
init_waitqueue_head(&cct->wait_queue);
|
|
||||||
cct->task = kthread_create_on_node(comp_task, cct, cpu_to_node(cpu),
|
|
||||||
"ehca_comp/%d", cpu);
|
|
||||||
|
|
||||||
return cct->task;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_comp_task(struct ehca_comp_pool *pool,
|
|
||||||
int cpu)
|
|
||||||
{
|
|
||||||
struct ehca_cpu_comp_task *cct;
|
|
||||||
struct task_struct *task;
|
|
||||||
unsigned long flags_cct;
|
|
||||||
|
|
||||||
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&cct->task_lock, flags_cct);
|
|
||||||
|
|
||||||
task = cct->task;
|
|
||||||
cct->task = NULL;
|
|
||||||
cct->cq_jobs = 0;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&cct->task_lock, flags_cct);
|
|
||||||
|
|
||||||
if (task)
|
|
||||||
kthread_stop(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __cpuinit take_over_work(struct ehca_comp_pool *pool, int cpu)
|
|
||||||
{
|
{
|
||||||
struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
||||||
|
struct ehca_cpu_comp_task *target;
|
||||||
|
struct task_struct *thread;
|
||||||
|
struct ehca_cq *cq, *tmp;
|
||||||
LIST_HEAD(list);
|
LIST_HEAD(list);
|
||||||
struct ehca_cq *cq;
|
|
||||||
unsigned long flags_cct;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&cct->task_lock, flags_cct);
|
|
||||||
|
|
||||||
|
spin_lock_irq(&cct->task_lock);
|
||||||
|
cct->cq_jobs = 0;
|
||||||
|
cct->active = 0;
|
||||||
list_splice_init(&cct->cq_list, &list);
|
list_splice_init(&cct->cq_list, &list);
|
||||||
|
spin_unlock_irq(&cct->task_lock);
|
||||||
|
|
||||||
while (!list_empty(&list)) {
|
cpu = find_next_online_cpu(pool);
|
||||||
cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
|
target = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
||||||
|
thread = per_cpu_ptr(pool->cpu_comp_threads, cpu);
|
||||||
|
spin_lock_irq(&target->task_lock);
|
||||||
|
list_for_each_entry_safe(cq, tmp, &list, entry) {
|
||||||
list_del(&cq->entry);
|
list_del(&cq->entry);
|
||||||
__queue_comp_task(cq, this_cpu_ptr(pool->cpu_comp_tasks));
|
__queue_comp_task(cq, target, thread);
|
||||||
}
|
}
|
||||||
|
spin_unlock_irq(&target->task_lock);
|
||||||
spin_unlock_irqrestore(&cct->task_lock, flags_cct);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __cpuinit comp_pool_callback(struct notifier_block *nfb,
|
static void comp_task_stop(unsigned int cpu, bool online)
|
||||||
unsigned long action,
|
|
||||||
void *hcpu)
|
|
||||||
{
|
{
|
||||||
unsigned int cpu = (unsigned long)hcpu;
|
struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
||||||
struct ehca_cpu_comp_task *cct;
|
|
||||||
|
|
||||||
switch (action) {
|
spin_lock_irq(&cct->task_lock);
|
||||||
case CPU_UP_PREPARE:
|
cct->cq_jobs = 0;
|
||||||
case CPU_UP_PREPARE_FROZEN:
|
cct->active = 0;
|
||||||
ehca_gen_dbg("CPU: %x (CPU_PREPARE)", cpu);
|
WARN_ON(!list_empty(&cct->cq_list));
|
||||||
if (!create_comp_task(pool, cpu)) {
|
spin_unlock_irq(&cct->task_lock);
|
||||||
ehca_gen_err("Can't create comp_task for cpu: %x", cpu);
|
|
||||||
return notifier_from_errno(-ENOMEM);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CPU_UP_CANCELED:
|
|
||||||
case CPU_UP_CANCELED_FROZEN:
|
|
||||||
ehca_gen_dbg("CPU: %x (CPU_CANCELED)", cpu);
|
|
||||||
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
|
||||||
kthread_bind(cct->task, cpumask_any(cpu_online_mask));
|
|
||||||
destroy_comp_task(pool, cpu);
|
|
||||||
break;
|
|
||||||
case CPU_ONLINE:
|
|
||||||
case CPU_ONLINE_FROZEN:
|
|
||||||
ehca_gen_dbg("CPU: %x (CPU_ONLINE)", cpu);
|
|
||||||
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
|
||||||
kthread_bind(cct->task, cpu);
|
|
||||||
wake_up_process(cct->task);
|
|
||||||
break;
|
|
||||||
case CPU_DOWN_PREPARE:
|
|
||||||
case CPU_DOWN_PREPARE_FROZEN:
|
|
||||||
ehca_gen_dbg("CPU: %x (CPU_DOWN_PREPARE)", cpu);
|
|
||||||
break;
|
|
||||||
case CPU_DOWN_FAILED:
|
|
||||||
case CPU_DOWN_FAILED_FROZEN:
|
|
||||||
ehca_gen_dbg("CPU: %x (CPU_DOWN_FAILED)", cpu);
|
|
||||||
break;
|
|
||||||
case CPU_DEAD:
|
|
||||||
case CPU_DEAD_FROZEN:
|
|
||||||
ehca_gen_dbg("CPU: %x (CPU_DEAD)", cpu);
|
|
||||||
destroy_comp_task(pool, cpu);
|
|
||||||
take_over_work(pool, cpu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NOTIFY_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct notifier_block comp_pool_callback_nb __cpuinitdata = {
|
static int comp_task_should_run(unsigned int cpu)
|
||||||
.notifier_call = comp_pool_callback,
|
{
|
||||||
.priority = 0,
|
struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
||||||
|
|
||||||
|
return cct->cq_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int comp_task(unsigned int cpu)
|
||||||
|
{
|
||||||
|
struct ehca_cpu_comp_task *cct = this_cpu_ptr(pool->cpu_comp_tasks);
|
||||||
|
int cql_empty;
|
||||||
|
|
||||||
|
spin_lock_irq(&cct->task_lock);
|
||||||
|
cql_empty = list_empty(&cct->cq_list);
|
||||||
|
if (!cql_empty) {
|
||||||
|
__set_current_state(TASK_RUNNING);
|
||||||
|
run_comp_task(cct);
|
||||||
|
}
|
||||||
|
spin_unlock_irq(&cct->task_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct smp_hotplug_thread comp_pool_threads = {
|
||||||
|
.thread_should_run = comp_task_should_run,
|
||||||
|
.thread_fn = comp_task,
|
||||||
|
.thread_comm = "ehca_comp/%u",
|
||||||
|
.cleanup = comp_task_stop,
|
||||||
|
.park = comp_task_park,
|
||||||
};
|
};
|
||||||
|
|
||||||
int ehca_create_comp_pool(void)
|
int ehca_create_comp_pool(void)
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu, ret = -ENOMEM;
|
||||||
struct task_struct *task;
|
|
||||||
|
|
||||||
if (!ehca_scaling_code)
|
if (!ehca_scaling_code)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -905,38 +825,46 @@ int ehca_create_comp_pool(void)
|
||||||
pool->last_cpu = cpumask_any(cpu_online_mask);
|
pool->last_cpu = cpumask_any(cpu_online_mask);
|
||||||
|
|
||||||
pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task);
|
pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task);
|
||||||
if (pool->cpu_comp_tasks == NULL) {
|
if (!pool->cpu_comp_tasks)
|
||||||
kfree(pool);
|
goto out_pool;
|
||||||
return -EINVAL;
|
|
||||||
|
pool->cpu_comp_threads = alloc_percpu(struct task_struct *);
|
||||||
|
if (!pool->cpu_comp_threads)
|
||||||
|
goto out_tasks;
|
||||||
|
|
||||||
|
for_each_present_cpu(cpu) {
|
||||||
|
struct ehca_cpu_comp_task *cct;
|
||||||
|
|
||||||
|
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
|
||||||
|
spin_lock_init(&cct->task_lock);
|
||||||
|
INIT_LIST_HEAD(&cct->cq_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
for_each_online_cpu(cpu) {
|
comp_pool_threads.store = pool->cpu_comp_threads;
|
||||||
task = create_comp_task(pool, cpu);
|
ret = smpboot_register_percpu_thread(&comp_pool_threads);
|
||||||
if (task) {
|
if (ret)
|
||||||
kthread_bind(task, cpu);
|
goto out_threads;
|
||||||
wake_up_process(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register_hotcpu_notifier(&comp_pool_callback_nb);
|
pr_info("eHCA scaling code enabled\n");
|
||||||
|
return ret;
|
||||||
|
|
||||||
printk(KERN_INFO "eHCA scaling code enabled\n");
|
out_threads:
|
||||||
|
free_percpu(pool->cpu_comp_threads);
|
||||||
return 0;
|
out_tasks:
|
||||||
|
free_percpu(pool->cpu_comp_tasks);
|
||||||
|
out_pool:
|
||||||
|
kfree(pool);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ehca_destroy_comp_pool(void)
|
void ehca_destroy_comp_pool(void)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!ehca_scaling_code)
|
if (!ehca_scaling_code)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unregister_hotcpu_notifier(&comp_pool_callback_nb);
|
smpboot_unregister_percpu_thread(&comp_pool_threads);
|
||||||
|
|
||||||
for_each_online_cpu(i)
|
|
||||||
destroy_comp_task(pool, i);
|
|
||||||
|
|
||||||
|
free_percpu(pool->cpu_comp_threads);
|
||||||
free_percpu(pool->cpu_comp_tasks);
|
free_percpu(pool->cpu_comp_tasks);
|
||||||
kfree(pool);
|
kfree(pool);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,15 +58,15 @@ void ehca_tasklet_eq(unsigned long data);
|
||||||
void ehca_process_eq(struct ehca_shca *shca, int is_irq);
|
void ehca_process_eq(struct ehca_shca *shca, int is_irq);
|
||||||
|
|
||||||
struct ehca_cpu_comp_task {
|
struct ehca_cpu_comp_task {
|
||||||
wait_queue_head_t wait_queue;
|
|
||||||
struct list_head cq_list;
|
struct list_head cq_list;
|
||||||
struct task_struct *task;
|
|
||||||
spinlock_t task_lock;
|
spinlock_t task_lock;
|
||||||
int cq_jobs;
|
int cq_jobs;
|
||||||
|
int active;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ehca_comp_pool {
|
struct ehca_comp_pool {
|
||||||
struct ehca_cpu_comp_task *cpu_comp_tasks;
|
struct ehca_cpu_comp_task __percpu *cpu_comp_tasks;
|
||||||
|
struct task_struct * __percpu *cpu_comp_threads;
|
||||||
int last_cpu;
|
int last_cpu;
|
||||||
spinlock_t last_cpu_lock;
|
spinlock_t last_cpu_lock;
|
||||||
};
|
};
|
||||||
|
|
Reference in a new issue