diff options
Diffstat (limited to 'arch/ia64/kernel/salinfo.c')
-rw-r--r-- | arch/ia64/kernel/salinfo.c | 170 |
1 files changed, 119 insertions, 51 deletions
diff --git a/arch/ia64/kernel/salinfo.c b/arch/ia64/kernel/salinfo.c index a87a162a3086..9d5a823479a3 100644 --- a/arch/ia64/kernel/salinfo.c +++ b/arch/ia64/kernel/salinfo.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Creates entries in /proc/sal for various system features. | 4 | * Creates entries in /proc/sal for various system features. |
5 | * | 5 | * |
6 | * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. | 6 | * Copyright (c) 2003, 2006 Silicon Graphics, Inc. All rights reserved. |
7 | * Copyright (c) 2003 Hewlett-Packard Co | 7 | * Copyright (c) 2003 Hewlett-Packard Co |
8 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | 8 | * Bjorn Helgaas <bjorn.helgaas@hp.com> |
9 | * | 9 | * |
@@ -27,9 +27,17 @@ | |||
27 | * mca.c may not pass a buffer, a NULL buffer just indicates that a new | 27 | * mca.c may not pass a buffer, a NULL buffer just indicates that a new |
28 | * record is available in SAL. | 28 | * record is available in SAL. |
29 | * Replace some NR_CPUS by cpus_online, for hotplug cpu. | 29 | * Replace some NR_CPUS by cpus_online, for hotplug cpu. |
30 | * | ||
31 | * Jan 5 2006 kaos@sgi.com | ||
32 | * Handle hotplug cpus coming online. | ||
33 | * Handle hotplug cpus going offline while they still have outstanding records. | ||
34 | * Use the cpu_* macros consistently. | ||
35 | * Replace the counting semaphore with a mutex and a test if the cpumask is non-empty. | ||
36 | * Modify the locking to make the test for "work to do" an atomic operation. | ||
30 | */ | 37 | */ |
31 | 38 | ||
32 | #include <linux/capability.h> | 39 | #include <linux/capability.h> |
40 | #include <linux/cpu.h> | ||
33 | #include <linux/types.h> | 41 | #include <linux/types.h> |
34 | #include <linux/proc_fs.h> | 42 | #include <linux/proc_fs.h> |
35 | #include <linux/module.h> | 43 | #include <linux/module.h> |
@@ -132,8 +140,8 @@ enum salinfo_state { | |||
132 | }; | 140 | }; |
133 | 141 | ||
134 | struct salinfo_data { | 142 | struct salinfo_data { |
135 | volatile cpumask_t cpu_event; /* which cpus have outstanding events */ | 143 | cpumask_t cpu_event; /* which cpus have outstanding events */ |
136 | struct semaphore sem; /* count of cpus with outstanding events (bits set in cpu_event) */ | 144 | struct semaphore mutex; |
137 | u8 *log_buffer; | 145 | u8 *log_buffer; |
138 | u64 log_size; | 146 | u64 log_size; |
139 | u8 *oemdata; /* decoded oem data */ | 147 | u8 *oemdata; /* decoded oem data */ |
@@ -174,6 +182,21 @@ struct salinfo_platform_oemdata_parms { | |||
174 | int ret; | 182 | int ret; |
175 | }; | 183 | }; |
176 | 184 | ||
185 | /* Kick the mutex that tells user space that there is work to do. Instead of | ||
186 | * trying to track the state of the mutex across multiple cpus, in user | ||
187 | * context, interrupt context, non-maskable interrupt context and hotplug cpu, | ||
188 | * it is far easier just to grab the mutex if it is free then release it. | ||
189 | * | ||
190 | * This routine must be called with data_saved_lock held, to make the down/up | ||
191 | * operation atomic. | ||
192 | */ | ||
193 | static void | ||
194 | salinfo_work_to_do(struct salinfo_data *data) | ||
195 | { | ||
196 | down_trylock(&data->mutex); | ||
197 | up(&data->mutex); | ||
198 | } | ||
199 | |||
177 | static void | 200 | static void |
178 | salinfo_platform_oemdata_cpu(void *context) | 201 | salinfo_platform_oemdata_cpu(void *context) |
179 | { | 202 | { |
@@ -212,9 +235,9 @@ salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe) | |||
212 | 235 | ||
213 | BUG_ON(type >= ARRAY_SIZE(salinfo_log_name)); | 236 | BUG_ON(type >= ARRAY_SIZE(salinfo_log_name)); |
214 | 237 | ||
238 | if (irqsafe) | ||
239 | spin_lock_irqsave(&data_saved_lock, flags); | ||
215 | if (buffer) { | 240 | if (buffer) { |
216 | if (irqsafe) | ||
217 | spin_lock_irqsave(&data_saved_lock, flags); | ||
218 | for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) { | 241 | for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) { |
219 | if (!data_saved->buffer) | 242 | if (!data_saved->buffer) |
220 | break; | 243 | break; |
@@ -232,13 +255,11 @@ salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe) | |||
232 | data_saved->size = size; | 255 | data_saved->size = size; |
233 | data_saved->buffer = buffer; | 256 | data_saved->buffer = buffer; |
234 | } | 257 | } |
235 | if (irqsafe) | ||
236 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
237 | } | 258 | } |
238 | 259 | cpu_set(smp_processor_id(), data->cpu_event); | |
239 | if (!test_and_set_bit(smp_processor_id(), &data->cpu_event)) { | 260 | if (irqsafe) { |
240 | if (irqsafe) | 261 | salinfo_work_to_do(data); |
241 | up(&data->sem); | 262 | spin_unlock_irqrestore(&data_saved_lock, flags); |
242 | } | 263 | } |
243 | } | 264 | } |
244 | 265 | ||
@@ -249,20 +270,17 @@ static struct timer_list salinfo_timer; | |||
249 | static void | 270 | static void |
250 | salinfo_timeout_check(struct salinfo_data *data) | 271 | salinfo_timeout_check(struct salinfo_data *data) |
251 | { | 272 | { |
252 | int i; | 273 | unsigned long flags; |
253 | if (!data->open) | 274 | if (!data->open) |
254 | return; | 275 | return; |
255 | for_each_online_cpu(i) { | 276 | if (!cpus_empty(data->cpu_event)) { |
256 | if (test_bit(i, &data->cpu_event)) { | 277 | spin_lock_irqsave(&data_saved_lock, flags); |
257 | /* double up() is not a problem, user space will see no | 278 | salinfo_work_to_do(data); |
258 | * records for the additional "events". | 279 | spin_unlock_irqrestore(&data_saved_lock, flags); |
259 | */ | ||
260 | up(&data->sem); | ||
261 | } | ||
262 | } | 280 | } |
263 | } | 281 | } |
264 | 282 | ||
265 | static void | 283 | static void |
266 | salinfo_timeout (unsigned long arg) | 284 | salinfo_timeout (unsigned long arg) |
267 | { | 285 | { |
268 | salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA); | 286 | salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA); |
@@ -290,16 +308,20 @@ salinfo_event_read(struct file *file, char __user *buffer, size_t count, loff_t | |||
290 | int i, n, cpu = -1; | 308 | int i, n, cpu = -1; |
291 | 309 | ||
292 | retry: | 310 | retry: |
293 | if (down_trylock(&data->sem)) { | 311 | if (cpus_empty(data->cpu_event) && down_trylock(&data->mutex)) { |
294 | if (file->f_flags & O_NONBLOCK) | 312 | if (file->f_flags & O_NONBLOCK) |
295 | return -EAGAIN; | 313 | return -EAGAIN; |
296 | if (down_interruptible(&data->sem)) | 314 | if (down_interruptible(&data->mutex)) |
297 | return -EINTR; | 315 | return -EINTR; |
298 | } | 316 | } |
299 | 317 | ||
300 | n = data->cpu_check; | 318 | n = data->cpu_check; |
301 | for (i = 0; i < NR_CPUS; i++) { | 319 | for (i = 0; i < NR_CPUS; i++) { |
302 | if (test_bit(n, &data->cpu_event) && cpu_online(n)) { | 320 | if (cpu_isset(n, data->cpu_event)) { |
321 | if (!cpu_online(n)) { | ||
322 | cpu_clear(n, data->cpu_event); | ||
323 | continue; | ||
324 | } | ||
303 | cpu = n; | 325 | cpu = n; |
304 | break; | 326 | break; |
305 | } | 327 | } |
@@ -310,9 +332,6 @@ retry: | |||
310 | if (cpu == -1) | 332 | if (cpu == -1) |
311 | goto retry; | 333 | goto retry; |
312 | 334 | ||
313 | /* events are sticky until the user says "clear" */ | ||
314 | up(&data->sem); | ||
315 | |||
316 | /* for next read, start checking at next CPU */ | 335 | /* for next read, start checking at next CPU */ |
317 | data->cpu_check = cpu; | 336 | data->cpu_check = cpu; |
318 | if (++data->cpu_check == NR_CPUS) | 337 | if (++data->cpu_check == NR_CPUS) |
@@ -381,10 +400,8 @@ salinfo_log_release(struct inode *inode, struct file *file) | |||
381 | static void | 400 | static void |
382 | call_on_cpu(int cpu, void (*fn)(void *), void *arg) | 401 | call_on_cpu(int cpu, void (*fn)(void *), void *arg) |
383 | { | 402 | { |
384 | cpumask_t save_cpus_allowed, new_cpus_allowed; | 403 | cpumask_t save_cpus_allowed = current->cpus_allowed; |
385 | memcpy(&save_cpus_allowed, ¤t->cpus_allowed, sizeof(save_cpus_allowed)); | 404 | cpumask_t new_cpus_allowed = cpumask_of_cpu(cpu); |
386 | memset(&new_cpus_allowed, 0, sizeof(new_cpus_allowed)); | ||
387 | set_bit(cpu, &new_cpus_allowed); | ||
388 | set_cpus_allowed(current, new_cpus_allowed); | 405 | set_cpus_allowed(current, new_cpus_allowed); |
389 | (*fn)(arg); | 406 | (*fn)(arg); |
390 | set_cpus_allowed(current, save_cpus_allowed); | 407 | set_cpus_allowed(current, save_cpus_allowed); |
@@ -433,10 +450,10 @@ retry: | |||
433 | if (!data->saved_num) | 450 | if (!data->saved_num) |
434 | call_on_cpu(cpu, salinfo_log_read_cpu, data); | 451 | call_on_cpu(cpu, salinfo_log_read_cpu, data); |
435 | if (!data->log_size) { | 452 | if (!data->log_size) { |
436 | data->state = STATE_NO_DATA; | 453 | data->state = STATE_NO_DATA; |
437 | clear_bit(cpu, &data->cpu_event); | 454 | cpu_clear(cpu, data->cpu_event); |
438 | } else { | 455 | } else { |
439 | data->state = STATE_LOG_RECORD; | 456 | data->state = STATE_LOG_RECORD; |
440 | } | 457 | } |
441 | } | 458 | } |
442 | 459 | ||
@@ -473,27 +490,31 @@ static int | |||
473 | salinfo_log_clear(struct salinfo_data *data, int cpu) | 490 | salinfo_log_clear(struct salinfo_data *data, int cpu) |
474 | { | 491 | { |
475 | sal_log_record_header_t *rh; | 492 | sal_log_record_header_t *rh; |
493 | unsigned long flags; | ||
494 | spin_lock_irqsave(&data_saved_lock, flags); | ||
476 | data->state = STATE_NO_DATA; | 495 | data->state = STATE_NO_DATA; |
477 | if (!test_bit(cpu, &data->cpu_event)) | 496 | if (!cpu_isset(cpu, data->cpu_event)) { |
497 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
478 | return 0; | 498 | return 0; |
479 | down(&data->sem); | 499 | } |
480 | clear_bit(cpu, &data->cpu_event); | 500 | cpu_clear(cpu, data->cpu_event); |
481 | if (data->saved_num) { | 501 | if (data->saved_num) { |
482 | unsigned long flags; | 502 | shift1_data_saved(data, data->saved_num - 1); |
483 | spin_lock_irqsave(&data_saved_lock, flags); | ||
484 | shift1_data_saved(data, data->saved_num - 1 ); | ||
485 | data->saved_num = 0; | 503 | data->saved_num = 0; |
486 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
487 | } | 504 | } |
505 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
488 | rh = (sal_log_record_header_t *)(data->log_buffer); | 506 | rh = (sal_log_record_header_t *)(data->log_buffer); |
489 | /* Corrected errors have already been cleared from SAL */ | 507 | /* Corrected errors have already been cleared from SAL */ |
490 | if (rh->severity != sal_log_severity_corrected) | 508 | if (rh->severity != sal_log_severity_corrected) |
491 | call_on_cpu(cpu, salinfo_log_clear_cpu, data); | 509 | call_on_cpu(cpu, salinfo_log_clear_cpu, data); |
492 | /* clearing a record may make a new record visible */ | 510 | /* clearing a record may make a new record visible */ |
493 | salinfo_log_new_read(cpu, data); | 511 | salinfo_log_new_read(cpu, data); |
494 | if (data->state == STATE_LOG_RECORD && | 512 | if (data->state == STATE_LOG_RECORD) { |
495 | !test_and_set_bit(cpu, &data->cpu_event)) | 513 | spin_lock_irqsave(&data_saved_lock, flags); |
496 | up(&data->sem); | 514 | cpu_set(cpu, data->cpu_event); |
515 | salinfo_work_to_do(data); | ||
516 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
517 | } | ||
497 | return 0; | 518 | return 0; |
498 | } | 519 | } |
499 | 520 | ||
@@ -550,6 +571,53 @@ static struct file_operations salinfo_data_fops = { | |||
550 | .write = salinfo_log_write, | 571 | .write = salinfo_log_write, |
551 | }; | 572 | }; |
552 | 573 | ||
574 | #ifdef CONFIG_HOTPLUG_CPU | ||
575 | static int __devinit | ||
576 | salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) | ||
577 | { | ||
578 | unsigned int i, cpu = (unsigned long)hcpu; | ||
579 | unsigned long flags; | ||
580 | struct salinfo_data *data; | ||
581 | switch (action) { | ||
582 | case CPU_ONLINE: | ||
583 | spin_lock_irqsave(&data_saved_lock, flags); | ||
584 | for (i = 0, data = salinfo_data; | ||
585 | i < ARRAY_SIZE(salinfo_data); | ||
586 | ++i, ++data) { | ||
587 | cpu_set(cpu, data->cpu_event); | ||
588 | salinfo_work_to_do(data); | ||
589 | } | ||
590 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
591 | break; | ||
592 | case CPU_DEAD: | ||
593 | spin_lock_irqsave(&data_saved_lock, flags); | ||
594 | for (i = 0, data = salinfo_data; | ||
595 | i < ARRAY_SIZE(salinfo_data); | ||
596 | ++i, ++data) { | ||
597 | struct salinfo_data_saved *data_saved; | ||
598 | int j; | ||
599 | for (j = ARRAY_SIZE(data->data_saved) - 1, data_saved = data->data_saved + j; | ||
600 | j >= 0; | ||
601 | --j, --data_saved) { | ||
602 | if (data_saved->buffer && data_saved->cpu == cpu) { | ||
603 | shift1_data_saved(data, j); | ||
604 | } | ||
605 | } | ||
606 | cpu_clear(cpu, data->cpu_event); | ||
607 | } | ||
608 | spin_unlock_irqrestore(&data_saved_lock, flags); | ||
609 | break; | ||
610 | } | ||
611 | return NOTIFY_OK; | ||
612 | } | ||
613 | |||
614 | static struct notifier_block salinfo_cpu_notifier = | ||
615 | { | ||
616 | .notifier_call = salinfo_cpu_callback, | ||
617 | .priority = 0, | ||
618 | }; | ||
619 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
620 | |||
553 | static int __init | 621 | static int __init |
554 | salinfo_init(void) | 622 | salinfo_init(void) |
555 | { | 623 | { |
@@ -557,7 +625,7 @@ salinfo_init(void) | |||
557 | struct proc_dir_entry **sdir = salinfo_proc_entries; /* keeps track of every entry */ | 625 | struct proc_dir_entry **sdir = salinfo_proc_entries; /* keeps track of every entry */ |
558 | struct proc_dir_entry *dir, *entry; | 626 | struct proc_dir_entry *dir, *entry; |
559 | struct salinfo_data *data; | 627 | struct salinfo_data *data; |
560 | int i, j, online; | 628 | int i, j; |
561 | 629 | ||
562 | salinfo_dir = proc_mkdir("sal", NULL); | 630 | salinfo_dir = proc_mkdir("sal", NULL); |
563 | if (!salinfo_dir) | 631 | if (!salinfo_dir) |
@@ -572,7 +640,7 @@ salinfo_init(void) | |||
572 | for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) { | 640 | for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) { |
573 | data = salinfo_data + i; | 641 | data = salinfo_data + i; |
574 | data->type = i; | 642 | data->type = i; |
575 | sema_init(&data->sem, 0); | 643 | init_MUTEX(&data->mutex); |
576 | dir = proc_mkdir(salinfo_log_name[i], salinfo_dir); | 644 | dir = proc_mkdir(salinfo_log_name[i], salinfo_dir); |
577 | if (!dir) | 645 | if (!dir) |
578 | continue; | 646 | continue; |
@@ -592,12 +660,8 @@ salinfo_init(void) | |||
592 | *sdir++ = entry; | 660 | *sdir++ = entry; |
593 | 661 | ||
594 | /* we missed any events before now */ | 662 | /* we missed any events before now */ |
595 | online = 0; | 663 | for_each_online_cpu(j) |
596 | for_each_online_cpu(j) { | 664 | cpu_set(j, data->cpu_event); |
597 | set_bit(j, &data->cpu_event); | ||
598 | ++online; | ||
599 | } | ||
600 | sema_init(&data->sem, online); | ||
601 | 665 | ||
602 | *sdir++ = dir; | 666 | *sdir++ = dir; |
603 | } | 667 | } |
@@ -609,6 +673,10 @@ salinfo_init(void) | |||
609 | salinfo_timer.function = &salinfo_timeout; | 673 | salinfo_timer.function = &salinfo_timeout; |
610 | add_timer(&salinfo_timer); | 674 | add_timer(&salinfo_timer); |
611 | 675 | ||
676 | #ifdef CONFIG_HOTPLUG_CPU | ||
677 | register_cpu_notifier(&salinfo_cpu_notifier); | ||
678 | #endif | ||
679 | |||
612 | return 0; | 680 | return 0; |
613 | } | 681 | } |
614 | 682 | ||