[Devel,RHEL7,COMMIT] ploop: fix freeze/thaw ioctls

Submitted by Konstantin Khorenko on July 19, 2016, 12:32 p.m.

Details

Message ID 201607191232.u6JCWnhg021287@finist_cl7.x64_64.work.ct
State New
Series "ploop: fix freeze/thaw ioctls"
Headers show

Commit Message

Konstantin Khorenko July 19, 2016, 12:32 p.m.
The commit is pushed to "branch-rh7-3.10.0-327.18.2.vz7.15.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-327.18.2.vz7.14.27
------>
commit 28150e9b63d016913a70e044ac405a2e356ddf1b
Author: Maxim Patlasov <mpatlasov@virtuozzo.com>
Date:   Tue Jul 19 16:32:47 2016 +0400

    ploop: fix freeze/thaw ioctls
    
    Current implementation suffers from several problems:
    
    1) If someone, e.g. another instance of push-backup tool, mistakenly
    attempts to freeze ploop while its thawing is in progress, we can
    end up in double freeze.
    2) After initiating thawing, no way to find out it by sysctl or /sys.
    3) Handling PLOOP_S_FROZEN bit is not synchronized with ploop STOP/CLEAR
    ioctls. It's not nice if ploop releases bdev keeping it in frozen state.
    
    The patch fixes the above in straightforward way: more descriptive
    plo->freeze_state, visible via /sys/block/ploopN/pstate/freeze_state, and
    special checks in ioctl-s to ensure that freeze/thaw is allowed only on
    running ploops and that thaw must preceed ploop STOP.
    
    https://jira.sw.ru/browse/PSBM-49699
    
    Signed-off-by: Maxim Patlasov <mpatlasov@virtuozzo.com>
    Acked-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 drivers/block/ploop/dev.c   | 34 ++++++++++++++++++++++++++++++----
 drivers/block/ploop/sysfs.c |  6 ++++++
 include/linux/ploop/ploop.h |  8 +++++++-
 3 files changed, 43 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/drivers/block/ploop/dev.c b/drivers/block/ploop/dev.c
index 9b7a764..98fae18 100644
--- a/drivers/block/ploop/dev.c
+++ b/drivers/block/ploop/dev.c
@@ -3979,6 +3979,13 @@  static int ploop_stop(struct ploop_device * plo, struct block_device *bdev)
 		return -EBUSY;
 	}
 
+	if (plo->freeze_state != PLOOP_F_NORMAL) {
+		if (printk_ratelimit())
+			printk(KERN_INFO "stop ploop%d failed (freeze_state=%d)\n",
+			       plo->index, plo->freeze_state);
+		return -EBUSY;
+	}
+
 	clear_bit(PLOOP_S_PUSH_BACKUP, &plo->state);
 	ploop_pb_stop(plo->pbd, true);
 
@@ -4896,15 +4903,21 @@  static int ploop_freeze(struct ploop_device *plo, struct block_device *bdev)
 {
 	struct super_block *sb = plo->sb;
 
-	if (test_bit(PLOOP_S_FROZEN, &plo->state))
+	if (!test_bit(PLOOP_S_RUNNING, &plo->state))
+		return -EINVAL;
+
+	if (plo->freeze_state == PLOOP_F_FROZEN)
 		return 0;
 
+	if (plo->freeze_state == PLOOP_F_THAWING)
+		return -EBUSY;
+
 	sb = freeze_bdev(bdev);
 	if (sb && IS_ERR(sb))
 		return PTR_ERR(sb);
 
 	plo->sb = sb;
-	set_bit(PLOOP_S_FROZEN, &plo->state);
+	plo->freeze_state = PLOOP_F_FROZEN;
 	return 0;
 }
 
@@ -4913,16 +4926,29 @@  static int ploop_thaw(struct ploop_device *plo, struct block_device *bdev)
 	struct super_block *sb = plo->sb;
 	int err;
 
-	if (!test_bit(PLOOP_S_FROZEN, &plo->state))
+	if (!test_bit(PLOOP_S_RUNNING, &plo->state))
+		return -EINVAL;
+
+	if (plo->freeze_state == PLOOP_F_NORMAL)
 		return 0;
 
+	if (plo->freeze_state == PLOOP_F_THAWING)
+		return -EBUSY;
+
 	plo->sb = NULL;
-	clear_bit(PLOOP_S_FROZEN, &plo->state);
+	plo->freeze_state = PLOOP_F_THAWING;
 
 	mutex_unlock(&plo->ctl_mutex);
 	err = thaw_bdev(bdev, sb);
 	mutex_lock(&plo->ctl_mutex);
 
+	BUG_ON(plo->freeze_state != PLOOP_F_THAWING);
+
+	if (!err)
+		plo->freeze_state = PLOOP_F_NORMAL;
+	else
+		plo->freeze_state = PLOOP_F_FROZEN;
+
 	return err;
 }
 
diff --git a/drivers/block/ploop/sysfs.c b/drivers/block/ploop/sysfs.c
index 9e4c9ab..55f5d58 100644
--- a/drivers/block/ploop/sysfs.c
+++ b/drivers/block/ploop/sysfs.c
@@ -445,6 +445,11 @@  static u32 show_blocked_bios(struct ploop_device * plo)
 	return plo->blocked_bios;
 }
 
+static u32 show_freeze_state(struct ploop_device * plo)
+{
+	return plo->freeze_state;
+}
+
 #define _TUNE_U32(_name)				\
 static u32 show_##_name(struct ploop_device * plo)	\
 {							\
@@ -531,6 +536,7 @@  static struct attribute *state_attributes[] = {
 	_A(free_qmax),
 	_A(blockable_reqs),
 	_A(blocked_bios),
+	_A(freeze_state),
 	NULL
 };
 
diff --git a/include/linux/ploop/ploop.h b/include/linux/ploop/ploop.h
index 7ce2ca8..9d3410b 100644
--- a/include/linux/ploop/ploop.h
+++ b/include/linux/ploop/ploop.h
@@ -61,7 +61,12 @@  enum {
 				   (for minor mgmt only) */
 	PLOOP_S_ONCE,	        /* An event (e.g. printk once) happened */
 	PLOOP_S_PUSH_BACKUP,	/* Push_backup is in progress */
-	PLOOP_S_FROZEN		/* Frozen PLOOP_IOC_FREEZE */
+};
+
+enum {
+	PLOOP_F_NORMAL,		/* Default: not yet freezed or unfrozen */
+	PLOOP_F_FROZEN,		/* Frozen PLOOP_IOC_FREEZE */
+	PLOOP_F_THAWING,	/* thaw_bdev is in progress */
 };
 
 struct ploop_snapdata
@@ -415,6 +420,7 @@  struct ploop_device
 	struct request_queue	*queue;
 	struct task_struct	*thread;
 	struct super_block	*sb;
+	int			freeze_state;
 	struct rb_node		link;
 
 	/* someone who wants to quiesce state-machine waits