Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/amd/amdgpu/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 142 kB image not shown  

Quelle  amdgpu_ras.c   Sprache: C

 
/*
 * 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 <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"

#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;
};

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 MAX_UMC_POISON_POLLING_TIME_ASYNC  10

#define AMDGPU_RAS_RETIRE_PAGE_INTERVAL 100  //ms

#define MAX_FLUSH_RETIRE_DWORK_TIMES  100

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);
#ifdef CONFIG_X86_MCE_AMD
static void amdgpu_register_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 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 (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, 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.
 * - 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> <mask>" > /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 and poison where,
 * ue is multi-uncorrectable
 * ce is single-correctable
 * poison is poison
 *
 * 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.
 * The mask means instance mask, is optional, default value is 0x1.
 *
 * 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 3 > /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 ret;

 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:
  /* umc ce/ue error injection for a bad page is not allowed */
  if (data.head.block == AMDGPU_RAS_BLOCK__UMC)
   ret = amdgpu_ras_check_bad_page(adev, data.inject.address);
  if (ret == -EINVAL) {
   dev_warn(adev->dev, "RAS WARN: input address 0x%llx is invalid.",
     data.inject.address);
   break;
  } else if (ret == 1) {
   dev_warn(adev->dev, "RAS WARN: inject: 0x%llx has already been marked as bad!\n",
     data.inject.address);
   break;
  }

  amdgpu_ras_instance_mask_check(adev, &data);

  /* 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 ret;

 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) {
  /* Something was written to EEPROM.
 */

  amdgpu_ras_get_context(adev)->flags = RAS_DEFAULT_FLAGS;
  return size;
 } else {
  return ret;
 }
}

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");

 if (amdgpu_ras_query_error_status(obj->adev, &info))
  return -EINVAL;

 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");
 }

 if (info.head.block == AMDGPU_RAS_BLOCK__UMC)
  return sysfs_emit(buf, "%s: %lu\n%s: %lu\n%s: %lu\n""ue", info.ue_count,
    "ce", info.ce_count, "de", info.de_count);
 else
  return sysfs_emit(buf, "%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);
  amdgpu_ras_error_data_fini(&obj->err_data);
 }

 if (obj && (obj->use < 0))
  DRM_ERROR("RAS ERROR: Unbalance obj(%s) use\n", get_ras_block_str(&obj->head));
}

/* 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 (!adev->ras_enabled || !con)
  return NULL;

 if (head->block >= AMDGPU_RAS_BLOCK_COUNT)
  return NULL;

 if (head->block == AMDGPU_RAS_BLOCK__MCA) {
  if (head->sub_block_index >= AMDGPU_RAS_MCA_BLOCK__LAST)
   return NULL;

  obj = &con->objs[AMDGPU_RAS_BLOCK__LAST + head->sub_block_index];
 } else
  obj = &con->objs[head->block];

 /* already exist. return obj? */
 if (alive_obj(obj))
  return NULL;

 if (amdgpu_ras_error_data_init(&obj->err_data))
  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 */
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 (!adev->ras_enabled || !con)
  return NULL;

 if (head) {
  if (head->block >= AMDGPU_RAS_BLOCK_COUNT)
   return NULL;

  if (head->block == AMDGPU_RAS_BLOCK__MCA) {
   if (head->sub_block_index >= AMDGPU_RAS_MCA_BLOCK__LAST)
    return NULL;

   obj = &con->objs[AMDGPU_RAS_BLOCK__LAST + head->sub_block_index];
  } else
   obj = &con->objs[head->block];

  if (alive_obj(obj))
   return obj;
 } else {
  for (i = 0; i < AMDGPU_RAS_BLOCK_COUNT + AMDGPU_RAS_MCA_BLOCK_COUNT; i++) {
   obj = &con->objs[i];
   if (alive_obj(obj))
    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)
{
 return adev->ras_hw_enabled & 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) {
  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;

 /* For non-gfx ip, do not enable ras feature if it is not allowed */
 /* For gfx ip, regardless of feature support status, */
 /* Force issue enable or disable ras feature commands */
 if (head->block != AMDGPU_RAS_BLOCK__GFX &&
     !amdgpu_ras_is_feature_allowed(adev, head))
  return 0;

 /* Only enable gfx ras feature from host side */
 if (head->block == AMDGPU_RAS_BLOCK__GFX &&
     !amdgpu_sriov_vf(adev) &&
     !amdgpu_ras_intr_triggered()) {
  info = kzalloc(sizeof(union ta_ras_cmd_input), GFP_KERNEL);
  if (!info)
   return -ENOMEM;

  if (!enable) {
   info->disable_features = (struct ta_ras_disable_features_input) {
    .block_id =  amdgpu_ras_block_to_ta(head->block),
    .error_type = amdgpu_ras_error_to_ta(head->type),
   };
  } else {
   info->enable_features = (struct ta_ras_enable_features_input) {
    .block_id =  amdgpu_ras_block_to_ta(head->block),
    .error_type = amdgpu_ras_error_to_ta(head->type),
   };
  }

  ret = psp_ras_enable_features(&adev->psp, info, enable);
  if (ret) {
   dev_err(adev->dev, "ras %s %s failed poison:%d ret:%d\n",
    enable ? "enable":"disable",
    get_ras_block_str(head),
    amdgpu_ras_is_poison_mode_supported(adev), ret);
   kfree(info);
   return ret;
  }

  kfree(info);
 }

 /* setup the obj */
 __amdgpu_ras_feature_enable(adev, head, enable);

 return 0;
}

/* Only used in device probe stage and called only once. */
int amdgpu_ras_feature_enable_on_boot(struct amdgpu_device *adev,
  struct ras_common_if *head, bool enable)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 int ret;

 if (!con)
  return -EINVAL;

 if (con->flags & AMDGPU_RAS_FLAG_INIT_BY_VBIOS) {
  if (enable) {
   /* There is no harm to issue a ras TA cmd regardless of
 * the currecnt ras state.
 * If current state == target state, it will do nothing
 * But sometimes it requests driver to reset and repost
 * with error code -EAGAIN.
 */

   ret = amdgpu_ras_feature_enable(adev, head, 1);
   /* With old ras TA, we might fail to enable ras.
 * Log it and just setup the object.
 * TODO need remove this WA in the future.
 */

   if (ret == -EINVAL) {
    ret = __amdgpu_ras_feature_enable(adev, head, 1);
    if (!ret)
     dev_info(adev->dev,
      "RAS INFO: %s setup object\n",
      get_ras_block_str(head));
   }
  } else {
   /* setup the object then issue a ras TA disable cmd.*/
   ret = __amdgpu_ras_feature_enable(adev, head, 1);
   if (ret)
    return ret;

   /* gfx block ras disable cmd must send to ras-ta */
   if (head->block == AMDGPU_RAS_BLOCK__GFX)
    con->features |= BIT(head->block);

   ret = amdgpu_ras_feature_enable(adev, head, 0);

   /* clean gfx block ras features flag */
   if (adev->ras_enabled && head->block == AMDGPU_RAS_BLOCK__GFX)
    con->features &= ~BIT(head->block);
  }
 } else
  ret = amdgpu_ras_feature_enable(adev, head, enable);

 return ret;
}

