[SRU,Xenial,1/1] nbd: Create size change events for userspace

Message ID dab1787c8dd4da85efae928b4dbafbf005c726aa.1508520497.git.joseph.salisbury@canonical.com
State New
Headers show
Series
  • [SRU,Xenial,1/1] nbd: Create size change events for userspace
Related show

Commit Message

Joseph Salisbury Oct. 20, 2017, 5:33 p.m.
From: Markus Pargmann <mpa@pengutronix.de>

BugLink: http://bugs.launchpad.net/bugs/696435

The userspace needs to know when nbd devices are ready for use.
Currently no events are created for the userspace which doesn't work for
systemd.

See the discussion here: https://github.com/systemd/systemd/pull/358

This patch uses a central point to setup the nbd-internal sizes. A ioctl
to set a size does not lead to a visible size change. The size of the
block device will be kept at 0 until nbd is connected. As soon as it
connects, the size will be changed to the real value and a uevent is
created. When disconnecting, the blockdevice is set to 0 size and
another uevent is generated.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
(cherry picked from commit 37091fdd831f28a6509008542174ed324dd645bc)
Signed-off-by: Joseph Salisbury <joseph.salisbury@canonical.com>
---
 drivers/block/nbd.c | 79 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 58 insertions(+), 21 deletions(-)

Comments

Kleber Souza Oct. 23, 2017, 11:22 a.m. | #1
On 10/20/17 19:33, Joseph Salisbury wrote:
> From: Markus Pargmann <mpa@pengutronix.de>
> 
> BugLink: http://bugs.launchpad.net/bugs/696435
> 
> The userspace needs to know when nbd devices are ready for use.
> Currently no events are created for the userspace which doesn't work for
> systemd.
> 
> See the discussion here: https://github.com/systemd/systemd/pull/358
> 
> This patch uses a central point to setup the nbd-internal sizes. A ioctl
> to set a size does not lead to a visible size change. The size of the
> block device will be kept at 0 until nbd is connected. As soon as it
> connects, the size will be changed to the real value and a uevent is
> created. When disconnecting, the blockdevice is set to 0 size and
> another uevent is generated.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> (cherry picked from commit 37091fdd831f28a6509008542174ed324dd645bc)
> Signed-off-by: Joseph Salisbury <joseph.salisbury@canonical.com>

Clean cherry-pick, good test results, fixing 6 y/o bug :-).

Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>

