public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Kai-Heng Feng <kaihengf@nvidia.com>
To: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: rafael@kernel.org, Shiju Jose <shiju.jose@huawei.com>,
	Tony Luck <tony.luck@intel.com>, Borislav Petkov <bp@alien8.de>,
	Hanjun Guo <guohanjun@huawei.com>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Shuai Xue <xueshuai@linux.alibaba.com>,
	Len Brown <lenb@kernel.org>,
	Huang Yiwei <quic_hyiwei@quicinc.com>,
	Will Deacon <will@kernel.org>, Gavin Shan <gshan@redhat.com>,
	"Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>,
	Nathan Chancellor <nathan@kernel.org>,
	linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org
Subject: Re: [PATCH] acpi/apei: Add NVIDIA GHES vendor CPER record handler
Date: Tue, 17 Mar 2026 21:09:32 +0800	[thread overview]
Message-ID: <b5bbe411-5786-4fa2-88ba-c26c7baeb419@nvidia.com> (raw)
In-Reply-To: <20260316170720.00004e66@huawei.com>



On 2026/3/17 1:07 AM, Jonathan Cameron wrote:
> External email: Use caution opening links or attachments
> 
> 
> On Mon, 16 Mar 2026 18:50:50 +0800
> Kai-Heng Feng <kaihengf@nvidia.com> wrote:
> 
>> Add support for decoding NVIDIA-specific CPER sections delivered via
>> the APEI GHES vendor record notifier chain. NVIDIA hardware generates
>> vendor-specific CPER sections containing error signatures and diagnostic
>> register dumps. This implementation registers a notifier_block with the
>> GHES vendor record notifier and decodes these sections, printing error
>> details via dev_info().
>>
>> The driver binds to ACPI device NVDA2012, present on NVIDIA server
>> platforms. The NVIDIA CPER section contains a fixed header with error
>> metadata (signature, error type, severity, socket) followed by
>> variable-length register address-value pairs for hardware diagnostics.
>>
>> This work is based on libcper [0].
>>
>> Example output:
>> nvidia-ghes NVDA2012:00: NVIDIA CPER section, error_data_length: 544
>> nvidia-ghes NVDA2012:00: signature: CMET-INFO
>> nvidia-ghes NVDA2012:00: error_type: 0
>> nvidia-ghes NVDA2012:00: error_instance: 0
>> nvidia-ghes NVDA2012:00: severity: 3
>> nvidia-ghes NVDA2012:00: socket: 0
>> nvidia-ghes NVDA2012:00: number_regs: 32
>> nvidia-ghes NVDA2012:00: instance_base: 0x0000000000000000
>> nvidia-ghes NVDA2012:00: register[0]: address=0x8000000100000000 value=0x0000000100000000
>>
>> [0] https://github.com/openbmc/libcper/commit/683e055061ce
>> Cc: Shiju Jose <shiju.jose@huawei.com>
>> Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
> 
> Hi Kai-Heng Feng,
> 
> Looks pretty good to me.  A few suggestions inline.
> The devm one probably wants input from Rafael and maybe others.
> 
> Jonathan
> 
> 
> 
>> diff --git a/drivers/acpi/apei/nvidia-ghes.c b/drivers/acpi/apei/nvidia-ghes.c
>> new file mode 100644
>> index 000000000000..0e866f536a7a
>> --- /dev/null
>> +++ b/drivers/acpi/apei/nvidia-ghes.c
>> @@ -0,0 +1,168 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * NVIDIA GHES vendor record handler
>> + *
>> + * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/kernel.h>
> 
> Generally avoid including kernel.h in new code. There are normally
> a small number of more appropriate specific headers that should be included
> instead.

Will change in next revision.

> 
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/unaligned.h>
> 
> See below - may be fine to drop this as I think they are all aligned.


Will change in next revision.

> 
>> +#include <acpi/ghes.h>
> 
> Expect to see at least
> linux/uuid.h and linux/types.h (for the endian types)
> here.  Maybe others. I didn't check closely.


Will change in next revision.

> 
>> +
>> +static const guid_t nvidia_sec_guid =
>> +     GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
>> +               0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
>> +
>> +#define NVIDIA_CPER_REG_PAIR_SIZE    16      /* address + value, each u64 */
> 
> See structure definition below. I think you can make this all nice and explicit.
> If you do keep this they are __le64 not u64 I think.

Will embed this in the struct to make it more explicit.

> 
>> +
>> +struct cper_sec_nvidia {
>> +     char    signature[16];
>> +     __le16  error_type;
>> +     __le16  error_instance;
>> +     u8      severity;
>> +     u8      socket;
>> +     u8      number_regs;
>> +     u8      reserved;
>> +     __le64  instance_base;
> Could do something like
>          struct {
>                  __le64 addr;
>                  __le64 val;
>          } regs[] __counted_by(number_regs);
> to constraint remaining elements.

OK, will do in next revision.

>> +} __packed;
> 
> Given you have code that assumes aligned instance_base etc, can we actually
> be sure this is aligned and given the content also that we can drop the __packed?