static int amdgpu_ras_disable_all_features(struct amdgpu_device *adev,
  bool bypass)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct ras_manager *obj, *tmp;

 list_for_each_entry_safe(obj, tmp, &con->head, node) {
  /* bypass psp.
 * aka just release the obj and corresponding flags
 */

  if (bypass) {
   if (__amdgpu_ras_feature_enable(adev, &obj->head, 0))
    break;
  } else {
   if (amdgpu_ras_feature_enable(adev, &obj->head, 0))
    break;
  }
 }

 return con->features;
}

static int amdgpu_ras_enable_all_features(struct amdgpu_device *adev,
  bool bypass)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 int i;
 const enum amdgpu_ras_error_type default_ras_type = AMDGPU_RAS_ERROR__NONE;

 for (i = 0; i < AMDGPU_RAS_BLOCK_COUNT; i++) {
  struct ras_common_if head = {
   .block = i,
   .type = default_ras_type,
   .sub_block_index = 0,
  };

  if (i == AMDGPU_RAS_BLOCK__MCA)
   continue;

  if (bypass) {
   /*
 * bypass psp. vbios enable ras for us.
 * so just create the obj
 */

   if (__amdgpu_ras_feature_enable(adev, &head, 1))
    break;
  } else {
   if (amdgpu_ras_feature_enable(adev, &head, 1))
    break;
  }
 }

 for (i = 0; i < AMDGPU_RAS_MCA_BLOCK_COUNT; i++) {
  struct ras_common_if head = {
   .block = AMDGPU_RAS_BLOCK__MCA,
   .type = default_ras_type,
   .sub_block_index = i,
  };

  if (bypass) {
   /*
 * bypass psp. vbios enable ras for us.
 * so just create the obj
 */

   if (__amdgpu_ras_feature_enable(adev, &head, 1))
    break;
  } else {
   if (amdgpu_ras_feature_enable(adev, &head, 1))
    break;
  }
 }

 return con->features;
}
/* feature ctl end */

static int amdgpu_ras_block_match_default(struct amdgpu_ras_block_object *block_obj,
  enum amdgpu_ras_block block)
{
 if (!block_obj)
  return -EINVAL;

 if (block_obj->ras_comm.block == block)
  return 0;

 return -EINVAL;
}

static struct amdgpu_ras_block_object *amdgpu_ras_get_ras_block(struct amdgpu_device *adev,
     enum amdgpu_ras_block block, uint32_t sub_block_index)
{
 struct amdgpu_ras_block_list *node, *tmp;
 struct amdgpu_ras_block_object *obj;

 if (block >= AMDGPU_RAS_BLOCK__LAST)
  return NULL;

 list_for_each_entry_safe(node, tmp, &adev->ras_list, node) {
  if (!node->ras_obj) {
   dev_warn(adev->dev, "Warning: abnormal ras list node.\n");
   continue;
  }

  obj = node->ras_obj;
  if (obj->ras_block_match) {
   if (obj->ras_block_match(obj, block, sub_block_index) == 0)
    return obj;
  } else {
   if (amdgpu_ras_block_match_default(obj, block) == 0)
    return obj;
  }
 }

 return NULL;
}

static void amdgpu_ras_get_ecc_info(struct amdgpu_device *adev, struct ras_err_data *err_data)
{
 struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
 int ret = 0;

 /*
 * choosing right query method according to
 * whether smu support query error information
 */

 ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(ras->umc_ecc));
 if (ret == -EOPNOTSUPP) {
  if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
   adev->umc.ras->ras_block.hw_ops->query_ras_error_count)
   adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev, err_data);

  /* umc query_ras_error_address is also responsible for clearing
 * error status
 */

  if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
      adev->umc.ras->ras_block.hw_ops->query_ras_error_address)
   adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev, err_data);
 } else if (!ret) {
  if (adev->umc.ras &&
   adev->umc.ras->ecc_info_query_ras_error_count)
   adev->umc.ras->ecc_info_query_ras_error_count(adev, err_data);

  if (adev->umc.ras &&
   adev->umc.ras->ecc_info_query_ras_error_address)
   adev->umc.ras->ecc_info_query_ras_error_address(adev, err_data);
 }
}

static void amdgpu_ras_error_print_error_data(struct amdgpu_device *adev,
           struct ras_manager *ras_mgr,
           struct ras_err_data *err_data,
           struct ras_query_context *qctx,
           const char *blk_name,
           bool is_ue,
           bool is_de)
{
 struct amdgpu_smuio_mcm_config_info *mcm_info;
 struct ras_err_node *err_node;
 struct ras_err_info *err_info;
 u64 event_id = qctx->evid.event_id;

 if (is_ue) {
  for_each_ras_error(err_node, err_data) {
   err_info = &err_node->err_info;
   mcm_info = &err_info->mcm_info;
   if (err_info->ue_count) {
    RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d, "
           "%lld new uncorrectable hardware errors detected in %s block\n",
           mcm_info->socket_id,
           mcm_info->die_id,
           err_info->ue_count,
           blk_name);
   }
  }

  for_each_ras_error(err_node, &ras_mgr->err_data) {
   err_info = &err_node->err_info;
   mcm_info = &err_info->mcm_info;
   RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d, "
          "%lld uncorrectable hardware errors detected in total in %s block\n",
          mcm_info->socket_id, mcm_info->die_id, err_info->ue_count, blk_name);
  }

 } else {
  if (is_de) {
   for_each_ras_error(err_node, err_data) {
    err_info = &err_node->err_info;
    mcm_info = &err_info->mcm_info;
    if (err_info->de_count) {
     RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d, "
            "%lld new deferred hardware errors detected in %s block\n",
            mcm_info->socket_id,
            mcm_info->die_id,
            err_info->de_count,
            blk_name);
    }
   }

   for_each_ras_error(err_node, &ras_mgr->err_data) {
    err_info = &err_node->err_info;
    mcm_info = &err_info->mcm_info;
    RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d, "
           "%lld deferred hardware errors detected in total in %s block\n",
           mcm_info->socket_id, mcm_info->die_id,
           err_info->de_count, blk_name);
   }
  } else {
   if (adev->debug_disable_ce_logs)
    return;

   for_each_ras_error(err_node, err_data) {
    err_info = &err_node->err_info;
    mcm_info = &err_info->mcm_info;
    if (err_info->ce_count) {
     RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d, "
            "%lld new correctable hardware errors detected in %s block\n",
            mcm_info->socket_id,
            mcm_info->die_id,
            err_info->ce_count,
            blk_name);
    }
   }

   for_each_ras_error(err_node, &ras_mgr->err_data) {
    err_info = &err_node->err_info;
    mcm_info = &err_info->mcm_info;
    RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d, "
           "%lld correctable hardware errors detected in total in %s block\n",
           mcm_info->socket_id, mcm_info->die_id,
           err_info->ce_count, blk_name);
   }
  }
 }
}

