Skip to content

Commit 8bb705e

Browse files
ChristianKoenigAMDbjorn-helgaas
authored andcommitted
PCI: Add pci_resize_resource() for resizing BARs
Add a pci_resize_resource() interface to allow device drivers to resize BARs of their devices. This is useful for devices with large local storage, e.g., graphics devices. These devices often only expose 256MB BARs initially to be compatible with 32-bit systems. This function only tries to reprogram the windows of the bridge directly above the requesting device and only the BAR of the same type (usually mem, 64bit, prefetchable). This is done to avoid disturbing other drivers by changing the BARs of their devices. Drivers should use the following sequence to resize their BARs: 1. Disable memory decoding of the device using the PCI cfg dword. 2. Use pci_release_resource() to release all BARs which can move during the resize, including the one you want to resize. 3. Call pci_resize_resource() for each BAR you want to resize. 4. Call pci_assign_unassigned_bus_resources() to reassign new locations for all BARs which are not resized, but could move. 5. If everything worked as expected, enable memory decoding in the device again using the PCI cfg dword. Signed-off-by: Christian König <christian.koenig@amd.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
1 parent 276b738 commit 8bb705e

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

drivers/pci/setup-bus.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,104 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
19131913
}
19141914
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
19151915

1916+
int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
1917+
{
1918+
struct pci_dev_resource *dev_res;
1919+
struct pci_dev *next;
1920+
LIST_HEAD(saved);
1921+
LIST_HEAD(added);
1922+
LIST_HEAD(failed);
1923+
unsigned int i;
1924+
int ret;
1925+
1926+
/* Walk to the root hub, releasing bridge BARs when possible */
1927+
next = bridge;
1928+
do {
1929+
bridge = next;
1930+
for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END;
1931+
i++) {
1932+
struct resource *res = &bridge->resource[i];
1933+
1934+
if ((res->flags ^ type) & PCI_RES_TYPE_MASK)
1935+
continue;
1936+
1937+
/* Ignore BARs which are still in use */
1938+
if (res->child)
1939+
continue;
1940+
1941+
ret = add_to_list(&saved, bridge, res, 0, 0);
1942+
if (ret)
1943+
goto cleanup;
1944+
1945+
dev_info(&bridge->dev, "BAR %d: releasing %pR\n",
1946+
i, res);
1947+
1948+
if (res->parent)
1949+
release_resource(res);
1950+
res->start = 0;
1951+
res->end = 0;
1952+
break;
1953+
}
1954+
if (i == PCI_BRIDGE_RESOURCE_END)
1955+
break;
1956+
1957+
next = bridge->bus ? bridge->bus->self : NULL;
1958+
} while (next);
1959+
1960+
if (list_empty(&saved))
1961+
return -ENOENT;
1962+
1963+
__pci_bus_size_bridges(bridge->subordinate, &added);
1964+
__pci_bridge_assign_resources(bridge, &added, &failed);
1965+
BUG_ON(!list_empty(&added));
1966+
1967+
if (!list_empty(&failed)) {
1968+
ret = -ENOSPC;
1969+
goto cleanup;
1970+
}
1971+
1972+
list_for_each_entry(dev_res, &saved, list) {
1973+
/* Skip the bridge we just assigned resources for. */
1974+
if (bridge == dev_res->dev)
1975+
continue;
1976+
1977+
bridge = dev_res->dev;
1978+
pci_setup_bridge(bridge->subordinate);
1979+
}
1980+
1981+
free_list(&saved);
1982+
return 0;
1983+
1984+
cleanup:
1985+
/* restore size and flags */
1986+
list_for_each_entry(dev_res, &failed, list) {
1987+
struct resource *res = dev_res->res;
1988+
1989+
res->start = dev_res->start;
1990+
res->end = dev_res->end;
1991+
res->flags = dev_res->flags;
1992+
}
1993+
free_list(&failed);
1994+
1995+
/* Revert to the old configuration */
1996+
list_for_each_entry(dev_res, &saved, list) {
1997+
struct resource *res = dev_res->res;
1998+
1999+
bridge = dev_res->dev;
2000+
i = res - bridge->resource;
2001+
2002+
res->start = dev_res->start;
2003+
res->end = dev_res->end;
2004+
res->flags = dev_res->flags;
2005+
2006+
pci_claim_resource(bridge, i);
2007+
pci_setup_bridge(bridge->subordinate);
2008+
}
2009+
free_list(&saved);
2010+
2011+
return ret;
2012+
}
2013+
19162014
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
19172015
{
19182016
struct pci_dev *dev;

drivers/pci/setup-res.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,64 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz
396396
return 0;
397397
}
398398

399+
void pci_release_resource(struct pci_dev *dev, int resno)
400+
{
401+
struct resource *res = dev->resource + resno;
402+
403+
dev_info(&dev->dev, "BAR %d: releasing %pR\n", resno, res);
404+
release_resource(res);
405+
res->end = resource_size(res) - 1;
406+
res->start = 0;
407+
res->flags |= IORESOURCE_UNSET;
408+
}
409+
EXPORT_SYMBOL(pci_release_resource);
410+
411+
int pci_resize_resource(struct pci_dev *dev, int resno, int size)
412+
{
413+
struct resource *res = dev->resource + resno;
414+
int old, ret;
415+
u32 sizes;
416+
u16 cmd;
417+
418+
/* Make sure the resource isn't assigned before resizing it. */
419+
if (!(res->flags & IORESOURCE_UNSET))
420+
return -EBUSY;
421+
422+
pci_read_config_word(dev, PCI_COMMAND, &cmd);
423+
if (cmd & PCI_COMMAND_MEMORY)
424+
return -EBUSY;
425+
426+
sizes = pci_rebar_get_possible_sizes(dev, resno);
427+
if (!sizes)
428+
return -ENOTSUPP;
429+
430+
if (!(sizes & BIT(size)))
431+
return -EINVAL;
432+
433+
old = pci_rebar_get_current_size(dev, resno);
434+
if (old < 0)
435+
return old;
436+
437+
ret = pci_rebar_set_size(dev, resno, size);
438+
if (ret)
439+
return ret;
440+
441+
res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
442+
443+
/* Check if the new config works by trying to assign everything. */
444+
ret = pci_reassign_bridge_resources(dev->bus->self, res->flags);
445+
if (ret)
446+
goto error_resize;
447+
448+
return 0;
449+
450+
error_resize:
451+
pci_rebar_set_size(dev, resno, old);
452+
res->end = res->start + pci_rebar_size_to_bytes(old) - 1;
453+
return ret;
454+
}
455+
EXPORT_SYMBOL(pci_resize_resource);
456+
399457
int pci_enable_resources(struct pci_dev *dev, int mask)
400458
{
401459
u16 cmd, old_cmd;

include/linux/pci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,8 @@ void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
11061106
void pci_update_resource(struct pci_dev *dev, int resno);
11071107
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
11081108
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
1109+
void pci_release_resource(struct pci_dev *dev, int resno);
1110+
int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
11091111
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
11101112
bool pci_device_is_present(struct pci_dev *pdev);
11111113
void pci_ignore_hotplug(struct pci_dev *dev);
@@ -1185,6 +1187,7 @@ void pci_assign_unassigned_resources(void);
11851187
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge);
11861188
void pci_assign_unassigned_bus_resources(struct pci_bus *bus);
11871189
void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus);
1190+
int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type);
11881191
void pdev_enable_device(struct pci_dev *);
11891192
int pci_enable_resources(struct pci_dev *, int mask);
11901193
void pci_assign_irq(struct pci_dev *dev);

0 commit comments

Comments
 (0)