Commit 1a2ad211 authored by Pavel Machek's avatar Pavel Machek Committed by Linus Torvalds

nbd: add locking to nbd_ioctl

The code was written to rely on big kernel lock to protect it from races.
It mostly works when interface is not abused.

So this uses tx_lock to protect data structures from concurrent use
between ioctl and worker threads.

Next step will be moving from ioctl to unlocked_ioctl.

[akpm@linux-foundation.org: coding-style fixes]
[akpm@linux-foundation.org: add missing return]
Signed-off-by: default avatarPavel Machek <pavel@suse.cz>
Acked-by: default avatarPaul Clements <paul.clements@steeleye.com>
Cc: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1b0f7ffd
...@@ -568,27 +568,17 @@ static void do_nbd_request(struct request_queue * q) ...@@ -568,27 +568,17 @@ static void do_nbd_request(struct request_queue * q)
} }
} }
static int nbd_ioctl(struct block_device *bdev, fmode_t mode, /* Must be called with tx_lock held */
static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct nbd_device *lo = bdev->bd_disk->private_data;
struct file *file;
int error;
struct request sreq ;
struct task_struct *thread;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
BUG_ON(lo->magic != LO_MAGIC);
/* Anyone capable of this syscall can do *real bad* things */
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
switch (cmd) { switch (cmd) {
case NBD_DISCONNECT: case NBD_DISCONNECT: {
struct request sreq;
printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
blk_rq_init(NULL, &sreq); blk_rq_init(NULL, &sreq);
sreq.cmd_type = REQ_TYPE_SPECIAL; sreq.cmd_type = REQ_TYPE_SPECIAL;
nbd_cmd(&sreq) = NBD_CMD_DISC; nbd_cmd(&sreq) = NBD_CMD_DISC;
...@@ -601,27 +591,27 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -601,27 +591,27 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
sreq.nr_sectors = 0; sreq.nr_sectors = 0;
if (!lo->sock) if (!lo->sock)
return -EINVAL; return -EINVAL;
mutex_lock(&lo->tx_lock);
nbd_send_req(lo, &sreq); nbd_send_req(lo, &sreq);
mutex_unlock(&lo->tx_lock);
return 0; return 0;
}
case NBD_CLEAR_SOCK: {
struct file *file;
case NBD_CLEAR_SOCK:
error = 0;
mutex_lock(&lo->tx_lock);
lo->sock = NULL; lo->sock = NULL;
mutex_unlock(&lo->tx_lock);
file = lo->file; file = lo->file;
lo->file = NULL; lo->file = NULL;
nbd_clear_que(lo); nbd_clear_que(lo);
BUG_ON(!list_empty(&lo->queue_head)); BUG_ON(!list_empty(&lo->queue_head));
if (file) if (file)
fput(file); fput(file);
return error; return 0;
case NBD_SET_SOCK: }
case NBD_SET_SOCK: {
struct file *file;
if (lo->file) if (lo->file)
return -EBUSY; return -EBUSY;
error = -EINVAL;
file = fget(arg); file = fget(arg);
if (file) { if (file) {
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
...@@ -630,12 +620,14 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -630,12 +620,14 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
lo->sock = SOCKET_I(inode); lo->sock = SOCKET_I(inode);
if (max_part > 0) if (max_part > 0)
bdev->bd_invalidated = 1; bdev->bd_invalidated = 1;
error = 0; return 0;
} else { } else {
fput(file); fput(file);
} }
} }
return error; return -EINVAL;
}
case NBD_SET_BLKSIZE: case NBD_SET_BLKSIZE:
lo->blksize = arg; lo->blksize = arg;
lo->bytesize &= ~(lo->blksize-1); lo->bytesize &= ~(lo->blksize-1);
...@@ -643,35 +635,50 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -643,35 +635,50 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
set_blocksize(bdev, lo->blksize); set_blocksize(bdev, lo->blksize);
set_capacity(lo->disk, lo->bytesize >> 9); set_capacity(lo->disk, lo->bytesize >> 9);
return 0; return 0;
case NBD_SET_SIZE: case NBD_SET_SIZE:
lo->bytesize = arg & ~(lo->blksize-1); lo->bytesize = arg & ~(lo->blksize-1);
bdev->bd_inode->i_size = lo->bytesize; bdev->bd_inode->i_size = lo->bytesize;
set_blocksize(bdev, lo->blksize); set_blocksize(bdev, lo->blksize);
set_capacity(lo->disk, lo->bytesize >> 9); set_capacity(lo->disk, lo->bytesize >> 9);
return 0; return 0;
case NBD_SET_TIMEOUT: case NBD_SET_TIMEOUT:
lo->xmit_timeout = arg * HZ; lo->xmit_timeout = arg * HZ;
return 0; return 0;
case NBD_SET_SIZE_BLOCKS: case NBD_SET_SIZE_BLOCKS:
lo->bytesize = ((u64) arg) * lo->blksize; lo->bytesize = ((u64) arg) * lo->blksize;
bdev->bd_inode->i_size = lo->bytesize; bdev->bd_inode->i_size = lo->bytesize;
set_blocksize(bdev, lo->blksize); set_blocksize(bdev, lo->blksize);
set_capacity(lo->disk, lo->bytesize >> 9); set_capacity(lo->disk, lo->bytesize >> 9);
return 0; return 0;
case NBD_DO_IT:
case NBD_DO_IT: {
struct task_struct *thread;
struct file *file;
int error;
if (lo->pid) if (lo->pid)
return -EBUSY; return -EBUSY;
if (!lo->file) if (!lo->file)
return -EINVAL; return -EINVAL;
mutex_unlock(&lo->tx_lock);
thread = kthread_create(nbd_thread, lo, lo->disk->disk_name); thread = kthread_create(nbd_thread, lo, lo->disk->disk_name);
if (IS_ERR(thread)) if (IS_ERR(thread)) {
mutex_lock(&lo->tx_lock);
return PTR_ERR(thread); return PTR_ERR(thread);
}
wake_up_process(thread); wake_up_process(thread);
error = nbd_do_it(lo); error = nbd_do_it(lo);
kthread_stop(thread); kthread_stop(thread);
mutex_lock(&lo->tx_lock);
if (error) if (error)
return error; return error;
sock_shutdown(lo, 1); sock_shutdown(lo, 0);
file = lo->file; file = lo->file;
lo->file = NULL; lo->file = NULL;
nbd_clear_que(lo); nbd_clear_que(lo);
...@@ -684,6 +691,8 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -684,6 +691,8 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
if (max_part > 0) if (max_part > 0)
ioctl_by_bdev(bdev, BLKRRPART, 0); ioctl_by_bdev(bdev, BLKRRPART, 0);
return lo->harderror; return lo->harderror;
}
case NBD_CLEAR_QUE: case NBD_CLEAR_QUE:
/* /*
* This is for compatibility only. The queue is always cleared * This is for compatibility only. The queue is always cleared
...@@ -691,6 +700,7 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -691,6 +700,7 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
*/ */
BUG_ON(!lo->sock && !list_empty(&lo->queue_head)); BUG_ON(!lo->sock && !list_empty(&lo->queue_head));
return 0; return 0;
case NBD_PRINT_DEBUG: case NBD_PRINT_DEBUG:
printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n",
bdev->bd_disk->disk_name, bdev->bd_disk->disk_name,
...@@ -698,7 +708,29 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -698,7 +708,29 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
&lo->queue_head); &lo->queue_head);
return 0; return 0;
} }
return -EINVAL; return -ENOTTY;
}
static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct nbd_device *lo = bdev->bd_disk->private_data;
int error;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
BUG_ON(lo->magic != LO_MAGIC);
/* Anyone capable of this syscall can do *real bad* things */
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
mutex_lock(&lo->tx_lock);
error = __nbd_ioctl(bdev, lo, cmd, arg);
mutex_unlock(&lo->tx_lock);
return error;
} }
static struct block_device_operations nbd_fops = static struct block_device_operations nbd_fops =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment