salinfo.c (18403B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * salinfo.c 4 * 5 * Creates entries in /proc/sal for various system features. 6 * 7 * Copyright (c) 2003, 2006 Silicon Graphics, Inc. All rights reserved. 8 * Copyright (c) 2003 Hewlett-Packard Co 9 * Bjorn Helgaas <bjorn.helgaas@hp.com> 10 * 11 * 10/30/2001 jbarnes@sgi.com copied much of Stephane's palinfo 12 * code to create this file 13 * Oct 23 2003 kaos@sgi.com 14 * Replace IPI with set_cpus_allowed() to read a record from the required cpu. 15 * Redesign salinfo log processing to separate interrupt and user space 16 * contexts. 17 * Cache the record across multi-block reads from user space. 18 * Support > 64 cpus. 19 * Delete module_exit and MOD_INC/DEC_COUNT, salinfo cannot be a module. 20 * 21 * Jan 28 2004 kaos@sgi.com 22 * Periodically check for outstanding MCA or INIT records. 23 * 24 * Dec 5 2004 kaos@sgi.com 25 * Standardize which records are cleared automatically. 26 * 27 * Aug 18 2005 kaos@sgi.com 28 * mca.c may not pass a buffer, a NULL buffer just indicates that a new 29 * record is available in SAL. 30 * Replace some NR_CPUS by cpus_online, for hotplug cpu. 31 * 32 * Jan 5 2006 kaos@sgi.com 33 * Handle hotplug cpus coming online. 34 * Handle hotplug cpus going offline while they still have outstanding records. 35 * Use the cpu_* macros consistently. 36 * Replace the counting semaphore with a mutex and a test if the cpumask is non-empty. 37 * Modify the locking to make the test for "work to do" an atomic operation. 38 */ 39 40#include <linux/capability.h> 41#include <linux/cpu.h> 42#include <linux/types.h> 43#include <linux/proc_fs.h> 44#include <linux/seq_file.h> 45#include <linux/module.h> 46#include <linux/smp.h> 47#include <linux/timer.h> 48#include <linux/vmalloc.h> 49#include <linux/semaphore.h> 50 51#include <asm/sal.h> 52#include <linux/uaccess.h> 53 54MODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>"); 55MODULE_DESCRIPTION("/proc interface to IA-64 SAL features"); 56MODULE_LICENSE("GPL"); 57 58typedef struct { 59 const char *name; /* name of the proc entry */ 60 unsigned long feature; /* feature bit */ 61 struct proc_dir_entry *entry; /* registered entry (removal) */ 62} salinfo_entry_t; 63 64/* 65 * List {name,feature} pairs for every entry in /proc/sal/<feature> 66 * that this module exports 67 */ 68static const salinfo_entry_t salinfo_entries[]={ 69 { "bus_lock", IA64_SAL_PLATFORM_FEATURE_BUS_LOCK, }, 70 { "irq_redirection", IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT, }, 71 { "ipi_redirection", IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT, }, 72 { "itc_drift", IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT, }, 73}; 74 75#define NR_SALINFO_ENTRIES ARRAY_SIZE(salinfo_entries) 76 77static char *salinfo_log_name[] = { 78 "mca", 79 "init", 80 "cmc", 81 "cpe", 82}; 83 84static struct proc_dir_entry *salinfo_proc_entries[ 85 ARRAY_SIZE(salinfo_entries) + /* /proc/sal/bus_lock */ 86 ARRAY_SIZE(salinfo_log_name) + /* /proc/sal/{mca,...} */ 87 (2 * ARRAY_SIZE(salinfo_log_name)) + /* /proc/sal/mca/{event,data} */ 88 1]; /* /proc/sal */ 89 90/* Some records we get ourselves, some are accessed as saved data in buffers 91 * that are owned by mca.c. 92 */ 93struct salinfo_data_saved { 94 u8* buffer; 95 u64 size; 96 u64 id; 97 int cpu; 98}; 99 100/* State transitions. Actions are :- 101 * Write "read <cpunum>" to the data file. 102 * Write "clear <cpunum>" to the data file. 103 * Write "oemdata <cpunum> <offset> to the data file. 104 * Read from the data file. 105 * Close the data file. 106 * 107 * Start state is NO_DATA. 108 * 109 * NO_DATA 110 * write "read <cpunum>" -> NO_DATA or LOG_RECORD. 111 * write "clear <cpunum>" -> NO_DATA or LOG_RECORD. 112 * write "oemdata <cpunum> <offset> -> return -EINVAL. 113 * read data -> return EOF. 114 * close -> unchanged. Free record areas. 115 * 116 * LOG_RECORD 117 * write "read <cpunum>" -> NO_DATA or LOG_RECORD. 118 * write "clear <cpunum>" -> NO_DATA or LOG_RECORD. 119 * write "oemdata <cpunum> <offset> -> format the oem data, goto OEMDATA. 120 * read data -> return the INIT/MCA/CMC/CPE record. 121 * close -> unchanged. Keep record areas. 122 * 123 * OEMDATA 124 * write "read <cpunum>" -> NO_DATA or LOG_RECORD. 125 * write "clear <cpunum>" -> NO_DATA or LOG_RECORD. 126 * write "oemdata <cpunum> <offset> -> format the oem data, goto OEMDATA. 127 * read data -> return the formatted oemdata. 128 * close -> unchanged. Keep record areas. 129 * 130 * Closing the data file does not change the state. This allows shell scripts 131 * to manipulate salinfo data, each shell redirection opens the file, does one 132 * action then closes it again. The record areas are only freed at close when 133 * the state is NO_DATA. 134 */ 135enum salinfo_state { 136 STATE_NO_DATA, 137 STATE_LOG_RECORD, 138 STATE_OEMDATA, 139}; 140 141struct salinfo_data { 142 cpumask_t cpu_event; /* which cpus have outstanding events */ 143 wait_queue_head_t read_wait; 144 u8 *log_buffer; 145 u64 log_size; 146 u8 *oemdata; /* decoded oem data */ 147 u64 oemdata_size; 148 int open; /* single-open to prevent races */ 149 u8 type; 150 u8 saved_num; /* using a saved record? */ 151 enum salinfo_state state :8; /* processing state */ 152 u8 padding; 153 int cpu_check; /* next CPU to check */ 154 struct salinfo_data_saved data_saved[5];/* save last 5 records from mca.c, must be < 255 */ 155}; 156 157static struct salinfo_data salinfo_data[ARRAY_SIZE(salinfo_log_name)]; 158 159static DEFINE_SPINLOCK(data_lock); 160static DEFINE_SPINLOCK(data_saved_lock); 161 162/** salinfo_platform_oemdata - optional callback to decode oemdata from an error 163 * record. 164 * @sect_header: pointer to the start of the section to decode. 165 * @oemdata: returns vmalloc area containing the decoded output. 166 * @oemdata_size: returns length of decoded output (strlen). 167 * 168 * Description: If user space asks for oem data to be decoded by the kernel 169 * and/or prom and the platform has set salinfo_platform_oemdata to the address 170 * of a platform specific routine then call that routine. salinfo_platform_oemdata 171 * vmalloc's and formats its output area, returning the address of the text 172 * and its strlen. Returns 0 for success, -ve for error. The callback is 173 * invoked on the cpu that generated the error record. 174 */ 175int (*salinfo_platform_oemdata)(const u8 *sect_header, u8 **oemdata, u64 *oemdata_size); 176 177struct salinfo_platform_oemdata_parms { 178 const u8 *efi_guid; 179 u8 **oemdata; 180 u64 *oemdata_size; 181}; 182 183static long 184salinfo_platform_oemdata_cpu(void *context) 185{ 186 struct salinfo_platform_oemdata_parms *parms = context; 187 188 return salinfo_platform_oemdata(parms->efi_guid, parms->oemdata, parms->oemdata_size); 189} 190 191static void 192shift1_data_saved (struct salinfo_data *data, int shift) 193{ 194 memcpy(data->data_saved+shift, data->data_saved+shift+1, 195 (ARRAY_SIZE(data->data_saved) - (shift+1)) * sizeof(data->data_saved[0])); 196 memset(data->data_saved + ARRAY_SIZE(data->data_saved) - 1, 0, 197 sizeof(data->data_saved[0])); 198} 199 200/* This routine is invoked in interrupt context. Note: mca.c enables 201 * interrupts before calling this code for CMC/CPE. MCA and INIT events are 202 * not irq safe, do not call any routines that use spinlocks, they may deadlock. 203 * MCA and INIT records are recorded, a timer event will look for any 204 * outstanding events and wake up the user space code. 205 * 206 * The buffer passed from mca.c points to the output from ia64_log_get. This is 207 * a persistent buffer but its contents can change between the interrupt and 208 * when user space processes the record. Save the record id to identify 209 * changes. If the buffer is NULL then just update the bitmap. 210 */ 211void 212salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe) 213{ 214 struct salinfo_data *data = salinfo_data + type; 215 struct salinfo_data_saved *data_saved; 216 unsigned long flags = 0; 217 int i; 218 int saved_size = ARRAY_SIZE(data->data_saved); 219 220 BUG_ON(type >= ARRAY_SIZE(salinfo_log_name)); 221 222 if (irqsafe) 223 spin_lock_irqsave(&data_saved_lock, flags); 224 if (buffer) { 225 for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) { 226 if (!data_saved->buffer) 227 break; 228 } 229 if (i == saved_size) { 230 if (!data->saved_num) { 231 shift1_data_saved(data, 0); 232 data_saved = data->data_saved + saved_size - 1; 233 } else 234 data_saved = NULL; 235 } 236 if (data_saved) { 237 data_saved->cpu = smp_processor_id(); 238 data_saved->id = ((sal_log_record_header_t *)buffer)->id; 239 data_saved->size = size; 240 data_saved->buffer = buffer; 241 } 242 } 243 cpumask_set_cpu(smp_processor_id(), &data->cpu_event); 244 if (irqsafe) { 245 wake_up_interruptible(&data->read_wait); 246 spin_unlock_irqrestore(&data_saved_lock, flags); 247 } 248} 249 250/* Check for outstanding MCA/INIT records every minute (arbitrary) */ 251#define SALINFO_TIMER_DELAY (60*HZ) 252static struct timer_list salinfo_timer; 253extern void ia64_mlogbuf_dump(void); 254 255static void 256salinfo_timeout_check(struct salinfo_data *data) 257{ 258 if (!data->open) 259 return; 260 if (!cpumask_empty(&data->cpu_event)) 261 wake_up_interruptible(&data->read_wait); 262} 263 264static void 265salinfo_timeout(struct timer_list *unused) 266{ 267 ia64_mlogbuf_dump(); 268 salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA); 269 salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_INIT); 270 salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY; 271 add_timer(&salinfo_timer); 272} 273 274static int 275salinfo_event_open(struct inode *inode, struct file *file) 276{ 277 if (!capable(CAP_SYS_ADMIN)) 278 return -EPERM; 279 return 0; 280} 281 282static ssize_t 283salinfo_event_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 284{ 285 struct salinfo_data *data = pde_data(file_inode(file)); 286 char cmd[32]; 287 size_t size; 288 int i, n, cpu = -1; 289 290retry: 291 if (cpumask_empty(&data->cpu_event)) { 292 if (file->f_flags & O_NONBLOCK) 293 return -EAGAIN; 294 if (wait_event_interruptible(data->read_wait, 295 !cpumask_empty(&data->cpu_event))) 296 return -EINTR; 297 } 298 299 n = data->cpu_check; 300 for (i = 0; i < nr_cpu_ids; i++) { 301 if (cpumask_test_cpu(n, &data->cpu_event)) { 302 if (!cpu_online(n)) { 303 cpumask_clear_cpu(n, &data->cpu_event); 304 continue; 305 } 306 cpu = n; 307 break; 308 } 309 if (++n == nr_cpu_ids) 310 n = 0; 311 } 312 313 if (cpu == -1) 314 goto retry; 315 316 ia64_mlogbuf_dump(); 317 318 /* for next read, start checking at next CPU */ 319 data->cpu_check = cpu; 320 if (++data->cpu_check == nr_cpu_ids) 321 data->cpu_check = 0; 322 323 snprintf(cmd, sizeof(cmd), "read %d\n", cpu); 324 325 size = strlen(cmd); 326 if (size > count) 327 size = count; 328 if (copy_to_user(buffer, cmd, size)) 329 return -EFAULT; 330 331 return size; 332} 333 334static const struct proc_ops salinfo_event_proc_ops = { 335 .proc_open = salinfo_event_open, 336 .proc_read = salinfo_event_read, 337 .proc_lseek = noop_llseek, 338}; 339 340static int 341salinfo_log_open(struct inode *inode, struct file *file) 342{ 343 struct salinfo_data *data = pde_data(inode); 344 345 if (!capable(CAP_SYS_ADMIN)) 346 return -EPERM; 347 348 spin_lock(&data_lock); 349 if (data->open) { 350 spin_unlock(&data_lock); 351 return -EBUSY; 352 } 353 data->open = 1; 354 spin_unlock(&data_lock); 355 356 if (data->state == STATE_NO_DATA && 357 !(data->log_buffer = vmalloc(ia64_sal_get_state_info_size(data->type)))) { 358 data->open = 0; 359 return -ENOMEM; 360 } 361 362 return 0; 363} 364 365static int 366salinfo_log_release(struct inode *inode, struct file *file) 367{ 368 struct salinfo_data *data = pde_data(inode); 369 370 if (data->state == STATE_NO_DATA) { 371 vfree(data->log_buffer); 372 vfree(data->oemdata); 373 data->log_buffer = NULL; 374 data->oemdata = NULL; 375 } 376 spin_lock(&data_lock); 377 data->open = 0; 378 spin_unlock(&data_lock); 379 return 0; 380} 381 382static long 383salinfo_log_read_cpu(void *context) 384{ 385 struct salinfo_data *data = context; 386 sal_log_record_header_t *rh; 387 data->log_size = ia64_sal_get_state_info(data->type, (u64 *) data->log_buffer); 388 rh = (sal_log_record_header_t *)(data->log_buffer); 389 /* Clear corrected errors as they are read from SAL */ 390 if (rh->severity == sal_log_severity_corrected) 391 ia64_sal_clear_state_info(data->type); 392 return 0; 393} 394 395static void 396salinfo_log_new_read(int cpu, struct salinfo_data *data) 397{ 398 struct salinfo_data_saved *data_saved; 399 unsigned long flags; 400 int i; 401 int saved_size = ARRAY_SIZE(data->data_saved); 402 403 data->saved_num = 0; 404 spin_lock_irqsave(&data_saved_lock, flags); 405retry: 406 for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) { 407 if (data_saved->buffer && data_saved->cpu == cpu) { 408 sal_log_record_header_t *rh = (sal_log_record_header_t *)(data_saved->buffer); 409 data->log_size = data_saved->size; 410 memcpy(data->log_buffer, rh, data->log_size); 411 barrier(); /* id check must not be moved */ 412 if (rh->id == data_saved->id) { 413 data->saved_num = i+1; 414 break; 415 } 416 /* saved record changed by mca.c since interrupt, discard it */ 417 shift1_data_saved(data, i); 418 goto retry; 419 } 420 } 421 spin_unlock_irqrestore(&data_saved_lock, flags); 422 423 if (!data->saved_num) 424 work_on_cpu_safe(cpu, salinfo_log_read_cpu, data); 425 if (!data->log_size) { 426 data->state = STATE_NO_DATA; 427 cpumask_clear_cpu(cpu, &data->cpu_event); 428 } else { 429 data->state = STATE_LOG_RECORD; 430 } 431} 432 433static ssize_t 434salinfo_log_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 435{ 436 struct salinfo_data *data = pde_data(file_inode(file)); 437 u8 *buf; 438 u64 bufsize; 439 440 if (data->state == STATE_LOG_RECORD) { 441 buf = data->log_buffer; 442 bufsize = data->log_size; 443 } else if (data->state == STATE_OEMDATA) { 444 buf = data->oemdata; 445 bufsize = data->oemdata_size; 446 } else { 447 buf = NULL; 448 bufsize = 0; 449 } 450 return simple_read_from_buffer(buffer, count, ppos, buf, bufsize); 451} 452 453static long 454salinfo_log_clear_cpu(void *context) 455{ 456 struct salinfo_data *data = context; 457 458 ia64_sal_clear_state_info(data->type); 459 return 0; 460} 461 462static int 463salinfo_log_clear(struct salinfo_data *data, int cpu) 464{ 465 sal_log_record_header_t *rh; 466 unsigned long flags; 467 spin_lock_irqsave(&data_saved_lock, flags); 468 data->state = STATE_NO_DATA; 469 if (!cpumask_test_cpu(cpu, &data->cpu_event)) { 470 spin_unlock_irqrestore(&data_saved_lock, flags); 471 return 0; 472 } 473 cpumask_clear_cpu(cpu, &data->cpu_event); 474 if (data->saved_num) { 475 shift1_data_saved(data, data->saved_num - 1); 476 data->saved_num = 0; 477 } 478 spin_unlock_irqrestore(&data_saved_lock, flags); 479 rh = (sal_log_record_header_t *)(data->log_buffer); 480 /* Corrected errors have already been cleared from SAL */ 481 if (rh->severity != sal_log_severity_corrected) 482 work_on_cpu_safe(cpu, salinfo_log_clear_cpu, data); 483 /* clearing a record may make a new record visible */ 484 salinfo_log_new_read(cpu, data); 485 if (data->state == STATE_LOG_RECORD) { 486 spin_lock_irqsave(&data_saved_lock, flags); 487 cpumask_set_cpu(cpu, &data->cpu_event); 488 wake_up_interruptible(&data->read_wait); 489 spin_unlock_irqrestore(&data_saved_lock, flags); 490 } 491 return 0; 492} 493 494static ssize_t 495salinfo_log_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 496{ 497 struct salinfo_data *data = pde_data(file_inode(file)); 498 char cmd[32]; 499 size_t size; 500 u32 offset; 501 int cpu; 502 503 size = sizeof(cmd); 504 if (count < size) 505 size = count; 506 if (copy_from_user(cmd, buffer, size)) 507 return -EFAULT; 508 509 if (sscanf(cmd, "read %d", &cpu) == 1) { 510 salinfo_log_new_read(cpu, data); 511 } else if (sscanf(cmd, "clear %d", &cpu) == 1) { 512 int ret; 513 if ((ret = salinfo_log_clear(data, cpu))) 514 count = ret; 515 } else if (sscanf(cmd, "oemdata %d %d", &cpu, &offset) == 2) { 516 if (data->state != STATE_LOG_RECORD && data->state != STATE_OEMDATA) 517 return -EINVAL; 518 if (offset > data->log_size - sizeof(efi_guid_t)) 519 return -EINVAL; 520 data->state = STATE_OEMDATA; 521 if (salinfo_platform_oemdata) { 522 struct salinfo_platform_oemdata_parms parms = { 523 .efi_guid = data->log_buffer + offset, 524 .oemdata = &data->oemdata, 525 .oemdata_size = &data->oemdata_size 526 }; 527 count = work_on_cpu_safe(cpu, salinfo_platform_oemdata_cpu, 528 &parms); 529 } else 530 data->oemdata_size = 0; 531 } else 532 return -EINVAL; 533 534 return count; 535} 536 537static const struct proc_ops salinfo_data_proc_ops = { 538 .proc_open = salinfo_log_open, 539 .proc_release = salinfo_log_release, 540 .proc_read = salinfo_log_read, 541 .proc_write = salinfo_log_write, 542 .proc_lseek = default_llseek, 543}; 544 545static int salinfo_cpu_online(unsigned int cpu) 546{ 547 unsigned int i, end = ARRAY_SIZE(salinfo_data); 548 struct salinfo_data *data; 549 550 spin_lock_irq(&data_saved_lock); 551 for (i = 0, data = salinfo_data; i < end; ++i, ++data) { 552 cpumask_set_cpu(cpu, &data->cpu_event); 553 wake_up_interruptible(&data->read_wait); 554 } 555 spin_unlock_irq(&data_saved_lock); 556 return 0; 557} 558 559static int salinfo_cpu_pre_down(unsigned int cpu) 560{ 561 unsigned int i, end = ARRAY_SIZE(salinfo_data); 562 struct salinfo_data *data; 563 564 spin_lock_irq(&data_saved_lock); 565 for (i = 0, data = salinfo_data; i < end; ++i, ++data) { 566 struct salinfo_data_saved *data_saved; 567 int j = ARRAY_SIZE(data->data_saved) - 1; 568 569 for (data_saved = data->data_saved + j; j >= 0; 570 --j, --data_saved) { 571 if (data_saved->buffer && data_saved->cpu == cpu) 572 shift1_data_saved(data, j); 573 } 574 cpumask_clear_cpu(cpu, &data->cpu_event); 575 } 576 spin_unlock_irq(&data_saved_lock); 577 return 0; 578} 579 580/* 581 * 'data' contains an integer that corresponds to the feature we're 582 * testing 583 */ 584static int proc_salinfo_show(struct seq_file *m, void *v) 585{ 586 unsigned long data = (unsigned long)v; 587 seq_puts(m, (sal_platform_features & data) ? "1\n" : "0\n"); 588 return 0; 589} 590 591static int __init 592salinfo_init(void) 593{ 594 struct proc_dir_entry *salinfo_dir; /* /proc/sal dir entry */ 595 struct proc_dir_entry **sdir = salinfo_proc_entries; /* keeps track of every entry */ 596 struct proc_dir_entry *dir, *entry; 597 struct salinfo_data *data; 598 int i; 599 600 salinfo_dir = proc_mkdir("sal", NULL); 601 if (!salinfo_dir) 602 return 0; 603 604 for (i=0; i < NR_SALINFO_ENTRIES; i++) { 605 /* pass the feature bit in question as misc data */ 606 *sdir++ = proc_create_single_data(salinfo_entries[i].name, 0, 607 salinfo_dir, proc_salinfo_show, 608 (void *)salinfo_entries[i].feature); 609 } 610 611 for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) { 612 data = salinfo_data + i; 613 data->type = i; 614 init_waitqueue_head(&data->read_wait); 615 dir = proc_mkdir(salinfo_log_name[i], salinfo_dir); 616 if (!dir) 617 continue; 618 619 entry = proc_create_data("event", S_IRUSR, dir, 620 &salinfo_event_proc_ops, data); 621 if (!entry) 622 continue; 623 *sdir++ = entry; 624 625 entry = proc_create_data("data", S_IRUSR | S_IWUSR, dir, 626 &salinfo_data_proc_ops, data); 627 if (!entry) 628 continue; 629 *sdir++ = entry; 630 631 *sdir++ = dir; 632 } 633 634 *sdir++ = salinfo_dir; 635 636 timer_setup(&salinfo_timer, salinfo_timeout, 0); 637 salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY; 638 add_timer(&salinfo_timer); 639 640 i = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ia64/salinfo:online", 641 salinfo_cpu_online, salinfo_cpu_pre_down); 642 WARN_ON(i < 0); 643 return 0; 644} 645 646module_init(salinfo_init);