blob: 14b380fb060290a77f24a96872e8b6b439707128 [file] [log] [blame]
David Teiglandb3b94fa2006-01-16 16:50:04 +00001/*
2 * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
Steven Whitehouse3a8a9a12006-05-18 15:09:15 -04003 * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
David Teiglandb3b94fa2006-01-16 16:50:04 +00004 *
5 * This copyrighted material is made available to anyone wishing to use,
6 * modify, copy, or redistribute it subject to the terms and conditions
Steven Whitehousee9fc2aa2006-09-01 11:05:15 -04007 * of the GNU General Public License version 2.
David Teiglandb3b94fa2006-01-16 16:50:04 +00008 */
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>
Steven Whitehouse5c676f62006-02-27 17:23:27 -050017#include <linux/gfs2_ondisk.h>
Fabio Massimo Di Nitto7d308592006-09-19 07:56:29 +020018#include <linux/lm_interface.h>
David Teiglandb3b94fa2006-01-16 16:50:04 +000019
20#include "gfs2.h"
Steven Whitehouse5c676f62006-02-27 17:23:27 -050021#include "incore.h"
David Teiglandb3b94fa2006-01-16 16:50:04 +000022#include "bmap.h"
23#include "glock.h"
24#include "inode.h"
25#include "ops_vm.h"
David Teiglandb3b94fa2006-01-16 16:50:04 +000026#include "quota.h"
27#include "rgrp.h"
28#include "trans.h"
Steven Whitehouse5c676f62006-02-27 17:23:27 -050029#include "util.h"
David Teiglandb3b94fa2006-01-16 16:50:04 +000030
David Teiglandb3b94fa2006-01-16 16:50:04 +000031static struct page *gfs2_private_nopage(struct vm_area_struct *area,
32 unsigned long address, int *type)
33{
Steven Whitehousefeaa7bb2006-06-14 15:32:57 -040034 struct gfs2_inode *ip = GFS2_I(area->vm_file->f_mapping->host);
David Teiglandb3b94fa2006-01-16 16:50:04 +000035
36 set_bit(GIF_PAGED, &ip->i_flags);
Steven Whitehousee5dab552007-01-18 17:44:20 +000037 return filemap_nopage(area, address, type);
David Teiglandb3b94fa2006-01-16 16:50:04 +000038}
39
40static int alloc_page_backing(struct gfs2_inode *ip, struct page *page)
41{
Steven Whitehousefeaa7bb2006-06-14 15:32:57 -040042 struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
David Teiglandb3b94fa2006-01-16 16:50:04 +000043 unsigned long index = page->index;
Steven Whitehousecd915492006-09-04 12:49:07 -040044 u64 lblock = index << (PAGE_CACHE_SHIFT -
Steven Whitehouse568f4c92006-02-27 12:00:42 -050045 sdp->sd_sb.sb_bsize_shift);
David Teiglandb3b94fa2006-01-16 16:50:04 +000046 unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift;
47 struct gfs2_alloc *al;
48 unsigned int data_blocks, ind_blocks;
49 unsigned int x;
50 int error;
51
52 al = gfs2_alloc_get(ip);
53
54 error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
55 if (error)
56 goto out;
57
Steven Whitehouse2933f922006-11-01 13:23:29 -050058 error = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid);
David Teiglandb3b94fa2006-01-16 16:50:04 +000059 if (error)
60 goto out_gunlock_q;
61
Steven Whitehousefd88de562006-05-05 16:59:11 -040062 gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks);
David Teiglandb3b94fa2006-01-16 16:50:04 +000063
64 al->al_requested = data_blocks + ind_blocks;
65
66 error = gfs2_inplace_reserve(ip);
67 if (error)
68 goto out_gunlock_q;
69
Steven Whitehousefd88de562006-05-05 16:59:11 -040070 error = gfs2_trans_begin(sdp, al->al_rgd->rd_ri.ri_length +
David Teiglandb3b94fa2006-01-16 16:50:04 +000071 ind_blocks + RES_DINODE +
72 RES_STATFS + RES_QUOTA, 0);
73 if (error)
74 goto out_ipres;
75
76 if (gfs2_is_stuffed(ip)) {
Steven Whitehousef25ef0c2006-07-26 10:51:20 -040077 error = gfs2_unstuff_dinode(ip, NULL);
David Teiglandb3b94fa2006-01-16 16:50:04 +000078 if (error)
79 goto out_trans;
80 }
81
82 for (x = 0; x < blocks; ) {
Steven Whitehousecd915492006-09-04 12:49:07 -040083 u64 dblock;
David Teiglandb3b94fa2006-01-16 16:50:04 +000084 unsigned int extlen;
85 int new = 1;
86
Steven Whitehousefeaa7bb2006-06-14 15:32:57 -040087 error = gfs2_extent_map(&ip->i_inode, lblock, &new, &dblock, &extlen);
David Teiglandb3b94fa2006-01-16 16:50:04 +000088 if (error)
89 goto out_trans;
90
91 lblock += extlen;
92 x += extlen;
93 }
94
95 gfs2_assert_warn(sdp, al->al_alloced);
96
Steven Whitehousea91ea692006-09-04 12:04:26 -040097out_trans:
David Teiglandb3b94fa2006-01-16 16:50:04 +000098 gfs2_trans_end(sdp);
Steven Whitehousea91ea692006-09-04 12:04:26 -040099out_ipres:
David Teiglandb3b94fa2006-01-16 16:50:04 +0000100 gfs2_inplace_release(ip);
Steven Whitehousea91ea692006-09-04 12:04:26 -0400101out_gunlock_q:
David Teiglandb3b94fa2006-01-16 16:50:04 +0000102 gfs2_quota_unlock(ip);
Steven Whitehousea91ea692006-09-04 12:04:26 -0400103out:
David Teiglandb3b94fa2006-01-16 16:50:04 +0000104 gfs2_alloc_put(ip);
David Teiglandb3b94fa2006-01-16 16:50:04 +0000105 return error;
106}
107
108static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area,
109 unsigned long address, int *type)
110{
Steven Whitehouse59a1cc62006-08-04 15:41:22 -0400111 struct file *file = area->vm_file;
112 struct gfs2_file *gf = file->private_data;
113 struct gfs2_inode *ip = GFS2_I(file->f_mapping->host);
David Teiglandb3b94fa2006-01-16 16:50:04 +0000114 struct gfs2_holder i_gh;
115 struct page *result = NULL;
Steven Whitehouse568f4c92006-02-27 12:00:42 -0500116 unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) +
117 area->vm_pgoff;
David Teiglandb3b94fa2006-01-16 16:50:04 +0000118 int alloc_required;
119 int error;
120
David Teiglandb3b94fa2006-01-16 16:50:04 +0000121 error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
122 if (error)
123 return NULL;
124
David Teiglandb3b94fa2006-01-16 16:50:04 +0000125 set_bit(GIF_PAGED, &ip->i_flags);
126 set_bit(GIF_SW_PAGED, &ip->i_flags);
127
Steven Whitehouse59a1cc62006-08-04 15:41:22 -0400128 error = gfs2_write_alloc_required(ip, (u64)index << PAGE_CACHE_SHIFT,
David Teiglandb3b94fa2006-01-16 16:50:04 +0000129 PAGE_CACHE_SIZE, &alloc_required);
130 if (error)
131 goto out;
132
Steven Whitehouse59a1cc62006-08-04 15:41:22 -0400133 set_bit(GFF_EXLOCK, &gf->f_flags);
David Teiglandb3b94fa2006-01-16 16:50:04 +0000134 result = filemap_nopage(area, address, type);
Steven Whitehouse59a1cc62006-08-04 15:41:22 -0400135 clear_bit(GFF_EXLOCK, &gf->f_flags);
David Teiglandb3b94fa2006-01-16 16:50:04 +0000136 if (!result || result == NOPAGE_OOM)
137 goto out;
138
139 if (alloc_required) {
140 error = alloc_page_backing(ip, result);
141 if (error) {
142 page_cache_release(result);
143 result = NULL;
144 goto out;
145 }
146 set_page_dirty(result);
147 }
148
Steven Whitehouse59a1cc62006-08-04 15:41:22 -0400149out:
David Teiglandb3b94fa2006-01-16 16:50:04 +0000150 gfs2_glock_dq_uninit(&i_gh);
151
152 return result;
153}
154
155struct vm_operations_struct gfs2_vm_ops_private = {
156 .nopage = gfs2_private_nopage,
157};
158
159struct vm_operations_struct gfs2_vm_ops_sharewrite = {
160 .nopage = gfs2_sharewrite_nopage,
161};
162