static inline bool err_data_has_source_info(struct ras_err_data *data)
{
 return !list_empty(&data->err_node_list);
}

static void amdgpu_ras_error_generate_report(struct amdgpu_device *adev,
          struct ras_query_if *query_if,
          struct ras_err_data *err_data,
          struct ras_query_context *qctx)
{
 struct ras_manager *ras_mgr = amdgpu_ras_find_obj(adev, &query_if->head);
 const char *blk_name = get_ras_block_str(&query_if->head);
 u64 event_id = qctx->evid.event_id;

 if (err_data->ce_count) {
  if (err_data_has_source_info(err_data)) {
   amdgpu_ras_error_print_error_data(adev, ras_mgr, err_data, qctx,
         blk_name, falsefalse);
  } else if (!adev->aid_mask &&
      adev->smuio.funcs &&
      adev->smuio.funcs->get_socket_id &&
      adev->smuio.funcs->get_die_id) {
   RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d "
          "%ld correctable hardware errors "
          "detected in %s block\n",
          adev->smuio.funcs->get_socket_id(adev),
          adev->smuio.funcs->get_die_id(adev),
          ras_mgr->err_data.ce_count,
          blk_name);
  } else {
   RAS_EVENT_LOG(adev, event_id, "%ld correctable hardware errors "
          "detected in %s block\n",
          ras_mgr->err_data.ce_count,
          blk_name);
  }
 }

 if (err_data->ue_count) {
  if (err_data_has_source_info(err_data)) {
   amdgpu_ras_error_print_error_data(adev, ras_mgr, err_data, qctx,
         blk_name, truefalse);
  } else if (!adev->aid_mask &&
      adev->smuio.funcs &&
      adev->smuio.funcs->get_socket_id &&
      adev->smuio.funcs->get_die_id) {
   RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d "
          "%ld uncorrectable hardware errors "
          "detected in %s block\n",
          adev->smuio.funcs->get_socket_id(adev),
          adev->smuio.funcs->get_die_id(adev),
          ras_mgr->err_data.ue_count,
          blk_name);
  } else {
   RAS_EVENT_LOG(adev, event_id, "%ld uncorrectable hardware errors "
          "detected in %s block\n",
          ras_mgr->err_data.ue_count,
          blk_name);
  }
 }

 if (err_data->de_count) {
  if (err_data_has_source_info(err_data)) {
   amdgpu_ras_error_print_error_data(adev, ras_mgr, err_data, qctx,
         blk_name, falsetrue);
  } else if (!adev->aid_mask &&
      adev->smuio.funcs &&
      adev->smuio.funcs->get_socket_id &&
      adev->smuio.funcs->get_die_id) {
   RAS_EVENT_LOG(adev, event_id, "socket: %d, die: %d "
          "%ld deferred hardware errors "
          "detected in %s block\n",
          adev->smuio.funcs->get_socket_id(adev),
          adev->smuio.funcs->get_die_id(adev),
          ras_mgr->err_data.de_count,
          blk_name);
  } else {
   RAS_EVENT_LOG(adev, event_id, "%ld deferred hardware errors "
          "detected in %s block\n",
          ras_mgr->err_data.de_count,
          blk_name);
  }
 }
}

static void amdgpu_ras_virt_error_generate_report(struct amdgpu_device *adev,
        struct ras_query_if *query_if,
        struct ras_err_data *err_data,
        struct ras_query_context *qctx)
{
 unsigned long new_ue, new_ce, new_de;
 struct ras_manager *obj = amdgpu_ras_find_obj(adev, &query_if->head);
 const char *blk_name = get_ras_block_str(&query_if->head);
 u64 event_id = qctx->evid.event_id;

 new_ce = err_data->ce_count - obj->err_data.ce_count;
 new_ue = err_data->ue_count - obj->err_data.ue_count;
 new_de = err_data->de_count - obj->err_data.de_count;

 if (new_ce) {
  RAS_EVENT_LOG(adev, event_id, "%lu correctable hardware errors "
         "detected in %s block\n",
         new_ce,
         blk_name);
 }

 if (new_ue) {
  RAS_EVENT_LOG(adev, event_id, "%lu uncorrectable hardware errors "
         "detected in %s block\n",
         new_ue,
         blk_name);
 }

 if (new_de) {
  RAS_EVENT_LOG(adev, event_id, "%lu deferred hardware errors "
         "detected in %s block\n",
         new_de,
         blk_name);
 }
}

static void amdgpu_rasmgr_error_data_statistic_update(struct ras_manager *obj, struct ras_err_data *err_data)
{
 struct ras_err_node *err_node;
 struct ras_err_info *err_info;

 if (err_data_has_source_info(err_data)) {
  for_each_ras_error(err_node, err_data) {
   err_info = &err_node->err_info;
   amdgpu_ras_error_statistic_de_count(&obj->err_data,
     &err_info->mcm_info, err_info->de_count);
   amdgpu_ras_error_statistic_ce_count(&obj->err_data,
     &err_info->mcm_info, err_info->ce_count);
   amdgpu_ras_error_statistic_ue_count(&obj->err_data,
     &err_info->mcm_info, err_info->ue_count);
  }
 } else {
  /* for legacy asic path which doesn't has error source info */
  obj->err_data.ue_count += err_data->ue_count;
  obj->err_data.ce_count += err_data->ce_count;
  obj->err_data.de_count += err_data->de_count;
 }
}

static void amdgpu_ras_mgr_virt_error_data_statistics_update(struct ras_manager *obj,
            struct ras_err_data *err_data)
{
 /* Host reports absolute counts */
 obj->err_data.ue_count = err_data->ue_count;
 obj->err_data.ce_count = err_data->ce_count;
 obj->err_data.de_count = err_data->de_count;
}

static struct ras_manager *get_ras_manager(struct amdgpu_device *adev, enum amdgpu_ras_block blk)
{
 struct ras_common_if head;

 memset(&head, 0, sizeof(head));
 head.block = blk;

 return amdgpu_ras_find_obj(adev, &head);
}

int amdgpu_ras_bind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
   const struct aca_info *aca_info, void *data)
{
 struct ras_manager *obj;

 /* in resume phase, no need to create aca fs node */
 if (adev->in_suspend || amdgpu_reset_in_recovery(adev))
  return 0;

 obj = get_ras_manager(adev, blk);
 if (!obj)
  return -EINVAL;

 return amdgpu_aca_add_handle(adev, &obj->aca_handle, ras_block_str(blk), aca_info, data);
}

int amdgpu_ras_unbind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk)
{
 struct ras_manager *obj;

 obj = get_ras_manager(adev, blk);
 if (!obj)
  return -EINVAL;

 amdgpu_aca_remove_handle(&obj->aca_handle);

 return 0;
}

