diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-07-20 05:15:08 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-07-20 05:15:08 -0400 |
commit | 27f6b416626a240e1b46f646d2e0c5266f4eac95 (patch) | |
tree | 4549855d7996ce9d18e1586e2f0bfb5fa5835718 /arch/s390/appldata | |
parent | 921486b92bcb1b82ab6668dcbb36d05604966351 (diff) |
s390/vtimer: rework virtual timer interface
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>
Diffstat (limited to 'arch/s390/appldata')
-rw-r--r-- | arch/s390/appldata/appldata_base.c | 130 |
1 files changed, 14 insertions, 116 deletions
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index fadefce09962..bae0f402bf2a 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include <linux/suspend.h> | 27 | #include <linux/suspend.h> |
28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <asm/appldata.h> | 29 | #include <asm/appldata.h> |
30 | #include <asm/timer.h> | 30 | #include <asm/vtimer.h> |
31 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
32 | #include <asm/io.h> | 32 | #include <asm/io.h> |
33 | #include <asm/smp.h> | 33 | #include <asm/smp.h> |
@@ -82,8 +82,7 @@ static struct ctl_table appldata_dir_table[] = { | |||
82 | /* | 82 | /* |
83 | * Timer | 83 | * Timer |
84 | */ | 84 | */ |
85 | static DEFINE_PER_CPU(struct vtimer_list, appldata_timer); | 85 | static struct vtimer_list appldata_timer; |
86 | static atomic_t appldata_expire_count = ATOMIC_INIT(0); | ||
87 | 86 | ||
88 | static DEFINE_SPINLOCK(appldata_timer_lock); | 87 | static DEFINE_SPINLOCK(appldata_timer_lock); |
89 | static int appldata_interval = APPLDATA_CPU_INTERVAL; | 88 | static int appldata_interval = APPLDATA_CPU_INTERVAL; |
@@ -113,10 +112,7 @@ static LIST_HEAD(appldata_ops_list); | |||
113 | */ | 112 | */ |
114 | static void appldata_timer_function(unsigned long data) | 113 | static void appldata_timer_function(unsigned long data) |
115 | { | 114 | { |
116 | if (atomic_dec_and_test(&appldata_expire_count)) { | 115 | queue_work(appldata_wq, (struct work_struct *) data); |
117 | atomic_set(&appldata_expire_count, num_online_cpus()); | ||
118 | queue_work(appldata_wq, (struct work_struct *) data); | ||
119 | } | ||
120 | } | 116 | } |
121 | 117 | ||
122 | /* | 118 | /* |
@@ -129,7 +125,6 @@ static void appldata_work_fn(struct work_struct *work) | |||
129 | struct list_head *lh; | 125 | struct list_head *lh; |
130 | struct appldata_ops *ops; | 126 | struct appldata_ops *ops; |
131 | 127 | ||
132 | get_online_cpus(); | ||
133 | mutex_lock(&appldata_ops_mutex); | 128 | mutex_lock(&appldata_ops_mutex); |
134 | list_for_each(lh, &appldata_ops_list) { | 129 | list_for_each(lh, &appldata_ops_list) { |
135 | ops = list_entry(lh, struct appldata_ops, list); | 130 | ops = list_entry(lh, struct appldata_ops, list); |
@@ -138,7 +133,6 @@ static void appldata_work_fn(struct work_struct *work) | |||
138 | } | 133 | } |
139 | } | 134 | } |
140 | mutex_unlock(&appldata_ops_mutex); | 135 | mutex_unlock(&appldata_ops_mutex); |
141 | put_online_cpus(); | ||
142 | } | 136 | } |
143 | 137 | ||
144 | /* | 138 | /* |
@@ -166,20 +160,6 @@ int appldata_diag(char record_nr, u16 function, unsigned long buffer, | |||
166 | 160 | ||
167 | /****************************** /proc stuff **********************************/ | 161 | /****************************** /proc stuff **********************************/ |
168 | 162 | ||
169 | /* | ||
170 | * appldata_mod_vtimer_wrap() | ||
171 | * | ||
172 | * wrapper function for mod_virt_timer(), because smp_call_function_single() | ||
173 | * accepts only one parameter. | ||
174 | */ | ||
175 | static void __appldata_mod_vtimer_wrap(void *p) { | ||
176 | struct { | ||
177 | struct vtimer_list *timer; | ||
178 | u64 expires; | ||
179 | } *args = p; | ||
180 | mod_virt_timer_periodic(args->timer, args->expires); | ||
181 | } | ||
182 | |||
183 | #define APPLDATA_ADD_TIMER 0 | 163 | #define APPLDATA_ADD_TIMER 0 |
184 | #define APPLDATA_DEL_TIMER 1 | 164 | #define APPLDATA_DEL_TIMER 1 |
185 | #define APPLDATA_MOD_TIMER 2 | 165 | #define APPLDATA_MOD_TIMER 2 |
@@ -190,49 +170,28 @@ static void __appldata_mod_vtimer_wrap(void *p) { | |||
190 | * Add, delete or modify virtual timers on all online cpus. | 170 | * Add, delete or modify virtual timers on all online cpus. |
191 | * The caller needs to get the appldata_timer_lock spinlock. | 171 | * The caller needs to get the appldata_timer_lock spinlock. |
192 | */ | 172 | */ |
193 | static void | 173 | static void __appldata_vtimer_setup(int cmd) |
194 | __appldata_vtimer_setup(int cmd) | ||
195 | { | 174 | { |
196 | u64 per_cpu_interval; | 175 | u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO; |
197 | int i; | ||
198 | 176 | ||
199 | switch (cmd) { | 177 | switch (cmd) { |
200 | case APPLDATA_ADD_TIMER: | 178 | case APPLDATA_ADD_TIMER: |
201 | if (appldata_timer_active) | 179 | if (appldata_timer_active) |
202 | break; | 180 | break; |
203 | per_cpu_interval = (u64) (appldata_interval*1000 / | 181 | appldata_timer.expires = timer_interval; |
204 | num_online_cpus()) * TOD_MICRO; | 182 | add_virt_timer_periodic(&appldata_timer); |
205 | for_each_online_cpu(i) { | ||
206 | per_cpu(appldata_timer, i).expires = per_cpu_interval; | ||
207 | smp_call_function_single(i, add_virt_timer_periodic, | ||
208 | &per_cpu(appldata_timer, i), | ||
209 | 1); | ||
210 | } | ||
211 | appldata_timer_active = 1; | 183 | appldata_timer_active = 1; |
212 | break; | 184 | break; |
213 | case APPLDATA_DEL_TIMER: | 185 | case APPLDATA_DEL_TIMER: |
214 | for_each_online_cpu(i) | 186 | del_virt_timer(&appldata_timer); |
215 | del_virt_timer(&per_cpu(appldata_timer, i)); | ||
216 | if (!appldata_timer_active) | 187 | if (!appldata_timer_active) |
217 | break; | 188 | break; |
218 | appldata_timer_active = 0; | 189 | appldata_timer_active = 0; |
219 | atomic_set(&appldata_expire_count, num_online_cpus()); | ||
220 | break; | 190 | break; |
221 | case APPLDATA_MOD_TIMER: | 191 | case APPLDATA_MOD_TIMER: |
222 | per_cpu_interval = (u64) (appldata_interval*1000 / | ||
223 | num_online_cpus()) * TOD_MICRO; | ||
224 | if (!appldata_timer_active) | 192 | if (!appldata_timer_active) |
225 | break; | 193 | break; |
226 | for_each_online_cpu(i) { | 194 | mod_virt_timer_periodic(&appldata_timer, timer_interval); |
227 | struct { | ||
228 | struct vtimer_list *timer; | ||
229 | u64 expires; | ||
230 | } args; | ||
231 | args.timer = &per_cpu(appldata_timer, i); | ||
232 | args.expires = per_cpu_interval; | ||
233 | smp_call_function_single(i, __appldata_mod_vtimer_wrap, | ||
234 | &args, 1); | ||
235 | } | ||
236 | } | 195 | } |
237 | } | 196 | } |
238 | 197 | ||
@@ -263,14 +222,12 @@ appldata_timer_handler(ctl_table *ctl, int write, | |||
263 | len = *lenp; | 222 | len = *lenp; |
264 | if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) | 223 | if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) |
265 | return -EFAULT; | 224 | return -EFAULT; |
266 | get_online_cpus(); | ||
267 | spin_lock(&appldata_timer_lock); | 225 | spin_lock(&appldata_timer_lock); |
268 | if (buf[0] == '1') | 226 | if (buf[0] == '1') |
269 | __appldata_vtimer_setup(APPLDATA_ADD_TIMER); | 227 | __appldata_vtimer_setup(APPLDATA_ADD_TIMER); |
270 | else if (buf[0] == '0') | 228 | else if (buf[0] == '0') |
271 | __appldata_vtimer_setup(APPLDATA_DEL_TIMER); | 229 | __appldata_vtimer_setup(APPLDATA_DEL_TIMER); |
272 | spin_unlock(&appldata_timer_lock); | 230 | spin_unlock(&appldata_timer_lock); |
273 | put_online_cpus(); | ||
274 | out: | 231 | out: |
275 | *lenp = len; | 232 | *lenp = len; |
276 | *ppos += len; | 233 | *ppos += len; |
@@ -303,20 +260,17 @@ appldata_interval_handler(ctl_table *ctl, int write, | |||
303 | goto out; | 260 | goto out; |
304 | } | 261 | } |
305 | len = *lenp; | 262 | len = *lenp; |
306 | if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) { | 263 | if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) |
307 | return -EFAULT; | 264 | return -EFAULT; |
308 | } | ||
309 | interval = 0; | 265 | interval = 0; |
310 | sscanf(buf, "%i", &interval); | 266 | sscanf(buf, "%i", &interval); |
311 | if (interval <= 0) | 267 | if (interval <= 0) |
312 | return -EINVAL; | 268 | return -EINVAL; |
313 | 269 | ||
314 | get_online_cpus(); | ||
315 | spin_lock(&appldata_timer_lock); | 270 | spin_lock(&appldata_timer_lock); |
316 | appldata_interval = interval; | 271 | appldata_interval = interval; |
317 | __appldata_vtimer_setup(APPLDATA_MOD_TIMER); | 272 | __appldata_vtimer_setup(APPLDATA_MOD_TIMER); |
318 | spin_unlock(&appldata_timer_lock); | 273 | spin_unlock(&appldata_timer_lock); |
319 | put_online_cpus(); | ||
320 | out: | 274 | out: |
321 | *lenp = len; | 275 | *lenp = len; |
322 | *ppos += len; | 276 | *ppos += len; |
@@ -483,14 +437,12 @@ static int appldata_freeze(struct device *dev) | |||
483 | int rc; | 437 | int rc; |
484 | struct list_head *lh; | 438 | struct list_head *lh; |
485 | 439 | ||
486 | get_online_cpus(); | ||
487 | spin_lock(&appldata_timer_lock); | 440 | spin_lock(&appldata_timer_lock); |
488 | if (appldata_timer_active) { | 441 | if (appldata_timer_active) { |
489 | __appldata_vtimer_setup(APPLDATA_DEL_TIMER); | 442 | __appldata_vtimer_setup(APPLDATA_DEL_TIMER); |
490 | appldata_timer_suspended = 1; | 443 | appldata_timer_suspended = 1; |
491 | } | 444 | } |
492 | spin_unlock(&appldata_timer_lock); | 445 | spin_unlock(&appldata_timer_lock); |
493 | put_online_cpus(); | ||
494 | 446 | ||
495 | mutex_lock(&appldata_ops_mutex); | 447 | mutex_lock(&appldata_ops_mutex); |
496 | list_for_each(lh, &appldata_ops_list) { | 448 | list_for_each(lh, &appldata_ops_list) { |
@@ -514,14 +466,12 @@ static int appldata_restore(struct device *dev) | |||
514 | int rc; | 466 | int rc; |
515 | struct list_head *lh; | 467 | struct list_head *lh; |
516 | 468 | ||
517 | get_online_cpus(); | ||
518 | spin_lock(&appldata_timer_lock); | 469 | spin_lock(&appldata_timer_lock); |
519 | if (appldata_timer_suspended) { | 470 | if (appldata_timer_suspended) { |
520 | __appldata_vtimer_setup(APPLDATA_ADD_TIMER); | 471 | __appldata_vtimer_setup(APPLDATA_ADD_TIMER); |
521 | appldata_timer_suspended = 0; | 472 | appldata_timer_suspended = 0; |
522 | } | 473 | } |
523 | spin_unlock(&appldata_timer_lock); | 474 | spin_unlock(&appldata_timer_lock); |
524 | put_online_cpus(); | ||
525 | 475 | ||
526 | mutex_lock(&appldata_ops_mutex); | 476 | mutex_lock(&appldata_ops_mutex); |
527 | list_for_each(lh, &appldata_ops_list) { | 477 | list_for_each(lh, &appldata_ops_list) { |
@@ -565,53 +515,6 @@ static struct platform_driver appldata_pdrv = { | |||
565 | 515 | ||
566 | /******************************* init / exit *********************************/ | 516 | /******************************* init / exit *********************************/ |
567 | 517 | ||
568 | static void __cpuinit appldata_online_cpu(int cpu) | ||
569 | { | ||
570 | init_virt_timer(&per_cpu(appldata_timer, cpu)); | ||
571 | per_cpu(appldata_timer, cpu).function = appldata_timer_function; | ||
572 | per_cpu(appldata_timer, cpu).data = (unsigned long) | ||
573 | &appldata_work; | ||
574 | atomic_inc(&appldata_expire_count); | ||
575 | spin_lock(&appldata_timer_lock); | ||
576 | __appldata_vtimer_setup(APPLDATA_MOD_TIMER); | ||
577 | spin_unlock(&appldata_timer_lock); | ||
578 | } | ||
579 | |||
580 | static void __cpuinit appldata_offline_cpu(int cpu) | ||
581 | { | ||
582 | del_virt_timer(&per_cpu(appldata_timer, cpu)); | ||
583 | if (atomic_dec_and_test(&appldata_expire_count)) { | ||
584 | atomic_set(&appldata_expire_count, num_online_cpus()); | ||
585 | queue_work(appldata_wq, &appldata_work); | ||
586 | } | ||
587 | spin_lock(&appldata_timer_lock); | ||
588 | __appldata_vtimer_setup(APPLDATA_MOD_TIMER); | ||
589 | spin_unlock(&appldata_timer_lock); | ||
590 | } | ||
591 | |||
592 | static int __cpuinit appldata_cpu_notify(struct notifier_block *self, | ||
593 | unsigned long action, | ||
594 | void *hcpu) | ||
595 | { | ||
596 | switch (action) { | ||
597 | case CPU_ONLINE: | ||
598 | case CPU_ONLINE_FROZEN: | ||
599 | appldata_online_cpu((long) hcpu); | ||
600 | break; | ||
601 | case CPU_DEAD: | ||
602 | case CPU_DEAD_FROZEN: | ||
603 | appldata_offline_cpu((long) hcpu); | ||
604 | break; | ||
605 | default: | ||
606 | break; | ||
607 | } | ||
608 | return NOTIFY_OK; | ||
609 | } | ||
610 | |||
611 | static struct notifier_block __cpuinitdata appldata_nb = { | ||
612 | .notifier_call = appldata_cpu_notify, | ||
613 | }; | ||
614 | |||
615 | /* | 518 | /* |
616 | * appldata_init() | 519 | * appldata_init() |
617 | * | 520 | * |
@@ -619,7 +522,10 @@ static struct notifier_block __cpuinitdata appldata_nb = { | |||
619 | */ | 522 | */ |
620 | static int __init appldata_init(void) | 523 | static int __init appldata_init(void) |
621 | { | 524 | { |
622 | int i, rc; | 525 | int rc; |
526 | |||
527 | appldata_timer.function = appldata_timer_function; | ||
528 | appldata_timer.data = (unsigned long) &appldata_work; | ||
623 | 529 | ||
624 | rc = platform_driver_register(&appldata_pdrv); | 530 | rc = platform_driver_register(&appldata_pdrv); |
625 | if (rc) | 531 | if (rc) |
@@ -637,14 +543,6 @@ static int __init appldata_init(void) | |||
637 | goto out_device; | 543 | goto out_device; |
638 | } | 544 | } |
639 | 545 | ||
640 | get_online_cpus(); | ||
641 | for_each_online_cpu(i) | ||
642 | appldata_online_cpu(i); | ||
643 | put_online_cpus(); | ||
644 | |||
645 | /* Register cpu hotplug notifier */ | ||
646 | register_hotcpu_notifier(&appldata_nb); | ||
647 | |||
648 | appldata_sysctl_header = register_sysctl_table(appldata_dir_table); | 546 | appldata_sysctl_header = register_sysctl_table(appldata_dir_table); |
649 | return 0; | 547 | return 0; |
650 | 548 | ||