void debug_set_level(debug_info_t* id, int new_level);
 
+void debug_set_critical(void);
 void debug_stop_all(void);
 
 static inline debug_entry_t*
 
 extern int diag308(unsigned long subcode, void *addr);
 extern void diag308_reset(void);
 extern void store_status(void);
+extern void lgr_info_log(void);
 
 #endif /* _ASM_S390_IPL_H */
 
 #ifndef __ASM_SYSTEM_H
 #define __ASM_SYSTEM_H
 
+#include <linux/preempt.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 #include <asm/types.h>
 #include <asm/ptrace.h>
 #include <asm/setup.h>
        return (*ptr & (0x80 >> (nr & 7))) != 0;
 }
 
+/**
+ * stfle - Store facility list extended
+ * @stfle_fac_list: array where facility list can be stored
+ * @size: size of passed in array in double words
+ */
+static inline void stfle(u64 *stfle_fac_list, int size)
+{
+       unsigned long nr;
+
+       preempt_disable();
+       S390_lowcore.stfl_fac_list = 0;
+       asm volatile(
+               "       .insn s,0xb2b10000,0(0)\n" /* stfl */
+               "0:\n"
+               EX_TABLE(0b, 0b)
+               : "=m" (S390_lowcore.stfl_fac_list));
+       nr = 4; /* bytes stored by stfl */
+       memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4);
+       if (S390_lowcore.stfl_fac_list & 0x01000000) {
+               /* More facility bits available with stfle */
+               register unsigned long reg0 asm("0") = size - 1;
+
+               asm volatile(".insn s,0xb2b00000,0(%1)" /* stfle */
+                            : "+d" (reg0)
+                            : "a" (stfle_fac_list)
+                            : "memory", "cc");
+               nr = (reg0 + 1) * 8; /* # bytes stored by stfle */
+       }
+       memset((char *) stfle_fac_list + nr, 0, size * 8 - nr);
+       preempt_enable();
+}
+
 static inline unsigned short stap(void)
 {
        unsigned short cpu_address;
 
 obj-y  :=  bitmap.o traps.o time.o process.o base.o early.o setup.o vtime.o \
            processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o \
            debug.o irq.o ipl.o dis.o diag.o mem_detect.o sclp.o vdso.o \
-           sysinfo.o jump_label.o
+           sysinfo.o jump_label.o lgr.o
 
 obj-y  += $(if $(CONFIG_64BIT),entry64.o,entry.o)
 obj-y  += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
 
  *  arch/s390/kernel/debug.c
  *   S/390 debug facility
  *
- *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
- *                             IBM Corporation
+ *    Copyright IBM Corp. 1999, 2012
+ *
  *    Author(s): Michael Holzheu (holzheu@de.ibm.com),
  *               Holger Smolinski (Holger.Smolinski@de.ibm.com)
  *
 static DEFINE_MUTEX(debug_mutex);
 
 static int initialized;
+static int debug_critical;
 
 static const struct file_operations debug_file_ops = {
        .owner   = THIS_MODULE,
 }
 
 
+void debug_set_critical(void)
+{
+       debug_critical = 1;
+}
+
 /*
  * debug_event_common:
  * - write debug entry with given size
 
        if (!debug_active || !id->areas)
                return NULL;
-       spin_lock_irqsave(&id->lock, flags);
+       if (debug_critical) {
+               if (!spin_trylock_irqsave(&id->lock, flags))
+                       return NULL;
+       } else
+               spin_lock_irqsave(&id->lock, flags);
        active = get_active_entry(id);
        memset(DEBUG_DATA(active), 0, id->buf_size);
        memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
 
        if (!debug_active || !id->areas)
                return NULL;
-       spin_lock_irqsave(&id->lock, flags);
+       if (debug_critical) {
+               if (!spin_trylock_irqsave(&id->lock, flags))
+                       return NULL;
+       } else
+               spin_lock_irqsave(&id->lock, flags);
        active = get_active_entry(id);
        memset(DEBUG_DATA(active), 0, id->buf_size);
        memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
                return NULL;
        numargs=debug_count_numargs(string);
 
-       spin_lock_irqsave(&id->lock, flags);
+       if (debug_critical) {
+               if (!spin_trylock_irqsave(&id->lock, flags))
+                       return NULL;
+       } else
+               spin_lock_irqsave(&id->lock, flags);
        active = get_active_entry(id);
        curr_event=(debug_sprintf_entry_t *) DEBUG_DATA(active);
        va_start(ap,string);
 
        numargs=debug_count_numargs(string);
 
-       spin_lock_irqsave(&id->lock, flags);
+       if (debug_critical) {
+               if (!spin_trylock_irqsave(&id->lock, flags))
+                       return NULL;
+       } else
+               spin_lock_irqsave(&id->lock, flags);
        active = get_active_entry(id);
        curr_event=(debug_sprintf_entry_t *)DEBUG_DATA(active);
        va_start(ap,string);
        rc += sprintf(out_buf + rc, "| ");
        for (i = 0; i < id->buf_size; i++) {
                unsigned char c = in_buf[i];
-               if (!isprint(c))
-                       rc += sprintf(out_buf + rc, ".");
-               else
+               if (isascii(c) && isprint(c))
                        rc += sprintf(out_buf + rc, "%c", c);
+               else
+                       rc += sprintf(out_buf + rc, ".");
        }
        rc += sprintf(out_buf + rc, "\n");
        return rc;
 
 #include <asm/sysinfo.h>
 #include <asm/cpcmd.h>
 #include <asm/sclp.h>
+#include <asm/system.h>
 #include "entry.h"
 
 /*
 
 static noinline __init void setup_facility_list(void)
 {
-       unsigned long nr;
-
-       S390_lowcore.stfl_fac_list = 0;
-       asm volatile(
-               "       .insn   s,0xb2b10000,0(0)\n" /* stfl */
-               "0:\n"
-               EX_TABLE(0b,0b) : "=m" (S390_lowcore.stfl_fac_list));
-       memcpy(&S390_lowcore.stfle_fac_list, &S390_lowcore.stfl_fac_list, 4);
-       nr = 4;                         /* # bytes stored by stfl */
-       if (test_facility(7)) {
-               /* More facility bits available with stfle */
-               register unsigned long reg0 asm("0") = MAX_FACILITY_BIT/64 - 1;
-               asm volatile(".insn s,0xb2b00000,%0" /* stfle */
-                            : "=m" (S390_lowcore.stfle_fac_list), "+d" (reg0)
-                            : : "cc");
-               nr = (reg0 + 1) * 8;    /* # bytes stored by stfle */
-       }
-       memset((char *) S390_lowcore.stfle_fac_list + nr, 0,
-              MAX_FACILITY_BIT/8 - nr);
+       stfle(S390_lowcore.stfle_fac_list,
+             ARRAY_SIZE(S390_lowcore.stfle_fac_list));
 }
 
 static noinline __init void setup_hpage(void)
 
 #include <linux/fs.h>
 #include <linux/gfp.h>
 #include <linux/crash_dump.h>
