mirror of https://github.com/torvalds/linux.git
Merge branch 'pci/resource'
- Prevent resource tree corruption when BAR resize fails (Ilpo Järvinen) - Restore BARs to the original size if a BAR resize fails (Ilpo Järvinen) - Remove BAR release from BAR resize attempts by the xe, i915, and amdgpu drivers so the PCI core can restore BARs if the resize fails (Ilpo Järvinen) - Move Resizable BAR code to rebar.c (Ilpo Järvinen) - Add pci_rebar_size_supported() and use it in i915 and xe (Ilpo Järvinen) - Add pci_rebar_get_max_size() and use it in xe and amdgpu (Ilpo Järvinen) * pci/resource: PCI: Validate pci_rebar_size_supported() input PCI: Convert BAR sizes bitmasks to u64 drm/amdgpu: Use pci_rebar_get_max_size() drm/xe/vram: Use pci_rebar_get_max_size() PCI: Add pci_rebar_get_max_size() drm/xe/vram: Use PCI rebar helpers in resize_vram_bar() drm/i915/gt: Use pci_rebar_size_supported() PCI: Add pci_rebar_size_supported() helper PCI: Improve Resizable BAR functions kernel doc PCI: Move pci_rebar_size_to_bytes() and export it PCI: Move pci_rebar_bytes_to_size() and clean it up PCI: Move Resizable BAR code to rebar.c PCI: Prevent restoring assigned resources drm/amdgpu: Remove driver side BAR release before resize drm/i915: Remove driver side BAR release before resize drm/xe: Remove driver side BAR release before resize PCI: Add kerneldoc for pci_resize_resource() PCI: Fix restoring BARs on BAR resize rollback path PCI: Free saved list without holding pci_bus_sem PCI: Try BAR resize even when no window was released PCI: Change pci_dev variable from 'bridge' to 'dev' PCI/IOV: Adjust ->barsz[] when changing BAR size PCI: Prevent resource tree corruption when BAR resize fails
This commit is contained in:
commit
13571584e1
|
|
@ -37,6 +37,9 @@ PCI Support Library
|
||||||
.. kernel-doc:: drivers/pci/slot.c
|
.. kernel-doc:: drivers/pci/slot.c
|
||||||
:export:
|
:export:
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/pci/rebar.c
|
||||||
|
:export:
|
||||||
|
|
||||||
.. kernel-doc:: drivers/pci/rom.c
|
.. kernel-doc:: drivers/pci/rom.c
|
||||||
:export:
|
:export:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1673,9 +1673,9 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev)
|
||||||
int rbar_size = pci_rebar_bytes_to_size(adev->gmc.real_vram_size);
|
int rbar_size = pci_rebar_bytes_to_size(adev->gmc.real_vram_size);
|
||||||
struct pci_bus *root;
|
struct pci_bus *root;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
int max_size, r;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
u16 cmd;
|
u16 cmd;
|
||||||
int r;
|
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
|
if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1721,30 +1721,28 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Limit the BAR size to what is available */
|
/* Limit the BAR size to what is available */
|
||||||
rbar_size = min(fls(pci_rebar_get_possible_sizes(adev->pdev, 0)) - 1,
|
max_size = pci_rebar_get_max_size(adev->pdev, 0);
|
||||||
rbar_size);
|
if (max_size < 0)
|
||||||
|
return 0;
|
||||||
|
rbar_size = min(max_size, rbar_size);
|
||||||
|
|
||||||
/* Disable memory decoding while we change the BAR addresses and size */
|
/* Disable memory decoding while we change the BAR addresses and size */
|
||||||
pci_read_config_word(adev->pdev, PCI_COMMAND, &cmd);
|
pci_read_config_word(adev->pdev, PCI_COMMAND, &cmd);
|
||||||
pci_write_config_word(adev->pdev, PCI_COMMAND,
|
pci_write_config_word(adev->pdev, PCI_COMMAND,
|
||||||
cmd & ~PCI_COMMAND_MEMORY);
|
cmd & ~PCI_COMMAND_MEMORY);
|
||||||
|
|
||||||
/* Free the VRAM and doorbell BAR, we most likely need to move both. */
|
/* Tear down doorbell as resizing will release BARs */
|
||||||
amdgpu_doorbell_fini(adev);
|
amdgpu_doorbell_fini(adev);
|
||||||
if (adev->asic_type >= CHIP_BONAIRE)
|
|
||||||
pci_release_resource(adev->pdev, 2);
|
|
||||||
|
|
||||||
pci_release_resource(adev->pdev, 0);
|
r = pci_resize_resource(adev->pdev, 0, rbar_size,
|
||||||
|
(adev->asic_type >= CHIP_BONAIRE) ? 1 << 5
|
||||||
r = pci_resize_resource(adev->pdev, 0, rbar_size);
|
: 1 << 2);
|
||||||
if (r == -ENOSPC)
|
if (r == -ENOSPC)
|
||||||
dev_info(adev->dev,
|
dev_info(adev->dev,
|
||||||
"Not enough PCI address space for a large BAR.");
|
"Not enough PCI address space for a large BAR.");
|
||||||
else if (r && r != -ENOTSUPP)
|
else if (r && r != -ENOTSUPP)
|
||||||
dev_err(adev->dev, "Problem resizing BAR0 (%d).", r);
|
dev_err(adev->dev, "Problem resizing BAR0 (%d).", r);
|
||||||
|
|
||||||
pci_assign_unassigned_bus_resources(adev->pdev->bus);
|
|
||||||
|
|
||||||
/* When the doorbell or fb BAR isn't available we have no chance of
|
/* When the doorbell or fb BAR isn't available we have no chance of
|
||||||
* using the device.
|
* using the device.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,6 @@
|
||||||
#include "gt/intel_gt_regs.h"
|
#include "gt/intel_gt_regs.h"
|
||||||
|
|
||||||
#ifdef CONFIG_64BIT
|
#ifdef CONFIG_64BIT
|
||||||
static void _release_bars(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
int resno;
|
|
||||||
|
|
||||||
for (resno = PCI_STD_RESOURCES; resno < PCI_STD_RESOURCE_END; resno++) {
|
|
||||||
if (pci_resource_len(pdev, resno))
|
|
||||||
pci_release_resource(pdev, resno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
|
_resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
|
||||||
{
|
{
|
||||||
|
|
@ -35,9 +25,7 @@ _resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
|
||||||
int bar_size = pci_rebar_bytes_to_size(size);
|
int bar_size = pci_rebar_bytes_to_size(size);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
_release_bars(pdev);
|
ret = pci_resize_resource(pdev, resno, bar_size, 0);
|
||||||
|
|
||||||
ret = pci_resize_resource(pdev, resno, bar_size);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n",
|
drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n",
|
||||||
resno, 1 << bar_size, ERR_PTR(ret));
|
resno, 1 << bar_size, ERR_PTR(ret));
|
||||||
|
|
@ -61,16 +49,12 @@ static void i915_resize_lmem_bar(struct drm_i915_private *i915, resource_size_t
|
||||||
current_size = roundup_pow_of_two(pci_resource_len(pdev, GEN12_LMEM_BAR));
|
current_size = roundup_pow_of_two(pci_resource_len(pdev, GEN12_LMEM_BAR));
|
||||||
|
|
||||||
if (i915->params.lmem_bar_size) {
|
if (i915->params.lmem_bar_size) {
|
||||||
u32 bar_sizes;
|
rebar_size = i915->params.lmem_bar_size * (resource_size_t)SZ_1M;
|
||||||
|
|
||||||
rebar_size = i915->params.lmem_bar_size *
|
|
||||||
(resource_size_t)SZ_1M;
|
|
||||||
bar_sizes = pci_rebar_get_possible_sizes(pdev, GEN12_LMEM_BAR);
|
|
||||||
|
|
||||||
if (rebar_size == current_size)
|
if (rebar_size == current_size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(bar_sizes & BIT(pci_rebar_bytes_to_size(rebar_size))) ||
|
if (!pci_rebar_size_supported(pdev, GEN12_LMEM_BAR,
|
||||||
|
pci_rebar_bytes_to_size(rebar_size)) ||
|
||||||
rebar_size >= roundup_pow_of_two(lmem_size)) {
|
rebar_size >= roundup_pow_of_two(lmem_size)) {
|
||||||
rebar_size = lmem_size;
|
rebar_size = lmem_size;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@
|
||||||
#include "xe_vram.h"
|
#include "xe_vram.h"
|
||||||
#include "xe_vram_types.h"
|
#include "xe_vram_types.h"
|
||||||
|
|
||||||
#define BAR_SIZE_SHIFT 20
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_resize_bar(struct xe_device *xe, int resno, resource_size_t size)
|
_resize_bar(struct xe_device *xe, int resno, resource_size_t size)
|
||||||
{
|
{
|
||||||
|
|
@ -33,10 +31,7 @@ _resize_bar(struct xe_device *xe, int resno, resource_size_t size)
|
||||||
int bar_size = pci_rebar_bytes_to_size(size);
|
int bar_size = pci_rebar_bytes_to_size(size);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (pci_resource_len(pdev, resno))
|
ret = pci_resize_resource(pdev, resno, bar_size, 0);
|
||||||
pci_release_resource(pdev, resno);
|
|
||||||
|
|
||||||
ret = pci_resize_resource(pdev, resno, bar_size);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n",
|
drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n",
|
||||||
resno, 1 << bar_size, ERR_PTR(ret));
|
resno, 1 << bar_size, ERR_PTR(ret));
|
||||||
|
|
@ -58,41 +53,37 @@ static void resize_vram_bar(struct xe_device *xe)
|
||||||
resource_size_t current_size;
|
resource_size_t current_size;
|
||||||
resource_size_t rebar_size;
|
resource_size_t rebar_size;
|
||||||
struct resource *root_res;
|
struct resource *root_res;
|
||||||
u32 bar_size_mask;
|
int max_size, i;
|
||||||
u32 pci_cmd;
|
u32 pci_cmd;
|
||||||
int i;
|
|
||||||
|
|
||||||
/* gather some relevant info */
|
/* gather some relevant info */
|
||||||
current_size = pci_resource_len(pdev, LMEM_BAR);
|
current_size = pci_resource_len(pdev, LMEM_BAR);
|
||||||
bar_size_mask = pci_rebar_get_possible_sizes(pdev, LMEM_BAR);
|
|
||||||
|
|
||||||
if (!bar_size_mask)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (force_vram_bar_size < 0)
|
if (force_vram_bar_size < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* set to a specific size? */
|
/* set to a specific size? */
|
||||||
if (force_vram_bar_size) {
|
if (force_vram_bar_size) {
|
||||||
u32 bar_size_bit;
|
rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size *
|
||||||
|
(resource_size_t)SZ_1M);
|
||||||
|
|
||||||
rebar_size = force_vram_bar_size * (resource_size_t)SZ_1M;
|
if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) {
|
||||||
|
|
||||||
bar_size_bit = bar_size_mask & BIT(pci_rebar_bytes_to_size(rebar_size));
|
|
||||||
|
|
||||||
if (!bar_size_bit) {
|
|
||||||
drm_info(&xe->drm,
|
drm_info(&xe->drm,
|
||||||
"Requested size: %lluMiB is not supported by rebar sizes: 0x%x. Leaving default: %lluMiB\n",
|
"Requested size: %lluMiB is not supported by rebar sizes: 0x%llx. Leaving default: %lluMiB\n",
|
||||||
(u64)rebar_size >> 20, bar_size_mask, (u64)current_size >> 20);
|
(u64)pci_rebar_size_to_bytes(rebar_size) >> 20,
|
||||||
|
pci_rebar_get_possible_sizes(pdev, LMEM_BAR),
|
||||||
|
(u64)current_size >> 20);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rebar_size = 1ULL << (__fls(bar_size_bit) + BAR_SIZE_SHIFT);
|
rebar_size = pci_rebar_size_to_bytes(rebar_size);
|
||||||
|
|
||||||
if (rebar_size == current_size)
|
if (rebar_size == current_size)
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
rebar_size = 1ULL << (__fls(bar_size_mask) + BAR_SIZE_SHIFT);
|
max_size = pci_rebar_get_max_size(pdev, LMEM_BAR);
|
||||||
|
if (max_size < 0)
|
||||||
|
return;
|
||||||
|
rebar_size = pci_rebar_size_to_bytes(max_size);
|
||||||
|
|
||||||
/* only resize if larger than current */
|
/* only resize if larger than current */
|
||||||
if (rebar_size <= current_size)
|
if (rebar_size <= current_size)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
|
obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
|
||||||
remove.o pci.o pci-driver.o search.o \
|
remove.o pci.o pci-driver.o search.o \
|
||||||
rom.o setup-res.o irq.o vpd.o \
|
rebar.o rom.o setup-res.o irq.o vpd.o \
|
||||||
setup-bus.o vc.o mmap.o devres.o
|
setup-bus.o vc.o mmap.o devres.o
|
||||||
|
|
||||||
obj-$(CONFIG_PCI) += msi/
|
obj-$(CONFIG_PCI) += msi/
|
||||||
|
|
|
||||||
|
|
@ -158,8 +158,7 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
|
||||||
return dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)];
|
return dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
|
void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size)
|
||||||
resource_size_t size)
|
|
||||||
{
|
{
|
||||||
if (!pci_resource_is_iov(resno)) {
|
if (!pci_resource_is_iov(resno)) {
|
||||||
pci_warn(dev, "%s is not an IOV resource\n",
|
pci_warn(dev, "%s is not an IOV resource\n",
|
||||||
|
|
@ -167,7 +166,8 @@ void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)] = size;
|
resno = pci_resource_num_to_vf_bar(resno);
|
||||||
|
dev->sriov->barsz[resno] = pci_rebar_size_to_bytes(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
|
bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
|
||||||
|
|
@ -1339,29 +1339,16 @@ EXPORT_SYMBOL_GPL(pci_sriov_configure_simple);
|
||||||
*/
|
*/
|
||||||
int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
|
int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
|
||||||
{
|
{
|
||||||
u32 sizes;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!pci_resource_is_iov(resno))
|
if (!pci_resource_is_iov(resno))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (pci_iov_is_memory_decoding_enabled(dev))
|
if (pci_iov_is_memory_decoding_enabled(dev))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
sizes = pci_rebar_get_possible_sizes(dev, resno);
|
if (!pci_rebar_size_supported(dev, resno, size))
|
||||||
if (!sizes)
|
|
||||||
return -ENOTSUPP;
|
|
||||||
|
|
||||||
if (!(sizes & BIT(size)))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = pci_rebar_set_size(dev, resno, size);
|
return pci_rebar_set_size(dev, resno, size);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
pci_iov_resource_set_size(dev, resno, pci_rebar_size_to_bytes(size));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
|
EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
|
||||||
|
|
||||||
|
|
@ -1380,7 +1367,7 @@ EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
|
||||||
u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs)
|
u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs)
|
||||||
{
|
{
|
||||||
u64 vf_len = pci_resource_len(dev, resno);
|
u64 vf_len = pci_resource_len(dev, resno);
|
||||||
u32 sizes;
|
u64 sizes;
|
||||||
|
|
||||||
if (!num_vfs)
|
if (!num_vfs)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -1587,7 +1587,7 @@ static ssize_t __resource_resize_show(struct device *dev, int n, char *buf)
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
|
|
||||||
ret = sysfs_emit(buf, "%016llx\n",
|
ret = sysfs_emit(buf, "%016llx\n",
|
||||||
(u64)pci_rebar_get_possible_sizes(pdev, n));
|
pci_rebar_get_possible_sizes(pdev, n));
|
||||||
|
|
||||||
pci_config_pm_runtime_put(pdev);
|
pci_config_pm_runtime_put(pdev);
|
||||||
|
|
||||||
|
|
@ -1599,18 +1599,13 @@ static ssize_t __resource_resize_store(struct device *dev, int n,
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = to_pci_dev(dev);
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
struct pci_bus *bus = pdev->bus;
|
struct pci_bus *bus = pdev->bus;
|
||||||
struct resource *b_win, *res;
|
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
int ret, i;
|
int ret;
|
||||||
u16 cmd;
|
u16 cmd;
|
||||||
|
|
||||||
if (kstrtoul(buf, 0, &size) < 0)
|
if (kstrtoul(buf, 0, &size) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
b_win = pbus_select_window(bus, pci_resource_n(pdev, n));
|
|
||||||
if (!b_win)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
device_lock(dev);
|
device_lock(dev);
|
||||||
if (dev->driver || pci_num_vf(pdev)) {
|
if (dev->driver || pci_num_vf(pdev)) {
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
|
|
@ -1632,15 +1627,7 @@ static ssize_t __resource_resize_store(struct device *dev, int n,
|
||||||
|
|
||||||
pci_remove_resource_files(pdev);
|
pci_remove_resource_files(pdev);
|
||||||
|
|
||||||
pci_dev_for_each_resource(pdev, res, i) {
|
ret = pci_resize_resource(pdev, n, size, 0);
|
||||||
if (i >= PCI_BRIDGE_RESOURCES)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (b_win == pbus_select_window(bus, res))
|
|
||||||
pci_release_resource(pdev, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = pci_resize_resource(pdev, n, size);
|
|
||||||
|
|
||||||
pci_assign_unassigned_bus_resources(bus);
|
pci_assign_unassigned_bus_resources(bus);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1823,32 +1823,6 @@ static void pci_restore_config_space(struct pci_dev *pdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_restore_rebar_state(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
unsigned int pos, nbars, i;
|
|
||||||
u32 ctrl;
|
|
||||||
|
|
||||||
pos = pdev->rebar_cap;
|
|
||||||
if (!pos)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
|
||||||
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
|
||||||
|
|
||||||
for (i = 0; i < nbars; i++, pos += 8) {
|
|
||||||
struct resource *res;
|
|
||||||
int bar_idx, size;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
|
||||||
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
|
|
||||||
res = pci_resource_n(pdev, bar_idx);
|
|
||||||
size = pci_rebar_bytes_to_size(resource_size(res));
|
|
||||||
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
|
|
||||||
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
|
|
||||||
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_restore_state - Restore the saved state of a PCI device
|
* pci_restore_state - Restore the saved state of a PCI device
|
||||||
* @dev: PCI device that we're dealing with
|
* @dev: PCI device that we're dealing with
|
||||||
|
|
@ -3684,125 +3658,6 @@ void pci_acs_init(struct pci_dev *dev)
|
||||||
pci_enable_acs(dev);
|
pci_enable_acs(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pci_rebar_init(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pci_rebar_find_pos - find position of resize ctrl reg for BAR
|
|
||||||
* @pdev: PCI device
|
|
||||||
* @bar: BAR to find
|
|
||||||
*
|
|
||||||
* Helper to find the position of the ctrl register for a BAR.
|
|
||||||
* Returns -ENOTSUPP if resizable BARs are not supported at all.
|
|
||||||
* Returns -ENOENT if no ctrl register for the BAR could be found.
|
|
||||||
*/
|
|
||||||
static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
|
|
||||||
{
|
|
||||||
unsigned int pos, nbars, i;
|
|
||||||
u32 ctrl;
|
|
||||||
|
|
||||||
if (pci_resource_is_iov(bar)) {
|
|
||||||
pos = pci_iov_vf_rebar_cap(pdev);
|
|
||||||
bar = pci_resource_num_to_vf_bar(bar);
|
|
||||||
} else {
|
|
||||||
pos = pdev->rebar_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pos)
|
|
||||||
return -ENOTSUPP;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
|
||||||
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
|
||||||
|
|
||||||
for (i = 0; i < nbars; i++, pos += 8) {
|
|
||||||
int bar_idx;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
|
||||||
bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
|
|
||||||
if (bar_idx == bar)
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pci_rebar_get_possible_sizes - get possible sizes for BAR
|
|
||||||
* @pdev: PCI device
|
|
||||||
* @bar: BAR to query
|
|
||||||
*
|
|
||||||
* Get the possible sizes of a resizable BAR as bitmask defined in the spec
|
|
||||||
* (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable.
|
|
||||||
*/
|
|
||||||
u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
u32 cap;
|
|
||||||
|
|
||||||
pos = pci_rebar_find_pos(pdev, bar);
|
|
||||||
if (pos < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
|
|
||||||
cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
|
|
||||||
|
|
||||||
/* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
|
|
||||||
if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
|
|
||||||
bar == 0 && cap == 0x700)
|
|
||||||
return 0x3f00;
|
|
||||||
|
|
||||||
return cap;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pci_rebar_get_current_size - get the current size of a BAR
|
|
||||||
* @pdev: PCI device
|
|
||||||
* @bar: BAR to set size to
|
|
||||||
*
|
|
||||||
* Read the size of a BAR from the resizable BAR config.
|
|
||||||
* Returns size if found or negative error code.
|
|
||||||
*/
|
|
||||||
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
u32 ctrl;
|
|
||||||
|
|
||||||
pos = pci_rebar_find_pos(pdev, bar);
|
|
||||||
if (pos < 0)
|
|
||||||
return pos;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
|
||||||
return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pci_rebar_set_size - set a new size for a BAR
|
|
||||||
* @pdev: PCI device
|
|
||||||
* @bar: BAR to set size to
|
|
||||||
* @size: new size as defined in the spec (0=1MB, 31=128TB)
|
|
||||||
*
|
|
||||||
* Set the new size of a BAR as defined in the spec.
|
|
||||||
* Returns zero if resizing was successful, error code otherwise.
|
|
||||||
*/
|
|
||||||
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
u32 ctrl;
|
|
||||||
|
|
||||||
pos = pci_rebar_find_pos(pdev, bar);
|
|
||||||
if (pos < 0)
|
|
||||||
return pos;
|
|
||||||
|
|
||||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
|
||||||
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
|
|
||||||
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
|
|
||||||
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
|
* pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
|
||||||
* @dev: the PCI device
|
* @dev: the PCI device
|
||||||
|
|
|
||||||
|
|
@ -421,8 +421,10 @@ enum pci_bar_type {
|
||||||
struct device *pci_get_host_bridge_device(struct pci_dev *dev);
|
struct device *pci_get_host_bridge_device(struct pci_dev *dev);
|
||||||
void pci_put_host_bridge_device(struct device *dev);
|
void pci_put_host_bridge_device(struct device *dev);
|
||||||
|
|
||||||
|
void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size);
|
||||||
|
int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size,
|
||||||
|
int exclude_bars);
|
||||||
unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
|
unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
|
||||||
int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res);
|
|
||||||
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
|
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
|
||||||
|
|
||||||
int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
|
int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
|
||||||
|
|
@ -808,8 +810,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno);
|
||||||
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
|
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
|
||||||
void pci_restore_iov_state(struct pci_dev *dev);
|
void pci_restore_iov_state(struct pci_dev *dev);
|
||||||
int pci_iov_bus_range(struct pci_bus *bus);
|
int pci_iov_bus_range(struct pci_bus *bus);
|
||||||
void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
|
void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size);
|
||||||
resource_size_t size);
|
|
||||||
bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
|
bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
|
||||||
static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev)
|
static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
|
|
@ -851,7 +852,7 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
|
static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
|
||||||
resource_size_t size) { }
|
int size) { }
|
||||||
static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
|
static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1020,12 +1021,9 @@ static inline int acpi_get_rc_resources(struct device *dev, const char *hid,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void pci_rebar_init(struct pci_dev *pdev);
|
void pci_rebar_init(struct pci_dev *pdev);
|
||||||
|
void pci_restore_rebar_state(struct pci_dev *pdev);
|
||||||
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
|
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
|
||||||
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
|
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
|
||||||
static inline u64 pci_rebar_size_to_bytes(int size)
|
|
||||||
{
|
|
||||||
return 1ULL << (size + 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct device_node;
|
struct device_node;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,328 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* PCI Resizable BAR Extended Capability handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/log2.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "pci.h"
|
||||||
|
|
||||||
|
#define PCI_REBAR_MIN_SIZE ((resource_size_t)SZ_1M)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_bytes_to_size - Convert size in bytes to PCI BAR Size
|
||||||
|
* @bytes: size in bytes
|
||||||
|
*
|
||||||
|
* Convert size in bytes to encoded BAR Size in Resizable BAR Capability
|
||||||
|
* (PCIe r6.2, sec. 7.8.6.3).
|
||||||
|
*
|
||||||
|
* Return: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB)
|
||||||
|
*/
|
||||||
|
int pci_rebar_bytes_to_size(u64 bytes)
|
||||||
|
{
|
||||||
|
int rebar_minsize = ilog2(PCI_REBAR_MIN_SIZE);
|
||||||
|
|
||||||
|
bytes = roundup_pow_of_two(bytes);
|
||||||
|
|
||||||
|
return max(ilog2(bytes), rebar_minsize) - rebar_minsize;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_rebar_bytes_to_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_size_to_bytes - Convert encoded BAR Size to size in bytes
|
||||||
|
* @size: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB)
|
||||||
|
*
|
||||||
|
* Return: BAR size in bytes
|
||||||
|
*/
|
||||||
|
resource_size_t pci_rebar_size_to_bytes(int size)
|
||||||
|
{
|
||||||
|
return 1ULL << (size + ilog2(PCI_REBAR_MIN_SIZE));
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_rebar_size_to_bytes);
|
||||||
|
|
||||||
|
void pci_rebar_init(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_find_pos - find position of resize control reg for BAR
|
||||||
|
* @pdev: PCI device
|
||||||
|
* @bar: BAR to find
|
||||||
|
*
|
||||||
|
* Helper to find the position of the control register for a BAR.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* * %-ENOTSUPP if resizable BARs are not supported at all,
|
||||||
|
* * %-ENOENT if no control register for the BAR could be found.
|
||||||
|
*/
|
||||||
|
static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
|
||||||
|
{
|
||||||
|
unsigned int pos, nbars, i;
|
||||||
|
u32 ctrl;
|
||||||
|
|
||||||
|
if (pci_resource_is_iov(bar)) {
|
||||||
|
pos = pci_iov_vf_rebar_cap(pdev);
|
||||||
|
bar = pci_resource_num_to_vf_bar(bar);
|
||||||
|
} else {
|
||||||
|
pos = pdev->rebar_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pos)
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||||
|
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
||||||
|
|
||||||
|
for (i = 0; i < nbars; i++, pos += 8) {
|
||||||
|
int bar_idx;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||||
|
bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
|
||||||
|
if (bar_idx == bar)
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_get_possible_sizes - get possible sizes for Resizable BAR
|
||||||
|
* @pdev: PCI device
|
||||||
|
* @bar: BAR to query
|
||||||
|
*
|
||||||
|
* Get the possible sizes of a resizable BAR as bitmask.
|
||||||
|
*
|
||||||
|
* Return: A bitmask of possible sizes (bit 0=1MB, bit 31=128TB), or %0 if
|
||||||
|
* BAR isn't resizable.
|
||||||
|
*/
|
||||||
|
u64 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
u32 cap;
|
||||||
|
|
||||||
|
pos = pci_rebar_find_pos(pdev, bar);
|
||||||
|
if (pos < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
|
||||||
|
cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
|
||||||
|
|
||||||
|
/* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
|
||||||
|
if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
|
||||||
|
bar == 0 && cap == 0x700)
|
||||||
|
return 0x3f00;
|
||||||
|
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_size_supported - check if size is supported for BAR
|
||||||
|
* @pdev: PCI device
|
||||||
|
* @bar: BAR to check
|
||||||
|
* @size: encoded size as defined in the PCIe spec (0=1MB, 31=128TB)
|
||||||
|
*
|
||||||
|
* Return: %true if @bar is resizable and @size is supported, otherwise
|
||||||
|
* %false.
|
||||||
|
*/
|
||||||
|
bool pci_rebar_size_supported(struct pci_dev *pdev, int bar, int size)
|
||||||
|
{
|
||||||
|
u64 sizes = pci_rebar_get_possible_sizes(pdev, bar);
|
||||||
|
|
||||||
|
if (size < 0 || size > ilog2(SZ_128T) - ilog2(PCI_REBAR_MIN_SIZE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return BIT(size) & sizes;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_rebar_size_supported);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_get_max_size - get the maximum supported size of a BAR
|
||||||
|
* @pdev: PCI device
|
||||||
|
* @bar: BAR to query
|
||||||
|
*
|
||||||
|
* Get the largest supported size of a resizable BAR as a size.
|
||||||
|
*
|
||||||
|
* Return: the encoded maximum BAR size as defined in the PCIe spec
|
||||||
|
* (0=1MB, 31=128TB), or %-NOENT on error.
|
||||||
|
*/
|
||||||
|
int pci_rebar_get_max_size(struct pci_dev *pdev, int bar)
|
||||||
|
{
|
||||||
|
u64 sizes;
|
||||||
|
|
||||||
|
sizes = pci_rebar_get_possible_sizes(pdev, bar);
|
||||||
|
if (!sizes)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
return __fls(sizes);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_rebar_get_max_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_get_current_size - get the current size of a Resizable BAR
|
||||||
|
* @pdev: PCI device
|
||||||
|
* @bar: BAR to get the size from
|
||||||
|
*
|
||||||
|
* Read the current size of a BAR from the Resizable BAR config.
|
||||||
|
*
|
||||||
|
* Return: BAR Size if @bar is resizable (0=1MB, 31=128TB), or negative on
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
u32 ctrl;
|
||||||
|
|
||||||
|
pos = pci_rebar_find_pos(pdev, bar);
|
||||||
|
if (pos < 0)
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||||
|
return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_rebar_set_size - set a new size for a Resizable BAR
|
||||||
|
* @pdev: PCI device
|
||||||
|
* @bar: BAR to set size to
|
||||||
|
* @size: new size as defined in the PCIe spec (0=1MB, 31=128TB)
|
||||||
|
*
|
||||||
|
* Set the new size of a BAR as defined in the spec.
|
||||||
|
*
|
||||||
|
* Return: %0 if resizing was successful, or negative on error.
|
||||||
|
*/
|
||||||
|
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
u32 ctrl;
|
||||||
|
|
||||||
|
pos = pci_rebar_find_pos(pdev, bar);
|
||||||
|
if (pos < 0)
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||||
|
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
|
||||||
|
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
|
||||||
|
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
|
||||||
|
|
||||||
|
if (pci_resource_is_iov(bar))
|
||||||
|
pci_iov_resource_set_size(pdev, bar, size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_restore_rebar_state(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
unsigned int pos, nbars, i;
|
||||||
|
u32 ctrl;
|
||||||
|
|
||||||
|
pos = pdev->rebar_cap;
|
||||||
|
if (!pos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||||
|
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
||||||
|
|
||||||
|
for (i = 0; i < nbars; i++, pos += 8) {
|
||||||
|
struct resource *res;
|
||||||
|
int bar_idx, size;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||||
|
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
|
||||||
|
res = pci_resource_n(pdev, bar_idx);
|
||||||
|
size = pci_rebar_bytes_to_size(resource_size(res));
|
||||||
|
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
|
||||||
|
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
|
||||||
|
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
|
||||||
|
int resno)
|
||||||
|
{
|
||||||
|
u16 cmd;
|
||||||
|
|
||||||
|
if (pci_resource_is_iov(resno))
|
||||||
|
return pci_iov_is_memory_decoding_enabled(dev);
|
||||||
|
|
||||||
|
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||||
|
|
||||||
|
return cmd & PCI_COMMAND_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size)
|
||||||
|
{
|
||||||
|
resource_size_t res_size = pci_rebar_size_to_bytes(size);
|
||||||
|
struct resource *res = pci_resource_n(dev, resno);
|
||||||
|
|
||||||
|
if (pci_resource_is_iov(resno))
|
||||||
|
res_size *= pci_sriov_get_totalvfs(dev);
|
||||||
|
|
||||||
|
resource_set_size(res, res_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_resize_resource - reconfigure a Resizable BAR and resources
|
||||||
|
* @dev: the PCI device
|
||||||
|
* @resno: index of the BAR to be resized
|
||||||
|
* @size: new size as defined in the spec (0=1MB, 31=128TB)
|
||||||
|
* @exclude_bars: a mask of BARs that should not be released
|
||||||
|
*
|
||||||
|
* Reconfigure @resno to @size and re-run resource assignment algorithm
|
||||||
|
* with the new size.
|
||||||
|
*
|
||||||
|
* Prior to resize, release @dev resources that share a bridge window with
|
||||||
|
* @resno. This unpins the bridge window resource to allow changing it.
|
||||||
|
*
|
||||||
|
* The caller may prevent releasing a particular BAR by providing
|
||||||
|
* @exclude_bars mask, but this may result in the resize operation failing
|
||||||
|
* due to insufficient space.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or negative on error. In case of an error, the
|
||||||
|
* resources are restored to their original places.
|
||||||
|
*/
|
||||||
|
int pci_resize_resource(struct pci_dev *dev, int resno, int size,
|
||||||
|
int exclude_bars)
|
||||||
|
{
|
||||||
|
struct pci_host_bridge *host;
|
||||||
|
int old, ret;
|
||||||
|
|
||||||
|
/* Check if we must preserve the firmware's resource assignment */
|
||||||
|
host = pci_find_host_bridge(dev->bus);
|
||||||
|
if (host->preserve_config)
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
if (pci_resize_is_memory_decoding_enabled(dev, resno))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (!pci_rebar_size_supported(dev, resno, size))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
old = pci_rebar_get_current_size(dev, resno);
|
||||||
|
if (old < 0)
|
||||||
|
return old;
|
||||||
|
|
||||||
|
ret = pci_rebar_set_size(dev, resno, size);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
|
||||||
|
if (ret)
|
||||||
|
goto error_resize;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_resize:
|
||||||
|
pci_rebar_set_size(dev, resno, old);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pci_resize_resource);
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
@ -135,6 +136,9 @@ static void restore_dev_resource(struct pci_dev_resource *dev_res)
|
||||||
{
|
{
|
||||||
struct resource *res = dev_res->res;
|
struct resource *res = dev_res->res;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(res->parent))
|
||||||
|
return;
|
||||||
|
|
||||||
res->start = dev_res->start;
|
res->start = dev_res->start;
|
||||||
res->end = dev_res->end;
|
res->end = dev_res->end;
|
||||||
res->flags = dev_res->flags;
|
res->flags = dev_res->flags;
|
||||||
|
|
@ -2420,18 +2424,16 @@ EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
|
||||||
* release it when possible. If the bridge window contains assigned
|
* release it when possible. If the bridge window contains assigned
|
||||||
* resources, it cannot be released.
|
* resources, it cannot be released.
|
||||||
*/
|
*/
|
||||||
int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
|
static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res,
|
||||||
|
struct list_head *saved)
|
||||||
{
|
{
|
||||||
unsigned long type = res->flags;
|
unsigned long type = res->flags;
|
||||||
struct pci_dev_resource *dev_res;
|
struct pci_dev_resource *dev_res;
|
||||||
struct pci_dev *bridge;
|
struct pci_dev *bridge = NULL;
|
||||||
LIST_HEAD(saved);
|
|
||||||
LIST_HEAD(added);
|
LIST_HEAD(added);
|
||||||
LIST_HEAD(failed);
|
LIST_HEAD(failed);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
down_read(&pci_bus_sem);
|
|
||||||
|
|
||||||
while (!pci_is_root_bus(bus)) {
|
while (!pci_is_root_bus(bus)) {
|
||||||
bridge = bus->self;
|
bridge = bus->self;
|
||||||
|
|
@ -2443,9 +2445,9 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
|
||||||
|
|
||||||
/* Ignore BARs which are still in use */
|
/* Ignore BARs which are still in use */
|
||||||
if (!res->child) {
|
if (!res->child) {
|
||||||
ret = add_to_list(&saved, bridge, res, 0, 0);
|
ret = add_to_list(saved, bridge, res, 0, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto cleanup;
|
return ret;
|
||||||
|
|
||||||
pci_release_resource(bridge, i);
|
pci_release_resource(bridge, i);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2459,10 +2461,8 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
|
||||||
bus = bus->parent;
|
bus = bus->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list_empty(&saved)) {
|
if (!bridge)
|
||||||
up_read(&pci_bus_sem);
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
|
||||||
|
|
||||||
__pci_bus_size_bridges(bridge->subordinate, &added);
|
__pci_bus_size_bridges(bridge->subordinate, &added);
|
||||||
__pci_bridge_assign_resources(bridge, &added, &failed);
|
__pci_bridge_assign_resources(bridge, &added, &failed);
|
||||||
|
|
@ -2470,49 +2470,107 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
|
||||||
free_list(&added);
|
free_list(&added);
|
||||||
|
|
||||||
if (!list_empty(&failed)) {
|
if (!list_empty(&failed)) {
|
||||||
if (pci_required_resource_failed(&failed, type)) {
|
if (pci_required_resource_failed(&failed, type))
|
||||||
ret = -ENOSPC;
|
ret = -ENOSPC;
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
/* Only resources with unrelated types failed (again) */
|
|
||||||
free_list(&failed);
|
free_list(&failed);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Only resources with unrelated types failed (again) */
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(dev_res, &saved, list) {
|
list_for_each_entry(dev_res, saved, list) {
|
||||||
|
struct pci_dev *dev = dev_res->dev;
|
||||||
|
|
||||||
/* Skip the bridge we just assigned resources for */
|
/* Skip the bridge we just assigned resources for */
|
||||||
if (bridge == dev_res->dev)
|
if (bridge == dev)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bridge = dev_res->dev;
|
if (!dev->subordinate)
|
||||||
pci_setup_bridge(bridge->subordinate);
|
continue;
|
||||||
|
|
||||||
|
pci_setup_bridge(dev->subordinate);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_list(&saved);
|
|
||||||
up_read(&pci_bus_sem);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
cleanup:
|
int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size,
|
||||||
/* Restore size and flags */
|
int exclude_bars)
|
||||||
list_for_each_entry(dev_res, &failed, list)
|
{
|
||||||
restore_dev_resource(dev_res);
|
struct resource *res = pci_resource_n(pdev, resno);
|
||||||
free_list(&failed);
|
struct pci_dev_resource *dev_res;
|
||||||
|
struct pci_bus *bus = pdev->bus;
|
||||||
|
struct resource *b_win, *r;
|
||||||
|
LIST_HEAD(saved);
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
b_win = pbus_select_window(bus, res);
|
||||||
|
if (!b_win)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pci_dev_for_each_resource(pdev, r, i) {
|
||||||
|
if (i >= PCI_BRIDGE_RESOURCES)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (exclude_bars & BIT(i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (b_win != pbus_select_window(bus, r))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = add_to_list(&saved, pdev, r, 0, 0);
|
||||||
|
if (ret)
|
||||||
|
goto restore;
|
||||||
|
pci_release_resource(pdev, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_resize_resource_set_size(pdev, resno, size);
|
||||||
|
|
||||||
|
if (!bus->self)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
down_read(&pci_bus_sem);
|
||||||
|
ret = pbus_reassign_bridge_resources(bus, res, &saved);
|
||||||
|
if (ret)
|
||||||
|
goto restore;
|
||||||
|
|
||||||
|
out:
|
||||||
|
up_read(&pci_bus_sem);
|
||||||
|
free_list(&saved);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
restore:
|
||||||
/* Revert to the old configuration */
|
/* Revert to the old configuration */
|
||||||
list_for_each_entry(dev_res, &saved, list) {
|
list_for_each_entry(dev_res, &saved, list) {
|
||||||
struct resource *res = dev_res->res;
|
struct resource *res = dev_res->res;
|
||||||
|
struct pci_dev *dev = dev_res->dev;
|
||||||
|
|
||||||
bridge = dev_res->dev;
|
i = pci_resource_num(dev, res);
|
||||||
i = pci_resource_num(bridge, res);
|
|
||||||
|
if (res->parent) {
|
||||||
|
release_child_resources(res);
|
||||||
|
pci_release_resource(dev, i);
|
||||||
|
}
|
||||||
|
|
||||||
restore_dev_resource(dev_res);
|
restore_dev_resource(dev_res);
|
||||||
|
|
||||||
pci_claim_resource(bridge, i);
|
ret = pci_claim_resource(dev, i);
|
||||||
pci_setup_bridge(bridge->subordinate);
|
if (ret)
|
||||||
}
|
continue;
|
||||||
free_list(&saved);
|
|
||||||
up_read(&pci_bus_sem);
|
|
||||||
|
|
||||||
return ret;
|
if (i < PCI_BRIDGE_RESOURCES) {
|
||||||
|
const char *res_name = pci_resource_name(dev, i);
|
||||||
|
|
||||||
|
pci_update_resource(dev, i);
|
||||||
|
pci_info(dev, "%s %pR: old value restored\n",
|
||||||
|
res_name, res);
|
||||||
|
}
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_setup_bridge(dev->subordinate);
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
|
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
|
||||||
|
|
|
||||||
|
|
@ -431,84 +431,6 @@ int pci_release_resource(struct pci_dev *dev, int resno)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_release_resource);
|
EXPORT_SYMBOL(pci_release_resource);
|
||||||
|
|
||||||
static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
|
|
||||||
int resno)
|
|
||||||
{
|
|
||||||
u16 cmd;
|
|
||||||
|
|
||||||
if (pci_resource_is_iov(resno))
|
|
||||||
return pci_iov_is_memory_decoding_enabled(dev);
|
|
||||||
|
|
||||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
|
||||||
|
|
||||||
return cmd & PCI_COMMAND_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pci_resize_resource_set_size(struct pci_dev *dev, int resno,
|
|
||||||
int size)
|
|
||||||
{
|
|
||||||
resource_size_t res_size = pci_rebar_size_to_bytes(size);
|
|
||||||
struct resource *res = pci_resource_n(dev, resno);
|
|
||||||
|
|
||||||
if (!pci_resource_is_iov(resno)) {
|
|
||||||
resource_set_size(res, res_size);
|
|
||||||
} else {
|
|
||||||
resource_set_size(res, res_size * pci_sriov_get_totalvfs(dev));
|
|
||||||
pci_iov_resource_set_size(dev, resno, res_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int pci_resize_resource(struct pci_dev *dev, int resno, int size)
|
|
||||||
{
|
|
||||||
struct resource *res = pci_resource_n(dev, resno);
|
|
||||||
struct pci_host_bridge *host;
|
|
||||||
int old, ret;
|
|
||||||
u32 sizes;
|
|
||||||
|
|
||||||
/* Check if we must preserve the firmware's resource assignment */
|
|
||||||
host = pci_find_host_bridge(dev->bus);
|
|
||||||
if (host->preserve_config)
|
|
||||||
return -ENOTSUPP;
|
|
||||||
|
|
||||||
/* Make sure the resource isn't assigned before resizing it. */
|
|
||||||
if (!(res->flags & IORESOURCE_UNSET))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
if (pci_resize_is_memory_decoding_enabled(dev, resno))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
sizes = pci_rebar_get_possible_sizes(dev, resno);
|
|
||||||
if (!sizes)
|
|
||||||
return -ENOTSUPP;
|
|
||||||
|
|
||||||
if (!(sizes & BIT(size)))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
old = pci_rebar_get_current_size(dev, resno);
|
|
||||||
if (old < 0)
|
|
||||||
return old;
|
|
||||||
|
|
||||||
ret = pci_rebar_set_size(dev, resno, size);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
pci_resize_resource_set_size(dev, resno, size);
|
|
||||||
|
|
||||||
/* Check if the new config works by trying to assign everything. */
|
|
||||||
if (dev->bus->self) {
|
|
||||||
ret = pbus_reassign_bridge_resources(dev->bus, res);
|
|
||||||
if (ret)
|
|
||||||
goto error_resize;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error_resize:
|
|
||||||
pci_rebar_set_size(dev, resno, old);
|
|
||||||
pci_resize_resource_set_size(dev, resno, old);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(pci_resize_resource);
|
|
||||||
|
|
||||||
int pci_enable_resources(struct pci_dev *dev, int mask)
|
int pci_enable_resources(struct pci_dev *dev, int mask)
|
||||||
{
|
{
|
||||||
u16 cmd, old_cmd;
|
u16 cmd, old_cmd;
|
||||||
|
|
|
||||||
|
|
@ -1421,16 +1421,16 @@ void pcibios_reset_secondary_bus(struct pci_dev *dev);
|
||||||
void pci_update_resource(struct pci_dev *dev, int resno);
|
void pci_update_resource(struct pci_dev *dev, int resno);
|
||||||
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
||||||
int pci_release_resource(struct pci_dev *dev, int resno);
|
int pci_release_resource(struct pci_dev *dev, int resno);
|
||||||
static inline int pci_rebar_bytes_to_size(u64 bytes)
|
|
||||||
{
|
|
||||||
bytes = roundup_pow_of_two(bytes);
|
|
||||||
|
|
||||||
/* Return BAR size as defined in the resizable BAR specification */
|
/* Resizable BAR related routines */
|
||||||
return max(ilog2(bytes), 20) - 20;
|
int pci_rebar_bytes_to_size(u64 bytes);
|
||||||
}
|
resource_size_t pci_rebar_size_to_bytes(int size);
|
||||||
|
u64 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
|
||||||
|
bool pci_rebar_size_supported(struct pci_dev *pdev, int bar, int size);
|
||||||
|
int pci_rebar_get_max_size(struct pci_dev *pdev, int bar);
|
||||||
|
int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size,
|
||||||
|
int exclude_bars);
|
||||||
|
|
||||||
u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
|
|
||||||
int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
|
|
||||||
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
|
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
|
||||||
bool pci_device_is_present(struct pci_dev *pdev);
|
bool pci_device_is_present(struct pci_dev *pdev);
|
||||||
void pci_ignore_hotplug(struct pci_dev *dev);
|
void pci_ignore_hotplug(struct pci_dev *dev);
|
||||||
|
|
|
||||||
|
|
@ -67,5 +67,6 @@
|
||||||
#define SZ_16T _AC(0x100000000000, ULL)
|
#define SZ_16T _AC(0x100000000000, ULL)
|
||||||
#define SZ_32T _AC(0x200000000000, ULL)
|
#define SZ_32T _AC(0x200000000000, ULL)
|
||||||
#define SZ_64T _AC(0x400000000000, ULL)
|
#define SZ_64T _AC(0x400000000000, ULL)
|
||||||
|
#define SZ_128T _AC(0x800000000000, ULL)
|
||||||
|
|
||||||
#endif /* __LINUX_SIZES_H__ */
|
#endif /* __LINUX_SIZES_H__ */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue