/* * procdiv64.c: example of using /proc for div64 output, using proc_read() * with the addition of a div64() function (strictly an example) * Copyright (C) 2002-2003 Randy Dunlap */ /********************************************************************* # 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., 675 Mass Ave, Cambridge, MA 02139, USA. *********************************************************************/ #define MODULE #include #include #include #include #include #include #include #include /* * This module can be used for printing debug info in /proc/numbers . * /proc/numbers is currently read-only. * It could be made to accept written data also. * * This procfs module exhibits a well-known syndrome: * For each instance of 'cat /proc/numbers', numbers_read() is * called 2 times, and is incremented 2 times. * This is a side-effect of how apps and/or libc read a file * until read() returns 0, meaning EOF. * * version 0.2: * remove_proc_entry for "numbers64", not "numbers"; */ #define VERSION "0.2" #define MODNAME "div64-example" ////////////////////////////////////////////////////////////////////// /* div64.c */ #include /* * Function copied/adapted/optimized from: * * nemesis.sourceforge.net/browse/lib/static/intmath/ix86/intmath.c.html * * Copyright 1994, University of Cambridge Computer Laboratory * All Rights Reserved. * * TODO: When running on a 64-bit CPU platform, this should no longer be * TODO: necessary. * * Function modified to return both quotient and remainder * instead of one of them at a time. * * returns integer quotient (x/y); * if (rem != NULL) returns remainder part of (x/y) in *rem; * where remainder part of (x/y) == x % y == x MOD y; * * version 0.2: add 2.5 external module support + Makefile; */ s64 divrem64(s64 x, s64 y, s64 *rem) { u64 a = (x < 0) ? -x : x; u64 b = (y < 0) ? -y : y; u64 res = 0, d = 1; if (b > 0) while (b < a) b <<= 1, d <<= 1; do { if (a >= b) a -= b, res += d; b >>= 1; d >>= 1; } while (d); if (rem) *rem = ((x & (1ll<<63)) == 0) ? a : -(s64)a; return (((x ^ y) & (1ll<<63)) == 0) ? res : -(s64)res; } ////////////////////////////////////////////////////////////////////// extern struct proc_dir_entry proc_root; static struct proc_dir_entry *numdir; static int number_current; /* * kstat. unsigned int pgpgin, pgpgout; unsigned int pswpin, pswpout; unsigned int context_swtch; */ /////extern int nr_running, nr_threads, last_pid; extern unsigned long volatile jiffies; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) static int numbers_read (char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; __u64 ctxtsw = kstat.context_swtch; __u64 pages = kstat.pgpgin + kstat.pgpgout + kstat.pswpin + kstat.pswpout; __u64 jifs = jiffies; __u64 t1, t2; len = sprintf(buf, "module_name: %s\n", MODNAME); len += sprintf(buf + len, "version: %s\n", VERSION); /* paging * 1000 / ctxtsw ::= */ t1 = divrem64(pages * 1000, ctxtsw, &t2); len += sprintf(buf + len, "pages, ctxtsw, t1, t2: %lld, %lld, %lld, %lld\n", pages, ctxtsw, t1, t2); /* paging * 1000 / jifs ::= */ t1 = divrem64(pages * 1000, jifs, &t2); len += sprintf(buf + len, "pages, jifs, t1, t2: %lld, %lld, %lld, %lld\n", pages, jifs, t1, t2); /* ctxtsw * 1000 / jifs ::= */ t1 = divrem64(ctxtsw * 1000, jifs, &t2); len += sprintf(buf + len, "ctxtsw, jifs, t1, t2: %lld, %lld, %lld, %lld\n", ctxtsw, jifs, t1, t2); *eof = 1; return len; } #else // for 2.5.40+ #include #include #include #include #include #define KB(x) ((x) << (PAGE_SHIFT - 10)) static int numbers_read (char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; __u64 jifs = jiffies; __u64 t1, t2; /// ctxtsw and pages aren't good for >= 2.5.40, so use different data; /// for 2.5.40+, maybe use /proc/cpuinfo.cpuMHz: ; Y /// or something from /proc/meminfo (si_meminfo(&si)); /// or something else from /proc/stat (not ctxtsw; /// btime := - (ulong) ); struct sysinfo si; __u64 btime = xtime.tv_sec - jifs; si_meminfo (&si); // convert si to KB si.totalram = KB(si.totalram); // MemTotal: si.freeram = KB(si.freeram); // MemFree: si.bufferram = KB(si.bufferram); // Buffers: si.totalhigh = KB(si.totalhigh); // HighTotal: si.freehigh = KB(si.freehigh); // HighFree: //KB(si.totalram - si.totalhigh); // LowTotal: //KB(si.freeram - si.freehigh); // LowFree: len = sprintf(buf, "module_name: %s\n", MODNAME); len += sprintf(buf + len, "version: %s\n", VERSION); // do some div64() examples: // /* cpu_khz / 1000 ::= cpu MHz */ t1 = divrem64(cpu_khz, 1000, &t2); len += sprintf(buf + len, "cpu_khz; cpu_MHz := t1, t2: %lld, %lld.%lld\n", (__u64)cpu_khz, t1, t2); /* btime / xtime.tv_sec ::= */ t1 = divrem64(btime, xtime.tv_sec, &t2); len += sprintf(buf + len, "btime / xtime.tv_sec := t1, t2: %lld, %lld, %lld, %lld\n", btime, (__u64)xtime.tv_sec, t1, t2); /* btime / jifs ::= */ t1 = divrem64(btime, jifs, &t2); len += sprintf(buf + len, "btime / jifs := t1, t2: %lld, %lld, %lld, %lld\n", btime, jifs, t1, t2); *eof = 1; return len; } #endif /* * In this example, the function doesn't write the * passed data. It is only used to reset the counter. */ static int numbers_write (struct file *file, const char *buf, unsigned long count, void *data) { number_current = 0; return count < sizeof (number_current) ? count : sizeof (number_current); } static int __init numbers_init (void) { numdir = create_proc_entry ("numbers64", 0644, &proc_root); if (numdir) { numdir->owner = THIS_MODULE; numdir->uid = 0; numdir->read_proc = numbers_read; numdir->write_proc = numbers_write; } else printk (KERN_INFO MODNAME ": numdir create error\n"); return 0; } static void __exit numbers_exit (void) { if (numdir) remove_proc_entry ("numbers64", NULL); } module_init (numbers_init); module_exit (numbers_exit); #ifdef MODULE_LICENSE MODULE_LICENSE ("GPL"); #endif MODULE_DESCRIPTION ("procfs-div64-example");