/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2014 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #ifdef CONFIG_PROC_FS struct _tffs_stat { unsigned int id; unsigned long access; unsigned long firstaccess_jiffies; unsigned long lastaccess_jiffies; unsigned long long size; struct _tffs_stat *next; }; static struct _tffs_stat *tffsstat_readanchor; static struct _tffs_stat *tffsstat_writeanchor; /*--------------------------------------------------------------------------------*\ * access_inc: add one to access and sort in list \*--------------------------------------------------------------------------------*/ static struct _tffs_stat *tffsstat_process(unsigned int id, struct _tffs_stat **anchor, unsigned access_inc) { struct _tffs_stat *act, *tmpact = NULL, *lastact = NULL; unsigned long access = 0; act = *anchor; while(act) { if(act->access != access) { access = act->access; tmpact = lastact; /*--- printk(KERN_INFO"mark last access=%lu lastact=%p\n", act->access, tmpact); ---*/ } if(act->id == id) { if(access_inc) { act->access++; if(act->access > access) { /*--- printk(KERN_INFO"resort id=%u access=%lu\n", act->id, act->access); ---*/ /*--- resort ---*/ if(lastact) lastact->next = act->next; if(tmpact) { act->next = tmpact->next; tmpact->next = act; /*--- printk(KERN_INFO"set id=%u(%lu) behind id=%u(%lu)\n", act->id, act->access, tmpact->id, tmpact->access); ---*/ } else if(*anchor != act) { /*--- printk(KERN_INFO"set id=%u(%lu) as anchor\n", act->id, act->access); ---*/ act->next = *anchor; *anchor = act; } } } return act; } lastact = act; act = act->next; } if(access_inc == 0) { return act; } act = kzalloc(sizeof(struct _tffs_stat), GFP_KERNEL); if(act == NULL) { return act; } act->id = id; act->access = 1; if(lastact == NULL) { *anchor = act; } else { lastact->next = act; } /*--- printk(KERN_INFO"%p: new entry id=%u access=%lu lastact=%p\n", act, act->id, act->access, lastact); ---*/ return act; } /*--------------------------------------------------------------------------------*\ * collect statistic value * id: TFFS-ID * len: written/read len * mode: 0: read 1: write \*--------------------------------------------------------------------------------*/ void tffs3_write_statistic(unsigned int id, unsigned int len, unsigned int mode) { struct _tffs_stat *ptffsstat_per_id; ptffsstat_per_id = tffsstat_process(id, mode == 0 ? &tffsstat_readanchor : &tffsstat_writeanchor, 1); if(ptffsstat_per_id == NULL) { return; } if(ptffsstat_per_id->access == 1) { ptffsstat_per_id->firstaccess_jiffies = jiffies; } ptffsstat_per_id->size += len; ptffsstat_per_id->lastaccess_jiffies = jiffies; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline void fill_sum(struct _tffs_stat *psum, struct _tffs_stat *pact) { psum->access += pact->access; psum->size += pact->size; if((psum->firstaccess_jiffies == 0) || (psum->firstaccess_jiffies > pact->firstaccess_jiffies)) { psum->firstaccess_jiffies = pact->firstaccess_jiffies; } if((psum->lastaccess_jiffies == 0) || (psum->lastaccess_jiffies < pact->lastaccess_jiffies)) { psum->lastaccess_jiffies = pact->lastaccess_jiffies; } } /*--------------------------------------------------------------------------------*\ * timediff in jiffies \*--------------------------------------------------------------------------------*/ static char *human_time(char *buf, long timediff) { if(timediff >= 0) { unsigned long sec, min, hour; unsigned long time = (timediff * 1000) / HZ; time /= 1000; sec = time % 60; time /= 60; min = time % 60; time /= 60; hour = time; sprintf(buf, "%lu:%02lu:%02lu", hour, min, sec); } else { strcpy(buf, "never"); } return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *human_bytes(char *buf, unsigned long long bytes) { static const char *tab[] = {" B", "kiB", "MiB", "GiB", "TiB" }; unsigned int i = 0, rest = 0; while((bytes > (1 << 10)) && i < (size_t)(sizeof(tab) / sizeof(tab[0]) ) - 1) { rest = ((unsigned int)bytes & ((1 << 10) -1)); bytes >>= 10; i++; } if(rest) { sprintf(buf, "%3u.%03u %s", (unsigned) bytes, (rest * 1000) >> 10, tab[i]); } else { sprintf(buf, " %3u %s", (unsigned) bytes, tab[i]); } return buf; } /*--------------------------------------------------------------------------------*\ * id == 0: Summe \*--------------------------------------------------------------------------------*/ static inline int print_entry(char *buf, int buflen, const char *prefix, struct _tffs_stat *pact, int sum) { unsigned long timediff; int len, start_buflen = buflen; unsigned long long bytes_per_sec; char txtbuf[3][32]; if(buflen <= 0) { return 0; } timediff = (jiffies - pact->firstaccess_jiffies) / HZ; if(timediff) { bytes_per_sec = pact->size; do_div(bytes_per_sec, timediff); } else { bytes_per_sec = 0; } if(!sum) { if(pact->id == FLASH_FS_ID_SKIP) { len = snprintf(buf, buflen, "Managing "); } else { len = snprintf(buf, buflen, "ID %4u ", pact->id); } if(len > 0) { buflen -= len, buf += len; } } if(buflen) { len = snprintf(buf, buflen, "%s access %8lu - %s (%s/s) last access before %s\n", prefix, pact->access, human_bytes(txtbuf[0], pact->size), human_bytes(txtbuf[1], bytes_per_sec), human_time(txtbuf[2], jiffies - pact->lastaccess_jiffies)); if(len > 0) { buflen -= len; } } return start_buflen - buflen; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void tffs_statistic(char *buf, unsigned int buflen, unsigned int reset) { struct _tffs_stat *act; struct _tffs_stat readsum, writesum; unsigned int len; memset(&readsum, 0, sizeof(readsum)); memset(&writesum, 0, sizeof(writesum)); act = tffsstat_readanchor; while(act) { len = print_entry(buf, buflen, "read ", act, 0); buflen -= len, buf += len; fill_sum(&readsum, act); if(reset) { act->size = act->access = act->firstaccess_jiffies = act->lastaccess_jiffies = 0; } act = act->next; } len = snprintf(buf, buflen, "-----------------------------------------------------------------------\n"); if(len > 0) { buflen -= len, buf += len; } len = print_entry(buf, buflen, " summary read ", &readsum, 1); buflen -= len, buf += len; len = snprintf(buf, buflen, "\n"); if(len > 0) { buflen -= len, buf += len; } act = tffsstat_writeanchor; while(act) { len = print_entry(buf, buflen, "write", act, 0); buflen -= len, buf += len; fill_sum(&writesum, act); if(reset) { act->size = act->access = act->firstaccess_jiffies = act->lastaccess_jiffies = 0; } act = act->next; } len = snprintf(buf, buflen, "-----------------------------------------------------------------------\n"); if(len > 0) { buflen -= len, buf += len; } len = print_entry(buf, buflen, " summary write", &writesum, 1); buflen -= len, buf += len; len = snprintf(buf, buflen, "\n"); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int tffsstat_show(struct seq_file *seq, void *data __attribute__((unused))) { /*--- printk("%s %p %p\n", __func__, seq->private, data); ---*/ if(seq->private) { seq_printf(seq, "%s", (char *)seq->private); } return 0; } #define TFFSTSTAT_BUF_SIZE (64 << 10) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int tffsstat_open(struct inode *inode __attribute__((unused)), struct file *file) { unsigned int bufsize = TFFSTSTAT_BUF_SIZE; char *buf; buf = kmalloc(bufsize, GFP_KERNEL); if(buf == NULL) { return -ENOMEM; } buf[0] = 0; tffs_statistic(buf, bufsize, 0); return single_open(file, tffsstat_show, buf); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static const struct file_operations tffsstat_fops = { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) .owner = THIS_MODULE, #endif .open = tffsstat_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int tffs_procstat_init(void) { if(tffs2_active()){ pr_info("[%s] TFFS version 2 running, not starting up version 3\n", __func__); return 0; } proc_create("tffs_stat", 0444, NULL, &tffsstat_fops); return 0; } late_initcall(tffs_procstat_init); #endif/*--- #ifdef CONFIG_PROC_FS ---*/