|
@@ -2066,97 +2066,82 @@ err:
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * search for the previous snap in a null delimited string list
|
|
|
- */
|
|
|
-const char *rbd_prev_snap_name(const char *name, const char *start)
|
|
|
-{
|
|
|
- if (name < start + 2)
|
|
|
- return NULL;
|
|
|
-
|
|
|
- name -= 2;
|
|
|
- while (*name) {
|
|
|
- if (name == start)
|
|
|
- return start;
|
|
|
- name--;
|
|
|
- }
|
|
|
- return name + 1;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * compare the old list of snapshots that we have to what's in the header
|
|
|
- * and update it accordingly. Note that the header holds the snapshots
|
|
|
- * in a reverse order (from newest to oldest) and we need to go from
|
|
|
- * older to new so that we don't get a duplicate snap name when
|
|
|
- * doing the process (e.g., removed snapshot and recreated a new
|
|
|
- * one with the same name.
|
|
|
+ * Scan the rbd device's current snapshot list and compare it to the
|
|
|
+ * newly-received snapshot context. Remove any existing snapshots
|
|
|
+ * not present in the new snapshot context. Add a new snapshot for
|
|
|
+ * any snaphots in the snapshot context not in the current list.
|
|
|
+ * And verify there are no changes to snapshots we already know
|
|
|
+ * about.
|
|
|
+ *
|
|
|
+ * Assumes the snapshots in the snapshot context are sorted by
|
|
|
+ * snapshot id, highest id first. (Snapshots in the rbd_dev's list
|
|
|
+ * are also maintained in that order.)
|
|
|
*/
|
|
|
static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
|
|
|
{
|
|
|
- const char *name, *first_name;
|
|
|
- int i = rbd_dev->header.total_snaps;
|
|
|
- struct rbd_snap *snap, *old_snap = NULL;
|
|
|
- struct list_head *p, *n;
|
|
|
+ struct ceph_snap_context *snapc = rbd_dev->header.snapc;
|
|
|
+ const u32 snap_count = snapc->num_snaps;
|
|
|
+ char *snap_name = rbd_dev->header.snap_names;
|
|
|
+ struct list_head *head = &rbd_dev->snaps;
|
|
|
+ struct list_head *links = head->next;
|
|
|
+ u32 index = 0;
|
|
|
|
|
|
- first_name = rbd_dev->header.snap_names;
|
|
|
- name = first_name + rbd_dev->header.snap_names_len;
|
|
|
+ while (index < snap_count || links != head) {
|
|
|
+ u64 snap_id;
|
|
|
+ struct rbd_snap *snap;
|
|
|
|
|
|
- list_for_each_prev_safe(p, n, &rbd_dev->snaps) {
|
|
|
- u64 cur_id;
|
|
|
+ snap_id = index < snap_count ? snapc->snaps[index]
|
|
|
+ : CEPH_NOSNAP;
|
|
|
+ snap = links != head ? list_entry(links, struct rbd_snap, node)
|
|
|
+ : NULL;
|
|
|
+ BUG_ON(snap && snap->id == CEPH_NOSNAP);
|
|
|
|
|
|
- old_snap = list_entry(p, struct rbd_snap, node);
|
|
|
+ if (snap_id == CEPH_NOSNAP || (snap && snap->id > snap_id)) {
|
|
|
+ struct list_head *next = links->next;
|
|
|
|
|
|
- if (i)
|
|
|
- cur_id = rbd_dev->header.snapc->snaps[i - 1];
|
|
|
+ /* Existing snapshot not in the new snap context */
|
|
|
|
|
|
- if (!i || old_snap->id < cur_id) {
|
|
|
- /*
|
|
|
- * old_snap->id was skipped, thus was
|
|
|
- * removed. If this rbd_dev is mapped to
|
|
|
- * the removed snapshot, record that it no
|
|
|
- * longer exists, to prevent further I/O.
|
|
|
- */
|
|
|
- if (rbd_dev->snap_id == old_snap->id)
|
|
|
+ if (rbd_dev->snap_id == snap->id)
|
|
|
rbd_dev->snap_exists = false;
|
|
|
- __rbd_remove_snap_dev(old_snap);
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (old_snap->id == cur_id) {
|
|
|
- /* we have this snapshot already */
|
|
|
- i--;
|
|
|
- name = rbd_prev_snap_name(name, first_name);
|
|
|
+ __rbd_remove_snap_dev(snap);
|
|
|
+
|
|
|
+ /* Done with this list entry; advance */
|
|
|
+
|
|
|
+ links = next;
|
|
|
continue;
|
|
|
}
|
|
|
- for (; i > 0;
|
|
|
- i--, name = rbd_prev_snap_name(name, first_name)) {
|
|
|
- if (!name) {
|
|
|
- WARN_ON(1);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- cur_id = rbd_dev->header.snapc->snaps[i];
|
|
|
- /* snapshot removal? handle it above */
|
|
|
- if (cur_id >= old_snap->id)
|
|
|
- break;
|
|
|
- /* a new snapshot */
|
|
|
- snap = __rbd_add_snap_dev(rbd_dev, i - 1, name);
|
|
|
- if (IS_ERR(snap))
|
|
|
- return PTR_ERR(snap);
|
|
|
-
|
|
|
- /* note that we add it backward so using n and not p */
|
|
|
- list_add(&snap->node, n);
|
|
|
- p = &snap->node;
|
|
|
- }
|
|
|
- }
|
|
|
- /* we're done going over the old snap list, just add what's left */
|
|
|
- for (; i > 0; i--) {
|
|
|
- name = rbd_prev_snap_name(name, first_name);
|
|
|
- if (!name) {
|
|
|
- WARN_ON(1);
|
|
|
- return -EINVAL;
|
|
|
+
|
|
|
+ if (!snap || (snap_id != CEPH_NOSNAP && snap->id < snap_id)) {
|
|
|
+ struct rbd_snap *new_snap;
|
|
|
+
|
|
|
+ /* We haven't seen this snapshot before */
|
|
|
+
|
|
|
+ new_snap = __rbd_add_snap_dev(rbd_dev, index,
|
|
|
+ snap_name);
|
|
|
+ if (IS_ERR(new_snap))
|
|
|
+ return PTR_ERR(new_snap);
|
|
|
+
|
|
|
+ /* New goes before existing, or at end of list */
|
|
|
+
|
|
|
+ if (snap)
|
|
|
+ list_add_tail(&new_snap->node, &snap->node);
|
|
|
+ else
|
|
|
+ list_add(&new_snap->node, head);
|
|
|
+ } else {
|
|
|
+ /* Already have this one */
|
|
|
+
|
|
|
+ BUG_ON(snap->size != rbd_dev->header.snap_sizes[index]);
|
|
|
+ BUG_ON(strcmp(snap->name, snap_name));
|
|
|
+
|
|
|
+ /* Done with this list entry; advance */
|
|
|
+
|
|
|
+ links = links->next;
|
|
|
}
|
|
|
- snap = __rbd_add_snap_dev(rbd_dev, i - 1, name);
|
|
|
- if (IS_ERR(snap))
|
|
|
- return PTR_ERR(snap);
|
|
|
- list_add(&snap->node, &rbd_dev->snaps);
|
|
|
+
|
|
|
+ /* Advance to the next entry in the snapshot context */
|
|
|
+
|
|
|
+ index++;
|
|
|
+ snap_name += strlen(snap_name) + 1;
|
|
|
}
|
|
|
|
|
|
return 0;
|