Skip to content

Commit 8278c69

Browse files
ij-intelbjorn-helgaas
authored andcommitted
PCI: Preserve bridge window resource type flags
When a bridge window is found unused or fails to assign, the flags of the associated resource are cleared. Clearing flags is problematic as it also removes the type information of the resource which is needed later. Thus, always preserve the bridge window type flags and use IORESOURCE_UNSET and IORESOURCE_DISABLED to indicate the status of the bridge window. Also, when initializing resources, make sure all valid bridge windows do get their type flags set. Change various places that relied on resource flags being cleared to check for IORESOURCE_UNSET and IORESOURCE_DISABLED to allow bridge window resource to retain their type flags. Add pdev_resource_assignable() and pdev_resource_should_fit() helpers to filter out disabled bridge windows during resource fitting; the latter combines more common checks into the helper. When reading the bridge windows from the registers, instead of leaving the resource flags cleared for bridge windows that are not enabled, always set up the flags and set IORESOURCE_UNSET | IORESOURCE_DISABLED as needed. When resource fitting or assignment fails for a bridge window resource, or the bridge window is not needed, mark the resource with IORESOURCE_UNSET or IORESOURCE_DISABLED, respectively. Use dummy zero resource in resource_show() for backwards compatibility as lspci will otherwise misrepresent disabled bridge windows. This change fixes an issue which highlights the importance of keeping the resource type flags intact: At the end of __assign_resources_sorted(), reset_resource() is called, previously clearing the flags. Later, pci_prepare_next_assign_round() attempted to release bridge resources using pci_bus_release_bridge_resources() that calls into pci_bridge_release_resources() that assumes type flags are still present. As type flags were cleared, IORESOURCE_MEM_64 was not set leading to resources under an incorrect bridge window to be released (idx = 1 instead of idx = 2). While the assignments performed later covered this problem so that the wrongly released resources got assigned in the end, it was still causing extra release+assign pairs. There are other reasons why the resource flags should be retained in upcoming changes too. Removing the flag reset for non-bridge window resource is left as future work, in part because it has a much higher regression potential due to pci_enable_resources() that will start to work also for those resources then and due to what endpoint drivers might assume about resources. Despite the Fixes tag, backporting this (at least any time soon) is highly discouraged. The issue fixed is borderline cosmetic as the later assignments normally cover the problem entirely. Also there might be non-obvious dependencies. Fixes: 5b28541 ("PCI: Restrict 64-bit prefetchable bridge windows to 64-bit resources") Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20250829131113.36754-11-ilpo.jarvinen@linux.intel.com
1 parent 1cdffa5 commit 8278c69

5 files changed

Lines changed: 90 additions & 37 deletions

File tree

drivers/pci/bus.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
204204
if (!r)
205205
continue;
206206

207+
if (r->flags & (IORESOURCE_UNSET|IORESOURCE_DISABLED))
208+
continue;
209+
207210
/* type_mask must match */
208211
if ((res->flags ^ r->flags) & type_mask)
209212
continue;

drivers/pci/pci-sysfs.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
177177

178178
for (i = 0; i < max; i++) {
179179
struct resource *res = &pci_dev->resource[i];
180+
struct resource zerores = {};
181+
182+
/* For backwards compatibility */
183+
if (i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END &&
184+
res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED))
185+
res = &zerores;
186+
180187
pci_resource_to_user(pci_dev, i, res, &start, &end);
181188
len += sysfs_emit_at(buf, len, "0x%016llx 0x%016llx 0x%016llx\n",
182189
(unsigned long long)start,

drivers/pci/probe.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -419,13 +419,17 @@ static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res,
419419
limit |= ((unsigned long) io_limit_hi << 16);
420420
}
421421

422+
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
423+
422424
if (base <= limit) {
423-
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
424425
region.start = base;
425426
region.end = limit + io_granularity - 1;
426427
pcibios_bus_to_resource(dev->bus, res, &region);
427428
if (log)
428429
pci_info(dev, " bridge window %pR\n", res);
430+
} else {
431+
resource_set_range(res, 0, 0);
432+
res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
429433
}
430434
}
431435

