[Devel] [PATCH RH8 07/61] ploop: Setup device from file

Kirill Tkhai ktkhai at virtuozzo.com
Fri May 14 18:55:13 MSK 2021


(Comment some code to use it to optimize delta BAT parse)

Signed-off-by: Kirill Tkhai <ktkhai at virtuozzo.com>
---
 drivers/md/dm-ploop-bat.c    |   91 ++++++++++++++++++++++--------------------
 drivers/md/dm-ploop-cmd.c    |   23 ++++++-----
 drivers/md/dm-ploop-map.c    |    8 ++--
 drivers/md/dm-ploop-target.c |   88 +++++++++++++++++++++++++----------------
 drivers/md/dm-ploop.h        |    5 +-
 5 files changed, 123 insertions(+), 92 deletions(-)

diff --git a/drivers/md/dm-ploop-bat.c b/drivers/md/dm-ploop-bat.c
index 04698d99a67a..632077ad781e 100644
--- a/drivers/md/dm-ploop-bat.c
+++ b/drivers/md/dm-ploop-bat.c
@@ -138,6 +138,7 @@ bool try_update_bat_entry(struct ploop *ploop, unsigned int cluster,
 	return false;
 }
 
+#if 0
 /*
  * Clear all clusters, which are referred to in BAT, from holes_bitmap.
  * Set bat_levels[] to top delta's level. Mark unmapped clusters as
@@ -156,7 +157,7 @@ static int parse_bat_entries(struct ploop *ploop, map_index_t *bat_entries,
 		if (bat_entries[i] == BAT_ENTRY_NONE)
 			return -EINVAL;
 		if (bat_entries[i]) {
-			bat_levels[i] = nr_deltas; /* See top_level() */
+			bat_levels[i] = nr_deltas - 1; /* See top_level() */
 			/* Cluster may refer out holes_bitmap after shrinking */
 			if (bat_entries[i] < ploop->hb_nr)
 				ploop_hole_clear_bit(bat_entries[i], ploop);
