From 27ea43fe2a32f63bb6f442dafc2133232b8af4a6 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 24 Oct 2015 21:15:34 +0200 Subject: nbd: Fix debugfs error handling Static checker complains about the implemented error handling. It is indeed wrong. We don't care about the return values of created debugfs files. We only have to check the return values of created dirs for NULL pointer. If we use a null pointer as parent directory for files, this may lead to debugfs files in wrong places. Signed-off-by: Markus Pargmann --- drivers/block/nbd.c | 55 ++++++++++++++--------------------------------------- 1 file changed, 14 insertions(+), 41 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index e4c5cc107934..d61a04155d99 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -892,50 +892,23 @@ static const struct file_operations nbd_dbg_flags_ops = { static int nbd_dev_dbg_init(struct nbd_device *nbd) { struct dentry *dir; - struct dentry *f; + + if (!nbd_dbg_dir) + return -EIO; dir = debugfs_create_dir(nbd_name(nbd), nbd_dbg_dir); - if (IS_ERR_OR_NULL(dir)) { - dev_err(nbd_to_dev(nbd), "Failed to create debugfs dir for '%s' (%ld)\n", - nbd_name(nbd), PTR_ERR(dir)); - return PTR_ERR(dir); + if (!dir) { + dev_err(nbd_to_dev(nbd), "Failed to create debugfs dir for '%s'\n", + nbd_name(nbd)); + return -EIO; } nbd->dbg_dir = dir; - f = debugfs_create_file("tasks", 0444, dir, nbd, &nbd_dbg_tasks_ops); - if (IS_ERR_OR_NULL(f)) { - dev_err(nbd_to_dev(nbd), "Failed to create debugfs file 'tasks', %ld\n", - PTR_ERR(f)); - return PTR_ERR(f); - } - - f = debugfs_create_u64("size_bytes", 0444, dir, &nbd->bytesize); - if (IS_ERR_OR_NULL(f)) { - dev_err(nbd_to_dev(nbd), "Failed to create debugfs file 'size_bytes', %ld\n", - PTR_ERR(f)); - return PTR_ERR(f); - } - - f = debugfs_create_u32("timeout", 0444, dir, &nbd->xmit_timeout); - if (IS_ERR_OR_NULL(f)) { - dev_err(nbd_to_dev(nbd), "Failed to create debugfs file 'timeout', %ld\n", - PTR_ERR(f)); - return PTR_ERR(f); - } - - f = debugfs_create_u32("blocksize", 0444, dir, &nbd->blksize); - if (IS_ERR_OR_NULL(f)) { - dev_err(nbd_to_dev(nbd), "Failed to create debugfs file 'blocksize', %ld\n", - PTR_ERR(f)); - return PTR_ERR(f); - } - - f = debugfs_create_file("flags", 0444, dir, &nbd, &nbd_dbg_flags_ops); - if (IS_ERR_OR_NULL(f)) { - dev_err(nbd_to_dev(nbd), "Failed to create debugfs file 'flags', %ld\n", - PTR_ERR(f)); - return PTR_ERR(f); - } + debugfs_create_file("tasks", 0444, dir, nbd, &nbd_dbg_tasks_ops); + debugfs_create_u64("size_bytes", 0444, dir, &nbd->bytesize); + debugfs_create_u32("timeout", 0444, dir, &nbd->xmit_timeout); + debugfs_create_u32("blocksize", 0444, dir, &nbd->blksize); + debugfs_create_file("flags", 0444, dir, &nbd, &nbd_dbg_flags_ops); return 0; } @@ -950,8 +923,8 @@ static int nbd_dbg_init(void) struct dentry *dbg_dir; dbg_dir = debugfs_create_dir("nbd", NULL); - if (IS_ERR(dbg_dir)) - return PTR_ERR(dbg_dir); + if (!dbg_dir) + return -EIO; nbd_dbg_dir = dbg_dir; -- cgit v1.2.3 From 23272a6754b81ff6503e09c743bb4ceeeab39997 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 29 Oct 2015 11:51:16 +0100 Subject: nbd: Remove signal usage As discussed on the mailing list, the usage of signals for timeout handling has a lot of potential issues. The nbd driver used for some time signals for timeouts. These signals where able to get the threads out of the blocking socket operations. This patch removes all signal usage and uses a socket shutdown instead. The socket descriptor itself is cleared later when the whole nbd device is closed. The tasks_lock is removed as we do not depend on this anymore. Instead a new lock for the socket is introduced so we can safely work with the socket in the timeout handler outside of the two main threads. Cc: Oleg Nesterov Cc: Christoph Hellwig Signed-off-by: Markus Pargmann Reviewed-by: Christoph Hellwig --- drivers/block/nbd.c | 126 ++++++++++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 78 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index d61a04155d99..438f4dc549db 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -60,7 +60,8 @@ struct nbd_device { bool disconnect; /* a disconnect has been requested by user */ struct timer_list timeout_timer; - spinlock_t tasks_lock; + /* protects initialization and shutdown of the socket */ + spinlock_t sock_lock; struct task_struct *task_recv; struct task_struct *task_send; @@ -129,13 +130,20 @@ static void nbd_end_request(struct nbd_device *nbd, struct request *req) */ static void sock_shutdown(struct nbd_device *nbd) { - if (!nbd->sock) + spin_lock_irq(&nbd->sock_lock); + + if (!nbd->sock) { + spin_unlock_irq(&nbd->sock_lock); return; + } dev_warn(disk_to_dev(nbd->disk), "shutting down socket\n"); kernel_sock_shutdown(nbd->sock, SHUT_RDWR); + sockfd_put(nbd->sock); nbd->sock = NULL; - del_timer_sync(&nbd->timeout_timer); + spin_unlock_irq(&nbd->sock_lock); + + del_timer(&nbd->timeout_timer); } static void nbd_xmit_timeout(unsigned long arg) @@ -148,17 +156,15 @@ static void nbd_xmit_timeout(unsigned long arg) nbd->disconnect = true; - spin_lock_irqsave(&nbd->tasks_lock, flags); + spin_lock_irqsave(&nbd->sock_lock, flags); - if (nbd->task_recv) - force_sig(SIGKILL, nbd->task_recv); - if (nbd->task_send) - force_sig(SIGKILL, nbd->task_send); + if (nbd->sock) + kernel_sock_shutdown(nbd->sock, SHUT_RDWR); - spin_unlock_irqrestore(&nbd->tasks_lock, flags); + spin_unlock_irqrestore(&nbd->sock_lock, flags); - dev_err(nbd_to_dev(nbd), "Connection timed out, killed receiver and sender, shutting down connection\n"); + dev_err(nbd_to_dev(nbd), "Connection timed out, shutting down connection\n"); } /* @@ -171,7 +177,6 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size, int result; struct msghdr msg; struct kvec iov; - sigset_t blocked, oldset; unsigned long pflags = current->flags; if (unlikely(!sock)) { @@ -181,11 +186,6 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size, return -EINVAL; } - /* Allow interception of SIGKILL only - * Don't allow other signals to interrupt the transmission */ - siginitsetinv(&blocked, sigmask(SIGKILL)); - sigprocmask(SIG_SETMASK, &blocked, &oldset); - current->flags |= PF_MEMALLOC; do { sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC; @@ -212,7 +212,6 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size, buf += result; } while (size > 0); - sigprocmask(SIG_SETMASK, &oldset, NULL); tsk_restore_flags(current, pflags, PF_MEMALLOC); if (!send && nbd->xmit_timeout) @@ -406,23 +405,18 @@ static int nbd_thread_recv(struct nbd_device *nbd) { struct request *req; int ret; - unsigned long flags; BUG_ON(nbd->magic != NBD_MAGIC); sk_set_memalloc(nbd->sock->sk); - spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_recv = current; - spin_unlock_irqrestore(&nbd->tasks_lock, flags); ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr); if (ret) { dev_err(disk_to_dev(nbd->disk), "device_create_file failed!\n"); - spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_recv = NULL; - spin_unlock_irqrestore(&nbd->tasks_lock, flags); return ret; } @@ -439,19 +433,7 @@ static int nbd_thread_recv(struct nbd_device *nbd) device_remove_file(disk_to_dev(nbd->disk), &pid_attr); - spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_recv = NULL; - spin_unlock_irqrestore(&nbd->tasks_lock, flags); - - if (signal_pending(current)) { - ret = kernel_dequeue_signal(NULL); - dev_warn(nbd_to_dev(nbd), "pid %d, %s, got signal %d\n", - task_pid_nr(current), current->comm, ret); - mutex_lock(&nbd->tx_lock); - sock_shutdown(nbd); - mutex_unlock(&nbd->tx_lock); - ret = -ETIMEDOUT; - } return ret; } @@ -544,11 +526,8 @@ static int nbd_thread_send(void *data) { struct nbd_device *nbd = data; struct request *req; - unsigned long flags; - spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_send = current; - spin_unlock_irqrestore(&nbd->tasks_lock, flags); set_user_nice(current, MIN_NICE); while (!kthread_should_stop() || !list_empty(&nbd->waiting_queue)) { @@ -557,17 +536,6 @@ static int nbd_thread_send(void *data) kthread_should_stop() || !list_empty(&nbd->waiting_queue)); - if (signal_pending(current)) { - int ret = kernel_dequeue_signal(NULL); - - dev_warn(nbd_to_dev(nbd), "pid %d, %s, got signal %d\n", - task_pid_nr(current), current->comm, ret); - mutex_lock(&nbd->tx_lock); - sock_shutdown(nbd); - mutex_unlock(&nbd->tx_lock); - break; - } - /* extract request */ if (list_empty(&nbd->waiting_queue)) continue; @@ -582,13 +550,7 @@ static int nbd_thread_send(void *data) nbd_handle_req(nbd, req); } - spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_send = NULL; - spin_unlock_irqrestore(&nbd->tasks_lock, flags); - - /* Clear maybe pending signals */ - if (signal_pending(current)) - kernel_dequeue_signal(NULL); return 0; } @@ -636,6 +598,25 @@ static void nbd_request_handler(struct request_queue *q) } } +static int nbd_set_socket(struct nbd_device *nbd, struct socket *sock) +{ + int ret = 0; + + spin_lock_irq(&nbd->sock_lock); + + if (nbd->sock) { + ret = -EBUSY; + goto out; + } + + nbd->sock = sock; + +out: + spin_unlock_irq(&nbd->sock_lock); + + return ret; +} + static int nbd_dev_dbg_init(struct nbd_device *nbd); static void nbd_dev_dbg_close(struct nbd_device *nbd); @@ -668,32 +649,26 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, return 0; } - case NBD_CLEAR_SOCK: { - struct socket *sock = nbd->sock; - nbd->sock = NULL; + case NBD_CLEAR_SOCK: + sock_shutdown(nbd); nbd_clear_que(nbd); BUG_ON(!list_empty(&nbd->queue_head)); BUG_ON(!list_empty(&nbd->waiting_queue)); kill_bdev(bdev); - if (sock) - sockfd_put(sock); return 0; - } case NBD_SET_SOCK: { - struct socket *sock; int err; - if (nbd->sock) - return -EBUSY; - sock = sockfd_lookup(arg, &err); - if (sock) { - nbd->sock = sock; - if (max_part > 0) - bdev->bd_invalidated = 1; - nbd->disconnect = false; /* we're connected now */ - return 0; - } - return -EINVAL; + struct socket *sock = sockfd_lookup(arg, &err); + + if (!sock) + return err; + + err = nbd_set_socket(nbd, sock); + if (!err && max_part) + bdev->bd_invalidated = 1; + + return err; } case NBD_SET_BLKSIZE: @@ -734,7 +709,6 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, case NBD_DO_IT: { struct task_struct *thread; - struct socket *sock; int error; if (nbd->task_recv) @@ -769,14 +743,10 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, mutex_lock(&nbd->tx_lock); sock_shutdown(nbd); - sock = nbd->sock; - nbd->sock = NULL; nbd_clear_que(nbd); kill_bdev(bdev); queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); set_device_ro(bdev, false); - if (sock) - sockfd_put(sock); nbd->flags = 0; nbd->bytesize = 0; bdev->bd_inode->i_size = 0; @@ -1042,7 +1012,7 @@ static int __init nbd_init(void) nbd_dev[i].magic = NBD_MAGIC; INIT_LIST_HEAD(&nbd_dev[i].waiting_queue); spin_lock_init(&nbd_dev[i].queue_lock); - spin_lock_init(&nbd_dev[i].tasks_lock); + spin_lock_init(&nbd_dev[i].sock_lock); INIT_LIST_HEAD(&nbd_dev[i].queue_head); mutex_init(&nbd_dev[i].tx_lock); init_timer(&nbd_dev[i].timeout_timer); -- cgit v1.2.3 From 1f7b5cf1be4351e60cf8ae7aab976503dd73c5f8 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 29 Oct 2015 12:01:34 +0100 Subject: nbd: Timeouts are not user requested disconnects It may be useful to know in the client that a connection timed out. The current code returns success for a timeout. This patch reports the error code -ETIMEDOUT for a timeout. Signed-off-by: Markus Pargmann --- drivers/block/nbd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 438f4dc549db..2e14e51b5ea3 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -57,6 +57,7 @@ struct nbd_device { int blksize; loff_t bytesize; int xmit_timeout; + bool timedout; bool disconnect; /* a disconnect has been requested by user */ struct timer_list timeout_timer; @@ -154,10 +155,9 @@ static void nbd_xmit_timeout(unsigned long arg) if (list_empty(&nbd->queue_head)) return; - nbd->disconnect = true; - spin_lock_irqsave(&nbd->sock_lock, flags); + nbd->timedout = true; if (nbd->sock) kernel_sock_shutdown(nbd->sock, SHUT_RDWR); @@ -754,7 +754,10 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, if (max_part > 0) blkdev_reread_part(bdev); if (nbd->disconnect) /* user requested, ignore socket errors */ - return 0; + error = 0; + if (nbd->timedout) + error = -ETIMEDOUT; + return error; } -- cgit v1.2.3 From 0e4f0f6f63d3416a9e529d99febfe98545427b81 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 29 Oct 2015 12:04:51 +0100 Subject: nbd: Cleanup reset of nbd and bdev after a disconnect Group all variables that are reset after a disconnect into reset functions. This patch adds two of these functions, nbd_reset() and nbd_bdev_reset(). Signed-off-by: Markus Pargmann --- drivers/block/nbd.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 2e14e51b5ea3..34a46c32c24f 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -617,6 +617,30 @@ out: return ret; } +/* Reset all properties of an NBD device */ +static void nbd_reset(struct nbd_device *nbd) +{ + nbd->disconnect = false; + nbd->timedout = false; + nbd->blksize = 1024; + nbd->bytesize = 0; + set_capacity(nbd->disk, 0); + nbd->flags = 0; + nbd->xmit_timeout = 0; + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); + del_timer_sync(&nbd->timeout_timer); +} + +static void nbd_bdev_reset(struct block_device *bdev) +{ + set_device_ro(bdev, false); + bdev->bd_inode->i_size = 0; + if (max_part > 0) { + blkdev_reread_part(bdev); + bdev->bd_invalidated = 1; + } +} + static int nbd_dev_dbg_init(struct nbd_device *nbd); static void nbd_dev_dbg_close(struct nbd_device *nbd); @@ -745,19 +769,15 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, sock_shutdown(nbd); nbd_clear_que(nbd); kill_bdev(bdev); - queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); - set_device_ro(bdev, false); - nbd->flags = 0; - nbd->bytesize = 0; - bdev->bd_inode->i_size = 0; - set_capacity(nbd->disk, 0); - if (max_part > 0) - blkdev_reread_part(bdev); + nbd_bdev_reset(bdev); + if (nbd->disconnect) /* user requested, ignore socket errors */ error = 0; if (nbd->timedout) error = -ETIMEDOUT; + nbd_reset(nbd); + return error; } @@ -1023,14 +1043,12 @@ static int __init nbd_init(void) nbd_dev[i].timeout_timer.data = (unsigned long)&nbd_dev[i]; init_waitqueue_head(&nbd_dev[i].active_wq); init_waitqueue_head(&nbd_dev[i].waiting_wq); - nbd_dev[i].blksize = 1024; - nbd_dev[i].bytesize = 0; disk->major = NBD_MAJOR; disk->first_minor = i << part_shift; disk->fops = &nbd_fops; disk->private_data = &nbd_dev[i]; sprintf(disk->disk_name, "nbd%d", i); - set_capacity(disk, 0); + nbd_reset(&nbd_dev[i]); add_disk(disk); } -- cgit v1.2.3 From d02cf53107792df373558851d6162dc4e4ceb95a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 29 Oct 2015 12:06:15 +0100 Subject: nbd: Move flag parsing to a function nbd changes properties of the blockdevice depending on flags that were received. This patch moves this flag parsing into a separate function nbd_parse_flags(). Signed-off-by: Markus Pargmann --- drivers/block/nbd.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 34a46c32c24f..b67500d5b338 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -641,6 +641,18 @@ static void nbd_bdev_reset(struct block_device *bdev) } } +static void nbd_parse_flags(struct nbd_device *nbd, struct block_device *bdev) +{ + if (nbd->flags & NBD_FLAG_READ_ONLY) + set_device_ro(bdev, true); + if (nbd->flags & NBD_FLAG_SEND_TRIM) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); + if (nbd->flags & NBD_FLAG_SEND_FLUSH) + blk_queue_flush(nbd->disk->queue, REQ_FLUSH); + else + blk_queue_flush(nbd->disk->queue, 0); +} + static int nbd_dev_dbg_init(struct nbd_device *nbd); static void nbd_dev_dbg_close(struct nbd_device *nbd); @@ -742,15 +754,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, mutex_unlock(&nbd->tx_lock); - if (nbd->flags & NBD_FLAG_READ_ONLY) - set_device_ro(bdev, true); - if (nbd->flags & NBD_FLAG_SEND_TRIM) - queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, - nbd->disk->queue); - if (nbd->flags & NBD_FLAG_SEND_FLUSH) - blk_queue_flush(nbd->disk->queue, REQ_FLUSH); - else - blk_queue_flush(nbd->disk->queue, 0); + nbd_parse_flags(nbd, bdev); thread = kthread_run(nbd_thread_send, nbd, "%s", nbd_name(nbd)); -- cgit v1.2.3 From da6ccaaa79caca4f38b540b651238f87215217a2 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 14 Jan 2016 13:42:32 -0500 Subject: nbd: ratelimit error msgs after socket close Make the "Attempted send on closed socket" error messages generated in nbd_request_handler() ratelimited. When the nbd socket is shutdown, the nbd_request_handler() function emits an error message for every request remaining in its queue. If the queue is large, this will spam a large amount of messages to the log. There's no need for a separate error message for each request, so this patch ratelimits it. In the specific case this was found, the system was virtual and the error messages were logged to the serial port, which overwhelmed it. Fixes: 4d48a542b427 ("nbd: fix I/O hang on disconnected nbds") Signed-off-by: Dan Streetman Signed-off-by: Markus Pargmann --- drivers/block/nbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index b67500d5b338..4c5d94146aa3 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -580,8 +580,8 @@ static void nbd_request_handler(struct request_queue *q) req, req->cmd_type); if (unlikely(!nbd->sock)) { - dev_err(disk_to_dev(nbd->disk), - "Attempted send on closed socket\n"); + dev_err_ratelimited(disk_to_dev(nbd->disk), + "Attempted send on closed socket\n"); req->errors++; nbd_end_request(nbd, req); spin_lock_irq(q->queue_lock); -- cgit v1.2.3 From 37091fdd831f28a6509008542174ed324dd645bc Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 27 Jul 2015 07:36:49 +0200 Subject: nbd: Create size change events for userspace 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 --- drivers/block/nbd.c | 79 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 21 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 4c5d94146aa3..f6b51d76e578 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -100,6 +100,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) { @@ -112,6 +117,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; @@ -401,7 +442,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; @@ -421,6 +462,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)) { @@ -431,6 +474,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); nbd->task_recv = NULL; @@ -707,20 +752,19 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, return err; } - 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; @@ -736,13 +780,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; int error; @@ -764,7 +801,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); -- cgit v1.2.3