summaryrefslogtreecommitdiff
path: root/arch/ia64/sn/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/sn/kernel')
-rw-r--r--arch/ia64/sn/kernel/bte.c1
-rw-r--r--arch/ia64/sn/kernel/io_init.c153
-rw-r--r--arch/ia64/sn/kernel/setup.c157
-rw-r--r--arch/ia64/sn/kernel/sn2/ptc_deadlock.S8
-rw-r--r--arch/ia64/sn/kernel/sn2/sn2_smp.c5
-rw-r--r--arch/ia64/sn/kernel/sn2/sn_hwperf.c3
-rw-r--r--arch/ia64/sn/kernel/xpc.h2
-rw-r--r--arch/ia64/sn/kernel/xpc_main.c102
-rw-r--r--arch/ia64/sn/kernel/xpc_partition.c8
9 files changed, 404 insertions, 35 deletions
diff --git a/arch/ia64/sn/kernel/bte.c b/arch/ia64/sn/kernel/bte.c
index d71f4de44f79..dd73c0cb754b 100644
--- a/arch/ia64/sn/kernel/bte.c
+++ b/arch/ia64/sn/kernel/bte.c
@@ -137,6 +137,7 @@ retry_bteop:
bte = bte_if_on_node(nasid_to_try[nasid_index],bte_if_index);
if (bte == NULL) {
+ nasid_index++;
continue;
}
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c
index b4f5053f5e1b..318087e35b66 100644
--- a/arch/ia64/sn/kernel/io_init.c
+++ b/arch/ia64/sn/kernel/io_init.c
@@ -3,7 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 1992 - 1997, 2000-2005 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/bootmem.h>
@@ -147,6 +147,24 @@ sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
}
/*
+ * sn_pcidev_info_get() - Retrieve the pcidev_info struct for the specified
+ * device.
+ */
+inline struct pcidev_info *
+sn_pcidev_info_get(struct pci_dev *dev)
+{
+ struct pcidev_info *pcidev;
+
+ list_for_each_entry(pcidev,
+ &(SN_PCI_CONTROLLER(dev)->pcidev_info), pdi_list) {
+ if (pcidev->pdi_linux_pcidev == dev) {
+ return pcidev;
+ }
+ }
+ return NULL;
+}
+
+/*
* sn_fixup_ionodes() - This routine initializes the HUB data strcuture for
* each node in the system.
*/
@@ -229,6 +247,50 @@ static void sn_fixup_ionodes(void)
}
+/*
+ * sn_pci_window_fixup() - Create a pci_window for each device resource.
+ * Until ACPI support is added, we need this code
+ * to setup pci_windows for use by
+ * pcibios_bus_to_resource(),
+ * pcibios_resource_to_bus(), etc.
+ */
+static void
+sn_pci_window_fixup(struct pci_dev *dev, unsigned int count,
+ int64_t * pci_addrs)
+{
+ struct pci_controller *controller = PCI_CONTROLLER(dev->bus);
+ unsigned int i;
+ unsigned int idx;
+ unsigned int new_count;
+ struct pci_window *new_window;
+
+ if (count == 0)
+ return;
+ idx = controller->windows;
+ new_count = controller->windows + count;
+ new_window = kcalloc(new_count, sizeof(struct pci_window), GFP_KERNEL);
+ if (new_window == NULL)
+ BUG();
+ if (controller->window) {
+ memcpy(new_window, controller->window,
+ sizeof(struct pci_window) * controller->windows);
+ kfree(controller->window);
+ }
+
+ /* Setup a pci_window for each device resource. */
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+ if (pci_addrs[i] == -1)
+ continue;
+
+ new_window[idx].offset = dev->resource[i].start - pci_addrs[i];
+ new_window[idx].resource = dev->resource[i];
+ idx++;
+ }
+
+ controller->windows = new_count;
+ controller->window = new_window;
+}
+
void sn_pci_unfixup_slot(struct pci_dev *dev)
{
struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev;
@@ -246,21 +308,23 @@ void sn_pci_unfixup_slot(struct pci_dev *dev)
*/
void sn_pci_fixup_slot(struct pci_dev *dev)
{
+ unsigned int count = 0;
int idx;
int segment = pci_domain_nr(dev->bus);
int status = 0;
struct pcibus_bussoft *bs;
struct pci_bus *host_pci_bus;
struct pci_dev *host_pci_dev;
+ struct pcidev_info *pcidev_info;
+ int64_t pci_addrs[PCI_ROM_RESOURCE + 1];
struct sn_irq_info *sn_irq_info;
unsigned long size;
unsigned int bus_no, devfn;
pci_dev_get(dev); /* for the sysdata pointer */
- dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL);
- if (SN_PCIDEV_INFO(dev) <= 0)
+ pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
+ if (pcidev_info <= 0)
BUG(); /* Cannot afford to run out of memory */
- memset(SN_PCIDEV_INFO(dev), 0, sizeof(struct pcidev_info));
sn_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
if (sn_irq_info <= 0)
@@ -270,22 +334,34 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
/* Call to retrieve pci device information needed by kernel. */
status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number,
dev->devfn,
- (u64) __pa(SN_PCIDEV_INFO(dev)),
+ (u64) __pa(pcidev_info),
(u64) __pa(sn_irq_info));
if (status)
BUG(); /* Cannot get platform pci device information */
+ /* Add pcidev_info to list in sn_pci_controller struct */
+ list_add_tail(&pcidev_info->pdi_list,
+ &(SN_PCI_CONTROLLER(dev->bus)->pcidev_info));
+
/* Copy over PIO Mapped Addresses */
for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
unsigned long start, end, addr;
- if (!SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx])
+ if (!pcidev_info->pdi_pio_mapped_addr[idx]) {
+ pci_addrs[idx] = -1;
continue;
+ }
start = dev->resource[idx].start;
end = dev->resource[idx].end;
size = end - start;
- addr = SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx];
+ if (size == 0) {
+ pci_addrs[idx] = -1;
+ continue;
+ }
+ pci_addrs[idx] = start;
+ count++;
+ addr = pcidev_info->pdi_pio_mapped_addr[idx];
addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET;
dev->resource[idx].start = addr;
dev->resource[idx].end = addr + size;
@@ -294,23 +370,27 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
else
dev->resource[idx].parent = &iomem_resource;
}
+ /* Create a pci_window in the pci_controller struct for
+ * each device resource.
+ */
+ if (count > 0)
+ sn_pci_window_fixup(dev, count, pci_addrs);
/*
* Using the PROMs values for the PCI host bus, get the Linux
* PCI host_pci_dev struct and set up host bus linkages
*/
- bus_no = (SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32) & 0xff;
- devfn = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle & 0xffffffff;
+ bus_no = (pcidev_info->pdi_slot_host_handle >> 32) & 0xff;
+ devfn = pcidev_info->pdi_slot_host_handle & 0xffffffff;
host_pci_bus = pci_find_bus(segment, bus_no);
host_pci_dev = pci_get_slot(host_pci_bus, devfn);
- SN_PCIDEV_INFO(dev)->host_pci_dev = host_pci_dev;
- SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info =
- SN_PCIDEV_INFO(host_pci_dev);
- SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev;
+ pcidev_info->host_pci_dev = host_pci_dev;
+ pcidev_info->pdi_linux_pcidev = dev;
+ pcidev_info->pdi_host_pcidev_info = SN_PCIDEV_INFO(host_pci_dev);
bs = SN_PCIBUS_BUSSOFT(dev->bus);
- SN_PCIDEV_INFO(dev)->pdi_pcibus_info = bs;
+ pcidev_info->pdi_pcibus_info = bs;
if (bs && bs->bs_asic_type < PCIIO_ASIC_MAX_TYPES) {
SN_PCIDEV_BUSPROVIDER(dev) = sn_pci_provider[bs->bs_asic_type];
@@ -320,11 +400,11 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
/* Only set up IRQ stuff if this device has a host bus context */
if (bs && sn_irq_info->irq_irq) {
- SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info;
- dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq;
+ pcidev_info->pdi_sn_irq_info = sn_irq_info;
+ dev->irq = pcidev_info->pdi_sn_irq_info->irq_irq;
sn_irq_fixup(dev, sn_irq_info);
} else {
- SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = NULL;
+ pcidev_info->pdi_sn_irq_info = NULL;
kfree(sn_irq_info);
}
}
@@ -338,6 +418,7 @@ void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
int status = 0;
int nasid, cnode;
struct pci_controller *controller;
+ struct sn_pci_controller *sn_controller;
struct pcibus_bussoft *prom_bussoft_ptr;
struct hubdev_info *hubdev_info;
void *provider_soft = NULL;
@@ -349,10 +430,15 @@ void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
return; /*bus # does not exist */
prom_bussoft_ptr = __va(prom_bussoft_ptr);
- controller = kcalloc(1,sizeof(struct pci_controller), GFP_KERNEL);
+ /* Allocate a sn_pci_controller, which has a pci_controller struct
+ * as the first member.
+ */
+ sn_controller = kzalloc(sizeof(struct sn_pci_controller), GFP_KERNEL);
+ if (!sn_controller)
+ BUG();
+ INIT_LIST_HEAD(&sn_controller->pcidev_info);
+ controller = &sn_controller->pci_controller;
controller->segment = segment;
- if (!controller)
- BUG();
if (bus == NULL) {
bus = pci_scan_bus(busnum, &pci_root_ops, controller);
@@ -390,6 +476,29 @@ void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
}
/*
+ * Setup pci_windows for legacy IO and MEM space.
+ * (Temporary until ACPI support is in place.)
+ */
+ controller->window = kcalloc(2, sizeof(struct pci_window), GFP_KERNEL);
+ if (controller->window == NULL)
+ BUG();
+ controller->window[0].offset = prom_bussoft_ptr->bs_legacy_io;
+ controller->window[0].resource.name = "legacy_io";
+ controller->window[0].resource.flags = IORESOURCE_IO;
+ controller->window[0].resource.start = prom_bussoft_ptr->bs_legacy_io;
+ controller->window[0].resource.end =
+ controller->window[0].resource.start + 0xffff;
+ controller->window[0].resource.parent = &ioport_resource;
+ controller->window[1].offset = prom_bussoft_ptr->bs_legacy_mem;
+ controller->window[1].resource.name = "legacy_mem";
+ controller->window[1].resource.flags = IORESOURCE_MEM;
+ controller->window[1].resource.start = prom_bussoft_ptr->bs_legacy_mem;
+ controller->window[1].resource.end =
+ controller->window[1].resource.start + (1024 * 1024) - 1;
+ controller->window[1].resource.parent = &iomem_resource;
+ controller->windows = 2;
+
+ /*
* Generic bus fixup goes here. Don't reference prom_bussoft_ptr
* after this point.
*/
@@ -421,7 +530,7 @@ void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
error_return:
- kfree(controller);
+ kfree(sn_controller);
return;
}
@@ -434,7 +543,7 @@ void sn_bus_store_sysdata(struct pci_dev *dev)
dev_dbg(dev, "%s: out of memory!\n", __FUNCTION__);
return;
}
- element->sysdata = dev->sysdata;
+ element->sysdata = SN_PCIDEV_INFO(dev);
list_add(&element->entry, &sn_sysdata_list);
}
diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c
index 0fb579ef18c2..e510dce9971f 100644
--- a/arch/ia64/sn/kernel/setup.c
+++ b/arch/ia64/sn/kernel/setup.c
@@ -30,6 +30,7 @@
#include <linux/root_dev.h>
#include <linux/nodemask.h>
#include <linux/pm.h>
+#include <linux/efi.h>
#include <asm/io.h>
#include <asm/sal.h>
@@ -242,6 +243,135 @@ static void __init sn_check_for_wars(void)
}
}
+/*
+ * Scan the EFI PCDP table (if it exists) for an acceptable VGA console
+ * output device. If one exists, pick it and set sn_legacy_{io,mem} to
+ * reflect the bus offsets needed to address it.
+ *
+ * Since pcdp support in SN is not supported in the 2.4 kernel (or at least
+ * the one lbs is based on) just declare the needed structs here.
+ *
+ * Reference spec http://www.dig64.org/specifications/DIG64_PCDPv20.pdf
+ *
+ * Returns 0 if no acceptable vga is found, !0 otherwise.
+ *
+ * Note: This stuff is duped here because Altix requires the PCDP to
+ * locate a usable VGA device due to lack of proper ACPI support. Structures
+ * could be used from drivers/firmware/pcdp.h, but it was decided that moving
+ * this file to a more public location just for Altix use was undesireable.
+ */
+
+struct hcdp_uart_desc {
+ u8 pad[45];
+};
+
+struct pcdp {
+ u8 signature[4]; /* should be 'HCDP' */
+ u32 length;
+ u8 rev; /* should be >=3 for pcdp, <3 for hcdp */
+ u8 sum;
+ u8 oem_id[6];
+ u64 oem_tableid;
+ u32 oem_rev;
+ u32 creator_id;
+ u32 creator_rev;
+ u32 num_type0;
+ struct hcdp_uart_desc uart[0]; /* num_type0 of these */
+ /* pcdp descriptors follow */
+} __attribute__((packed));
+
+struct pcdp_device_desc {
+ u8 type;
+ u8 primary;
+ u16 length;
+ u16 index;
+ /* interconnect specific structure follows */
+ /* device specific structure follows that */
+} __attribute__((packed));
+
+struct pcdp_interface_pci {
+ u8 type; /* 1 == pci */
+ u8 reserved;
+ u16 length;
+ u8 segment;
+ u8 bus;
+ u8 dev;
+ u8 fun;
+ u16 devid;
+ u16 vendid;
+ u32 acpi_interrupt;
+ u64 mmio_tra;
+ u64 ioport_tra;
+ u8 flags;
+ u8 translation;
+} __attribute__((packed));
+
+struct pcdp_vga_device {
+ u8 num_eas_desc;
+ /* ACPI Extended Address Space Desc follows */
+} __attribute__((packed));
+
+/* from pcdp_device_desc.primary */
+#define PCDP_PRIMARY_CONSOLE 0x01
+
+/* from pcdp_device_desc.type */
+#define PCDP_CONSOLE_INOUT 0x0
+#define PCDP_CONSOLE_DEBUG 0x1
+#define PCDP_CONSOLE_OUT 0x2
+#define PCDP_CONSOLE_IN 0x3
+#define PCDP_CONSOLE_TYPE_VGA 0x8
+
+#define PCDP_CONSOLE_VGA (PCDP_CONSOLE_TYPE_VGA | PCDP_CONSOLE_OUT)
+
+/* from pcdp_interface_pci.type */
+#define PCDP_IF_PCI 1
+
+/* from pcdp_interface_pci.translation */
+#define PCDP_PCI_TRANS_IOPORT 0x02
+#define PCDP_PCI_TRANS_MMIO 0x01
+
+static void
+sn_scan_pcdp(void)
+{
+ u8 *bp;
+ struct pcdp *pcdp;
+ struct pcdp_device_desc device;
+ struct pcdp_interface_pci if_pci;
+ extern struct efi efi;
+
+ pcdp = efi.hcdp;
+ if (! pcdp)
+ return; /* no hcdp/pcdp table */
+
+ if (pcdp->rev < 3)
+ return; /* only support PCDP (rev >= 3) */
+
+ for (bp = (u8 *)&pcdp->uart[pcdp->num_type0];
+ bp < (u8 *)pcdp + pcdp->length;
+ bp += device.length) {
+ memcpy(&device, bp, sizeof(device));
+ if (! (device.primary & PCDP_PRIMARY_CONSOLE))
+ continue; /* not primary console */
+
+ if (device.type != PCDP_CONSOLE_VGA)
+ continue; /* not VGA descriptor */
+
+ memcpy(&if_pci, bp+sizeof(device), sizeof(if_pci));
+ if (if_pci.type != PCDP_IF_PCI)
+ continue; /* not PCI interconnect */
+
+ if (if_pci.translation & PCDP_PCI_TRANS_IOPORT)
+ vga_console_iobase =
+ if_pci.ioport_tra | __IA64_UNCACHED_OFFSET;
+
+ if (if_pci.translation & PCDP_PCI_TRANS_MMIO)
+ vga_console_membase =
+ if_pci.mmio_tra | __IA64_UNCACHED_OFFSET;
+
+ break; /* once we find the primary, we're done */
+ }
+}
+
/**
* sn_setup - SN platform setup routine
* @cmdline_p: kernel command line
@@ -263,16 +393,35 @@ void __init sn_setup(char **cmdline_p)
#if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
/*
- * If there was a primary vga adapter identified through the
- * EFI PCDP table, make it the preferred console. Otherwise
- * zero out conswitchp.
+ * Handle SN vga console.
+ *
+ * SN systems do not have enough ACPI table information
+ * being passed from prom to identify VGA adapters and the legacy
+ * addresses to access them. Until that is done, SN systems rely
+ * on the PCDP table to identify the primary VGA console if one
+ * exists.
+ *
+ * However, kernel PCDP support is optional, and even if it is built
+ * into the kernel, it will not be used if the boot cmdline contains
+ * console= directives.
+ *
+ * So, to work around this mess, we duplicate some of the PCDP code
+ * here so that the primary VGA console (as defined by PCDP) will
+ * work on SN systems even if a different console (e.g. serial) is
+ * selected on the boot line (or CONFIG_EFI_PCDP is off).
*/
+ if (! vga_console_membase)
+ sn_scan_pcdp();
+
if (vga_console_membase) {
/* usable vga ... make tty0 the preferred default console */
- add_preferred_console("tty", 0, NULL);
+ if (!strstr(*cmdline_p, "console="))
+ add_preferred_console("tty", 0, NULL);
} else {
printk(KERN_DEBUG "SGI: Disabling VGA console\n");
+ if (!strstr(*cmdline_p, "console="))
+ add_preferred_console("ttySG", 0, NULL);
#ifdef CONFIG_DUMMY_CONSOLE
conswitchp = &dummy_con;
#else
diff --git a/arch/ia64/sn/kernel/sn2/ptc_deadlock.S b/arch/ia64/sn/kernel/sn2/ptc_deadlock.S
index 3fa95065a446..bebbcc4f8dd4 100644
--- a/arch/ia64/sn/kernel/sn2/ptc_deadlock.S
+++ b/arch/ia64/sn/kernel/sn2/ptc_deadlock.S
@@ -39,9 +39,13 @@ sn2_ptc_deadlock_recovery_core:
mov r8=r0
1:
+ cmp.ne p8,p9=r0,ptc1 // Test for shub type (ptc1 non-null on shub1)
+ // p8 = 1 if shub1, p9 = 1 if shub2
+
add scr2=ALIAS_OFFSET,piowc // Address of WRITE_STATUS alias register
- ;;
- ld8.acq scr1=[scr2];;
+ mov scr1=7;; // Clear DEADLOCK, WRITE_ERROR, MULTI_WRITE_ERROR
+(p8) st8.rel [scr2]=scr1;;
+(p9) ld8.acq scr1=[scr2];;
5: ld8.acq scr1=[piowc];; // Wait for PIOs to complete.
hint @pause
diff --git a/arch/ia64/sn/kernel/sn2/sn2_smp.c b/arch/ia64/sn/kernel/sn2/sn2_smp.c
index 49b530c39a42..471bbaa65d1b 100644
--- a/arch/ia64/sn/kernel/sn2/sn2_smp.c
+++ b/arch/ia64/sn/kernel/sn2/sn2_smp.c
@@ -202,7 +202,7 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start,
unsigned long end, unsigned long nbits)
{
int i, opt, shub1, cnode, mynasid, cpu, lcpu = 0, nasid, flushed = 0;
- int mymm = (mm == current->active_mm);
+ int mymm = (mm == current->active_mm && current->mm);
volatile unsigned long *ptc0, *ptc1;
unsigned long itc, itc2, flags, data0 = 0, data1 = 0, rr_value;
short nasids[MAX_NUMNODES], nix;
@@ -492,6 +492,9 @@ static struct proc_dir_entry *proc_sn2_ptc;
static int __init sn2_ptc_init(void)
{
+ if (!ia64_platform_is("sn2"))
+ return -ENOSYS;
+
if (!(proc_sn2_ptc = create_proc_entry(PTC_BASENAME, 0444, NULL))) {
printk(KERN_ERR "unable to create %s proc entry", PTC_BASENAME);
return -EINVAL;
diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
index 6c6fbca3229c..19b54fbcd7ea 100644
--- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c
+++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
@@ -743,13 +743,14 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
memset(p, 0, a.sz);
for (i = 0; i < nobj; i++) {
+ int cpuobj_index = 0;
if (!SN_HWPERF_IS_NODE(objs + i))
continue;
node = sn_hwperf_obj_to_cnode(objs + i);
for_each_online_cpu(j) {
if (node != cpu_to_node(j))
continue;
- cpuobj = (struct sn_hwperf_object_info *) p + j;
+ cpuobj = (struct sn_hwperf_object_info *) p + cpuobj_index++;
slice = 'a' + cpuid_to_slice(j);
cdata = cpu_data(j);
cpuobj->id = j;
diff --git a/arch/ia64/sn/kernel/xpc.h b/arch/ia64/sn/kernel/xpc.h
index fbcedc7c27fa..5483a9f227d4 100644
--- a/arch/ia64/sn/kernel/xpc.h
+++ b/arch/ia64/sn/kernel/xpc.h
@@ -163,7 +163,7 @@ struct xpc_vars {
u8 version;
u64 heartbeat;
u64 heartbeating_to_mask;
- u64 kdb_status; /* 0 = machine running */
+ u64 heartbeat_offline; /* if 0, heartbeat should be changing */
int act_nasid;
int act_phys_cpuid;
u64 vars_part_pa;
diff --git a/arch/ia64/sn/kernel/xpc_main.c b/arch/ia64/sn/kernel/xpc_main.c
index cece3c7c69be..b617236524c6 100644
--- a/arch/ia64/sn/kernel/xpc_main.c
+++ b/arch/ia64/sn/kernel/xpc_main.c
@@ -57,6 +57,7 @@
#include <linux/reboot.h>
#include <asm/sn/intr.h>
#include <asm/sn/sn_sal.h>
+#include <asm/kdebug.h>
#include <asm/uaccess.h>
#include "xpc.h"
@@ -188,6 +189,11 @@ static struct notifier_block xpc_reboot_notifier = {
.notifier_call = xpc_system_reboot,
};
+static int xpc_system_die(struct notifier_block *, unsigned long, void *);
+static struct notifier_block xpc_die_notifier = {
+ .notifier_call = xpc_system_die,
+};
+
/*
* Timer function to enforce the timelimit on the partition disengage request.
@@ -997,6 +1003,9 @@ xpc_do_exit(enum xpc_retval reason)
/* take ourselves off of the reboot_notifier_list */
(void) unregister_reboot_notifier(&xpc_reboot_notifier);
+ /* take ourselves off of the die_notifier list */
+ (void) unregister_die_notifier(&xpc_die_notifier);
+
/* close down protections for IPI operations */
xpc_restrict_IPI_ops();
@@ -1011,6 +1020,63 @@ xpc_do_exit(enum xpc_retval reason)
/*
+ * Called when the system is about to be either restarted or halted.
+ */
+static void
+xpc_die_disengage(void)
+{
+ struct xpc_partition *part;
+ partid_t partid;
+ unsigned long engaged;
+ long time, print_time, disengage_request_timeout;
+
+
+ /* keep xpc_hb_checker thread from doing anything (just in case) */
+ xpc_exiting = 1;
+
+ xpc_vars->heartbeating_to_mask = 0; /* indicate we're deactivated */
+
+ for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+ part = &xpc_partitions[partid];
+
+ if (!XPC_SUPPORTS_DISENGAGE_REQUEST(part->
+ remote_vars_version)) {
+
+ /* just in case it was left set by an earlier XPC */
+ xpc_clear_partition_engaged(1UL << partid);
+ continue;
+ }
+
+ if (xpc_partition_engaged(1UL << partid) ||
+ part->act_state != XPC_P_INACTIVE) {
+ xpc_request_partition_disengage(part);
+ xpc_mark_partition_disengaged(part);
+ xpc_IPI_send_disengage(part);
+ }
+ }
+
+ print_time = rtc_time();
+ disengage_request_timeout = print_time +
+ (xpc_disengage_request_timelimit * sn_rtc_cycles_per_second);
+
+ /* wait for all other partitions to disengage from us */
+
+ while ((engaged = xpc_partition_engaged(-1UL)) &&
+ (time = rtc_time()) < disengage_request_timeout) {
+
+ if (time >= print_time) {
+ dev_info(xpc_part, "waiting for remote partitions to "
+ "disengage, engaged=0x%lx\n", engaged);
+ print_time = time + (XPC_DISENGAGE_PRINTMSG_INTERVAL *
+ sn_rtc_cycles_per_second);
+ }
+ }
+ dev_info(xpc_part, "finished waiting for remote partitions to "
+ "disengage, engaged=0x%lx\n", engaged);
+}
+
+
+/*
* This function is called when the system is being rebooted.
*/
static int
@@ -1038,6 +1104,33 @@ xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused)
}
+/*
+ * This function is called when the system is being rebooted.
+ */
+static int
+xpc_system_die(struct notifier_block *nb, unsigned long event, void *unused)
+{
+ switch (event) {
+ case DIE_MACHINE_RESTART:
+ case DIE_MACHINE_HALT:
+ xpc_die_disengage();
+ break;
+ case DIE_MCA_MONARCH_ENTER:
+ case DIE_INIT_MONARCH_ENTER:
+ xpc_vars->heartbeat++;
+ xpc_vars->heartbeat_offline = 1;
+ break;
+ case DIE_MCA_MONARCH_LEAVE:
+ case DIE_INIT_MONARCH_LEAVE:
+ xpc_vars->heartbeat++;
+ xpc_vars->heartbeat_offline = 0;
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+
int __init
xpc_init(void)
{
@@ -1154,6 +1247,12 @@ xpc_init(void)
dev_warn(xpc_part, "can't register reboot notifier\n");
}
+ /* add ourselves to the die_notifier list (i.e., ia64die_chain) */
+ ret = register_die_notifier(&xpc_die_notifier);
+ if (ret != 0) {
+ dev_warn(xpc_part, "can't register die notifier\n");
+ }
+
/*
* Set the beating to other partitions into motion. This is
@@ -1179,6 +1278,9 @@ xpc_init(void)
/* take ourselves off of the reboot_notifier_list */
(void) unregister_reboot_notifier(&xpc_reboot_notifier);
+ /* take ourselves off of the die_notifier list */
+ (void) unregister_die_notifier(&xpc_die_notifier);
+
del_timer_sync(&xpc_hb_timer);
free_irq(SGI_XPC_ACTIVATE, NULL);
xpc_restrict_IPI_ops();
diff --git a/arch/ia64/sn/kernel/xpc_partition.c b/arch/ia64/sn/kernel/xpc_partition.c
index 581e113d2d37..cdd6431853a1 100644
--- a/arch/ia64/sn/kernel/xpc_partition.c
+++ b/arch/ia64/sn/kernel/xpc_partition.c
@@ -436,13 +436,13 @@ xpc_check_remote_hb(void)
}
dev_dbg(xpc_part, "partid = %d, heartbeat = %ld, last_heartbeat"
- " = %ld, kdb_status = %ld, HB_mask = 0x%lx\n", partid,
- remote_vars->heartbeat, part->last_heartbeat,
- remote_vars->kdb_status,
+ " = %ld, heartbeat_offline = %ld, HB_mask = 0x%lx\n",
+ partid, remote_vars->heartbeat, part->last_heartbeat,
+ remote_vars->heartbeat_offline,
remote_vars->heartbeating_to_mask);
if (((remote_vars->heartbeat == part->last_heartbeat) &&
- (remote_vars->kdb_status == 0)) ||
+ (remote_vars->heartbeat_offline == 0)) ||
!xpc_hb_allowed(sn_partition_id, remote_vars)) {
XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat);