diff options
author | Pavel Machek <pavel@suse.cz> | 2009-04-02 19:58:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-02 22:05:02 -0400 |
commit | 1a2ad21128bb4eb79f3c05e5801edcc5ed3ef1d3 (patch) | |
tree | ba5845f5e7f225b427d4ce250b911b9303aa9511 /drivers/block | |
parent | 1b0f7ffd0ea27cd3a0b9ca04e3df9522048c32a3 (diff) |
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: Pavel Machek <pavel@suse.cz>
Acked-by: Paul Clements <paul.clements@steeleye.com>
Cc: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/nbd.c | 102 |
1 files changed, 67 insertions, 35 deletions
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 8299e2d3b611..5e982814797d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c | |||
@@ -568,27 +568,17 @@ static void do_nbd_request(struct request_queue * q) | |||
568 | } | 568 | } |
569 | } | 569 | } |
570 | 570 | ||
571 | static int nbd_ioctl(struct block_device *bdev, fmode_t mode, | 571 | /* Must be called with tx_lock held */ |
572 | unsigned int cmd, unsigned long arg) | ||
573 | { | ||
574 | struct nbd_device *lo = bdev->bd_disk->private_data; | ||
575 | struct file *file; | ||
576 | int error; | ||
577 | struct request sreq ; | ||
578 | struct task_struct *thread; | ||
579 | |||
580 | if (!capable(CAP_SYS_ADMIN)) | ||
581 | return -EPERM; | ||
582 | |||
583 | BUG_ON(lo->magic != LO_MAGIC); | ||
584 | |||
585 | /* Anyone capable of this syscall can do *real bad* things */ | ||
586 | dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n", | ||
587 | lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg); | ||
588 | 572 | ||
573 | static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, | ||
574 | unsigned int cmd, unsigned long arg) | ||
575 | { | ||
589 | switch (cmd) { | 576 | switch (cmd) { |
590 | case NBD_DISCONNECT: | 577 | case NBD_DISCONNECT: { |
578 | struct request sreq; | ||
579 | |||
591 | printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); | 580 | printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); |
581 | |||
592 | blk_rq_init(NULL, &sreq); | 582 | blk_rq_init(NULL, &sreq); |
593 | sreq.cmd_type = REQ_TYPE_SPECIAL; | 583 | sreq.cmd_type = REQ_TYPE_SPECIAL; |
594 | nbd_cmd(&sreq) = NBD_CMD_DISC; | 584 | nbd_cmd(&sreq) = NBD_CMD_DISC; |
@@ -599,29 +589,29 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, | |||
599 | */ | 589 | */ |
600 | sreq.sector = 0; | 590 | sreq.sector = 0; |
601 | sreq.nr_sectors = 0; | 591 | sreq.nr_sectors = 0; |
602 | if (!lo->sock) | 592 | if (!lo->sock) |
603 | return -EINVAL; | 593 | return -EINVAL; |
604 | mutex_lock(&lo->tx_lock); | 594 | nbd_send_req(lo, &sreq); |
605 | nbd_send_req(lo, &sreq); | ||
606 | mutex_unlock(&lo->tx_lock); | ||
607 | return 0; | 595 | return 0; |
596 | } | ||
608 | 597 | ||
609 | case NBD_CLEAR_SOCK: | 598 | case NBD_CLEAR_SOCK: { |
610 | error = 0; | 599 | struct file *file; |
611 | mutex_lock(&lo->tx_lock); | 600 | |
612 | lo->sock = NULL; | 601 | lo->sock = NULL; |
613 | mutex_unlock(&lo->tx_lock); | ||
614 | file = lo->file; | 602 | file = lo->file; |
615 | lo->file = NULL; | 603 | lo->file = NULL; |
616 | nbd_clear_que(lo); | 604 | nbd_clear_que(lo); |
617 | BUG_ON(!list_empty(&lo->queue_head)); | 605 | BUG_ON(!list_empty(&lo->queue_head)); |
618 | if (file) | 606 | if (file) |
619 | fput(file); | 607 | fput(file); |
620 | return error; | 608 | return 0; |
621 | case NBD_SET_SOCK: | 609 | } |
610 | |||
611 | case NBD_SET_SOCK: { | ||
612 | struct file *file; | ||
622 | if (lo->file) | 613 | if (lo->file) |
623 | return -EBUSY; | 614 | return -EBUSY; |
624 | error = -EINVAL; | ||
625 | file = fget(arg); | 615 | file = fget(arg); |
626 | if (file) { | 616 | if (file) { |
627 | struct inode *inode = file->f_path.dentry->d_inode; | 617 | 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 | lo->sock = SOCKET_I(inode); | 620 | lo->sock = SOCKET_I(inode); |
631 | if (max_part > 0) | 621 | if (max_part > 0) |
632 | bdev->bd_invalidated = 1; | 622 | bdev->bd_invalidated = 1; |
633 | error = 0; | 623 | return 0; |
634 | } else { | 624 | } else { |
635 | fput(file); | 625 | fput(file); |
636 | } | 626 | } |
637 | } | 627 | } |
638 | return error; | 628 | return -EINVAL; |
629 | } | ||
630 | |||
639 | case NBD_SET_BLKSIZE: | 631 | case NBD_SET_BLKSIZE: |
640 | lo->blksize = arg; | 632 | lo->blksize = arg; |
641 | lo->bytesize &= ~(lo->blksize-1); | 633 | lo->bytesize &= ~(lo->blksize-1); |
@@ -643,35 +635,50 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, | |||
643 | set_blocksize(bdev, lo->blksize); | 635 | set_blocksize(bdev, lo->blksize); |
644 | set_capacity(lo->disk, lo->bytesize >> 9); | 636 | set_capacity(lo->disk, lo->bytesize >> 9); |
645 | return 0; | 637 | return 0; |
638 | |||
646 | case NBD_SET_SIZE: | 639 | case NBD_SET_SIZE: |
647 | lo->bytesize = arg & ~(lo->blksize-1); | 640 | lo->bytesize = arg & ~(lo->blksize-1); |
648 | bdev->bd_inode->i_size = lo->bytesize; | 641 | bdev->bd_inode->i_size = lo->bytesize; |
649 | set_blocksize(bdev, lo->blksize); | 642 | set_blocksize(bdev, lo->blksize); |
650 | set_capacity(lo->disk, lo->bytesize >> 9); | 643 | set_capacity(lo->disk, lo->bytesize >> 9); |
651 | return 0; | 644 | return 0; |
645 | |||
652 | case NBD_SET_TIMEOUT: | 646 | case NBD_SET_TIMEOUT: |
653 | lo->xmit_timeout = arg * HZ; | 647 | lo->xmit_timeout = arg * HZ; |
654 | return 0; | 648 | return 0; |
649 | |||
655 | case NBD_SET_SIZE_BLOCKS: | 650 | case NBD_SET_SIZE_BLOCKS: |
656 | lo->bytesize = ((u64) arg) * lo->blksize; | 651 | lo->bytesize = ((u64) arg) * lo->blksize; |
657 | bdev->bd_inode->i_size = lo->bytesize; | 652 | bdev->bd_inode->i_size = lo->bytesize; |
658 | set_blocksize(bdev, lo->blksize); | 653 | set_blocksize(bdev, lo->blksize); |
659 | set_capacity(lo->disk, lo->bytesize >> 9); | 654 | set_capacity(lo->disk, lo->bytesize >> 9); |
660 | return 0; | 655 | return 0; |
661 | case NBD_DO_IT: | 656 | |
657 | case NBD_DO_IT: { | ||
658 | struct task_struct *thread; | ||
659 | struct file *file; | ||
660 | int error; | ||
661 | |||
662 | if (lo->pid) | 662 | if (lo->pid) |
663 | return -EBUSY; | 663 | return -EBUSY; |
664 | if (!lo->file) | 664 | if (!lo->file) |
665 | return -EINVAL; | 665 | return -EINVAL; |
666 | |||
667 | mutex_unlock(&lo->tx_lock); | ||
668 | |||
666 | thread = kthread_create(nbd_thread, lo, lo->disk->disk_name); | 669 | thread = kthread_create(nbd_thread, lo, lo->disk->disk_name); |
667 | if (IS_ERR(thread)) | 670 | if (IS_ERR(thread)) { |
671 | mutex_lock(&lo->tx_lock); | ||
668 | return PTR_ERR(thread); | 672 | return PTR_ERR(thread); |
673 | } | ||
669 | wake_up_process(thread); | 674 | wake_up_process(thread); |
670 | error = nbd_do_it(lo); | 675 | error = nbd_do_it(lo); |
671 | kthread_stop(thread); | 676 | kthread_stop(thread); |
677 | |||
678 | mutex_lock(&lo->tx_lock); | ||
672 | if (error) | 679 | if (error) |
673 | return error; | 680 | return error; |
674 | sock_shutdown(lo, 1); | 681 | sock_shutdown(lo, 0); |
675 | file = lo->file; | 682 | file = lo->file; |
676 | lo->file = NULL; | 683 | lo->file = NULL; |
677 | nbd_clear_que(lo); | 684 | nbd_clear_que(lo); |
@@ -684,6 +691,8 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, | |||
684 | if (max_part > 0) | 691 | if (max_part > 0) |
685 | ioctl_by_bdev(bdev, BLKRRPART, 0); | 692 | ioctl_by_bdev(bdev, BLKRRPART, 0); |
686 | return lo->harderror; | 693 | return lo->harderror; |
694 | } | ||
695 | |||
687 | case NBD_CLEAR_QUE: | 696 | case NBD_CLEAR_QUE: |
688 | /* | 697 | /* |
689 | * This is for compatibility only. The queue is always cleared | 698 | * 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 | */ | 700 | */ |
692 | BUG_ON(!lo->sock && !list_empty(&lo->queue_head)); | 701 | BUG_ON(!lo->sock && !list_empty(&lo->queue_head)); |
693 | return 0; | 702 | return 0; |
703 | |||
694 | case NBD_PRINT_DEBUG: | 704 | case NBD_PRINT_DEBUG: |
695 | printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", | 705 | printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", |
696 | bdev->bd_disk->disk_name, | 706 | bdev->bd_disk->disk_name, |
@@ -698,7 +708,29 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, | |||
698 | &lo->queue_head); | 708 | &lo->queue_head); |
699 | return 0; | 709 | return 0; |
700 | } | 710 | } |
701 | return -EINVAL; | 711 | return -ENOTTY; |
712 | } | ||
713 | |||
714 | static int nbd_ioctl(struct block_device *bdev, fmode_t mode, | ||
715 | unsigned int cmd, unsigned long arg) | ||
716 | { | ||
717 | struct nbd_device *lo = bdev->bd_disk->private_data; | ||
718 | int error; | ||
719 | |||
720 | if (!capable(CAP_SYS_ADMIN)) | ||
721 | return -EPERM; | ||
722 | |||
723 | BUG_ON(lo->magic != LO_MAGIC); | ||
724 | |||
725 | /* Anyone capable of this syscall can do *real bad* things */ | ||
726 | dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n", | ||
727 | lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg); | ||
728 | |||
729 | mutex_lock(&lo->tx_lock); | ||
730 | error = __nbd_ioctl(bdev, lo, cmd, arg); | ||
731 | mutex_unlock(&lo->tx_lock); | ||
732 | |||
733 | return error; | ||
702 | } | 734 | } |
703 | 735 | ||
704 | static struct block_device_operations nbd_fops = | 736 | static struct block_device_operations nbd_fops = |