diff options
| -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 | ||
