summaryrefslogtreecommitdiffstats
path: root/include/linux/pagemap.h
blob: 9af62e4a2f9b73e8f8cfda759e8d7a0739e07674 (plain)
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
133
134
135
136
#ifndef _LINUX_PAGEMAP_H
#define _LINUX_PAGEMAP_H

#include <asm/system.h>

/*
 * Page-mapping primitive inline functions
 *
 * Copyright 1995 Linus Torvalds
 */

#include <linux/mm.h>
#include <linux/fs.h>

static inline unsigned long page_address(struct page * page)
{
	return PAGE_OFFSET + PAGE_SIZE * page->map_nr;
}

#define PAGE_HASH_BITS 11
#define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)

#define PAGE_AGE_VALUE 16

extern unsigned long page_cache_size; /* # of pages currently in the hash table */
extern struct page * page_hash_table[PAGE_HASH_SIZE];

/*
 * We use a power-of-two hash table to avoid a modulus,
 * and get a reasonable hash by knowing roughly how the
 * inode pointer and offsets are distributed (ie, we
 * roughly know which bits are "significant")
 */
static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset)
{
#define i (((unsigned long) inode)/(sizeof(struct inode) & ~ (sizeof(struct inode) - 1)))
#define o (offset >> PAGE_SHIFT)
#define s(x) ((x)+((x)>>PAGE_HASH_BITS))
	return s(i+o) & (PAGE_HASH_SIZE-1);
#undef i
#undef o
#undef s
}

#define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset))

static inline struct page * __find_page(struct inode * inode, unsigned long offset, struct page *page)
{
	goto inside;
	for (;;) {
		page = page->next_hash;
inside:
		if (!page)
			goto not_found;
		if (page->inode != inode)
			continue;
		if (page->offset == offset)
			break;
	}
	/* Found the page. */
	atomic_inc(&page->count);
	set_bit(PG_referenced, &page->flags);
not_found:
	return page;
}

static inline struct page *find_page(struct inode * inode, unsigned long offset)
{
	return __find_page(inode, offset, *page_hash(inode, offset));
}

static inline void remove_page_from_hash_queue(struct page * page)
{
	if(page->pprev_hash) {
		if(page->next_hash)
			page->next_hash->pprev_hash = page->pprev_hash;
		*page->pprev_hash = page->next_hash;
		page->pprev_hash = NULL;
	}
	page_cache_size--;
}

static inline void __add_page_to_hash_queue(struct page * page, struct page **p)
{
	page_cache_size++;
	set_bit(PG_referenced, &page->flags);
	page->age = PAGE_AGE_VALUE;
	if((page->next_hash = *p) != NULL)
		(*p)->pprev_hash = &page->next_hash;
	*p = page;
	page->pprev_hash = p;
}

static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset)
{
	__add_page_to_hash_queue(page, page_hash(inode,offset));
}

static inline void remove_page_from_inode_queue(struct page * page)
{
	struct inode * inode = page->inode;

	page->inode = NULL;
	inode->i_nrpages--;
	if (inode->i_pages == page)
		inode->i_pages = page->next;
	if (page->next)
		page->next->prev = page->prev;
	if (page->prev)
		page->prev->next = page->next;
	page->next = NULL;
	page->prev = NULL;
}

static inline void add_page_to_inode_queue(struct inode * inode, struct page * page)
{
	struct page **p = &inode->i_pages;

	inode->i_nrpages++;
	page->inode = inode;
	page->prev = NULL;
	if ((page->next = *p) != NULL)
		page->next->prev = page;
	*p = page;
}

extern void __wait_on_page(struct page *);
static inline void wait_on_page(struct page * page)
{
	if (PageLocked(page))
		__wait_on_page(page);
}

extern void update_vm_cache(struct inode *, unsigned long, const char *, int);

#endif