@@ -440,13 +444,18 @@ static void pci_read_bridge_mmio(struct pci_dev *dev, struct resource *res,
440444
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
441445
base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
442446
limit = ((unsigned long) mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
447+
448+
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
449+
443450
if (base <= limit) {
444-
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
445451
region.start = base;
446452
region.end = limit + 0xfffff;
447453
pcibios_bus_to_resource(dev->bus, res, &region);
448454
if (log)
449455
pci_info(dev, " bridge window %pR\n", res);
456+
} else {
457+
resource_set_range(res, 0, 0);
458+
res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
450459
}
451460
}
452461

@@ -489,16 +498,20 @@ static void pci_read_bridge_mmio_pref(struct pci_dev *dev, struct resource *res,
489498
return;
490499
}
491500

501+
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | IORESOURCE_MEM |
502+
IORESOURCE_PREFETCH;
503+
if (res->flags & PCI_PREF_RANGE_TYPE_64)
504+
res->flags |= IORESOURCE_MEM_64;
505+
492506
if (base <= limit) {
493-
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
494-
IORESOURCE_MEM | IORESOURCE_PREFETCH;
495-
if (res->flags & PCI_PREF_RANGE_TYPE_64)
496-
res->flags |= IORESOURCE_MEM_64;
497507
region.start = base;
498508
region.end = limit + 0xfffff;
499509
pcibios_bus_to_resource(dev->bus, res, &region);
500510
if (log)
501511
pci_info(dev, " bridge window %pR\n", res);
512+
} else {
513+
resource_set_range(res, 0, 0);
514+
res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
502515
}
503516
}
504517

drivers/pci/setup-bus.c

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,31 @@ static bool pdev_resources_assignable(struct pci_dev *dev)
190190
return true;
191191
}
192192

193+
static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res)
194+
{
195+
int idx = pci_resource_num(dev, res);
196+
197+
if (!res->flags)
198+
return false;
199+
200+
if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END &&
201+
res->flags & IORESOURCE_DISABLED)
202+
return false;
203+
204+
return true;
205+
}
206+
207+
static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res)
208+
{
209+
if (res->parent)
210+
return false;
211+
212+
if (res->flags & IORESOURCE_PCI_FIXED)
213+
return false;
214+
215+
return pdev_resource_assignable(dev, res);
216+
}
217+
193218
/* Sort resources by alignment */
194219
static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
195220
{
@@ -205,10 +230,7 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
205230
resource_size_t r_align;
206231
struct list_head *n;
207232

208-
if (r->flags & IORESOURCE_PCI_FIXED)
209-
continue;
210-
211-
if (!(r->flags) || r->parent)
233+
if (!pdev_resource_should_fit(dev, r))
212234
continue;
213235

214236
r_align = pci_resource_alignment(dev, r);
@@ -257,8 +279,15 @@ bool pci_resource_is_optional(const struct pci_dev *dev, int resno)
257279
return false;
258280
}
259281

260-
static inline void reset_resource(struct resource *res)
282+
static inline void reset_resource(struct pci_dev *dev, struct resource *res)
261283
{
284+
int idx = pci_resource_num(dev, res);
285+
286+
if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) {
287+
res->flags |= IORESOURCE_UNSET;
288+
return;
289+
}
290+
262291
res->start = 0;
263292
res->end = 0;
264293
res->flags = 0;
@@ -610,7 +639,7 @@ static void __assign_resources_sorted(struct list_head *head,
610639
0 /* don't care */);
611640
}
612641

613-
reset_resource(res);
642+
reset_resource(dev, res);
614643
}
615644

616645
free_list(head);
@@ -1014,8 +1043,11 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
10141043

10151044
if (r->parent || !(r->flags & IORESOURCE_IO))
10161045
continue;
1017-
r_size = resource_size(r);
10181046

1047+
if (!pdev_resource_assignable(dev, r))
1048+
continue;
1049+
1050+
r_size = resource_size(r);
10191051
if (r_size < SZ_1K)
10201052
/* Might be re-aligned for ISA */
10211053
size += r_size;
@@ -1034,6 +1066,9 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
10341066
size0 = calculate_iosize(size, min_size, size1, 0, 0,
10351067
resource_size(b_res), min_align);
10361068

1069+
if (size0)
1070+
b_res->flags &= ~IORESOURCE_DISABLED;
1071+
10371072
size1 = size0;
10381073
if (realloc_head && (add_size > 0 || children_add_size > 0)) {
10391074
size1 = calculate_iosize(size, min_size, size1, add_size,
@@ -1045,13 +1080,14 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
10451080
if (bus->self && (b_res->start || b_res->end))
10461081
pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n",
10471082
b_res, &bus->busn_res);
1048-
b_res->flags = 0;
1083+
b_res->flags |= IORESOURCE_DISABLED;
10491084
return;
10501085
}
10511086

