diff options
Diffstat (limited to 'drivers/block/nbd.c')
-rw-r--r-- | drivers/block/nbd.c | 171 |
1 files changed, 117 insertions, 54 deletions
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index f75bda16a1fc..ad98dda6037d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <net/sock.h> | 30 | #include <net/sock.h> |
31 | #include <linux/net.h> | 31 | #include <linux/net.h> |
32 | #include <linux/kthread.h> | ||
32 | 33 | ||
33 | #include <asm/uaccess.h> | 34 | #include <asm/uaccess.h> |
34 | #include <asm/system.h> | 35 | #include <asm/system.h> |
@@ -55,6 +56,7 @@ static unsigned int debugflags; | |||
55 | 56 | ||
56 | static unsigned int nbds_max = 16; | 57 | static unsigned int nbds_max = 16; |
57 | static struct nbd_device *nbd_dev; | 58 | static struct nbd_device *nbd_dev; |
59 | static int max_part; | ||
58 | 60 | ||
59 | /* | 61 | /* |
60 | * Use just one lock (or at most 1 per NIC). Two arguments for this: | 62 | * Use just one lock (or at most 1 per NIC). Two arguments for this: |
@@ -337,7 +339,7 @@ static struct request *nbd_read_stat(struct nbd_device *lo) | |||
337 | } | 339 | } |
338 | 340 | ||
339 | req = nbd_find_request(lo, *(struct request **)reply.handle); | 341 | req = nbd_find_request(lo, *(struct request **)reply.handle); |
340 | if (unlikely(IS_ERR(req))) { | 342 | if (IS_ERR(req)) { |
341 | result = PTR_ERR(req); | 343 | result = PTR_ERR(req); |
342 | if (result != -ENOENT) | 344 | if (result != -ENOENT) |
343 | goto harderror; | 345 | goto harderror; |
@@ -441,6 +443,85 @@ static void nbd_clear_que(struct nbd_device *lo) | |||
441 | } | 443 | } |
442 | 444 | ||
443 | 445 | ||
446 | static void nbd_handle_req(struct nbd_device *lo, struct request *req) | ||
447 | { | ||
448 | if (!blk_fs_request(req)) | ||
449 | goto error_out; | ||
450 | |||
451 | nbd_cmd(req) = NBD_CMD_READ; | ||
452 | if (rq_data_dir(req) == WRITE) { | ||
453 | nbd_cmd(req) = NBD_CMD_WRITE; | ||
454 | if (lo->flags & NBD_READ_ONLY) { | ||
455 | printk(KERN_ERR "%s: Write on read-only\n", | ||
456 | lo->disk->disk_name); | ||
457 | goto error_out; | ||
458 | } | ||
459 | } | ||
460 | |||
461 | req->errors = 0; | ||
462 | |||
463 | mutex_lock(&lo->tx_lock); | ||
464 | if (unlikely(!lo->sock)) { | ||
465 | mutex_unlock(&lo->tx_lock); | ||
466 | printk(KERN_ERR "%s: Attempted send on closed socket\n", | ||
467 | lo->disk->disk_name); | ||
468 | req->errors++; | ||
469 | nbd_end_request(req); | ||
470 | return; | ||
471 | } | ||
472 | |||
473 | lo->active_req = req; | ||
474 | |||
475 | if (nbd_send_req(lo, req) != 0) { | ||
476 | printk(KERN_ERR "%s: Request send failed\n", | ||
477 | lo->disk->disk_name); | ||
478 | req->errors++; | ||
479 | nbd_end_request(req); | ||
480 | } else { | ||
481 | spin_lock(&lo->queue_lock); | ||
482 | list_add(&req->queuelist, &lo->queue_head); | ||
483 | spin_unlock(&lo->queue_lock); | ||
484 | } | ||
485 | |||
486 | lo->active_req = NULL; | ||
487 | mutex_unlock(&lo->tx_lock); | ||
488 | wake_up_all(&lo->active_wq); | ||
489 | |||
490 | return; | ||
491 | |||
492 | error_out: | ||
493 | req->errors++; | ||
494 | nbd_end_request(req); | ||
495 | } | ||
496 | |||
497 | static int nbd_thread(void *data) | ||
498 | { | ||
499 | struct nbd_device *lo = data; | ||
500 | struct request *req; | ||
501 | |||
502 | set_user_nice(current, -20); | ||
503 | while (!kthread_should_stop() || !list_empty(&lo->waiting_queue)) { | ||
504 | /* wait for something to do */ | ||
505 | wait_event_interruptible(lo->waiting_wq, | ||
506 | kthread_should_stop() || | ||
507 | !list_empty(&lo->waiting_queue)); | ||
508 | |||
509 | /* extract request */ | ||
510 | if (list_empty(&lo->waiting_queue)) | ||
511 | continue; | ||
512 | |||
513 | spin_lock_irq(&lo->queue_lock); | ||
514 | req = list_entry(lo->waiting_queue.next, struct request, | ||
515 | queuelist); | ||
516 | list_del_init(&req->queuelist); | ||
517 | spin_unlock_irq(&lo->queue_lock); | ||
518 | |||
519 | /* handle request */ | ||
520 | nbd_handle_req(lo, req); | ||
521 | } | ||
522 | return 0; | ||
523 | } | ||
524 | |||
444 | /* | 525 | /* |
445 | * We always wait for result of write, for now. It would be nice to make it optional | 526 | * We always wait for result of write, for now. It would be nice to make it optional |
446 | * in future | 527 | * in future |
@@ -456,65 +537,23 @@ static void do_nbd_request(struct request_queue * q) | |||
456 | struct nbd_device *lo; | 537 | struct nbd_device *lo; |
457 | 538 | ||
458 | blkdev_dequeue_request(req); | 539 | blkdev_dequeue_request(req); |
540 | |||
541 | spin_unlock_irq(q->queue_lock); | ||
542 | |||
459 | dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n", | 543 | dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n", |
460 | req->rq_disk->disk_name, req, req->cmd_type); | 544 | req->rq_disk->disk_name, req, req->cmd_type); |
461 | 545 | ||
462 | if (!blk_fs_request(req)) | ||
463 | goto error_out; | ||
464 | |||
465 | lo = req->rq_disk->private_data; | 546 | lo = req->rq_disk->private_data; |
466 | 547 | ||
467 | BUG_ON(lo->magic != LO_MAGIC); | 548 | BUG_ON(lo->magic != LO_MAGIC); |
468 | 549 | ||
469 | nbd_cmd(req) = NBD_CMD_READ; | 550 | spin_lock_irq(&lo->queue_lock); |
470 | if (rq_data_dir(req) == WRITE) { | 551 | list_add_tail(&req->queuelist, &lo->waiting_queue); |
471 | nbd_cmd(req) = NBD_CMD_WRITE; | 552 | spin_unlock_irq(&lo->queue_lock); |
472 | if (lo->flags & NBD_READ_ONLY) { | ||
473 | printk(KERN_ERR "%s: Write on read-only\n", | ||
474 | lo->disk->disk_name); | ||
475 | goto error_out; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | req->errors = 0; | ||
480 | spin_unlock_irq(q->queue_lock); | ||
481 | |||
482 | mutex_lock(&lo->tx_lock); | ||
483 | if (unlikely(!lo->sock)) { | ||
484 | mutex_unlock(&lo->tx_lock); | ||
485 | printk(KERN_ERR "%s: Attempted send on closed socket\n", | ||
486 | lo->disk->disk_name); | ||
487 | req->errors++; | ||
488 | nbd_end_request(req); | ||
489 | spin_lock_irq(q->queue_lock); | ||
490 | continue; | ||
491 | } | ||
492 | |||
493 | lo->active_req = req; | ||
494 | 553 | ||
495 | if (nbd_send_req(lo, req) != 0) { | 554 | wake_up(&lo->waiting_wq); |
496 | printk(KERN_ERR "%s: Request send failed\n", | ||
497 | lo->disk->disk_name); | ||
498 | req->errors++; | ||
499 | nbd_end_request(req); | ||
500 | } else { | ||
501 | spin_lock(&lo->queue_lock); | ||
502 | list_add(&req->queuelist, &lo->queue_head); | ||
503 | spin_unlock(&lo->queue_lock); | ||
504 | } | ||
505 | |||
506 | lo->active_req = NULL; | ||
507 | mutex_unlock(&lo->tx_lock); | ||
508 | wake_up_all(&lo->active_wq); | ||
509 | 555 | ||
510 | spin_lock_irq(q->queue_lock); | 556 | spin_lock_irq(q->queue_lock); |
511 | continue; | ||
512 | |||
513 | error_out: | ||
514 | req->errors++; | ||
515 | spin_unlock(q->queue_lock); | ||
516 | nbd_end_request(req); | ||
517 | spin_lock(q->queue_lock); | ||
518 | } | 557 | } |
519 | } | 558 | } |
520 | 559 | ||
@@ -524,6 +563,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file, | |||
524 | struct nbd_device *lo = inode->i_bdev->bd_disk->private_data; | 563 | struct nbd_device *lo = inode->i_bdev->bd_disk->private_data; |
525 | int error; | 564 | int error; |
526 | struct request sreq ; | 565 | struct request sreq ; |
566 | struct task_struct *thread; | ||
527 | 567 | ||
528 | if (!capable(CAP_SYS_ADMIN)) | 568 | if (!capable(CAP_SYS_ADMIN)) |
529 | return -EPERM; | 569 | return -EPERM; |
@@ -572,10 +612,13 @@ static int nbd_ioctl(struct inode *inode, struct file *file, | |||
572 | error = -EINVAL; | 612 | error = -EINVAL; |
573 | file = fget(arg); | 613 | file = fget(arg); |
574 | if (file) { | 614 | if (file) { |
615 | struct block_device *bdev = inode->i_bdev; | ||
575 | inode = file->f_path.dentry->d_inode; | 616 | inode = file->f_path.dentry->d_inode; |
576 | if (S_ISSOCK(inode->i_mode)) { | 617 | if (S_ISSOCK(inode->i_mode)) { |
577 | lo->file = file; | 618 | lo->file = file; |
578 | lo->sock = SOCKET_I(inode); | 619 | lo->sock = SOCKET_I(inode); |
620 | if (max_part > 0) | ||
621 | bdev->bd_invalidated = 1; | ||
579 | error = 0; | 622 | error = 0; |
580 | } else { | 623 | } else { |
581 | fput(file); | 624 | fput(file); |
@@ -607,7 +650,12 @@ static int nbd_ioctl(struct inode *inode, struct file *file, | |||
607 | case NBD_DO_IT: | 650 | case NBD_DO_IT: |
608 | if (!lo->file) | 651 | if (!lo->file) |
609 | return -EINVAL; | 652 | return -EINVAL; |
653 | thread = kthread_create(nbd_thread, lo, lo->disk->disk_name); | ||
654 | if (IS_ERR(thread)) | ||
655 | return PTR_ERR(thread); | ||
656 | wake_up_process(thread); | ||
610 | error = nbd_do_it(lo); | 657 | error = nbd_do_it(lo); |
658 | kthread_stop(thread); | ||
611 | if (error) | 659 | if (error) |
612 | return error; | 660 | return error; |
613 | sock_shutdown(lo, 1); | 661 | sock_shutdown(lo, 1); |
@@ -620,6 +668,8 @@ static int nbd_ioctl(struct inode *inode, struct file *file, | |||
620 | lo->bytesize = 0; | 668 | lo->bytesize = 0; |
621 | inode->i_bdev->bd_inode->i_size = 0; | 669 | inode->i_bdev->bd_inode->i_size = 0; |
622 | set_capacity(lo->disk, 0); | 670 | set_capacity(lo->disk, 0); |
671 | if (max_part > 0) | ||
672 | ioctl_by_bdev(inode->i_bdev, BLKRRPART, 0); | ||
623 | return lo->harderror; | 673 | return lo->harderror; |
624 | case NBD_CLEAR_QUE: | 674 | case NBD_CLEAR_QUE: |
625 | /* | 675 | /* |
@@ -653,6 +703,7 @@ static int __init nbd_init(void) | |||
653 | { | 703 | { |
654 | int err = -ENOMEM; | 704 | int err = -ENOMEM; |
655 | int i; | 705 | int i; |
706 | int part_shift; | ||
656 | 707 | ||
657 | BUILD_BUG_ON(sizeof(struct nbd_request) != 28); | 708 | BUILD_BUG_ON(sizeof(struct nbd_request) != 28); |
658 | 709 | ||
@@ -660,8 +711,17 @@ static int __init nbd_init(void) | |||
660 | if (!nbd_dev) | 711 | if (!nbd_dev) |
661 | return -ENOMEM; | 712 | return -ENOMEM; |
662 | 713 | ||
714 | if (max_part < 0) { | ||
715 | printk(KERN_CRIT "nbd: max_part must be >= 0\n"); | ||
716 | return -EINVAL; | ||
717 | } | ||
718 | |||
719 | part_shift = 0; | ||
720 | if (max_part > 0) | ||
721 | part_shift = fls(max_part); | ||
722 | |||
663 | for (i = 0; i < nbds_max; i++) { | 723 | for (i = 0; i < nbds_max; i++) { |
664 | struct gendisk *disk = alloc_disk(1); | 724 | struct gendisk *disk = alloc_disk(1 << part_shift); |
665 | elevator_t *old_e; | 725 | elevator_t *old_e; |
666 | if (!disk) | 726 | if (!disk) |
667 | goto out; | 727 | goto out; |
@@ -696,17 +756,18 @@ static int __init nbd_init(void) | |||
696 | nbd_dev[i].file = NULL; | 756 | nbd_dev[i].file = NULL; |
697 | nbd_dev[i].magic = LO_MAGIC; | 757 | nbd_dev[i].magic = LO_MAGIC; |
698 | nbd_dev[i].flags = 0; | 758 | nbd_dev[i].flags = 0; |
759 | INIT_LIST_HEAD(&nbd_dev[i].waiting_queue); | ||
699 | spin_lock_init(&nbd_dev[i].queue_lock); | 760 | spin_lock_init(&nbd_dev[i].queue_lock); |
700 | INIT_LIST_HEAD(&nbd_dev[i].queue_head); | 761 | INIT_LIST_HEAD(&nbd_dev[i].queue_head); |
701 | mutex_init(&nbd_dev[i].tx_lock); | 762 | mutex_init(&nbd_dev[i].tx_lock); |
702 | init_waitqueue_head(&nbd_dev[i].active_wq); | 763 | init_waitqueue_head(&nbd_dev[i].active_wq); |
764 | init_waitqueue_head(&nbd_dev[i].waiting_wq); | ||
703 | nbd_dev[i].blksize = 1024; | 765 | nbd_dev[i].blksize = 1024; |
704 | nbd_dev[i].bytesize = 0; | 766 | nbd_dev[i].bytesize = 0; |
705 | disk->major = NBD_MAJOR; | 767 | disk->major = NBD_MAJOR; |
706 | disk->first_minor = i; | 768 | disk->first_minor = i << part_shift; |
707 | disk->fops = &nbd_fops; | 769 | disk->fops = &nbd_fops; |
708 | disk->private_data = &nbd_dev[i]; | 770 | disk->private_data = &nbd_dev[i]; |
709 | disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO; | ||
710 | sprintf(disk->disk_name, "nbd%d", i); | 771 | sprintf(disk->disk_name, "nbd%d", i); |
711 | set_capacity(disk, 0); | 772 | set_capacity(disk, 0); |
712 | add_disk(disk); | 773 | add_disk(disk); |
@@ -744,7 +805,9 @@ MODULE_DESCRIPTION("Network Block Device"); | |||
744 | MODULE_LICENSE("GPL"); | 805 | MODULE_LICENSE("GPL"); |
745 | 806 | ||
746 | module_param(nbds_max, int, 0444); | 807 | module_param(nbds_max, int, 0444); |
747 | MODULE_PARM_DESC(nbds_max, "How many network block devices to initialize."); | 808 | MODULE_PARM_DESC(nbds_max, "number of network block devices to initialize (default: 16)"); |
809 | module_param(max_part, int, 0444); | ||
810 | MODULE_PARM_DESC(max_part, "number of partitions per device (default: 0)"); | ||
748 | #ifndef NDEBUG | 811 | #ifndef NDEBUG |
749 | module_param(debugflags, int, 0644); | 812 | module_param(debugflags, int, 0644); |
750 | MODULE_PARM_DESC(debugflags, "flags for controlling debug output"); | 813 | MODULE_PARM_DESC(debugflags, "flags for controlling debug output"); |