/*------------------------------------------------------------------------- * * oscache.c * * * Copyright (c) 2011, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/oscache/oscache.c * *------------------------------------------------------------------------- */ /* { POSIX stuff */ #include /* exit, calloc, free */ #include /* stat, fstat */ #include /* size_t, mincore */ #include /* sysconf, close */ #include /* mmap, mincore */ /* } */ /* { PostgreSQL stuff */ #include "postgres.h" /* general Postgres declarations */ #include "utils/rel.h" /* Relation */ #include "storage/bufmgr.h" #include "catalog/catalog.h" /* relpath */ /* } */ PG_MODULE_MAGIC; void _PG_init(void); float4 oscache(Relation, ForkNumber); /* * Module load callback */ void _PG_init(void) { /* Install hook. */ OSCache_hook = &oscache; } /* * oscache process the os cache inspection for the relation. * It returns the percentage of blocks in OS cache. */ float4 oscache(Relation relation, ForkNumber forkNum) { int segment = 0; char *relationpath; char filename[MAXPGPATH]; int fd; int64 total_block_disk = 0; int64 total_block_mem = 0; /* OS things */ int64 pageSize = sysconf(_SC_PAGESIZE); /* Page size */ register int64 pageIndex; relationpath = relpathperm(relation->rd_node, forkNum); /* * For each segment of the relation */ snprintf(filename, MAXPGPATH, "%s", relationpath); while ((fd = open(filename, O_RDONLY)) != -1) { // for stat file struct stat st; // for mmap file void *pa = (char *)0; // for calloc file unsigned char *vec = (unsigned char *)0; int64 block_disk = 0; int64 block_mem = 0; if (fstat(fd, &st) == -1) { close(fd); elog(ERROR, "Can not stat object file : %s", filename); return 0; } /* * if file ok * then process */ if (st.st_size != 0) { /* number of block in the current file */ block_disk = st.st_size/pageSize; /* TODO We need to split mmap size to be sure (?) to be able to mmap */ pa = mmap(NULL, st.st_size, PROT_NONE, MAP_SHARED, fd, 0); if (pa == MAP_FAILED) { close(fd); elog(ERROR, "Can not mmap object file : %s, errno = %i,%s\nThis error can happen if there is not enought space in memory to do the projection. Please mail cedric@2ndQuadrant.fr with '[oscache] ENOMEM' as subject.", filename, errno, strerror(errno)); return 0; } /* Prepare our vector containing all blocks information */ vec = calloc(1, (st.st_size+pageSize-1)/pageSize); if ((void *)0 == vec) { munmap(pa, st.st_size); close(fd); elog(ERROR, "Can not calloc object file : %s", filename); return 0; } /* Affect vec with mincore */ if (mincore(pa, st.st_size, vec) != 0) { free(vec); munmap(pa, st.st_size); close(fd); elog(ERROR, "mincore(%p, %lld, %p): %s\n", pa, (int64)st.st_size, vec, strerror(errno)); return 0; } /* handle the results */ for (pageIndex = 0; pageIndex <= st.st_size/pageSize; pageIndex++) { // block in memory if (vec[pageIndex] & 1) { block_mem++; } } } elog(DEBUG1, "oscache %s: %lld of %lld block in linux cache", filename, block_mem, block_disk); // free things free(vec); munmap(pa, st.st_size); close(fd); total_block_mem += block_mem; total_block_disk += block_disk; snprintf(filename, MAXPGPATH, "%s.%u", relationpath, segment++); } return (float4)(total_block_mem*100/(total_block_disk+1)); }