diff options
author | Yinghai Lu <yhlu.kernel@gmail.com> | 2008-04-13 04:41:58 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-04-26 17:41:04 -0400 |
commit | e8ee6f0ae5cd860e8e6c02807edfa3c1fa01bcb5 (patch) | |
tree | 800e742a999207617506a9475dc40e3c6234d11a /arch | |
parent | cbf9bd603ab1fc4d2ecb1c6a4b7bd1cc50a7e82a (diff) |
x86: work around io allocation overlap of HT links
normally BIOSes assign io/mmio range to different HT links without
overlapping, even same node same link should get non overlapping
entries.
but Rafael L. Wysocki's buggy BIOS creates a link with overlapping
entries for mmio and io:
node 0 link 0: io port [1000, ffffff]
node 0 link 0: mmio [e0000000, efffffff]
node 0 link 0: mmio [a0000, bffff]
node 0 link 0: mmio [80000000, ffffffff]
try to merge them and we will get:
bus: [00, ff] on node 0 link 0
bus: 00 index 0 io port: [0, ffff]
bus: 00 index 1 mmio: [80000000, fcffffffff]
bus: 00 index 2 mmio: [a0000, bffff]
so later we will reduce the chance to assign used resource to
unassigned device.
Reported-by: "Rafael J. Wysocki" <rjw@sisk.pl>
Signed-off-by: Yinghai Lu <yhlu.kernel@gmail.com>
Tested-by: "Rafael J. Wysocki" <rjw@sisk.pl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/pci/k8-bus_64.c | 56 |
1 files changed, 38 insertions, 18 deletions
diff --git a/arch/x86/pci/k8-bus_64.c b/arch/x86/pci/k8-bus_64.c index 4e64c58e17a0..ab6d4b18a88f 100644 --- a/arch/x86/pci/k8-bus_64.c +++ b/arch/x86/pci/k8-bus_64.c | |||
@@ -112,17 +112,25 @@ static void __init update_range(struct res_range *range, size_t start, | |||
112 | for (j = 0; j < RANGE_NUM; j++) { | 112 | for (j = 0; j < RANGE_NUM; j++) { |
113 | if (!range[j].end) | 113 | if (!range[j].end) |
114 | continue; | 114 | continue; |
115 | if (start == range[j].start && end < range[j].end) { | 115 | |
116 | range[j].start = end + 1; | 116 | if (start <= range[j].start && end >= range[j].end) { |
117 | break; | ||
118 | } else if (start == range[j].start && end == range[j].end) { | ||
119 | range[j].start = 0; | 117 | range[j].start = 0; |
120 | range[j].end = 0; | 118 | range[j].end = 0; |
121 | break; | 119 | continue; |
122 | } else if (start > range[j].start && end == range[j].end) { | 120 | } |
121 | |||
122 | if (start <= range[j].start && end < range[j].end && range[j].start < end + 1) { | ||
123 | range[j].start = end + 1; | ||
124 | continue; | ||
125 | } | ||
126 | |||
127 | |||
128 | if (start > range[j].start && end >= range[j].end && range[j].end > start - 1) { | ||
123 | range[j].end = start - 1; | 129 | range[j].end = start - 1; |
124 | break; | 130 | continue; |
125 | } else if (start > range[j].start && end < range[j].end) { | 131 | } |
132 | |||
133 | if (start > range[j].start && end < range[j].end) { | ||
126 | /* find the new spare */ | 134 | /* find the new spare */ |
127 | for (i = 0; i < RANGE_NUM; i++) { | 135 | for (i = 0; i < RANGE_NUM; i++) { |
128 | if (range[i].end == 0) | 136 | if (range[i].end == 0) |
@@ -135,7 +143,7 @@ static void __init update_range(struct res_range *range, size_t start, | |||
135 | printk(KERN_ERR "run of slot in ranges\n"); | 143 | printk(KERN_ERR "run of slot in ranges\n"); |
136 | } | 144 | } |
137 | range[j].end = start - 1; | 145 | range[j].end = start - 1; |
138 | break; | 146 | continue; |
139 | } | 147 | } |
140 | } | 148 | } |
141 | } | 149 | } |
@@ -151,16 +159,24 @@ static void __init update_res(struct pci_root_info *info, size_t start, | |||
151 | 159 | ||
152 | /* try to merge it with old one */ | 160 | /* try to merge it with old one */ |
153 | for (i = 0; i < info->res_num; i++) { | 161 | for (i = 0; i < info->res_num; i++) { |
162 | size_t final_start, final_end; | ||
163 | size_t common_start, common_end; | ||
164 | |||
154 | res = &info->res[i]; | 165 | res = &info->res[i]; |
155 | if (res->flags != flags) | 166 | if (res->flags != flags) |
156 | continue; | 167 | continue; |
157 | if (res->end + 1 == start) { | 168 | |
158 | res->end = end; | 169 | common_start = max((size_t)res->start, start); |
159 | return; | 170 | common_end = min((size_t)res->end, end); |
160 | } else if (end + 1 == res->start) { | 171 | if (common_start > common_end + 1) |
161 | res->start = start; | 172 | continue; |
162 | return; | 173 | |
163 | } | 174 | final_start = min((size_t)res->start, start); |
175 | final_end = max((size_t)res->end, end); | ||
176 | |||
177 | res->start = final_start; | ||
178 | res->end = final_end; | ||
179 | return; | ||
164 | } | 180 | } |
165 | 181 | ||
166 | addit: | 182 | addit: |
@@ -336,7 +352,11 @@ static int __init early_fill_mp_bus_info(void) | |||
336 | info = &pci_root_info[j]; | 352 | info = &pci_root_info[j]; |
337 | printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n", | 353 | printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n", |
338 | node, link, (u64)start, (u64)end); | 354 | node, link, (u64)start, (u64)end); |
339 | update_res(info, start, end, IORESOURCE_IO, 0); | 355 | |
356 | /* kernel only handle 16 bit only */ | ||
357 | if (end > 0xffff) | ||
358 | end = 0xffff; | ||
359 | update_res(info, start, end, IORESOURCE_IO, 1); | ||
340 | update_range(range, start, end); | 360 | update_range(range, start, end); |
341 | } | 361 | } |
342 | /* add left over io port range to def node/link, [0, 0xffff] */ | 362 | /* add left over io port range to def node/link, [0, 0xffff] */ |
@@ -444,7 +464,7 @@ static int __init early_fill_mp_bus_info(void) | |||
444 | } | 464 | } |
445 | } | 465 | } |
446 | 466 | ||
447 | update_res(info, start, end, IORESOURCE_MEM, 0); | 467 | update_res(info, start, end, IORESOURCE_MEM, 1); |
448 | update_range(range, start, end); | 468 | update_range(range, start, end); |
449 | printk(KERN_CONT "\n"); | 469 | printk(KERN_CONT "\n"); |
450 | } | 470 | } |