Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* Includes */
- #include <asm/uaccess.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/sysfs.h>
- /* Defines */
- /**
- * Maximum number of PMCs (fixed-function and general-purpose combined)
- */
- #define MAXPMC 25
- #define MSR_IA32_PERFEVTSEL0 0x186
- #define MSR_IA32_FIXED_CTR0 0x309
- #define MSR_IA32_FIXED_CTR_CTRL 0x38D
- #define MSR_IA32_PERF_GLOBAL_STATUS 0x38E
- #define MSR_IA32_PERF_GLOBAL_CTRL 0x38F
- #define MSR_IA32_PERF_GLOBAL_OVF_CTRL 0x390
- #define MSR_IA32_A_PMC0 0x4C1
- /* Forward Declarations */
- static ssize_t pfcCfgRd (struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len);
- static ssize_t pfcCfgWr(struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len);
- static ssize_t pfcCntRd (struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len);
- static ssize_t pfcCntWr(struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len);
- /* Global Variables & Constants */
- static int pmcArchVer = 0;
- static int pmcFf = 0;
- static int pmcGp = 0;
- static int pmcStartFf = 0;
- static int pmcEndFf = 0;
- static int pmcStartGp = 0;
- static int pmcEndGp = 0;
- static uint64_t pmcMaskFf = 0;
- static uint64_t pmcMaskGp = 0;
- /**
- * The counters consist in the following MSRs on Core i7:
- * [0 ] IA32_FIXED_CTR0: Fixed-function - Retired Instructions
- * [1 ] IA32_FIXED_CTR1: Fixed-function - Unhalted Core CCs
- * [2 ] IA32_FIXED_CTR2: Fixed-function - Unhalted Reference CCs
- * [3+ ] IA32_A_PMCx: General-purpose - Configurable
- */
- /* Attribute Hierarchy */
- static const struct bin_attribute PFC_ATTR_config = {
- .attr = {.name="config", .mode=0666},
- .size = MAXPMC*sizeof(uint64_t),
- .read = pfcCfgRd,
- .write = pfcCfgWr
- };
- static const struct bin_attribute PFC_ATTR_counts = {
- .attr = {.name="counts", .mode=0666},
- .size = MAXPMC*sizeof(uint64_t),
- .read = pfcCntRd,
- .write = pfcCntWr
- };
- static const struct bin_attribute* PFC_ATTR_GRP_LIST[] = {
- &PFC_ATTR_config,
- &PFC_ATTR_counts,
- NULL
- };
- static const struct attribute_group PFC_ATTR_GRP = {
- .name = NULL,
- .bin_attrs = (struct bin_attribute **)PFC_ATTR_GRP_LIST
- };
- /* Static Function Definitions */
- /***************** UTILITIES *****************/
- /**
- * @brief Ones Vector.
- *
- * Generate a 64-bit bitvector of ones with
- *
- * val[63 :n+k] = 0
- * val[n+k-1: k] = 1
- * val[ k-1: 0] = 0
- *
- * i.e. where the n bits starting at k counting from the LSB are all set, and
- * all other bits are 0.
- */
- static uint64_t OV(int n, int k){
- uint64_t v = n >= 64 ? ~0 : ((uint64_t)1 << n) - 1;
- return v << k;
- }
- /**
- * @brief Bit Vector.
- *
- * Generate a 64-bit bitvector with
- *
- * val[63 :n+k] = 0
- * val[n+k-1: k] = v[n:0]
- * val[ k-1: 0] = 0
- *
- * i.e. where the n bits starting at k counting from the LSB are taken from
- * the LSBs of v, and all other bits are 0.
- */
- static uint64_t BV(uint64_t v, int n, int k){
- v &= OV(n, 0);
- return v << k;
- }
- /**
- * @brief Clear Vector.
- *
- * Generate a 64-bit bitvector with
- *
- * val = v
- * val[n+k-1: k] = 0
- *
- * i.e. where the n bits starting at k counting from the LSB are set to 0,
- * and all other bits are taken from v.
- */
- static uint64_t CV(uint64_t v, int n, int k){
- return v & ~OV(n,k);
- }
- /**
- * @brief CPUID wrapper.
- *
- * @note Makes no attempt at validating EAX/ECX before calling CPUID!
- */
- static void pfcCPUID(uint32_t ieax,
- uint32_t iecx,
- uint32_t* oeax,
- uint32_t* oebx,
- uint32_t* oecx,
- uint32_t* oedx){
- asm volatile(
- "cpuid\n\t" /* ASM */
- :"=a"(*oeax), /* Outputs */
- "=b"(*oebx),
- "=c"(*oecx),
- "=d"(*oedx)
- :"a"(ieax), /* Inputs */
- "c"(iecx)
- :"memory" /* Clobbers */
- );
- }
- /**
- * @brief RDMSR wrapper.
- *
- * Returns the unmodified value of the given MSR.
- *
- * @returns The value read at the given MSR.
- * @note Does *not* check that addr is valid!
- */
- static uint64_t pfcRDMSR(uint64_t addr){
- uint32_t lo, hi;
- uint64_t lo64, hi64;
- asm volatile(
- "rdmsr\n\t" /* ASM */
- :"=a"(lo), /* Outputs */
- "=d"(hi)
- :"c"(addr) /* Inputs */
- :"memory" /* Clobbers */
- );
- lo64 = lo;
- hi64 = hi;
- return (hi64 << 32) | (lo64 << 0);
- }
- /**
- * @brief WRMSR wrapper.
- *
- * Writes to the given MSR. If it is a known MSR, mask out reserved bits into
- * a temporary, logic-OR the reserved bits into the temporary and write back
- * this temporary.
- *
- * @note Does *not* check that addr is valid!
- */
- static void pfcWRMSR(uint64_t addr, uint64_t val){
- uint64_t oldVal, mask;
- /**
- * For writing to MSRs, it's required to retrieve the old value of
- * reserved bits and write them back. Things seem to blow up big-time
- * otherwise.
- *
- * We retrieve a mask which indicates using the bits that are set what
- * corresponding bits are reserved.
- */
- if(addr == MSR_IA32_PERF_GLOBAL_CTRL){ mask=~(OV(pmcGp,0)|OV(pmcFf,32));
- }else if(addr == MSR_IA32_PERF_GLOBAL_OVF_CTRL){mask=~(OV(pmcGp,0)|OV(pmcFf,32)|OV(2,62));
- }else if(addr == MSR_IA32_FIXED_CTR_CTRL){ mask=~OV(4*pmcFf,0);
- }else if(addr == MSR_IA32_PERF_GLOBAL_STATUS){ return;/* RO MSR! */
- }else if(addr >= MSR_IA32_FIXED_CTR0 &&
- addr < MSR_IA32_FIXED_CTR0+pmcFf){ mask=~pmcMaskFf;
- }else if(addr >= MSR_IA32_PERFEVTSEL0 &&
- addr < MSR_IA32_PERFEVTSEL0+pmcGp){ mask=OV(32,32);
- }else if(addr >= MSR_IA32_A_PMC0 &&
- addr < MSR_IA32_A_PMC0+pmcGp){ mask=~pmcMaskGp;
- }else{ mask=OV(64,0);
- }
- oldVal = pfcRDMSR(addr);
- /**
- * Blend new and old & Writeback
- */
- val = (val&~mask) | (oldVal&mask);
- asm volatile(
- "wrmsr\n\t" /* ASM */
- :
- :"c"(addr), /* Inputs */
- "a"((uint32_t)val),
- "d"((uint32_t)(val >> 32))
- :"memory" /* Clobbers */
- );
- }
- /**
- * Clamp offset+len into a given counter range.
- *
- * Writes out the overlap's bounds through the output arguments.
- *
- * Returns whether or not [offset+len) overlaps [rangeStart, rangeEnd)
- */
- static int pfcClampRange(int off,
- int len,
- int rangeStart,
- int rangeEnd,
- int* pmcStart,
- int* pmcEnd){
- if(off+len <= rangeStart || /* If given range is fully before or */
- off >= rangeEnd || /* fully after the target range, or */
- len <= 0){ /* its length is <=0, then */
- return 0;
- }else{
- *pmcStart = off < rangeStart ? rangeStart : off;
- *pmcEnd = off+len > rangeEnd ? rangeEnd : off+len;
- return 1;
- }
- }
- /**
- * Check whether or not the given offset+length are sanely aligned.
- */
- static int pfcIsAligned(loff_t off, size_t len, size_t mask){
- return !((off|len) & mask);
- }
- /*************** END UTILITIES ***************/
- /**
- * The basic operations on a single counter are:
- *
- * - Write Enable
- * - Read Enable
- * - Write Configuration
- * - Read Configuration
- * - Write Value
- * - Read Value
- * - Clear Flags
- * - Read Flags / Status
- *
- * The following operations can be done globally over all counters atomically:
- *
- * - Write Enable
- * - Read Enable
- * - Ff Write Configuration
- * - Ff Read Configuration
- * - Clear Flags
- * - Read Flags/Status
- */
- /* All */
- void pfcAllCntWrEnb (uint64_t v){
- pfcWRMSR(MSR_IA32_PERF_GLOBAL_CTRL, v);
- }
- uint64_t pfcAllCntRdEnb (void){
- return pfcRDMSR(MSR_IA32_PERF_GLOBAL_CTRL);
- }
- void pfcAllFfCntWrCfg(uint64_t c){
- /**
- * We forbid the setting of the following bits in each 4-bit config group.
- * Bit 3: PMI Interrupt on counter overflow
- * Bit 2: AnyThread bit
- * Bit 0: OS-mode bit
- * for all counters. Effectively the only bit permitted is Bit 1 (User-mode
- * tracking of event).
- */
- c &= ~0xDDDDDDDDDDDDDDDDULL;
- pfcWRMSR(MSR_IA32_FIXED_CTR_CTRL, c);
- }
- uint64_t pfcAllFfCntRdCfg(void){
- return pfcRDMSR(MSR_IA32_FIXED_CTR_CTRL);
- }
- void pfcAllCntClFlg (uint64_t v){
- pfcWRMSR(MSR_IA32_PERF_GLOBAL_OVF_CTRL, v);
- }
- uint64_t pfcAllCntRdFlg (void){
- return pfcRDMSR(MSR_IA32_PERF_GLOBAL_STATUS);
- }
- /* Ff */
- void pfcFfCntWrEnb(int i, int v){
- int n = 1, k = 32+i;
- pfcAllCntWrEnb(CV(pfcAllCntRdEnb(),n,k) | BV(v,n,k));
- }
- int pfcFfCntRdEnb(int i){
- int n = 1, k = 32+i;
- return !!(pfcAllCntRdEnb() & OV(n,k));
- }
- void pfcFfCntWrCfg(int i, uint64_t c){
- int n = 4, k = 4*i;
- pfcAllFfCntWrCfg(CV(pfcAllFfCntRdCfg(),n,k) | BV(c,n,k));
- }
- uint64_t pfcFfCntRdCfg(int i){
- int n = 4, k = 4*i;
- return (pfcAllFfCntRdCfg() & OV(n,k)) >> k;
- }
- void pfcFfCntWrVal(int i, uint64_t v){
- pfcWRMSR(MSR_IA32_FIXED_CTR0+i, v);
- }
- uint64_t pfcFfCntRdVal(int i){
- return pfcRDMSR(MSR_IA32_FIXED_CTR0+i);
- }
- void pfcFfCntClFlg(int i, int v){
- int n = 1, k = 32+i;
- pfcAllCntClFlg(BV(v,n,k));
- }
- int pfcFfCntRdFlg(int i){
- int n = 1, k = 32+i;
- return !!(pfcAllCntRdFlg() & OV(n,k));
- }
- /* Gp */
- void pfcGpCntWrEnb(int i, int v){
- int n = 1, k = i;
- pfcAllCntWrEnb(CV(pfcAllCntRdEnb(),n,k) | BV(v,n,k));
- }
- int pfcGpCntRdEnb(int i){
- int n = 1, k = i;
- return !!(pfcAllCntRdEnb() & OV(n,k));
- }
- void pfcGpCntWrCfg(int i, uint64_t c){
- uint64_t evtNum, umask;
- /**
- * We forbid the setting of the following bits in each PERFEVTSELx MSR:
- * Bit 21: AnyThread bit
- * Bit 20: APIC Interrupt Enable on overflow bit
- * Bit 19: Pin Control bit
- * Bit 17: OS-mode bit
- * for all counters.
- */
- c &= ~0x00000000003A0000ULL;
- /**
- * For odd reasons, certain cache statistics can only be collected on
- * certain counters.
- */
- evtNum = (c >> 0) & 0xFF;
- umask = (c >> 8) & 0xFF;
- if((evtNum == 0x48) || /* l1d_pend_miss */
- (evtNum == 0xA3 && (umask == 0x08 || umask == 0x0C))){/* cycle_activity.l1d_pending */
- if(i != 2){
- c = 0;/* Disable. */
- }
- }
- if(evtNum == 0xC0 && umask == 0x01){
- if(i != 1){
- c = 0;/* Disable. */
- }
- }
- pfcWRMSR(MSR_IA32_PERFEVTSEL0+i, c);
- }
- uint64_t pfcGpCntRdCfg(int i){
- return pfcRDMSR(MSR_IA32_PERFEVTSEL0+i);
- }
- void pfcGpCntWrVal(int i, uint64_t v){
- pfcWRMSR(MSR_IA32_A_PMC0+i, v);
- }
- uint64_t pfcGpCntRdVal(int i){
- return pfcRDMSR(MSR_IA32_A_PMC0+i);
- }
- void pfcGpCntClFlg(int i, int v){
- int n = 1, k = i;
- pfcAllCntClFlg(BV(v,n,k));
- }
- int pfcGpCntRdFlg(int i){
- int n = 1, k = i;
- return !!(pfcAllCntRdFlg() & OV(n,k));
- }
- /**************** SYSFS ATTRIBUTES ****************/
- /**
- * Read configuration.
- *
- * Returns the configuration of the selected counters, one 64-bit word per
- * counter, with the Ff counters first and the Gp counters last.
- *
- * @return Bytes of configuration data read
- */
- static ssize_t pfcCfgRd (struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len){
- int pmcStart, pmcEnd, i, j;
- uint64_t* buf64 = (uint64_t*)buf;
- /* Check access is reasonable. */
- if(!pfcIsAligned(off, len, 0x7) || off<0 || len<0){
- return -1;
- }
- /* Read relevant MSRs */
- j=0;
- if(pfcClampRange(off>>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* READ */ buf64[j] = pfcFfCntRdCfg(i-pmcStartFf);
- }
- }
- if(pfcClampRange(off>>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* READ */ buf64[j] = pfcGpCntRdCfg(i-pmcStartGp);
- }
- }
- /* Report read data */
- return j*sizeof(uint64_t);
- }
- /**
- * Write configuration.
- *
- * Sets the configuration of the selected counters, given one 64-bit word per
- * counter, with the Ff counters first and the Gp counters last.
- *
- * Disables and leaves disabled all selected counters.
- *
- * @return Bytes of configuration data written
- */
- static ssize_t pfcCfgWr(struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len){
- int pmcStart, pmcEnd, i, j;
- uint64_t* buf64 = (uint64_t*)buf;
- /* Check access is reasonable. */
- if(!pfcIsAligned(off, len, 0x7) || off<0 || len<0){
- return -1;
- }
- /* Read relevant MSRs */
- j=0;
- if(pfcClampRange(off>>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* DISABLE */ pfcFfCntWrEnb(i-pmcStartFf, 0);
- /* WRITE */ pfcFfCntWrCfg(i-pmcStartFf, buf64[j]);
- }
- }
- if(pfcClampRange(off>>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* DISABLE */ pfcGpCntWrEnb(i-pmcStartGp, 0);
- /* WRITE */ pfcGpCntWrCfg(i-pmcStartGp, buf64[j]);
- }
- }
- /* Report written data */
- return j*sizeof(uint64_t);
- }
- /**
- * Reads counts.
- *
- * Returns the counts of the selected counters, one 64-bit word per counter,
- * with the Ff counters first and the Gp counters last.
- *
- * Enables and starts the selected counters.
- *
- * @return Bytes of counter data read
- */
- static ssize_t pfcCntRd (struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len){
- int pmcStart, pmcEnd, i, j;
- uint64_t* buf64 = (uint64_t*)buf;
- /* Check access is reasonable. */
- if(!pfcIsAligned(off, len, 0x7) || off<0 || len<0){
- return -1;
- }
- /* Read relevant MSRs */
- j=0;
- if(pfcClampRange(off>>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* READ */ buf64[j] = pfcFfCntRdVal(i-pmcStartFf);
- /* ENABLE */ pfcFfCntWrEnb(i-pmcStartFf, 1);
- }
- }
- if(pfcClampRange(off>>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* READ */ buf64[j] = pfcGpCntRdVal(i-pmcStartGp);
- /* ENABLE */ pfcGpCntWrEnb(i-pmcStartGp, 1);
- }
- }
- /* Report read data */
- return j*sizeof(uint64_t);
- }
- /**
- * Write counts.
- *
- * Sets the value of the selected counters, given one 64-bit word per
- * counter, with the Ff counters first and the Gp counters last.
- *
- * Disables and leaves disabled all selected counters.
- *
- * @return Bytes of counter data written
- */
- static ssize_t pfcCntWr(struct file* f,
- struct kobject* kobj,
- struct bin_attribute* binattr,
- char* buf,
- loff_t off,
- size_t len){
- int pmcStart, pmcEnd, i, j;
- uint64_t* buf64 = (uint64_t*)buf;
- /* Check access is reasonable. */
- if(!pfcIsAligned(off, len, 0x7) || off<0 || len<0){
- return -1;
- }
- /* Read relevant MSRs */
- j=0;
- if(pfcClampRange(off>>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* DISABLE */ pfcFfCntWrEnb(i-pmcStartFf, 0);
- /* WRITE */ pfcFfCntWrVal(i-pmcStartFf, buf64[j]);
- }
- }
- if(pfcClampRange(off>>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){
- for(i=pmcStart;i<pmcEnd;i++,j++){
- /* DISABLE */ pfcGpCntWrEnb(i-pmcStartGp, 0);
- /* WRITE */ pfcGpCntWrVal(i-pmcStartGp, buf64[j]);
- }
- }
- /* Report written data */
- return j*sizeof(uint64_t);
- }
- /**************** INIT CODE ****************/
- /**
- * Read CPUID to identify # of fixed-function and general-purpose PMCs, as well
- * as quite a few other things.
- */
- static void pfcInitCPUID(void){
- uint32_t a,b,c,d;
- /**
- * PMC information is gotten by CPUID.EAX = 0xA.
- * PerfMon architecture version is in EAX[ 7: 0].
- * #Gp PMCs is in EAX[15: 8]
- * Gp bitwidth is in EAX[23:16]
- * #Ff PMCs is in EDX[ 4: 0] if PMArchVer > 1.
- * Ff bitwidth is in EDX[12: 5] if PMArchVer > 1.
- */
- pfcCPUID(0xA,0,&a,&b,&c,&d);
- pmcArchVer = (a >> 0) & 0xFF;
- pmcGp = (a >> 8) & 0xFF;
- pmcMaskGp = (a >> 16) & 0xFF;
- if(pmcArchVer <= 1){
- pmcFf = 0;
- }else{
- pmcFf = (d >> 0) & 0x1F;
- pmcMaskFf = (d >> 5) & 0xFF;
- }
- pmcMaskGp = OV(pmcMaskGp,0);
- pmcMaskFf = OV(pmcMaskFf,0);
- /* Dump out this data */
- printk(KERN_INFO "PM Arch Version: %d\n", pmcArchVer);
- if(pmcFf + pmcGp > MAXPMC){
- printk(KERN_INFO "More than %d PMCs found! Clamping to %d.\n",
- MAXPMC,
- MAXPMC);
- pmcGp = pmcGp>MAXPMC ? MAXPMC : pmcGp;
- pmcFf = pmcGp+pmcFf>MAXPMC ? MAXPMC-pmcGp : pmcFf;
- }
- printk(KERN_INFO "Fixed-function PMCs: %d\tMask %016llx\n", pmcFf, pmcMaskFf);
- printk(KERN_INFO "General-purpose PMCs: %d\tMask %016llx\n", pmcGp, pmcMaskGp);
- /* Save bounds. */
- pmcStartFf = 0;
- pmcEndFf = pmcFf;
- pmcStartGp = pmcFf;
- pmcEndGp = pmcFf+pmcGp;
- }
- /**
- * Initialize all counters.
- *
- * For each counter, therefore,
- * 1. Globally disable (En=0)
- * 2. Deconfigure (Cf=0)
- * 3. Zero (Wr=0)
- * 4. Clear Flags (Cl=1)
- */
- void pfcInitCnts(void){
- int i;
- pfcAllCntWrEnb (0);
- pfcAllFfCntWrCfg(0);
- for(i=0;i<pmcFf;i++){
- pfcFfCntWrVal(i, 0);
- }
- for(i=0;i<pmcGp;i++){
- pfcGpCntWrCfg(i, 0);
- pfcGpCntWrVal(i, 0);
- }
- pfcAllCntClFlg (1);
- }
- /**
- * Load module.
- *
- * @return 0 if module load successful, non-0 otherwise.
- */
- static int __init pfcInit(void){
- int ret;
- /* Loading sequence begins... */
- printk(KERN_INFO "Module perfcount loading...\n");
- /**
- * Initialization depends on various constants retrieved using CPUID.
- */
- pfcInitCPUID();
- /**
- * We implicitly rely far too heavily on PMArchVer being 3.
- *
- * Abort if PM Arch Version != 3
- */
- if(pmcArchVer != 3){
- printk(KERN_INFO "Performance monitoring architecture version %d, not 3!"
- "Failed to load module perfcount.\n", pmcArchVer);
- return 1;
- }
- /**
- * If it is pmcArchVer == 3, we can safely proceed with counter init.
- */
- pfcInitCnts();
- /**
- * Register binary attributes under own directory.
- */
- ret = sysfs_create_group((struct kobject*)&THIS_MODULE->mkobj,
- &PFC_ATTR_GRP);
- /* Either it was successful, or no. */
- if(ret != 0){
- printk(KERN_INFO "Failed to create sysfs attributes. Failed to load "
- "module perfcount.\n");
- return ret;
- }else{
- printk(KERN_INFO "Module perfcount loaded successfully.\n");
- return 0;
- }
- }
- /**
- * Exit module.
- */
- static void __exit pfcExit(void){
- printk(KERN_INFO "Module perfcount exiting...\n");
- /* Disable everything */
- pfcInitCnts();
- /* Deregister binary attributes under own directory. */
- sysfs_remove_group((struct kobject*)&THIS_MODULE->mkobj,
- &PFC_ATTR_GRP);
- printk(KERN_INFO "Module perfcount exited.\n");
- }
- /* Module magic */
- module_init(pfcInit);
- module_exit(pfcExit);
- MODULE_LICENSE("Dual MIT/GPL");
- MODULE_AUTHOR("");
- MODULE_DESCRIPTION("Grants ***EXTREMELY UNSAFE*** access to the Intel Performance Monitor Counters (PMC).");
- /* Notes */
- /**
- * Required boot args:
- * nmi_watchdog=0 modprobe.blacklist=iTCO_wdt,iTCO_vendor_support
- *
- * Otherwise, IA32_FIXED_CTR1 is monopolised by !@#$ NMI watchdog crapware. Even
- * so, it seems that something gratuitously, though rarely, sets IA32_PMC0 to
- * 0xFFFF every so often.
- */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement