Skip to content

Commit

Permalink
x86, iommu: Add proper dependency sort routine (and sanity check).
Browse files Browse the repository at this point in the history
We are using a very simple sort routine which sorts the .iommu_table
array in the order of dependencies. Specifically each structure
of iommu_table_entry has a field 'depend' which contains the function
pointer to the IOMMU that MUST be run before us. We sort the array
of structures so that the struct iommu_table_entry with no
'depend' field are first, and then the subsequent ones are the
ones for which the 'depend' function has been already invoked
(in other words, precede us).

Using the kernel's version 'sort', which is a mergeheap is
feasible, but would require making the comparison operator
scan recursivly the array to satisfy the "heapify" process: setting the
levels properly. The end result would much more complex than it should
be an it is just much simpler to utilize this simple sort routine.

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
LKML-Reference: <1282845485-8991-4-git-send-email-konrad.wilk@oracle.com>
CC: H. Peter Anvin <hpa@zytor.com>
CC: Fujita Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
  • Loading branch information
Konrad Rzeszutek Wilk authored and H. Peter Anvin committed Aug 26, 2010
1 parent 480125b commit 5bef80a
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
6 changes: 6 additions & 0 deletions arch/x86/include/asm/iommu_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,10 @@ struct iommu_table_entry {
#define IOMMU_INIT(_detect, _depend, _init, _late_init) \
__IOMMU_INIT(_detect, _depend, _init, _late_init, 0)

void sort_iommu_table(struct iommu_table_entry *start,
struct iommu_table_entry *finish);

void check_iommu_entries(struct iommu_table_entry *start,
struct iommu_table_entry *finish);

#endif /* _ASM_X86_IOMMU_TABLE_H */
1 change: 1 addition & 0 deletions arch/x86/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ obj-y += bootflag.o e820.o
obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o
obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
obj-y += tsc.o io_delay.o rtc.o
obj-y += pci-iommu_table.o

obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-y += process.o
Expand Down
89 changes: 89 additions & 0 deletions arch/x86/kernel/pci-iommu_table.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <linux/dma-mapping.h>
#include <asm/iommu_table.h>
#include <linux/string.h>
#include <linux/kallsyms.h>


#define DEBUG 1

static struct iommu_table_entry * __init
find_dependents_of(struct iommu_table_entry *start,
struct iommu_table_entry *finish,
struct iommu_table_entry *q)
{
struct iommu_table_entry *p;

if (!q)
return NULL;

for (p = start; p < finish; p++)
if (p->detect == q->depend)
return p;

return NULL;
}


void __init sort_iommu_table(struct iommu_table_entry *start,
struct iommu_table_entry *finish) {

struct iommu_table_entry *p, *q, tmp;

for (p = start; p < finish; p++) {
again:
q = find_dependents_of(start, finish, p);
/* We are bit sneaky here. We use the memory address to figure
* out if the node we depend on is past our point, if so, swap.
*/
if (q > p) {
tmp = *p;
memmove(p, q, sizeof(*p));
*q = tmp;
goto again;
}
}

}

#ifdef DEBUG
void __init check_iommu_entries(struct iommu_table_entry *start,
struct iommu_table_entry *finish)
{
struct iommu_table_entry *p, *q, *x;
char sym_p[KSYM_SYMBOL_LEN];
char sym_q[KSYM_SYMBOL_LEN];

/* Simple cyclic dependency checker. */
for (p = start; p < finish; p++) {
q = find_dependents_of(start, finish, p);
x = find_dependents_of(start, finish, q);
if (p == x) {
sprint_symbol(sym_p, (unsigned long)p->detect);
sprint_symbol(sym_q, (unsigned long)q->detect);

printk(KERN_ERR "CYCLIC DEPENDENCY FOUND! %s depends" \
" on %s and vice-versa. BREAKING IT.\n",
sym_p, sym_q);
/* Heavy handed way..*/
x->depend = 0;
}
}

for (p = start; p < finish; p++) {
q = find_dependents_of(p, finish, p);
if (q && q > p) {
sprint_symbol(sym_p, (unsigned long)p->detect);
sprint_symbol(sym_q, (unsigned long)q->detect);

printk(KERN_ERR "EXECUTION ORDER INVALID! %s "\
"should be called before %s!\n",
sym_p, sym_q);
}
}
}
#else
inline void check_iommu_entries(struct iommu_table_entry *start,
struct iommu_table_entry *finish)
{
}
#endif

0 comments on commit 5bef80a

Please sign in to comment.