[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