/* mmlib.c Interface library for mergemod & mergemem This file is part of mergemem by Philipp Richter & Philipp Reisner Created 1999-01-15 by Ulrich Neumerkel mergemem 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, or (at your option) any later version. mergemem 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 mergemem; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This is a rather inefficient reference implementation based on mergemod version 0.12. To become more efficient, more flexible ioctl calls are needed. */ #include #include #include #include #include #include #include #include long int random(void); /* shoud be prototyped by stdlib.h */ #include "mmlib.h" #include "../mergemem/mergemem.h" #include "kern.h" /* should be part of mergemem.h */ /* end of includes */ /* #define H fprintf(stderr,"%s:%d Here\n",__FILE__,__LINE__) */ /* test function */ static void randomizearea(void *p, size_t len) { const int randomization_required = 1 == 2; char * sp = (char *) p; size_t i; if (randomization_required) for( i = 0 ; i < len; i++ ) sp[i] = random(); } int /* should be static, after kern.c has been removed */ m_fd = -1; static unsigned long (*m_hash_default)(const void* addr, size_t size) = NULL; /* semi exported */ void dosimpleioctl(void) { int modver; ioctl(m_fd, MERGEMEM_CHECK_VER, &modver); } static ssize_t do_initialization(void) { int modver; if ( (m_fd = open("/dev/mergemem", O_RDONLY)) == -1 ) { struct stat buf; if ( stat("/dev/mergemem", &buf) == -1 ) errno = ENOPKG; /* package not installed */ else errno = EPERM; /* exists, but cannot read */ return -1; } if ( ioctl(m_fd, MERGEMEM_CHECK_VER, &modver) != 0 || modver != MOD_VERSION ) { m_fd = -1; errno = EPROTO; /* version mismatch or similar */ return -1; } m_hash_default = m_hash_addrothalf; return 0; } static ssize_t ensure_initialization(void) { if (m_fd != -1) return 0; return do_initialization(); } ssize_t m_getpageinfos( const pid_t pid, const void * const start, const void * const end, MPAGEINFO * const pageinfos, const size_t nel, const void * * const contp ) { size_t i = 0; struct ioctl_getpageinfos args; int retval; if ( ensure_initialization() < 0) return -1; if ( start > end || nel == 0 ) { errno = EINVAL; return -1; } args.pid = pid; args.start = start; args.end = end; while ( i < nel ) { args.pageinfos = pageinfos+i; args.nel = nel-i; retval = IOCTL_GETPAGEINFOS( & args ); if ( retval != 0 ) { errno = args.errno; return -1; } i += args.wel; if ( args.cont == NULL ) break; args.start = args.cont; } *contp = args.cont; return i; } ssize_t m_hashpages( const pid_t pid, MPAGEINFO * const pageinfos, const size_t nel, unsigned long (*hashfunction)(const void* addr, size_t size) ) { ssize_t hashedpages, i; struct ioctl_hashpages args; #define MAXPAGES 11 unsigned char pagearray [PAGE_SIZE*MAXPAGES]; randomizearea(pagearray, sizeof(pagearray)); if ( ensure_initialization() < 0) return -1; if ( nel == 0 ) { errno = EINVAL; return -1; } if ( hashfunction == NULL ) hashfunction = m_hash_default; /* current default */ hashedpages = 0; args.pid = pid; if ( hashfunction == m_hash_addrothalf ) args.khashmethod = KHADDROTHALF; else if ( hashfunction == m_hash_const ) args.khashmethod = KHCONST; else { args.khashmethod = KHEXTERNAL; args.pbufferp = pagearray; args.size = sizeof(pagearray); } i = 0; while (i < nel) { int retval; args.pageinfos = pageinfos+i; if ( args.khashmethod == KHEXTERNAL && nel - i >= MAXPAGES ) args.nel = MAXPAGES; else args.nel = nel - i; retval = IOCTL_HASHPAGES( & args); if (retval != 0) { errno = args.errno; return -1; } if (args.khashmethod == KHEXTERNAL) { size_t j; const unsigned char * pagestart = args.pbufferp; /* the kernel might have chosen a better location */ size_t pagesize = args.pagesize; for (j = 0; j < args.wel; j++) { if ( pageinfos[j+i].count != 0) { pageinfos[j+i].hash ^= (*hashfunction)(pagestart,pagesize); pagestart += pagesize; } } } i += args.wel; hashedpages += args.succwel; } if ( args.khashmethod == KHEXTERNAL && args.pbufferp != pagearray ) { int retval; /* the kernel changed pbufferp to make it point to its own area, now remove this map from our process */ args.nel = 0; retval = IOCTL_HASHPAGES( & args); if (retval != 0) { errno = args.errno; return -1; } } randomizearea(pagearray, sizeof(pagearray)); return hashedpages; } ssize_t m_merge( MEQUALPAIR * const pagepairs, const size_t nel ) { ssize_t mergedpages, i; if ( ensure_initialization() < 0) return -1; mergedpages = 0; i = 0; while ( i < nel ) { struct ioctl_mergepairs args; int retval; args.pagepairs = pagepairs+i; args.nel = nel - i; retval = IOCTL_MERGEPAIRS( &args ); if (retval != 0) { errno = args.errno; return -1; } i += args.wel; mergedpages += args.mergedpages; } return mergedpages; } ssize_t m_set_hash_default( unsigned long (* const hashfunction)(const void* addr, size_t size) ) { if ( ensure_initialization() < 0 ) return -1; if ( hashfunction == NULL ) { errno = EINVAL; return -1; } m_hash_default = hashfunction; return 0; } /* Predefined hashfunctions */ #define BITS_OF_UNSIGNEDLONG (sizeof(unsigned long)*8) unsigned long m_hash_addrothalf(const void* addr, const size_t size) { unsigned long * page = (unsigned long *) addr; unsigned long * end = (unsigned long *) ( (char *) addr + size/2 ); unsigned long chksum = 0; while (page < end) { chksum = (chksum << 3) + (chksum >> (BITS_OF_UNSIGNEDLONG-3)) + *page; page++; } return chksum; } unsigned long m_libhash_addrothalf(const void* addr, size_t size) { return m_hash_addrothalf(addr, size); } unsigned long m_hash_const(const void* addr, size_t size) { return 27; } unsigned long m_libhash_const(const void* addr, size_t size) { return m_hash_const(addr, size); }