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/slab.h> 11#include <linux/spinlock.h> 12#include <linux/completion.h> 13#include <linux/buffer_head.h> 14#include <linux/mm.h> 15#include <linux/pagemap.h> 16#include <linux/gfs2_ondisk.h> 17#include <linux/lm_interface.h> 18 19#include "gfs2.h" 20#include "incore.h" 21#include "bmap.h" 22#include "glock.h" 23#include "inode.h" 24#include "ops_vm.h" 25#include "quota.h" 26#include "rgrp.h" 27#include "trans.h" 28#include "util.h" 29 30static struct page *gfs2_private_nopage(struct vm_area_struct *area, 31 unsigned long address, int *type) 32{ 33 struct gfs2_inode *ip = GFS2_I(area->vm_file->f_mapping->host); 34 35 set_bit(GIF_PAGED, &ip->i_flags); 36 return filemap_nopage(area, address, type); 37} 38 39static int alloc_page_backing(struct gfs2_inode *ip, struct page *page) 40{ 41 struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); 42 unsigned long index = page->index; 43 u64 lblock = index << (PAGE_CACHE_SHIFT - 44 sdp->sd_sb.sb_bsize_shift); 45 unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift; 46 struct gfs2_alloc *al; 47 unsigned int data_blocks, ind_blocks; 48 unsigned int x; 49 int error; 50 51 al = gfs2_alloc_get(ip); 52 53 error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); 54 if (error) 55 goto out; 56 57 error = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid); 58 if (error) 59 goto out_gunlock_q; 60 61 gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); 62 63 al->al_requested = data_blocks + ind_blocks; 64 65 error = gfs2_inplace_reserve(ip); 66 if (error) 67 goto out_gunlock_q; 68 69 error = gfs2_trans_begin(sdp, al->al_rgd->rd_ri.ri_length + 70 ind_blocks + RES_DINODE + 71 RES_STATFS + RES_QUOTA, 0); 72 if (error) 73 goto out_ipres; 74 75 if (gfs2_is_stuffed(ip)) { 76 error = gfs2_unstuff_dinode(ip, NULL); 77 if (error) 78 goto out_trans; 79 } 80 81 for (x = 0; x < blocks; ) { 82 u64 dblock; 83 unsigned int extlen; 84 int new = 1; 85 86 error = gfs2_extent_map(&ip->i_inode, lblock, &new, &dblock, &extlen); 87 if (error) 88 goto out_trans; 89 90 lblock += extlen; 91 x += extlen; 92 } 93 94 gfs2_assert_warn(sdp, al->al_alloced); 95 96out_trans: 97 gfs2_trans_end(sdp); 98out_ipres: 99 gfs2_inplace_release(ip); 100out_gunlock_q: 101 gfs2_quota_unlock(ip); 102out: 103 gfs2_alloc_put(ip); 104 return error; 105} 106 107static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area, 108 unsigned long address, int *type) 109{ 110 struct file *file = area->vm_file; 111 struct gfs2_file *gf = file->private_data; 112 struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); 113 struct gfs2_holder i_gh; 114 struct page *result = NULL; 115 unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + 116 area->vm_pgoff; 117 int alloc_required; 118 int error; 119 120 error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); 121 if (error) 122 return NULL; 123 124 set_bit(GIF_PAGED, &ip->i_flags); 125 set_bit(GIF_SW_PAGED, &ip->i_flags); 126 127 error = gfs2_write_alloc_required(ip, (u64)index << PAGE_CACHE_SHIFT, 128 PAGE_CACHE_SIZE, &alloc_required); 129 if (error) 130 goto out; 131 132 set_bit(GFF_EXLOCK, &gf->f_flags); 133 result = filemap_nopage(area, address, type); 134 clear_bit(GFF_EXLOCK, &gf->f_flags); 135 if (!result || result == NOPAGE_OOM) 136 goto out; 137 138 if (alloc_required) { 139 error = alloc_page_backing(ip, result); 140 if (error) { 141 page_cache_release(result); 142 result = NULL; 143 goto out; 144 } 145 set_page_dirty(result); 146 } 147 148out: 149 gfs2_glock_dq_uninit(&i_gh); 150 151 return result; 152} 153 154struct vm_operations_struct gfs2_vm_ops_private = { 155 .nopage = gfs2_private_nopage, 156}; 157 158struct vm_operations_struct gfs2_vm_ops_sharewrite = { 159 .nopage = gfs2_sharewrite_nopage, 160}; 161