static int amdgpu_aca_log_ras_error_data(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
      enum aca_error_type type, struct ras_err_data *err_data,
      struct ras_query_context *qctx)
{
 struct ras_manager *obj;

 obj = get_ras_manager(adev, blk);
 if (!obj)
  return -EINVAL;

 return amdgpu_aca_get_error_data(adev, &obj->aca_handle, type, err_data, qctx);
}

ssize_t amdgpu_ras_aca_sysfs_read(struct device *dev, struct device_attribute *attr,
      struct aca_handle *handle, char *buf, void *data)
{
 struct ras_manager *obj = container_of(handle, struct ras_manager, aca_handle);
 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");

 if (amdgpu_ras_query_error_status(obj->adev, &info))
  return -EINVAL;

 return sysfs_emit(buf, "%s: %lu\n%s: %lu\n%s: %lu\n""ue", info.ue_count,
     "ce", info.ce_count, "de", info.de_count);
}

static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev,
      struct ras_query_if *info,
      struct ras_err_data *err_data,
      struct ras_query_context *qctx,
      unsigned int error_query_mode)
{
 enum amdgpu_ras_block blk = info ? info->head.block : AMDGPU_RAS_BLOCK_COUNT;
 struct amdgpu_ras_block_object *block_obj = NULL;
 int ret;

 if (blk == AMDGPU_RAS_BLOCK_COUNT)
  return -EINVAL;

 if (error_query_mode == AMDGPU_RAS_INVALID_ERROR_QUERY)
  return -EINVAL;

 if (error_query_mode == AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY) {
  return amdgpu_virt_req_ras_err_count(adev, blk, err_data);
 } else if (error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) {
  if (info->head.block == AMDGPU_RAS_BLOCK__UMC) {
   amdgpu_ras_get_ecc_info(adev, err_data);
  } else {
   block_obj = amdgpu_ras_get_ras_block(adev, info->head.block, 0);
   if (!block_obj || !block_obj->hw_ops) {
    dev_dbg_once(adev->dev, "%s doesn't config RAS function\n",
          get_ras_block_str(&info->head));
    return -EINVAL;
   }

   if (block_obj->hw_ops->query_ras_error_count)
    block_obj->hw_ops->query_ras_error_count(adev, err_data);

   if ((info->head.block == AMDGPU_RAS_BLOCK__SDMA) ||
       (info->head.block == AMDGPU_RAS_BLOCK__GFX) ||
       (info->head.block == AMDGPU_RAS_BLOCK__MMHUB)) {
    if (block_obj->hw_ops->query_ras_error_status)
     block_obj->hw_ops->query_ras_error_status(adev);
   }
  }
 } else {
  if (amdgpu_aca_is_enabled(adev)) {
   ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_UE, err_data, qctx);
   if (ret)
    return ret;

   ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_CE, err_data, qctx);
   if (ret)
    return ret;

   ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_DEFERRED, err_data, qctx);
   if (ret)
    return ret;
  } else {
   /* FIXME: add code to check return value later */
   amdgpu_mca_smu_log_ras_error(adev, blk, AMDGPU_MCA_ERROR_TYPE_UE, err_data, qctx);
   amdgpu_mca_smu_log_ras_error(adev, blk, AMDGPU_MCA_ERROR_TYPE_CE, err_data, qctx);
  }
 }

 return 0;
}

/* query/inject/cure begin */
static int amdgpu_ras_query_error_status_with_event(struct amdgpu_device *adev,
          struct ras_query_if *info,
          enum ras_event_type type)
{
 struct ras_manager *obj = amdgpu_ras_find_obj(adev, &info->head);
 struct ras_err_data err_data;
 struct ras_query_context qctx;
 unsigned int error_query_mode;
 int ret;

 if (!obj)
  return -EINVAL;

 ret = amdgpu_ras_error_data_init(&err_data);
 if (ret)
  return ret;

 if (!amdgpu_ras_get_error_query_mode(adev, &error_query_mode))
  return -EINVAL;

 memset(&qctx, 0, sizeof(qctx));
 qctx.evid.type = type;
 qctx.evid.event_id = amdgpu_ras_acquire_event_id(adev, type);

 if (!down_read_trylock(&adev->reset_domain->sem)) {
  ret = -EIO;
  goto out_fini_err_data;
 }

 ret = amdgpu_ras_query_error_status_helper(adev, info,
         &err_data,
         &qctx,
         error_query_mode);
 up_read(&adev->reset_domain->sem);
 if (ret)
  goto out_fini_err_data;

 if (error_query_mode != AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY) {
  amdgpu_rasmgr_error_data_statistic_update(obj, &err_data);
  amdgpu_ras_error_generate_report(adev, info, &err_data, &qctx);
 } else {
  /* Host provides absolute error counts. First generate the report
 * using the previous VF internal count against new host count.
 * Then Update VF internal count.
 */

  amdgpu_ras_virt_error_generate_report(adev, info, &err_data, &qctx);
  amdgpu_ras_mgr_virt_error_data_statistics_update(obj, &err_data);
 }

 info->ue_count = obj->err_data.ue_count;
 info->ce_count = obj->err_data.ce_count;
 info->de_count = obj->err_data.de_count;

out_fini_err_data:
 amdgpu_ras_error_data_fini(&err_data);

 return ret;
}

int amdgpu_ras_query_error_status(struct amdgpu_device *adev, struct ras_query_if *info)
{
 return amdgpu_ras_query_error_status_with_event(adev, info, RAS_EVENT_TYPE_INVALID);
}

int amdgpu_ras_reset_error_count(struct amdgpu_device *adev,
  enum amdgpu_ras_block block)
{
 struct amdgpu_ras_block_object *block_obj = amdgpu_ras_get_ras_block(adev, block, 0);
 const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
 const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs;

 if (!block_obj || !block_obj->hw_ops) {
  dev_dbg_once(adev->dev, "%s doesn't config RAS function\n",
    ras_block_str(block));
  return -EOPNOTSUPP;
 }

 if (!amdgpu_ras_is_supported(adev, block) ||
     !amdgpu_ras_get_aca_debug_mode(adev))
  return -EOPNOTSUPP;

 if (amdgpu_sriov_vf(adev))
  return -EOPNOTSUPP;

 /* skip ras error reset in gpu reset */
 if ((amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) &&
     ((smu_funcs && smu_funcs->set_debug_mode) ||
      (mca_funcs && mca_funcs->mca_set_debug_mode)))
  return -EOPNOTSUPP;

 if (block_obj->hw_ops->reset_ras_error_count)
  block_obj->hw_ops->reset_ras_error_count(adev);

 return 0;
}

int amdgpu_ras_reset_error_status(struct amdgpu_device *adev,
  enum amdgpu_ras_block block)
{
 struct amdgpu_ras_block_object *block_obj = amdgpu_ras_get_ras_block(adev, block, 0);

