1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "pageinfo.h"
#include "nocache.h"
static int insert_into_br_list(struct file_pageinfo *pi,
struct byterange **brtail, size_t pos, size_t len);
struct file_pageinfo *fd_get_pageinfo(int fd, struct file_pageinfo *pi)
{
int PAGESIZE;
void *file;
struct byterange *br = NULL; /* tail of our interval list */
struct stat st;
unsigned char *page_vec = NULL;
PAGESIZE = getpagesize();
if(pi->fd != fd) {
DEBUG("fd_get_pageinfo BUG, pi->fd != fd\n");
return NULL;
}
pi->fd = fd;
pi->unmapped = NULL;
if(fstat(fd, &st) == -1 || !S_ISREG(st.st_mode))
return NULL;
pi->size = st.st_size;
pi->nr_pages = (st.st_size + PAGESIZE - 1) / PAGESIZE;
DEBUG("fd_get_pageinfo(fd=%d): st.st_size=%lld, nr_pages=%lld\n",
fd, (long long)st.st_size, (long long)pi->nr_pages);
/* If size is 0, mmap() will fail. We'll keep the fd stored, anyway, to
* make sure the newly written pages will be freed on close(). */
if(pi->size == 0)
return pi;
/* If mmap() fails, we will probably have a file in write-only or
* append-only mode. In this mode the caller will not be able to
* bring in new pages anyway, but we'll record the current size */
file = mmap(NULL, st.st_size, PROT_NONE, MAP_SHARED, fd, 0);
if(file == MAP_FAILED) {
DEBUG("fd_get_pageinfo(fd=%d): mmap failed (don't worry), errno:%d, %s\n",
fd, errno, strerror(errno));
return pi;
}
page_vec = calloc(sizeof(*page_vec), pi->nr_pages);
if(!page_vec) {
DEBUG("calloc failed: size=%zd on fd=%d\n", pi->nr_pages, fd);
goto cleanup;
}
if(mincore(file, pi->size, page_vec) == -1)
goto cleanup;
munmap(file, st.st_size);
file = NULL;
/* compute (byte) intervals that are *not* in the file system
* cache, since we will want to free those on close() */
pi->nr_pages_cached = pi->nr_pages;
size_t i, start = 0;
for(i = 0; i < pi->nr_pages; i++) {
if(!(page_vec[i] & 1))
continue;
if(start < i) {
insert_into_br_list(pi, &br, start * PAGESIZE,
(i - start) * PAGESIZE);
pi->nr_pages_cached -= i - start;
}
start = i + 1;
}
/* Leftover interval: clear until end of file */
if(start < pi->nr_pages) {
insert_into_br_list(pi, &br, start * PAGESIZE,
pi->size - start * PAGESIZE);
pi->nr_pages_cached -= pi->nr_pages - start;
}
free(page_vec);
return pi;
cleanup:
if(file)
munmap(file, st.st_size);
free(page_vec);
return NULL;
}
static int insert_into_br_list(struct file_pageinfo *pi,
struct byterange **brtail, size_t pos, size_t len)
{
struct byterange *tmp;
tmp = malloc(sizeof(*tmp));
if(!tmp)
return 0;
tmp->pos = pos;
tmp->len = len;
tmp->next = NULL;
if(pi->unmapped == NULL) {
pi->unmapped = tmp;
} else if(*brtail != NULL) {
(*brtail)->next = tmp;
}
*brtail = tmp;
return 1;
}
void free_br_list(struct byterange **br)
{
struct byterange *tmp;
while(*br != NULL) {
tmp = *br;
(*br) = tmp->next;
free(tmp);
}
*br = NULL;
}
/* vim:set et sw=4 ts=4: */
|