2019-05-19 13:08:20 +01:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-16 15:20:36 -07:00
/*
* salinfo . c
*
* Creates entries in / proc / sal for various system features .
*
2006-01-06 10:36:06 +11:00
* Copyright ( c ) 2003 , 2006 Silicon Graphics , Inc . All rights reserved .
2005-04-16 15:20:36 -07:00
* Copyright ( c ) 2003 Hewlett - Packard Co
* Bjorn Helgaas < bjorn . helgaas @ hp . com >
*
* 10 / 30 / 2001 jbarnes @ sgi . com copied much of Stephane ' s palinfo
* code to create this file
* Oct 23 2003 kaos @ sgi . com
* Replace IPI with set_cpus_allowed ( ) to read a record from the required cpu .
* Redesign salinfo log processing to separate interrupt and user space
* contexts .
* Cache the record across multi - block reads from user space .
* Support > 64 cpus .
* Delete module_exit and MOD_INC / DEC_COUNT , salinfo cannot be a module .
*
* Jan 28 2004 kaos @ sgi . com
* Periodically check for outstanding MCA or INIT records .
*
* Dec 5 2004 kaos @ sgi . com
* Standardize which records are cleared automatically .
2005-09-11 17:21:46 +10:00
*
* Aug 18 2005 kaos @ sgi . com
* mca . c may not pass a buffer , a NULL buffer just indicates that a new
* record is available in SAL .
* Replace some NR_CPUS by cpus_online , for hotplug cpu .
2006-01-06 10:36:06 +11:00
*
* Jan 5 2006 kaos @ sgi . com
* Handle hotplug cpus coming online .
* Handle hotplug cpus going offline while they still have outstanding records .
* Use the cpu_ * macros consistently .
* Replace the counting semaphore with a mutex and a test if the cpumask is non - empty .
* Modify the locking to make the test for " work to do " an atomic operation .
2005-04-16 15:20:36 -07:00
*/
2006-01-11 12:17:48 -08:00
# include <linux/capability.h>
2006-01-06 10:36:06 +11:00
# include <linux/cpu.h>
2005-04-16 15:20:36 -07:00
# include <linux/types.h>
# include <linux/proc_fs.h>
2013-04-11 01:28:40 +01:00
# include <linux/seq_file.h>
2005-04-16 15:20:36 -07:00
# include <linux/module.h>
# include <linux/smp.h>
# include <linux/timer.h>
# include <linux/vmalloc.h>
2008-04-18 22:21:05 -04:00
# include <linux/semaphore.h>
2005-04-16 15:20:36 -07:00
# include <asm/sal.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2005-04-16 15:20:36 -07:00
MODULE_AUTHOR ( " Jesse Barnes <jbarnes@sgi.com> " ) ;
MODULE_DESCRIPTION ( " /proc interface to IA-64 SAL features " ) ;
MODULE_LICENSE ( " GPL " ) ;
typedef struct {
const char * name ; /* name of the proc entry */
unsigned long feature ; /* feature bit */
struct proc_dir_entry * entry ; /* registered entry (removal) */
} salinfo_entry_t ;
/*
* List { name , feature } pairs for every entry in / proc / sal / < feature >
* that this module exports
*/
2013-04-11 01:28:40 +01:00
static const salinfo_entry_t salinfo_entries [ ] = {
2005-04-16 15:20:36 -07:00
{ " bus_lock " , IA64_SAL_PLATFORM_FEATURE_BUS_LOCK , } ,
{ " irq_redirection " , IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT , } ,
{ " ipi_redirection " , IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT , } ,
{ " itc_drift " , IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT , } ,
} ;
# define NR_SALINFO_ENTRIES ARRAY_SIZE(salinfo_entries)
static char * salinfo_log_name [ ] = {
" mca " ,
" init " ,
" cmc " ,
" cpe " ,
} ;
static struct proc_dir_entry * salinfo_proc_entries [
ARRAY_SIZE ( salinfo_entries ) + /* /proc/sal/bus_lock */
ARRAY_SIZE ( salinfo_log_name ) + /* /proc/sal/{mca,...} */
( 2 * ARRAY_SIZE ( salinfo_log_name ) ) + /* /proc/sal/mca/{event,data} */
1 ] ; /* /proc/sal */
/* Some records we get ourselves, some are accessed as saved data in buffers
* that are owned by mca . c .
*/
struct salinfo_data_saved {
u8 * buffer ;
u64 size ;
u64 id ;
int cpu ;
} ;
/* State transitions. Actions are :-
* Write " read <cpunum> " to the data file .
* Write " clear <cpunum> " to the data file .
* Write " oemdata <cpunum> <offset> to the data file.
* Read from the data file .
* Close the data file .
*
* Start state is NO_DATA .
*
* NO_DATA
* write " read <cpunum> " - > NO_DATA or LOG_RECORD .
* write " clear <cpunum> " - > NO_DATA or LOG_RECORD .
* write " oemdata <cpunum> <offset> -> return -EINVAL.
* read data - > return EOF .
* close - > unchanged . Free record areas .
*
* LOG_RECORD
* write " read <cpunum> " - > NO_DATA or LOG_RECORD .
* write " clear <cpunum> " - > NO_DATA or LOG_RECORD .
* write " oemdata <cpunum> <offset> -> format the oem data, goto OEMDATA.
* read data - > return the INIT / MCA / CMC / CPE record .
* close - > unchanged . Keep record areas .
*
* OEMDATA
* write " read <cpunum> " - > NO_DATA or LOG_RECORD .
* write " clear <cpunum> " - > NO_DATA or LOG_RECORD .
* write " oemdata <cpunum> <offset> -> format the oem data, goto OEMDATA.
* read data - > return the formatted oemdata .
* close - > unchanged . Keep record areas .
*
* Closing the data file does not change the state . This allows shell scripts
* to manipulate salinfo data , each shell redirection opens the file , does one
* action then closes it again . The record areas are only freed at close when
* the state is NO_DATA .
*/
enum salinfo_state {
STATE_NO_DATA ,
STATE_LOG_RECORD ,
STATE_OEMDATA ,
} ;
struct salinfo_data {
2006-01-06 10:36:06 +11:00
cpumask_t cpu_event ; /* which cpus have outstanding events */
2016-07-25 10:43:13 +02:00
wait_queue_head_t read_wait ;
2005-04-16 15:20:36 -07:00
u8 * log_buffer ;
u64 log_size ;
u8 * oemdata ; /* decoded oem data */
u64 oemdata_size ;
int open ; /* single-open to prevent races */
u8 type ;
u8 saved_num ; /* using a saved record? */
enum salinfo_state state : 8 ; /* processing state */
u8 padding ;
int cpu_check ; /* next CPU to check */
struct salinfo_data_saved data_saved [ 5 ] ; /* save last 5 records from mca.c, must be < 255 */
} ;
static struct salinfo_data salinfo_data [ ARRAY_SIZE ( salinfo_log_name ) ] ;
2005-07-30 17:52:00 -07:00
static DEFINE_SPINLOCK ( data_lock ) ;
static DEFINE_SPINLOCK ( data_saved_lock ) ;
2005-04-16 15:20:36 -07:00
/** salinfo_platform_oemdata - optional callback to decode oemdata from an error
* record .
* @ sect_header : pointer to the start of the section to decode .
2007-05-11 14:55:43 -07:00
* @ oemdata : returns vmalloc area containing the decoded output .
2005-04-16 15:20:36 -07:00
* @ oemdata_size : returns length of decoded output ( strlen ) .
*
* Description : If user space asks for oem data to be decoded by the kernel
* and / or prom and the platform has set salinfo_platform_oemdata to the address
* of a platform specific routine then call that routine . salinfo_platform_oemdata
* vmalloc ' s and formats its output area , returning the address of the text
* and its strlen . Returns 0 for success , - ve for error . The callback is
* invoked on the cpu that generated the error record .
*/
int ( * salinfo_platform_oemdata ) ( const u8 * sect_header , u8 * * oemdata , u64 * oemdata_size ) ;
struct salinfo_platform_oemdata_parms {
const u8 * efi_guid ;
u8 * * oemdata ;
u64 * oemdata_size ;
} ;
2017-04-12 22:07:29 +02:00
static long
2005-04-16 15:20:36 -07:00
salinfo_platform_oemdata_cpu ( void * context )
{
struct salinfo_platform_oemdata_parms * parms = context ;
2017-04-12 22:07:29 +02:00
return salinfo_platform_oemdata ( parms - > efi_guid , parms - > oemdata , parms - > oemdata_size ) ;
2005-04-16 15:20:36 -07:00
}
static void
shift1_data_saved ( struct salinfo_data * data , int shift )
{
memcpy ( data - > data_saved + shift , data - > data_saved + shift + 1 ,
( ARRAY_SIZE ( data - > data_saved ) - ( shift + 1 ) ) * sizeof ( data - > data_saved [ 0 ] ) ) ;
memset ( data - > data_saved + ARRAY_SIZE ( data - > data_saved ) - 1 , 0 ,
sizeof ( data - > data_saved [ 0 ] ) ) ;
}
/* This routine is invoked in interrupt context. Note: mca.c enables
* interrupts before calling this code for CMC / CPE . MCA and INIT events are
* not irq safe , do not call any routines that use spinlocks , they may deadlock .
* MCA and INIT records are recorded , a timer event will look for any
* outstanding events and wake up the user space code .
*
* The buffer passed from mca . c points to the output from ia64_log_get . This is
* a persistent buffer but its contents can change between the interrupt and
* when user space processes the record . Save the record id to identify
2005-09-11 17:21:46 +10:00
* changes . If the buffer is NULL then just update the bitmap .
2005-04-16 15:20:36 -07:00
*/
void
salinfo_log_wakeup ( int type , u8 * buffer , u64 size , int irqsafe )
{
struct salinfo_data * data = salinfo_data + type ;
struct salinfo_data_saved * data_saved ;
unsigned long flags = 0 ;
int i ;
int saved_size = ARRAY_SIZE ( data - > data_saved ) ;
BUG_ON ( type > = ARRAY_SIZE ( salinfo_log_name ) ) ;
2006-01-06 10:36:06 +11:00
if ( irqsafe )
spin_lock_irqsave ( & data_saved_lock , flags ) ;
2005-09-11 17:21:46 +10:00
if ( buffer ) {
for ( i = 0 , data_saved = data - > data_saved ; i < saved_size ; + + i , + + data_saved ) {
if ( ! data_saved - > buffer )
break ;
}
if ( i = = saved_size ) {
if ( ! data - > saved_num ) {
shift1_data_saved ( data , 0 ) ;
data_saved = data - > data_saved + saved_size - 1 ;
} else
data_saved = NULL ;
}
if ( data_saved ) {
data_saved - > cpu = smp_processor_id ( ) ;
data_saved - > id = ( ( sal_log_record_header_t * ) buffer ) - > id ;
data_saved - > size = size ;
data_saved - > buffer = buffer ;
}
2005-04-16 15:20:36 -07:00
}
2015-03-05 10:49:16 +10:30
cpumask_set_cpu ( smp_processor_id ( ) , & data - > cpu_event ) ;
2006-01-06 10:36:06 +11:00
if ( irqsafe ) {
2016-07-25 10:43:13 +02:00
wake_up_interruptible ( & data - > read_wait ) ;
2006-01-06 10:36:06 +11:00
spin_unlock_irqrestore ( & data_saved_lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
}
/* Check for outstanding MCA/INIT records every minute (arbitrary) */
# define SALINFO_TIMER_DELAY (60*HZ)
static struct timer_list salinfo_timer ;
2006-09-26 14:44:37 -07:00
extern void ia64_mlogbuf_dump ( void ) ;
2005-04-16 15:20:36 -07:00
static void
salinfo_timeout_check ( struct salinfo_data * data )
{
if ( ! data - > open )
return ;
2016-07-25 10:43:13 +02:00
if ( ! cpumask_empty ( & data - > cpu_event ) )
wake_up_interruptible ( & data - > read_wait ) ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 10:36:06 +11:00
static void
2017-10-19 14:24:01 -07:00
salinfo_timeout ( struct timer_list * unused )
2005-04-16 15:20:36 -07:00
{
2006-09-26 14:44:37 -07:00
ia64_mlogbuf_dump ( ) ;
2005-04-16 15:20:36 -07:00
salinfo_timeout_check ( salinfo_data + SAL_INFO_TYPE_MCA ) ;
salinfo_timeout_check ( salinfo_data + SAL_INFO_TYPE_INIT ) ;
salinfo_timer . expires = jiffies + SALINFO_TIMER_DELAY ;
add_timer ( & salinfo_timer ) ;
}
static int
salinfo_event_open ( struct inode * inode , struct file * file )
{
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
return 0 ;
}
static ssize_t
salinfo_event_read ( struct file * file , char __user * buffer , size_t count , loff_t * ppos )
{
2013-03-31 18:16:14 -04:00
struct salinfo_data * data = PDE_DATA ( file_inode ( file ) ) ;
2005-04-16 15:20:36 -07:00
char cmd [ 32 ] ;
size_t size ;
int i , n , cpu = - 1 ;
retry :
2016-07-25 10:43:13 +02:00
if ( cpumask_empty ( & data - > cpu_event ) ) {
2005-04-16 15:20:36 -07:00
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
2016-07-25 10:43:13 +02:00
if ( wait_event_interruptible ( data - > read_wait ,
! cpumask_empty ( & data - > cpu_event ) ) )
2005-12-02 13:40:15 +11:00
return - EINTR ;
2005-04-16 15:20:36 -07:00
}
n = data - > cpu_check ;
2009-03-16 14:12:42 +10:30
for ( i = 0 ; i < nr_cpu_ids ; i + + ) {
2015-03-05 10:49:16 +10:30
if ( cpumask_test_cpu ( n , & data - > cpu_event ) ) {
2006-01-06 10:36:06 +11:00
if ( ! cpu_online ( n ) ) {
2015-03-05 10:49:16 +10:30
cpumask_clear_cpu ( n , & data - > cpu_event ) ;
2006-01-06 10:36:06 +11:00
continue ;
}
2005-04-16 15:20:36 -07:00
cpu = n ;
break ;
}
2009-03-16 14:12:42 +10:30
if ( + + n = = nr_cpu_ids )
2005-04-16 15:20:36 -07:00
n = 0 ;
}
if ( cpu = = - 1 )
goto retry ;
2006-09-26 14:44:37 -07:00
ia64_mlogbuf_dump ( ) ;
2005-04-16 15:20:36 -07:00
/* for next read, start checking at next CPU */
data - > cpu_check = cpu ;
2009-03-16 14:12:42 +10:30
if ( + + data - > cpu_check = = nr_cpu_ids )
2005-04-16 15:20:36 -07:00
data - > cpu_check = 0 ;
snprintf ( cmd , sizeof ( cmd ) , " read %d \n " , cpu ) ;
size = strlen ( cmd ) ;
if ( size > count )
size = count ;
if ( copy_to_user ( buffer , cmd , size ) )
return - EFAULT ;
return size ;
}
2020-02-03 17:37:17 -08:00
static const struct proc_ops salinfo_event_proc_ops = {
. proc_open = salinfo_event_open ,
. proc_read = salinfo_event_read ,
. proc_lseek = noop_llseek ,
2005-04-16 15:20:36 -07:00
} ;
static int
salinfo_log_open ( struct inode * inode , struct file * file )
{
2013-03-31 18:16:14 -04:00
struct salinfo_data * data = PDE_DATA ( inode ) ;
2005-04-16 15:20:36 -07:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
spin_lock ( & data_lock ) ;
if ( data - > open ) {
spin_unlock ( & data_lock ) ;
return - EBUSY ;
}
data - > open = 1 ;
spin_unlock ( & data_lock ) ;
if ( data - > state = = STATE_NO_DATA & &
! ( data - > log_buffer = vmalloc ( ia64_sal_get_state_info_size ( data - > type ) ) ) ) {
data - > open = 0 ;
return - ENOMEM ;
}
return 0 ;
}
static int
salinfo_log_release ( struct inode * inode , struct file * file )
{
2013-03-31 18:16:14 -04:00
struct salinfo_data * data = PDE_DATA ( inode ) ;
2005-04-16 15:20:36 -07:00
if ( data - > state = = STATE_NO_DATA ) {
vfree ( data - > log_buffer ) ;
vfree ( data - > oemdata ) ;
data - > log_buffer = NULL ;
data - > oemdata = NULL ;
}
spin_lock ( & data_lock ) ;
data - > open = 0 ;
spin_unlock ( & data_lock ) ;
return 0 ;
}
2017-04-12 22:07:29 +02:00
static long
2005-04-16 15:20:36 -07:00
salinfo_log_read_cpu ( void * context )
{
struct salinfo_data * data = context ;
sal_log_record_header_t * rh ;
data - > log_size = ia64_sal_get_state_info ( data - > type , ( u64 * ) data - > log_buffer ) ;
rh = ( sal_log_record_header_t * ) ( data - > log_buffer ) ;
/* Clear corrected errors as they are read from SAL */
if ( rh - > severity = = sal_log_severity_corrected )
ia64_sal_clear_state_info ( data - > type ) ;
2017-04-12 22:07:29 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static void
salinfo_log_new_read ( int cpu , struct salinfo_data * data )
{
struct salinfo_data_saved * data_saved ;
unsigned long flags ;
int i ;
int saved_size = ARRAY_SIZE ( data - > data_saved ) ;
data - > saved_num = 0 ;
spin_lock_irqsave ( & data_saved_lock , flags ) ;
retry :
for ( i = 0 , data_saved = data - > data_saved ; i < saved_size ; + + i , + + data_saved ) {
if ( data_saved - > buffer & & data_saved - > cpu = = cpu ) {
sal_log_record_header_t * rh = ( sal_log_record_header_t * ) ( data_saved - > buffer ) ;
data - > log_size = data_saved - > size ;
memcpy ( data - > log_buffer , rh , data - > log_size ) ;
barrier ( ) ; /* id check must not be moved */
if ( rh - > id = = data_saved - > id ) {
data - > saved_num = i + 1 ;
break ;
}
/* saved record changed by mca.c since interrupt, discard it */
shift1_data_saved ( data , i ) ;
goto retry ;
}
}
spin_unlock_irqrestore ( & data_saved_lock , flags ) ;
if ( ! data - > saved_num )
2017-04-12 22:07:29 +02:00
work_on_cpu_safe ( cpu , salinfo_log_read_cpu , data ) ;
2005-04-16 15:20:36 -07:00
if ( ! data - > log_size ) {
2006-01-06 10:36:06 +11:00
data - > state = STATE_NO_DATA ;
2015-03-05 10:49:16 +10:30
cpumask_clear_cpu ( cpu , & data - > cpu_event ) ;
2005-04-16 15:20:36 -07:00
} else {
2006-01-06 10:36:06 +11:00
data - > state = STATE_LOG_RECORD ;
2005-04-16 15:20:36 -07:00
}
}
static ssize_t
salinfo_log_read ( struct file * file , char __user * buffer , size_t count , loff_t * ppos )
{
2013-03-31 18:16:14 -04:00
struct salinfo_data * data = PDE_DATA ( file_inode ( file ) ) ;
2005-04-16 15:20:36 -07:00
u8 * buf ;
u64 bufsize ;
if ( data - > state = = STATE_LOG_RECORD ) {
buf = data - > log_buffer ;
bufsize = data - > log_size ;
} else if ( data - > state = = STATE_OEMDATA ) {
buf = data - > oemdata ;
bufsize = data - > oemdata_size ;
} else {
buf = NULL ;
bufsize = 0 ;
}
return simple_read_from_buffer ( buffer , count , ppos , buf , bufsize ) ;
}
2017-04-12 22:07:29 +02:00
static long
2005-04-16 15:20:36 -07:00
salinfo_log_clear_cpu ( void * context )
{
struct salinfo_data * data = context ;
2017-04-12 22:07:29 +02:00
2005-04-16 15:20:36 -07:00
ia64_sal_clear_state_info ( data - > type ) ;
2017-04-12 22:07:29 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static int
salinfo_log_clear ( struct salinfo_data * data , int cpu )
{
sal_log_record_header_t * rh ;
2006-01-06 10:36:06 +11:00
unsigned long flags ;
spin_lock_irqsave ( & data_saved_lock , flags ) ;
2005-04-16 15:20:36 -07:00
data - > state = STATE_NO_DATA ;
2015-03-05 10:49:16 +10:30
if ( ! cpumask_test_cpu ( cpu , & data - > cpu_event ) ) {
2006-01-06 10:36:06 +11:00
spin_unlock_irqrestore ( & data_saved_lock , flags ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
2006-01-06 10:36:06 +11:00
}
2015-03-05 10:49:16 +10:30
cpumask_clear_cpu ( cpu , & data - > cpu_event ) ;
2005-04-16 15:20:36 -07:00
if ( data - > saved_num ) {
2006-01-06 10:36:06 +11:00
shift1_data_saved ( data , data - > saved_num - 1 ) ;
2005-04-16 15:20:36 -07:00
data - > saved_num = 0 ;
}
2006-01-06 10:36:06 +11:00
spin_unlock_irqrestore ( & data_saved_lock , flags ) ;
2005-04-16 15:20:36 -07:00
rh = ( sal_log_record_header_t * ) ( data - > log_buffer ) ;
/* Corrected errors have already been cleared from SAL */
if ( rh - > severity ! = sal_log_severity_corrected )
2017-04-12 22:07:29 +02:00
work_on_cpu_safe ( cpu , salinfo_log_clear_cpu , data ) ;
2005-04-16 15:20:36 -07:00
/* clearing a record may make a new record visible */
salinfo_log_new_read ( cpu , data ) ;
2006-01-06 10:36:06 +11:00
if ( data - > state = = STATE_LOG_RECORD ) {
spin_lock_irqsave ( & data_saved_lock , flags ) ;
2015-03-05 10:49:16 +10:30
cpumask_set_cpu ( cpu , & data - > cpu_event ) ;
2016-07-25 10:43:13 +02:00
wake_up_interruptible ( & data - > read_wait ) ;
2006-01-06 10:36:06 +11:00
spin_unlock_irqrestore ( & data_saved_lock , flags ) ;
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t
salinfo_log_write ( struct file * file , const char __user * buffer , size_t count , loff_t * ppos )
{
2013-03-31 18:16:14 -04:00
struct salinfo_data * data = PDE_DATA ( file_inode ( file ) ) ;
2005-04-16 15:20:36 -07:00
char cmd [ 32 ] ;
size_t size ;
u32 offset ;
int cpu ;
size = sizeof ( cmd ) ;
if ( count < size )
size = count ;
if ( copy_from_user ( cmd , buffer , size ) )
return - EFAULT ;
if ( sscanf ( cmd , " read %d " , & cpu ) = = 1 ) {
salinfo_log_new_read ( cpu , data ) ;
} else if ( sscanf ( cmd , " clear %d " , & cpu ) = = 1 ) {
int ret ;
if ( ( ret = salinfo_log_clear ( data , cpu ) ) )
count = ret ;
} else if ( sscanf ( cmd , " oemdata %d %d " , & cpu , & offset ) = = 2 ) {
if ( data - > state ! = STATE_LOG_RECORD & & data - > state ! = STATE_OEMDATA )
return - EINVAL ;
if ( offset > data - > log_size - sizeof ( efi_guid_t ) )
return - EINVAL ;
data - > state = STATE_OEMDATA ;
if ( salinfo_platform_oemdata ) {
struct salinfo_platform_oemdata_parms parms = {
. efi_guid = data - > log_buffer + offset ,
. oemdata = & data - > oemdata ,
. oemdata_size = & data - > oemdata_size
} ;
2017-04-12 22:07:29 +02:00
count = work_on_cpu_safe ( cpu , salinfo_platform_oemdata_cpu ,
& parms ) ;
2005-04-16 15:20:36 -07:00
} else
data - > oemdata_size = 0 ;
} else
return - EINVAL ;
return count ;
}
2020-02-03 17:37:17 -08:00
static const struct proc_ops salinfo_data_proc_ops = {
. proc_open = salinfo_log_open ,
. proc_release = salinfo_log_release ,
. proc_read = salinfo_log_read ,
. proc_write = salinfo_log_write ,
. proc_lseek = default_llseek ,
2005-04-16 15:20:36 -07:00
} ;
2016-11-03 18:31:29 +01:00
static int salinfo_cpu_online ( unsigned int cpu )
2006-01-06 10:36:06 +11:00
{
2016-11-03 18:31:29 +01:00
unsigned int i , end = ARRAY_SIZE ( salinfo_data ) ;
2006-01-06 10:36:06 +11:00
struct salinfo_data * data ;
2016-11-03 18:31:29 +01:00
spin_lock_irq ( & data_saved_lock ) ;
for ( i = 0 , data = salinfo_data ; i < end ; + + i , + + data ) {
cpumask_set_cpu ( cpu , & data - > cpu_event ) ;
wake_up_interruptible ( & data - > read_wait ) ;
2006-01-06 10:36:06 +11:00
}
2016-11-03 18:31:29 +01:00
spin_unlock_irq ( & data_saved_lock ) ;
return 0 ;
2006-01-06 10:36:06 +11:00
}
2016-11-03 18:31:29 +01:00
static int salinfo_cpu_pre_down ( unsigned int cpu )
2006-01-06 10:36:06 +11:00
{
2016-11-03 18:31:29 +01:00
unsigned int i , end = ARRAY_SIZE ( salinfo_data ) ;
struct salinfo_data * data ;
spin_lock_irq ( & data_saved_lock ) ;
for ( i = 0 , data = salinfo_data ; i < end ; + + i , + + data ) {
struct salinfo_data_saved * data_saved ;
int j = ARRAY_SIZE ( data - > data_saved ) - 1 ;
for ( data_saved = data - > data_saved + j ; j > = 0 ;
- - j , - - data_saved ) {
if ( data_saved - > buffer & & data_saved - > cpu = = cpu )
shift1_data_saved ( data , j ) ;
}
cpumask_clear_cpu ( cpu , & data - > cpu_event ) ;
}
spin_unlock_irq ( & data_saved_lock ) ;
return 0 ;
}
2006-01-06 10:36:06 +11:00
2018-05-15 15:57:23 +02:00
/*
* ' data ' contains an integer that corresponds to the feature we ' re
* testing
*/
static int proc_salinfo_show ( struct seq_file * m , void * v )
{
unsigned long data = ( unsigned long ) v ;
seq_puts ( m , ( sal_platform_features & data ) ? " 1 \n " : " 0 \n " ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
static int __init
salinfo_init ( void )
{
struct proc_dir_entry * salinfo_dir ; /* /proc/sal dir entry */
struct proc_dir_entry * * sdir = salinfo_proc_entries ; /* keeps track of every entry */
struct proc_dir_entry * dir , * entry ;
struct salinfo_data * data ;
2016-11-03 18:31:29 +01:00
int i ;
2005-04-16 15:20:36 -07:00
salinfo_dir = proc_mkdir ( " sal " , NULL ) ;
if ( ! salinfo_dir )
return 0 ;
for ( i = 0 ; i < NR_SALINFO_ENTRIES ; i + + ) {
/* pass the feature bit in question as misc data */
2018-05-15 15:57:23 +02:00
* sdir + + = proc_create_single_data ( salinfo_entries [ i ] . name , 0 ,
salinfo_dir , proc_salinfo_show ,
( void * ) salinfo_entries [ i ] . feature ) ;
2005-04-16 15:20:36 -07:00
}
for ( i = 0 ; i < ARRAY_SIZE ( salinfo_log_name ) ; i + + ) {
data = salinfo_data + i ;
data - > type = i ;
2016-07-25 10:43:13 +02:00
init_waitqueue_head ( & data - > read_wait ) ;
2005-04-16 15:20:36 -07:00
dir = proc_mkdir ( salinfo_log_name [ i ] , salinfo_dir ) ;
if ( ! dir )
continue ;
2008-04-29 01:02:25 -07:00
entry = proc_create_data ( " event " , S_IRUSR , dir ,
2020-02-03 17:37:17 -08:00
& salinfo_event_proc_ops , data ) ;
2005-04-16 15:20:36 -07:00
if ( ! entry )
continue ;
* sdir + + = entry ;
2008-04-29 01:02:25 -07:00
entry = proc_create_data ( " data " , S_IRUSR | S_IWUSR , dir ,
2020-02-03 17:37:17 -08:00
& salinfo_data_proc_ops , data ) ;
2005-04-16 15:20:36 -07:00
if ( ! entry )
continue ;
* sdir + + = entry ;
* sdir + + = dir ;
}
* sdir + + = salinfo_dir ;
2017-10-19 14:24:01 -07:00
timer_setup ( & salinfo_timer , salinfo_timeout , 0 ) ;
2005-04-16 15:20:36 -07:00
salinfo_timer . expires = jiffies + SALINFO_TIMER_DELAY ;
add_timer ( & salinfo_timer ) ;
2016-11-03 18:31:29 +01:00
i = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN , " ia64/salinfo:online " ,
salinfo_cpu_online , salinfo_cpu_pre_down ) ;
WARN_ON ( i < 0 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
module_init ( salinfo_init ) ;