1*011d8261SBorislav Petkov #include <linux/mm.h> 2*011d8261SBorislav Petkov #include <linux/gfp.h> 3*011d8261SBorislav Petkov #include <linux/kernel.h> 4*011d8261SBorislav Petkov 5*011d8261SBorislav Petkov #include <asm/mce.h> 6*011d8261SBorislav Petkov 7*011d8261SBorislav Petkov #include "debugfs.h" 8*011d8261SBorislav Petkov 9*011d8261SBorislav Petkov /* 10*011d8261SBorislav Petkov * RAS Correctable Errors Collector 11*011d8261SBorislav Petkov * 12*011d8261SBorislav Petkov * This is a simple gadget which collects correctable errors and counts their 13*011d8261SBorislav Petkov * occurrence per physical page address. 14*011d8261SBorislav Petkov * 15*011d8261SBorislav Petkov * We've opted for possibly the simplest data structure to collect those - an 16*011d8261SBorislav Petkov * array of the size of a memory page. It stores 512 u64's with the following 17*011d8261SBorislav Petkov * structure: 18*011d8261SBorislav Petkov * 19*011d8261SBorislav Petkov * [63 ... PFN ... 12 | 11 ... generation ... 10 | 9 ... count ... 0] 20*011d8261SBorislav Petkov * 21*011d8261SBorislav Petkov * The generation in the two highest order bits is two bits which are set to 11b 22*011d8261SBorislav Petkov * on every insertion. During the course of each entry's existence, the 23*011d8261SBorislav Petkov * generation field gets decremented during spring cleaning to 10b, then 01b and 24*011d8261SBorislav Petkov * then 00b. 25*011d8261SBorislav Petkov * 26*011d8261SBorislav Petkov * This way we're employing the natural numeric ordering to make sure that newly 27*011d8261SBorislav Petkov * inserted/touched elements have higher 12-bit counts (which we've manufactured) 28*011d8261SBorislav Petkov * and thus iterating over the array initially won't kick out those elements 29*011d8261SBorislav Petkov * which were inserted last. 30*011d8261SBorislav Petkov * 31*011d8261SBorislav Petkov * Spring cleaning is what we do when we reach a certain number CLEAN_ELEMS of 32*011d8261SBorislav Petkov * elements entered into the array, during which, we're decaying all elements. 33*011d8261SBorislav Petkov * If, after decay, an element gets inserted again, its generation is set to 11b 34*011d8261SBorislav Petkov * to make sure it has higher numerical count than other, older elements and 35*011d8261SBorislav Petkov * thus emulate an an LRU-like behavior when deleting elements to free up space 36*011d8261SBorislav Petkov * in the page. 37*011d8261SBorislav Petkov * 38*011d8261SBorislav Petkov * When an element reaches it's max count of count_threshold, we try to poison 39*011d8261SBorislav Petkov * it by assuming that errors triggered count_threshold times in a single page 40*011d8261SBorislav Petkov * are excessive and that page shouldn't be used anymore. count_threshold is 41*011d8261SBorislav Petkov * initialized to COUNT_MASK which is the maximum. 42*011d8261SBorislav Petkov * 43*011d8261SBorislav Petkov * That error event entry causes cec_add_elem() to return !0 value and thus 44*011d8261SBorislav Petkov * signal to its callers to log the error. 45*011d8261SBorislav Petkov * 46*011d8261SBorislav Petkov * To the question why we've chosen a page and moving elements around with 47*011d8261SBorislav Petkov * memmove(), it is because it is a very simple structure to handle and max data 48*011d8261SBorislav Petkov * movement is 4K which on highly optimized modern CPUs is almost unnoticeable. 49*011d8261SBorislav Petkov * We wanted to avoid the pointer traversal of more complex structures like a 50*011d8261SBorislav Petkov * linked list or some sort of a balancing search tree. 51*011d8261SBorislav Petkov * 52*011d8261SBorislav Petkov * Deleting an element takes O(n) but since it is only a single page, it should 53*011d8261SBorislav Petkov * be fast enough and it shouldn't happen all too often depending on error 54*011d8261SBorislav Petkov * patterns. 55*011d8261SBorislav Petkov */ 56*011d8261SBorislav Petkov 57*011d8261SBorislav Petkov #undef pr_fmt 58*011d8261SBorislav Petkov #define pr_fmt(fmt) "RAS: " fmt 59*011d8261SBorislav Petkov 60*011d8261SBorislav Petkov /* 61*011d8261SBorislav Petkov * We use DECAY_BITS bits of PAGE_SHIFT bits for counting decay, i.e., how long 62*011d8261SBorislav Petkov * elements have stayed in the array without having been accessed again. 63*011d8261SBorislav Petkov */ 64*011d8261SBorislav Petkov #define DECAY_BITS 2 65*011d8261SBorislav Petkov #define DECAY_MASK ((1ULL << DECAY_BITS) - 1) 66*011d8261SBorislav Petkov #define MAX_ELEMS (PAGE_SIZE / sizeof(u64)) 67*011d8261SBorislav Petkov 68*011d8261SBorislav Petkov /* 69*011d8261SBorislav Petkov * Threshold amount of inserted elements after which we start spring 70*011d8261SBorislav Petkov * cleaning. 71*011d8261SBorislav Petkov */ 72*011d8261SBorislav Petkov #define CLEAN_ELEMS (MAX_ELEMS >> DECAY_BITS) 73*011d8261SBorislav Petkov 74*011d8261SBorislav Petkov /* Bits which count the number of errors happened in this 4K page. */ 75*011d8261SBorislav Petkov #define COUNT_BITS (PAGE_SHIFT - DECAY_BITS) 76*011d8261SBorislav Petkov #define COUNT_MASK ((1ULL << COUNT_BITS) - 1) 77*011d8261SBorislav Petkov #define FULL_COUNT_MASK (PAGE_SIZE - 1) 78*011d8261SBorislav Petkov 79*011d8261SBorislav Petkov /* 80*011d8261SBorislav Petkov * u64: [ 63 ... 12 | DECAY_BITS | COUNT_BITS ] 81*011d8261SBorislav Petkov */ 82*011d8261SBorislav Petkov 83*011d8261SBorislav Petkov #define PFN(e) ((e) >> PAGE_SHIFT) 84*011d8261SBorislav Petkov #define DECAY(e) (((e) >> COUNT_BITS) & DECAY_MASK) 85*011d8261SBorislav Petkov #define COUNT(e) ((unsigned int)(e) & COUNT_MASK) 86*011d8261SBorislav Petkov #define FULL_COUNT(e) ((e) & (PAGE_SIZE - 1)) 87*011d8261SBorislav Petkov 88*011d8261SBorislav Petkov static struct ce_array { 89*011d8261SBorislav Petkov u64 *array; /* container page */ 90*011d8261SBorislav Petkov unsigned int n; /* number of elements in the array */ 91*011d8261SBorislav Petkov 92*011d8261SBorislav Petkov unsigned int decay_count; /* 93*011d8261SBorislav Petkov * number of element insertions/increments 94*011d8261SBorislav Petkov * since the last spring cleaning. 95*011d8261SBorislav Petkov */ 96*011d8261SBorislav Petkov 97*011d8261SBorislav Petkov u64 pfns_poisoned; /* 98*011d8261SBorislav Petkov * number of PFNs which got poisoned. 99*011d8261SBorislav Petkov */ 100*011d8261SBorislav Petkov 101*011d8261SBorislav Petkov u64 ces_entered; /* 102*011d8261SBorislav Petkov * The number of correctable errors 103*011d8261SBorislav Petkov * entered into the collector. 104*011d8261SBorislav Petkov */ 105*011d8261SBorislav Petkov 106*011d8261SBorislav Petkov u64 decays_done; /* 107*011d8261SBorislav Petkov * Times we did spring cleaning. 108*011d8261SBorislav Petkov */ 109*011d8261SBorislav Petkov 110*011d8261SBorislav Petkov union { 111*011d8261SBorislav Petkov struct { 112*011d8261SBorislav Petkov __u32 disabled : 1, /* cmdline disabled */ 113*011d8261SBorislav Petkov __resv : 31; 114*011d8261SBorislav Petkov }; 115*011d8261SBorislav Petkov __u32 flags; 116*011d8261SBorislav Petkov }; 117*011d8261SBorislav Petkov } ce_arr; 118*011d8261SBorislav Petkov 119*011d8261SBorislav Petkov static DEFINE_MUTEX(ce_mutex); 120*011d8261SBorislav Petkov static u64 dfs_pfn; 121*011d8261SBorislav Petkov 122*011d8261SBorislav Petkov /* Amount of errors after which we offline */ 123*011d8261SBorislav Petkov static unsigned int count_threshold = COUNT_MASK; 124*011d8261SBorislav Petkov 125*011d8261SBorislav Petkov /* 126*011d8261SBorislav Petkov * The timer "decays" element count each timer_interval which is 24hrs by 127*011d8261SBorislav Petkov * default. 128*011d8261SBorislav Petkov */ 129*011d8261SBorislav Petkov 130*011d8261SBorislav Petkov #define CEC_TIMER_DEFAULT_INTERVAL 24 * 60 * 60 /* 24 hrs */ 131*011d8261SBorislav Petkov #define CEC_TIMER_MIN_INTERVAL 1 * 60 * 60 /* 1h */ 132*011d8261SBorislav Petkov #define CEC_TIMER_MAX_INTERVAL 30 * 24 * 60 * 60 /* one month */ 133*011d8261SBorislav Petkov static struct timer_list cec_timer; 134*011d8261SBorislav Petkov static u64 timer_interval = CEC_TIMER_DEFAULT_INTERVAL; 135*011d8261SBorislav Petkov 136*011d8261SBorislav Petkov /* 137*011d8261SBorislav Petkov * Decrement decay value. We're using DECAY_BITS bits to denote decay of an 138*011d8261SBorislav Petkov * element in the array. On insertion and any access, it gets reset to max. 139*011d8261SBorislav Petkov */ 140*011d8261SBorislav Petkov static void do_spring_cleaning(struct ce_array *ca) 141*011d8261SBorislav Petkov { 142*011d8261SBorislav Petkov int i; 143*011d8261SBorislav Petkov 144*011d8261SBorislav Petkov for (i = 0; i < ca->n; i++) { 145*011d8261SBorislav Petkov u8 decay = DECAY(ca->array[i]); 146*011d8261SBorislav Petkov 147*011d8261SBorislav Petkov if (!decay) 148*011d8261SBorislav Petkov continue; 149*011d8261SBorislav Petkov 150*011d8261SBorislav Petkov decay--; 151*011d8261SBorislav Petkov 152*011d8261SBorislav Petkov ca->array[i] &= ~(DECAY_MASK << COUNT_BITS); 153*011d8261SBorislav Petkov ca->array[i] |= (decay << COUNT_BITS); 154*011d8261SBorislav Petkov } 155*011d8261SBorislav Petkov ca->decay_count = 0; 156*011d8261SBorislav Petkov ca->decays_done++; 157*011d8261SBorislav Petkov } 158*011d8261SBorislav Petkov 159*011d8261SBorislav Petkov /* 160*011d8261SBorislav Petkov * @interval in seconds 161*011d8261SBorislav Petkov */ 162*011d8261SBorislav Petkov static void cec_mod_timer(struct timer_list *t, unsigned long interval) 163*011d8261SBorislav Petkov { 164*011d8261SBorislav Petkov unsigned long iv; 165*011d8261SBorislav Petkov 166*011d8261SBorislav Petkov iv = interval * HZ + jiffies; 167*011d8261SBorislav Petkov 168*011d8261SBorislav Petkov mod_timer(t, round_jiffies(iv)); 169*011d8261SBorislav Petkov } 170*011d8261SBorislav Petkov 171*011d8261SBorislav Petkov static void cec_timer_fn(unsigned long data) 172*011d8261SBorislav Petkov { 173*011d8261SBorislav Petkov struct ce_array *ca = (struct ce_array *)data; 174*011d8261SBorislav Petkov 175*011d8261SBorislav Petkov do_spring_cleaning(ca); 176*011d8261SBorislav Petkov 177*011d8261SBorislav Petkov cec_mod_timer(&cec_timer, timer_interval); 178*011d8261SBorislav Petkov } 179*011d8261SBorislav Petkov 180*011d8261SBorislav Petkov /* 181*011d8261SBorislav Petkov * @to: index of the smallest element which is >= then @pfn. 182*011d8261SBorislav Petkov * 183*011d8261SBorislav Petkov * Return the index of the pfn if found, otherwise negative value. 184*011d8261SBorislav Petkov */ 185*011d8261SBorislav Petkov static int __find_elem(struct ce_array *ca, u64 pfn, unsigned int *to) 186*011d8261SBorislav Petkov { 187*011d8261SBorislav Petkov u64 this_pfn; 188*011d8261SBorislav Petkov int min = 0, max = ca->n; 189*011d8261SBorislav Petkov 190*011d8261SBorislav Petkov while (min < max) { 191*011d8261SBorislav Petkov int tmp = (max + min) >> 1; 192*011d8261SBorislav Petkov 193*011d8261SBorislav Petkov this_pfn = PFN(ca->array[tmp]); 194*011d8261SBorislav Petkov 195*011d8261SBorislav Petkov if (this_pfn < pfn) 196*011d8261SBorislav Petkov min = tmp + 1; 197*011d8261SBorislav Petkov else if (this_pfn > pfn) 198*011d8261SBorislav Petkov max = tmp; 199*011d8261SBorislav Petkov else { 200*011d8261SBorislav Petkov min = tmp; 201*011d8261SBorislav Petkov break; 202*011d8261SBorislav Petkov } 203*011d8261SBorislav Petkov } 204*011d8261SBorislav Petkov 205*011d8261SBorislav Petkov if (to) 206*011d8261SBorislav Petkov *to = min; 207*011d8261SBorislav Petkov 208*011d8261SBorislav Petkov this_pfn = PFN(ca->array[min]); 209*011d8261SBorislav Petkov 210*011d8261SBorislav Petkov if (this_pfn == pfn) 211*011d8261SBorislav Petkov return min; 212*011d8261SBorislav Petkov 213*011d8261SBorislav Petkov return -ENOKEY; 214*011d8261SBorislav Petkov } 215*011d8261SBorislav Petkov 216*011d8261SBorislav Petkov static int find_elem(struct ce_array *ca, u64 pfn, unsigned int *to) 217*011d8261SBorislav Petkov { 218*011d8261SBorislav Petkov WARN_ON(!to); 219*011d8261SBorislav Petkov 220*011d8261SBorislav Petkov if (!ca->n) { 221*011d8261SBorislav Petkov *to = 0; 222*011d8261SBorislav Petkov return -ENOKEY; 223*011d8261SBorislav Petkov } 224*011d8261SBorislav Petkov return __find_elem(ca, pfn, to); 225*011d8261SBorislav Petkov } 226*011d8261SBorislav Petkov 227*011d8261SBorislav Petkov static void del_elem(struct ce_array *ca, int idx) 228*011d8261SBorislav Petkov { 229*011d8261SBorislav Petkov /* Save us a function call when deleting the last element. */ 230*011d8261SBorislav Petkov if (ca->n - (idx + 1)) 231*011d8261SBorislav Petkov memmove((void *)&ca->array[idx], 232*011d8261SBorislav Petkov (void *)&ca->array[idx + 1], 233*011d8261SBorislav Petkov (ca->n - (idx + 1)) * sizeof(u64)); 234*011d8261SBorislav Petkov 235*011d8261SBorislav Petkov ca->n--; 236*011d8261SBorislav Petkov } 237*011d8261SBorislav Petkov 238*011d8261SBorislav Petkov static u64 del_lru_elem_unlocked(struct ce_array *ca) 239*011d8261SBorislav Petkov { 240*011d8261SBorislav Petkov unsigned int min = FULL_COUNT_MASK; 241*011d8261SBorislav Petkov int i, min_idx = 0; 242*011d8261SBorislav Petkov 243*011d8261SBorislav Petkov for (i = 0; i < ca->n; i++) { 244*011d8261SBorislav Petkov unsigned int this = FULL_COUNT(ca->array[i]); 245*011d8261SBorislav Petkov 246*011d8261SBorislav Petkov if (min > this) { 247*011d8261SBorislav Petkov min = this; 248*011d8261SBorislav Petkov min_idx = i; 249*011d8261SBorislav Petkov } 250*011d8261SBorislav Petkov } 251*011d8261SBorislav Petkov 252*011d8261SBorislav Petkov del_elem(ca, min_idx); 253*011d8261SBorislav Petkov 254*011d8261SBorislav Petkov return PFN(ca->array[min_idx]); 255*011d8261SBorislav Petkov } 256*011d8261SBorislav Petkov 257*011d8261SBorislav Petkov /* 258*011d8261SBorislav Petkov * We return the 0th pfn in the error case under the assumption that it cannot 259*011d8261SBorislav Petkov * be poisoned and excessive CEs in there are a serious deal anyway. 260*011d8261SBorislav Petkov */ 261*011d8261SBorislav Petkov static u64 __maybe_unused del_lru_elem(void) 262*011d8261SBorislav Petkov { 263*011d8261SBorislav Petkov struct ce_array *ca = &ce_arr; 264*011d8261SBorislav Petkov u64 pfn; 265*011d8261SBorislav Petkov 266*011d8261SBorislav Petkov if (!ca->n) 267*011d8261SBorislav Petkov return 0; 268*011d8261SBorislav Petkov 269*011d8261SBorislav Petkov mutex_lock(&ce_mutex); 270*011d8261SBorislav Petkov pfn = del_lru_elem_unlocked(ca); 271*011d8261SBorislav Petkov mutex_unlock(&ce_mutex); 272*011d8261SBorislav Petkov 273*011d8261SBorislav Petkov return pfn; 274*011d8261SBorislav Petkov } 275*011d8261SBorislav Petkov 276*011d8261SBorislav Petkov 277*011d8261SBorislav Petkov int cec_add_elem(u64 pfn) 278*011d8261SBorislav Petkov { 279*011d8261SBorislav Petkov struct ce_array *ca = &ce_arr; 280*011d8261SBorislav Petkov unsigned int to; 281*011d8261SBorislav Petkov int count, ret = 0; 282*011d8261SBorislav Petkov 283*011d8261SBorislav Petkov /* 284*011d8261SBorislav Petkov * We can be called very early on the identify_cpu() path where we are 285*011d8261SBorislav Petkov * not initialized yet. We ignore the error for simplicity. 286*011d8261SBorislav Petkov */ 287*011d8261SBorislav Petkov if (!ce_arr.array || ce_arr.disabled) 288*011d8261SBorislav Petkov return -ENODEV; 289*011d8261SBorislav Petkov 290*011d8261SBorislav Petkov ca->ces_entered++; 291*011d8261SBorislav Petkov 292*011d8261SBorislav Petkov mutex_lock(&ce_mutex); 293*011d8261SBorislav Petkov 294*011d8261SBorislav Petkov if (ca->n == MAX_ELEMS) 295*011d8261SBorislav Petkov WARN_ON(!del_lru_elem_unlocked(ca)); 296*011d8261SBorislav Petkov 297*011d8261SBorislav Petkov ret = find_elem(ca, pfn, &to); 298*011d8261SBorislav Petkov if (ret < 0) { 299*011d8261SBorislav Petkov /* 300*011d8261SBorislav Petkov * Shift range [to-end] to make room for one more element. 301*011d8261SBorislav Petkov */ 302*011d8261SBorislav Petkov memmove((void *)&ca->array[to + 1], 303*011d8261SBorislav Petkov (void *)&ca->array[to], 304*011d8261SBorislav Petkov (ca->n - to) * sizeof(u64)); 305*011d8261SBorislav Petkov 306*011d8261SBorislav Petkov ca->array[to] = (pfn << PAGE_SHIFT) | 307*011d8261SBorislav Petkov (DECAY_MASK << COUNT_BITS) | 1; 308*011d8261SBorislav Petkov 309*011d8261SBorislav Petkov ca->n++; 310*011d8261SBorislav Petkov 311*011d8261SBorislav Petkov ret = 0; 312*011d8261SBorislav Petkov 313*011d8261SBorislav Petkov goto decay; 314*011d8261SBorislav Petkov } 315*011d8261SBorislav Petkov 316*011d8261SBorislav Petkov count = COUNT(ca->array[to]); 317*011d8261SBorislav Petkov 318*011d8261SBorislav Petkov if (count < count_threshold) { 319*011d8261SBorislav Petkov ca->array[to] |= (DECAY_MASK << COUNT_BITS); 320*011d8261SBorislav Petkov ca->array[to]++; 321*011d8261SBorislav Petkov 322*011d8261SBorislav Petkov ret = 0; 323*011d8261SBorislav Petkov } else { 324*011d8261SBorislav Petkov u64 pfn = ca->array[to] >> PAGE_SHIFT; 325*011d8261SBorislav Petkov 326*011d8261SBorislav Petkov if (!pfn_valid(pfn)) { 327*011d8261SBorislav Petkov pr_warn("CEC: Invalid pfn: 0x%llx\n", pfn); 328*011d8261SBorislav Petkov } else { 329*011d8261SBorislav Petkov /* We have reached max count for this page, soft-offline it. */ 330*011d8261SBorislav Petkov pr_err("Soft-offlining pfn: 0x%llx\n", pfn); 331*011d8261SBorislav Petkov memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE); 332*011d8261SBorislav Petkov ca->pfns_poisoned++; 333*011d8261SBorislav Petkov } 334*011d8261SBorislav Petkov 335*011d8261SBorislav Petkov del_elem(ca, to); 336*011d8261SBorislav Petkov 337*011d8261SBorislav Petkov /* 338*011d8261SBorislav Petkov * Return a >0 value to denote that we've reached the offlining 339*011d8261SBorislav Petkov * threshold. 340*011d8261SBorislav Petkov */ 341*011d8261SBorislav Petkov ret = 1; 342*011d8261SBorislav Petkov 343*011d8261SBorislav Petkov goto unlock; 344*011d8261SBorislav Petkov } 345*011d8261SBorislav Petkov 346*011d8261SBorislav Petkov decay: 347*011d8261SBorislav Petkov ca->decay_count++; 348*011d8261SBorislav Petkov 349*011d8261SBorislav Petkov if (ca->decay_count >= CLEAN_ELEMS) 350*011d8261SBorislav Petkov do_spring_cleaning(ca); 351*011d8261SBorislav Petkov 352*011d8261SBorislav Petkov unlock: 353*011d8261SBorislav Petkov mutex_unlock(&ce_mutex); 354*011d8261SBorislav Petkov 355*011d8261SBorislav Petkov return ret; 356*011d8261SBorislav Petkov } 357*011d8261SBorislav Petkov 358*011d8261SBorislav Petkov static int u64_get(void *data, u64 *val) 359*011d8261SBorislav Petkov { 360*011d8261SBorislav Petkov *val = *(u64 *)data; 361*011d8261SBorislav Petkov 362*011d8261SBorislav Petkov return 0; 363*011d8261SBorislav Petkov } 364*011d8261SBorislav Petkov 365*011d8261SBorislav Petkov static int pfn_set(void *data, u64 val) 366*011d8261SBorislav Petkov { 367*011d8261SBorislav Petkov *(u64 *)data = val; 368*011d8261SBorislav Petkov 369*011d8261SBorislav Petkov return cec_add_elem(val); 370*011d8261SBorislav Petkov } 371*011d8261SBorislav Petkov 372*011d8261SBorislav Petkov DEFINE_DEBUGFS_ATTRIBUTE(pfn_ops, u64_get, pfn_set, "0x%llx\n"); 373*011d8261SBorislav Petkov 374*011d8261SBorislav Petkov static int decay_interval_set(void *data, u64 val) 375*011d8261SBorislav Petkov { 376*011d8261SBorislav Petkov *(u64 *)data = val; 377*011d8261SBorislav Petkov 378*011d8261SBorislav Petkov if (val < CEC_TIMER_MIN_INTERVAL) 379*011d8261SBorislav Petkov return -EINVAL; 380*011d8261SBorislav Petkov 381*011d8261SBorislav Petkov if (val > CEC_TIMER_MAX_INTERVAL) 382*011d8261SBorislav Petkov return -EINVAL; 383*011d8261SBorislav Petkov 384*011d8261SBorislav Petkov timer_interval = val; 385*011d8261SBorislav Petkov 386*011d8261SBorislav Petkov cec_mod_timer(&cec_timer, timer_interval); 387*011d8261SBorislav Petkov return 0; 388*011d8261SBorislav Petkov } 389*011d8261SBorislav Petkov DEFINE_DEBUGFS_ATTRIBUTE(decay_interval_ops, u64_get, decay_interval_set, "%lld\n"); 390*011d8261SBorislav Petkov 391*011d8261SBorislav Petkov static int count_threshold_set(void *data, u64 val) 392*011d8261SBorislav Petkov { 393*011d8261SBorislav Petkov *(u64 *)data = val; 394*011d8261SBorislav Petkov 395*011d8261SBorislav Petkov if (val > COUNT_MASK) 396*011d8261SBorislav Petkov val = COUNT_MASK; 397*011d8261SBorislav Petkov 398*011d8261SBorislav Petkov count_threshold = val; 399*011d8261SBorislav Petkov 400*011d8261SBorislav Petkov return 0; 401*011d8261SBorislav Petkov } 402*011d8261SBorislav Petkov DEFINE_DEBUGFS_ATTRIBUTE(count_threshold_ops, u64_get, count_threshold_set, "%lld\n"); 403*011d8261SBorislav Petkov 404*011d8261SBorislav Petkov static int array_dump(struct seq_file *m, void *v) 405*011d8261SBorislav Petkov { 406*011d8261SBorislav Petkov struct ce_array *ca = &ce_arr; 407*011d8261SBorislav Petkov u64 prev = 0; 408*011d8261SBorislav Petkov int i; 409*011d8261SBorislav Petkov 410*011d8261SBorislav Petkov mutex_lock(&ce_mutex); 411*011d8261SBorislav Petkov 412*011d8261SBorislav Petkov seq_printf(m, "{ n: %d\n", ca->n); 413*011d8261SBorislav Petkov for (i = 0; i < ca->n; i++) { 414*011d8261SBorislav Petkov u64 this = PFN(ca->array[i]); 415*011d8261SBorislav Petkov 416*011d8261SBorislav Petkov seq_printf(m, " %03d: [%016llx|%03llx]\n", i, this, FULL_COUNT(ca->array[i])); 417*011d8261SBorislav Petkov 418*011d8261SBorislav Petkov WARN_ON(prev > this); 419*011d8261SBorislav Petkov 420*011d8261SBorislav Petkov prev = this; 421*011d8261SBorislav Petkov } 422*011d8261SBorislav Petkov 423*011d8261SBorislav Petkov seq_printf(m, "}\n"); 424*011d8261SBorislav Petkov 425*011d8261SBorislav Petkov seq_printf(m, "Stats:\nCEs: %llu\nofflined pages: %llu\n", 426*011d8261SBorislav Petkov ca->ces_entered, ca->pfns_poisoned); 427*011d8261SBorislav Petkov 428*011d8261SBorislav Petkov seq_printf(m, "Flags: 0x%x\n", ca->flags); 429*011d8261SBorislav Petkov 430*011d8261SBorislav Petkov seq_printf(m, "Timer interval: %lld seconds\n", timer_interval); 431*011d8261SBorislav Petkov seq_printf(m, "Decays: %lld\n", ca->decays_done); 432*011d8261SBorislav Petkov 433*011d8261SBorislav Petkov seq_printf(m, "Action threshold: %d\n", count_threshold); 434*011d8261SBorislav Petkov 435*011d8261SBorislav Petkov mutex_unlock(&ce_mutex); 436*011d8261SBorislav Petkov 437*011d8261SBorislav Petkov return 0; 438*011d8261SBorislav Petkov } 439*011d8261SBorislav Petkov 440*011d8261SBorislav Petkov static int array_open(struct inode *inode, struct file *filp) 441*011d8261SBorislav Petkov { 442*011d8261SBorislav Petkov return single_open(filp, array_dump, NULL); 443*011d8261SBorislav Petkov } 444*011d8261SBorislav Petkov 445*011d8261SBorislav Petkov static const struct file_operations array_ops = { 446*011d8261SBorislav Petkov .owner = THIS_MODULE, 447*011d8261SBorislav Petkov .open = array_open, 448*011d8261SBorislav Petkov .read = seq_read, 449*011d8261SBorislav Petkov .llseek = seq_lseek, 450*011d8261SBorislav Petkov .release = single_release, 451*011d8261SBorislav Petkov }; 452*011d8261SBorislav Petkov 453*011d8261SBorislav Petkov static int __init create_debugfs_nodes(void) 454*011d8261SBorislav Petkov { 455*011d8261SBorislav Petkov struct dentry *d, *pfn, *decay, *count, *array; 456*011d8261SBorislav Petkov 457*011d8261SBorislav Petkov d = debugfs_create_dir("cec", ras_debugfs_dir); 458*011d8261SBorislav Petkov if (!d) { 459*011d8261SBorislav Petkov pr_warn("Error creating cec debugfs node!\n"); 460*011d8261SBorislav Petkov return -1; 461*011d8261SBorislav Petkov } 462*011d8261SBorislav Petkov 463*011d8261SBorislav Petkov pfn = debugfs_create_file("pfn", S_IRUSR | S_IWUSR, d, &dfs_pfn, &pfn_ops); 464*011d8261SBorislav Petkov if (!pfn) { 465*011d8261SBorislav Petkov pr_warn("Error creating pfn debugfs node!\n"); 466*011d8261SBorislav Petkov goto err; 467*011d8261SBorislav Petkov } 468*011d8261SBorislav Petkov 469*011d8261SBorislav Petkov array = debugfs_create_file("array", S_IRUSR, d, NULL, &array_ops); 470*011d8261SBorislav Petkov if (!array) { 471*011d8261SBorislav Petkov pr_warn("Error creating array debugfs node!\n"); 472*011d8261SBorislav Petkov goto err; 473*011d8261SBorislav Petkov } 474*011d8261SBorislav Petkov 475*011d8261SBorislav Petkov decay = debugfs_create_file("decay_interval", S_IRUSR | S_IWUSR, d, 476*011d8261SBorislav Petkov &timer_interval, &decay_interval_ops); 477*011d8261SBorislav Petkov if (!decay) { 478*011d8261SBorislav Petkov pr_warn("Error creating decay_interval debugfs node!\n"); 479*011d8261SBorislav Petkov goto err; 480*011d8261SBorislav Petkov } 481*011d8261SBorislav Petkov 482*011d8261SBorislav Petkov count = debugfs_create_file("count_threshold", S_IRUSR | S_IWUSR, d, 483*011d8261SBorislav Petkov &count_threshold, &count_threshold_ops); 484*011d8261SBorislav Petkov if (!decay) { 485*011d8261SBorislav Petkov pr_warn("Error creating count_threshold debugfs node!\n"); 486*011d8261SBorislav Petkov goto err; 487*011d8261SBorislav Petkov } 488*011d8261SBorislav Petkov 489*011d8261SBorislav Petkov 490*011d8261SBorislav Petkov return 0; 491*011d8261SBorislav Petkov 492*011d8261SBorislav Petkov err: 493*011d8261SBorislav Petkov debugfs_remove_recursive(d); 494*011d8261SBorislav Petkov 495*011d8261SBorislav Petkov return 1; 496*011d8261SBorislav Petkov } 497*011d8261SBorislav Petkov 498*011d8261SBorislav Petkov void __init cec_init(void) 499*011d8261SBorislav Petkov { 500*011d8261SBorislav Petkov if (ce_arr.disabled) 501*011d8261SBorislav Petkov return; 502*011d8261SBorislav Petkov 503*011d8261SBorislav Petkov ce_arr.array = (void *)get_zeroed_page(GFP_KERNEL); 504*011d8261SBorislav Petkov if (!ce_arr.array) { 505*011d8261SBorislav Petkov pr_err("Error allocating CE array page!\n"); 506*011d8261SBorislav Petkov return; 507*011d8261SBorislav Petkov } 508*011d8261SBorislav Petkov 509*011d8261SBorislav Petkov if (create_debugfs_nodes()) 510*011d8261SBorislav Petkov return; 511*011d8261SBorislav Petkov 512*011d8261SBorislav Petkov setup_timer(&cec_timer, cec_timer_fn, (unsigned long)&ce_arr); 513*011d8261SBorislav Petkov cec_mod_timer(&cec_timer, CEC_TIMER_DEFAULT_INTERVAL); 514*011d8261SBorislav Petkov 515*011d8261SBorislav Petkov pr_info("Correctable Errors collector initialized.\n"); 516*011d8261SBorislav Petkov } 517*011d8261SBorislav Petkov 518*011d8261SBorislav Petkov int __init parse_cec_param(char *str) 519*011d8261SBorislav Petkov { 520*011d8261SBorislav Petkov if (!str) 521*011d8261SBorislav Petkov return 0; 522*011d8261SBorislav Petkov 523*011d8261SBorislav Petkov if (*str == '=') 524*011d8261SBorislav Petkov str++; 525*011d8261SBorislav Petkov 526*011d8261SBorislav Petkov if (!strncmp(str, "cec_disable", 7)) 527*011d8261SBorislav Petkov ce_arr.disabled = 1; 528*011d8261SBorislav Petkov else 529*011d8261SBorislav Petkov return 0; 530*011d8261SBorislav Petkov 531*011d8261SBorislav Petkov return 1; 532*011d8261SBorislav Petkov } 533