[IA64] Add hotplug cpu to salinfo.c, replace semaphore with mutex

Add hotplug cpu support to salinfo.c.

The cpu_event field is a cpumask so use the cpu_* macros consistently,
replacing the existing mixture of cpu_* and *_bit macros.

Instead of counting the number of outstanding events in a semaphore and
trying to track that count over user space context, interrupt context,
non-maskable interrupt context and cpu hotplug, replace the semaphore
with a test for "any bits set" combined with a mutex.

Modify the locking to make the test for "work to do" an atomic
operation.

Signed-off-by: Keith Owens <kaos@sgi.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>

authored by Keith Owens and committed by Tony Luck e026cca0 15029285

+121 -53
+121 -53
arch/ia64/kernel/salinfo.c
··· 3 3 * 4 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 7 * Copyright (c) 2003 Hewlett-Packard Co 8 8 * Bjorn Helgaas <bjorn.helgaas@hp.com> 9 9 * ··· 27 27 * mca.c may not pass a buffer, a NULL buffer just indicates that a new 28 28 * record is available in SAL. 29 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 39 #include <linux/capability.h> 40 + #include <linux/cpu.h> 33 41 #include <linux/types.h> 34 42 #include <linux/proc_fs.h> 35 43 #include <linux/module.h> ··· 140 132 }; 141 133 142 134 struct salinfo_data { 143 - volatile cpumask_t cpu_event; /* which cpus have outstanding events */ 144 - struct semaphore sem; /* count of cpus with outstanding events (bits set in cpu_event) */ 135 + cpumask_t cpu_event; /* which cpus have outstanding events */ 136 + struct semaphore mutex; 145 137 u8 *log_buffer; 146 138 u64 log_size; 147 139 u8 *oemdata; /* decoded oem data */ ··· 182 174 int ret; 183 175 }; 184 176 177 + /* Kick the mutex that tells user space that there is work to do. Instead of 178 + * trying to track the state of the mutex across multiple cpus, in user 179 + * context, interrupt context, non-maskable interrupt context and hotplug cpu, 180 + * it is far easier just to grab the mutex if it is free then release it. 181 + * 182 + * This routine must be called with data_saved_lock held, to make the down/up 183 + * operation atomic. 184 + */ 185 + static void 186 + salinfo_work_to_do(struct salinfo_data *data) 187 + { 188 + down_trylock(&data->mutex); 189 + up(&data->mutex); 190 + } 191 + 185 192 static void 186 193 salinfo_platform_oemdata_cpu(void *context) 187 194 { ··· 235 212 236 213 BUG_ON(type >= ARRAY_SIZE(salinfo_log_name)); 237 214 215 + if (irqsafe) 216 + spin_lock_irqsave(&data_saved_lock, flags); 238 217 if (buffer) { 239 - if (irqsafe) 240 - spin_lock_irqsave(&data_saved_lock, flags); 241 218 for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) { 242 219 if (!data_saved->buffer) 243 220 break; ··· 255 232 data_saved->size = size; 256 233 data_saved->buffer = buffer; 257 234 } 258 - if (irqsafe) 259 - spin_unlock_irqrestore(&data_saved_lock, flags); 260 235 } 261 - 262 - if (!test_and_set_bit(smp_processor_id(), &data->cpu_event)) { 263 - if (irqsafe) 264 - up(&data->sem); 236 + cpu_set(smp_processor_id(), data->cpu_event); 237 + if (irqsafe) { 238 + salinfo_work_to_do(data); 239 + spin_unlock_irqrestore(&data_saved_lock, flags); 265 240 } 266 241 } 267 242 ··· 270 249 static void 271 250 salinfo_timeout_check(struct salinfo_data *data) 272 251 { 273 - int i; 252 + unsigned long flags; 274 253 if (!data->open) 275 254 return; 276 - for_each_online_cpu(i) { 277 - if (test_bit(i, &data->cpu_event)) { 278 - /* double up() is not a problem, user space will see no 279 - * records for the additional "events". 280 - */ 281 - up(&data->sem); 282 - } 255 + if (!cpus_empty(data->cpu_event)) { 256 + spin_lock_irqsave(&data_saved_lock, flags); 257 + salinfo_work_to_do(data); 258 + spin_unlock_irqrestore(&data_saved_lock, flags); 283 259 } 284 260 } 285 261 286 - static void 262 + static void 287 263 salinfo_timeout (unsigned long arg) 288 264 { 289 265 salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA); ··· 308 290 int i, n, cpu = -1; 309 291 310 292 retry: 311 - if (down_trylock(&data->sem)) { 293 + if (cpus_empty(data->cpu_event) && down_trylock(&data->mutex)) { 312 294 if (file->f_flags & O_NONBLOCK) 313 295 return -EAGAIN; 314 - if (down_interruptible(&data->sem)) 296 + if (down_interruptible(&data->mutex)) 315 297 return -EINTR; 316 298 } 317 299 318 300 n = data->cpu_check; 319 301 for (i = 0; i < NR_CPUS; i++) { 320 - if (test_bit(n, &data->cpu_event) && cpu_online(n)) { 302 + if (cpu_isset(n, data->cpu_event)) { 303 + if (!cpu_online(n)) { 304 + cpu_clear(n, data->cpu_event); 305 + continue; 306 + } 321 307 cpu = n; 322 308 break; 323 309 } ··· 331 309 332 310 if (cpu == -1) 333 311 goto retry; 334 - 335 - /* events are sticky until the user says "clear" */ 336 - up(&data->sem); 337 312 338 313 /* for next read, start checking at next CPU */ 339 314 data->cpu_check = cpu; ··· 400 381 static void 401 382 call_on_cpu(int cpu, void (*fn)(void *), void *arg) 402 383 { 403 - cpumask_t save_cpus_allowed, new_cpus_allowed; 404 - memcpy(&save_cpus_allowed, &current->cpus_allowed, sizeof(save_cpus_allowed)); 405 - memset(&new_cpus_allowed, 0, sizeof(new_cpus_allowed)); 406 - set_bit(cpu, &new_cpus_allowed); 384 + cpumask_t save_cpus_allowed = current->cpus_allowed; 385 + cpumask_t new_cpus_allowed = cpumask_of_cpu(cpu); 407 386 set_cpus_allowed(current, new_cpus_allowed); 408 387 (*fn)(arg); 409 388 set_cpus_allowed(current, save_cpus_allowed); ··· 450 433 if (!data->saved_num) 451 434 call_on_cpu(cpu, salinfo_log_read_cpu, data); 452 435 if (!data->log_size) { 453 - data->state = STATE_NO_DATA; 454 - clear_bit(cpu, &data->cpu_event); 436 + data->state = STATE_NO_DATA; 437 + cpu_clear(cpu, data->cpu_event); 455 438 } else { 456 - data->state = STATE_LOG_RECORD; 439 + data->state = STATE_LOG_RECORD; 457 440 } 458 441 } 459 442 ··· 490 473 salinfo_log_clear(struct salinfo_data *data, int cpu) 491 474 { 492 475 sal_log_record_header_t *rh; 476 + unsigned long flags; 477 + spin_lock_irqsave(&data_saved_lock, flags); 493 478 data->state = STATE_NO_DATA; 494 - if (!test_bit(cpu, &data->cpu_event)) 495 - return 0; 496 - down(&data->sem); 497 - clear_bit(cpu, &data->cpu_event); 498 - if (data->saved_num) { 499 - unsigned long flags; 500 - spin_lock_irqsave(&data_saved_lock, flags); 501 - shift1_data_saved(data, data->saved_num - 1 ); 502 - data->saved_num = 0; 479 + if (!cpu_isset(cpu, data->cpu_event)) { 503 480 spin_unlock_irqrestore(&data_saved_lock, flags); 481 + return 0; 504 482 } 483 + cpu_clear(cpu, data->cpu_event); 484 + if (data->saved_num) { 485 + shift1_data_saved(data, data->saved_num - 1); 486 + data->saved_num = 0; 487 + } 488 + spin_unlock_irqrestore(&data_saved_lock, flags); 505 489 rh = (sal_log_record_header_t *)(data->log_buffer); 506 490 /* Corrected errors have already been cleared from SAL */ 507 491 if (rh->severity != sal_log_severity_corrected) 508 492 call_on_cpu(cpu, salinfo_log_clear_cpu, data); 509 493 /* clearing a record may make a new record visible */ 510 494 salinfo_log_new_read(cpu, data); 511 - if (data->state == STATE_LOG_RECORD && 512 - !test_and_set_bit(cpu, &data->cpu_event)) 513 - up(&data->sem); 495 + if (data->state == STATE_LOG_RECORD) { 496 + spin_lock_irqsave(&data_saved_lock, flags); 497 + cpu_set(cpu, data->cpu_event); 498 + salinfo_work_to_do(data); 499 + spin_unlock_irqrestore(&data_saved_lock, flags); 500 + } 514 501 return 0; 515 502 } 516 503 ··· 571 550 .write = salinfo_log_write, 572 551 }; 573 552 553 + #ifdef CONFIG_HOTPLUG_CPU 554 + static int __devinit 555 + salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) 556 + { 557 + unsigned int i, cpu = (unsigned long)hcpu; 558 + unsigned long flags; 559 + struct salinfo_data *data; 560 + switch (action) { 561 + case CPU_ONLINE: 562 + spin_lock_irqsave(&data_saved_lock, flags); 563 + for (i = 0, data = salinfo_data; 564 + i < ARRAY_SIZE(salinfo_data); 565 + ++i, ++data) { 566 + cpu_set(cpu, data->cpu_event); 567 + salinfo_work_to_do(data); 568 + } 569 + spin_unlock_irqrestore(&data_saved_lock, flags); 570 + break; 571 + case CPU_DEAD: 572 + spin_lock_irqsave(&data_saved_lock, flags); 573 + for (i = 0, data = salinfo_data; 574 + i < ARRAY_SIZE(salinfo_data); 575 + ++i, ++data) { 576 + struct salinfo_data_saved *data_saved; 577 + int j; 578 + for (j = ARRAY_SIZE(data->data_saved) - 1, data_saved = data->data_saved + j; 579 + j >= 0; 580 + --j, --data_saved) { 581 + if (data_saved->buffer && data_saved->cpu == cpu) { 582 + shift1_data_saved(data, j); 583 + } 584 + } 585 + cpu_clear(cpu, data->cpu_event); 586 + } 587 + spin_unlock_irqrestore(&data_saved_lock, flags); 588 + break; 589 + } 590 + return NOTIFY_OK; 591 + } 592 + 593 + static struct notifier_block salinfo_cpu_notifier = 594 + { 595 + .notifier_call = salinfo_cpu_callback, 596 + .priority = 0, 597 + }; 598 + #endif /* CONFIG_HOTPLUG_CPU */ 599 + 574 600 static int __init 575 601 salinfo_init(void) 576 602 { ··· 625 557 struct proc_dir_entry **sdir = salinfo_proc_entries; /* keeps track of every entry */ 626 558 struct proc_dir_entry *dir, *entry; 627 559 struct salinfo_data *data; 628 - int i, j, online; 560 + int i, j; 629 561 630 562 salinfo_dir = proc_mkdir("sal", NULL); 631 563 if (!salinfo_dir) ··· 640 572 for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) { 641 573 data = salinfo_data + i; 642 574 data->type = i; 643 - sema_init(&data->sem, 0); 575 + init_MUTEX(&data->mutex); 644 576 dir = proc_mkdir(salinfo_log_name[i], salinfo_dir); 645 577 if (!dir) 646 578 continue; ··· 660 592 *sdir++ = entry; 661 593 662 594 /* we missed any events before now */ 663 - online = 0; 664 - for_each_online_cpu(j) { 665 - set_bit(j, &data->cpu_event); 666 - ++online; 667 - } 668 - sema_init(&data->sem, online); 595 + for_each_online_cpu(j) 596 + cpu_set(j, data->cpu_event); 669 597 670 598 *sdir++ = dir; 671 599 } ··· 672 608 salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY; 673 609 salinfo_timer.function = &salinfo_timeout; 674 610 add_timer(&salinfo_timer); 611 + 612 + #ifdef CONFIG_HOTPLUG_CPU 613 + register_cpu_notifier(&salinfo_cpu_notifier); 614 + #endif 675 615 676 616 return 0; 677 617 }