diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2006-02-08 06:50:51 -0500 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2006-02-08 06:50:51 -0500 |
commit | 18ec7d5c3f434aed9661ed10a9e1f48cdeb4981d (patch) | |
tree | a7161a4c4b3592052e6772e1c23849de16cac649 /fs/gfs2/ops_address.c | |
parent | 257f9b4e97e9a6cceeb247cead92119a4396d37b (diff) |
[GFS2] Make journaled data files identical to normal files on disk
This is a very large patch, with a few still to be resolved issues
so you might want to check out the previous head of the tree since
this is known to be unstable. Fixes for the various bugs will be
forthcoming shortly.
This patch removes the special data format which has been used
up till now for journaled data files. Directories still retain the
old format so that they will remain on disk compatible with earlier
releases. As a result you can now do the following with journaled
data files:
1) mmap them
2) export them over NFS
3) convert to/from normal files whenever you want to (the zero length
restriction is gone)
In addition the level at which GFS' locking is done has changed for all
files (since they all now use the page cache) such that the locking is
done at the page cache level rather than the level of the fs operations.
This should mean that things like loopback mounts and other things which
touch the page cache directly should now work.
Current known issues:
1. There is a lock mode inversion problem related to the resource
group hold function which needs to be resolved.
2. Any significant amount of I/O causes an oops with an offset of hex 320
(NULL pointer dereference) which appears to be related to a journaled data
buffer appearing on a list where it shouldn't be.
3. Direct I/O writes are disabled for the time being (will reappear later)
4. There is probably a deadlock between the page lock and GFS' locks under
certain combinations of mmap and fs operation I/O.
5. Issue relating to ref counting on internally used inodes causes a hang
on umount (discovered before this patch, and not fixed by it)
6. One part of the directory metadata is different from GFS1 and will need
to be resolved before next release.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
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); |