diff options
author | Douglas Thompson <dougthompson@xmission.com> | 2007-07-19 04:49:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:53 -0400 |
commit | e27e3dac651771fe3250f6305dee277bce29fc5d (patch) | |
tree | 9c0ac81a0948d8e52a72865ff9fbae4a12031a32 /drivers/edac/edac_core.h | |
parent | 7c9281d76c1c0b130f79d5fc021084e9749959d4 (diff) |
drivers/edac: add edac_device class
This patch adds the new 'class' of object to be managed, named: 'edac_device'.
As a peer of the 'edac_mc' class of object, it provides a non-memory centric
view of an ERROR DETECTING device in hardware. It provides a sysfs interface
and an abstraction for varioius EDAC type devices.
Multiple 'instances' within the class are possible, with each 'instance'
able to have multiple 'blocks', and each 'block' having 'attributes'.
At the 'block' level there are the 'ce_count' and 'ue_count' fields
which the device driver can update and/or call edac_device_handle_XX()
functions. At each higher level are additional 'total' count fields,
which are a summation of counts below that level.
This 'edac_device' has been used to capture and present ECC errors
which are found in a a L1 and L2 system on a per CORE/CPU basis.
Signed-off-by: Douglas Thompson <dougthompson@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/edac/edac_core.h')
-rw-r--r-- | drivers/edac/edac_core.h | 252 |
1 files changed, 246 insertions, 6 deletions
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 397f144791ec..a3e4b97fe4fe 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h | |||
@@ -32,9 +32,14 @@ | |||
32 | #include <linux/completion.h> | 32 | #include <linux/completion.h> |
33 | #include <linux/kobject.h> | 33 | #include <linux/kobject.h> |
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/sysdev.h> | ||
36 | #include <linux/workqueue.h> | ||
37 | #include <linux/version.h> | ||
35 | 38 | ||
36 | #define EDAC_MC_LABEL_LEN 31 | 39 | #define EDAC_MC_LABEL_LEN 31 |
37 | #define MC_PROC_NAME_MAX_LEN 7 | 40 | #define EDAC_DEVICE_NAME_LEN 31 |
41 | #define EDAC_ATTRIB_VALUE_LEN 15 | ||
42 | #define MC_PROC_NAME_MAX_LEN 7 | ||
38 | 43 | ||
39 | #if PAGE_SHIFT < 20 | 44 | #if PAGE_SHIFT < 20 |
40 | #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) | 45 | #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) |
@@ -51,6 +56,10 @@ | |||
51 | #define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ | 56 | #define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ |
52 | printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) | 57 | printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) |
53 | 58 | ||
59 | /* edac_device printk */ | ||
60 | #define edac_device_printk(ctl, level, fmt, arg...) \ | ||
61 | printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) | ||
62 | |||
54 | /* prefixes for edac_printk() and edac_mc_printk() */ | 63 | /* prefixes for edac_printk() and edac_mc_printk() */ |
55 | #define EDAC_MC "MC" | 64 | #define EDAC_MC "MC" |
56 | #define EDAC_PCI "PCI" | 65 | #define EDAC_PCI "PCI" |
@@ -62,7 +71,7 @@ extern int edac_debug_level; | |||
62 | #define edac_debug_printk(level, fmt, arg...) \ | 71 | #define edac_debug_printk(level, fmt, arg...) \ |
63 | do { \ | 72 | do { \ |
64 | if (level <= edac_debug_level) \ | 73 | if (level <= edac_debug_level) \ |
65 | edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \ | 74 | edac_printk(KERN_EMERG, EDAC_DEBUG, fmt, ##arg); \ |
66 | } while(0) | 75 | } while(0) |
67 | 76 | ||
68 | #define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) | 77 | #define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) |
@@ -195,6 +204,8 @@ enum scrub_type { | |||
195 | 204 | ||
196 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ | 205 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ |
197 | 206 | ||
207 | extern char * edac_align_ptr(void *ptr, unsigned size); | ||
208 | |||
198 | /* | 209 | /* |
199 | * There are several things to be aware of that aren't at all obvious: | 210 | * There are several things to be aware of that aren't at all obvious: |
200 | * | 211 | * |
@@ -376,6 +387,231 @@ struct mem_ctl_info { | |||
376 | struct completion kobj_complete; | 387 | struct completion kobj_complete; |
377 | }; | 388 | }; |
378 | 389 | ||
390 | /* | ||
391 | * The following are the structures to provide for a generice | ||
392 | * or abstract 'edac_device'. This set of structures and the | ||
393 | * code that implements the APIs for the same, provide for | ||
394 | * registering EDAC type devices which are NOT standard memory. | ||
395 | * | ||
396 | * CPU caches (L1 and L2) | ||
397 | * DMA engines | ||
398 | * Core CPU swithces | ||
399 | * Fabric switch units | ||
400 | * PCIe interface controllers | ||
401 | * other EDAC/ECC type devices that can be monitored for | ||
402 | * errors, etc. | ||
403 | * | ||
404 | * It allows for a 2 level set of hiearchry. For example: | ||
405 | * | ||
406 | * cache could be composed of L1, L2 and L3 levels of cache. | ||
407 | * Each CPU core would have its own L1 cache, while sharing | ||
408 | * L2 and maybe L3 caches. | ||
409 | * | ||
410 | * View them arranged, via the sysfs presentation: | ||
411 | * /sys/devices/system/edac/.. | ||
412 | * | ||
413 | * mc/ <existing memory device directory> | ||
414 | * cpu/cpu0/.. <L1 and L2 block directory> | ||
415 | * /L1-cache/ce_count | ||
416 | * /ue_count | ||
417 | * /L2-cache/ce_count | ||
418 | * /ue_count | ||
419 | * cpu/cpu1/.. <L1 and L2 block directory> | ||
420 | * /L1-cache/ce_count | ||
421 | * /ue_count | ||
422 | * /L2-cache/ce_count | ||
423 | * /ue_count | ||
424 | * ... | ||
425 | * | ||
426 | * the L1 and L2 directories would be "edac_device_block's" | ||
427 | */ | ||
428 | |||
429 | struct edac_device_counter { | ||
430 | u32 ue_count; | ||
431 | u32 ce_count; | ||
432 | }; | ||
433 | |||
434 | #define INC_COUNTER(cnt) (cnt++) | ||
435 | |||
436 | /* | ||
437 | * An array of these is passed to the alloc() function | ||
438 | * to specify attributes of the edac_block | ||
439 | */ | ||
440 | struct edac_attrib_spec { | ||
441 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
442 | |||
443 | int type; | ||
444 | #define EDAC_ATTR_INT 0x01 | ||
445 | #define EDAC_ATTR_CHAR 0x02 | ||
446 | }; | ||
447 | |||
448 | |||
449 | /* Attribute control structure | ||
450 | * In this structure is a pointer to the driver's edac_attrib_spec | ||
451 | * The life of this pointer is inclusive in the life of the driver's | ||
452 | * life cycle. | ||
453 | */ | ||
454 | struct edac_attrib { | ||
455 | struct edac_device_block *block; /* Up Pointer */ | ||
456 | |||
457 | struct edac_attrib_spec *spec; /* ptr to module spec entry */ | ||
458 | |||
459 | union { /* actual value */ | ||
460 | int edac_attrib_int_value; | ||
461 | char edac_attrib_char_value[EDAC_ATTRIB_VALUE_LEN + 1]; | ||
462 | } edac_attrib_value; | ||
463 | }; | ||
464 | |||
465 | /* device block control structure */ | ||
466 | struct edac_device_block { | ||
467 | struct edac_device_instance *instance; /* Up Pointer */ | ||
468 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
469 | |||
470 | struct edac_device_counter counters; /* basic UE and CE counters */ | ||
471 | |||
472 | int nr_attribs; /* how many attributes */ | ||
473 | struct edac_attrib *attribs; /* this block's attributes */ | ||
474 | |||
475 | /* edac sysfs device control */ | ||
476 | struct kobject kobj; | ||
477 | struct completion kobj_complete; | ||
478 | }; | ||
479 | |||
480 | /* device instance control structure */ | ||
481 | struct edac_device_instance { | ||
482 | struct edac_device_ctl_info *ctl; /* Up pointer */ | ||
483 | char name[EDAC_DEVICE_NAME_LEN + 4]; | ||
484 | |||
485 | struct edac_device_counter counters; /* instance counters */ | ||
486 | |||
487 | u32 nr_blocks; /* how many blocks */ | ||
488 | struct edac_device_block *blocks; /* block array */ | ||
489 | |||
490 | /* edac sysfs device control */ | ||
491 | struct kobject kobj; | ||
492 | struct completion kobj_complete; | ||
493 | }; | ||
494 | |||
495 | |||
496 | /* | ||
497 | * Abstract edac_device control info structure | ||
498 | * | ||
499 | */ | ||
500 | struct edac_device_ctl_info { | ||
501 | /* for global list of edac_device_ctl_info structs */ | ||
502 | struct list_head link; | ||
503 | |||
504 | int dev_idx; | ||
505 | |||
506 | /* Per instance controls for this edac_device */ | ||
507 | int log_ue; /* boolean for logging UEs */ | ||
508 | int log_ce; /* boolean for logging CEs */ | ||
509 | int panic_on_ue; /* boolean for panic'ing on an UE */ | ||
510 | unsigned poll_msec; /* number of milliseconds to poll interval */ | ||
511 | unsigned long delay; /* number of jiffies for poll_msec */ | ||
512 | |||
513 | struct sysdev_class *edac_class; /* pointer to class */ | ||
514 | |||
515 | /* the internal state of this controller instance */ | ||
516 | int op_state; | ||
517 | #define OP_ALLOC 0x100 | ||
518 | #define OP_RUNNING_POLL 0x201 | ||
519 | #define OP_RUNNING_INTERRUPT 0x202 | ||
520 | #define OP_RUNNING_POLL_INTR 0x203 | ||
521 | #define OP_OFFLINE 0x300 | ||
522 | |||
523 | /* work struct for this instance */ | ||
524 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
525 | struct delayed_work work; | ||
526 | #else | ||
527 | struct work_struct work; | ||
528 | #endif | ||
529 | |||
530 | /* pointer to edac polling checking routine: | ||
531 | * If NOT NULL: points to polling check routine | ||
532 | * If NULL: Then assumes INTERRUPT operation, where | ||
533 | * MC driver will receive events | ||
534 | */ | ||
535 | void (*edac_check) (struct edac_device_ctl_info * edac_dev); | ||
536 | |||
537 | struct device *dev; /* pointer to device structure */ | ||
538 | |||
539 | const char *mod_name; /* module name */ | ||
540 | const char *ctl_name; /* edac controller name */ | ||
541 | |||
542 | void *pvt_info; /* pointer to 'private driver' info */ | ||
543 | |||
544 | unsigned long start_time;/* edac_device load start time (jiffies)*/ | ||
545 | |||
546 | /* these are for safe removal of mc devices from global list while | ||
547 | * NMI handlers may be traversing list | ||
548 | */ | ||
549 | struct rcu_head rcu; | ||
550 | struct completion complete; | ||
551 | |||
552 | /* sysfs top name under 'edac' directory | ||
553 | * and instance name: | ||
554 | * cpu/cpu0/... | ||
555 | * cpu/cpu1/... | ||
556 | * cpu/cpu2/... | ||
557 | * ... | ||
558 | */ | ||
559 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
560 | |||
561 | /* Number of instances supported on this control structure | ||
562 | * and the array of those instances | ||
563 | */ | ||
564 | u32 nr_instances; | ||
565 | struct edac_device_instance *instances; | ||
566 | |||
567 | /* Event counters for the this whole EDAC Device */ | ||
568 | struct edac_device_counter counters; | ||
569 | |||
570 | /* edac sysfs device control for the 'name' | ||
571 | * device this structure controls | ||
572 | */ | ||
573 | struct kobject kobj; | ||
574 | struct completion kobj_complete; | ||
575 | }; | ||
576 | |||
577 | /* To get from the instance's wq to the beginning of the ctl structure */ | ||
578 | #define to_edac_device_ctl_work(w) \ | ||
579 | container_of(w,struct edac_device_ctl_info,work) | ||
580 | |||
581 | /* Function to calc the number of delay jiffies from poll_msec */ | ||
582 | static inline void edac_device_calc_delay( | ||
583 | struct edac_device_ctl_info *edac_dev) | ||
584 | { | ||
585 | /* convert from msec to jiffies */ | ||
586 | edac_dev->delay = edac_dev->poll_msec * HZ / 1000; | ||
587 | } | ||
588 | |||
589 | /* | ||
590 | * The alloc() and free() functions for the 'edac_device' control info | ||
591 | * structure. A MC driver will allocate one of these for each edac_device | ||
592 | * it is going to control/register with the EDAC CORE. | ||
593 | */ | ||
594 | extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( | ||
595 | unsigned sizeof_private, | ||
596 | char *edac_device_name, | ||
597 | unsigned nr_instances, | ||
598 | char *edac_block_name, | ||
599 | unsigned nr_blocks, | ||
600 | unsigned offset_value, | ||
601 | struct edac_attrib_spec *attrib_spec, | ||
602 | unsigned nr_attribs | ||
603 | ); | ||
604 | |||
605 | /* The offset value can be: | ||
606 | * -1 indicating no offset value | ||
607 | * 0 for zero-based block numbers | ||
608 | * 1 for 1-based block number | ||
609 | * other for other-based block number | ||
610 | */ | ||
611 | #define BLOCK_OFFSET_VALUE_OFF ((unsigned) -1) | ||
612 | |||
613 | extern void edac_device_free_ctl_info( struct edac_device_ctl_info *ctl_info); | ||
614 | |||
379 | #ifdef CONFIG_PCI | 615 | #ifdef CONFIG_PCI |
380 | 616 | ||
381 | /* write all or some bits in a byte-register*/ | 617 | /* write all or some bits in a byte-register*/ |
@@ -466,13 +702,17 @@ extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | |||
466 | char *msg); | 702 | char *msg); |
467 | 703 | ||
468 | /* | 704 | /* |
469 | * This kmalloc's and initializes all the structures. | 705 | * edac_device APIs |
470 | * Can't be used if all structures don't have the same lifetime. | ||
471 | */ | 706 | */ |
472 | extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, | 707 | extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, |
473 | unsigned nr_chans); | 708 | unsigned nr_chans); |
474 | |||
475 | /* Free an mc previously allocated by edac_mc_alloc() */ | ||
476 | extern void edac_mc_free(struct mem_ctl_info *mci); | 709 | extern void edac_mc_free(struct mem_ctl_info *mci); |
710 | extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx); | ||
711 | extern struct edac_device_ctl_info * edac_device_del_device(struct device *dev); | ||
712 | extern void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, | ||
713 | int inst_nr, int block_nr, const char *msg); | ||
714 | extern void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, | ||
715 | int inst_nr, int block_nr, const char *msg); | ||
716 | |||
477 | 717 | ||
478 | #endif /* _EDAC_CORE_H_ */ | 718 | #endif /* _EDAC_CORE_H_ */ |