Skip to content

Commit

Permalink
calgary iommu: use the first kernels TCE tables in kdump
Browse files Browse the repository at this point in the history
kdump kernel fails to boot with calgary iommu and aacraid driver on a x366
box.  The ongoing dma's of aacraid from the first kernel continue to exist
until the driver is loaded in the kdump kernel.  Calgary is initialized
prior to aacraid and creation of new tce tables causes wrong dma's to
occur.  Here we try to get the tce tables of the first kernel in kdump
kernel and use them.  While in the kdump kernel we do not allocate new tce
tables but instead read the base address register contents of calgary
iommu and use the tables that the registers point to.  With these changes
the kdump kernel and hence aacraid now boots normally.

Signed-off-by: Chandru Siddalingappa <[email protected]>
Acked-by: Muli Ben-Yehuda <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Chandru authored and torvalds committed Jul 25, 2008
1 parent 8448502 commit 95b68de
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 6 deletions.
85 changes: 79 additions & 6 deletions arch/x86/kernel/pci-calgary_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/crash_dump.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include <linux/pci_ids.h>
Expand Down Expand Up @@ -167,6 +168,8 @@ static void calgary_dump_error_regs(struct iommu_table *tbl);
static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
static void calioc2_tce_cache_blast(struct iommu_table *tbl);
static void calioc2_dump_error_regs(struct iommu_table *tbl);
static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl);
static void get_tce_space_from_tar(void);

static struct cal_chipset_ops calgary_chip_ops = {
.handle_quirks = calgary_handle_quirks,
Expand Down Expand Up @@ -830,7 +833,11 @@ static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar)

tbl = pci_iommu(dev->bus);
tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space;
tce_free(tbl, 0, tbl->it_size);

if (is_kdump_kernel())
calgary_init_bitmap_from_tce_table(tbl);
else
tce_free(tbl, 0, tbl->it_size);

if (is_calgary(dev->device))
tbl->chip_ops = &calgary_chip_ops;
Expand Down Expand Up @@ -1209,6 +1216,10 @@ static int __init calgary_init(void)
if (ret)
return ret;

/* Purely for kdump kernel case */
if (is_kdump_kernel())
get_tce_space_from_tar();

do {
dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
if (!dev)
Expand Down Expand Up @@ -1339,6 +1350,61 @@ static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev)
return (val != 0xffffffff);
}

/*
* calgary_init_bitmap_from_tce_table():
* Funtion for kdump case. In the second/kdump kernel initialize
* the bitmap based on the tce table entries obtained from first kernel
*/
static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl)
{
u64 *tp;
unsigned int index;
tp = ((u64 *)tbl->it_base);
for (index = 0 ; index < tbl->it_size; index++) {
if (*tp != 0x0)
set_bit(index, tbl->it_map);
tp++;
}
}

/*
* get_tce_space_from_tar():
* Function for kdump case. Get the tce tables from first kernel
* by reading the contents of the base adress register of calgary iommu
*/
static void get_tce_space_from_tar()
{
int bus;
void __iomem *target;
unsigned long tce_space;

for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
struct calgary_bus_info *info = &bus_info[bus];
unsigned short pci_device;
u32 val;

val = read_pci_config(bus, 0, 0, 0);
pci_device = (val & 0xFFFF0000) >> 16;

if (!is_cal_pci_dev(pci_device))
continue;
if (info->translation_disabled)
continue;

if (calgary_bus_has_devices(bus, pci_device) ||
translate_empty_slots) {
target = calgary_reg(bus_info[bus].bbar,
tar_offset(bus));
tce_space = be64_to_cpu(readq(target));
tce_space = tce_space & TAR_SW_BITS;

tce_space = tce_space & (~specified_table_size);
info->tce_space = (u64 *)__va(tce_space);
}
}
return;
}

void __init detect_calgary(void)
{
int bus;
Expand Down Expand Up @@ -1394,7 +1460,8 @@ void __init detect_calgary(void)
return;
}

specified_table_size = determine_tce_table_size(max_pfn * PAGE_SIZE);
specified_table_size = determine_tce_table_size((is_kdump_kernel() ?
saved_max_pfn : max_pfn) * PAGE_SIZE);

for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
struct calgary_bus_info *info = &bus_info[bus];
Expand All @@ -1412,10 +1479,16 @@ void __init detect_calgary(void)

if (calgary_bus_has_devices(bus, pci_device) ||
translate_empty_slots) {
tbl = alloc_tce_table();
if (!tbl)
goto cleanup;
info->tce_space = tbl;
/*
* If it is kdump kernel, find and use tce tables
* from first kernel, else allocate tce tables here
*/
if (!is_kdump_kernel()) {
tbl = alloc_tce_table();
if (!tbl)
goto cleanup;
info->tce_space = tbl;
}
calgary_found = 1;
}
}
Expand Down
8 changes: 8 additions & 0 deletions include/linux/crash_dump.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,13 @@ extern struct proc_dir_entry *proc_vmcore;

#define vmcore_elf_check_arch(x) (elf_check_arch(x) || vmcore_elf_check_arch_cross(x))

static inline int is_kdump_kernel(void)
{
return (elfcorehdr_addr != ELFCORE_ADDR_MAX) ? 1 : 0;
}
#else /* !CONFIG_CRASH_DUMP */
static inline int is_kdump_kernel(void) { return 0; }
#endif /* CONFIG_CRASH_DUMP */

extern unsigned long saved_max_pfn;
#endif /* LINUX_CRASHDUMP_H */

0 comments on commit 95b68de

Please sign in to comment.