10521087
resource_set_range(b_res, min_align, size0);
10531088
b_res->flags |= IORESOURCE_STARTALIGN;
10541089
if (bus->self && size1 > size0 && realloc_head) {
1090+
b_res->flags &= ~IORESOURCE_DISABLED;
10551091
add_to_list(realloc_head, bus->self, b_res, size1-size0,
10561092
min_align);
10571093
pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n",
@@ -1198,11 +1234,13 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
11981234
const char *r_name = pci_resource_name(dev, i);
11991235
resource_size_t r_size;
12001236

1201-
if (r->parent || (r->flags & IORESOURCE_PCI_FIXED) ||
1202-
!pdev_resources_assignable(dev) ||
1203-
((r->flags & mask) != type &&
1204-
(r->flags & mask) != type2 &&
1205-
(r->flags & mask) != type3))
1237+
if (!pdev_resources_assignable(dev) ||
1238+
!pdev_resource_should_fit(dev, r))
1239+
continue;
1240+
1241+
if ((r->flags & mask) != type &&
1242+
(r->flags & mask) != type2 &&
1243+
(r->flags & mask) != type3)
12061244
continue;
12071245
r_size = resource_size(r);
12081246

@@ -1253,6 +1291,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
12531291
min_align = max(min_align, win_align);
12541292
size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align);
12551293

1294+
if (size0)
1295+
b_res->flags &= ~IORESOURCE_DISABLED;
1296+
12561297
if (bus->self && size0 &&
12571298
!pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type,
12581299
size0, min_align)) {
@@ -1287,13 +1328,14 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
12871328
if (bus->self && (b_res->start || b_res->end))
12881329
pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n",
12891330
b_res, &bus->busn_res);
1290-
b_res->flags = 0;
1331+
b_res->flags |= IORESOURCE_DISABLED;
12911332
return 0;
12921333
}
12931334

12941335
resource_set_range(b_res, min_align, size0);
12951336
b_res->flags |= IORESOURCE_STARTALIGN;
12961337
if (bus->self && size1 > size0 && realloc_head) {
1338+
b_res->flags &= ~IORESOURCE_DISABLED;
12971339
add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align);
12981340
pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n",
12991341
b_res, &bus->busn_res,
@@ -1721,7 +1763,6 @@ static void pci_bridge_release_resources(struct pci_bus *bus,
17211763
{
17221764
struct pci_dev *dev = bus->self;
17231765
struct resource *r;
1724-
unsigned int old_flags;
17251766
struct resource *b_res;
17261767
int idx, ret;
17271768

@@ -1758,17 +1799,15 @@ static void pci_bridge_release_resources(struct pci_bus *bus,
17581799
/* If there are children, release them all */
17591800
release_child_resources(r);
17601801

1761-
type = old_flags = r->flags & PCI_RES_TYPE_MASK;
17621802
ret = pci_release_resource(dev, PCI_BRIDGE_RESOURCES + idx);
17631803
if (ret)
17641804
return;
17651805

1806+
type = r->flags & PCI_RES_TYPE_MASK;
17661807
/* Avoiding touch the one without PREF */
17671808
if (type & IORESOURCE_PREFETCH)
17681809
type = IORESOURCE_PREFETCH;
17691810
__pci_setup_bridge(bus, type);
1770-
/* For next child res under same bridge */
1771-
r->flags = old_flags;
17721811
}
17731812

17741813
enum release_type {
@@ -2246,21 +2285,9 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head,
22462285
}
22472286

22482287
/* Restore size and flags */
2249-
list_for_each_entry(fail_res, fail_head, list) {
2250-
struct resource *res = fail_res->res;
2251-
struct pci_dev *dev = fail_res->dev;
2252-
int idx = pci_resource_num(dev, res);
2253-
2288+
list_for_each_entry(fail_res, fail_head, list)
22542289
restore_dev_resource(fail_res);
22552290

2256-
if (!pci_is_bridge(dev))
2257-
continue;
2258-
2259-
if (idx >= PCI_BRIDGE_RESOURCES &&
2260-
idx <= PCI_BRIDGE_RESOURCE_END)
2261-
res->flags = 0;
2262-
}
2263-
22642291
free_list(fail_head);
22652292
}
22662293

drivers/pci/setup-res.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
359359

360360
res->flags &= ~IORESOURCE_UNSET;
361361
res->flags &= ~IORESOURCE_STARTALIGN;
362+
if (resno >= PCI_BRIDGE_RESOURCES && resno <= PCI_BRIDGE_RESOURCE_END)
363+
res->flags &= ~IORESOURCE_DISABLED;
364+
362365
pci_info(dev, "%s %pR: assigned\n", res_name, res);
363366
if (resno < PCI_BRIDGE_RESOURCES)
364367
pci_update_resource(dev, resno);

0 commit comments

Comments
 (0)