@@ -172,7 +173,7 @@ static int parse_bat_entries(struct ploop *ploop, map_index_t *bat_entries,
  * Read from disk and fill bat_entries. Note, that on enter here, cluster #0
  * is already read from disk (with header) -- just parse bio pages content.
  */
-static int ploop_read_bat(struct ploop *ploop, struct bio *bio, u8 nr_deltas)
+int ploop_read_bat(struct ploop *ploop, struct bio *bio, u8 nr_deltas)
 {
 	unsigned int id, entries_per_page, nr_copy, nr_all, page, i = 0;
 	map_index_t *from, *to, cluster = 0;
@@ -224,6 +225,7 @@ static int ploop_read_bat(struct ploop *ploop, struct bio *bio, u8 nr_deltas)
 out:
 	return ret;
 }
+#endif
 
 /* Alloc holes_bitmap and set bits of free clusters */
 static int ploop_setup_holes_bitmap(struct ploop *ploop,
@@ -254,30 +256,14 @@ static int ploop_setup_holes_bitmap(struct ploop *ploop,
 	return 0;
 }
 
-/*
- * Allocate memory for bat_entries, bat_levels and holes_bitmap,
- * and read their content from disk.
- */
-int ploop_read_metadata(struct dm_target *ti, struct ploop *ploop, u8 nr_deltas)
+int ploop_setup_metadata(struct ploop *ploop, struct page *page)
 {
 	unsigned int bat_clusters, offset_clusters, cluster_log;
 	struct ploop_pvd_header *m_hdr = NULL;
 	unsigned long size;
-	struct page *page;
-	struct bio *bio;
 	int ret;
 
 	cluster_log = ploop->cluster_log;
-
-	bio = alloc_bio_with_pages(ploop);
-	if (!bio)
-		return -ENOMEM;
-
-	ret = ploop_read_cluster_sync(ploop, bio, 0);
-	if (ret < 0)
-		goto out;
-
-	page = bio->bi_io_vec[0].bv_page;
 	m_hdr = kmap(page);
 
 	ret = -ENOTSUPP;
@@ -309,18 +295,9 @@ int ploop_read_metadata(struct dm_target *ti, struct ploop *ploop, u8 nr_deltas)
 	m_hdr = NULL;
 
 	ret = ploop_setup_holes_bitmap(ploop, bat_clusters);
-	if (ret)
-		goto out;
-
-	ret = prealloc_md_pages(&ploop->bat_entries, 0, ploop->nr_bat_entries);
-	if (ret)
-		goto out;
-
-	ret = ploop_read_bat(ploop, bio, nr_deltas);
 out:
 	if (m_hdr)
 		kunmap(page);
-	free_bio_with_pages(ploop, bio);
 	return ret;
 }
 
@@ -367,6 +344,20 @@ static int ploop_delta_check_header(struct ploop *ploop, struct page *page,
 	return ret;
 }
 
+int convert_bat_entries(u32 *bat_entries, u32 count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (bat_entries[i] == BAT_ENTRY_NONE)
+			return -EPROTO;
+		if (!bat_entries[i])
+			bat_entries[i] = BAT_ENTRY_NONE;
+	}
+
+	return 0;
+}
+
 int ploop_read_delta_metadata(struct ploop *ploop, struct file *file,
 			      void **d_hdr)
 {
@@ -424,14 +415,7 @@ int ploop_read_delta_metadata(struct ploop *ploop, struct file *file,
 	}
 
 	delta_bat_entries = *d_hdr + PLOOP_MAP_OFFSET * sizeof(map_index_t);
-	for (i = 0; i < ploop->nr_bat_entries; i++) {
-		if (delta_bat_entries[i] == BAT_ENTRY_NONE) {
-			ret = -EPROTO;
-			goto out_vfree;
-		}
-		if (!delta_bat_entries[i])
-			delta_bat_entries[i] = BAT_ENTRY_NONE;
-	}
+	ret = convert_bat_entries(delta_bat_entries, ploop->nr_bat_entries);
 
 out_vfree:
 	if (ret) {
@@ -443,6 +427,13 @@ int ploop_read_delta_metadata(struct ploop *ploop, struct file *file,
 	return ret;
 }
 
+static void ploop_set_not_hole(struct ploop *ploop, u32 dst_cluster)
+{
+	/* Cluster may refer out holes_bitmap after shrinking */
+	if (dst_cluster < ploop->hb_nr)
+		ploop_hole_clear_bit(dst_cluster, ploop);
+}
+
 /*
  * Prefer first added delta, since the order is:
  * 1)add top device
@@ -454,14 +445,15 @@ static void apply_delta_mappings(struct ploop *ploop, struct ploop_delta *deltas
 				 u32 level, void *hdr, u64 size_in_clus)
 {
 	map_index_t *bat_entries, *delta_bat_entries;
+	bool is_top_level, is_raw, stop = false;
 	unsigned int i, end, dst_cluster, clu;
 	struct rb_node *node;
 	struct md_page *md;
-	bool is_raw;
 
 	/* Points to hdr since md_page[0] also contains hdr. */
 	delta_bat_entries = (map_index_t *)hdr;
 	is_raw = deltas[level].is_raw;
+	is_top_level = (level == top_level(ploop));
 
 	write_lock_irq(&ploop->bat_rwlock);
 	ploop_for_each_md_page(ploop, md, node) {
@@ -469,10 +461,17 @@ static void apply_delta_mappings(struct ploop *ploop, struct ploop_delta *deltas
 		bat_entries = kmap_atomic(md->page);
 		for (; i <= end; i++) {
 			clu = page_clu_idx_to_bat_clu(md->id, i);
-			if (clu >= size_in_clus)
-				goto unlock;
-			if (bat_entries[i] != BAT_ENTRY_NONE)
-				continue;
+			if (clu >= size_in_clus) {
+				WARN_ON_ONCE(is_top_level);
+				stop = true;
+				goto unmap;
+			}
+
+			if (bat_entries[i] != BAT_ENTRY_NONE) {
+				/* md0 is already populated */
+				WARN_ON_ONCE(md->id && is_top_level);
+				goto set_not_hole;
+			}
 
 			if (!is_raw)
 				dst_cluster = delta_bat_entries[i];
@@ -485,12 +484,16 @@ static void apply_delta_mappings(struct ploop *ploop, struct ploop_delta *deltas
 				continue;
 			md->bat_levels[i] = level;
 			bat_entries[i] = dst_cluster;
-
+set_not_hole:
+			if (is_top_level)
+				ploop_set_not_hole(ploop, bat_entries[i]);
 		}
+unmap:
 		kunmap_atomic(bat_entries);
+		if (stop)
+			break;
 		delta_bat_entries += PAGE_SIZE / sizeof(map_index_t);
 	}
-unlock:
 	write_unlock_irq(&ploop->bat_rwlock);
 }
 
@@ -529,7 +532,7 @@ int ploop_add_delta(struct ploop *ploop, u32 level, struct file *file, bool is_r
 	}
 
 	ret = -EBADSLT;
-	if (level != ploop->nr_deltas - 1 &&
+	if (level != top_level(ploop) &&
 	    size_in_clus > deltas[level + 1].size_in_clus)
 		goto out;
 
diff --git a/drivers/md/dm-ploop-cmd.c b/drivers/md/dm-ploop-cmd.c
index 7060e88675ed..9fd2b8664edc 100644
--- a/drivers/md/dm-ploop-cmd.c
+++ b/drivers/md/dm-ploop-cmd.c
@@ -581,7 +581,7 @@ static bool iter_delta_clusters(struct ploop *ploop, struct ploop_cmd *cmd)
 		/* FIXME: Optimize this. ploop_bat_entries() is overkill */
 		dst_cluster = ploop_bat_entries(ploop, *cluster, &level);
 		if (dst_cluster == BAT_ENTRY_NONE ||
-		    level != ploop->nr_deltas - 1)
+		    level != ploop->nr_deltas - 2)
 			continue;
 
 		spin_lock_irq(&ploop->deferred_lock);
@@ -653,7 +653,10 @@ static void process_merge_latest_snapshot_cmd(struct ploop *ploop,
 			goto complete;
 		}
 		write_lock_irq(&ploop->bat_rwlock);
-		file = ploop->deltas[--ploop->nr_deltas].file;
+		level = ploop->nr_deltas - 2;
+		file = ploop->deltas[level].file;
+		ploop->deltas[level] = ploop->deltas[level + 1];
+		ploop->nr_deltas--;
 		write_unlock_irq(&ploop->bat_rwlock);
 		fput(file);
 	}
@@ -670,7 +673,7 @@ static int ploop_merge_latest_snapshot(struct ploop *ploop)
 		return -EBUSY;
 	if (ploop_is_ro(ploop))
 		return -EROFS;
-	if (!ploop->nr_deltas)
+	if (ploop->nr_deltas < 2)
 		return -ENOENT;
 again:
 	memset(&cmd, 0, sizeof(cmd));
@@ -831,11 +834,13 @@ static int ploop_notify_merged(struct ploop *ploop, u8 level, bool forward)
 {
 	if (ploop->maintaince)
 		return -EBUSY;
-	if (level >= ploop->nr_deltas)
+	if (level >= top_level(ploop))
 		return -ENOENT;
 	if (level == 0 && !forward)
 		return -EINVAL;
-	if (level == ploop->nr_deltas - 1 && forward)
+	if (level == top_level(ploop) - 1 && forward)
+		return -EINVAL;
+	if (ploop->nr_deltas < 3)
 		return -EINVAL;
 	/*
 	 * Userspace notifies us, it has copied clusters of
@@ -896,7 +901,7 @@ static int ploop_update_delta_index(struct ploop *ploop, unsigned int level,
 
 	if (ploop->maintaince)
 		return -EBUSY;
-	if (level >= ploop->nr_deltas)
+	if (level >= top_level(ploop))
 		return -ENOENT;
 
 	cmd.update_delta_index.level = level;
@@ -914,7 +919,7 @@ static void process_flip_upper_deltas(struct ploop *ploop, struct ploop_cmd *cmd
 {
 	unsigned int i, size, end, bat_clusters, hb_nr, *bat_entries;
 	void *holes_bitmap = ploop->holes_bitmap;
-	u8 level = ploop->nr_deltas - 1;
+	u8 level = top_level(ploop) - 1;
 	struct rb_node *node;
 	struct md_page *md;
 
@@ -1165,9 +1170,9 @@ static int ploop_flip_upper_deltas(struct ploop *ploop, char *new_dev,
 		return -EBUSY;
 	if (ploop_is_ro(ploop))
 		return -EROFS;
-	if (!ploop->nr_deltas)
+	if (ploop->nr_deltas < 2)
 		return -ENOENT;
-	if (ploop->deltas[ploop->nr_deltas - 1].is_raw)
+	if (ploop->deltas[ploop->nr_deltas - 2].is_raw)
 		return -EBADSLT;
 	if (kstrtou32(new_ro_fd, 10, &new_fd) < 0 ||
 	    !(cmd.flip_upper_deltas.file = fget(new_fd)))
diff --git a/drivers/md/dm-ploop-map.c b/drivers/md/dm-ploop-map.c
index 9fe77addcb63..b1fd15d5516d 100644
--- a/drivers/md/dm-ploop-map.c
+++ b/drivers/md/dm-ploop-map.c
@@ -175,7 +175,7 @@ static int ploop_map_discard(struct ploop *ploop, struct bio *bio)
 		read_lock_irqsave(&ploop->bat_rwlock, flags);
 		/* Early checks to not wake up work for nought. */
 		if (cluster_is_in_top_delta(ploop, cluster) &&
-		    !ploop->nr_deltas)
+		    ploop->nr_deltas == 1)
 			supported = true;
 		read_unlock_irqrestore(&ploop->bat_rwlock, flags);
 	}
@@ -418,7 +418,7 @@ static void handle_discard_bio(struct ploop *ploop, struct bio *bio,
 	unsigned long flags;
 	int ret;
 
-	if (!cluster_is_in_top_delta(ploop, cluster) || ploop->nr_deltas) {
+	if (!cluster_is_in_top_delta(ploop, cluster) || ploop->nr_deltas != 1) {
 enotsupp:
 		bio->bi_status = BLK_STS_NOTSUPP;
 		bio_endio(bio);
@@ -550,7 +550,7 @@ static void piwb_discard_completed(struct ploop *ploop, bool success,
 		return;
 
 	if (cluster_is_in_top_delta(ploop, cluster)) {
-		WARN_ON_ONCE(ploop->nr_deltas);
+		WARN_ON_ONCE(ploop->nr_deltas != 1);
 		if (success)
 			ploop_release_cluster(ploop, cluster);
 	}
@@ -1387,7 +1387,7 @@ static int process_one_discard_bio(struct ploop *ploop, struct bio *bio,
 	map_index_t *to;
 	struct pio *h;
 
-	WARN_ON(ploop->nr_deltas);
+	WARN_ON(ploop->nr_deltas != 1);
 
 	h = bio_to_endio_hook(bio);
 	cluster = h->cluster;
diff --git a/drivers/md/dm-ploop-target.c b/drivers/md/dm-ploop-target.c
index e3955819f341..5797ff2660cd 100644
--- a/drivers/md/dm-ploop-target.c
+++ b/drivers/md/dm-ploop-target.c
@@ -173,25 +173,6 @@ static void ploop_destroy(struct ploop *ploop)
 	kfree(ploop);
 }
 
-static int ploop_check_origin_dev(struct dm_target *ti, struct ploop *ploop, u8 nr_deltas)
-{
-	struct block_device *bdev = ploop->origin_dev->bdev;
-	int r;
-
-	if (bdev->bd_block_size < PAGE_SIZE) {
-		ti->error = "Origin dev has too small block size";
-		return -EINVAL;
-	}
-
-	r = ploop_read_metadata(ti, ploop, nr_deltas);
-	if (r) {
-		ti->error = "Can't read ploop header";
-		return r;
-	}
-
-	return 0;
-}
-
 static struct file * get_delta_file(int fd)
 {
 	struct file *file;
@@ -207,17 +188,57 @@ static struct file * get_delta_file(int fd)
 	return file;
 }
 
+static int check_top_delta(struct ploop *ploop, struct file *file)
+{
+	u32 nr, *bat_entries;
+	struct md_page *md0;
+	int ret;
+
+	/* Prealloc a page to read hdr */
+	ret = prealloc_md_pages(&ploop->bat_entries, 0, 1);
+	if (ret)
+		goto out;
+
+	ret = -ENXIO;
+	md0 = md_page_find(ploop, 0);
+	if (!md0)
+		goto out;
+
+	ret = rw_page_sync(READ, file, 0, md0->page);
+	if (ret < 0)
+		goto out;
+
+	bat_entries = kmap(md0->page);
+	nr = PAGE_SIZE / sizeof(u32) - PLOOP_MAP_OFFSET;
+	ret = convert_bat_entries(bat_entries + PLOOP_MAP_OFFSET, nr);
+	kunmap(md0->page);
+	if (ret) {
+		pr_err("Can't read BAT\n");
+		goto out;
+	}
+
+	ret = ploop_setup_metadata(ploop, md0->page);
+	if (ret)
+		goto out;
+	/* Alloc rest of pages */
+	ret = prealloc_md_pages(&ploop->bat_entries, 1, ploop->nr_bat_entries);
+	if (ret)
+		goto out;
+out:
+	return ret;
+}
+
 static int ploop_add_deltas_stack(struct ploop *ploop, char **argv, int argc)
 {
 	struct ploop_delta *deltas;
-	int i, delta_fd, ret = 0;
+	int i, delta_fd, ret;
 	struct file *file;
 	const char *arg;
 	bool is_raw;
 
-	if (!argc)
-		goto out;
 	ret = -EINVAL;
+	if (argc < 1)
+		goto out;
 	if (argc > BAT_LEVEL_MAX - 1)
 		goto out;
 
@@ -247,16 +268,23 @@ static int ploop_add_deltas_stack(struct ploop *ploop, char **argv, int argc)
 			goto out;
 		}
 
-		ret = ploop_add_delta(ploop, i, file, is_raw);
-		if (ret < 0) {
-			fput(file);
-			goto out;
+		if (i == argc - 1) { /* Top delta */
+			ret = check_top_delta(ploop, file);
+			if (ret)
+				goto err_fput;
 		}
+
+		ret = ploop_add_delta(ploop, i, file, is_raw);
+		if (ret < 0)
+			goto err_fput;
 	}
 
 	ret = 0;
 out:
 	return ret;
+err_fput:
+	fput(file);
+	goto out;
 }
 /*
  * <data dev>
@@ -337,12 +365,6 @@ static int ploop_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto err;
 	}
 
-	ret = ploop_check_origin_dev(ti, ploop, argc - 2);
-	if (ret) {
-		/* ploop_check_origin_dev() assigns ti->error */
-		goto err;
-	}
-
 	ret = ploop_add_deltas_stack(ploop, &argv[2], argc - 2);
 	if (ret)
 		goto err;
@@ -422,7 +444,7 @@ static void ploop_status(struct dm_target *ti, status_type_t type,
 	if (p == stat)
 		p += sprintf(p, "o");
 	BUG_ON(p - stat >= sizeof(stat));
-	DMEMIT("%s %u v2 %u %s", ploop->origin_dev->name, ploop->nr_deltas,
+	DMEMIT("%u v2 %u %s", ploop->nr_deltas,
 		1 << ploop->cluster_log, stat);
 	read_unlock_irq(&ploop->bat_rwlock);
 }
diff --git a/drivers/md/dm-ploop.h b/drivers/md/dm-ploop.h
index 290924dc9b54..f8194e3869d7 100644
--- a/drivers/md/dm-ploop.h
+++ b/drivers/md/dm-ploop.h
@@ -319,7 +319,7 @@ static inline bool whole_cluster(struct ploop *ploop, struct bio *bio)
 #define BAT_LEVEL_MAX		(U8_MAX - 1)
 static inline u8 top_level(struct ploop *ploop)
 {
-	return ploop->nr_deltas;
+	return ploop->nr_deltas - 1;
 }
 
 static inline void ploop_hole_set_bit(unsigned long nr, struct ploop *ploop)
@@ -484,6 +484,7 @@ extern void free_md_page(struct md_page *md);
 extern void free_md_pages_tree(struct rb_root *root);
 extern bool try_update_bat_entry(struct ploop *ploop, unsigned int cluster,
 				 u8 level, unsigned int dst_cluster);
+extern int convert_bat_entries(u32 *bat_entries, u32 count);
 
 extern int ploop_add_delta(struct ploop *ploop, u32 level, struct file *file, bool is_raw);
 extern void defer_bios(struct ploop *ploop, struct bio *bio, struct bio_list *bio_list);
@@ -517,7 +518,7 @@ extern void cleanup_backup(struct ploop *ploop);
 
 extern int ploop_read_cluster_sync(struct ploop *, struct bio *, unsigned int);
 
-extern int ploop_read_metadata(struct dm_target *ti, struct ploop *ploop, u8 nr_deltas);
+extern int ploop_setup_metadata(struct ploop *ploop, struct page *page);
 extern int ploop_read_delta_metadata(struct ploop *ploop, struct file *file,
 				     void **d_hdr);
 extern void call_rw_iter(struct file *file, loff_t pos, unsigned rw,




More information about the Devel mailing list