|
@@ -87,3 +87,109 @@ static void print_elist(struct list_head *list)
|
|
|
}
|
|
|
dprintk("****************\n");
|
|
|
}
|
|
|
+
|
|
|
+static inline int
|
|
|
+extents_consistent(struct pnfs_block_extent *old, struct pnfs_block_extent *new)
|
|
|
+{
|
|
|
+ /* Note this assumes new->be_f_offset >= old->be_f_offset */
|
|
|
+ return (new->be_state == old->be_state) &&
|
|
|
+ ((new->be_state == PNFS_BLOCK_NONE_DATA) ||
|
|
|
+ ((new->be_v_offset - old->be_v_offset ==
|
|
|
+ new->be_f_offset - old->be_f_offset) &&
|
|
|
+ new->be_mdev == old->be_mdev));
|
|
|
+}
|
|
|
+
|
|
|
+/* Adds new to appropriate list in bl, modifying new and removing existing
|
|
|
+ * extents as appropriate to deal with overlaps.
|
|
|
+ *
|
|
|
+ * See bl_find_get_extent for list constraints.
|
|
|
+ *
|
|
|
+ * Refcount on new is already set. If end up not using it, or error out,
|
|
|
+ * need to put the reference.
|
|
|
+ *
|
|
|
+ * bl->bl_ext_lock is held by caller.
|
|
|
+ */
|
|
|
+int
|
|
|
+bl_add_merge_extent(struct pnfs_block_layout *bl,
|
|
|
+ struct pnfs_block_extent *new)
|
|
|
+{
|
|
|
+ struct pnfs_block_extent *be, *tmp;
|
|
|
+ sector_t end = new->be_f_offset + new->be_length;
|
|
|
+ struct list_head *list;
|
|
|
+
|
|
|
+ dprintk("%s enter with be=%p\n", __func__, new);
|
|
|
+ print_bl_extent(new);
|
|
|
+ list = &bl->bl_extents[bl_choose_list(new->be_state)];
|
|
|
+ print_elist(list);
|
|
|
+
|
|
|
+ /* Scan for proper place to insert, extending new to the left
|
|
|
+ * as much as possible.
|
|
|
+ */
|
|
|
+ list_for_each_entry_safe(be, tmp, list, be_node) {
|
|
|
+ if (new->be_f_offset < be->be_f_offset)
|
|
|
+ break;
|
|
|
+ if (end <= be->be_f_offset + be->be_length) {
|
|
|
+ /* new is a subset of existing be*/
|
|
|
+ if (extents_consistent(be, new)) {
|
|
|
+ dprintk("%s: new is subset, ignoring\n",
|
|
|
+ __func__);
|
|
|
+ bl_put_extent(new);
|
|
|
+ return 0;
|
|
|
+ } else
|
|
|
+ goto out_err;
|
|
|
+ } else if (new->be_f_offset <=
|
|
|
+ be->be_f_offset + be->be_length) {
|
|
|
+ /* new overlaps or abuts existing be */
|
|
|
+ if (extents_consistent(be, new)) {
|
|
|
+ /* extend new to fully replace be */
|
|
|
+ new->be_length += new->be_f_offset -
|
|
|
+ be->be_f_offset;
|
|
|
+ new->be_f_offset = be->be_f_offset;
|
|
|
+ new->be_v_offset = be->be_v_offset;
|
|
|
+ dprintk("%s: removing %p\n", __func__, be);
|
|
|
+ list_del(&be->be_node);
|
|
|
+ bl_put_extent(be);
|
|
|
+ } else if (new->be_f_offset !=
|
|
|
+ be->be_f_offset + be->be_length)
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* Note that if we never hit the above break, be will not point to a
|
|
|
+ * valid extent. However, in that case &be->be_node==list.
|
|
|
+ */
|
|
|
+ list_add_tail(&new->be_node, &be->be_node);
|
|
|
+ dprintk("%s: inserting new\n", __func__);
|
|
|
+ print_elist(list);
|
|
|
+ /* Scan forward for overlaps. If we find any, extend new and
|
|
|
+ * remove the overlapped extent.
|
|
|
+ */
|
|
|
+ be = list_prepare_entry(new, list, be_node);
|
|
|
+ list_for_each_entry_safe_continue(be, tmp, list, be_node) {
|
|
|
+ if (end < be->be_f_offset)
|
|
|
+ break;
|
|
|
+ /* new overlaps or abuts existing be */
|
|
|
+ if (extents_consistent(be, new)) {
|
|
|
+ if (end < be->be_f_offset + be->be_length) {
|
|
|
+ /* extend new to fully cover be */
|
|
|
+ end = be->be_f_offset + be->be_length;
|
|
|
+ new->be_length = end - new->be_f_offset;
|
|
|
+ }
|
|
|
+ dprintk("%s: removing %p\n", __func__, be);
|
|
|
+ list_del(&be->be_node);
|
|
|
+ bl_put_extent(be);
|
|
|
+ } else if (end != be->be_f_offset) {
|
|
|
+ list_del(&new->be_node);
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dprintk("%s: after merging\n", __func__);
|
|
|
+ print_elist(list);
|
|
|
+ /* FIXME - The per-list consistency checks have all been done,
|
|
|
+ * should now check cross-list consistency.
|
|
|
+ */
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ out_err:
|
|
|
+ bl_put_extent(new);
|
|
|
+ return -EIO;
|
|
|
+}
|