> ---
>  drivers/block/nbd.c | 79 +++++++++++++++++++++++++++++++++++++++--------------
>  1 file changed, 58 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
> index b05226d..5f807a6 100644
> --- a/drivers/block/nbd.c
> +++ b/drivers/block/nbd.c
> @@ -98,6 +98,11 @@ static inline struct device *nbd_to_dev(struct nbd_device *nbd)
>  	return disk_to_dev(nbd->disk);
>  }
>  
> +static bool nbd_is_connected(struct nbd_device *nbd)
> +{
> +	return !!nbd->task_recv;
> +}
> +
>  static const char *nbdcmd_to_ascii(int cmd)
>  {
>  	switch (cmd) {
> @@ -110,6 +115,42 @@ static const char *nbdcmd_to_ascii(int cmd)
>  	return "invalid";
>  }
>  
> +static int nbd_size_clear(struct nbd_device *nbd, struct block_device *bdev)
> +{
> +	bdev->bd_inode->i_size = 0;
> +	set_capacity(nbd->disk, 0);
> +	kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE);
> +
> +	return 0;
> +}
> +
> +static void nbd_size_update(struct nbd_device *nbd, struct block_device *bdev)
> +{
> +	if (!nbd_is_connected(nbd))
> +		return;
> +
> +	bdev->bd_inode->i_size = nbd->bytesize;
> +	set_capacity(nbd->disk, nbd->bytesize >> 9);
> +	kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE);
> +}
> +
> +static int nbd_size_set(struct nbd_device *nbd, struct block_device *bdev,
> +			int blocksize, int nr_blocks)
> +{
> +	int ret;
> +
> +	ret = set_blocksize(bdev, blocksize);
> +	if (ret)
> +		return ret;
> +
> +	nbd->blksize = blocksize;
> +	nbd->bytesize = (loff_t)blocksize * (loff_t)nr_blocks;
> +
> +	nbd_size_update(nbd, bdev);
> +
> +	return 0;
> +}
> +
>  static void nbd_end_request(struct nbd_device *nbd, struct request *req)
>  {
>  	int error = req->errors ? -EIO : 0;
> @@ -402,7 +443,7 @@ static struct device_attribute pid_attr = {
>  	.show = pid_show,
>  };
>  
> -static int nbd_thread_recv(struct nbd_device *nbd)
> +static int nbd_thread_recv(struct nbd_device *nbd, struct block_device *bdev)
>  {
>  	struct request *req;
>  	int ret;
> @@ -427,6 +468,8 @@ static int nbd_thread_recv(struct nbd_device *nbd)
>  		return ret;
>  	}
>  
> +	nbd_size_update(nbd, bdev);
> +
>  	while (1) {
>  		req = nbd_read_stat(nbd);
>  		if (IS_ERR(req)) {
> @@ -437,6 +480,8 @@ static int nbd_thread_recv(struct nbd_device *nbd)
>  		nbd_end_request(nbd, req);
>  	}
>  
> +	nbd_size_clear(nbd, bdev);
> +
>  	device_remove_file(disk_to_dev(nbd->disk), &pid_attr);
>  
>  	spin_lock_irqsave(&nbd->tasks_lock, flags);
> @@ -696,20 +741,19 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
>  		return -EINVAL;
>  	}
>  
> -	case NBD_SET_BLKSIZE:
> -		nbd->blksize = arg;
> -		nbd->bytesize &= ~(nbd->blksize-1);
> -		bdev->bd_inode->i_size = nbd->bytesize;
> -		set_blocksize(bdev, nbd->blksize);
> -		set_capacity(nbd->disk, nbd->bytesize >> 9);
> -		return 0;
> +	case NBD_SET_BLKSIZE: {
> +		loff_t bsize = nbd->bytesize;
> +		do_div(bsize, arg);
> +
> +		return nbd_size_set(nbd, bdev, arg, bsize);
> +	}
>  
>  	case NBD_SET_SIZE:
> -		nbd->bytesize = arg & ~(nbd->blksize-1);
> -		bdev->bd_inode->i_size = nbd->bytesize;
> -		set_blocksize(bdev, nbd->blksize);
> -		set_capacity(nbd->disk, nbd->bytesize >> 9);
> -		return 0;
> +		return nbd_size_set(nbd, bdev, nbd->blksize,
> +				    arg / nbd->blksize);
> +
> +	case NBD_SET_SIZE_BLOCKS:
> +		return nbd_size_set(nbd, bdev, nbd->blksize, arg);
>  
>  	case NBD_SET_TIMEOUT:
>  		nbd->xmit_timeout = arg * HZ;
> @@ -725,13 +769,6 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
>  		nbd->flags = arg;
>  		return 0;
>  
> -	case NBD_SET_SIZE_BLOCKS:
> -		nbd->bytesize = ((u64) arg) * nbd->blksize;
> -		bdev->bd_inode->i_size = nbd->bytesize;
> -		set_blocksize(bdev, nbd->blksize);
> -		set_capacity(nbd->disk, nbd->bytesize >> 9);
> -		return 0;
> -
>  	case NBD_DO_IT: {
>  		struct task_struct *thread;
>  		struct socket *sock;
> @@ -762,7 +799,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
>  		}
>  
>  		nbd_dev_dbg_init(nbd);
> -		error = nbd_thread_recv(nbd);
> +		error = nbd_thread_recv(nbd, bdev);
>  		nbd_dev_dbg_close(nbd);
>  		kthread_stop(thread);
>  
>
Kleber Souza Oct. 30, 2017, 4:23 p.m. | #2
Applied to xenial/master-next branch. Thanks.

Patch

diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index b05226d..5f807a6 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -98,6 +98,11 @@  static inline struct device *nbd_to_dev(struct nbd_device *nbd)
 	return disk_to_dev(nbd->disk);
 }
 
