[pnfs] [PATCH 18/21] pnfsblock: write_begin
Fred Isaman
iisaman at citi.umich.edu
Thu Apr 10 10:07:34 EDT 2008
Implements bl_write_begin and bl_do_flush, allowing block driver to read
in page "around" the data that is about to be copied to the page.
Signed-off-by: Fred Isaman <iisaman at citi.umich.edu>
Signed-off-by: Benny Halevy <bhalevy at panasas.com>
---
fs/nfs/blocklayout/blocklayout.c | 190 ++++++++++++++++++++++++++++++++++++++
1 files changed, 190 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c
index fdc6421..c541a32 100644
--- a/fs/nfs/blocklayout/blocklayout.c
+++ b/fs/nfs/blocklayout/blocklayout.c
@@ -722,6 +722,183 @@ bl_uninitialize_mountpoint(struct pnfs_mount_type *mtype)
return 0;
}
+/* STUB - mark intersection of layout and page as bad, so is not
+ * used again.
+ */
+static void mark_bad_read(void)
+{
+ return;
+}
+
+/* Copied from buffer.c */
+static void __end_buffer_read_notouch(struct buffer_head *bh, int uptodate)
+{
+ if (uptodate) {
+ set_buffer_uptodate(bh);
+ } else {
+ /* This happens, due to failed READA attempts. */
+ clear_buffer_uptodate(bh);
+ }
+ unlock_buffer(bh);
+}
+
+/* Copied from buffer.c */
+static void end_buffer_read_nobh(struct buffer_head *bh, int uptodate)
+{
+ __end_buffer_read_notouch(bh, uptodate);
+}
+
+/*
+ * map_block: map a requested I/0 block (isect) into an offset in the LVM
+ * meta block_device
+ */
+static void
+map_block(sector_t isect, struct pnfs_block_extent *be,
+ struct buffer_head *res_bh, unsigned int bitsize)
+{
+ dprintk("%s enter be=%p\n", __func__, be);
+
+ set_buffer_mapped(res_bh);
+ res_bh->b_bdev = be->be_mdev;
+ res_bh->b_blocknr = (isect - be->be_f_offset + be->be_v_offset) >>
+ bitsize;
+ res_bh->b_size = 1 << (bitsize + 9);
+
+ dprintk("%s isect %ld, res_bh->b_blocknr %ld, using bsize %Zd\n",
+ __func__, (long)isect,
+ (long)res_bh->b_blocknr,
+ res_bh->b_size);
+ return;
+}
+
+/* This is loosely based on nobh_write_begin */
+static int
+bl_write_begin(struct pnfs_layout_segment *lseg, struct page *page, loff_t pos,
+ unsigned count, struct pnfs_fsdata *fsdata)
+{
+ struct buffer_head *bh;
+ unsigned from, to;
+ int inval, ret = 0;
+ struct pnfs_block_extent *be = NULL, *cow_read = NULL;
+ sector_t isect;
+
+ dprintk("%s enter, %u@%Ld\n", __func__, count, pos);
+ print_page(page);
+ /* The following code assumes blocksize == PAGE_CACHE_SIZE */
+ if (PNFS_INODE(lseg->layout)->i_blkbits != PAGE_CACHE_SHIFT) {
+ dprintk("%s Can't handle blocksize\n", __func__);
+ fsdata->ok_to_use_pnfs = 0;
+ return 0;
+ }
+ from = pos & (PAGE_CACHE_SIZE - 1);
+ to = from + count;
+ fsdata->ok_to_use_pnfs = 1;
+
+ if (PageMappedToDisk(page)) {
+ /* Basically, this is a flag that says we have
+ * successfully called write_begin already on this page.
+ */
+ return 0;
+ }
+
+ bh = alloc_page_buffers(page, PAGE_CACHE_SIZE, 0);
+ if (!bh) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ isect = (sector_t)page->index << (PAGE_CACHE_SHIFT - 9);
+ be = find_get_extent(lseg, isect, &cow_read);
+ if (!be) {
+ fsdata->ok_to_use_pnfs = 0;
+ goto cleanup;
+ }
+ inval = is_hole(be, isect);
+ dprintk("%s inval=%i, from=%u, to=%u\n", __func__, inval, from, to);
+ if (inval) {
+ if (be->be_state == PNFS_BLOCK_NONE_DATA) {
+ dprintk("%s PANIC - got NONE_DATA extent %p\n",
+ __func__, be);
+ fsdata->ok_to_use_pnfs = 0;
+ goto cleanup;
+ }
+ map_block(isect, be, bh, PAGE_CACHE_SHIFT - 9);
+ /* FRED - I don't understand this, not sure is needed */
+ unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
+ }
+ if (PageUptodate(page)) {
+ /* Do nothing */
+ } else if (inval & !cow_read) {
+ char *kaddr = kmap_atomic(page, KM_USER0);
+ if (0 < from) {
+ dprintk("%s memset(0 -> %u)\n", __func__, from);
+ memset(kaddr, 0, from);
+ }
+ if (PAGE_CACHE_SIZE > to) {
+ dprintk("%s memset(%u -> %lu)\n", __func__,
+ to, PAGE_CACHE_SIZE);
+ memset(kaddr + to, 0, PAGE_CACHE_SIZE - to);
+ }
+ flush_dcache_page(page);
+ kunmap_atomic(kaddr, KM_USER0);
+ } else if (0 < from || PAGE_CACHE_SIZE > to) {
+ struct pnfs_block_extent *read_extent;
+
+ read_extent = (inval && cow_read) ? cow_read : be;
+ map_block(isect, read_extent, bh, PAGE_CACHE_SHIFT - 9);
+ lock_buffer(bh);
+ bh->b_end_io = end_buffer_read_nobh;
+ submit_bh(READ, bh);
+ dprintk("%s: Waiting for buffer read\n", __func__);
+ /* XXX Don't really want to hold layout lock here */
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ fsdata->ok_to_use_pnfs = 0;
+ ret = -EIO;
+ }
+ if (ret)
+ goto cleanup;
+ }
+ if (be->be_state == PNFS_BLOCK_INVALID_DATA) {
+ /* NOTE be changes, normally new be_state will be NEEDS_INIT */
+ be = split_inval_extent(lseg, be, isect, PAGE_CACHE_SIZE >> 9);
+ if (!be) {
+ dprintk("%s split failed\n", __func__);
+ fsdata->ok_to_use_pnfs = 0;
+ goto cleanup;
+ }
+ /* STUB - need to mark adjacent pages in large block */
+ }
+ if (be->be_state == PNFS_BLOCK_NEEDS_INIT) {
+ uint32_t mask = 1 << ((isect - be->be_f_offset) >>
+ (PAGE_CACHE_SHIFT - 9));
+ be->be_bitmap &= ~mask;
+ }
+ SetPageMappedToDisk(page);
+ ret = 0; /* Not technically needed */
+
+cleanup:
+ dprintk("%s cleanup, ret=%i, fsdata=%i\n", __func__, ret,
+ fsdata->ok_to_use_pnfs);
+ free_buffer_head(bh);
+ put_extent(be);
+ put_extent(cow_read);
+ if (ret) {
+ /* Need to mark layout with bad read...should now
+ * just use nfs4 for reads and writes.
+ */
+ mark_bad_read();
+ /* Revert back to plain NFS and just continue on with
+ * write. This assumes there is no request attached, which
+ * should be true if we get here.
+ */
+ BUG_ON(PagePrivate(page));
+ fsdata->ok_to_use_pnfs = 0;
+ ret = 0;
+ }
+ return ret;
+}
+
static ssize_t
bl_get_stripesize(struct pnfs_layout_type *layoutid)
{
@@ -753,10 +930,22 @@ bl_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
}
}
+static int
+bl_do_flush(struct pnfs_layout_segment *lseg, struct nfs_page *req,
+ struct pnfs_fsdata *fsdata)
+{
+ dprintk("%s enter\n", __func__);
+ /* This checks if old req will likely use same io method as soon
+ * to be created request, and returns False if they are the same.
+ */
+ return (!lseg != !test_bit(PG_USE_PNFS, &req->wb_flags));
+}
+
static struct layoutdriver_io_operations blocklayout_io_operations = {
.commit = bl_commit,
.read_pagelist = bl_read_pagelist,
.write_pagelist = bl_write_pagelist,
+ .write_begin = bl_write_begin,
.alloc_layout = bl_alloc_layout,
.free_layout = bl_free_layout,
.alloc_lseg = bl_alloc_lseg,
@@ -772,6 +961,7 @@ static struct layoutdriver_policy_operations blocklayout_policy_operations = {
.get_read_threshold = bl_get_io_threshold,
.get_write_threshold = bl_get_io_threshold,
.pg_test = bl_pg_test,
+ .do_flush = bl_do_flush,
};
static struct pnfs_layoutdriver_type blocklayout_type = {
--
1.5.3.3
More information about the pNFS
mailing list