2400 lines · 60.4 KB · C
RawDownload
/* * 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 <linux/debugfs.h>#include <linux/list.h>#include <linux/module.h>#include <linux/uaccess.h>#include <linux/reboot.h>#include <linux/syscalls.h> #include "amdgpu.h"#include "amdgpu_ras.h"#include "amdgpu_atomfirmware.h"#include "amdgpu_xgmi.h"#include "ivsrcid/nbio/irqsrcs_nbif_7_4.h" 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",}; #define ras_err_str(i) (ras_error_string[ffs(i)])#define ras_block_str(i) (ras_block_string[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(1 bad page per 100MB VRAM) */#define RAS_BAD_PAGE_RATE		(100 * 1024 * 1024ULL) 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 bool amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con,				uint64_t addr);static bool amdgpu_ras_check_bad_page(struct amdgpu_device *adev,				uint64_t addr); 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 = {0, 0, 0, NULL};	struct eeprom_table_record err_rec; 	if ((address >= adev->gmc.mc_vram_size) ||	    (address >= RAS_UMC_INJECT_ADDR_LIMIT)) {		dev_warn(adev->dev,		         "RAS WARN: input address 0x%llx is invalid.\n",		         address);		return -EINVAL;	} 	if (amdgpu_ras_check_bad_page(adev, address)) {		dev_warn(adev->dev,			 "RAS WARN: 0x%llx has already been marked as bad page!\n",			 address);		return 0;	} 	memset(&err_rec, 0x0, sizeof(struct eeprom_table_record)); 	err_rec.address = address;	err_rec.retired_page = address >> AMDGPU_GPU_PAGE_SHIFT;	err_rec.ts = (uint64_t)ktime_get_real_seconds();	err_rec.err_type = AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE; 	err_data.err_addr = &err_rec;	err_data.err_addr_cnt = 1; 	if (amdgpu_bad_page_threshold != 0) {		amdgpu_ras_add_bad_pages(adev, err_data.err_addr,					 err_data.err_addr_cnt);		amdgpu_ras_save_bad_pages(adev);	} 	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 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; 	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_str(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; 	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 (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;		} 		if (amdgpu_ras_find_block_id_by_name(block_name, &block_id))			return -EINVAL; 		data->head.block = block_id;		/* only ue and ce 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			return -EINVAL; 		data->op = op; 		if (op == 2) {			if (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;		}	} else {		if (size < sizeof(*data))			return -EINVAL; 		if (copy_from_user(data, buf, sizeof(*data)))			return -EINVAL;	} 	return 0;} /** * 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 two more members than head, they are address, value. * 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. * - 1: enable RAS on the block. Take ::head as its data. * - 2: inject errors on the block. Take ::inject as its data. * * How to use the interface? * * In a program * * Copy the struct ras_debug_if in your code and initialize it. * Write the struct to the control interface. * * From shell * * .. code-block:: bash * *	echo "disable <block>" > /sys/kernel/debug/dri/<N>/ras/ras_ctrl *	echo "enable  <block> <error>" > /sys/kernel/debug/dri/<N>/ras/ras_ctrl *	echo "inject  <block> <error> <sub-block> <address> <value> > /sys/kernel/debug/dri/<N>/ras/ras_ctrl * * Where N, is the card which you want to affect. * * "disable" requires only the block. * "enable" requires the block and error type. * "inject" requires the block, error type, address, and value. * The block is one of: umc, sdma, gfx, etc. *	see ras_block_string[] for details * The error type is one of: ue, ce, where, *	ue is multi-uncorrectable *	ce is single-correctable * The sub-block is a the sub-block index, pass 0 if there is no sub-block. * The address and value are hexadecimal numbers, leading 0x is optional. * * For instance, * * .. code-block:: bash * *	echo inject umc ue 0x0 0x0 0x0 > /sys/kernel/debug/dri/0/ras/ras_ctrl *	echo inject umc ce 0 0 0 > /sys/kernel/debug/dri/0/ras/ras_ctrl *	echo disable umc > /sys/kernel/debug/dri/0/ras/ras_ctrl * * How to check the result of the operation? * * To check disable/enable, see "ras" features at, * /sys/class/drm/card[0/1/2...]/device/ras/features * * To check inject, see the corresponding error count at, * /sys/class/drm/card[0/1/2...]/device/ras/[gfx|sdma|umc|...]_err_count * * .. note:: *	Operations are only allowed on blocks which are supported. *	Check the "ras" mask at /sys/module/amdgpu/parameters/ras_mask *	to see which blocks support RAS on a particular asic. * */static ssize_t amdgpu_ras_debugfs_ctrl_write(struct file *f, const char __user *buf,		size_t size, loff_t *pos){	struct amdgpu_device *adev = (struct amdgpu_device *)file_inode(f)->i_private;	struct ras_debug_if data;	int ret = 0; 	if (!amdgpu_ras_get_error_query_ready(adev)) {		dev_warn(adev->dev, "RAS WARN: error injection "				"currently inaccessible\n");		return size;	} 	ret = amdgpu_ras_debugfs_ctrl_parse_data(f, buf, size, pos, &data);	if (ret)		return -EINVAL; 	if (data.op == 3) {		ret = amdgpu_reserve_page_direct(adev, data.inject.address);		if (!ret)			return size;		else			return ret;	} 	if (!amdgpu_ras_is_supported(adev, data.head.block))		return -EINVAL; 	switch (data.op) {	case 0:		ret = amdgpu_ras_feature_enable(adev, &data.head, 0);		break;	case 1:		ret = amdgpu_ras_feature_enable(adev, &data.head, 1);		break;	case 2:		if ((data.inject.address >= adev->gmc.mc_vram_size) ||		    (data.inject.address >= RAS_UMC_INJECT_ADDR_LIMIT)) {			dev_warn(adev->dev, "RAS WARN: input address "					"0x%llx is invalid.",					data.inject.address);			ret = -EINVAL;			break;		} 		/* umc ce/ue error injection for a bad page is not allowed */		if ((data.head.block == AMDGPU_RAS_BLOCK__UMC) &&		    amdgpu_ras_check_bad_page(adev, data.inject.address)) {			dev_warn(adev->dev, "RAS WARN: 0x%llx has been marked "					"as bad before error injection!\n",					data.inject.address);			break;		} 		/* data.inject.address is offset instead of absolute gpu address */		ret = amdgpu_ras_error_inject(adev, &data.inject);		break;	default:		ret = -EINVAL;		break;	} 	if (ret)		return -EINVAL; 	return size;} /** * DOC: AMDGPU RAS debugfs EEPROM table reset interface * * Some boards contain an EEPROM which is used to persistently store a list of * bad pages which experiences ECC errors in vram.  This interface provides * a way to reset the EEPROM, e.g., after testing error injection. * * Usage: * * .. code-block:: bash * *	echo 1 > ../ras/ras_eeprom_reset * * will reset EEPROM table to 0 entries. * */static ssize_t amdgpu_ras_debugfs_eeprom_write(struct file *f, const char __user *buf,		size_t size, loff_t *pos){	struct amdgpu_device *adev =		(struct amdgpu_device *)file_inode(f)->i_private;	int ret; 	ret = amdgpu_ras_eeprom_reset_table(			&(amdgpu_ras_get_context(adev)->eeprom_control)); 	if (ret == 1) {		amdgpu_ras_get_context(adev)->flags = RAS_DEFAULT_FLAGS;		return size;	} else {		return -EIO;	}} static const struct file_operations amdgpu_ras_debugfs_ctrl_ops = {	.owner = THIS_MODULE,	.read = NULL,	.write = amdgpu_ras_debugfs_ctrl_write,	.llseek = default_llseek}; static const struct file_operations amdgpu_ras_debugfs_eeprom_ops = {	.owner = THIS_MODULE,	.read = NULL,	.write = amdgpu_ras_debugfs_eeprom_write,	.llseek = default_llseek}; /** * DOC: AMDGPU RAS sysfs Error Count Interface * * It allows the user to read the error count for each IP block on the gpu through * /sys/class/drm/card[0/1/2...]/device/ras/[gfx/sdma/...]_err_count * * It outputs the multiple lines which report the uncorrected (ue) and corrected * (ce) error counts. * * The format of one line is below, * * [ce|ue]: count * * Example: * * .. code-block:: bash * *	ue: 0 *	ce: 1 * */static ssize_t amdgpu_ras_sysfs_read(struct device *dev,		struct device_attribute *attr, char *buf){	struct ras_manager *obj = container_of(attr, struct ras_manager, sysfs_attr);	struct ras_query_if info = {		.head = obj->head,	}; 	if (!amdgpu_ras_get_error_query_ready(obj->adev))		return sysfs_emit(buf, "Query currently inaccessible\n"); 

Lines 1–500 of 2400.