+static bool nbd_is_connected(struct nbd_device *nbd)
+{
+	return !!nbd->task_recv;
+}
+
 static const char *nbdcmd_to_ascii(int cmd)
 {
 	switch (cmd) {
@@ -110,6 +115,42 @@  static const char *nbdcmd_to_ascii(int cmd)
 	return "invalid";
 }
 
+static int nbd_size_clear(struct nbd_device *nbd, struct block_device *bdev)
+{
+	bdev->bd_inode->i_size = 0;
+	set_capacity(nbd->disk, 0);
+	kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE);
+
+	return 0;
+}
+
+static void nbd_size_update(struct nbd_device *nbd, struct block_device *bdev)
+{
+	if (!nbd_is_connected(nbd))
+		return;
+
+	bdev->bd_inode->i_size = nbd->bytesize;
+	set_capacity(nbd->disk, nbd->bytesize >> 9);
+	kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE);
+}
+
+static int nbd_size_set(struct nbd_device *nbd, struct block_device *bdev,
+			int blocksize, int nr_blocks)
+{
+	int ret;
+
+	ret = set_blocksize(bdev, blocksize);
+	if (ret)
+		return ret;
+
+	nbd->blksize = blocksize;
+	nbd->bytesize = (loff_t)blocksize * (loff_t)nr_blocks;
+
+	nbd_size_update(nbd, bdev);
+
+	return 0;
+}
+
 static void nbd_end_request(struct nbd_device *nbd, struct request *req)
 {
 	int error = req->errors ? -EIO : 0;
@@ -402,7 +443,7 @@  static struct device_attribute pid_attr = {
 	.show = pid_show,
 };
 
-static int nbd_thread_recv(struct nbd_device *nbd)
+static int nbd_thread_recv(struct nbd_device *nbd, struct block_device *bdev)
 {
 	struct request *req;
 	int ret;
@@ -427,6 +468,8 @@  static int nbd_thread_recv(struct nbd_device *nbd)
 		return ret;
 	}
 
+	nbd_size_update(nbd, bdev);
+
 	while (1) {
 		req = nbd_read_stat(nbd);
 		if (IS_ERR(req)) {
@@ -437,6 +480,8 @@  static int nbd_thread_recv(struct nbd_device *nbd)
 		nbd_end_request(nbd, req);
 	}
 
+	nbd_size_clear(nbd, bdev);
+
 	device_remove_file(disk_to_dev(nbd->disk), &pid_attr);
 
 	spin_lock_irqsave(&nbd->tasks_lock, flags);
@@ -696,20 +741,19 @@  static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
 		return -EINVAL;
 	}
 
-	case NBD_SET_BLKSIZE:
-		nbd->blksize = arg;
-		nbd->bytesize &= ~(nbd->blksize-1);
-		bdev->bd_inode->i_size = nbd->bytesize;
-		set_blocksize(bdev, nbd->blksize);
-		set_capacity(nbd->disk, nbd->bytesize >> 9);
-		return 0;
+	case NBD_SET_BLKSIZE: {
+		loff_t bsize = nbd->bytesize;
+		do_div(bsize, arg);
+
+		return nbd_size_set(nbd, bdev, arg, bsize);
+	}
 
 	case NBD_SET_SIZE:
-		nbd->bytesize = arg & ~(nbd->blksize-1);
-		bdev->bd_inode->i_size = nbd->bytesize;
-		set_blocksize(bdev, nbd->blksize);
-		set_capacity(nbd->disk, nbd->bytesize >> 9);
-		return 0;
+		return nbd_size_set(nbd, bdev, nbd->blksize,
+				    arg / nbd->blksize);
+
+	case NBD_SET_SIZE_BLOCKS:
+		return nbd_size_set(nbd, bdev, nbd->blksize, arg);
 
 	case NBD_SET_TIMEOUT:
 		nbd->xmit_timeout = arg * HZ;
@@ -725,13 +769,6 @@  static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
 		nbd->flags = arg;
 		return 0;
 
-	case NBD_SET_SIZE_BLOCKS:
-		nbd->bytesize = ((u64) arg) * nbd->blksize;
-		bdev->bd_inode->i_size = nbd->bytesize;
-		set_blocksize(bdev, nbd->blksize);
-		set_capacity(nbd->disk, nbd->bytesize >> 9);
-		return 0;
-
 	case NBD_DO_IT: {
 		struct task_struct *thread;
 		struct socket *sock;
@@ -762,7 +799,7 @@  static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
 		}
 
 		nbd_dev_dbg_init(nbd);
-		error = nbd_thread_recv(nbd);
+		error = nbd_thread_recv(nbd, bdev);
 		nbd_dev_dbg_close(nbd);
 		kthread_stop(thread);