 if (amdgpu_ras_reset_error_count(adev, block) == -EOPNOTSUPP)
  return 0;

 if ((block == AMDGPU_RAS_BLOCK__GFX) ||
     (block == AMDGPU_RAS_BLOCK__MMHUB)) {
  if (block_obj->hw_ops->reset_ras_error_status)
   block_obj->hw_ops->reset_ras_error_status(adev);
 }

 return 0;
}

/* wrapper of psp_ras_trigger_error */
int amdgpu_ras_error_inject(struct amdgpu_device *adev,
  struct ras_inject_if *info)
{
 struct ras_manager *obj = amdgpu_ras_find_obj(adev, &info->head);
 struct ta_ras_trigger_error_input block_info = {
  .block_id =  amdgpu_ras_block_to_ta(info->head.block),
  .inject_error_type = amdgpu_ras_error_to_ta(info->head.type),
  .sub_block_index = info->head.sub_block_index,
  .address = info->address,
  .value = info->value,
 };
 int ret = -EINVAL;
 struct amdgpu_ras_block_object *block_obj = amdgpu_ras_get_ras_block(adev,
       info->head.block,
       info->head.sub_block_index);

 /* inject on guest isn't allowed, return success directly */
 if (amdgpu_sriov_vf(adev))
  return 0;

 if (!obj)
  return -EINVAL;

 if (!block_obj || !block_obj->hw_ops) {
  dev_dbg_once(adev->dev, "%s doesn't config RAS function\n",
        get_ras_block_str(&info->head));
  return -EINVAL;
 }

 /* Calculate XGMI relative offset */
 if (adev->gmc.xgmi.num_physical_nodes > 1 &&
     info->head.block != AMDGPU_RAS_BLOCK__GFX) {
  block_info.address =
   amdgpu_xgmi_get_relative_phy_addr(adev,
         block_info.address);
 }

 if (block_obj->hw_ops->ras_error_inject) {
  if (info->head.block == AMDGPU_RAS_BLOCK__GFX)
   ret = block_obj->hw_ops->ras_error_inject(adev, info, info->instance_mask);
  else /* Special ras_error_inject is defined (e.g: xgmi) */
   ret = block_obj->hw_ops->ras_error_inject(adev, &block_info,
      info->instance_mask);
 } else {
  /* default path */
  ret = psp_ras_trigger_error(&adev->psp, &block_info, info->instance_mask);
 }

 if (ret)
  dev_err(adev->dev, "ras inject %s failed %d\n",
   get_ras_block_str(&info->head), ret);

 return ret;
}

/**
 * amdgpu_ras_query_error_count_helper -- Get error counter for specific IP
 * @adev: pointer to AMD GPU device
 * @ce_count: pointer to an integer to be set to the count of correctible errors.
 * @ue_count: pointer to an integer to be set to the count of uncorrectible errors.
 * @query_info: pointer to ras_query_if
 *
 * Return 0 for query success or do nothing, otherwise return an error
 * on failures
 */

static int amdgpu_ras_query_error_count_helper(struct amdgpu_device *adev,
            unsigned long *ce_count,
            unsigned long *ue_count,
            struct ras_query_if *query_info)
{
 int ret;

 if (!query_info)
  /* do nothing if query_info is not specified */
  return 0;

 ret = amdgpu_ras_query_error_status(adev, query_info);
 if (ret)
  return ret;

 *ce_count += query_info->ce_count;
 *ue_count += query_info->ue_count;

 /* some hardware/IP supports read to clear
 * no need to explictly reset the err status after the query call */

 if (amdgpu_ip_version(adev, MP0_HWIP, 0) != IP_VERSION(11, 0, 2) &&
     amdgpu_ip_version(adev, MP0_HWIP, 0) != IP_VERSION(11, 0, 4)) {
  if (amdgpu_ras_reset_error_status(adev, query_info->head.block))
   dev_warn(adev->dev,
     "Failed to reset error counter and error status\n");
 }

 return 0;
}

/**
 * amdgpu_ras_query_error_count -- Get error counts of all IPs or specific IP
 * @adev: pointer to AMD GPU device
 * @ce_count: pointer to an integer to be set to the count of correctible errors.
 * @ue_count: pointer to an integer to be set to the count of uncorrectible
 * errors.
 * @query_info: pointer to ras_query_if if the query request is only for
 * specific ip block; if info is NULL, then the qurey request is for
 * all the ip blocks that support query ras error counters/status
 *
 * If set, @ce_count or @ue_count, count and return the corresponding
 * error counts in those integer pointers. Return 0 if the device
 * supports RAS. Return -EOPNOTSUPP if the device doesn't support RAS.
 */

int amdgpu_ras_query_error_count(struct amdgpu_device *adev,
     unsigned long *ce_count,
     unsigned long *ue_count,
     struct ras_query_if *query_info)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct ras_manager *obj;
 unsigned long ce, ue;
 int ret;

 if (!adev->ras_enabled || !con)
  return -EOPNOTSUPP;

 /* Don't count since no reporting.
 */

 if (!ce_count && !ue_count)
  return 0;

 ce = 0;
 ue = 0;
 if (!query_info) {
  /* query all the ip blocks that support ras query interface */
  list_for_each_entry(obj, &con->head, node) {
   struct ras_query_if info = {
    .head = obj->head,
   };

   ret = amdgpu_ras_query_error_count_helper(adev, &ce, &ue, &info);
  }
 } else {
  /* query specific ip block */
  ret = amdgpu_ras_query_error_count_helper(adev, &ce, &ue, query_info);
 }

 if (ret)
  return ret;

 if (ce_count)
  *ce_count = ce;

 if (ue_count)
  *ue_count = ue;

 return 0;
}
/* query/inject/cure end */


/* sysfs begin */

static int amdgpu_ras_badpages_read(struct amdgpu_device *adev,
  struct ras_badpage **bps, unsigned int *count);

static char *amdgpu_ras_badpage_flags_str(unsigned int flags)
{
 switch (flags) {
 case AMDGPU_RAS_RETIRE_PAGE_RESERVED:
  return "R";
 case AMDGPU_RAS_RETIRE_PAGE_PENDING:
  return "P";
 case AMDGPU_RAS_RETIRE_PAGE_FAULT:
 default:
  return "F";
 }
}

/**
 * DOC: AMDGPU RAS sysfs gpu_vram_bad_pages Interface
 *
 * It allows user to read the bad pages of vram on the gpu through
 * /sys/class/drm/card[0/1/2...]/device/ras/gpu_vram_bad_pages
 *
 * It outputs multiple lines, and each line stands for one gpu page.
 *
 * The format of one line is below,
 * gpu pfn : gpu page size : flags
 *
 * gpu pfn and gpu page size are printed in hex format.
 * flags can be one of below character,
 *
 * R: reserved, this gpu page is reserved and not able to use.
 *
 * P: pending for reserve, this gpu page is marked as bad, will be reserved
 * in next window of page_reserve.
 *
 * F: unable to reserve. this gpu page can't be reserved due to some reasons.
 *
 * Examples:
 *
 * .. code-block:: bash
 *
 * 0x00000001 : 0x00001000 : R
 * 0x00000002 : 0x00001000 : P
 *
 */


