diff options
Diffstat (limited to 'fs/gfs2/ops_address.c')
-rw-r--r-- | fs/gfs2/ops_address.c | 260 |
1 files changed, 169 insertions, 91 deletions
diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index d611b2ad2e97..b14357e89421 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c | |||
@@ -20,13 +20,13 @@ | |||
20 | #include "bmap.h" | 20 | #include "bmap.h" |
21 | #include "glock.h" | 21 | #include "glock.h" |
22 | #include "inode.h" | 22 | #include "inode.h" |
23 | #include "jdata.h" | ||
24 | #include "log.h" | 23 | #include "log.h" |
25 | #include "meta_io.h" | 24 | #include "meta_io.h" |
26 | #include "ops_address.h" | 25 | #include "ops_address.h" |
27 | #include "page.h" | 26 | #include "page.h" |
28 | #include "quota.h" | 27 | #include "quota.h" |
29 | #include "trans.h" | 28 | #include "trans.h" |
29 | #include "rgrp.h" | ||
30 | 30 | ||
31 | /** | 31 | /** |
32 | * gfs2_get_block - Fills in a buffer head with details about a block | 32 | * gfs2_get_block - Fills in a buffer head with details about a block |
@@ -149,33 +149,55 @@ static int get_blocks_noalloc(struct inode *inode, sector_t lblock, | |||
149 | * | 149 | * |
150 | * Returns: errno | 150 | * Returns: errno |
151 | * | 151 | * |
152 | * Use Linux VFS block_write_full_page() to write one page, | 152 | * Some of this is copied from block_write_full_page() although we still |
153 | * using GFS2's get_block_noalloc to find which blocks to write. | 153 | * call it to do most of the work. |
154 | */ | 154 | */ |
155 | 155 | ||
156 | static int gfs2_writepage(struct page *page, struct writeback_control *wbc) | 156 | static int gfs2_writepage(struct page *page, struct writeback_control *wbc) |
157 | { | 157 | { |
158 | struct inode *inode = page->mapping->host; | ||
158 | struct gfs2_inode *ip = get_v2ip(page->mapping->host); | 159 | struct gfs2_inode *ip = get_v2ip(page->mapping->host); |
159 | struct gfs2_sbd *sdp = ip->i_sbd; | 160 | struct gfs2_sbd *sdp = ip->i_sbd; |
161 | loff_t i_size = i_size_read(inode); | ||
162 | pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; | ||
163 | unsigned offset; | ||
160 | int error; | 164 | int error; |
165 | int done_trans = 0; | ||
161 | 166 | ||
162 | atomic_inc(&sdp->sd_ops_address); | 167 | atomic_inc(&sdp->sd_ops_address); |
163 | |||
164 | if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl))) { | 168 | if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl))) { |
165 | unlock_page(page); | 169 | unlock_page(page); |
166 | return -EIO; | 170 | return -EIO; |
167 | } | 171 | } |
168 | if (get_transaction) { | 172 | if (get_transaction) |
169 | redirty_page_for_writepage(wbc, page); | 173 | goto out_ignore; |
174 | |||
175 | /* Is the page fully outside i_size? (truncate in progress) */ | ||
176 | offset = i_size & (PAGE_CACHE_SIZE-1); | ||
177 | if (page->index >= end_index+1 || !offset) { | ||
178 | page->mapping->a_ops->invalidatepage(page, 0); | ||
170 | unlock_page(page); | 179 | unlock_page(page); |
171 | return 0; | 180 | return 0; /* don't care */ |
172 | } | 181 | } |
173 | 182 | ||
174 | error = block_write_full_page(page, get_block_noalloc, wbc); | 183 | if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) { |
184 | error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); | ||
185 | if (error) | ||
186 | goto out_ignore; | ||
187 | gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize-1); | ||
188 | done_trans = 1; | ||
189 | } | ||
175 | 190 | ||
191 | error = block_write_full_page(page, get_block_noalloc, wbc); | ||
192 | if (done_trans) | ||
193 | gfs2_trans_end(sdp); | ||
176 | gfs2_meta_cache_flush(ip); | 194 | gfs2_meta_cache_flush(ip); |
177 | |||
178 | return error; | 195 | return error; |
196 | |||
197 | out_ignore: | ||
198 | redirty_page_for_writepage(wbc, page); | ||
199 | unlock_page(page); | ||
200 | return 0; | ||
179 | } | 201 | } |
180 | 202 | ||
181 | /** | 203 | /** |
@@ -227,40 +249,9 @@ static int zero_readpage(struct page *page) | |||
227 | } | 249 | } |
228 | 250 | ||
229 | /** | 251 | /** |
230 | * jdata_readpage - readpage that goes through gfs2_jdata_read_mem() | ||
231 | * @ip: | ||
232 | * @page: The page to read | ||
233 | * | ||
234 | * Returns: errno | ||
235 | */ | ||
236 | |||
237 | static int jdata_readpage(struct gfs2_inode *ip, struct page *page) | ||
238 | { | ||
239 | void *kaddr; | ||
240 | int ret; | ||
241 | |||
242 | kaddr = kmap(page); | ||
243 | |||
244 | ret = gfs2_jdata_read_mem(ip, kaddr, | ||
245 | (uint64_t)page->index << PAGE_CACHE_SHIFT, | ||
246 | PAGE_CACHE_SIZE); | ||
247 | if (ret >= 0) { | ||
248 | if (ret < PAGE_CACHE_SIZE) | ||
249 | memset(kaddr + ret, 0, PAGE_CACHE_SIZE - ret); | ||
250 | SetPageUptodate(page); | ||
251 | ret = 0; | ||
252 | } | ||
253 | |||
254 | kunmap(page); | ||
255 | |||
256 | unlock_page(page); | ||
257 | |||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * gfs2_readpage - readpage with locking | 252 | * gfs2_readpage - readpage with locking |
263 | * @file: The file to read a page for | 253 | * @file: The file to read a page for. N.B. This may be NULL if we are |
254 | * reading an internal file. | ||
264 | * @page: The page to read | 255 | * @page: The page to read |
265 | * | 256 | * |
266 | * Returns: errno | 257 | * Returns: errno |
@@ -270,31 +261,35 @@ static int gfs2_readpage(struct file *file, struct page *page) | |||
270 | { | 261 | { |
271 | struct gfs2_inode *ip = get_v2ip(page->mapping->host); | 262 | struct gfs2_inode *ip = get_v2ip(page->mapping->host); |
272 | struct gfs2_sbd *sdp = ip->i_sbd; | 263 | struct gfs2_sbd *sdp = ip->i_sbd; |
264 | struct gfs2_holder gh; | ||
273 | int error; | 265 | int error; |
274 | 266 | ||
275 | atomic_inc(&sdp->sd_ops_address); | 267 | atomic_inc(&sdp->sd_ops_address); |
276 | 268 | ||
277 | if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl))) { | 269 | gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh); |
278 | unlock_page(page); | 270 | error = gfs2_glock_nq_m_atime(1, &gh); |
279 | return -EOPNOTSUPP; | 271 | if (error) |
280 | } | 272 | goto out_unlock; |
281 | 273 | ||
282 | if (!gfs2_is_jdata(ip)) { | 274 | if (gfs2_is_stuffed(ip)) { |
283 | if (gfs2_is_stuffed(ip)) { | 275 | if (!page->index) { |
284 | if (!page->index) { | 276 | error = stuffed_readpage(ip, page); |
285 | error = stuffed_readpage(ip, page); | 277 | unlock_page(page); |
286 | unlock_page(page); | ||
287 | } else | ||
288 | error = zero_readpage(page); | ||
289 | } else | 278 | } else |
290 | error = mpage_readpage(page, gfs2_get_block); | 279 | error = zero_readpage(page); |
291 | } else | 280 | } else |
292 | error = jdata_readpage(ip, page); | 281 | error = mpage_readpage(page, gfs2_get_block); |
293 | 282 | ||
294 | if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) | 283 | if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) |
295 | error = -EIO; | 284 | error = -EIO; |
296 | 285 | ||
286 | gfs2_glock_dq_m(1, &gh); | ||
287 | gfs2_holder_uninit(&gh); | ||
288 | out: | ||
297 | return error; | 289 | return error; |
290 | out_unlock: | ||
291 | unlock_page(page); | ||
292 | goto out; | ||
298 | } | 293 | } |
299 | 294 | ||
300 | /** | 295 | /** |
@@ -312,28 +307,82 @@ static int gfs2_prepare_write(struct file *file, struct page *page, | |||
312 | { | 307 | { |
313 | struct gfs2_inode *ip = get_v2ip(page->mapping->host); | 308 | struct gfs2_inode *ip = get_v2ip(page->mapping->host); |
314 | struct gfs2_sbd *sdp = ip->i_sbd; | 309 | struct gfs2_sbd *sdp = ip->i_sbd; |
310 | unsigned int data_blocks, ind_blocks, rblocks; | ||
311 | int alloc_required; | ||
315 | int error = 0; | 312 | int error = 0; |
313 | loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + from; | ||
314 | loff_t end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; | ||
315 | struct gfs2_alloc *al; | ||
316 | 316 | ||
317 | atomic_inc(&sdp->sd_ops_address); | 317 | atomic_inc(&sdp->sd_ops_address); |
318 | 318 | ||
319 | if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl))) | 319 | gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ATIME, &ip->i_gh); |
320 | return -EOPNOTSUPP; | 320 | error = gfs2_glock_nq_m_atime(1, &ip->i_gh); |
321 | if (error) | ||
322 | goto out_uninit; | ||
321 | 323 | ||
322 | if (gfs2_is_stuffed(ip)) { | 324 | gfs2_write_calc_reserv(ip, to - from, &data_blocks, &ind_blocks); |
323 | uint64_t file_size; | 325 | |
324 | file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to; | 326 | error = gfs2_write_alloc_required(ip, pos, from - to, &alloc_required); |
327 | if (error) | ||
328 | goto out_unlock; | ||
325 | 329 | ||
326 | if (file_size > sdp->sd_sb.sb_bsize - | 330 | |
327 | sizeof(struct gfs2_dinode)) { | 331 | if (alloc_required) { |
328 | error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page, | 332 | al = gfs2_alloc_get(ip); |
329 | page); | 333 | |
330 | if (!error) | 334 | error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); |
331 | error = block_prepare_write(page, from, to, | 335 | if (error) |
332 | gfs2_get_block); | 336 | goto out_alloc_put; |
333 | } else if (!PageUptodate(page)) | 337 | |
338 | error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); | ||
339 | if (error) | ||
340 | goto out_qunlock; | ||
341 | |||
342 | al->al_requested = data_blocks + ind_blocks; | ||
343 | error = gfs2_inplace_reserve(ip); | ||
344 | if (error) | ||
345 | goto out_qunlock; | ||
346 | } | ||
347 | |||
348 | rblocks = RES_DINODE + ind_blocks; | ||
349 | if (gfs2_is_jdata(ip)) | ||
350 | rblocks += data_blocks ? data_blocks : 1; | ||
351 | if (ind_blocks || data_blocks) | ||
352 | rblocks += RES_STATFS + RES_QUOTA; | ||
353 | |||
354 | error = gfs2_trans_begin(sdp, rblocks, 0); | ||
355 | if (error) | ||
356 | goto out; | ||
357 | |||
358 | if (gfs2_is_stuffed(ip)) { | ||
359 | if (end > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { | ||
360 | error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page, page); | ||
361 | if (error) | ||
362 | goto out; | ||
363 | } else if (!PageUptodate(page)) { | ||
334 | error = stuffed_readpage(ip, page); | 364 | error = stuffed_readpage(ip, page); |
335 | } else | 365 | goto out; |
336 | error = block_prepare_write(page, from, to, gfs2_get_block); | 366 | } |
367 | } | ||
368 | |||
369 | error = block_prepare_write(page, from, to, gfs2_get_block); | ||
370 | |||
371 | out: | ||
372 | if (error) { | ||
373 | gfs2_trans_end(sdp); | ||
374 | if (alloc_required) { | ||
375 | gfs2_inplace_release(ip); | ||
376 | out_qunlock: | ||
377 | gfs2_quota_unlock(ip); | ||
378 | out_alloc_put: | ||
379 | gfs2_alloc_put(ip); | ||
380 | } | ||
381 | out_unlock: | ||
382 | gfs2_glock_dq_m(1, &ip->i_gh); | ||
383 | out_uninit: | ||
384 | gfs2_holder_uninit(&ip->i_gh); | ||
385 | } | ||
337 | 386 | ||
338 | return error; | 387 | return error; |
339 | } | 388 | } |
@@ -354,48 +403,73 @@ static int gfs2_commit_write(struct file *file, struct page *page, | |||
354 | struct inode *inode = page->mapping->host; | 403 | struct inode *inode = page->mapping->host; |
355 | struct gfs2_inode *ip = get_v2ip(inode); | 404 | struct gfs2_inode *ip = get_v2ip(inode); |
356 | struct gfs2_sbd *sdp = ip->i_sbd; | 405 | struct gfs2_sbd *sdp = ip->i_sbd; |
357 | int error; | 406 | int error = -EOPNOTSUPP; |
407 | struct buffer_head *dibh; | ||
408 | struct gfs2_alloc *al = &ip->i_alloc;; | ||
358 | 409 | ||
359 | atomic_inc(&sdp->sd_ops_address); | 410 | atomic_inc(&sdp->sd_ops_address); |
360 | 411 | ||
412 | |||
413 | if (gfs2_assert_withdraw(sdp, gfs2_glock_is_locked_by_me(ip->i_gl))) | ||
414 | goto fail_nounlock; | ||
415 | |||
416 | error = gfs2_meta_inode_buffer(ip, &dibh); | ||
417 | if (error) | ||
418 | goto fail_endtrans; | ||
419 | |||
420 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | ||
421 | |||
361 | if (gfs2_is_stuffed(ip)) { | 422 | if (gfs2_is_stuffed(ip)) { |
362 | struct buffer_head *dibh; | ||
363 | uint64_t file_size; | 423 | uint64_t file_size; |
364 | void *kaddr; | 424 | void *kaddr; |
365 | 425 | ||
366 | file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to; | 426 | file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to; |
367 | 427 | ||
368 | error = gfs2_meta_inode_buffer(ip, &dibh); | 428 | kaddr = kmap_atomic(page, KM_USER0); |
369 | if (error) | ||
370 | goto fail; | ||
371 | |||
372 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | ||
373 | |||
374 | kaddr = kmap(page); | ||
375 | memcpy(dibh->b_data + sizeof(struct gfs2_dinode) + from, | 429 | memcpy(dibh->b_data + sizeof(struct gfs2_dinode) + from, |
376 | (char *)kaddr + from, | 430 | (char *)kaddr + from, to - from); |
377 | to - from); | 431 | kunmap_atomic(page, KM_USER0); |
378 | kunmap(page); | ||
379 | |||
380 | brelse(dibh); | ||
381 | 432 | ||
382 | SetPageUptodate(page); | 433 | SetPageUptodate(page); |
383 | 434 | ||
384 | if (inode->i_size < file_size) | 435 | if (inode->i_size < file_size) |
385 | i_size_write(inode, file_size); | 436 | i_size_write(inode, file_size); |
386 | } else { | 437 | } else { |
387 | if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED) | 438 | if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) |
388 | gfs2_page_add_databufs(ip, page, from, to); | 439 | gfs2_page_add_databufs(ip, page, from, to); |
389 | error = generic_commit_write(file, page, from, to); | 440 | error = generic_commit_write(file, page, from, to); |
390 | if (error) | 441 | if (error) |
391 | goto fail; | 442 | goto fail; |
392 | } | 443 | } |
393 | 444 | ||
445 | if (ip->i_di.di_size < inode->i_size) | ||
446 | ip->i_di.di_size = inode->i_size; | ||
447 | |||
448 | gfs2_dinode_out(&ip->i_di, dibh->b_data); | ||
449 | brelse(dibh); | ||
450 | gfs2_trans_end(sdp); | ||
451 | if (al->al_requested) { | ||
452 | gfs2_inplace_release(ip); | ||
453 | gfs2_quota_unlock(ip); | ||
454 | gfs2_alloc_put(ip); | ||
455 | } | ||
456 | gfs2_glock_dq_m(1, &ip->i_gh); | ||
457 | gfs2_holder_uninit(&ip->i_gh); | ||
394 | return 0; | 458 | return 0; |
395 | 459 | ||
396 | fail: | 460 | fail: |
461 | brelse(dibh); | ||
462 | fail_endtrans: | ||
463 | gfs2_trans_end(sdp); | ||
464 | if (al->al_requested) { | ||
465 | gfs2_inplace_release(ip); | ||
466 | gfs2_quota_unlock(ip); | ||
467 | gfs2_alloc_put(ip); | ||
468 | } | ||
469 | gfs2_glock_dq_m(1, &ip->i_gh); | ||
470 | gfs2_holder_uninit(&ip->i_gh); | ||
471 | fail_nounlock: | ||
397 | ClearPageUptodate(page); | 472 | ClearPageUptodate(page); |
398 | |||
399 | return error; | 473 | return error; |
400 | } | 474 | } |
401 | 475 | ||
@@ -492,12 +566,16 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *io | |||
492 | 566 | ||
493 | atomic_inc(&sdp->sd_ops_address); | 567 | atomic_inc(&sdp->sd_ops_address); |
494 | 568 | ||
495 | if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl)) || | 569 | if (gfs2_is_jdata(ip)) |
496 | gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip))) | ||
497 | return -EINVAL; | 570 | return -EINVAL; |
498 | 571 | ||
499 | if (rw == WRITE && !get_transaction) | 572 | if (rw == WRITE) { |
500 | gb = get_blocks_noalloc; | 573 | return -EOPNOTSUPP; /* for now */ |
574 | } else { | ||
575 | if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl)) || | ||
576 | gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip))) | ||
577 | return -EINVAL; | ||
578 | } | ||
501 | 579 | ||
502 | return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, | 580 | return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, |
503 | offset, nr_segs, gb, NULL); | 581 | offset, nr_segs, gb, NULL); |