drm/amdgpu: grow VF RAS bad page table with bounded dynamic alloc

The VF RAS error handler used fixed-size bps[] / bps_bo[] arrays (512
slots). When the PF2VF bad-page block listed more entries than fit,
amdgpu_virt_ras_add_bps() could memcpy() past the end of those arrays.

Replace the fixed backing store with a dynamically grown table:
- Add capacity to track allocated slots separately from count.
- Start at 512 slots and realloc bps / bps_bo together when full.
- Refuse growth beyond maximum EEPROM record limit (AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY).
- Return failure from amdgpu_virt_ras_add_bps() and stop processing
  the PF2VF block if allocation fails or the cap is reached.

Signed-off-by: Chenglei Xie <Chenglei.Xie@amd.com>
Reviewed-by: YiPeng Chai <YiPeng.Chai@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Chenglei Xie 2026-05-07 10:29:10 -04:00 committed by Alex Deucher
parent 758a868043
commit 10358a396d
2 changed files with 93 additions and 20 deletions

View File

@ -283,14 +283,66 @@ unsigned int amd_sriov_msg_checksum(void *obj,
return ret;
}
#define AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_INIT_CAPACITY 512
/* Max bad page slots allowed for SRIOV*/
#define AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY 10665U
/**
* amdgpu_virt_ras_realloc_eh_data_space - alloc/realloc VF bad-page @data->bps and @data->bps_bo
* @adev: amdgpu device
* @data: VF RAS error-handler data
* @pages: minimum number of new slots to add beyond @data->capacity
*
* Return: 0 on success, %-ENOMEM on failure.
*/
static int amdgpu_virt_ras_realloc_eh_data_space(struct amdgpu_device *adev,
struct amdgpu_virt_ras_err_handler_data *data,
int pages)
{
struct eeprom_table_record *new_bps;
struct amdgpu_bo **new_bo;
unsigned int old_space;
unsigned int new_space;
unsigned int align_space;
old_space = (unsigned int)data->capacity;
new_space = old_space + max_t(unsigned int, (unsigned int)pages,
(unsigned int)AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_INIT_CAPACITY);
if (new_space < old_space || new_space > AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY)
return -ENOMEM;
align_space = ALIGN(new_space, AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_INIT_CAPACITY);
if (align_space > AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY)
return -ENOMEM;
new_bps = kmalloc_array(align_space, sizeof(*data->bps), GFP_KERNEL);
new_bo = kcalloc(align_space, sizeof(*data->bps_bo), GFP_KERNEL);
if (!new_bps || !new_bo) {
kfree(new_bps);
kfree(new_bo);
dev_warn_ratelimited(adev->dev,
"RAS WARN: failed to grow bad page table to %u slots\n",
align_space);
return -ENOMEM;
}
memcpy(new_bps, data->bps, data->count * sizeof(*data->bps));
memcpy(new_bo, data->bps_bo, data->count * sizeof(*data->bps_bo));
kfree(data->bps);
kfree(data->bps_bo);
data->bps = new_bps;
data->bps_bo = new_bo;
data->capacity = (int)align_space;
return 0;
}
static int amdgpu_virt_init_ras_err_handler_data(struct amdgpu_device *adev)
{
struct amdgpu_virt *virt = &adev->virt;
struct amdgpu_virt_ras_err_handler_data **data = &virt->virt_eh_data;
/* GPU will be marked bad on host if bp count more then 10,
* so alloc 512 is enough.
*/
unsigned int align_space = 512;
unsigned int align_space = AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_INIT_CAPACITY;
void *bps = NULL;
struct amdgpu_bo **bps_bo = NULL;
@ -302,12 +354,13 @@ static int amdgpu_virt_init_ras_err_handler_data(struct amdgpu_device *adev)
if (!bps)
goto bps_failure;
bps_bo = kmalloc_objs(*(*data)->bps_bo, align_space);
bps_bo = kcalloc(align_space, sizeof(*(*data)->bps_bo), GFP_KERNEL);
if (!bps_bo)
goto bps_bo_failure;
(*data)->bps = bps;
(*data)->bps_bo = bps_bo;
(*data)->capacity = align_space;
(*data)->count = 0;
(*data)->last_reserved = 0;
@ -361,17 +414,33 @@ void amdgpu_virt_release_ras_err_handler_data(struct amdgpu_device *adev)
virt->virt_eh_data = NULL;
}
static void amdgpu_virt_ras_add_bps(struct amdgpu_device *adev,
struct eeprom_table_record *bps, int pages)
static bool amdgpu_virt_ras_add_bps(struct amdgpu_device *adev,
const struct eeprom_table_record *bps, int pages)
{
struct amdgpu_virt *virt = &adev->virt;
struct amdgpu_virt_ras_err_handler_data *data = virt->virt_eh_data;
int need;
if (!data)
return;
if (!data || pages <= 0)
return false;
if (pages > AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY - data->count) {
dev_warn_ratelimited(adev->dev,
"RAS WARN: bad page table at capacity (count=%d pages=%d max=%u)\n",
data->count, pages,
AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY);
return false;
}
need = data->count + pages;
if (need > data->capacity &&
amdgpu_virt_ras_realloc_eh_data_space(adev, data, need - data->capacity))
return false;
memcpy(&data->bps[data->count], bps, pages * sizeof(*data->bps));
data->count += pages;
return true;
}
static void amdgpu_virt_ras_reserve_bps(struct amdgpu_device *adev)
@ -443,20 +512,22 @@ static void amdgpu_virt_add_bad_page(struct amdgpu_device *adev,
memset(&bp, 0, sizeof(bp));
if (bp_block_size) {
bp_cnt = bp_block_size / sizeof(uint64_t);
for (bp_idx = 0; bp_idx < bp_cnt; bp_idx++) {
retired_page = *(uint64_t *)(vram_usage_va +
bp_block_offset + bp_idx * sizeof(uint64_t));
bp.retired_page = retired_page;
if (!bp_block_size)
return;
if (amdgpu_virt_ras_check_bad_page(adev, retired_page))
continue;
bp_cnt = bp_block_size / sizeof(uint64_t);
for (bp_idx = 0; bp_idx < bp_cnt; bp_idx++) {
retired_page = *(uint64_t *)(vram_usage_va +
bp_block_offset + bp_idx * sizeof(uint64_t));
bp.retired_page = retired_page;
amdgpu_virt_ras_add_bps(adev, &bp, 1);
if (amdgpu_virt_ras_check_bad_page(adev, retired_page))
continue;
amdgpu_virt_ras_reserve_bps(adev);
}
if (!amdgpu_virt_ras_add_bps(adev, &bp, 1))
break;
amdgpu_virt_ras_reserve_bps(adev);
}
}

View File

@ -265,6 +265,8 @@ struct amdgpu_virt_ras_err_handler_data {
struct eeprom_table_record *bps;
/* point to reserved bo array */
struct amdgpu_bo **bps_bo;
/* number of slots in bps[] / bps_bo[] (always >= count) */
int capacity;
/* the count of entries */
int count;
/* last reserved entry's index + 1 */