[pnfs] [PATCH 17/21] pnfsblock: extent manipulation functions

Fred Isaman iisaman at citi.umich.edu
Thu Apr 10 10:07:33 EDT 2008


Some extent manipulation that will be needed for bl_write_begin.

Signed-off-by: Fred Isaman <iisaman at citi.umich.edu>
---
 fs/nfs/blocklayout/blocklayout.c |  140 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 140 insertions(+), 0 deletions(-)

diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c
index c98afed..fdc6421 100644
--- a/fs/nfs/blocklayout/blocklayout.c
+++ b/fs/nfs/blocklayout/blocklayout.c
@@ -170,6 +170,146 @@ dont_like_caller(struct nfs_page *req)
 	}
 }
 
+/* Copy data from old to new, remove old from list and add new in its place. */
+static void
+clone_extent(struct pnfs_block_extent *old, struct pnfs_block_extent *new)
+{
+	memcpy(new, old, sizeof(*new));
+	INIT_LIST_HEAD(&new->be_node);
+	kref_init(&new->be_refcnt);
+	list_replace(&old->be_node, &new->be_node);
+	put_extent(old);
+}
+
+/*
+ *      |-------------old-----------------|
+ *      |----len----|
+ *
+ * 		becomes
+ *
+ *      |----new----|-----------old-------|
+ *
+*/
+static void
+split_extent_helper(struct pnfs_block_layout *bl,
+		    struct pnfs_block_extent *old, sector_t len,
+		    struct pnfs_block_extent *new)
+{
+	struct pnfs_block_extent *next;
+
+	INIT_LIST_HEAD(&new->be_node);
+	kref_init(&new->be_refcnt);
+	new->be_bitmap = 0;
+	new->be_f_offset = old->be_f_offset;
+	new->be_length = len;
+	new->be_v_offset = old->be_v_offset;
+	new->be_state = old->be_state;
+	old->be_f_offset += len;
+	old->be_length -= len;
+	old->be_v_offset += len;
+	/* Because list is sorted by offset, new will be in old's place */
+	list_replace(&old->be_node, &new->be_node);
+	/* However, old is not necessarily next.  A cow READ extent may
+	 * intervene.
+	 */
+	next = list_prepare_entry(new, &bl->bl_extents, be_node);
+	list_for_each_entry_continue(next, &bl->bl_extents, be_node) {
+		if (next->be_f_offset > old->be_f_offset)
+			break;
+	}
+	list_add_tail(&old->be_node, &next->be_node);
+	bl->bl_n_ext++;
+}
+
+/*
+ * Finds the extent containing isect, and if it is INVAL, splits it out
+ * so isect is in a NEEDS_INIT extent.
+ */
+static struct pnfs_block_extent *
+split_inval_extent(struct pnfs_layout_segment *lseg,
+		   struct pnfs_block_extent *be,
+		   sector_t isect, sector_t len)
+{
+	struct pnfs_block_layout *bl = BLK_LO(lseg);
+	struct pnfs_block_extent *rv = NULL, *inval = NULL, *e1, *e2, *e3;
+	struct nfs_server *nfss = PNFS_NFS_SERVER(lseg->layout);
+
+	dprintk("%s isect=%Lu\n", __func__, (u64)isect);
+	put_extent(be);
+
+	/* Preallocate prior to taking spinlock */
+	e1 = e2 = e3 = NULL;
+	e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
+	e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
+	e3 = kmalloc(sizeof(*e3), GFP_KERNEL);
+	if (!e1 || !e2 || !e3) {
+		kfree(e1);
+		kfree(e2);
+		kfree(e3);
+		return NULL;
+	}
+
+	spin_lock(&bl->bl_ext_lock);
+	/* Find the INVAL extent to split */
+	list_for_each_entry(be, &bl->bl_extents, be_node) {
+		if (isect < be->be_f_offset)
+			break;
+		if (isect < be->be_f_offset + be->be_length) {
+			if (be->be_state == PNFS_BLOCK_INVALID_DATA) {
+				inval = be;
+				break;
+			} else if (be->be_state == PNFS_BLOCK_READWRITE_DATA ||
+				   be->be_state == PNFS_BLOCK_NEEDS_INIT)
+				rv = be;
+		}
+	}
+	if (!inval) {
+		/* Hopefully, this is due to someone else doing the split */
+		dprintk("%s Could not find INVAL to split\n", __func__);
+		kfree(e1);
+		kfree(e2);
+		kfree(e3);
+		goto out;
+	}
+	/* Shift to block boundaries */
+	if (nfss->pnfs_blksize) {
+		u32 mask = (nfss->pnfs_blksize >> 9) - 1;
+		isect &= ~mask;
+		len = (len + mask) & ~mask;
+		/* We can only hold 32 pages in a NEEDS_INIT extent */
+		len = min(len, (sector_t) (32 << (PAGE_CACHE_SHIFT - 9)));
+	}
+
+	/* Split inval */
+	clone_extent(inval, e2);
+	rv = e2;
+	inval = NULL;   /* not needed, but helps prevent errors */
+	if (e2->be_f_offset != isect)
+		split_extent_helper(bl, e2, isect - e2->be_f_offset, e1);
+	else
+		kfree(e1);
+	if (e2->be_length > len) {
+		split_extent_helper(bl, e2, len, e3);
+		rv = e3;
+	} else {
+		kfree(e3);
+		if (e2->be_length < len) {
+			/* This can't happen now, while len == blksize, but
+			 * if we choose to use larger lengths, must take
+			 * care of this.
+			 */
+			dprintk("%s BUG - len too large\n", __func__);
+		}
+	}
+	rv->be_state = PNFS_BLOCK_NEEDS_INIT;
+	rv->be_bitmap = (1 << (len >> (PAGE_CACHE_SHIFT - 9))) - 1;
+ out:
+	spin_unlock(&bl->bl_ext_lock);
+	if (rv)
+		kref_get(&rv->be_refcnt);
+	return rv;
+}
+
 static int
 bl_commit(struct pnfs_layout_type *layoutid,
 		int sync,
-- 
1.5.3.3



More information about the pNFS mailing list