diff -ru rockbox-19669-dma/apps/debug_menu.c rockbox-19669-dmastats/apps/debug_menu.c --- rockbox-19669-dma/apps/debug_menu.c 2009-01-05 12:31:09.729200000 -0500 +++ rockbox-19669-dmastats/apps/debug_menu.c 2009-01-04 23:27:57.942000000 -0500 @@ -2079,6 +2079,9 @@ '0' + (i & 7)); } #endif +#if (CONFIG_STORAGE & STORAGE_ATA) && defined(ATA_STATS) + ata_stats_list(); +#endif return btn; } #else /* No SD, MMC or ATA */ @@ -2690,6 +2693,10 @@ { "View disk info", dbg_disk_info }, #if (CONFIG_STORAGE & STORAGE_ATA) { "Dump ATA identify info", dbg_identify_info}, +#ifdef ATA_STATS + {"Dump ATA stats", ata_stats_dump}, + {"Reset ATA stats", ata_stats_reset}, +#endif #endif #endif #ifdef HAVE_DIRCACHE diff -ru rockbox-19669-dma/firmware/drivers/ata.c rockbox-19669-dmastats/firmware/drivers/ata.c --- rockbox-19669-dma/firmware/drivers/ata.c 2009-01-05 12:31:09.750200000 -0500 +++ rockbox-19669-dmastats/firmware/drivers/ata.c 2009-01-05 12:25:18.903200000 -0500 @@ -34,6 +34,13 @@ #include "ata-target.h" #include "storage.h" +#ifdef ATA_STATS +#include "list.h" +#include "file.h" +#include "misc.h" +#include "sprintf.h" +#endif + #define SECTOR_SIZE (512) #define ATA_FEATURE ATA_ERROR @@ -207,6 +214,192 @@ static int set_multiple_mode(int sectors); static int set_features(void); +#ifdef ATA_STATS + +long spinstart; +static char statfilename[MAX_PATH]; + +struct { + const char *name; + unsigned long val; +} ata_stats[] = { +#define ATA_STAT_POWERONS 0 + { "Power-ons", 1 }, /* Count initial poweron */ +#define ATA_STAT_WAKEUPS 1 + { "Wake-ups", 0 }, + +#define ATA_STAT_SPINTIME 2 + { "Time spinning", 0 }, +#define ATA_STAT_ONTIME 3 + { "Time powered", 0 }, + +#define ATA_STAT_PIOREADS 4 + { "PIO reads", 0 }, +#define ATA_STAT_PIOREADSECT (ATA_STAT_PIOREADS+1) + { "PIO read sectors", 0 }, +#define ATA_STAT_PIOREADTIME (ATA_STAT_PIOREADS+2) + { "PIO read time", 0 }, +#define ATA_STAT_PIOREADFAILS (ATA_STAT_PIOREADS+3) + { "PIO read failures", 0 }, +#define ATA_STAT_PIOREADRETRIES (ATA_STAT_PIOREADS+4) + { "PIO read retries", 0 }, + +#define ATA_STAT_PIOWRITES 9 + { "PIO writes", 0 }, +#define ATA_STAT_PIOWRITESECT (ATA_STAT_PIOWRITES+1) + { "PIO written sectors", 0 }, +#define ATA_STAT_PIOWRITETIME (ATA_STAT_PIOWRITES+2) + { "PIO write time", 0 }, +#define ATA_STAT_PIOWRITEFAILS (ATA_STAT_PIOWRITES+3) + { "PIO write failures", 0 }, + +#ifdef HAVE_ATA_DMA +#define ATA_STAT_DMAREADS 13 + { "DMA reads", 0 }, +#define ATA_STAT_DMAREADSECT (ATA_STAT_DMAREADS+1) + { "DMA read sectors", 0 }, +#define ATA_STAT_DMAREADTIME (ATA_STAT_DMAREADS+2) + { "DMA read time", 0 }, +#define ATA_STAT_DMAREADFAILS (ATA_STAT_DMAREADS+3) + { "DMA read failures", 0 }, +#define ATA_STAT_DMAREADRETRIES (ATA_STAT_DMAREADS+4) + { "DMA read retries", 0 }, + +#define ATA_STAT_DMAWRITES 18 + { "DMA writes", 0 }, +#define ATA_STAT_DMAWRITESECT (ATA_STAT_DMAWRITES+1) + { "DMA written sectors", 0 }, +#define ATA_STAT_DMAWRITETIME (ATA_STAT_DMAWRITES+2) + { "DMA write time", 0 }, +#define ATA_STAT_DMAWRITEFAILS (ATA_STAT_DMAWRITES+3) + { "DMA write failures", 0 }, +#endif +}; + +#ifdef HAVE_ATA_DMA +#define ATA_STAT_ENTRIES 22 +#else +#define ATA_STAT_ENTRIES 13 +#endif + +static unsigned long statcache[ATA_STAT_ENTRIES]; + +#define ATA_STAT_INC(stat) ata_stats[stat].val++ +#define ATA_STAT_ADD(stat,add) ata_stats[stat].val+=(add) + +static unsigned long tokbs(unsigned long sectors, unsigned long us) +{ + /* Minimize error */ + if (sectors) { + while (!(sectors & 0x80000000) && !(us & 0x80000000)) { + sectors <<= 1; + us <<= 1; + } + } + + us /= 500000U; + if (!us) + return 0xFFFFFFFF; + else + return sectors / us; +} + +void ata_stats_list(void) +{ + unsigned long v; + + /* Aggregate error stats */ + simplelist_addline(SIMPLELIST_ADD_LINE, + "Spinning time: %lu.%03lu s", + ata_stats[ATA_STAT_SPINTIME].val/1000000, + (ata_stats[ATA_STAT_SPINTIME].val/1000)%1000); + + v = ata_stats[ATA_STAT_PIOREADFAILS].val + + ata_stats[ATA_STAT_PIOWRITEFAILS].val +#ifdef HAVE_ATA_DMA + + ata_stats[ATA_STAT_DMAREADFAILS].val + + ata_stats[ATA_STAT_DMAWRITEFAILS].val +#endif + ; + if (v) + simplelist_addline(SIMPLELIST_ADD_LINE, + "Failures %lu", v); + + v = ata_stats[ATA_STAT_PIOREADRETRIES].val +#ifdef HAVE_ATA_DMA + + ata_stats[ATA_STAT_DMAREADRETRIES].val +#endif + ; + if (v) + simplelist_addline(SIMPLELIST_ADD_LINE, + "Retries %lu", v); + + /* Aggregate stats for transfers */ + v = tokbs(ata_stats[ATA_STAT_PIOREADSECT].val, + ata_stats[ATA_STAT_PIOREADTIME].val); + if (v != 0xFFFFFFFF) + simplelist_addline(SIMPLELIST_ADD_LINE, + "PIO read speed: %lu KB/s", v); + + v = tokbs(ata_stats[ATA_STAT_PIOWRITESECT].val, + ata_stats[ATA_STAT_PIOWRITETIME].val); + if (v != 0xFFFFFFFF) + simplelist_addline(SIMPLELIST_ADD_LINE, + "PIO write speed: %lu KB/s", v); + +#ifdef HAVE_ATA_DMA + v = tokbs(ata_stats[ATA_STAT_DMAREADSECT].val, + ata_stats[ATA_STAT_DMAREADTIME].val); + if (v != 0xFFFFFFFF) + simplelist_addline(SIMPLELIST_ADD_LINE, + "DMA read speed: %lu KB/s", v); + + v = tokbs(ata_stats[ATA_STAT_DMAWRITESECT].val, + ata_stats[ATA_STAT_DMAWRITETIME].val); + if (v != 0xFFFFFFFF) + simplelist_addline(SIMPLELIST_ADD_LINE, + "DMA write speed: %lu KB/s", v); +#endif +} + +bool ata_stats_dump(void) +{ + int f, i; + + /* Avoid inaccuracies due to writes below */ + for (i = 0; i < ATA_STAT_ENTRIES; i++) + statcache[i] = ata_stats[i].val; + + create_numbered_filename(statfilename, "/", "ata_stats_", ".txt", + 2 IF_CNFN_NUM_(, NULL)); + f = open(statfilename, O_RDWR|O_CREAT|O_TRUNC); + + for (i = 0; i < ATA_STAT_ENTRIES; i++) + fdprintf(f, "%s: %lu\n", + ata_stats[i].name, + statcache[i]); + + close(f); + + return false; +} + +bool ata_stats_reset(void) +{ + int i; + + for (i = 0; i < ATA_STAT_ENTRIES; i++) + ata_stats[i].val = 0; + + return false; +} + +#else +/* Avoid need for conditionals in some places */ +#define ATA_STAT_INC(stat) +#define ATA_STAT_ADD(stat,add) +#endif /* ATA_STATS */ + STATICIRAM ICODE_ATTR int wait_for_bsy(void) { long timeout = current_tick + HZ*30; @@ -326,6 +519,9 @@ #ifdef HAVE_ATA_DMA bool usedma = false; #endif +#ifdef ATA_STATS + long cmd_start; +#endif #ifndef MAX_PHYS_SECTOR_SIZE #ifdef HAVE_MULTIVOLUME @@ -334,13 +530,15 @@ mutex_lock(&ata_mtx); #endif + cmd_start = USEC_TIMER; + if (start + incount > total_sectors) { ret = -1; goto error; } - last_disk_activity = current_tick; spinup_start = current_tick; + last_disk_activity = current_tick; ata_led(true); @@ -353,6 +551,10 @@ } } else { +#ifdef ATA_STATS + spinstart = USEC_TIMER; + ATA_STAT_INC(ATA_STAT_WAKEUPS); +#endif if (perform_soft_reset()) { ret = -2; goto error; @@ -369,11 +571,24 @@ goto error; } + /* For more accurate timing, don't include above */ + cmd_start = USEC_TIMER; + retry: buf = inbuf; count = incount; while (TIME_BEFORE(current_tick, timeout)) { - ret = 0; +#ifdef ATA_STATS + if (ret != 0) { +#ifdef HAVE_ATA_DMA + if (usedma) + ATA_STAT_INC(ATA_STAT_DMAREADRETRIES); + else +#endif + ATA_STAT_INC(ATA_STAT_PIOREADRETRIES); + } +#endif + last_disk_activity = current_tick; #ifdef HAVE_ATA_DMA @@ -384,8 +599,26 @@ /* If unflushed, old data may overwrite new on invalidate */ flush_icache(); #endif +#ifdef ATA_STATS + if (!ret) { + ATA_STAT_INC(ATA_STAT_DMAREADS); + ATA_STAT_ADD(ATA_STAT_DMAREADSECT, incount); + } +#endif } #endif +#ifdef ATA_STATS +#ifdef HAVE_ATA_DMA + else +#endif + if (!ret) + { + ATA_STAT_INC(ATA_STAT_PIOREADS); + ATA_STAT_ADD(ATA_STAT_PIOREADSECT, incount); + } +#endif + + ret = 0; #ifdef HAVE_LBA48 if (lba48) @@ -516,7 +749,24 @@ } error: +#ifdef ATA_STATS +#ifdef HAVE_ATA_DMA + if (usedma) { + ATA_STAT_ADD(ATA_STAT_DMAREADTIME, USEC_TIMER - cmd_start); + if (ret) + ATA_STAT_INC(ATA_STAT_DMAREADFAILS); + } + else +#endif + { + ATA_STAT_ADD(ATA_STAT_PIOREADTIME, USEC_TIMER - cmd_start); + if (ret) + ATA_STAT_INC(ATA_STAT_PIOREADFAILS); + } +#endif + ata_led(false); + #ifndef MAX_PHYS_SECTOR_SIZE mutex_unlock(&ata_mtx); #endif @@ -578,6 +828,9 @@ #ifdef HAVE_ATA_DMA bool usedma = false; #endif +#ifdef ATA_STATS + long cmd_start; +#endif #ifndef MAX_PHYS_SECTOR_SIZE #ifdef HAVE_MULTIVOLUME @@ -594,6 +847,8 @@ ata_led(true); + cmd_start = USEC_TIMER; + if ( sleeping ) { spinup = true; if (poweroff) { @@ -603,6 +858,10 @@ } } else { +#ifdef ATA_STATS + spinstart = USEC_TIMER; + ATA_STAT_INC(ATA_STAT_WAKEUPS); +#endif if (perform_soft_reset()) { ret = -1; goto error; @@ -617,6 +876,8 @@ goto error; } + cmd_start = USEC_TIMER; + #ifdef HAVE_ATA_DMA /* If DMA is supported and parameters are ok for DMA, use it */ if (dma_mode && ata_dma_setup((void *)buf, count * SECTOR_SIZE, true)) { @@ -625,6 +886,19 @@ /* If unflushed, old data may overwrite new on invalidate */ flush_icache(); #endif +#ifdef ATA_STATS + ATA_STAT_INC(ATA_STAT_DMAWRITES); + ATA_STAT_ADD(ATA_STAT_DMAWRITESECT, count); +#endif + } +#endif +#ifdef ATA_STATS +#ifdef HAVE_ATA_DMA + else +#endif + { + ATA_STAT_INC(ATA_STAT_PIOWRITES); + ATA_STAT_ADD(ATA_STAT_PIOWRITESECT, count); } #endif @@ -705,6 +979,22 @@ } error: +#ifdef ATA_STATS +#ifdef HAVE_ATA_DMA + if (usedma) { + ATA_STAT_ADD(ATA_STAT_DMAWRITETIME, USEC_TIMER - cmd_start); + if (ret) + ATA_STAT_INC(ATA_STAT_DMAWRITEFAILS); + } + else +#endif + { + ATA_STAT_ADD(ATA_STAT_PIOWRITETIME, USEC_TIMER - cmd_start); + if (ret) + ATA_STAT_INC(ATA_STAT_PIOWRITEFAILS); + } +#endif + ata_led(false); #ifndef MAX_PHYS_SECTOR_SIZE mutex_unlock(&ata_mtx); @@ -957,6 +1247,8 @@ return -2; } + ATA_STAT_ADD(ATA_STAT_SPINTIME, USEC_TIMER - spinstart); + sleeping = true; mutex_unlock(&ata_mtx); return 0; @@ -1031,6 +1323,7 @@ { mutex_lock(&ata_mtx); ide_power_enable(false); + ATA_STAT_ADD(ATA_STAT_ONTIME, USEC_TIMER - spinstart); mutex_unlock(&ata_mtx); poweroff = true; } @@ -1163,6 +1456,11 @@ static int ata_power_on(void) { int rc; + +#ifdef ATA_STATS + spinstart = USEC_TIMER; + ATA_STAT_INC(ATA_STAT_POWERONS); +#endif ide_power_enable(true); sleep(HZ/4); /* allow voltage to build up */ @@ -1423,6 +1721,11 @@ * may return at any point without having to unlock */ mutex_unlock(&ata_mtx); +#ifdef ATA_STATS + /* This is inaccurate, we don't know when it started spinning */ + spinstart = USEC_TIMER; +#endif + if (!ide_powered()) /* somebody has switched it off */ { ide_power_enable(true); diff -ru rockbox-19669-dma/firmware/export/ata.h rockbox-19669-dmastats/firmware/export/ata.h --- rockbox-19669-dma/firmware/export/ata.h 2009-01-05 12:31:09.772200000 -0500 +++ rockbox-19669-dmastats/firmware/export/ata.h 2009-01-05 12:35:42.140200000 -0500 @@ -66,4 +66,11 @@ int ata_get_dma_mode(void); #endif +#define ATA_STATS +#ifdef ATA_STATS +void ata_stats_list(void); +bool ata_stats_dump(void); +bool ata_stats_reset(void); +#endif + #endif