[RHEL7,COMMIT] ploop: Add interface to dump cached BAT

Submitted by Konstantin Khorenko on March 23, 2020, 11:32 a.m.

Details

Message ID 202003231132.02NBWWYs026658@finist-ce7.sw.ru
State New
Series "ploop: Add interface to dump cached BAT"
Headers show

Commit Message

Konstantin Khorenko March 23, 2020, 11:32 a.m.
The commit is pushed to "branch-rh7-3.10.0-1062.12.1.vz7.145.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1062.12.1.vz7.145.3
------>
commit b5421375294906a77072840b7900ae4bc07cef7a
Author: Kirill Tkhai <ktkhai@virtuozzo.com>
Date:   Mon Mar 23 14:32:31 2020 +0300

    ploop: Add interface to dump cached BAT
    
    This adds an interface for dumping cached in kernel BAT to userspace.
    The new ioctl(PLOOP_IOC_DUMP_CACHED_BAT) expects struct ploop_dump_bat_ctl
    parameter. A caller has to set desired delta level, start cluster
    and number of clusters to dump. It returns array or clusters mapping.
    The array member contains zero or not-zero value, in case of related cluster
    is cached for the delta. Otherwise it contains PLOOP_DUMP_BAT_UNCACHED_INDEX
    (0xFFFFFFFFU).
    
    Example: dump cached clusters from 0 to 10485759 for delta with level 1:
    
    main()
    {
            int ret, fd = open("/dev/ploop12345", O_RDONLY);
            struct ploop_dump_bat_ctl *ctl;
            unsigned int i, nr_clusters = 10485760;
    
            if (fd < 0) {
                    perror("open");
                    exit(1);
            }
    
            ctl = malloc(sizeof(*ctl) + nr_clusters * sizeof(unsigned int));
            if (!ctl) {
                    perror("malloc");
                    exit(1);
            }
    
            ctl->level = 1; /* Delta level */
            ctl->start_cluster = 0;
            ctl->nr_clusters = nr_clusters;
    
            ret = ioctl(fd, PLOOP_IOC_DUMP_CACHED_BAT, ctl);
            if (ret) {
                    perror("ioctl");
                    exit(1);
            }
    
           for (i = 0; i < nr_clusters; i++)
                    if (ctl->bat[i] != PLOOP_DUMP_BAT_UNCACHED_INDEX)
                            printf("%d->%d\n", i, ctl->bat[i]);
    }
    
    Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
---
 drivers/block/ploop/dev.c        | 35 +++++++++++++++++++++++
 drivers/block/ploop/fmt_ploop1.c |  1 +
 drivers/block/ploop/map.c        | 61 ++++++++++++++++++++++++++++++++++++++++
 include/linux/ploop/ploop.h      |  4 +++
 include/linux/ploop/ploop_if.h   | 12 ++++++++
 5 files changed, 113 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/block/ploop/dev.c b/drivers/block/ploop/dev.c
index 69430472d93d8..da124fa50250b 100644
--- a/drivers/block/ploop/dev.c
+++ b/drivers/block/ploop/dev.c
@@ -5345,6 +5345,38 @@  static int ploop_thaw(struct ploop_device *plo)
 	return err;
 }
 
+static int ploop_dump_cached_bat(struct ploop_device *plo, unsigned long arg)
+{
+	struct ploop_dump_bat_ctl ctl, __user *uctl;
+	struct ploop_delta *delta;
+	u32 end_cluster;
+
+	uctl = (struct ploop_dump_bat_ctl __user *)arg;
+
+	if (!test_bit(PLOOP_S_RUNNING, &plo->state))
+		return -ENODEV;
+
+	if (plo->maintenance_type != PLOOP_MNTN_OFF)
+		return -EBUSY;
+
+	if (copy_from_user(&ctl, uctl, sizeof(ctl)))
+		return -EFAULT;
+
+	delta = find_delta(plo, ctl.level);
+	if (!delta)
+		return -ENOENT;
+
+	end_cluster = ctl.start_cluster + ctl.nr_clusters - 1;
+	if (end_cluster <= ctl.start_cluster)
+		return -EINVAL;
+
+	if (!delta->ops->dump_bat)
+		return -ENOTSUPP;
+
+	return delta->ops->dump_bat(delta, ctl.start_cluster,
+				    end_cluster, &uctl->bat[0]);
+}
+
 static int ploop_ioctl(struct block_device *bdev, fmode_t fmode, unsigned int cmd,
 		       unsigned long arg)
 {
@@ -5464,6 +5496,9 @@  static int ploop_ioctl(struct block_device *bdev, fmode_t fmode, unsigned int cm
 	case PLOOP_IOC_THAW:
 		err = ploop_thaw(plo);
 		break;
+	case PLOOP_IOC_DUMP_CACHED_BAT:
+		err = ploop_dump_cached_bat(plo, arg);
+		break;
 	default:
 		err = -EINVAL;
 	}
diff --git a/drivers/block/ploop/fmt_ploop1.c b/drivers/block/ploop/fmt_ploop1.c
index 40e24a31689b0..326862a2fd07e 100644
--- a/drivers/block/ploop/fmt_ploop1.c
+++ b/drivers/block/ploop/fmt_ploop1.c
@@ -920,6 +920,7 @@  static struct ploop_delta_ops ploop1_delta_ops =
 	.prepare_grow	=	ploop1_prepare_grow,
 	.complete_grow	=	ploop1_complete_grow,
 	.add_free_blk	=	ploop1_add_free_blk,
+	.dump_bat	=	ploop_map_dump_bat,
 };
 
 static int __init pfmt_ploop1_mod_init(void)
diff --git a/drivers/block/ploop/map.c b/drivers/block/ploop/map.c
index f6a38e40d8c45..6276fb9ec4147 100644
--- a/drivers/block/ploop/map.c
+++ b/drivers/block/ploop/map.c
@@ -1225,6 +1225,67 @@  ploop_index_wb_complete(struct ploop_request * preq)
 	map_wb_complete(m, preq->error);
 }
 
+int ploop_map_dump_bat(struct ploop_delta *delta, u32 start_cluster,
+			u32 end_cluster, u32 __user *to_addr)
+{
+	struct ploop_device *plo = delta->plo;
+	struct ploop_map *map = &plo->map;
+	unsigned int i, idx, level, count;
+	struct map_node *m;
+	struct page *page;
+	u32 *bat;
+	int ret;
+
+	page = alloc_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+	bat = kmap(page);
+
+	while (start_cluster <= end_cluster) {
+		idx = (start_cluster + PLOOP_MAP_OFFSET) & (INDEX_PER_PAGE - 1);
+		count = INDEX_PER_PAGE - idx;
+		if (count > end_cluster + 1 - start_cluster)
+			count = end_cluster + 1 - start_cluster;
+
+		spin_lock_irq(&plo->lock);
+		m = map_lookup(map, start_cluster);
+		if (!m || !test_bit(PLOOP_MAP_UPTODATE, &m->state)) {
+			BUILD_BUG_ON(PLOOP_DUMP_BAT_UNCACHED_INDEX != 0xFFFFFFFFU);
+			memset(&bat[idx], 0xff, count * sizeof(u32));
+			goto unlock;
+		}
+
+		for (i = idx; i < idx + count; i++) {
+			if (m->levels)
+				level = m->levels[i];
+			else
+				level = MAP_LEVEL(m);
+
+			if (level != delta->level)
+				bat[i] = PLOOP_DUMP_BAT_UNCACHED_INDEX;
+			else
+				bat[i] = ((map_index_t *)page_address(m->page))[i];
+		}
+unlock:
+		spin_unlock_irq(&map->plo->lock);
+
+		if (copy_to_user(to_addr, &bat[idx], count * sizeof(u32))) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		start_cluster += count;
+		to_addr += count;
+	}
+
+	ret = 0;
+out:
+	kunmap(page);
+	put_page(page);
+	return ret;
+}
+EXPORT_SYMBOL(ploop_map_dump_bat);
+
 void ploop_map_start(struct ploop_map * map, u64 bd_size)
 {
 	struct ploop_device * plo = map->plo;
diff --git a/include/linux/ploop/ploop.h b/include/linux/ploop/ploop.h
index a22696e9c42e8..4b58d530435e5 100644
--- a/include/linux/ploop/ploop.h
+++ b/include/linux/ploop/ploop.h
@@ -285,6 +285,8 @@  struct ploop_delta_ops
 	int		(*prepare_grow)(struct ploop_delta *, u64 *new_size, int *reloc);
 	int		(*complete_grow)(struct ploop_delta *, u64 new_size);
 	void		(*add_free_blk)(struct ploop_delta *, struct ploop_request *);
+	int		(*dump_bat)(struct ploop_delta *delta, u32 start_cluster,
+				    u32 end_cluster, u32 __user *to_addr);
 };
 
 /* Virtual image. */
@@ -893,6 +895,8 @@  void ploop_index_update(struct ploop_request * preq);
 void ploop_index_wb_complete(struct ploop_request * preq);
 int __init ploop_map_init(void);
 void ploop_map_exit(void);
+int ploop_map_dump_bat(struct ploop_delta *delta, u32 start_cluster,
+			u32 end_cluster, u32 __user *to_addr);
 void ploop_add_req_to_fsync_queue(struct ploop_request * preq);
 int ploop_submit_alloc(struct ploop_delta *delta, struct ploop_request *preq,
 		       struct bio_list *sbl, unsigned int size, iblock_t iblk);
diff --git a/include/linux/ploop/ploop_if.h b/include/linux/ploop/ploop_if.h
index 852e04d50eb9e..213fc3adb289c 100644
--- a/include/linux/ploop/ploop_if.h
+++ b/include/linux/ploop/ploop_if.h
@@ -227,6 +227,15 @@  struct ploop_push_backup_stop_ctl
 	__u32	status; /* for sanity: non-zero if pending or active queue is not empty */
 } __attribute__ ((aligned (8)));
 
+struct ploop_dump_bat_ctl
+{
+	__u32	level;
+	__u32	start_cluster;
+	__u32	nr_clusters;
+#define PLOOP_DUMP_BAT_UNCACHED_INDEX	0xFFFFFFFFU
+	__u32	bat[0];
+} __attribute__ ((aligned (8)));
+
 /* maintenance types */
 enum {
 	PLOOP_MNTN_OFF = 0,  /* no maintenance is in progress */
@@ -367,6 +376,9 @@  struct ploop_track_extent
 /* Unfreeze FS mounted over ploop */
 #define PLOOP_IOC_THAW		_IO(PLOOPCTLTYPE, 33)
 
+/* Get cached BAT */
+#define PLOOP_IOC_DUMP_CACHED_BAT _IOW(PLOOPCTLTYPE, 34, struct ploop_dump_bat_ctl)
+
 /* Events exposed via /sys/block/ploopN/pstate/event */
 #define PLOOP_EVENT_ABORTED	1
 #define PLOOP_EVENT_STOPPED	2