23161be5db1f9400f8b64d9c56596f4795343f95
[linux-2.6.git] / fs / gfs2 / ops_vm.c
1 /*
2  * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
3  * Copyright (C) 2004-2005 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 v.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
19 #include "gfs2.h"
20 #include "lm_interface.h"
21 #include "incore.h"
22 #include "bmap.h"
23 #include "glock.h"
24 #include "inode.h"
25 #include "ops_vm.h"
26 #include "page.h"
27 #include "quota.h"
28 #include "rgrp.h"
29 #include "trans.h"
30 #include "util.h"
31
32 static void pfault_be_greedy(struct gfs2_inode *ip)
33 {
34         unsigned int time;
35
36         spin_lock(&ip->i_spin);
37         time = ip->i_greedy;
38         ip->i_last_pfault = jiffies;
39         spin_unlock(&ip->i_spin);
40
41         gfs2_inode_hold(ip);
42         if (gfs2_glock_be_greedy(ip->i_gl, time))
43                 gfs2_inode_put(ip);
44 }
45
46 static struct page *gfs2_private_nopage(struct vm_area_struct *area,
47                                         unsigned long address, int *type)
48 {
49         struct gfs2_inode *ip = area->vm_file->f_mapping->host->u.generic_ip;
50         struct gfs2_holder i_gh;
51         struct page *result;
52         int error;
53
54         error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
55         if (error)
56                 return NULL;
57
58         set_bit(GIF_PAGED, &ip->i_flags);
59
60         result = filemap_nopage(area, address, type);
61
62         if (result && result != NOPAGE_OOM)
63                 pfault_be_greedy(ip);
64
65         gfs2_glock_dq_uninit(&i_gh);
66
67         return result;
68 }
69
70 static int alloc_page_backing(struct gfs2_inode *ip, struct page *page)
71 {
72         struct gfs2_sbd *sdp = ip->i_sbd;
73         unsigned long index = page->index;
74         uint64_t lblock = index << (PAGE_CACHE_SHIFT -
75                                     sdp->sd_sb.sb_bsize_shift);
76         unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift;
77         struct gfs2_alloc *al;
78         unsigned int data_blocks, ind_blocks;
79         unsigned int x;
80         int error;
81
82         al = gfs2_alloc_get(ip);
83
84         error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
85         if (error)
86                 goto out;
87
88         error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
89         if (error)
90                 goto out_gunlock_q;
91
92         gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks);
93
94         al->al_requested = data_blocks + ind_blocks;
95
96         error = gfs2_inplace_reserve(ip);
97         if (error)
98                 goto out_gunlock_q;
99
100         error = gfs2_trans_begin(sdp, al->al_rgd->rd_ri.ri_length +
101                                  ind_blocks + RES_DINODE +
102                                  RES_STATFS + RES_QUOTA, 0);
103         if (error)
104                 goto out_ipres;
105
106         if (gfs2_is_stuffed(ip)) {
107                 error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page, NULL);
108                 if (error)
109                         goto out_trans;
110         }
111
112         for (x = 0; x < blocks; ) {
113                 uint64_t dblock;
114                 unsigned int extlen;
115                 int new = 1;
116
117                 error = gfs2_extent_map(ip->i_vnode, lblock, &new, &dblock, &extlen);
118                 if (error)
119                         goto out_trans;
120
121                 lblock += extlen;
122                 x += extlen;
123         }
124
125         gfs2_assert_warn(sdp, al->al_alloced);
126
127  out_trans:
128         gfs2_trans_end(sdp);
129
130  out_ipres:
131         gfs2_inplace_release(ip);
132
133  out_gunlock_q:
134         gfs2_quota_unlock(ip);
135
136  out:
137         gfs2_alloc_put(ip);
138
139         return error;
140 }
141
142 static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area,
143                                            unsigned long address, int *type)
144 {
145         struct gfs2_inode *ip = area->vm_file->f_mapping->host->u.generic_ip;
146         struct gfs2_holder i_gh;
147         struct page *result = NULL;
148         unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) +
149                               area->vm_pgoff;
150         int alloc_required;
151         int error;
152
153         error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
154         if (error)
155                 return NULL;
156
157         set_bit(GIF_PAGED, &ip->i_flags);
158         set_bit(GIF_SW_PAGED, &ip->i_flags);
159
160         error = gfs2_write_alloc_required(ip,
161                                           (uint64_t)index << PAGE_CACHE_SHIFT,
162                                           PAGE_CACHE_SIZE, &alloc_required);
163         if (error)
164                 goto out;
165
166         result = filemap_nopage(area, address, type);
167         if (!result || result == NOPAGE_OOM)
168                 goto out;
169
170         if (alloc_required) {
171                 error = alloc_page_backing(ip, result);
172                 if (error) {
173                         page_cache_release(result);
174                         result = NULL;
175                         goto out;
176                 }
177                 set_page_dirty(result);
178         }
179
180         pfault_be_greedy(ip);
181
182  out:
183         gfs2_glock_dq_uninit(&i_gh);
184
185         return result;
186 }
187
188 struct vm_operations_struct gfs2_vm_ops_private = {
189         .nopage = gfs2_private_nopage,
190 };
191
192 struct vm_operations_struct gfs2_vm_ops_sharewrite = {
193         .nopage = gfs2_sharewrite_nopage,
194 };
195