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