s390/pci: add error record for CC 2 retries
authorNiklas Schnelle <schnelle@linux.ibm.com>
Fri, 25 Feb 2022 08:45:24 +0000 (09:45 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Mon, 25 Apr 2022 11:54:15 +0000 (13:54 +0200)
Currently it is not detectable from within Linux when PCI instructions
are retried because of a busy condition. Detecting such conditions and
especially how long they lasted can however be quite useful in problem
determination. This patch enables this by adding an s390dbf error log
when a CC 2 is first encountered as well as after the retried
instruction.

Despite being unlikely it may be possible that these added debug
messages drown out important other messages so allow setting the debug
level in zpci_err_insn*() and set their level to 1 so they can be
filtered out if need be.

Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Pierre Morel <pmorel@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/include/asm/pci_debug.h
arch/s390/pci/pci_insn.c

index 5dfe475..3bb4e7e 100644 (file)
@@ -17,9 +17,14 @@ extern debug_info_t *pci_debug_err_id;
                debug_text_event(pci_debug_err_id, 0, debug_buffer);            \
        } while (0)
 
+static inline void zpci_err_hex_level(int level, void *addr, int len)
+{
+       debug_event(pci_debug_err_id, level, addr, len);
+}
+
 static inline void zpci_err_hex(void *addr, int len)
 {
-       debug_event(pci_debug_err_id, 0, addr, len);
+       zpci_err_hex_level(0, addr, len);
 }
 
 #endif
index c49a934..1a822b7 100644 (file)
@@ -34,24 +34,24 @@ struct zpci_err_insn_data {
        };
 } __packed;
 
-static inline void zpci_err_insn_req(u8 insn, u8 cc, u8 status,
+static inline void zpci_err_insn_req(int lvl, u8 insn, u8 cc, u8 status,
                                     u64 req, u64 offset)
 {
        struct zpci_err_insn_data data = {
                .insn = insn, .cc = cc, .status = status,
                .req = req, .offset = offset};
 
-       zpci_err_hex(&data, sizeof(data));
+       zpci_err_hex_level(lvl, &data, sizeof(data));
 }
 
-static inline void zpci_err_insn_addr(u8 insn, u8 cc, u8 status,
+static inline void zpci_err_insn_addr(int lvl, u8 insn, u8 cc, u8 status,
                                      u64 addr, u64 len)
 {
        struct zpci_err_insn_data data = {
                .insn = insn, .cc = cc, .status = status,
                .addr = addr, .len = len};
 
-       zpci_err_hex(&data, sizeof(data));
+       zpci_err_hex_level(lvl, &data, sizeof(data));
 }
 
 /* Modify PCI Function Controls */
@@ -71,16 +71,24 @@ static inline u8 __mpcifc(u64 req, struct zpci_fib *fib, u8 *status)
 
 u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status)
 {
+       bool retried = false;
        u8 cc;
 
        do {
                cc = __mpcifc(req, fib, status);
-               if (cc == 2)
+               if (cc == 2) {
                        msleep(ZPCI_INSN_BUSY_DELAY);
+                       if (!retried) {
+                               zpci_err_insn_req(1, 'M', cc, *status, req, 0);
+                               retried = true;
+                       }
+               }
        } while (cc == 2);
 
        if (cc)
-               zpci_err_insn_req('M', cc, *status, req, 0);
+               zpci_err_insn_req(0, 'M', cc, *status, req, 0);
+       else if (retried)
+               zpci_err_insn_req(1, 'M', cc, *status, req, 0);
 
        return cc;
 }
@@ -104,16 +112,24 @@ static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status)
 
 int zpci_refresh_trans(u64 fn, u64 addr, u64 range)
 {
+       bool retried = false;
        u8 cc, status;
 
        do {
                cc = __rpcit(fn, addr, range, &status);
-               if (cc == 2)
+               if (cc == 2) {
                        udelay(ZPCI_INSN_BUSY_DELAY);
+                       if (!retried) {
+                               zpci_err_insn_addr(1, 'R', cc, status, addr, range);
+                               retried = true;
+                       }
+               }
        } while (cc == 2);
 
        if (cc)
-               zpci_err_insn_addr('R', cc, status, addr, range);
+               zpci_err_insn_addr(0, 'R', cc, status, addr, range);
+       else if (retried)
+               zpci_err_insn_addr(1, 'R', cc, status, addr, range);
 
        if (cc == 1 && (status == 4 || status == 16))
                return -ENOMEM;
@@ -168,17 +184,25 @@ static inline int __pcilg(u64 *data, u64 req, u64 offset, u8 *status)
 
 int __zpci_load(u64 *data, u64 req, u64 offset)
 {
+       bool retried = false;
        u8 status;
        int cc;
 
        do {
                cc = __pcilg(data, req, offset, &status);
-               if (cc == 2)
+               if (cc == 2) {
                        udelay(ZPCI_INSN_BUSY_DELAY);
+                       if (!retried) {
+                               zpci_err_insn_req(1, 'l', cc, status, req, offset);
+                               retried = true;
+                       }
+               }
        } while (cc == 2);
 
        if (cc)
-               zpci_err_insn_req('l', cc, status, req, offset);
+               zpci_err_insn_req(0, 'l', cc, status, req, offset);
+       else if (retried)
+               zpci_err_insn_req(1, 'l', cc, status, req, offset);
 
        return (cc > 0) ? -EIO : cc;
 }
@@ -222,7 +246,7 @@ int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len)
 
        cc = __pcilg_mio(data, (__force u64) addr, len, &status);
        if (cc)
-               zpci_err_insn_addr('L', cc, status, (__force u64) addr, len);
+               zpci_err_insn_addr(0, 'L', cc, status, (__force u64) addr, len);
 
        return (cc > 0) ? -EIO : cc;
 }
@@ -249,17 +273,25 @@ static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status)
 
 int __zpci_store(u64 data, u64 req, u64 offset)
 {
+       bool retried = false;
        u8 status;
        int cc;
 
        do {
                cc = __pcistg(data, req, offset, &status);
-               if (cc == 2)
+               if (cc == 2) {
                        udelay(ZPCI_INSN_BUSY_DELAY);
+                       if (!retried) {
+                               zpci_err_insn_req(1, 's', cc, status, req, offset);
+                               retried = true;
+                       }
+               }
        } while (cc == 2);
 
        if (cc)
-               zpci_err_insn_req('s', cc, status, req, offset);
+               zpci_err_insn_req(0, 's', cc, status, req, offset);
+       else if (retried)
+               zpci_err_insn_req(1, 's', cc, status, req, offset);
 
        return (cc > 0) ? -EIO : cc;
 }
@@ -302,7 +334,7 @@ int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len)
 
        cc = __pcistg_mio(data, (__force u64) addr, len, &status);
        if (cc)
-               zpci_err_insn_addr('S', cc, status, (__force u64) addr, len);
+               zpci_err_insn_addr(0, 'S', cc, status, (__force u64) addr, len);
 
        return (cc > 0) ? -EIO : cc;
 }
@@ -328,17 +360,25 @@ static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status)
 
 int __zpci_store_block(const u64 *data, u64 req, u64 offset)
 {
+       bool retried = false;
        u8 status;
        int cc;
 
        do {
                cc = __pcistb(data, req, offset, &status);
-               if (cc == 2)
+               if (cc == 2) {
                        udelay(ZPCI_INSN_BUSY_DELAY);
+                       if (!retried) {
+                               zpci_err_insn_req(0, 'b', cc, status, req, offset);
+                               retried = true;
+                       }
+               }
        } while (cc == 2);
 
        if (cc)
-               zpci_err_insn_req('b', cc, status, req, offset);
+               zpci_err_insn_req(0, 'b', cc, status, req, offset);
+       else if (retried)
+               zpci_err_insn_req(1, 'b', cc, status, req, offset);
 
        return (cc > 0) ? -EIO : cc;
 }
@@ -382,7 +422,7 @@ int zpci_write_block(volatile void __iomem *dst,
 
        cc = __pcistb_mio(src, (__force u64) dst, len, &status);
        if (cc)
-               zpci_err_insn_addr('B', cc, status, (__force u64) dst, len);
+               zpci_err_insn_addr(0, 'B', cc, status, (__force u64) dst, len);
 
        return (cc > 0) ? -EIO : cc;
 }