@@ -364,6 +364,66 @@ struct super_block *
return id;
}
+/*
+ * are two octet ranges overlapping or adjacent?
+ */
+static bool
+lo_seg_mergeable(struct nfsd4_layout_seg *l1, struct nfsd4_layout_seg *l2)
+{
+ u64 start1 = l1->offset;
+ u64 end1 = end_offset(start1, l1->length);
+ u64 start2 = l2->offset;
+ u64 end2 = end_offset(start2, l2->length);
+
+ /* is end1 == start2 ranges are adjacent */
+ return (end2 >= start1) && (end1 >= start2);
+}
+
+static void
+extend_layout(struct nfsd4_layout_seg *lo, struct nfsd4_layout_seg *lg)
+{
+ u64 lo_start = lo->offset;
+ u64 lo_end = end_offset(lo_start, lo->length);
+ u64 lg_start = lg->offset;
+ u64 lg_end = end_offset(lg_start, lg->length);
+
+ /* lo already covers lg? */
+ if (lo_start <= lg_start && lg_end <= lo_end)
+ return;
+
+ /* extend start offset */
+ if (lo_start > lg_start)
+ lo_start = lg_start;
+
+ /* extend end offset */
+ if (lo_end < lg_end)
+ lo_end = lg_end;
+
+ lo->offset = lo_start;
+ lo->length = (lo_end == NFS4_MAX_UINT64) ?
+ lo_end : lo_end - lo_start;
+}
+
+static bool
+merge_layout(struct nfs4_layout_state *ls, struct nfsd4_layout_seg *seg)
+{
+ bool ret = false;
+ struct nfs4_layout *lp;
+
+ spin_lock(&layout_lock);
+ list_for_each_entry (lp, &ls->ls_layouts, lo_perstate)
+ if (lp->lo_seg.layout_type == seg->layout_type &&
+ lp->lo_seg.clientid == seg->clientid &&
+ lp->lo_seg.iomode == seg->iomode &&
+ (ret = lo_seg_mergeable(&lp->lo_seg, seg))) {
+ extend_layout(&lp->lo_seg, seg);
+ break;
+ }
+ spin_unlock(&layout_lock);
+
+ return ret;
+}
+
__be32
nfs4_pnfs_get_layout(struct svc_rqst *rqstp,
struct nfsd4_pnfs_layoutget *lgp,
@@ -373,6 +433,7 @@ struct super_block *
__be32 nfserr;
struct inode *ino = lgp->lg_fhp->fh_dentry->d_inode;
struct super_block *sb = ino->i_sb;
+ int can_merge;
struct nfs4_file *fp;
struct nfs4_client *clp;
struct nfs4_layout *lp = NULL;
@@ -412,6 +473,9 @@ struct super_block *
goto out;
}
+ can_merge = sb->s_pnfs_op->can_merge_layouts != NULL &&
+ sb->s_pnfs_op->can_merge_layouts(lgp->lg_seg.layout_type);
+
nfs4_lock_state();
fp = find_alloc_file(ino, lgp->lg_fhp);
clp = find_confirmed_client((clientid_t *)&lgp->lg_seg.clientid, true,
@@ -430,6 +494,9 @@ struct super_block *
if (nfserr)
goto out_unlock;
+ /* pre-alloc layout in case we can't merge after we call
+ * the file system
+ */
lp = alloc_layout();
if (!lp) {
nfserr = nfserr_layouttrylater;
@@ -486,6 +553,14 @@ struct super_block *
lgp->lg_seg = res.lg_seg;
lgp->lg_roc = res.lg_return_on_close;
+ /* SUCCESS!
+ * Can the new layout be merged into an existing one?
+ * If so, free unused layout struct
+ */
+ if (can_merge && merge_layout(ls, &res.lg_seg))
+ goto out_freelayout;
+
+ /* Can't merge, so let's initialize this new layout */
init_layout(lp, ls, lgp->lg_fhp, &res.lg_seg, &lgp->lg_sid);
out_unlock:
if (ls)
Signed-off-by: Benny Halevy <bhalevy@primarydata.com> --- fs/nfsd/nfs4pnfsd.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+)