linux/arch/s390/kernel/lgr.c
<<
>>
Prefs
   1/*
   2 * Linux Guest Relocation (LGR) detection
   3 *
   4 * Copyright IBM Corp. 2012
   5 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/timer.h>
  10#include <linux/slab.h>
  11#include <asm/facility.h>
  12#include <asm/sysinfo.h>
  13#include <asm/ebcdic.h>
  14#include <asm/debug.h>
  15#include <asm/ipl.h>
  16
  17#define LGR_TIMER_INTERVAL_SECS (30 * 60)
  18#define VM_LEVEL_MAX 2 /* Maximum is 8, but we only record two levels */
  19
  20/*
  21 * LGR info: Contains stfle and stsi data
  22 */
  23struct lgr_info {
  24        /* Bit field with facility information: 4 DWORDs are stored */
  25        u64 stfle_fac_list[4];
  26        /* Level of system (1 = CEC, 2 = LPAR, 3 = z/VM */
  27        u32 level;
  28        /* Level 1: CEC info (stsi 1.1.1) */
  29        char manufacturer[16];
  30        char type[4];
  31        char sequence[16];
  32        char plant[4];
  33        char model[16];
  34        /* Level 2: LPAR info (stsi 2.2.2) */
  35        u16 lpar_number;
  36        char name[8];
  37        /* Level 3: VM info (stsi 3.2.2) */
  38        u8 vm_count;
  39        struct {
  40                char name[8];
  41                char cpi[16];
  42        } vm[VM_LEVEL_MAX];
  43} __packed __aligned(8);
  44
  45/*
  46 * LGR globals
  47 */
  48static char lgr_page[PAGE_SIZE] __aligned(PAGE_SIZE);
  49static struct lgr_info lgr_info_last;
  50static struct lgr_info lgr_info_cur;
  51static struct debug_info *lgr_dbf;
  52
  53/*
  54 * Copy buffer and then convert it to ASCII
  55 */
  56static void cpascii(char *dst, char *src, int size)
  57{
  58        memcpy(dst, src, size);
  59        EBCASC(dst, size);
  60}
  61
  62/*
  63 * Fill LGR info with 1.1.1 stsi data
  64 */
  65static void lgr_stsi_1_1_1(struct lgr_info *lgr_info)
  66{
  67        struct sysinfo_1_1_1 *si = (void *) lgr_page;
  68
  69        if (stsi(si, 1, 1, 1))
  70                return;
  71        cpascii(lgr_info->manufacturer, si->manufacturer,
  72                sizeof(si->manufacturer));
  73        cpascii(lgr_info->type, si->type, sizeof(si->type));
  74        cpascii(lgr_info->model, si->model, sizeof(si->model));
  75        cpascii(lgr_info->sequence, si->sequence, sizeof(si->sequence));
  76        cpascii(lgr_info->plant, si->plant, sizeof(si->plant));
  77}
  78
  79/*
  80 * Fill LGR info with 2.2.2 stsi data
  81 */
  82static void lgr_stsi_2_2_2(struct lgr_info *lgr_info)
  83{
  84        struct sysinfo_2_2_2 *si = (void *) lgr_page;
  85
  86        if (stsi(si, 2, 2, 2))
  87                return;
  88        cpascii(lgr_info->name, si->name, sizeof(si->name));
  89        memcpy(&lgr_info->lpar_number, &si->lpar_number,
  90               sizeof(lgr_info->lpar_number));
  91}
  92
  93/*
  94 * Fill LGR info with 3.2.2 stsi data
  95 */
  96static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
  97{
  98        struct sysinfo_3_2_2 *si = (void *) lgr_page;
  99        int i;
 100
 101        if (stsi(si, 3, 2, 2))
 102                return;
 103        for (i = 0; i < min_t(u8, si->count, VM_LEVEL_MAX); i++) {
 104                cpascii(lgr_info->vm[i].name, si->vm[i].name,
 105                        sizeof(si->vm[i].name));
 106                cpascii(lgr_info->vm[i].cpi, si->vm[i].cpi,
 107                        sizeof(si->vm[i].cpi));
 108        }
 109        lgr_info->vm_count = si->count;
 110}
 111
 112/*
 113 * Fill LGR info with current data
 114 */
 115static void lgr_info_get(struct lgr_info *lgr_info)
 116{
 117        int level;
 118
 119        memset(lgr_info, 0, sizeof(*lgr_info));
 120        stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
 121        level = stsi(NULL, 0, 0, 0);
 122        lgr_info->level = level;
 123        if (level >= 1)
 124                lgr_stsi_1_1_1(lgr_info);
 125        if (level >= 2)
 126                lgr_stsi_2_2_2(lgr_info);
 127        if (level >= 3)
 128                lgr_stsi_3_2_2(lgr_info);
 129}
 130
 131/*
 132 * Check if LGR info has changed and if yes log new LGR info to s390dbf
 133 */
 134void lgr_info_log(void)
 135{
 136        static DEFINE_SPINLOCK(lgr_info_lock);
 137        unsigned long flags;
 138
 139        if (!spin_trylock_irqsave(&lgr_info_lock, flags))
 140                return;
 141        lgr_info_get(&lgr_info_cur);
 142        if (memcmp(&lgr_info_last, &lgr_info_cur, sizeof(lgr_info_cur)) != 0) {
 143                debug_event(lgr_dbf, 1, &lgr_info_cur, sizeof(lgr_info_cur));
 144                lgr_info_last = lgr_info_cur;
 145        }
 146        spin_unlock_irqrestore(&lgr_info_lock, flags);
 147}
 148EXPORT_SYMBOL_GPL(lgr_info_log);
 149
 150static void lgr_timer_set(void);
 151
 152/*
 153 * LGR timer callback
 154 */
 155static void lgr_timer_fn(unsigned long ignored)
 156{
 157        lgr_info_log();
 158        lgr_timer_set();
 159}
 160
 161static struct timer_list lgr_timer =
 162        TIMER_DEFERRED_INITIALIZER(lgr_timer_fn, 0, 0);
 163
 164/*
 165 * Setup next LGR timer
 166 */
 167static void lgr_timer_set(void)
 168{
 169        mod_timer(&lgr_timer, jiffies + LGR_TIMER_INTERVAL_SECS * HZ);
 170}
 171
 172/*
 173 * Initialize LGR: Add s390dbf, write initial lgr_info and setup timer
 174 */
 175static int __init lgr_init(void)
 176{
 177        lgr_dbf = debug_register("lgr", 1, 1, sizeof(struct lgr_info));
 178        if (!lgr_dbf)
 179                return -ENOMEM;
 180        debug_register_view(lgr_dbf, &debug_hex_ascii_view);
 181        lgr_info_get(&lgr_info_last);
 182        debug_event(lgr_dbf, 1, &lgr_info_last, sizeof(lgr_info_last));
 183        lgr_timer_set();
 184        return 0;
 185}
 186module_init(lgr_init);
 187