Test case for cache leak in 2.17.2-rc2

From: Sasha Pachev (sasha@mysql.com)
Date: Tue Jan 08 2002 - 21:45:49 EST


The following test program demonstrates incorrect cache shrinking logic in
2.17.2-rc2 with 0 swap. I suspect the same problem will happen if swap is not
zero but is filled up. I have not noticed anything relevant in the changelogs
between my version and 2.4.18-pre2, so I assume the bug is still there. It
would be nice if somebody could verify it.

First the code:

-------------------start---------------------
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

#define BLOCK_SIZE 1024

int done_hog_cache=0;
int done_hog_free=0;
int go_pipe[2];
pid_t child_pid=0;

void stop_hog_cache()
{
  done_hog_cache=1;
}

void stop_hog_free()
{
  done_hog_free=1;
}

void stop_all()
{
  done_hog_free=done_hog_cache=1;
  if (child_pid>0)
    kill(child_pid,SIGHUP);
}

void hog_free(int blocks)
{
  char* buf;
  char go_buf[2];
  
  signal(SIGHUP,stop_hog_free);
  signal(SIGTERM,stop_hog_free);
  printf("Hogging free mem, to stop, do kill -HUP %d in another terminal\n",
         getpid());
  read(go_pipe[0],go_buf,2);
  if (!(buf=(char*)malloc(blocks*BLOCK_SIZE)))
  {
    fprintf(stderr,"malloc() failed\n");
    exit(1);
  }
  memset(buf,0xff,blocks*BLOCK_SIZE);
  while (!done_hog_free)
  {
    sleep(1);
  }
}

void hog_cache(int blocks)
{
  FILE* fp;
  char buf[BLOCK_SIZE];
  const char* hog_file="/tmp/hog-file";
  int i;
  
  memset(buf,0xff,sizeof(buf));
  if (!(fp=fopen(hog_file,"w+")))
  {
    fprintf(stderr,"Could not open crash file\n");
    exit(1);
  }
  signal(SIGHUP,stop_hog_cache);
  signal(SIGTERM,stop_hog_free);
  printf("Hogging disk cache, to stop, do kill -HUP %d in another terminal\n",
         getpid());
  for (i=0;i<blocks;i++)
  {
    fwrite(buf,1,sizeof(buf),fp);
  }
  fflush(fp);
  write(go_pipe[1],"go",2);
  while (!done_hog_cache)
  {
    fseek(fp,0L,SEEK_SET);
    for (i=0;i<blocks;i++)
    {
      fread(buf,1,sizeof(buf),fp);
    }
  }
  
  fclose(fp);
  unlink(hog_file);
  printf("Finished cleanup of disk cache hog\n");
}

int main(int argc,char** argv)
{
  int cache_blocks,free_blocks;
  if (argc<3)
  {
    fprintf(stderr,"Usage: ./crash-kernel cache_blocks free_blocks\n");
    exit(1);
  }
  cache_blocks=atoi(argv[1]);
  free_blocks=atoi(argv[2]);
  if (pipe(go_pipe) == -1)
  {
    fprintf(stderr,"could not create pipe\n");
    exit(1);
  }
  signal(SIGINT,stop_all);
  switch ((child_pid=fork()))
  {
  case 0: hog_cache(cache_blocks); break;
  case -1:
    fprintf(stderr, "cannot fork\n");
    exit(1);
  default: hog_free(free_blocks); break;
  }
}
-----------------end------

To see the problem, ./crash-kernel cache_blocks free_blocks

set cache_blocks to eat up all of your physical RAM + swap, and free_blocks
to some sigficant amount, but less than what is actually available before you
run the test.

As you can see from the source, crash-kernel first tries to bloat the cache
by creating a large file, and then tries to keep it bloated by reading the
file data in a loop. As soon as the file is created, the other fork tries to
allocate and initialize a chunk of memory, and then goes to sleep.

The correct behaviour would be, of course, to shrink the bloated cache and
give memory from it. But what happens on 2.4.17-rc2 is that the memory thread
gets killed instead.

Please CC me on replies as I do not subsribe to the list...

-- 
MySQL Development Team
For technical support contracts, visit https://order.mysql.com/
   __  ___     ___ ____  __ 
  /  |/  /_ __/ __/ __ \/ /   Sasha Pachev <sasha@mysql.com>
 / /|_/ / // /\ \/ /_/ / /__  MySQL AB, http://www.mysql.com/
/_/  /_/\_, /___/\___\_\___/  Provo, Utah, USA
       <___/                  
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue Jan 15 2002 - 21:00:25 EST