linux/arch/s390/kernel/lgr.c
Greg Kroah-Hartman a17ae4c3a6 s390: kernel: add SPDX identifiers to the remaining files
It's good to have SPDX identifiers in all files to make it easier to
audit the kernel tree for correct licenses.

Update the arch/s390/kernel/ files with the correct SPDX license
identifier based on the license text in the file itself.  The SPDX
identifier is a legally binding shorthand, which can be used instead of
the full boiler plate text.

This work is based on a script and data from Thomas Gleixner, Philippe
Ombredanne, and Kate Stewart.

Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Kate Stewart <kstewart@linuxfoundation.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2017-11-24 15:37:12 +01:00

189 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Linux Guest Relocation (LGR) detection
*
* Copyright IBM Corp. 2012
* Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
*/
#include <linux/init.h>
#include <linux/export.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <asm/facility.h>
#include <asm/sysinfo.h>
#include <asm/ebcdic.h>
#include <asm/debug.h>
#include <asm/ipl.h>
#define LGR_TIMER_INTERVAL_SECS (30 * 60)
#define VM_LEVEL_MAX 2 /* Maximum is 8, but we only record two levels */
/*
* LGR info: Contains stfle and stsi data
*/
struct lgr_info {
/* Bit field with facility information: 4 DWORDs are stored */
u64 stfle_fac_list[4];
/* Level of system (1 = CEC, 2 = LPAR, 3 = z/VM */
u32 level;
/* Level 1: CEC info (stsi 1.1.1) */
char manufacturer[16];
char type[4];
char sequence[16];
char plant[4];
char model[16];
/* Level 2: LPAR info (stsi 2.2.2) */
u16 lpar_number;
char name[8];
/* Level 3: VM info (stsi 3.2.2) */
u8 vm_count;
struct {
char name[8];
char cpi[16];
} vm[VM_LEVEL_MAX];
} __packed __aligned(8);
/*
* LGR globals
*/
static char lgr_page[PAGE_SIZE] __aligned(PAGE_SIZE);
static struct lgr_info lgr_info_last;
static struct lgr_info lgr_info_cur;
static struct debug_info *lgr_dbf;
/*
* Copy buffer and then convert it to ASCII
*/
static void cpascii(char *dst, char *src, int size)
{
memcpy(dst, src, size);
EBCASC(dst, size);
}
/*
* Fill LGR info with 1.1.1 stsi data
*/
static void lgr_stsi_1_1_1(struct lgr_info *lgr_info)
{
struct sysinfo_1_1_1 *si = (void *) lgr_page;
if (stsi(si, 1, 1, 1))
return;
cpascii(lgr_info->manufacturer, si->manufacturer,
sizeof(si->manufacturer));
cpascii(lgr_info->type, si->type, sizeof(si->type));
cpascii(lgr_info->model, si->model, sizeof(si->model));
cpascii(lgr_info->sequence, si->sequence, sizeof(si->sequence));
cpascii(lgr_info->plant, si->plant, sizeof(si->plant));
}
/*
* Fill LGR info with 2.2.2 stsi data
*/
static void lgr_stsi_2_2_2(struct lgr_info *lgr_info)
{
struct sysinfo_2_2_2 *si = (void *) lgr_page;
if (stsi(si, 2, 2, 2))
return;
cpascii(lgr_info->name, si->name, sizeof(si->name));
memcpy(&lgr_info->lpar_number, &si->lpar_number,
sizeof(lgr_info->lpar_number));
}
/*
* Fill LGR info with 3.2.2 stsi data
*/
static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
{
struct sysinfo_3_2_2 *si = (void *) lgr_page;
int i;
if (stsi(si, 3, 2, 2))
return;
for (i = 0; i < min_t(u8, si->count, VM_LEVEL_MAX); i++) {
cpascii(lgr_info->vm[i].name, si->vm[i].name,
sizeof(si->vm[i].name));
cpascii(lgr_info->vm[i].cpi, si->vm[i].cpi,
sizeof(si->vm[i].cpi));
}
lgr_info->vm_count = si->count;
}
/*
* Fill LGR info with current data
*/
static void lgr_info_get(struct lgr_info *lgr_info)
{
int level;
memset(lgr_info, 0, sizeof(*lgr_info));
stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
level = stsi(NULL, 0, 0, 0);
lgr_info->level = level;
if (level >= 1)
lgr_stsi_1_1_1(lgr_info);
if (level >= 2)
lgr_stsi_2_2_2(lgr_info);
if (level >= 3)
lgr_stsi_3_2_2(lgr_info);
}
/*
* Check if LGR info has changed and if yes log new LGR info to s390dbf
*/
void lgr_info_log(void)
{
static DEFINE_SPINLOCK(lgr_info_lock);
unsigned long flags;
if (!spin_trylock_irqsave(&lgr_info_lock, flags))
return;
lgr_info_get(&lgr_info_cur);
if (memcmp(&lgr_info_last, &lgr_info_cur, sizeof(lgr_info_cur)) != 0) {
debug_event(lgr_dbf, 1, &lgr_info_cur, sizeof(lgr_info_cur));
lgr_info_last = lgr_info_cur;
}
spin_unlock_irqrestore(&lgr_info_lock, flags);
}
EXPORT_SYMBOL_GPL(lgr_info_log);
static void lgr_timer_set(void);
/*
* LGR timer callback
*/
static void lgr_timer_fn(struct timer_list *unused)
{
lgr_info_log();
lgr_timer_set();
}
static struct timer_list lgr_timer;
/*
* Setup next LGR timer
*/
static void lgr_timer_set(void)
{
mod_timer(&lgr_timer, jiffies + LGR_TIMER_INTERVAL_SECS * HZ);
}
/*
* Initialize LGR: Add s390dbf, write initial lgr_info and setup timer
*/
static int __init lgr_init(void)
{
lgr_dbf = debug_register("lgr", 1, 1, sizeof(struct lgr_info));
if (!lgr_dbf)
return -ENOMEM;
debug_register_view(lgr_dbf, &debug_hex_ascii_view);
lgr_info_get(&lgr_info_last);
debug_event(lgr_dbf, 1, &lgr_info_last, sizeof(lgr_info_last));
timer_setup(&lgr_timer, lgr_timer_fn, TIMER_DEFERRABLE);
lgr_timer_set();
return 0;
}
device_initcall(lgr_init);