static ssize_t amdgpu_ras_sysfs_badpages_read(struct file *f,
  struct kobject *kobj, const struct bin_attribute *attr,
  char *buf, loff_t ppos, size_t count)
{
 struct amdgpu_ras *con =
  container_of(attr, struct amdgpu_ras, badpages_attr);
 struct amdgpu_device *adev = con->adev;
 const unsigned int element_size =
  sizeof("0xabcdabcd : 0x12345678 : R\n") - 1;
 unsigned int start = div64_ul(ppos + element_size - 1, element_size);
 unsigned int end = div64_ul(ppos + count - 1, element_size);
 ssize_t s = 0;
 struct ras_badpage *bps = NULL;
 unsigned int bps_count = 0;

 memset(buf, 0, count);

 if (amdgpu_ras_badpages_read(adev, &bps, &bps_count))
  return 0;

 for (; start < end && start < bps_count; start++)
  s += scnprintf(&buf[s], element_size + 1,
    "0x%08x : 0x%08x : %1s\n",
    bps[start].bp,
    bps[start].size,
    amdgpu_ras_badpage_flags_str(bps[start].flags));

 kfree(bps);

 return s;
}

static ssize_t amdgpu_ras_sysfs_features_read(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct amdgpu_ras *con =
  container_of(attr, struct amdgpu_ras, features_attr);

 return sysfs_emit(buf, "feature mask: 0x%x\n", con->features);
}

static ssize_t amdgpu_ras_sysfs_version_show(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct amdgpu_ras *con =
  container_of(attr, struct amdgpu_ras, version_attr);
 return sysfs_emit(buf, "table version: 0x%x\n", con->eeprom_control.tbl_hdr.version);
}

static ssize_t amdgpu_ras_sysfs_schema_show(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct amdgpu_ras *con =
  container_of(attr, struct amdgpu_ras, schema_attr);
 return sysfs_emit(buf, "schema: 0x%x\n", con->schema);
}

static struct {
 enum ras_event_type type;
 const char *name;
} dump_event[] = {
 {RAS_EVENT_TYPE_FATAL, "Fatal Error"},
 {RAS_EVENT_TYPE_POISON_CREATION, "Poison Creation"},
 {RAS_EVENT_TYPE_POISON_CONSUMPTION, "Poison Consumption"},
};

static ssize_t amdgpu_ras_sysfs_event_state_show(struct device *dev,
       struct device_attribute *attr, char *buf)
{
 struct amdgpu_ras *con =
  container_of(attr, struct amdgpu_ras, event_state_attr);
 struct ras_event_manager *event_mgr = con->event_mgr;
 struct ras_event_state *event_state;
 int i, size = 0;

 if (!event_mgr)
  return -EINVAL;

 size += sysfs_emit_at(buf, size, "current seqno: %llu\n", atomic64_read(&event_mgr->seqno));
 for (i = 0; i < ARRAY_SIZE(dump_event); i++) {
  event_state = &event_mgr->event_state[dump_event[i].type];
  size += sysfs_emit_at(buf, size, "%s: count:%llu, last_seqno:%llu\n",
          dump_event[i].name,
          atomic64_read(&event_state->count),
          event_state->last_seqno);
 }

 return (ssize_t)size;
}

static void amdgpu_ras_sysfs_remove_bad_page_node(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);

 if (adev->dev->kobj.sd)
  sysfs_remove_file_from_group(&adev->dev->kobj,
    &con->badpages_attr.attr,
    RAS_FS_NAME);
}

static int amdgpu_ras_sysfs_remove_dev_attr_node(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct attribute *attrs[] = {
  &con->features_attr.attr,
  &con->version_attr.attr,
  &con->schema_attr.attr,
  &con->event_state_attr.attr,
  NULL
 };
 struct attribute_group group = {
  .name = RAS_FS_NAME,
  .attrs = attrs,
 };

 if (adev->dev->kobj.sd)
  sysfs_remove_group(&adev->dev->kobj, &group);

 return 0;
}

int amdgpu_ras_sysfs_create(struct amdgpu_device *adev,
  struct ras_common_if *head)
{
 struct ras_manager *obj = amdgpu_ras_find_obj(adev, head);

 if (amdgpu_aca_is_enabled(adev))
  return 0;

 if (!obj || obj->attr_inuse)
  return -EINVAL;

 if (amdgpu_sriov_vf(adev) && !amdgpu_virt_ras_telemetry_block_en(adev, head->block))
  return 0;

 get_obj(obj);

 snprintf(obj->fs_data.sysfs_name, sizeof(obj->fs_data.sysfs_name),
  "%s_err_count", head->name);

 obj->sysfs_attr = (struct device_attribute){
  .attr = {
   .name = obj->fs_data.sysfs_name,
   .mode = S_IRUGO,
  },
   .show = amdgpu_ras_sysfs_read,
 };
 sysfs_attr_init(&obj->sysfs_attr.attr);

 if (sysfs_add_file_to_group(&adev->dev->kobj,
    &obj->sysfs_attr.attr,
    RAS_FS_NAME)) {
  put_obj(obj);
  return -EINVAL;
 }

 obj->attr_inuse = 1;

 return 0;
}

int amdgpu_ras_sysfs_remove(struct amdgpu_device *adev,
  struct ras_common_if *head)
{
 struct ras_manager *obj = amdgpu_ras_find_obj(adev, head);

 if (amdgpu_aca_is_enabled(adev))
  return 0;

 if (!obj || !obj->attr_inuse)
  return -EINVAL;

 if (adev->dev->kobj.sd)
  sysfs_remove_file_from_group(&adev->dev->kobj,
    &obj->sysfs_attr.attr,
    RAS_FS_NAME);
 obj->attr_inuse = 0;
 put_obj(obj);

 return 0;
}

static int amdgpu_ras_sysfs_remove_all(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct ras_manager *obj, *tmp;

 list_for_each_entry_safe(obj, tmp, &con->head, node) {
  amdgpu_ras_sysfs_remove(adev, &obj->head);
 }

 if (amdgpu_bad_page_threshold != 0)
  amdgpu_ras_sysfs_remove_bad_page_node(adev);

 amdgpu_ras_sysfs_remove_dev_attr_node(adev);

 return 0;
}
/* sysfs end */

/**
 * DOC: AMDGPU RAS Reboot Behavior for Unrecoverable Errors
 *
 * Normally when there is an uncorrectable error, the driver will reset
 * the GPU to recover.  However, in the event of an unrecoverable error,
 * the driver provides an interface to reboot the system automatically
 * in that event.
 *
 * The following file in debugfs provides that interface:
 * /sys/kernel/debug/dri/[0/1/2...]/ras/auto_reboot
 *
 * Usage:
 *
 * .. code-block:: bash
 *
 * echo true > .../ras/auto_reboot
 *
 */

