MM, TLB and speeding up suspend to disk for 2.4

Stefan Voelkel
2005-02-07T16:30:16Z

[-- Attachment #1: Type: text/plain, Size: 1507 bytes --]

Hello,

since neither Understanding the Linux VMM nor the kernelnewbies ML could
help me, I am takin the problem here:

I have a IBM Thinkpad 600X and use the suspend to disk feature (called
hibernation) when carring the notebook around, but writing ~600MB to the
disk takes a while.

After finding out, that the bios does only write used pages to disk, I
wrote a small userspace programm called mhog (see attached mhog.c), that
allocates roughly as many pages as used by the page cache. mhog retrieves
the number of pages to allocate from /proc/meminfo.

This works find, but does take a while, so I wrote a kernel module
(see attached nocache.c) to do the same. During module initilization I
simply loop around a get_free_page(GFP_NIO) to get all pages that can be
discarded, and upon the first failure I free all reserved pages again and
return -ENOMEM.

The kernel module does not speed up hibernation (pages are freed just
fine), but something must be diffrent between userspace and kernelspace
page allocation. I suspect the TLB, but did not find a hint on which of the
many tlb flushing calls to use.

So what am I missing?

thanks in advance
Stefan

--
Stefan Volkel stefan.voelkel@millenux.com
Millenux GmbH mobile: +49.170.79177.17
Lilienthalstrasse 2 phone: +49.711.88770.300
70825 Stuttgart-Korntal fax: +49.711.88770.349
-= linux without limits -=- http://linux.zSeries.org/ =-

[-- Attachment #2: nocache.c --]
[-- Type: application/octet-stream, Size: 1530 bytes --]

/*
nocache.c (c) 2005 Stefan.Voelkel@millenux.com

licensed under the GPL v2.
*/

#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <asm/pgalloc.h>

struct nc_pages;

#define NC_PAGES ((PAGE_SIZE - sizeof(struct nc_pages *) - sizeof(unsigned int))/ sizeof(unsigned long))

struct nc_pages {
struct nc_pages *next;
unsigned int order;
unsigned long pages[NC_PAGES];
};

static struct nc_pages *pa = 0;

static inline void nc_alloc_pages(void) {
struct nc_pages *list;
unsigned long page;

unsigned long index = 0;
unsigned int order = MAX_ORDER - 1;

while (order < MAX_ORDER) {
page = __get_free_pages(GFP_NOIO, order);
if (!page) {
--order;
continue;
}

if (!pa || index >= NC_PAGES || order != pa->order) {

list = (struct nc_pages *) get_zeroed_page(GFP_NOIO);

if (!list) {
free_pages(page, order);
break;
}

list->next = pa;
pa = list;
pa->order = order;
index = 0;
}

pa->pages[index] = page;
++index;
}
}

static inline void nc_free_pages(void) {
struct nc_pages *save;
unsigned long index;

while(pa) {
for (index = 0; index < NC_PAGES && pa->pages[index]; ++index)
free_pages(pa->pages[index], pa->order);

save = pa;
pa = pa->next;

free_page((unsigned long) save);
}
}

int init_module(void)
{
nc_alloc_pages();
nc_free_pages();

flush_tlb();

return -ENOMEM;
}

void cleanup_module(void)
{
}

MODULE_AUTHOR("Stefan Völkel <stefan.voelkel@millenux.com>");
MODULE_LICENSE("GPL");

EXPORT_NO_SYMBOLS;

[-- Attachment #3: mhog.c --]
[-- Type: application/octet-stream, Size: 1346 bytes --]

/*
* mhog.c (c) Stefan Völkel <stefan.voelkel@millenux.com>
*
* LICENSED UNDER THE GPL v2
*
*/

#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define PAGE_BIT 12
#define PAGE_SIZE (1 << PAGE_BIT)

int main(void)
{
FILE *f;
long buffers, cached, free, memory;
int ret;

/* get free memory, buffers and cached from /proc to calculate the
* number of pages we need to malloc() in order to invalidate the
* cache */
f = fopen("/proc/meminfo", "r");
if (!f) {
printf("E: fopen() %d:'%s'\n", errno, strerror(errno));
exit(errno);
}

ret = fscanf(f, "%*s %*s %*s %*s %*s %*s\n");
if (ret) {
printf("E: sscanf() (1) %d:'%s'\n", errno, strerror(errno));
exit(errno);
}

ret = fscanf(f, "%*s %*ld %*ld %ld %*ld %ld %ld\n", &free,
&buffers, &cached);
if (3 != ret) {
printf("E: %d = sscanf() (2) %d:'%s'\n", ret, errno,
strerror(errno));
exit(errno);
}

fclose(f);

memory = buffers + cached + free;
memory >>= PAGE_BIT;

printf("Need to flush %ld pages\n", memory);

/* we need to write at least one byte to each page to take it away from
* the system */
while (memory--) {
char *c = malloc(PAGE_SIZE);
*c = '0';

printf("%d left \r", memory);
}

return 0;
}