Maxim Patlasov <mpatla...@virtuozzo.com> writes:

> The patch introduce push_backup descriptor ("pbd") and a few simple
> functions to create and release it.
>
> Userspace can govern it by new ioctls: PLOOP_IOC_PUSH_BACKUP_INIT and
> PLOOP_IOC_PUSH_BACKUP_STOP.
Acked-by: Dmitry Monakhov <dmonak...@openvz.org>
>
> Signed-off-by: Maxim Patlasov <mpatla...@virtuozzo.com>
> ---
>  drivers/block/ploop/Makefile      |    2 
>  drivers/block/ploop/dev.c         |   89 ++++++++++++
>  drivers/block/ploop/push_backup.c |  271 
> +++++++++++++++++++++++++++++++++++++
>  drivers/block/ploop/push_backup.h |    8 +
>  include/linux/ploop/ploop.h       |    3 
>  include/linux/ploop/ploop_if.h    |   19 +++
>  6 files changed, 391 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/block/ploop/push_backup.c
>  create mode 100644 drivers/block/ploop/push_backup.h
>
> diff --git a/drivers/block/ploop/Makefile b/drivers/block/ploop/Makefile
> index e36a027..0fecf16 100644
> --- a/drivers/block/ploop/Makefile
> +++ b/drivers/block/ploop/Makefile
> @@ -5,7 +5,7 @@ CFLAGS_io_direct.o = -I$(src)
>  CFLAGS_ploop_events.o = -I$(src)
>  
>  obj-$(CONFIG_BLK_DEV_PLOOP)  += ploop.o
> -ploop-objs := dev.o map.o io.o sysfs.o tracker.o freeblks.o ploop_events.o 
> discard.o
> +ploop-objs := dev.o map.o io.o sysfs.o tracker.o freeblks.o ploop_events.o 
> discard.o push_backup.o
>  
>  obj-$(CONFIG_BLK_DEV_PLOOP)  += pfmt_ploop1.o
>  pfmt_ploop1-objs := fmt_ploop1.o
> diff --git a/drivers/block/ploop/dev.c b/drivers/block/ploop/dev.c
> index 1da073c..23da9f5 100644
> --- a/drivers/block/ploop/dev.c
> +++ b/drivers/block/ploop/dev.c
> @@ -19,6 +19,7 @@
>  #include "ploop_events.h"
>  #include "freeblks.h"
>  #include "discard.h"
> +#include "push_backup.h"
>  
>  /* Structures and terms:
>   *
> @@ -3766,6 +3767,9 @@ static int ploop_stop(struct ploop_device * plo, struct 
> block_device *bdev)
>               return -EBUSY;
>       }
>  
> +     clear_bit(PLOOP_S_PUSH_BACKUP, &plo->state);
> +     ploop_pb_stop(plo->pbd);
> +
>       for (p = plo->disk->minors - 1; p > 0; p--)
>               invalidate_partition(plo->disk, p);
>       invalidate_partition(plo->disk, 0);
> @@ -3892,6 +3896,7 @@ static int ploop_clear(struct ploop_device * plo, 
> struct block_device * bdev)
>       }
>  
>       ploop_fb_fini(plo->fbd, 0);
> +     ploop_pb_fini(plo->pbd);
>  
>       plo->maintenance_type = PLOOP_MNTN_OFF;
>       plo->bd_size = 0;
> @@ -4477,6 +4482,84 @@ static int ploop_getdevice_ioc(unsigned long arg)
>       return err;
>  }
>  
> +static int ploop_push_backup_init(struct ploop_device *plo, unsigned long 
> arg)
> +{
> +     struct ploop_push_backup_init_ctl ctl;
> +     struct ploop_pushbackup_desc *pbd = NULL;
> +     int rc = 0;
> +
> +     if (list_empty(&plo->map.delta_list))
> +             return -ENOENT;
> +
> +     if (plo->maintenance_type != PLOOP_MNTN_OFF)
> +             return -EINVAL;
> +
> +     BUG_ON(plo->pbd);
> +
> +     if (copy_from_user(&ctl, (void*)arg, sizeof(ctl)))
> +             return -EFAULT;
> +
> +     pbd = ploop_pb_alloc(plo);
> +     if (!pbd) {
> +             rc = -ENOMEM;
> +             goto pb_init_done;
> +     }
> +
> +     ploop_quiesce(plo);
> +
> +     rc = ploop_pb_init(pbd, ctl.cbt_uuid, !ctl.cbt_mask_addr);
> +     if (rc) {
> +             ploop_relax(plo);
> +             goto pb_init_done;
> +     }
> +
> +     plo->pbd = pbd;
> +
> +     atomic_set(&plo->maintenance_cnt, 0);
> +     plo->maintenance_type = PLOOP_MNTN_PUSH_BACKUP;
> +     set_bit(PLOOP_S_PUSH_BACKUP, &plo->state);
> +
> +     ploop_relax(plo);
> +
> +     if (ctl.cbt_mask_addr)
> +             rc = ploop_pb_copy_cbt_to_user(pbd, (char *)ctl.cbt_mask_addr);
> +pb_init_done:
> +     if (rc)
> +             ploop_pb_fini(pbd);
> +     return rc;
> +}
> +
> +static int ploop_push_backup_stop(struct ploop_device *plo, unsigned long 
> arg)
> +{
> +     struct ploop_pushbackup_desc *pbd = plo->pbd;
> +     struct ploop_push_backup_stop_ctl ctl;
> +
> +     if (plo->maintenance_type != PLOOP_MNTN_PUSH_BACKUP)
> +             return -EINVAL;
> +
> +     if (copy_from_user(&ctl, (void*)arg, sizeof(ctl)))
> +             return -EFAULT;
> +
> +     if (pbd && ploop_pb_check_uuid(pbd, ctl.cbt_uuid)) {
> +             printk("ploop(%d): PUSH_BACKUP_STOP uuid mismatch\n",
> +                    plo->index);
> +             return -EINVAL;
> +     }
> +
> +     if (!test_and_clear_bit(PLOOP_S_PUSH_BACKUP, &plo->state))
> +             return -EINVAL;
> +
> +     BUG_ON (!pbd);
> +     ctl.status = ploop_pb_stop(pbd);
> +
> +     ploop_quiesce(plo);
> +     ploop_pb_fini(plo->pbd);
> +     plo->maintenance_type = PLOOP_MNTN_OFF;
> +     ploop_relax(plo);
> +
> +     return 0;
> +}
> +
>  static int ploop_ioctl(struct block_device *bdev, fmode_t fmode, unsigned 
> int cmd,
>                      unsigned long arg)
>  {
> @@ -4581,6 +4664,12 @@ static int ploop_ioctl(struct block_device *bdev, 
> fmode_t fmode, unsigned int cm
>       case PLOOP_IOC_MAX_DELTA_SIZE:
>               err = ploop_set_max_delta_size(plo, arg);
>               break;
> +     case PLOOP_IOC_PUSH_BACKUP_INIT:
> +             err = ploop_push_backup_init(plo, arg);
> +             break;
> +     case PLOOP_IOC_PUSH_BACKUP_STOP:
> +             err = ploop_push_backup_stop(plo, arg);
> +             break;
>       default:
>               err = -EINVAL;
>       }
> diff --git a/drivers/block/ploop/push_backup.c 
> b/drivers/block/ploop/push_backup.c
> new file mode 100644
> index 0000000..ecc9862
> --- /dev/null
> +++ b/drivers/block/ploop/push_backup.c
> @@ -0,0 +1,271 @@
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/sched.h>
> +#include <linux/fs.h>
> +#include <linux/file.h>
> +#include <linux/bio.h>
> +#include <linux/interrupt.h>
> +#include <linux/buffer_head.h>
> +#include <linux/kthread.h>
> +
> +#include <trace/events/block.h>
> +
> +#include <linux/ploop/ploop.h>
> +#include "push_backup.h"
> +
> +#define NR_PAGES(bits) (((bits) + PAGE_SIZE*8 - 1) / (PAGE_SIZE*8))
> +#define BITS_PER_PAGE  (1UL << (PAGE_SHIFT + 3))
> +
> +struct ploop_pushbackup_desc {
> +     struct ploop_device *plo;
> +     struct page **cbt_map; /* a 'snapshot' copy of CBT mask */
> +     blkcnt_t cbt_block_max;
> +     blkcnt_t cbt_block_bits;
> +     __u8     cbt_uuid[16];
> +
> +     struct page **ppb_map; /* Ploop Push Backup mask */
> +     cluster_t ppb_block_max; /* first invalid index in ppb_map */
> +     cluster_t ppb_offset; /* [0, ppb_offset) is ACKed by userspace */
> +
> +     spinlock_t            ppb_lock;
> +     struct completion     ppb_comp;
> +     bool                  ppb_waiting;
> +
> +
> +     struct rb_root        pending_tree;
> +     struct rb_root        reported_tree;
> +};
> +
> +int ploop_pb_check_uuid(struct ploop_pushbackup_desc *pbd, __u8 *uuid)
> +{
> +     if (memcmp(pbd->cbt_uuid, uuid, sizeof(pbd->cbt_uuid)))
> +             return -1;
> +     return 0;
> +}
> +
> +struct ploop_pushbackup_desc *ploop_pb_alloc(struct ploop_device *plo)
> +{
> +     struct ploop_pushbackup_desc *pbd;
> +     int i, npages;
> +
> +     pbd = kmalloc(sizeof(struct ploop_pushbackup_desc), 
> GFP_KERNEL|__GFP_ZERO);
> +     if (pbd == NULL)
> +             return NULL;
> +
> +     pbd->ppb_block_max = (plo->bd_size + (1 << plo->cluster_log) - 1)
> +             >> plo->cluster_log;
> +     npages = NR_PAGES(pbd->ppb_block_max);
> +
> +     pbd->ppb_map = vmalloc(npages * sizeof(void *));
> +     if (!pbd->ppb_map) {
> +             kfree(pbd);
> +             return NULL;
> +     }
> +
> +     memset(pbd->ppb_map, 0, npages * sizeof(void *));
> +
> +     for (i = 0; i < npages; i++) {
> +             pbd->ppb_map[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
> +             if (!pbd->ppb_map[i]) {
> +                     while (--i >= 0)
> +                             __free_page(pbd->ppb_map[i]);
> +                     vfree(pbd->ppb_map);
> +                     kfree(pbd);
> +                     return NULL;
> +             }
> +     }
> +
> +     spin_lock_init(&pbd->ppb_lock);
> +     init_completion(&pbd->ppb_comp);
> +     pbd->pending_tree = RB_ROOT;
> +     pbd->reported_tree = RB_ROOT;
> +     pbd->plo = plo;
> +
> +     return pbd;
> +}
> +
> +static int find_first_blk_in_map(struct page **map, u64 map_max, u64 *blk_p)
> +{
> +     u64 blk = *blk_p;
> +     unsigned long idx = blk >> (PAGE_SHIFT + 3);
> +
> +     while (blk < map_max) {
> +             unsigned long off = blk & (BITS_PER_PAGE -1);
> +             unsigned long next_bit;
> +             struct page *page = map[idx];
> +
> +             if (!page)
> +                     goto next;
> +
> +             next_bit = find_next_bit(page_address(page), BITS_PER_PAGE, 
> off);
> +             if (next_bit != BITS_PER_PAGE) {
> +                     *blk_p = ((u64)idx << (PAGE_SHIFT + 3)) + next_bit;
> +                     return 0;
> +             }
> +
> +     next:
> +             idx++;
> +             blk = (u64)idx << (PAGE_SHIFT + 3);
> +     }
> +
> +     return -1;
> +}
> +
> +enum {
> +     SET_BIT,
> +     CLEAR_BIT,
> +     CHECK_BIT,
> +};
> +
> +static bool do_bit_in_map(struct page **map, u64 map_max, u64 blk, int 
> action)
> +{
> +     unsigned long idx = blk >> (PAGE_SHIFT + 3);
> +     unsigned long off = blk & (BITS_PER_PAGE -1);
> +     struct page *page = map[idx];
> +
> +     BUG_ON(blk >= map_max);
> +
> +     switch (action) {
> +     case SET_BIT:
> +             __set_bit(off, page_address(page));
> +             break;
> +     case CLEAR_BIT:
> +             __clear_bit(off, page_address(page));
> +             break;
> +     case CHECK_BIT:
> +             return test_bit(off, page_address(page));
> +     default:
> +             BUG();
> +     }
> +
> +     return false;
> +}
> +
> +static void set_bit_in_map(struct page **map, u64 map_max, u64 blk)
> +{
> +     do_bit_in_map(map, map_max, blk, SET_BIT);
> +}
> +
> +static int convert_map_to_map(struct ploop_pushbackup_desc *pbd)
> +{
> +     struct page **from_map = pbd->cbt_map;
> +     blkcnt_t from_max = pbd->cbt_block_max - 1;
> +     blkcnt_t from_bits = pbd->cbt_block_bits;
> +
> +     struct page **to_map = pbd->ppb_map;
> +     cluster_t to_max = pbd->ppb_block_max;
> +     int to_bits = pbd->plo->cluster_log + 9;
> +
> +     u64 from_blk, to_blk;
> +
> +     if ((u64)from_max << from_bits != (u64)to_max << to_bits) {
> +             printk("mismatch in map convert: %lu %lu ---> %u %d\n",
> +                    from_max, from_bits, to_max, to_bits);
> +             return -EINVAL;
> +     }
> +
> +     for (from_blk = 0; from_blk < from_max;
> +          from_blk = (++to_blk << to_bits) >> from_bits) {
> +
> +             if (find_first_blk_in_map(from_map, from_max, &from_blk))
> +                     break;
> +
> +             to_blk = (from_blk << from_bits) >> to_bits;
> +             set_bit_in_map(to_map, to_max, to_blk);
> +     }
> +
> +     return 0;
> +
> +}
> +
> +int ploop_pb_init(struct ploop_pushbackup_desc *pbd, __u8 *uuid, bool full)
> +{
> +     int rc;
> +
> +     memcpy(pbd->cbt_uuid, uuid, sizeof(pbd->cbt_uuid));
> +
> +     if (full) {
> +             int i;
> +             for (i = 0; i < NR_PAGES(pbd->ppb_block_max); i++)
> +                     memset(page_address(pbd->ppb_map[i]), 0xff, PAGE_SIZE);
> +             return 0;
> +     }
> +
> +     rc = blk_cbt_map_copy_once(pbd->plo->queue,
> +                                uuid,
> +                                &pbd->cbt_map,
> +                                &pbd->cbt_block_max,
> +                                &pbd->cbt_block_bits);
> +     if (rc)
> +             return rc;
> +
> +     return convert_map_to_map(pbd);
> +}
> +
> +static void ploop_pb_free_cbt_map(struct ploop_pushbackup_desc *pbd)
> +{
> +     if (pbd->cbt_map) {
> +             unsigned long i;
> +             for (i = 0; i < NR_PAGES(pbd->cbt_block_max); i++)
> +                     if (pbd->cbt_map[i])
> +                             __free_page(pbd->cbt_map[i]);
> +
> +             vfree(pbd->cbt_map);
> +             pbd->cbt_map = NULL;
> +     }
> +}
> +
> +void ploop_pb_fini(struct ploop_pushbackup_desc *pbd)
> +{
> +     int i;
> +
> +     if (pbd == NULL)
> +             return;
> +
> +     if (!RB_EMPTY_ROOT(&pbd->pending_tree))
> +             printk("ploop_pb_fini: pending_tree is not empty!\n");
> +     if (!RB_EMPTY_ROOT(&pbd->reported_tree))
> +             printk("ploop_pb_fini: reported_tree is not empty!\n");
> +
> +     if (pbd->plo)
> +             pbd->plo->pbd = NULL;
> +
> +     ploop_pb_free_cbt_map(pbd);
> +
> +     for (i = 0; i < NR_PAGES(pbd->ppb_block_max); i++)
> +             __free_page(pbd->ppb_map[i]);
> +
> +     vfree(pbd->ppb_map);
> +     kfree(pbd);
> +}
> +
> +int ploop_pb_copy_cbt_to_user(struct ploop_pushbackup_desc *pbd, char 
> *user_addr)
> +{
> +     unsigned long i;
> +
> +     for (i = 0; i < NR_PAGES(pbd->cbt_block_max); i++) {
> +             struct page *page = pbd->cbt_map[i] ? : ZERO_PAGE(0);
> +
> +             if (copy_to_user(user_addr, page_address(page), PAGE_SIZE))
> +                     return -EFAULT;
> +
> +             user_addr += PAGE_SIZE;
> +     }
> +
> +     ploop_pb_free_cbt_map(pbd);
> +     return 0;
> +}
> +
> +unsigned long ploop_pb_stop(struct ploop_pushbackup_desc *pbd)
> +{
> +     if (pbd == NULL)
> +             return 0;
> +
> +     spin_lock(&pbd->ppb_lock);
> +
> +     if (pbd->ppb_waiting)
> +             complete(&pbd->ppb_comp);
> +     spin_unlock(&pbd->ppb_lock);
> +
> +     return 0;
> +}
> diff --git a/drivers/block/ploop/push_backup.h 
> b/drivers/block/ploop/push_backup.h
> new file mode 100644
> index 0000000..40d23f5
> --- /dev/null
> +++ b/drivers/block/ploop/push_backup.h
> @@ -0,0 +1,8 @@
> +struct ploop_pushbackup_desc;
> +
> +struct ploop_pushbackup_desc *ploop_pb_alloc(struct ploop_device *plo);
> +int ploop_pb_init(struct ploop_pushbackup_desc *pbd, __u8 *uuid, bool full);
> +void ploop_pb_fini(struct ploop_pushbackup_desc *pbd);
> +int ploop_pb_copy_cbt_to_user(struct ploop_pushbackup_desc *pbd, char 
> *user_addr);
> +unsigned long ploop_pb_stop(struct ploop_pushbackup_desc *pbd);
> +int ploop_pb_check_uuid(struct ploop_pushbackup_desc *pbd, __u8 *uuid);
> diff --git a/include/linux/ploop/ploop.h b/include/linux/ploop/ploop.h
> index c9fb1b0..09f419d3 100644
> --- a/include/linux/ploop/ploop.h
> +++ b/include/linux/ploop/ploop.h
> @@ -53,6 +53,7 @@ enum {
>       PLOOP_S_LOCKED,         /* ploop is locked by userspace
>                                  (for minor mgmt only) */
>       PLOOP_S_ONCE,           /* An event (e.g. printk once) happened */
> +     PLOOP_S_PUSH_BACKUP,    /* Push_backup is in progress */
>  };
>  
>  struct ploop_snapdata
> @@ -337,6 +338,7 @@ struct ploop_stats
>  };
>  
>  struct ploop_freeblks_desc;
> +struct ploop_pushbackup_desc;
>  
>  struct ploop_device
>  {
> @@ -438,6 +440,7 @@ struct ploop_device
>       char                    cookie[PLOOP_COOKIE_SIZE];
>  
>       struct ploop_freeblks_desc *fbd;
> +     struct ploop_pushbackup_desc *pbd;
>  
>       unsigned long           locking_state; /* plo locked by userspace */
>  };
> diff --git a/include/linux/ploop/ploop_if.h b/include/linux/ploop/ploop_if.h
> index aacddb3..83a68e5 100644
> --- a/include/linux/ploop/ploop_if.h
> +++ b/include/linux/ploop/ploop_if.h
> @@ -186,6 +186,18 @@ struct ploop_getdevice_ctl
>       __u32   __mbz1;
>  } __attribute__ ((aligned (8)));
>  
> +struct ploop_push_backup_init_ctl
> +{
> +     __u8    cbt_uuid[16];
> +     __u64   cbt_mask_addr; /* page-aligned space for CBT mask */
> +} __attribute__ ((aligned (8)));
> +
> +struct ploop_push_backup_stop_ctl
> +{
> +     __u8    cbt_uuid[16];
> +     __u32   status; /* for sanity: non-zero if pending or active queue is 
> not empty */
> +} __attribute__ ((aligned (8)));
> +
>  /* maintenance types */
>  enum {
>       PLOOP_MNTN_OFF = 0,  /* no maintenance is in progress */
> @@ -202,6 +214,7 @@ enum {
>       PLOOP_MNTN_MERGE,    /* merge is in progress */
>       PLOOP_MNTN_GROW,     /* grow is in progress */
>       PLOOP_MNTN_RELOC,    /* relocation is in progress */
> +     PLOOP_MNTN_PUSH_BACKUP, /* push backup is in progress */
>  };
>  
>  /*
> @@ -302,6 +315,12 @@ struct ploop_track_extent
>  /* Set maximum size for the top delta . */
>  #define PLOOP_IOC_MAX_DELTA_SIZE _IOW(PLOOPCTLTYPE, 28, __u64)
>  
> +/* Start push backup */
> +#define PLOOP_IOC_PUSH_BACKUP_INIT _IOR(PLOOPCTLTYPE, 29, struct 
> ploop_push_backup_init_ctl)
> +
> +/* Stop push backup */
> +#define PLOOP_IOC_PUSH_BACKUP_STOP _IOR(PLOOPCTLTYPE, 31, struct 
> ploop_push_backup_stop_ctl)
> +
>  /* Events exposed via /sys/block/ploopN/pstate/event */
>  #define PLOOP_EVENT_ABORTED  1
>  #define PLOOP_EVENT_STOPPED  2

Attachment: signature.asc
Description: PGP signature

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to