+#include <linux/debug_locks.h>
 #include <asm/ipl.h>
 #include <asm/smp.h>
 #include <asm/setup.h>
 #include <asm/reset.h>
 #include <asm/sclp.h>
 #include <asm/checksum.h>
+#include <asm/debug.h>
 #include "entry.h"
 
 #define IPL_PARM_BLOCK_VERSION 0
 
 static void do_panic(void)
 {
+       lgr_info_log();
        on_panic_trigger.action->fn(&on_panic_trigger);
        stop_run(&on_panic_trigger);
 }
 
 void do_restart(void)
 {
+       tracing_off();
+       debug_locks_off();
+       lgr_info_log();
        smp_call_online_cpu(__do_restart, NULL);
 }
 
 
--- /dev/null
+/*
+ * Linux Guest Relocation (LGR) detection
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <asm/sysinfo.h>
+#include <asm/ebcdic.h>
+#include <asm/system.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 void *lgr_page;
+static struct lgr_info lgr_info_last;
+static struct lgr_info lgr_info_cur;
+static struct debug_info *lgr_dbf;
+
+/*
+ * Return number of valid stsi levels
+ */
+static inline int stsi_0(void)
+{
+       int rc = stsi(NULL, 0, 0, 0);
+
+       return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
+}
+
+/*
+ * 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 = lgr_page;
+
+       if (stsi(si, 1, 1, 1) == -ENOSYS)
+               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 = lgr_page;
+
+       if (stsi(si, 2, 2, 2) == -ENOSYS)
+               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 = lgr_page;
+       int i;
+
+       if (stsi(si, 3, 2, 2) == -ENOSYS)
+               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)
+{
+       memset(lgr_info, 0, sizeof(*lgr_info));
+       stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
+       lgr_info->level = stsi_0();
+       if (lgr_info->level == -ENOSYS)
+               return;
+       if (lgr_info->level >= 1)
+               lgr_stsi_1_1_1(lgr_info);
+       if (lgr_info->level >= 2)
+               lgr_stsi_2_2_2(lgr_info);
+       if (lgr_info->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(unsigned long ignored)
+{
+       lgr_info_log();
+       lgr_timer_set();
+}
+
+static struct timer_list lgr_timer =
+       TIMER_DEFERRED_INITIALIZER(lgr_timer_fn, 0, 0);
+
+/*
+ * 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_page = (void *) __get_free_pages(GFP_KERNEL, 0);
+       if (!lgr_page)
+               return -ENOMEM;
+       lgr_dbf = debug_register("lgr", 1, 1, sizeof(struct lgr_info));
+       if (!lgr_dbf) {
+               free_page((unsigned long) lgr_page);
+               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));
+       lgr_timer_set();
+       return 0;
+}
+module_init(lgr_init);
 
 #include <linux/delay.h>
 #include <linux/reboot.h>
 #include <linux/ftrace.h>
+#include <linux/debug_locks.h>
 #include <asm/cio.h>
 #include <asm/setup.h>
 #include <asm/pgtable.h>
        struct kimage *image = data;
 
        pfault_fini();
-       if (image->type == KEXEC_TYPE_CRASH)
+       tracing_off();
+       debug_locks_off();
+       if (image->type == KEXEC_TYPE_CRASH) {
+               lgr_info_log();
                s390_reset_system(__do_machine_kdump, data);
-       else
+       } else {
                s390_reset_system(__do_machine_kexec, data);
+       }
        disabled_wait((unsigned long) __builtin_return_address(0));
 }
 
 
 #include <asm/lowcore.h>
 #include <asm/sclp.h>
 #include <asm/vdso.h>
+#include <asm/debug.h>
 #include "entry.h"
 
 enum {
        __load_psw_mask(psw_kernel_bits | PSW_MASK_DAT);
        trace_hardirqs_off();
 
+       debug_set_critical();
        cpumask_copy(&cpumask, cpu_online_mask);
        cpumask_clear_cpu(smp_processor_id(), &cpumask);
 
 
        lghi    %r2,0
        brasl   %r14,arch_set_page_states
 
+       /* Log potential guest relocation */
+       brasl   %r14,lgr_info_log
+
        /* Reinitialize the channel subsystem */
        brasl   %r14,channel_subsystem_reinit
 
 
 #include <asm/cpcmd.h>
 #include <asm/lowcore.h>
 #include <asm/debug.h>
+#include <asm/ipl.h>
 #include "entry.h"
 
 void (*pgm_check_table[128])(struct pt_regs *regs);
        static int die_counter;
 
        oops_enter();
+       lgr_info_log();
        debug_stop_all();
        console_verbose();
        spin_lock_irq(&die_lock);
 
 #include <linux/atomic.h>
 #include <asm/debug.h>
 #include <asm/qdio.h>
+#include <asm/ipl.h>
 
 #include "cio.h"
 #include "css.h"
                   q->nr, q->first_to_kick, count, irq_ptr->int_parm);
 no_handler:
        qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
+       /*
+        * In case of z/VM LGR (Live Guest Migration) QDIO recovery will happen.
+        * Therefore we call the LGR detection function here.
+        */
+       lgr_info_log();
 }
 
 static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,