/* debugfs begin */
static struct dentry *amdgpu_ras_debugfs_create_ctrl_node(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct amdgpu_ras_eeprom_control *eeprom = &con->eeprom_control;
 struct drm_minor  *minor = adev_to_drm(adev)->primary;
 struct dentry     *dir;

 dir = debugfs_create_dir(RAS_FS_NAME, minor->debugfs_root);
 debugfs_create_file("ras_ctrl", S_IWUGO | S_IRUGO, dir, adev,
       &amdgpu_ras_debugfs_ctrl_ops);
 debugfs_create_file("ras_eeprom_reset", S_IWUGO | S_IRUGO, dir, adev,
       &amdgpu_ras_debugfs_eeprom_ops);
 debugfs_create_u32("bad_page_cnt_threshold", 0444, dir,
      &con->bad_page_cnt_threshold);
 debugfs_create_u32("ras_num_recs", 0444, dir, &eeprom->ras_num_recs);
 debugfs_create_x32("ras_hw_enabled", 0444, dir, &adev->ras_hw_enabled);
 debugfs_create_x32("ras_enabled", 0444, dir, &adev->ras_enabled);
 debugfs_create_file("ras_eeprom_size", S_IRUGO, dir, adev,
       &amdgpu_ras_debugfs_eeprom_size_ops);
 con->de_ras_eeprom_table = debugfs_create_file("ras_eeprom_table",
             S_IRUGO, dir, adev,
             &amdgpu_ras_debugfs_eeprom_table_ops);
 amdgpu_ras_debugfs_set_ret_size(&con->eeprom_control);

 /*
 * After one uncorrectable error happens, usually GPU recovery will
 * be scheduled. But due to the known problem in GPU recovery failing
 * to bring GPU back, below interface provides one direct way to
 * user to reboot system automatically in such case within
 * ERREVENT_ATHUB_INTERRUPT generated. Normal GPU recovery routine
 * will never be called.
 */

 debugfs_create_bool("auto_reboot", S_IWUGO | S_IRUGO, dir, &con->reboot);

 /*
 * User could set this not to clean up hardware's error count register
 * of RAS IPs during ras recovery.
 */

 debugfs_create_bool("disable_ras_err_cnt_harvest", 0644, dir,
       &con->disable_ras_err_cnt_harvest);
 return dir;
}

static void amdgpu_ras_debugfs_create(struct amdgpu_device *adev,
          struct ras_fs_if *head,
          struct dentry *dir)
{
 struct ras_manager *obj = amdgpu_ras_find_obj(adev, &head->head);

 if (!obj || !dir)
  return;

 get_obj(obj);

 memcpy(obj->fs_data.debugfs_name,
   head->debugfs_name,
   sizeof(obj->fs_data.debugfs_name));

 debugfs_create_file(obj->fs_data.debugfs_name, S_IWUGO | S_IRUGO, dir,
       obj, &amdgpu_ras_debugfs_ops);
}

static bool amdgpu_ras_aca_is_supported(struct amdgpu_device *adev)
{
 bool ret;

 switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) {
 case IP_VERSION(13, 0, 6):
 case IP_VERSION(13, 0, 12):
 case IP_VERSION(13, 0, 14):
  ret = true;
  break;
 default:
  ret = false;
  break;
 }

 return ret;
}

void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct dentry *dir;
 struct ras_manager *obj;
 struct ras_fs_if fs_info;

 /*
 * it won't be called in resume path, no need to check
 * suspend and gpu reset status
 */

 if (!IS_ENABLED(CONFIG_DEBUG_FS) || !con)
  return;

 dir = amdgpu_ras_debugfs_create_ctrl_node(adev);

 list_for_each_entry(obj, &con->head, node) {
  if (amdgpu_ras_is_supported(adev, obj->head.block) &&
   (obj->attr_inuse == 1)) {
   sprintf(fs_info.debugfs_name, "%s_err_inject",
     get_ras_block_str(&obj->head));
   fs_info.head = obj->head;
   amdgpu_ras_debugfs_create(adev, &fs_info, dir);
  }
 }

 if (amdgpu_ras_aca_is_supported(adev)) {
  if (amdgpu_aca_is_enabled(adev))
   amdgpu_aca_smu_debugfs_init(adev, dir);
  else
   amdgpu_mca_smu_debugfs_init(adev, dir);
 }
}

/* debugfs end */

/* ras fs */
static const BIN_ATTR(gpu_vram_bad_pages, S_IRUGO,
        amdgpu_ras_sysfs_badpages_read, NULL, 0);
static DEVICE_ATTR(features, S_IRUGO,
  amdgpu_ras_sysfs_features_read, NULL);
static DEVICE_ATTR(version, 0444,
  amdgpu_ras_sysfs_version_show, NULL);
static DEVICE_ATTR(schema, 0444,
  amdgpu_ras_sysfs_schema_show, NULL);
static DEVICE_ATTR(event_state, 0444,
     amdgpu_ras_sysfs_event_state_show, NULL);
static int amdgpu_ras_fs_init(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct attribute_group group = {
  .name = RAS_FS_NAME,
 };
 struct attribute *attrs[] = {
  &con->features_attr.attr,
  &con->version_attr.attr,
  &con->schema_attr.attr,
  &con->event_state_attr.attr,
  NULL
 };
 const struct bin_attribute *bin_attrs[] = {
  NULL,
  NULL,
 };
 int r;

 group.attrs = attrs;

 /* add features entry */
 con->features_attr = dev_attr_features;
 sysfs_attr_init(attrs[0]);

 /* add version entry */
 con->version_attr = dev_attr_version;
 sysfs_attr_init(attrs[1]);

 /* add schema entry */
 con->schema_attr = dev_attr_schema;
 sysfs_attr_init(attrs[2]);

 /* add event_state entry */
 con->event_state_attr = dev_attr_event_state;
 sysfs_attr_init(attrs[3]);

 if (amdgpu_bad_page_threshold != 0) {
  /* add bad_page_features entry */
  con->badpages_attr = bin_attr_gpu_vram_bad_pages;
  sysfs_bin_attr_init(&con->badpages_attr);
  bin_attrs[0] = &con->badpages_attr;
  group.bin_attrs = bin_attrs;
 }

 r = sysfs_create_group(&adev->dev->kobj, &group);
 if (r)
  dev_err(adev->dev, "Failed to create RAS sysfs group!");

 return 0;
}

static int amdgpu_ras_fs_fini(struct amdgpu_device *adev)
{
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 struct ras_manager *con_obj, *ip_obj, *tmp;

 if (IS_ENABLED(CONFIG_DEBUG_FS)) {
  list_for_each_entry_safe(con_obj, tmp, &con->head, node) {
   ip_obj = amdgpu_ras_find_obj(adev, &con_obj->head);
   if (ip_obj)
    put_obj(ip_obj);
  }
 }

 amdgpu_ras_sysfs_remove_all(adev);
 return 0;
}
/* ras fs end */

