cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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);