5454#include <asm/cpu_device_id.h>
5555#include <asm/mce.h>
5656
57+ #include "../debugfs.h"
58+
5759#define INVALID_CPU UINT_MAX
5860
5961/* Validation Bits */
@@ -116,6 +118,9 @@ static u64 *spa_entries;
116118
117119#define INVALID_SPA ~0ULL
118120
121+ static struct dentry * fmpm_dfs_dir ;
122+ static struct dentry * fmpm_dfs_entries ;
123+
119124#define CPER_CREATOR_FMP \
120125 GUID_INIT(0xcd5c2993, 0xf4b2, 0x41b2, 0xb5, 0xd4, 0xf9, 0xc3, \
121126 0xa0, 0x33, 0x08, 0x75)
@@ -152,6 +157,11 @@ static unsigned int spa_nr_entries;
152157 * Protect the local records cache in fru_records and prevent concurrent
153158 * writes to storage. This is only needed after init once notifier block
154159 * registration is done.
160+ *
161+ * The majority of a record is fixed at module init and will not change
162+ * during run time. The entries within a record will be updated as new
163+ * errors are reported. The mutex should be held whenever the entries are
164+ * accessed during run time.
155165 */
156166static DEFINE_MUTEX (fmpm_update_mutex );
157167
@@ -815,6 +825,124 @@ static int allocate_records(void)
815825 return ret ;
816826}
817827
828+ static void * fmpm_start (struct seq_file * f , loff_t * pos )
829+ {
830+ if (* pos >= (spa_nr_entries + 1 ))
831+ return NULL ;
832+ return pos ;
833+ }
834+
835+ static void * fmpm_next (struct seq_file * f , void * data , loff_t * pos )
836+ {
837+ if (++ (* pos ) >= (spa_nr_entries + 1 ))
838+ return NULL ;
839+ return pos ;
840+ }
841+
842+ static void fmpm_stop (struct seq_file * f , void * data )
843+ {
844+ }
845+
846+ #define SHORT_WIDTH 8
847+ #define U64_WIDTH 18
848+ #define TIMESTAMP_WIDTH 19
849+ #define LONG_WIDTH 24
850+ #define U64_PAD (LONG_WIDTH - U64_WIDTH)
851+ #define TS_PAD (LONG_WIDTH - TIMESTAMP_WIDTH)
852+ static int fmpm_show (struct seq_file * f , void * data )
853+ {
854+ unsigned int fru_idx , entry , spa_entry , line ;
855+ struct cper_fru_poison_desc * fpd ;
856+ struct fru_rec * rec ;
857+
858+ line = * (loff_t * )data ;
859+ if (line == 0 ) {
860+ seq_printf (f , "%-*s" , SHORT_WIDTH , "fru_idx" );
861+ seq_printf (f , "%-*s" , LONG_WIDTH , "fru_id" );
862+ seq_printf (f , "%-*s" , SHORT_WIDTH , "entry" );
863+ seq_printf (f , "%-*s" , LONG_WIDTH , "timestamp" );
864+ seq_printf (f , "%-*s" , LONG_WIDTH , "hw_id" );
865+ seq_printf (f , "%-*s" , LONG_WIDTH , "addr" );
866+ seq_printf (f , "%-*s" , LONG_WIDTH , "spa" );
867+ goto out_newline ;
868+ }
869+
870+ spa_entry = line - 1 ;
871+ fru_idx = spa_entry / max_nr_entries ;
872+ entry = spa_entry % max_nr_entries ;
873+
874+ rec = fru_records [fru_idx ];
875+ if (!rec )
876+ goto out ;
877+
878+ seq_printf (f , "%-*u" , SHORT_WIDTH , fru_idx );
879+ seq_printf (f , "0x%016llx%-*s" , rec -> fmp .fru_id , U64_PAD , "" );
880+ seq_printf (f , "%-*u" , SHORT_WIDTH , entry );
881+
882+ mutex_lock (& fmpm_update_mutex );
883+
884+ if (entry >= rec -> fmp .nr_entries ) {
885+ seq_printf (f , "%-*s" , LONG_WIDTH , "*" );
886+ seq_printf (f , "%-*s" , LONG_WIDTH , "*" );
887+ seq_printf (f , "%-*s" , LONG_WIDTH , "*" );
888+ seq_printf (f , "%-*s" , LONG_WIDTH , "*" );
889+ goto out_unlock ;
890+ }
891+
892+ fpd = & rec -> entries [entry ];
893+
894+ seq_printf (f , "%ptT%-*s" , & fpd -> timestamp , TS_PAD , "" );
895+ seq_printf (f , "0x%016llx%-*s" , fpd -> hw_id , U64_PAD , "" );
896+ seq_printf (f , "0x%016llx%-*s" , fpd -> addr , U64_PAD , "" );
897+
898+ if (spa_entries [spa_entry ] == INVALID_SPA )
899+ seq_printf (f , "%-*s" , LONG_WIDTH , "*" );
900+ else
901+ seq_printf (f , "0x%016llx%-*s" , spa_entries [spa_entry ], U64_PAD , "" );
902+
903+ out_unlock :
904+ mutex_unlock (& fmpm_update_mutex );
905+ out_newline :
906+ seq_putc (f , '\n' );
907+ out :
908+ return 0 ;
909+ }
910+
911+ static const struct seq_operations fmpm_seq_ops = {
912+ .start = fmpm_start ,
913+ .next = fmpm_next ,
914+ .stop = fmpm_stop ,
915+ .show = fmpm_show ,
916+ };
917+
918+ static int fmpm_open (struct inode * inode , struct file * file )
919+ {
920+ return seq_open (file , & fmpm_seq_ops );
921+ }
922+
923+ static const struct file_operations fmpm_fops = {
924+ .open = fmpm_open ,
925+ .release = seq_release ,
926+ .read = seq_read ,
927+ .llseek = seq_lseek ,
928+ };
929+
930+ static void setup_debugfs (void )
931+ {
932+ struct dentry * dfs = ras_get_debugfs_root ();
933+
934+ if (!dfs )
935+ return ;
936+
937+ fmpm_dfs_dir = debugfs_create_dir ("fmpm" , dfs );
938+ if (!fmpm_dfs_dir )
939+ return ;
940+
941+ fmpm_dfs_entries = debugfs_create_file ("entries" , 0400 , fmpm_dfs_dir , NULL , & fmpm_fops );
942+ if (!fmpm_dfs_entries )
943+ debugfs_remove (fmpm_dfs_dir );
944+ }
945+
818946static const struct x86_cpu_id fmpm_cpuids [] = {
819947 X86_MATCH_VENDOR_FAM (AMD , 0x19 , NULL ),
820948 { }
@@ -856,6 +984,8 @@ static int __init fru_mem_poison_init(void)
856984 if (ret )
857985 goto out_free ;
858986
987+ setup_debugfs ();
988+
859989 retire_mem_records ();
860990
861991 mce_register_decode_chain (& fru_mem_poison_nb );
@@ -872,6 +1002,7 @@ static int __init fru_mem_poison_init(void)
8721002static void __exit fru_mem_poison_exit (void )
8731003{
8741004 mce_unregister_decode_chain (& fru_mem_poison_nb );
1005+ debugfs_remove (fmpm_dfs_dir );
8751006 free_records ();
8761007}
8771008
0 commit comments