/* * Copyright 2018 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * */#include "amdgpu_reg_access.h"#include <linux/debugfs.h>#include <linux/list.h>#include <linux/module.h>#include <linux/uaccess.h>#include <linux/reboot.h>#include <linux/syscalls.h>#include <linux/pm_runtime.h>#include <linux/list_sort.h> #include "amdgpu.h"#include "amdgpu_ras.h"#include "amdgpu_atomfirmware.h"#include "amdgpu_xgmi.h"#include "ivsrcid/nbio/irqsrcs_nbif_7_4.h"#include "nbio_v4_3.h"#include "nbif_v6_3_1.h"#include "nbio_v7_9.h"#include "atom.h"#include "amdgpu_reset.h"#include "amdgpu_psp.h"#include "amdgpu_ras_mgr.h"#include "amdgpu_virt_ras_cmd.h" #ifdef CONFIG_X86_MCE_AMD#include <asm/mce.h> static bool notifier_registered;#endifstatic const char *RAS_FS_NAME = "ras"; const char *ras_error_string[] = { "none", "parity", "single_correctable", "multi_uncorrectable", "poison",}; const char *ras_block_string[] = { "umc", "sdma", "gfx", "mmhub", "athub", "pcie_bif", "hdp", "xgmi_wafl", "df", "smn", "sem", "mp0", "mp1", "fuse", "mca", "vcn", "jpeg", "ih", "mpio", "mmsch",}; const char *ras_mca_block_string[] = { "mca_mp0", "mca_mp1", "mca_mpio", "mca_iohc",}; struct amdgpu_ras_block_list { /* ras block link */ struct list_head node; struct amdgpu_ras_block_object *ras_obj; /* set by ras_late_init, cleared by ras_suspend/ras_fini */ bool active;}; const char *get_ras_block_str(struct ras_common_if *ras_block){ if (!ras_block) return "NULL"; if (ras_block->block >= AMDGPU_RAS_BLOCK_COUNT || ras_block->block >= ARRAY_SIZE(ras_block_string)) return "OUT OF RANGE"; if (ras_block->block == AMDGPU_RAS_BLOCK__MCA) return ras_mca_block_string[ras_block->sub_block_index]; return ras_block_string[ras_block->block];} #define ras_block_str(_BLOCK_) \ (((_BLOCK_) < ARRAY_SIZE(ras_block_string)) ? ras_block_string[_BLOCK_] : "Out Of Range") #define ras_err_str(i) (ras_error_string[ffs(i)]) #define RAS_DEFAULT_FLAGS (AMDGPU_RAS_FLAG_INIT_BY_VBIOS) /* inject address is 52 bits */#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52) /* typical ECC bad page rate is 1 bad page per 100MB VRAM */#define RAS_BAD_PAGE_COVER (100 * 1024 * 1024ULL) #define BYPASS_ALLOCATED_ADDRESS 0x0#define BYPASS_INITIALIZATION_ADDRESS 0x1 enum amdgpu_ras_retire_page_reservation { AMDGPU_RAS_RETIRE_PAGE_RESERVED, AMDGPU_RAS_RETIRE_PAGE_PENDING, AMDGPU_RAS_RETIRE_PAGE_FAULT,}; atomic_t amdgpu_ras_in_intr = ATOMIC_INIT(0); static int amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, uint64_t addr);static int amdgpu_ras_check_bad_page(struct amdgpu_device *adev, uint64_t addr); static void amdgpu_ras_critical_region_init(struct amdgpu_device *adev);static void amdgpu_ras_critical_region_fini(struct amdgpu_device *adev); #ifdef CONFIG_X86_MCE_AMDstatic void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev);static voidamdgpu_unregister_bad_pages_mca_notifier(struct amdgpu_device *adev);struct mce_notifier_adev_list { struct amdgpu_device *devs[MAX_GPU_INSTANCE]; int num_gpu;};static struct mce_notifier_adev_list mce_adev_list;#endif void amdgpu_ras_set_error_query_ready(struct amdgpu_device *adev, bool ready){ if (adev && amdgpu_ras_get_context(adev)) amdgpu_ras_get_context(adev)->error_query_ready = ready;} static bool amdgpu_ras_get_error_query_ready(struct amdgpu_device *adev){ if (adev && amdgpu_ras_get_context(adev)) return amdgpu_ras_get_context(adev)->error_query_ready; return false;} static int amdgpu_reserve_page_direct(struct amdgpu_device *adev, uint64_t address){ struct ras_err_data err_data; struct eeprom_table_record err_rec; int ret; ret = amdgpu_ras_check_bad_page(adev, address); if (ret == -EINVAL) { dev_warn(adev->dev, "RAS WARN: input address 0x%llx is invalid.\n", address); return -EINVAL; } else if (ret == 1) { dev_warn(adev->dev, "RAS WARN: 0x%llx has already been marked as bad page!\n", address); return 0; } ret = amdgpu_ras_error_data_init(&err_data); if (ret) return ret; memset(&err_rec, 0x0, sizeof(struct eeprom_table_record)); err_data.err_addr = &err_rec; amdgpu_umc_fill_error_record(&err_data, address, address, 0, 0); if (amdgpu_bad_page_threshold != 0) { amdgpu_ras_add_bad_pages(adev, err_data.err_addr, err_data.err_addr_cnt, false); amdgpu_ras_save_bad_pages(adev, NULL); } amdgpu_ras_error_data_fini(&err_data); dev_warn(adev->dev, "WARNING: THIS IS ONLY FOR TEST PURPOSES AND WILL CORRUPT RAS EEPROM\n"); dev_warn(adev->dev, "Clear EEPROM:\n"); dev_warn(adev->dev, " echo 1 > /sys/kernel/debug/dri/0/ras/ras_eeprom_reset\n"); return 0;} static int amdgpu_check_address_validity(struct amdgpu_device *adev, uint64_t address, uint64_t flags){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct amdgpu_vram_block_info blk_info; uint64_t page_pfns[32] = {0}; int i, ret, count; bool hit = false; if (amdgpu_ip_version(adev, UMC_HWIP, 0) < IP_VERSION(12, 0, 0)) return 0; if (amdgpu_sriov_vf(adev)) { if (amdgpu_uniras_enabled(adev)) { if (amdgpu_virt_ras_check_address_validity(adev, address, &hit)) return -EPERM; if (hit) return -EACCES; } else { if (amdgpu_virt_check_vf_critical_region(adev, address, &hit)) return -EPERM; return hit ? -EACCES : 0; } } if ((address >= adev->gmc.mc_vram_size) || (address >= RAS_UMC_INJECT_ADDR_LIMIT)) return -EFAULT; if (amdgpu_sriov_vf(adev)) count = amdgpu_virt_ras_convert_retired_address(adev, address, page_pfns, ARRAY_SIZE(page_pfns)); else count = amdgpu_ras_mgr_lookup_bad_pages_in_a_row(adev, address, page_pfns, ARRAY_SIZE(page_pfns)); if (count <= 0) return -EPERM; for (i = 0; i < count; i++) { memset(&blk_info, 0, sizeof(blk_info)); ret = amdgpu_vram_mgr_query_address_block_info(&adev->mman.vram_mgr, page_pfns[i] << AMDGPU_GPU_PAGE_SHIFT, &blk_info); if (!ret) { /* The input address that needs to be checked is allocated by * current calling process, so it is necessary to exclude * the calling process. */ if ((flags == BYPASS_ALLOCATED_ADDRESS) && ((blk_info.task.pid != task_pid_nr(current)) || strncmp(blk_info.task.comm, current->comm, TASK_COMM_LEN))) return -EACCES; else if ((flags == BYPASS_INITIALIZATION_ADDRESS) && (blk_info.task.pid == con->init_task_pid) && !strncmp(blk_info.task.comm, con->init_task_comm, TASK_COMM_LEN)) return -EACCES; } } return 0;} static ssize_t amdgpu_ras_debugfs_read(struct file *f, char __user *buf, size_t size, loff_t *pos){ struct ras_manager *obj = (struct ras_manager *)file_inode(f)->i_private; struct ras_query_if info = { .head = obj->head, }; ssize_t s; char val[128]; if (amdgpu_ras_query_error_status(obj->adev, &info)) return -EINVAL; /* Hardware counter will be reset automatically after the query on Vega20 and Arcturus */ if (amdgpu_ip_version(obj->adev, MP0_HWIP, 0) != IP_VERSION(11, 0, 2) && amdgpu_ip_version(obj->adev, MP0_HWIP, 0) != IP_VERSION(11, 0, 4)) { if (amdgpu_ras_reset_error_status(obj->adev, info.head.block)) dev_warn(obj->adev->dev, "Failed to reset error counter and error status"); } s = snprintf(val, sizeof(val), "%s: %lu\n%s: %lu\n", "ue", info.ue_count, "ce", info.ce_count); if (*pos >= s) return 0; s -= *pos; s = min_t(u64, s, size); if (copy_to_user(buf, &val[*pos], s)) return -EINVAL; *pos += s; return s;} static const struct file_operations amdgpu_ras_debugfs_ops = { .owner = THIS_MODULE, .read = amdgpu_ras_debugfs_read, .write = NULL, .llseek = default_llseek}; static int amdgpu_ras_find_block_id_by_name(const char *name, int *block_id){ int i; for (i = 0; i < ARRAY_SIZE(ras_block_string); i++) { *block_id = i; if (strcmp(name, ras_block_string[i]) == 0) return 0; } return -EINVAL;} static int amdgpu_ras_debugfs_ctrl_parse_data(struct file *f, const char __user *buf, size_t size, loff_t *pos, struct ras_debug_if *data){ ssize_t s = min_t(u64, 64, size); char str[65]; char block_name[33]; char err[9] = "ue"; int op = -1; int block_id; uint32_t sub_block; u64 address, value; /* default value is 0 if the mask is not set by user */ u32 instance_mask = 0; if (*pos) return -EINVAL; *pos = size; memset(str, 0, sizeof(str)); memset(data, 0, sizeof(*data)); if (copy_from_user(str, buf, s)) return -EINVAL; if (sscanf(str, "disable %32s", block_name) == 1) op = 0; else if (sscanf(str, "enable %32s %8s", block_name, err) == 2) op = 1; else if (sscanf(str, "inject %32s %8s", block_name, err) == 2) op = 2; else if (strstr(str, "retire_page") != NULL) op = 3; else if (strstr(str, "check_address") != NULL) op = 4; else if (str[0] && str[1] && str[2] && str[3]) /* ascii string, but commands are not matched. */ return -EINVAL; if (op != -1) { if (op == 3) { if (sscanf(str, "%*s 0x%llx", &address) != 1 && sscanf(str, "%*s %llu", &address) != 1) return -EINVAL; data->op = op; data->inject.address = address; return 0; } else if (op == 4) { if (sscanf(str, "%*s 0x%llx 0x%llx", &address, &value) != 2 && sscanf(str, "%*s %llu %llu", &address, &value) != 2) return -EINVAL; data->op = op; data->inject.address = address; data->inject.value = value; return 0; } if (amdgpu_ras_find_block_id_by_name(block_name, &block_id)) return -EINVAL; data->head.block = block_id; /* only ue, ce and poison errors are supported */ if (!memcmp("ue", err, 2)) data->head.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; else if (!memcmp("ce", err, 2)) data->head.type = AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE; else if (!memcmp("poison", err, 6)) data->head.type = AMDGPU_RAS_ERROR__POISON; else return -EINVAL; data->op = op; if (op == 2) { if (sscanf(str, "%*s %*s %*s 0x%x 0x%llx 0x%llx 0x%x", &sub_block, &address, &value, &instance_mask) != 4 && sscanf(str, "%*s %*s %*s %u %llu %llu %u", &sub_block, &address, &value, &instance_mask) != 4 && sscanf(str, "%*s %*s %*s 0x%x 0x%llx 0x%llx", &sub_block, &address, &value) != 3 && sscanf(str, "%*s %*s %*s %u %llu %llu", &sub_block, &address, &value) != 3) return -EINVAL; data->head.sub_block_index = sub_block; data->inject.address = address; data->inject.value = value; data->inject.instance_mask = instance_mask; } } else { if (size < sizeof(*data)) return -EINVAL; if (copy_from_user(data, buf, sizeof(*data))) return -EINVAL; } return 0;} static void amdgpu_ras_instance_mask_check(struct amdgpu_device *adev, struct ras_debug_if *data){ int num_xcc = adev->gfx.xcc_mask ? NUM_XCC(adev->gfx.xcc_mask) : 1; uint32_t mask, inst_mask = data->inject.instance_mask; /* no need to set instance mask if there is only one instance */ if (num_xcc <= 1 && inst_mask) { data->inject.instance_mask = 0; dev_dbg(adev->dev, "RAS inject mask(0x%x) isn't supported and force it to 0.\n", inst_mask); return; } switch (data->head.block) { case AMDGPU_RAS_BLOCK__GFX: mask = GENMASK(num_xcc - 1, 0); break; case AMDGPU_RAS_BLOCK__SDMA: mask = GENMASK(adev->sdma.num_instances - 1, 0); break; case AMDGPU_RAS_BLOCK__VCN: case AMDGPU_RAS_BLOCK__JPEG: mask = GENMASK(adev->vcn.num_vcn_inst - 1, 0); break; default: mask = inst_mask; break; } /* remove invalid bits in instance mask */ data->inject.instance_mask &= mask; if (inst_mask != data->inject.instance_mask) dev_dbg(adev->dev, "Adjust RAS inject mask 0x%x to 0x%x\n", inst_mask, data->inject.instance_mask);} /** * DOC: AMDGPU RAS debugfs control interface * * The control interface accepts struct ras_debug_if which has two members. * * First member: ras_debug_if::head or ras_debug_if::inject. * * head is used to indicate which IP block will be under control. * * head has four members, they are block, type, sub_block_index, name. * block: which IP will be under control. * type: what kind of error will be enabled/disabled/injected. * sub_block_index: some IPs have subcomponets. say, GFX, sDMA. * name: the name of IP. * * inject has three more members than head, they are address, value and mask. * As their names indicate, inject operation will write the * value to the address. * * The second member: struct ras_debug_if::op. * It has three kinds of operations. * * - 0: disable RAS on the block. Take ::head as its data.Lines 1–500 of 5007. Load more
/* * Copyright 2018 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * */ #include "amdgpu_reg_access.h" #include <linux/debugfs.h> #include <linux/list.h> #include <linux/module.h> #include <linux/uaccess.h> #include <linux/reboot.h> #include <linux/syscalls.h> #include <linux/pm_runtime.h> #include <linux/list_sort.h> #include "amdgpu.h" #include "amdgpu_ras.h" #include "amdgpu_atomfirmware.h" #include "amdgpu_xgmi.h" #include "ivsrcid/nbio/irqsrcs_nbif_7_4.h" #include "nbio_v4_3.h" #include "nbif_v6_3_1.h" #include "nbio_v7_9.h" #include "atom.h" #include "amdgpu_reset.h" #include "amdgpu_psp.h" #include "amdgpu_ras_mgr.h" #include "amdgpu_virt_ras_cmd.h" #ifdef CONFIG_X86_MCE_AMD #include <asm/mce.h> static bool notifier_registered; #endif static const char *RAS_FS_NAME = "ras"; const char *ras_error_string[] = { "none", "parity", "single_correctable", "multi_uncorrectable", "poison", }; const char *ras_block_string[] = { "umc", "sdma", "gfx", "mmhub", "athub", "pcie_bif", "hdp", "xgmi_wafl", "df", "smn", "sem", "mp0", "mp1", "fuse", "mca", "vcn", "jpeg", "ih", "mpio", "mmsch", }; const char *ras_mca_block_string[] = { "mca_mp0", "mca_mp1", "mca_mpio", "mca_iohc", }; struct amdgpu_ras_block_list { /* ras block link */ struct list_head node; struct amdgpu_ras_block_object *ras_obj; /* set by ras_late_init, cleared by ras_suspend/ras_fini */ bool active; }; const char *get_ras_block_str(struct ras_common_if *ras_block) { if (!ras_block) return "NULL"; if (ras_block->block >= AMDGPU_RAS_BLOCK_COUNT || ras_block->block >= ARRAY_SIZE(ras_block_string)) return "OUT OF RANGE"; if (ras_block->block == AMDGPU_RAS_BLOCK__MCA) return ras_mca_block_string[ras_block->sub_block_index]; return ras_block_string[ras_block->block]; } #define ras_block_str(_BLOCK_) \ (((_BLOCK_) < ARRAY_SIZE(ras_block_string)) ? ras_block_string[_BLOCK_] : "Out Of Range") #define ras_err_str(i) (ras_error_string[ffs(i)]) #define RAS_DEFAULT_FLAGS (AMDGPU_RAS_FLAG_INIT_BY_VBIOS) /* inject address is 52 bits */ #define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52) /* typical ECC bad page rate is 1 bad page per 100MB VRAM */ #define RAS_BAD_PAGE_COVER (100 * 1024 * 1024ULL) #define BYPASS_ALLOCATED_ADDRESS 0x0 #define BYPASS_INITIALIZATION_ADDRESS 0x1 enum amdgpu_ras_retire_page_reservation { AMDGPU_RAS_RETIRE_PAGE_RESERVED, AMDGPU_RAS_RETIRE_PAGE_PENDING, AMDGPU_RAS_RETIRE_PAGE_FAULT, }; atomic_t amdgpu_ras_in_intr = ATOMIC_INIT(0); static int amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, uint64_t addr); static int amdgpu_ras_check_bad_page(struct amdgpu_device *adev, uint64_t addr); static void amdgpu_ras_critical_region_init(struct amdgpu_device *adev); static void amdgpu_ras_critical_region_fini(struct amdgpu_device *adev); #ifdef CONFIG_X86_MCE_AMD static void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev); static void amdgpu_unregister_bad_pages_mca_notifier(struct amdgpu_device *adev); struct mce_notifier_adev_list { struct amdgpu_device *devs[MAX_GPU_INSTANCE]; int num_gpu; }; static struct mce_notifier_adev_list mce_adev_list; #endif void amdgpu_ras_set_error_query_ready(struct amdgpu_device *adev, bool ready) { if (adev && amdgpu_ras_get_context(adev)) amdgpu_ras_get_context(adev)->error_query_ready = ready; } static bool amdgpu_ras_get_error_query_ready(struct amdgpu_device *adev) { if (adev && amdgpu_ras_get_context(adev)) return amdgpu_ras_get_context(adev)->error_query_ready; return false; } static int amdgpu_reserve_page_direct(struct amdgpu_device *adev, uint64_t address) { struct ras_err_data err_data; struct eeprom_table_record err_rec; int ret; ret = amdgpu_ras_check_bad_page(adev, address); if (ret == -EINVAL) { dev_warn(adev->dev, "RAS WARN: input address 0x%llx is invalid.\n", address); return -EINVAL; } else if (ret == 1) { dev_warn(adev->dev, "RAS WARN: 0x%llx has already been marked as bad page!\n", address); return 0; } ret = amdgpu_ras_error_data_init(&err_data); if (ret) return ret; memset(&err_rec, 0x0, sizeof(struct eeprom_table_record)); err_data.err_addr = &err_rec; amdgpu_umc_fill_error_record(&err_data, address, address, 0, 0); if (amdgpu_bad_page_threshold != 0) { amdgpu_ras_add_bad_pages(adev, err_data.err_addr, err_data.err_addr_cnt, false); amdgpu_ras_save_bad_pages(adev, NULL); } amdgpu_ras_error_data_fini(&err_data); dev_warn(adev->dev, "WARNING: THIS IS ONLY FOR TEST PURPOSES AND WILL CORRUPT RAS EEPROM\n"); dev_warn(adev->dev, "Clear EEPROM:\n"); dev_warn(adev->dev, " echo 1 > /sys/kernel/debug/dri/0/ras/ras_eeprom_reset\n"); return 0; } static int amdgpu_check_address_validity(struct amdgpu_device *adev, uint64_t address, uint64_t flags) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct amdgpu_vram_block_info blk_info; uint64_t page_pfns[32] = {0}; int i, ret, count; bool hit = false; if (amdgpu_ip_version(adev, UMC_HWIP, 0) < IP_VERSION(12, 0, 0)) return 0; if (amdgpu_sriov_vf(adev)) { if (amdgpu_uniras_enabled(adev)) { if (amdgpu_virt_ras_check_address_validity(adev, address, &hit)) return -EPERM; if (hit) return -EACCES; } else { if (amdgpu_virt_check_vf_critical_region(adev, address, &hit)) return -EPERM; return hit ? -EACCES : 0; } } if ((address >= adev->gmc.mc_vram_size) || (address >= RAS_UMC_INJECT_ADDR_LIMIT)) return -EFAULT; if (amdgpu_sriov_vf(adev)) count = amdgpu_virt_ras_convert_retired_address(adev, address, page_pfns, ARRAY_SIZE(page_pfns)); else count = amdgpu_ras_mgr_lookup_bad_pages_in_a_row(adev, address, page_pfns, ARRAY_SIZE(page_pfns)); if (count <= 0) return -EPERM; for (i = 0; i < count; i++) { memset(&blk_info, 0, sizeof(blk_info)); ret = amdgpu_vram_mgr_query_address_block_info(&adev->mman.vram_mgr, page_pfns[i] << AMDGPU_GPU_PAGE_SHIFT, &blk_info); if (!ret) { /* The input address that needs to be checked is allocated by * current calling process, so it is necessary to exclude * the calling process. */ if ((flags == BYPASS_ALLOCATED_ADDRESS) && ((blk_info.task.pid != task_pid_nr(current)) || strncmp(blk_info.task.comm, current->comm, TASK_COMM_LEN))) return -EACCES; else if ((flags == BYPASS_INITIALIZATION_ADDRESS) && (blk_info.task.pid == con->init_task_pid) && !strncmp(blk_info.task.comm, con->init_task_comm, TASK_COMM_LEN)) return -EACCES; } } return 0; } static ssize_t amdgpu_ras_debugfs_read(struct file *f, char __user *buf, size_t size, loff_t *pos) { struct ras_manager *obj = (struct ras_manager *)file_inode(f)->i_private; struct ras_query_if info = { .head = obj->head, }; ssize_t s; char val[128]; if (amdgpu_ras_query_error_status(obj->adev, &info)) return -EINVAL; /* Hardware counter will be reset automatically after the query on Vega20 and Arcturus */ if (amdgpu_ip_version(obj->adev, MP0_HWIP, 0) != IP_VERSION(11, 0, 2) && amdgpu_ip_version(obj->adev, MP0_HWIP, 0) != IP_VERSION(11, 0, 4)) { if (amdgpu_ras_reset_error_status(obj->adev, info.head.block)) dev_warn(obj->adev->dev, "Failed to reset error counter and error status"); } s = snprintf(val, sizeof(val), "%s: %lu\n%s: %lu\n", "ue", info.ue_count, "ce", info.ce_count); if (*pos >= s) return 0; s -= *pos; s = min_t(u64, s, size); if (copy_to_user(buf, &val[*pos], s)) return -EINVAL; *pos += s; return s; } static const struct file_operations amdgpu_ras_debugfs_ops = { .owner = THIS_MODULE, .read = amdgpu_ras_debugfs_read, .write = NULL, .llseek = default_llseek }; static int amdgpu_ras_find_block_id_by_name(const char *name, int *block_id) { int i; for (i = 0; i < ARRAY_SIZE(ras_block_string); i++) { *block_id = i; if (strcmp(name, ras_block_string[i]) == 0) return 0; } return -EINVAL; } static int amdgpu_ras_debugfs_ctrl_parse_data(struct file *f, const char __user *buf, size_t size, loff_t *pos, struct ras_debug_if *data) { ssize_t s = min_t(u64, 64, size); char str[65]; char block_name[33]; char err[9] = "ue"; int op = -1; int block_id; uint32_t sub_block; u64 address, value; /* default value is 0 if the mask is not set by user */ u32 instance_mask = 0; if (*pos) return -EINVAL; *pos = size; memset(str, 0, sizeof(str)); memset(data, 0, sizeof(*data)); if (copy_from_user(str, buf, s)) return -EINVAL; if (sscanf(str, "disable %32s", block_name) == 1) op = 0; else if (sscanf(str, "enable %32s %8s", block_name, err) == 2) op = 1; else if (sscanf(str, "inject %32s %8s", block_name, err) == 2) op = 2; else if (strstr(str, "retire_page") != NULL) op = 3; else if (strstr(str, "check_address") != NULL) op = 4; else if (str[0] && str[1] && str[2] && str[3]) /* ascii string, but commands are not matched. */ return -EINVAL; if (op != -1) { if (op == 3) { if (sscanf(str, "%*s 0x%llx", &address) != 1 && sscanf(str, "%*s %llu", &address) != 1) return -EINVAL; data->op = op; data->inject.address = address; return 0; } else if (op == 4) { if (sscanf(str, "%*s 0x%llx 0x%llx", &address, &value) != 2 && sscanf(str, "%*s %llu %llu", &address, &value) != 2) return -EINVAL; data->op = op; data->inject.address = address; data->inject.value = value; return 0; } if (amdgpu_ras_find_block_id_by_name(block_name, &block_id)) return -EINVAL; data->head.block = block_id; /* only ue, ce and poison errors are supported */ if (!memcmp("ue", err, 2)) data->head.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; else if (!memcmp("ce", err, 2)) data->head.type = AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE; else if (!memcmp("poison", err, 6)) data->head.type = AMDGPU_RAS_ERROR__POISON; else return -EINVAL; data->op = op; if (op == 2) { if (sscanf(str, "%*s %*s %*s 0x%x 0x%llx 0x%llx 0x%x", &sub_block, &address, &value, &instance_mask) != 4 && sscanf(str, "%*s %*s %*s %u %llu %llu %u", &sub_block, &address, &value, &instance_mask) != 4 && sscanf(str, "%*s %*s %*s 0x%x 0x%llx 0x%llx", &sub_block, &address, &value) != 3 && sscanf(str, "%*s %*s %*s %u %llu %llu", &sub_block, &address, &value) != 3) return -EINVAL; data->head.sub_block_index = sub_block; data->inject.address = address; data->inject.value = value; data->inject.instance_mask = instance_mask; } } else { if (size < sizeof(*data)) return -EINVAL; if (copy_from_user(data, buf, sizeof(*data))) return -EINVAL; } return 0; } static void amdgpu_ras_instance_mask_check(struct amdgpu_device *adev, struct ras_debug_if *data) { int num_xcc = adev->gfx.xcc_mask ? NUM_XCC(adev->gfx.xcc_mask) : 1; uint32_t mask, inst_mask = data->inject.instance_mask; /* no need to set instance mask if there is only one instance */ if (num_xcc <= 1 && inst_mask) { data->inject.instance_mask = 0; dev_dbg(adev->dev, "RAS inject mask(0x%x) isn't supported and force it to 0.\n", inst_mask); return; } switch (data->head.block) { case AMDGPU_RAS_BLOCK__GFX: mask = GENMASK(num_xcc - 1, 0); break; case AMDGPU_RAS_BLOCK__SDMA: mask = GENMASK(adev->sdma.num_instances - 1, 0); break; case AMDGPU_RAS_BLOCK__VCN: case AMDGPU_RAS_BLOCK__JPEG: mask = GENMASK(adev->vcn.num_vcn_inst - 1, 0); break; default: mask = inst_mask; break; } /* remove invalid bits in instance mask */ data->inject.instance_mask &= mask; if (inst_mask != data->inject.instance_mask) dev_dbg(adev->dev, "Adjust RAS inject mask 0x%x to 0x%x\n", inst_mask, data->inject.instance_mask); } /** * DOC: AMDGPU RAS debugfs control interface * * The control interface accepts struct ras_debug_if which has two members. * * First member: ras_debug_if::head or ras_debug_if::inject. * * head is used to indicate which IP block will be under control. * * head has four members, they are block, type, sub_block_index, name. * block: which IP will be under control. * type: what kind of error will be enabled/disabled/injected. * sub_block_index: some IPs have subcomponets. say, GFX, sDMA. * name: the name of IP. * * inject has three more members than head, they are address, value and mask. * As their names indicate, inject operation will write the * value to the address. * * The second member: struct ras_debug_if::op. * It has three kinds of operations. * * - 0: disable RAS on the block. Take ::head as its data.