/* * 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 "amdgpu.h"#include "amdgpu_ras.h"#include "amdgpu_atomfirmware.h" struct ras_ih_data { /* interrupt bottom half */ struct work_struct ih_work; int inuse; /* IP callback */ ras_ih_cb cb; /* full of entries */ unsigned char *ring; unsigned int ring_size; unsigned int element_size; unsigned int aligned_element_size; unsigned int rptr; unsigned int wptr;}; struct ras_fs_data { char sysfs_name[32]; char debugfs_name[32];}; struct ras_err_data { unsigned long ue_count; unsigned long ce_count;}; struct ras_err_handler_data { /* point to bad pages array */ struct { unsigned long bp; struct amdgpu_bo *bo; } *bps; /* the count of entries */ int count; /* the space can place new entries */ int space_left; /* last reserved entry's index + 1 */ int last_reserved;}; struct ras_manager { struct ras_common_if head; /* reference count */ int use; /* ras block link */ struct list_head node; /* the device */ struct amdgpu_device *adev; /* debugfs */ struct dentry *ent; /* sysfs */ struct device_attribute sysfs_attr; int attr_inuse; /* fs node name */ struct ras_fs_data fs_data; /* IH data */ struct ras_ih_data ih_data; struct ras_err_data err_data;}; 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 AMDGPU_RAS_FLAG_INIT_BY_VBIOS 1#define RAS_DEFAULT_FLAGS (AMDGPU_RAS_FLAG_INIT_BY_VBIOS) static void amdgpu_ras_self_test(struct amdgpu_device *adev){ /* TODO */} 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_error_query(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; 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 (str[0] && str[1] && str[2] && str[3]) /* ascii string, but commands are not matched. */ return -EINVAL; if (op != -1) { if (amdgpu_ras_find_block_id_by_name(block_name, &block_id)) return -EINVAL; data->head.block = block_id; data->head.type = memcmp("ue", err, 2) == 0 ? AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE : AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE; data->op = op; if (op == 2) { if (sscanf(str, "%*s %*s %*s %llu %llu", &address, &value) != 2) if (sscanf(str, "%*s %*s %*s 0x%llx 0x%llx", &address, &value) != 2) return -EINVAL; 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: ras debugfs control interface * * It accepts struct ras_debug_if who 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. * * 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? * programs: * copy the struct ras_debug_if in your codes and initialize it. * write the struct to the control node. * * bash: * echo op block [error [address value]] > .../ras/ras_ctrl * op: disable, enable, inject * disable: only block is needed * enable: block and error are needed * inject: error, address, value are needed * block: umc, smda, gfx, ......... * see ras_block_string[] for details * error: ue, ce * ue: multi_uncorrectable * ce: single_correctable * * here are some examples for bash commands, * echo inject umc ue 0x0 0x0 > /sys/kernel/debug/dri/0/ras/ras_ctrl * echo inject umc ce 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? * * For disable/enable, please check ras features at * /sys/class/drm/card[0/1/2...]/device/ras/features * * For inject, please check corresponding err count at * /sys/class/drm/card[0/1/2...]/device/ras/[gfx/sdma/...]_err_count * * NOTE: operation is only allowed on blocks which are supported. * Please check ras mask at /sys/module/amdgpu/parameters/ras_mask */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; ret = amdgpu_ras_debugfs_ctrl_parse_data(f, buf, size, pos, &data); if (ret) return -EINVAL; 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: ret = amdgpu_ras_error_inject(adev, &data.inject); break; default: ret = -EINVAL; break; }; if (ret) return -EINVAL; return size;} 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 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_error_query(obj->adev, &info)) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%s: %lu\n%s: %lu\n", "ue", info.ue_count, "ce", info.ce_count);} /* obj begin */ #define get_obj(obj) do { (obj)->use++; } while (0)#define alive_obj(obj) ((obj)->use) static inline void put_obj(struct ras_manager *obj){ if (obj && --obj->use == 0) list_del(&obj->node); if (obj && obj->use < 0) { DRM_ERROR("RAS ERROR: Unbalance obj(%s) use\n", obj->head.name); }} /* make one obj and return it. */static struct ras_manager *amdgpu_ras_create_obj(struct amdgpu_device *adev, struct ras_common_if *head){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_manager *obj; if (!con) return NULL; if (head->block >= AMDGPU_RAS_BLOCK_COUNT) return NULL; obj = &con->objs[head->block]; /* already exist. return obj? */ if (alive_obj(obj)) return NULL; obj->head = *head; obj->adev = adev; list_add(&obj->node, &con->head); get_obj(obj); return obj;} /* return an obj equal to head, or the first when head is NULL */static struct ras_manager *amdgpu_ras_find_obj(struct amdgpu_device *adev, struct ras_common_if *head){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_manager *obj; int i; if (!con) return NULL; if (head) { if (head->block >= AMDGPU_RAS_BLOCK_COUNT) return NULL; obj = &con->objs[head->block]; if (alive_obj(obj)) { WARN_ON(head->block != obj->head.block); return obj; } } else { for (i = 0; i < AMDGPU_RAS_BLOCK_COUNT; i++) { obj = &con->objs[i]; if (alive_obj(obj)) { WARN_ON(i != obj->head.block); return obj; } } } return NULL;}/* obj end */ /* feature ctl begin */static int amdgpu_ras_is_feature_allowed(struct amdgpu_device *adev, struct ras_common_if *head){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); return con->hw_supported & BIT(head->block);} static int amdgpu_ras_is_feature_enabled(struct amdgpu_device *adev, struct ras_common_if *head){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); return con->features & BIT(head->block);} /* * if obj is not created, then create one. * set feature enable flag. */static int __amdgpu_ras_feature_enable(struct amdgpu_device *adev, struct ras_common_if *head, int enable){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_manager *obj = amdgpu_ras_find_obj(adev, head); /* If hardware does not support ras, then do not create obj. * But if hardware support ras, we can create the obj. * Ras framework checks con->hw_supported to see if it need do * corresponding initialization. * IP checks con->support to see if it need disable ras. */ if (!amdgpu_ras_is_feature_allowed(adev, head)) return 0; if (!(!!enable ^ !!amdgpu_ras_is_feature_enabled(adev, head))) return 0; if (enable) { if (!obj) { obj = amdgpu_ras_create_obj(adev, head); if (!obj) return -EINVAL; } else { /* In case we create obj somewhere else */ get_obj(obj); } con->features |= BIT(head->block); } else { if (obj && amdgpu_ras_is_feature_enabled(adev, head)) { con->features &= ~BIT(head->block); put_obj(obj); } } return 0;} /* wrapper of psp_ras_enable_features */int amdgpu_ras_feature_enable(struct amdgpu_device *adev, struct ras_common_if *head, bool enable){ struct amdgpu_ras *con = amdgpu_ras_get_context(adev); union ta_ras_cmd_input info; int ret; if (!con) return -EINVAL; if (!enable) {Lines 1–500 of 1482. 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 <linux/debugfs.h> #include <linux/list.h> #include <linux/module.h> #include "amdgpu.h" #include "amdgpu_ras.h" #include "amdgpu_atomfirmware.h" struct ras_ih_data { /* interrupt bottom half */ struct work_struct ih_work; int inuse; /* IP callback */ ras_ih_cb cb; /* full of entries */ unsigned char *ring; unsigned int ring_size; unsigned int element_size; unsigned int aligned_element_size; unsigned int rptr; unsigned int wptr; }; struct ras_fs_data { char sysfs_name[32]; char debugfs_name[32]; }; struct ras_err_data { unsigned long ue_count; unsigned long ce_count; }; struct ras_err_handler_data { /* point to bad pages array */ struct { unsigned long bp; struct amdgpu_bo *bo; } *bps; /* the count of entries */ int count; /* the space can place new entries */ int space_left; /* last reserved entry's index + 1 */ int last_reserved; }; struct ras_manager { struct ras_common_if head; /* reference count */ int use; /* ras block link */ struct list_head node; /* the device */ struct amdgpu_device *adev; /* debugfs */ struct dentry *ent; /* sysfs */ struct device_attribute sysfs_attr; int attr_inuse; /* fs node name */ struct ras_fs_data fs_data; /* IH data */ struct ras_ih_data ih_data; struct ras_err_data err_data; }; 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 AMDGPU_RAS_FLAG_INIT_BY_VBIOS 1 #define RAS_DEFAULT_FLAGS (AMDGPU_RAS_FLAG_INIT_BY_VBIOS) static void amdgpu_ras_self_test(struct amdgpu_device *adev) { /* TODO */ } 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_error_query(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; 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 (str[0] && str[1] && str[2] && str[3]) /* ascii string, but commands are not matched. */ return -EINVAL; if (op != -1) { if (amdgpu_ras_find_block_id_by_name(block_name, &block_id)) return -EINVAL; data->head.block = block_id; data->head.type = memcmp("ue", err, 2) == 0 ? AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE : AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE; data->op = op; if (op == 2) { if (sscanf(str, "%*s %*s %*s %llu %llu", &address, &value) != 2) if (sscanf(str, "%*s %*s %*s 0x%llx 0x%llx", &address, &value) != 2) return -EINVAL; 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: ras debugfs control interface * * It accepts struct ras_debug_if who 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. * * 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? * programs: * copy the struct ras_debug_if in your codes and initialize it. * write the struct to the control node. * * bash: * echo op block [error [address value]] > .../ras/ras_ctrl * op: disable, enable, inject * disable: only block is needed * enable: block and error are needed * inject: error, address, value are needed * block: umc, smda, gfx, ......... * see ras_block_string[] for details * error: ue, ce * ue: multi_uncorrectable * ce: single_correctable * * here are some examples for bash commands, * echo inject umc ue 0x0 0x0 > /sys/kernel/debug/dri/0/ras/ras_ctrl * echo inject umc ce 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? * * For disable/enable, please check ras features at * /sys/class/drm/card[0/1/2...]/device/ras/features * * For inject, please check corresponding err count at * /sys/class/drm/card[0/1/2...]/device/ras/[gfx/sdma/...]_err_count * * NOTE: operation is only allowed on blocks which are supported. * Please check ras mask at /sys/module/amdgpu/parameters/ras_mask */ 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; ret = amdgpu_ras_debugfs_ctrl_parse_data(f, buf, size, pos, &data); if (ret) return -EINVAL; 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: ret = amdgpu_ras_error_inject(adev, &data.inject); break; default: ret = -EINVAL; break; }; if (ret) return -EINVAL; return size; } 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 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_error_query(obj->adev, &info)) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%s: %lu\n%s: %lu\n", "ue", info.ue_count, "ce", info.ce_count); } /* obj begin */ #define get_obj(obj) do { (obj)->use++; } while (0) #define alive_obj(obj) ((obj)->use) static inline void put_obj(struct ras_manager *obj) { if (obj && --obj->use == 0) list_del(&obj->node); if (obj && obj->use < 0) { DRM_ERROR("RAS ERROR: Unbalance obj(%s) use\n", obj->head.name); } } /* make one obj and return it. */ static struct ras_manager *amdgpu_ras_create_obj(struct amdgpu_device *adev, struct ras_common_if *head) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_manager *obj; if (!con) return NULL; if (head->block >= AMDGPU_RAS_BLOCK_COUNT) return NULL; obj = &con->objs[head->block]; /* already exist. return obj? */ if (alive_obj(obj)) return NULL; obj->head = *head; obj->adev = adev; list_add(&obj->node, &con->head); get_obj(obj); return obj; } /* return an obj equal to head, or the first when head is NULL */ static struct ras_manager *amdgpu_ras_find_obj(struct amdgpu_device *adev, struct ras_common_if *head) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_manager *obj; int i; if (!con) return NULL; if (head) { if (head->block >= AMDGPU_RAS_BLOCK_COUNT) return NULL; obj = &con->objs[head->block]; if (alive_obj(obj)) { WARN_ON(head->block != obj->head.block); return obj; } } else { for (i = 0; i < AMDGPU_RAS_BLOCK_COUNT; i++) { obj = &con->objs[i]; if (alive_obj(obj)) { WARN_ON(i != obj->head.block); return obj; } } } return NULL; } /* obj end */ /* feature ctl begin */ static int amdgpu_ras_is_feature_allowed(struct amdgpu_device *adev, struct ras_common_if *head) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); return con->hw_supported & BIT(head->block); } static int amdgpu_ras_is_feature_enabled(struct amdgpu_device *adev, struct ras_common_if *head) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); return con->features & BIT(head->block); } /* * if obj is not created, then create one. * set feature enable flag. */ static int __amdgpu_ras_feature_enable(struct amdgpu_device *adev, struct ras_common_if *head, int enable) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_manager *obj = amdgpu_ras_find_obj(adev, head); /* If hardware does not support ras, then do not create obj. * But if hardware support ras, we can create the obj. * Ras framework checks con->hw_supported to see if it need do * corresponding initialization. * IP checks con->support to see if it need disable ras. */ if (!amdgpu_ras_is_feature_allowed(adev, head)) return 0; if (!(!!enable ^ !!amdgpu_ras_is_feature_enabled(adev, head))) return 0; if (enable) { if (!obj) { obj = amdgpu_ras_create_obj(adev, head); if (!obj) return -EINVAL; } else { /* In case we create obj somewhere else */ get_obj(obj); } con->features |= BIT(head->block); } else { if (obj && amdgpu_ras_is_feature_enabled(adev, head)) { con->features &= ~BIT(head->block); put_obj(obj); } } return 0; } /* wrapper of psp_ras_enable_features */ int amdgpu_ras_feature_enable(struct amdgpu_device *adev, struct ras_common_if *head, bool enable) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); union ta_ras_cmd_input info; int ret; if (!con) return -EINVAL; if (!enable) {