summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c171
1 files changed, 129 insertions, 42 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
index e8d180a412d1..9e8f7d2b898c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
@@ -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,26 +512,31 @@ 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);
}
}
static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
{
struct amd_sriov_msg_pf2vf_info_header *pf2vf_info = adev->virt.fw_reserve.p_pf2vf;
+ struct amdgim_pf2vf_info_v1 *pf2vf_v1;
+ struct amd_sriov_msg_pf2vf_info *pf2vf;
+
uint32_t checksum;
uint32_t checkval;
@@ -479,7 +553,8 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
switch (pf2vf_info->version) {
case 1:
- checksum = ((struct amdgim_pf2vf_info_v1 *)pf2vf_info)->checksum;
+ pf2vf_v1 = (struct amdgim_pf2vf_info_v1 *)pf2vf_info;
+ checksum = pf2vf_v1->checksum;
checkval = amd_sriov_msg_checksum(
adev->virt.fw_reserve.p_pf2vf, pf2vf_info->size,
adev->virt.fw_reserve.checksum_key, checksum);
@@ -490,12 +565,12 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
return -EINVAL;
}
- adev->virt.gim_feature =
- ((struct amdgim_pf2vf_info_v1 *)pf2vf_info)->feature_flags;
+ adev->virt.gim_feature = pf2vf_v1->feature_flags;
break;
case 2:
/* TODO: missing key, need to add it later */
- checksum = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->checksum;
+ pf2vf = (struct amd_sriov_msg_pf2vf_info *)pf2vf_info;
+ checksum = pf2vf->checksum;
checkval = amd_sriov_msg_checksum(
adev->virt.fw_reserve.p_pf2vf, pf2vf_info->size,
0, checksum);
@@ -507,11 +582,9 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
}
adev->virt.vf2pf_update_interval_ms =
- ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->vf2pf_update_interval_ms;
- adev->virt.gim_feature =
- ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->feature_flags.all;
- adev->virt.reg_access =
- ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->reg_access_flags.all;
+ pf2vf->vf2pf_update_interval_ms;
+ adev->virt.gim_feature = pf2vf->feature_flags.all;
+ adev->virt.reg_access = pf2vf->reg_access_flags.all;
adev->virt.decode_max_dimension_pixels = 0;
adev->virt.decode_max_frame_pixels = 0;
@@ -519,26 +592,30 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
adev->virt.encode_max_frame_pixels = 0;
adev->virt.is_mm_bw_enabled = false;
for (i = 0; i < AMD_SRIOV_MSG_RESERVE_VCN_INST; i++) {
- tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].decode_max_dimension_pixels;
+ tmp = pf2vf->mm_bw_management[i].decode_max_dimension_pixels;
adev->virt.decode_max_dimension_pixels = max(tmp, adev->virt.decode_max_dimension_pixels);
- tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].decode_max_frame_pixels;
+ tmp = pf2vf->mm_bw_management[i].decode_max_frame_pixels;
adev->virt.decode_max_frame_pixels = max(tmp, adev->virt.decode_max_frame_pixels);
- tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].encode_max_dimension_pixels;
+ tmp = pf2vf->mm_bw_management[i].encode_max_dimension_pixels;
adev->virt.encode_max_dimension_pixels = max(tmp, adev->virt.encode_max_dimension_pixels);
- tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].encode_max_frame_pixels;
+ tmp = pf2vf->mm_bw_management[i].encode_max_frame_pixels;
adev->virt.encode_max_frame_pixels = max(tmp, adev->virt.encode_max_frame_pixels);
}
if ((adev->virt.decode_max_dimension_pixels > 0) || (adev->virt.encode_max_dimension_pixels > 0))
adev->virt.is_mm_bw_enabled = true;
- adev->unique_id =
- ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->uuid;
- adev->virt.ras_en_caps.all = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->ras_en_caps.all;
+ adev->unique_id = pf2vf->uuid;
+
+ adev->unitid = 0;
+ if (amdgpu_sriov_is_unitid_support(adev))
+ adev->unitid = pf2vf->unitid;
+
+ adev->virt.ras_en_caps.all = pf2vf->ras_en_caps.all;
adev->virt.ras_telemetry_en_caps.all =
- ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->ras_telemetry_en_caps.all;
+ pf2vf->ras_telemetry_en_caps.all;
break;
default:
dev_err(adev->dev, "invalid pf2vf version: 0x%x\n", pf2vf_info->version);
@@ -1798,13 +1875,15 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
struct amd_sriov_ras_cper_dump *cper_dump = NULL;
struct cper_hdr *entry = NULL;
struct amdgpu_ring *ring = &adev->cper.ring_buf;
- uint32_t checksum, used_size, i;
+ uint32_t checksum, used_size;
+ u64 remaining, cnt, i;
int ret = 0;
checksum = host_telemetry->header.checksum;
used_size = host_telemetry->header.used_size;
- if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
+ if (used_size < offsetof(struct amd_sriov_ras_cper_dump, buf) ||
+ used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
return -EINVAL;
cper_dump = kmemdup(&host_telemetry->body.cper_dump, used_size, GFP_KERNEL);
@@ -1829,11 +1908,19 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
}
entry = (struct cper_hdr *)&cper_dump->buf[0];
+ remaining = (u64)used_size - offsetof(struct amd_sriov_ras_cper_dump, buf);
+ cnt = min_t(u64, cper_dump->count, CPER_MAX_ALLOWED_COUNT);
+
+ for (i = 0; i < cnt; i++) {
+ if (entry->record_length < sizeof(struct cper_hdr) ||
+ entry->record_length > remaining) {
+ ret = -EINVAL;
+ goto out;
+ }
- for (i = 0; i < cper_dump->count; i++) {
amdgpu_cper_ring_write(ring, entry, entry->record_length);
- entry = (struct cper_hdr *)((char *)entry +
- entry->record_length);
+ remaining -= entry->record_length;
+ entry = (struct cper_hdr *)((char *)entry + entry->record_length);
}
if (cper_dump->overflow_count)