The original libcper implementation does suggest that.
I'll drop the __packed in next revision.

> 
>> +
>> +struct nvidia_ghes_private {
>> +     struct notifier_block   nb;
>> +     struct device           *dev;
>> +};
>> +
>> +static void nvidia_ghes_print_error(struct device *dev,
>> +                                 const struct cper_sec_nvidia *nvidia_err,
>> +                                 size_t error_data_length, bool fatal)
>> +{
>> +     const char *level = fatal ? KERN_ERR : KERN_INFO;
>> +     const u8 *reg_data;
>> +     size_t min_size;
>> +     int i;
>> +
>> +     dev_printk(level, dev, "signature: %.16s\n", nvidia_err->signature);
>> +     dev_printk(level, dev, "error_type: %u\n", le16_to_cpu(nvidia_err->error_type));
>> +     dev_printk(level, dev, "error_instance: %u\n", le16_to_cpu(nvidia_err->error_instance));
>> +     dev_printk(level, dev, "severity: %u\n", nvidia_err->severity);
>> +     dev_printk(level, dev, "socket: %u\n", nvidia_err->socket);
>> +     dev_printk(level, dev, "number_regs: %u\n", nvidia_err->number_regs);
>> +     dev_printk(level, dev, "instance_base: 0x%016llx\n",
>> +                (unsigned long long)le64_to_cpu(nvidia_err->instance_base));
> So you are assume instance_base is aligned, but not what follows it
> (which are all the same type?)

Yes the following should be treated the same.

>> +
>> +     if (nvidia_err->number_regs == 0)
>> +             return;
>> +
>> +     /*
>> +      * Validate that all registers fit within error_data_length.
>> +      * Each register pair is NVIDIA_CPER_REG_PAIR_SIZE bytes (two u64s).
>> +      */
>> +     min_size = sizeof(struct cper_sec_nvidia) +
>> +                (size_t)nvidia_err->number_regs * NVIDIA_CPER_REG_PAIR_SIZE;
>> +     if (error_data_length < min_size) {
>> +             dev_err(dev, "Invalid number_regs %u (section size %zu, need %zu)\n",
>> +                     nvidia_err->number_regs, error_data_length, min_size);
>> +             return;
>> +     }
>> +
>> +     /*
>> +      * Registers are stored as address-value pairs immediately
>> +      * following the fixed header.  Each pair is two little-endian u64s.
>> +      */
>> +     reg_data = (const u8 *)(nvidia_err + 1);
>> +     for (i = 0; i < nvidia_err->number_regs; i++) {
>> +             u64 addr = get_unaligned_le64(reg_data + i * NVIDIA_CPER_REG_PAIR_SIZE);
>> +             u64 val = get_unaligned_le64(reg_data + i * NVIDIA_CPER_REG_PAIR_SIZE + 8);
> 
> See above for a suggestion on how to make this all explicit in the structure
> definition, making for easier to read code.
> 
>> +
>> +             dev_printk(level, dev, "register[%d]: address=0x%016llx value=0x%016llx\n",
>> +                        i, (unsigned long long)addr, (unsigned long long)val);
> Shouldn't need the casts I think
> https://www.kernel.org/doc/html/v5.14/core-api/printk-formats.html#integer-types

OK, will change.

> 
> 
>> +     }
>> +}
>> +
>> +static int nvidia_ghes_notify(struct notifier_block *nb,
>> +                           unsigned long event, void *data)
>> +{
>> +     struct acpi_hest_generic_data *gdata = data;
>> +     struct nvidia_ghes_private *priv;
>> +     const struct cper_sec_nvidia *nvidia_err;
>> +     guid_t sec_guid;
>> +
>> +     import_guid(&sec_guid, gdata->section_type);
>> +     if (!guid_equal(&sec_guid, &nvidia_sec_guid))
>> +             return NOTIFY_DONE;
>> +
>> +     priv = container_of(nb, struct nvidia_ghes_private, nb);
>> +
>> +     if (acpi_hest_get_error_length(gdata) < sizeof(struct cper_sec_nvidia)) {
> 
> Given you are about to use it for assignment I'd make the association
> more explicit and use sizeof(*nvidia_err) here and in the print.

OK.

> 
>> +             dev_err(priv->dev, "Section too small (%u < %zu)\n",
>> +                     acpi_hest_get_error_length(gdata), sizeof(struct cper_sec_nvidia));
>> +             return NOTIFY_OK;
>> +     }
>> +
>> +     nvidia_err = acpi_hest_get_payload(gdata);
>> +
>> +     if (event >= GHES_SEV_RECOVERABLE)
>> +             dev_err(priv->dev, "NVIDIA CPER section, error_data_length: %u\n",
>> +                     acpi_hest_get_error_length(gdata));
>> +     else
>> +             dev_info(priv->dev, "NVIDIA CPER section, error_data_length: %u\n",
>> +                      acpi_hest_get_error_length(gdata));
>> +
>> +     nvidia_ghes_print_error(priv->dev, nvidia_err, acpi_hest_get_error_length(gdata),
>> +                             event >= GHES_SEV_RECOVERABLE);
>> +
>> +     return NOTIFY_OK;
>> +}
>> +
>> +static int nvidia_ghes_probe(struct platform_device *pdev)
>> +{
>> +     struct nvidia_ghes_private *priv;
>> +     int ret;
>> +
>> +     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> Could make this devm_kmalloc and use
>> +     if (!priv)
>> +             return -ENOMEM;
>> +
>          *priv = (struct nvidia_ghes_private) {
>                  .nb.notifier_call = nvidia_ghes_notify,
>                  .dev = &pdev->dev,
>          };
> 
> It's a little borderline on whether that really helps readability though
> so up to you.

I think I'll stick to the "conventional" one.
> 
>> +     priv->nb.notifier_call = nvidia_ghes_notify;
>> +     priv->dev = &pdev->dev;
>> +
>> +     ret = ghes_register_vendor_record_notifier(&priv->nb);
>> +     if (ret) {
> Given it's in probe.
>                  return dev_err_probe(&pdev->dev,
>                                       "Failed to register NVIDIA GHES vendor record notifier");
> which is both shorter and pretty prints ret for you.

OK.

> + hides it in cases we don't want to print such -ENOMEM.
> 
>> +             dev_err(&pdev->dev,
>> +                     "Failed to register NVIDIA GHES vendor record notifier: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     platform_set_drvdata(pdev, priv);
>> +
>> +     return 0;
>> +}
>> +
>> +static void nvidia_ghes_remove(struct platform_device *pdev)
>> +{
>> +     struct nvidia_ghes_private *priv = platform_get_drvdata(pdev);
>> +
>> +     ghes_unregister_vendor_record_notifier(&priv->nb);
> 
> So we have two copies of this cleanup (here and in the pcie-hisi-error.c that
> used this infrastructure in the past).
> Both are in drivers that otherwise use devm_ based cleanup. Maybe we
> should just have
> 
> static void ghes_record_notifier_destroy(void *nb)
> {
>          ghes_unregister_vendor_record_notifier(nb);
> }
> 
> int devm_ghes_record_vendor_notifier(struct device *dev,
>                                       struct notifier_block *nb)
> {
>          int ret;
> 
>          ret = ghes_regiter_notifier(&priv->nb);
>          if (ret)
>                  return ret;
> 
>          return devm_add_action_or_reset(dev, ghes_record_notifier_destroy,
>                                          &priv->nb);
> }
> 
> then we can just use that prove and drop the remove entirely.

OK, I can add the change and let Rafael and Shiju review it.

> 
> 
> Rafael, Shiju - would this be acceptable? If we are going to see more
> of these drivers it'll probably make them in general simpler.
> Only two instances today though.
> 
> 
>> +}
>> +
>> +static const struct acpi_device_id nvidia_ghes_acpi_match[] = {
>> +     { "NVDA2012", 0 },
>          { "NVDA2012" },
> 
> I'm not sure why people feel the 0 should be there - though it is
> quite common!

Will drop it.

Kai-Heng

> 
>> +     { }
>> +};
>> +MODULE_DEVICE_TABLE(acpi, nvidia_ghes_acpi_match);
>> +
>> +static struct platform_driver nvidia_ghes_driver = {
>> +     .driver = {
>> +             .name           = "nvidia-ghes",
>> +             .acpi_match_table = nvidia_ghes_acpi_match,
>> +     },
>> +     .probe  = nvidia_ghes_probe,
>> +     .remove = nvidia_ghes_remove,
>> +};
>> +module_platform_driver(nvidia_ghes_driver);
>> +
>> +MODULE_AUTHOR("Kai-Heng Feng <kaihengf@nvidia.com>");
>> +MODULE_DESCRIPTION("NVIDIA GHES vendor CPER record handler");
>> +MODULE_LICENSE("GPL");
> 


      reply	other threads:[~2026-03-17 13:09 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-16 10:50 [PATCH] acpi/apei: Add NVIDIA GHES vendor CPER record handler Kai-Heng Feng
2026-03-16 17:07 ` Jonathan Cameron
2026-03-17 13:09   ` Kai-Heng Feng [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b5bbe411-5786-4fa2-88ba-c26c7baeb419@nvidia.com \
    --to=kaihengf@nvidia.com \
    --cc=bp@alien8.de \
    --cc=fabio.m.de.francesco@linux.intel.com \
    --cc=gshan@redhat.com \
    --cc=guohanjun@huawei.com \
    --cc=jonathan.cameron@huawei.com \
    --cc=lenb@kernel.org \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=nathan@kernel.org \
    --cc=quic_hyiwei@quicinc.com \
    --cc=rafael@kernel.org \
    --cc=shiju.jose@huawei.com \
    --cc=tony.luck@intel.com \
    --cc=will@kernel.org \
    --cc=xueshuai@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox