On Tue, May 20, 2025 at 04:50:17PM -0500, Bjorn Helgaas wrote:
> From: Bjorn Helgaas <bhelg...@google.com>
> 
> This work is mostly due to Jon Pan-Doh and Karolina Stolarek.  I rebased
> this to v6.15-rc1, factored out some of the trace and statistics updates,
> and added some minor cleanups.
> 
> I'm sorry to post a v7 so soon after v6, but I really want to get this in
> v6.16 so it needs to get into pci/next soonish.  I pushed this to pci/aer
> at https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git/log/?h=aer
> (head cbde036e5615 ("PCI/AER: Add sysfs attributes for log ratelimits"))
> and appended the interdiff from v6 to v7 below.

Thank you ever so much for all your reviewing time.  I addressed most
of the comments (except Sathy's comments about the sysfs
cor/uncor/nonfatal names), but I hate to deluge you all with another
full posting.

So for now I updated the "aer" branch above (current head d41e0decb7d7
("PCI/AER: Add sysfs attributes for log ratelimits")) and attached the
interdiff between v7 and d41e0decb7d7 below.


diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index a3261e842d6d..eca2812cfd25 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -587,13 +587,14 @@ static inline bool pci_dev_test_and_set_removed(struct 
pci_dev *dev)
 
 struct aer_err_info {
        struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES];
+       int ratelimit_print[AER_MAX_MULTI_ERR_DEVICES];
        int error_dev_num;
        const char *level;              /* printk level */
 
        unsigned int id:16;
 
        unsigned int severity:2;        /* 0:NONFATAL | 1:FATAL | 2:COR */
-       unsigned int ratelimit:1;       /* 0=skip, 1=print */
+       unsigned int root_ratelimit_print:1;    /* 0=skip, 1=print */
        unsigned int __pad1:4;
        unsigned int multi_error_valid:1;
 
@@ -606,15 +607,16 @@ struct aer_err_info {
        struct pcie_tlp_log tlp;        /* TLP Header */
 };
 
-int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
-void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
+int aer_get_device_error_info(struct aer_err_info *info, int i);
+void aer_print_error(struct aer_err_info *info, int i);
 
 int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
                      unsigned int tlp_len, bool flit,
                      struct pcie_tlp_log *log);
 unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc);
 void pcie_print_tlp_log(const struct pci_dev *dev,
-                       const struct pcie_tlp_log *log, const char *pfx);
+                       const struct pcie_tlp_log *log, const char *level,
+                       const char *pfx);
 #endif /* CONFIG_PCIEAER */
 
 #ifdef CONFIG_PCIEPORTBUS
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 9b8dea317a79..48014010dc8b 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -672,31 +672,31 @@ static DEVICE_ATTR_RW(ratelimit_log_enable);
        static ssize_t                                                  \
        name##_show(struct device *dev, struct device_attribute *attr,  \
                    char *buf)                                          \
-{                                                                      \
-       struct pci_dev *pdev = to_pci_dev(dev);                         \
+       {                                                               \
+               struct pci_dev *pdev = to_pci_dev(dev);                 \
                                                                        \
-       return sysfs_emit(buf, "%d\n",                                  \
-                         pdev->aer_info->ratelimit.burst);             \
-}                                                                      \
+               return sysfs_emit(buf, "%d\n",                          \
+                                 pdev->aer_info->ratelimit.burst);     \
+       }                                                               \
                                                                        \
        static ssize_t                                                  \
        name##_store(struct device *dev, struct device_attribute *attr, \
                     const char *buf, size_t count)                     \
-{                                                                      \
-       struct pci_dev *pdev = to_pci_dev(dev);                         \
-       int burst;                                                      \
+       {                                                               \
+               struct pci_dev *pdev = to_pci_dev(dev);                 \
+               int burst;                                              \
                                                                        \
-       if (!capable(CAP_SYS_ADMIN))                                    \
-               return -EPERM;                                          \
+               if (!capable(CAP_SYS_ADMIN))                            \
+                       return -EPERM;                                  \
                                                                        \
-       if (kstrtoint(buf, 0, &burst) < 0)                              \
-               return -EINVAL;                                         \
+               if (kstrtoint(buf, 0, &burst) < 0)                      \
+                       return -EINVAL;                                 \
                                                                        \
-       pdev->aer_info->ratelimit.burst = burst;                        \
+               pdev->aer_info->ratelimit.burst = burst;                \
                                                                        \
-       return count;                                                   \
-}                                                                      \
-static DEVICE_ATTR_RW(name)
+               return count;                                           \
+       }                                                               \
+       static DEVICE_ATTR_RW(name)
 
 aer_ratelimit_burst_attr(ratelimit_burst_cor_log, cor_log_ratelimit);
 aer_ratelimit_burst_attr(ratelimit_burst_uncor_log, uncor_log_ratelimit);
@@ -734,9 +734,6 @@ static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
        u64 *counter = NULL;
        struct aer_info *aer_info = pdev->aer_info;
 
-       trace_aer_event(pci_name(pdev), (info->status & ~info->mask),
-                       info->severity, info->tlp_header_valid, &info->tlp);
-
        if (!aer_info)
                return;
 
@@ -785,6 +782,9 @@ static int aer_ratelimit(struct pci_dev *dev, unsigned int 
severity)
 {
        struct ratelimit_state *ratelimit;
 
+       if (severity == AER_FATAL)
+               return 1;       /* AER_FATAL not ratelimited */
+
        if (severity == AER_CORRECTABLE)
                ratelimit = &dev->aer_info->cor_log_ratelimit;
        else
@@ -817,7 +817,7 @@ static void __aer_print_error(struct pci_dev *dev, struct 
aer_err_info *info)
 }
 
 static void aer_print_source(struct pci_dev *dev, struct aer_err_info *info,
-                            const char *details)
+                            bool found)
 {
        u16 source = info->id;
 
@@ -825,18 +825,27 @@ static void aer_print_source(struct pci_dev *dev, struct 
aer_err_info *info,
                 info->multi_error_valid ? "Multiple " : "",
                 aer_error_severity_string[info->severity],
                 pci_domain_nr(dev->bus), PCI_BUS_NUM(source),
-                PCI_SLOT(source), PCI_FUNC(source), details);
+                PCI_SLOT(source), PCI_FUNC(source),
+                found ? "" : " (no details found");
 }
 
-void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+void aer_print_error(struct aer_err_info *info, int i)
 {
-       int layer, agent;
-       int id = pci_dev_id(dev);
+       struct pci_dev *dev;
+       int layer, agent, id;
        const char *level = info->level;
 
-       pci_dev_aer_stats_incr(dev, info);
+       if (i >= AER_MAX_MULTI_ERR_DEVICES)
+               return;
 
-       if (!info->ratelimit)
+       dev = info->dev[i];
+       id = pci_dev_id(dev);
+
+       pci_dev_aer_stats_incr(dev, info);
+       trace_aer_event(pci_name(dev), (info->status & ~info->mask),
+                       info->severity, info->tlp_header_valid, &info->tlp);
+
+       if (!info->ratelimit_print[i])
                return;
 
        if (!info->status) {
@@ -858,7 +867,7 @@ void aer_print_error(struct pci_dev *dev, struct 
aer_err_info *info)
        __aer_print_error(dev, info);
 
        if (info->tlp_header_valid)
-               pcie_print_tlp_log(dev, &info->tlp, dev_fmt("  "));
+               pcie_print_tlp_log(dev, &info->tlp, level, dev_fmt("  "));
 
 out:
        if (info->id && info->error_dev_num > 1 && info->id == id)
@@ -903,11 +912,10 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
 
        info.status = status;
        info.mask = mask;
-       info.tlp_header_valid = tlp_header_valid;
-       if (tlp_header_valid)
-               info.tlp = aer->header_log;
 
        pci_dev_aer_stats_incr(dev, &info);
+       trace_aer_event(pci_name(dev), (status & ~mask),
+                       aer_severity, tlp_header_valid, &aer->header_log);
 
        if (!aer_ratelimit(dev, info.severity))
                return;
@@ -925,13 +933,9 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
                aer_printk(info.level, dev, "aer_uncor_severity: 0x%08x\n",
                           aer->uncor_severity);
 
-       /*
-        * pcie_print_tlp_log() uses KERN_ERR, but we only call it when
-        * tlp_header_valid is set, and info.level is always KERN_ERR in
-        * that case.
-        */
        if (tlp_header_valid)
-               pcie_print_tlp_log(dev, &aer->header_log, dev_fmt("  "));
+               pcie_print_tlp_log(dev, &aer->header_log, info.level,
+                                  dev_fmt("  "));
 }
 EXPORT_SYMBOL_NS_GPL(pci_print_aer, "CXL");
 
@@ -942,23 +946,27 @@ EXPORT_SYMBOL_NS_GPL(pci_print_aer, "CXL");
  */
 static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
 {
+       int i = e_info->error_dev_num;
+
+       if (i >= AER_MAX_MULTI_ERR_DEVICES)
+               return -ENOSPC;
+
+       e_info->dev[i] = pci_dev_get(dev);
+       e_info->error_dev_num++;
+
        /*
         * Ratelimit AER log messages.  "dev" is either the source
         * identified by the root's Error Source ID or it has an unmasked
-        * error logged in its own AER Capability.  If any of these devices
-        * has not reached its ratelimit, log messages for all of them.
-        * Messages are emitted when "e_info->ratelimit" is non-zero.
-        *
-        * Note that "e_info->ratelimit" was already initialized to 1 for the
-        * ERR_FATAL case.
+        * error logged in its own AER Capability.  Messages are emitted
+        * when "ratelimit_print[i]" is non-zero.  If we will print detail
+        * for a downstream device, make sure we print the Error Source ID
+        * from the root as well.
         */
-       if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
-               e_info->dev[e_info->error_dev_num] = pci_dev_get(dev);
-               e_info->ratelimit |= aer_ratelimit(dev, e_info->severity);
-               e_info->error_dev_num++;
-               return 0;
+       if (aer_ratelimit(dev, e_info->severity)) {
+               e_info->ratelimit_print[i] = 1;
+               e_info->root_ratelimit_print = 1;
        }
-       return -ENOSPC;
+       return 0;
 }
 
 /**
@@ -1337,19 +1345,26 @@ EXPORT_SYMBOL_GPL(aer_recover_queue);
 
 /**
  * aer_get_device_error_info - read error status from dev and store it to info
- * @dev: pointer to the device expected to have an error record
  * @info: pointer to structure to store the error record
+ * @i: index into info->dev[]
  *
  * Return: 1 on success, 0 on error.
  *
  * Note that @info is reused among all error devices. Clear fields properly.
  */
-int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
+int aer_get_device_error_info(struct aer_err_info *info, int i)
 {
-       int type = pci_pcie_type(dev);
-       int aer = dev->aer_cap;
+       struct pci_dev *dev;
+       int type, aer;
        u32 aercc;
 
+       if (i >= AER_MAX_MULTI_ERR_DEVICES)
+               return 0;
+
+       dev = info->dev[i];
+       aer = dev->aer_cap;
+       type = pci_pcie_type(dev);
+
        /* Must reset in this function */
        info->status = 0;
        info->tlp_header_valid = 0;
@@ -1401,11 +1416,11 @@ static inline void aer_process_err_devices(struct 
aer_err_info *e_info)
 
        /* Report all before handling them, to not lose records by reset etc. */
        for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
-               if (aer_get_device_error_info(e_info->dev[i], e_info))
-                       aer_print_error(e_info->dev[i], e_info);
+               if (aer_get_device_error_info(e_info, i))
+                       aer_print_error(e_info, i);
        }
        for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
-               if (aer_get_device_error_info(e_info->dev[i], e_info))
+               if (aer_get_device_error_info(e_info, i))
                        handle_error_source(e_info->dev[i], e_info);
        }
 }
@@ -1425,17 +1440,18 @@ static void aer_isr_one_error_type(struct pci_dev *root,
 
        /*
         * If we're going to log error messages, we've already set
-        * "info->ratelimit" to non-zero (which enables printing) because
-        * this is either an ERR_FATAL or we found a device with an error
-        * logged in its AER Capability.
+        * "info->root_ratelimit_print" and "info->ratelimit_print[i]" to
+        * non-zero (which enables printing) because this is either an
+        * ERR_FATAL or we found a device with an error logged in its AER
+        * Capability.
         *
         * If we didn't find the Error Source device, at least log the
         * Requester ID from the ERR_* Message received by the Root Port or
         * RCEC, ratelimited by the RP or RCEC.
         */
-       if (info->ratelimit ||
+       if (info->root_ratelimit_print ||
            (!found && aer_ratelimit(root, info->severity)))
-               aer_print_source(root, info, found ? "" : " (no details found");
+               aer_print_source(root, info, found);
 
        if (found)
                aer_process_err_devices(info);
@@ -1470,14 +1486,12 @@ static void aer_isr_one_error(struct pci_dev *root,
                aer_isr_one_error_type(root, &e_info);
        }
 
-       /* Note that messages for ERR_FATAL are never ratelimited */
        if (status & PCI_ERR_ROOT_UNCOR_RCV) {
                int fatal = status & PCI_ERR_ROOT_FATAL_RCV;
                int multi = status & PCI_ERR_ROOT_MULTI_UNCOR_RCV;
                struct aer_err_info e_info = {
                        .id = ERR_UNCOR_ID(e_src->id),
                        .severity = fatal ? AER_FATAL : AER_NONFATAL,
-                       .ratelimit = fatal ? 1 : 0,
                        .level = KERN_ERR,
                        .multi_error_valid = multi ? 1 : 0,
                };
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index 530c5e2cf7e8..fc18349614d7 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -222,7 +222,7 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)
                          dpc_tlp_log_len(pdev),
                          pdev->subordinate->flit_mode,
                          &tlp_log);
-       pcie_print_tlp_log(pdev, &tlp_log, dev_fmt(""));
+       pcie_print_tlp_log(pdev, &tlp_log, KERN_ERR, dev_fmt(""));
 
        if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG + 1)
                goto clear_status;
@@ -253,6 +253,10 @@ static int dpc_get_aer_uncorrect_severity(struct pci_dev 
*dev,
                info->severity = AER_NONFATAL;
 
        info->level = KERN_ERR;
+
+       info->dev[0] = dev;
+       info->error_dev_num = 1;
+
        return 1;
 }
 
@@ -270,9 +274,8 @@ void dpc_process_error(struct pci_dev *pdev)
                pci_warn(pdev, "containment event, status:%#06x: unmasked 
uncorrectable error detected\n",
                         status);
                if (dpc_get_aer_uncorrect_severity(pdev, &info) &&
-                   aer_get_device_error_info(pdev, &info)) {
-                       info.ratelimit = 1;     /* ERR_FATAL; no ratelimit */
-                       aer_print_error(pdev, &info);
+                   aer_get_device_error_info(&info, 0)) {
+                       aer_print_error(&info, 0);
                        pci_aer_clear_nonfatal_status(pdev);
                        pci_aer_clear_fatal_status(pdev);
                }
diff --git a/drivers/pci/pcie/tlp.c b/drivers/pci/pcie/tlp.c
index 890d5391d7f5..71f8fc9ea2ed 100644
--- a/drivers/pci/pcie/tlp.c
+++ b/drivers/pci/pcie/tlp.c
@@ -98,12 +98,14 @@ int pcie_read_tlp_log(struct pci_dev *dev, int where, int 
where2,
  * pcie_print_tlp_log - Print TLP Header / Prefix Log contents
  * @dev: PCIe device
  * @log: TLP Log structure
+ * @level: Printk log level
  * @pfx: String prefix
  *
  * Prints TLP Header and Prefix Log information held by @log.
  */
 void pcie_print_tlp_log(const struct pci_dev *dev,
-                       const struct pcie_tlp_log *log, const char *pfx)
+                       const struct pcie_tlp_log *log, const char *level,
+                       const char *pfx)
 {
        /* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */
        char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1];
@@ -130,6 +132,6 @@ void pcie_print_tlp_log(const struct pci_dev *dev,
                }
        }
 
-       pci_err(dev, "%sTLP Header%s: %s\n", pfx,
+       dev_printk(level, &dev->dev, "%sTLP Header%s: %s\n", pfx,
                log->flit ? " (Flit)" : "", buf);
 }

Reply via email to