/* ih begin */

/* For the hardware that cannot enable bif ring for both ras_controller_irq
 * and ras_err_evnet_athub_irq ih cookies, the driver has to poll status
 * register to check whether the interrupt is triggered or not, and properly
 * ack the interrupt if it is there
 */

void amdgpu_ras_interrupt_fatal_error_handler(struct amdgpu_device *adev)
{
 /* Fatal error events are handled on host side */
 if (amdgpu_sriov_vf(adev))
  return;
 /*
 * If the current interrupt is caused by a non-fatal RAS error, skip
 * check for fatal error. For fatal errors, FED status of all devices
 * in XGMI hive gets set when the first device gets fatal error
 * interrupt. The error gets propagated to other devices as well, so
 * make sure to ack the interrupt regardless of FED status.
 */

 if (!amdgpu_ras_get_fed_status(adev) &&
     amdgpu_ras_is_err_state(adev, AMDGPU_RAS_BLOCK__ANY))
  return;

 if (adev->nbio.ras &&
     adev->nbio.ras->handle_ras_controller_intr_no_bifring)
  adev->nbio.ras->handle_ras_controller_intr_no_bifring(adev);

 if (adev->nbio.ras &&
     adev->nbio.ras->handle_ras_err_event_athub_intr_no_bifring)
  adev->nbio.ras->handle_ras_err_event_athub_intr_no_bifring(adev);
}

static void amdgpu_ras_interrupt_poison_consumption_handler(struct ras_manager *obj,
    struct amdgpu_iv_entry *entry)
{
 bool poison_stat = false;
 struct amdgpu_device *adev = obj->adev;
 struct amdgpu_ras_block_object *block_obj =
  amdgpu_ras_get_ras_block(adev, obj->head.block, 0);
 struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
 enum ras_event_type type = RAS_EVENT_TYPE_POISON_CONSUMPTION;
 u64 event_id;
 int ret;

 if (!block_obj || !con)
  return;

 ret = amdgpu_ras_mark_ras_event(adev, type);
 if (ret)
  return;

 amdgpu_ras_set_err_poison(adev, block_obj->ras_comm.block);
 /* both query_poison_status and handle_poison_consumption are optional,
 * but at least one of them should be implemented if we need poison
 * consumption handler
 */

 if (block_obj->hw_ops && block_obj->hw_ops->query_poison_status) {
  poison_stat = block_obj->hw_ops->query_poison_status(adev);
  if (!poison_stat) {
   /* Not poison consumption interrupt, no need to handle it */
   dev_info(adev->dev, "No RAS poison status in %s poison IH.\n",
     block_obj->ras_comm.name);

   return;
  }
 }

 amdgpu_umc_poison_handler(adev, obj->head.block, 0);

 if (block_obj->hw_ops && block_obj->hw_ops->handle_poison_consumption)
  poison_stat = block_obj->hw_ops->handle_poison_consumption(adev);

 /* gpu reset is fallback for failed and default cases.
 * For RMA case, amdgpu_umc_poison_handler will handle gpu reset.
 */

 if (poison_stat && !amdgpu_ras_is_rma(adev)) {
  event_id = amdgpu_ras_acquire_event_id(adev, type);
  RAS_EVENT_LOG(adev, event_id,
         "GPU reset for %s RAS poison consumption is issued!\n",
         block_obj->ras_comm.name);
  amdgpu_ras_reset_gpu(adev);
 }

 if (!poison_stat)
  amdgpu_gfx_poison_consumption_handler(adev, entry);
}

static void amdgpu_ras_interrupt_poison_creation_handler(struct ras_manager *obj,
    struct amdgpu_iv_entry *entry)
{
 struct amdgpu_device *adev = obj->adev;
 enum ras_event_type type = RAS_EVENT_TYPE_POISON_CREATION;
 u64 event_id;
 int ret;

 ret = amdgpu_ras_mark_ras_event(adev, type);
 if (ret)
  return;

 event_id = amdgpu_ras_acquire_event_id(adev, type);
 RAS_EVENT_LOG(adev, event_id, "Poison is created\n");

 if (amdgpu_ip_version(obj->adev, UMC_HWIP, 0) >= IP_VERSION(12, 0, 0)) {
  struct amdgpu_ras *con = amdgpu_ras_get_context(obj->adev);

  atomic_inc(&con->page_retirement_req_cnt);
  atomic_inc(&con->poison_creation_count);

  wake_up(&con->page_retirement_wq);
 }
}

static void amdgpu_ras_interrupt_umc_handler(struct ras_manager *obj,
    struct amdgpu_iv_entry *entry)
{
 struct ras_ih_data *data = &obj->ih_data;
 struct ras_err_data err_data;
 int ret;

 if (!data->cb)
  return;

 ret = amdgpu_ras_error_data_init(&err_data);
 if (ret)
  return;

 /* Let IP handle its data, maybe we need get the output
 * from the callback to update the error type/count, etc
 */

 amdgpu_ras_set_fed(obj->adev, true);
 ret = data->cb(obj->adev, &err_data, entry);
 /* ue will trigger an interrupt, and in that case
 * we need do a reset to recovery the whole system.
 * But leave IP do that recovery, here we just dispatch
 * the error.
 */

 if (ret == AMDGPU_RAS_SUCCESS) {
  /* these counts could be left as 0 if
 * some blocks do not count error number
 */

  obj->err_data.ue_count += err_data.ue_count;
  obj->err_data.ce_count += err_data.ce_count;
  obj->err_data.de_count += err_data.de_count;
 }

 amdgpu_ras_error_data_fini(&err_data);
}

static void amdgpu_ras_interrupt_handler(struct ras_manager *obj)
{
 struct ras_ih_data *data = &obj->ih_data;
 struct amdgpu_iv_entry entry;

 while (data->rptr != data->wptr) {
  rmb();
  memcpy(&entry, &data->ring[data->rptr],
    data->element_size);

  wmb();
  data->rptr = (data->aligned_element_size +
    data->rptr) % data->ring_size;

  if (amdgpu_ras_is_poison_mode_supported(obj->adev)) {
   if (obj->head.block == AMDGPU_RAS_BLOCK__UMC)
    amdgpu_ras_interrupt_poison_creation_handler(obj, &entry);
   else
    amdgpu_ras_interrupt_poison_consumption_handler(obj, &entry);
  } else {
   if (obj->head.block == AMDGPU_RAS_BLOCK__UMC)
    amdgpu_ras_interrupt_umc_handler(obj, &entry);
   else
    dev_warn(obj->adev->dev,
     "No RAS interrupt handler for non-UMC block with poison disabled.\n");
  }
 }
}

static void amdgpu_ras_interrupt_process_handler(struct work_struct *work)
{
 struct ras_ih_data *data =
  container_of(work, struct ras_ih_data, ih_work);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.