Skip to content

Commit

Permalink
s390/vtimer: rework virtual timer interface
Browse files Browse the repository at this point in the history
The current virtual timer interface is inherently per-cpu and hard to
use. The sole user of the interface is appldata which uses it to execute
a function after a specific amount of cputime has been used over all cpus.

Rework the virtual timer interface to hook into the cputime accounting.
This makes the interface independent from the CPU timer interrupts, and
makes the virtual timers global as opposed to per-cpu.
Overall the code is greatly simplified. The downside is that the accuracy
is not as good as the original implementation, but it is still good enough
for appldata.

Reviewed-by: Jan Glauber <jang@linux.vnet.ibm.com>
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Martin Schwidefsky committed Jul 20, 2012
1 parent 921486b commit 27f6b41
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 472 deletions.
130 changes: 14 additions & 116 deletions arch/s390/appldata/appldata_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include <linux/suspend.h>
#include <linux/platform_device.h>
#include <asm/appldata.h>
#include <asm/timer.h>
#include <asm/vtimer.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/smp.h>
Expand Down Expand Up @@ -82,8 +82,7 @@ static struct ctl_table appldata_dir_table[] = {
/*
* Timer
*/
static DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
static atomic_t appldata_expire_count = ATOMIC_INIT(0);
static struct vtimer_list appldata_timer;

static DEFINE_SPINLOCK(appldata_timer_lock);
static int appldata_interval = APPLDATA_CPU_INTERVAL;
Expand Down Expand Up @@ -113,10 +112,7 @@ static LIST_HEAD(appldata_ops_list);
*/
static void appldata_timer_function(unsigned long data)
{
if (atomic_dec_and_test(&appldata_expire_count)) {
atomic_set(&appldata_expire_count, num_online_cpus());
queue_work(appldata_wq, (struct work_struct *) data);
}
queue_work(appldata_wq, (struct work_struct *) data);
}

/*
Expand All @@ -129,7 +125,6 @@ static void appldata_work_fn(struct work_struct *work)
struct list_head *lh;
struct appldata_ops *ops;

get_online_cpus();
mutex_lock(&appldata_ops_mutex);
list_for_each(lh, &appldata_ops_list) {
ops = list_entry(lh, struct appldata_ops, list);
Expand All @@ -138,7 +133,6 @@ static void appldata_work_fn(struct work_struct *work)
}
}
mutex_unlock(&appldata_ops_mutex);
put_online_cpus();
}

/*
Expand Down Expand Up @@ -166,20 +160,6 @@ int appldata_diag(char record_nr, u16 function, unsigned long buffer,

/****************************** /proc stuff **********************************/

/*
* appldata_mod_vtimer_wrap()
*
* wrapper function for mod_virt_timer(), because smp_call_function_single()
* accepts only one parameter.
*/
static void __appldata_mod_vtimer_wrap(void *p) {
struct {
struct vtimer_list *timer;
u64 expires;
} *args = p;
mod_virt_timer_periodic(args->timer, args->expires);
}

#define APPLDATA_ADD_TIMER 0
#define APPLDATA_DEL_TIMER 1
#define APPLDATA_MOD_TIMER 2
Expand All @@ -190,49 +170,28 @@ static void __appldata_mod_vtimer_wrap(void *p) {
* Add, delete or modify virtual timers on all online cpus.
* The caller needs to get the appldata_timer_lock spinlock.
*/
static void
__appldata_vtimer_setup(int cmd)
static void __appldata_vtimer_setup(int cmd)
{
u64 per_cpu_interval;
int i;
u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;

switch (cmd) {
case APPLDATA_ADD_TIMER:
if (appldata_timer_active)
break;
per_cpu_interval = (u64) (appldata_interval*1000 /
num_online_cpus()) * TOD_MICRO;
for_each_online_cpu(i) {
per_cpu(appldata_timer, i).expires = per_cpu_interval;
smp_call_function_single(i, add_virt_timer_periodic,
&per_cpu(appldata_timer, i),
1);
}
appldata_timer.expires = timer_interval;
add_virt_timer_periodic(&appldata_timer);
appldata_timer_active = 1;
break;
case APPLDATA_DEL_TIMER:
for_each_online_cpu(i)
del_virt_timer(&per_cpu(appldata_timer, i));
del_virt_timer(&appldata_timer);
if (!appldata_timer_active)
break;
appldata_timer_active = 0;
atomic_set(&appldata_expire_count, num_online_cpus());
break;
case APPLDATA_MOD_TIMER:
per_cpu_interval = (u64) (appldata_interval*1000 /
num_online_cpus()) * TOD_MICRO;
if (!appldata_timer_active)
break;
for_each_online_cpu(i) {
struct {
struct vtimer_list *timer;
u64 expires;
} args;
args.timer = &per_cpu(appldata_timer, i);
args.expires = per_cpu_interval;
smp_call_function_single(i, __appldata_mod_vtimer_wrap,
&args, 1);
}
mod_virt_timer_periodic(&appldata_timer, timer_interval);
}
}

Expand Down Expand Up @@ -263,14 +222,12 @@ appldata_timer_handler(ctl_table *ctl, int write,
len = *lenp;
if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
return -EFAULT;
get_online_cpus();
spin_lock(&appldata_timer_lock);
if (buf[0] == '1')
__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
else if (buf[0] == '0')
__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
spin_unlock(&appldata_timer_lock);
put_online_cpus();
out:
*lenp = len;
*ppos += len;
Expand Down Expand Up @@ -303,20 +260,17 @@ appldata_interval_handler(ctl_table *ctl, int write,
goto out;
}
len = *lenp;
if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
return -EFAULT;
}
interval = 0;
sscanf(buf, "%i", &interval);
if (interval <= 0)
return -EINVAL;

get_online_cpus();
spin_lock(&appldata_timer_lock);
appldata_interval = interval;
__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
spin_unlock(&appldata_timer_lock);
put_online_cpus();
out:
*lenp = len;
*ppos += len;
Expand Down Expand Up @@ -483,14 +437,12 @@ static int appldata_freeze(struct device *dev)
int rc;
struct list_head *lh;

get_online_cpus();
spin_lock(&appldata_timer_lock);
if (appldata_timer_active) {
__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
appldata_timer_suspended = 1;
}
spin_unlock(&appldata_timer_lock);
put_online_cpus();

mutex_lock(&appldata_ops_mutex);
list_for_each(lh, &appldata_ops_list) {
Expand All @@ -514,14 +466,12 @@ static int appldata_restore(struct device *dev)
int rc;
struct list_head *lh;

get_online_cpus();
spin_lock(&appldata_timer_lock);
if (appldata_timer_suspended) {
__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
appldata_timer_suspended = 0;
}
spin_unlock(&appldata_timer_lock);
put_online_cpus();

mutex_lock(&appldata_ops_mutex);
list_for_each(lh, &appldata_ops_list) {
Expand Down Expand Up @@ -565,61 +515,17 @@ static struct platform_driver appldata_pdrv = {

/******************************* init / exit *********************************/

static void __cpuinit appldata_online_cpu(int cpu)
{
init_virt_timer(&per_cpu(appldata_timer, cpu));
per_cpu(appldata_timer, cpu).function = appldata_timer_function;
per_cpu(appldata_timer, cpu).data = (unsigned long)
&appldata_work;
atomic_inc(&appldata_expire_count);
spin_lock(&appldata_timer_lock);
__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
spin_unlock(&appldata_timer_lock);
}

static void __cpuinit appldata_offline_cpu(int cpu)
{
del_virt_timer(&per_cpu(appldata_timer, cpu));
if (atomic_dec_and_test(&appldata_expire_count)) {
atomic_set(&appldata_expire_count, num_online_cpus());
queue_work(appldata_wq, &appldata_work);
}
spin_lock(&appldata_timer_lock);
__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
spin_unlock(&appldata_timer_lock);
}

static int __cpuinit appldata_cpu_notify(struct notifier_block *self,
unsigned long action,
void *hcpu)
{
switch (action) {
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
appldata_online_cpu((long) hcpu);
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
appldata_offline_cpu((long) hcpu);
break;
default:
break;
}
return NOTIFY_OK;
}

static struct notifier_block __cpuinitdata appldata_nb = {
.notifier_call = appldata_cpu_notify,
};

/*
* appldata_init()
*
* init timer, register /proc entries
*/
static int __init appldata_init(void)
{
int i, rc;
int rc;

appldata_timer.function = appldata_timer_function;
appldata_timer.data = (unsigned long) &appldata_work;

rc = platform_driver_register(&appldata_pdrv);
if (rc)
Expand All @@ -637,14 +543,6 @@ static int __init appldata_init(void)
goto out_device;
}

get_online_cpus();
for_each_online_cpu(i)
appldata_online_cpu(i);
put_online_cpus();

/* Register cpu hotplug notifier */
register_hotcpu_notifier(&appldata_nb);

appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
return 0;

Expand Down
6 changes: 4 additions & 2 deletions arch/s390/include/asm/cputime.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ struct s390_idle_data {
int nohz_delay;
unsigned int sequence;
unsigned long long idle_count;
unsigned long long idle_enter;
unsigned long long idle_exit;
unsigned long long idle_time;
unsigned long long clock_idle_enter;
unsigned long long clock_idle_exit;
unsigned long long timer_idle_enter;
unsigned long long timer_idle_exit;
};

DECLARE_PER_CPU(struct s390_idle_data, s390_idle);
Expand Down
51 changes: 0 additions & 51 deletions arch/s390/include/asm/timer.h

This file was deleted.

33 changes: 33 additions & 0 deletions arch/s390/include/asm/vtimer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright IBM Corp. 2003, 2012
* Virtual CPU timer
*
* Author(s): Jan Glauber <jan.glauber@de.ibm.com>
*/

#ifndef _ASM_S390_TIMER_H
#define _ASM_S390_TIMER_H

#define VTIMER_MAX_SLICE (0x7fffffffffffffffULL)

struct vtimer_list {
struct list_head entry;
u64 expires;
u64 interval;
void (*function)(unsigned long);
unsigned long data;
};

extern void init_virt_timer(struct vtimer_list *timer);
extern void add_virt_timer(struct vtimer_list *timer);
extern void add_virt_timer_periodic(struct vtimer_list *timer);
extern int mod_virt_timer(struct vtimer_list *timer, u64 expires);
extern int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires);
extern int del_virt_timer(struct vtimer_list *timer);

extern void init_cpu_vtimer(void);
extern void vtime_init(void);

extern void vtime_stop_cpu(void);

#endif /* _ASM_S390_TIMER_H */
10 changes: 4 additions & 6 deletions arch/s390/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <linux/kbuild.h>
#include <linux/sched.h>
#include <asm/cputime.h>
#include <asm/timer.h>
#include <asm/vdso.h>
#include <asm/pgtable.h>

Expand Down Expand Up @@ -72,11 +71,10 @@ int main(void)
DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
BLANK();
/* idle data offsets */
DEFINE(__IDLE_ENTER, offsetof(struct s390_idle_data, idle_enter));
DEFINE(__IDLE_EXIT, offsetof(struct s390_idle_data, idle_exit));
/* vtimer queue offsets */
DEFINE(__VQ_IDLE_ENTER, offsetof(struct vtimer_queue, idle_enter));
DEFINE(__VQ_IDLE_EXIT, offsetof(struct vtimer_queue, idle_exit));
DEFINE(__CLOCK_IDLE_ENTER, offsetof(struct s390_idle_data, clock_idle_enter));
DEFINE(__CLOCK_IDLE_EXIT, offsetof(struct s390_idle_data, clock_idle_exit));
DEFINE(__TIMER_IDLE_ENTER, offsetof(struct s390_idle_data, timer_idle_enter));
DEFINE(__TIMER_IDLE_EXIT, offsetof(struct s390_idle_data, timer_idle_exit));
/* lowcore offsets */
DEFINE(__LC_EXT_PARAMS, offsetof(struct _lowcore, ext_params));
DEFINE(__LC_EXT_CPU_ADDR, offsetof(struct _lowcore, ext_cpu_addr));
Expand Down
Loading

0 comments on commit 27f6b41

Please sign in to comment.