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