diff options
Diffstat (limited to 'fs/gfs2/ops_vm.c')
-rw-r--r-- | fs/gfs2/ops_vm.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/fs/gfs2/ops_vm.c b/fs/gfs2/ops_vm.c new file mode 100644 index 000000000000..5453d2947ab3 --- /dev/null +++ b/fs/gfs2/ops_vm.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. | ||
3 | * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. | ||
4 | * | ||
5 | * This copyrighted material is made available to anyone wishing to use, | ||
6 | * modify, copy, or redistribute it subject to the terms and conditions | ||
7 | * of the GNU General Public License version 2. | ||
8 | */ | ||
9 | |||
10 | #include <linux/sched.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/spinlock.h> | ||
13 | #include <linux/completion.h> | ||
14 | #include <linux/buffer_head.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/pagemap.h> | ||
17 | #include <linux/gfs2_ondisk.h> | ||
18 | #include <linux/lm_interface.h> | ||
19 | |||
20 | #include "gfs2.h" | ||
21 | #include "incore.h" | ||
22 | #include "bmap.h" | ||
23 | #include "glock.h" | ||
24 | #include "inode.h" | ||
25 | #include "ops_vm.h" | ||
26 | #include "quota.h" | ||
27 | #include "rgrp.h" | ||
28 | #include "trans.h" | ||
29 | #include "util.h" | ||
30 | |||
31 | static void pfault_be_greedy(struct gfs2_inode *ip) | ||
32 | { | ||
33 | unsigned int time; | ||
34 | |||
35 | spin_lock(&ip->i_spin); | ||
36 | time = ip->i_greedy; | ||
37 | ip->i_last_pfault = jiffies; | ||
38 | spin_unlock(&ip->i_spin); | ||
39 | |||
40 | igrab(&ip->i_inode); | ||
41 | if (gfs2_glock_be_greedy(ip->i_gl, time)) | ||
42 | iput(&ip->i_inode); | ||
43 | } | ||
44 | |||
45 | static struct page *gfs2_private_nopage(struct vm_area_struct *area, | ||
46 | unsigned long address, int *type) | ||
47 | { | ||
48 | struct gfs2_inode *ip = GFS2_I(area->vm_file->f_mapping->host); | ||
49 | struct page *result; | ||
50 | |||
51 | set_bit(GIF_PAGED, &ip->i_flags); | ||
52 | |||
53 | result = filemap_nopage(area, address, type); | ||
54 | |||
55 | if (result && result != NOPAGE_OOM) | ||
56 | pfault_be_greedy(ip); | ||
57 | |||
58 | return result; | ||
59 | } | ||
60 | |||
61 | static int alloc_page_backing(struct gfs2_inode *ip, struct page *page) | ||
62 | { | ||
63 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | ||
64 | unsigned long index = page->index; | ||
65 | u64 lblock = index << (PAGE_CACHE_SHIFT - | ||
66 | sdp->sd_sb.sb_bsize_shift); | ||
67 | unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift; | ||
68 | struct gfs2_alloc *al; | ||
69 | unsigned int data_blocks, ind_blocks; | ||
70 | unsigned int x; | ||
71 | int error; | ||
72 | |||
73 | al = gfs2_alloc_get(ip); | ||
74 | |||
75 | error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); | ||
76 | if (error) | ||
77 | goto out; | ||
78 | |||
79 | error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); | ||
80 | if (error) | ||
81 | goto out_gunlock_q; | ||
82 | |||
83 | gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); | ||
84 | |||
85 | al->al_requested = data_blocks + ind_blocks; | ||
86 | |||
87 | error = gfs2_inplace_reserve(ip); | ||
88 | if (error) | ||
89 | goto out_gunlock_q; | ||
90 | |||
91 | error = gfs2_trans_begin(sdp, al->al_rgd->rd_ri.ri_length + | ||
92 | ind_blocks + RES_DINODE + | ||
93 | RES_STATFS + RES_QUOTA, 0); | ||
94 | if (error) | ||
95 | goto out_ipres; | ||
96 | |||
97 | if (gfs2_is_stuffed(ip)) { | ||
98 | error = gfs2_unstuff_dinode(ip, NULL); | ||
99 | if (error) | ||
100 | goto out_trans; | ||
101 | } | ||
102 | |||
103 | for (x = 0; x < blocks; ) { | ||
104 | u64 dblock; | ||
105 | unsigned int extlen; | ||
106 | int new = 1; | ||
107 | |||
108 | error = gfs2_extent_map(&ip->i_inode, lblock, &new, &dblock, &extlen); | ||
109 | if (error) | ||
110 | goto out_trans; | ||
111 | |||
112 | lblock += extlen; | ||
113 | x += extlen; | ||
114 | } | ||
115 | |||
116 | gfs2_assert_warn(sdp, al->al_alloced); | ||
117 | |||
118 | out_trans: | ||
119 | gfs2_trans_end(sdp); | ||
120 | out_ipres: | ||
121 | gfs2_inplace_release(ip); | ||
122 | out_gunlock_q: | ||
123 | gfs2_quota_unlock(ip); | ||
124 | out: | ||
125 | gfs2_alloc_put(ip); | ||
126 | return error; | ||
127 | } | ||
128 | |||
129 | static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area, | ||
130 | unsigned long address, int *type) | ||
131 | { | ||
132 | struct file *file = area->vm_file; | ||
133 | struct gfs2_file *gf = file->private_data; | ||
134 | struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); | ||
135 | struct gfs2_holder i_gh; | ||
136 | struct page *result = NULL; | ||
137 | unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + | ||
138 | area->vm_pgoff; | ||
139 | int alloc_required; | ||
140 | int error; | ||
141 | |||
142 | error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); | ||
143 | if (error) | ||
144 | return NULL; | ||
145 | |||
146 | set_bit(GIF_PAGED, &ip->i_flags); | ||
147 | set_bit(GIF_SW_PAGED, &ip->i_flags); | ||
148 | |||
149 | error = gfs2_write_alloc_required(ip, (u64)index << PAGE_CACHE_SHIFT, | ||
150 | PAGE_CACHE_SIZE, &alloc_required); | ||
151 | if (error) | ||
152 | goto out; | ||
153 | |||
154 | set_bit(GFF_EXLOCK, &gf->f_flags); | ||
155 | result = filemap_nopage(area, address, type); | ||
156 | clear_bit(GFF_EXLOCK, &gf->f_flags); | ||
157 | if (!result || result == NOPAGE_OOM) | ||
158 | goto out; | ||
159 | |||
160 | if (alloc_required) { | ||
161 | error = alloc_page_backing(ip, result); | ||
162 | if (error) { | ||
163 | page_cache_release(result); | ||
164 | result = NULL; | ||
165 | goto out; | ||
166 | } | ||
167 | set_page_dirty(result); | ||
168 | } | ||
169 | |||
170 | pfault_be_greedy(ip); | ||
171 | out: | ||
172 | gfs2_glock_dq_uninit(&i_gh); | ||
173 | |||
174 | return result; | ||
175 | } | ||
176 | |||
177 | struct vm_operations_struct gfs2_vm_ops_private = { | ||
178 | .nopage = gfs2_private_nopage, | ||
179 | }; | ||
180 | |||
181 | struct vm_operations_struct gfs2_vm_ops_sharewrite = { | ||
182 | .nopage = gfs2_sharewrite_nopage, | ||
183 | }; | ||
184 | |||