diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2011-07-31 16:08:04 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2011-07-31 16:08:04 -0400 |
commit | 770fe30a46a12b6fb6b63fbe1737654d28e84844 (patch) | |
tree | e8a35712602ca086c41f94d191579ac2dc365cf5 /drivers/block | |
parent | 34dd82afd27da2537199d7f71f1542501c6f96e7 (diff) |
loop: add management interface for on-demand device allocation
Loop devices today have a fixed pre-allocated number of usually 8.
The number can only be changed at module init time. To find a free
device to use, /dev/loop%i needs to be scanned, and all devices need
to be opened until a free one is possibly found.
This adds a new /dev/loop-control device node, that allows to
dynamically find or allocate a free device, and to add and remove loop
devices from the running system:
LOOP_CTL_ADD adds a specific device. Arg is the number
of the device. It returns the device i or a negative
error code.
LOOP_CTL_REMOVE removes a specific device, Arg is the
number the device. It returns the device i or a negative
error code.
LOOP_CTL_GET_FREE finds the next unbound device or allocates
a new one. No arg is given. It returns the device i or a
negative error code.
The loop kernel module gets automatically loaded when
/dev/loop-control is accessed the first time. The alias
specified in the module, instructs udev to create this
'dead' device node, even when the module is not loaded.
Example:
cfd = open("/dev/loop-control", O_RDWR);
# add a new specific loop device
err = ioctl(cfd, LOOP_CTL_ADD, devnr);
# remove a specific loop device
err = ioctl(cfd, LOOP_CTL_REMOVE, devnr);
# find or allocate a free loop device to use
devnr = ioctl(cfd, LOOP_CTL_GET_FREE);
sprintf(loopname, "/dev/loop%i", devnr);
ffd = open("backing-file", O_RDWR);
lfd = open(loopname, O_RDWR);
err = ioctl(lfd, LOOP_SET_FD, ffd);
Cc: Tejun Heo <tj@kernel.org>
Cc: Karel Zak <kzak@redhat.com>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/loop.c | 120 |
1 files changed, 116 insertions, 4 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index f58532e77777..5c9edf944879 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -75,7 +75,7 @@ | |||
75 | #include <linux/kthread.h> | 75 | #include <linux/kthread.h> |
76 | #include <linux/splice.h> | 76 | #include <linux/splice.h> |
77 | #include <linux/sysfs.h> | 77 | #include <linux/sysfs.h> |
78 | 78 | #include <linux/miscdevice.h> | |
79 | #include <asm/uaccess.h> | 79 | #include <asm/uaccess.h> |
80 | 80 | ||
81 | static DEFINE_IDR(loop_index_idr); | 81 | static DEFINE_IDR(loop_index_idr); |
@@ -1478,13 +1478,22 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode, | |||
1478 | 1478 | ||
1479 | static int lo_open(struct block_device *bdev, fmode_t mode) | 1479 | static int lo_open(struct block_device *bdev, fmode_t mode) |
1480 | { | 1480 | { |
1481 | struct loop_device *lo = bdev->bd_disk->private_data; | 1481 | struct loop_device *lo; |
1482 | int err = 0; | ||
1483 | |||
1484 | mutex_lock(&loop_index_mutex); | ||
1485 | lo = bdev->bd_disk->private_data; | ||
1486 | if (!lo) { | ||
1487 | err = -ENXIO; | ||
1488 | goto out; | ||
1489 | } | ||
1482 | 1490 | ||
1483 | mutex_lock(&lo->lo_ctl_mutex); | 1491 | mutex_lock(&lo->lo_ctl_mutex); |
1484 | lo->lo_refcnt++; | 1492 | lo->lo_refcnt++; |
1485 | mutex_unlock(&lo->lo_ctl_mutex); | 1493 | mutex_unlock(&lo->lo_ctl_mutex); |
1486 | 1494 | out: | |
1487 | return 0; | 1495 | mutex_unlock(&loop_index_mutex); |
1496 | return err; | ||
1488 | } | 1497 | } |
1489 | 1498 | ||
1490 | static int lo_release(struct gendisk *disk, fmode_t mode) | 1499 | static int lo_release(struct gendisk *disk, fmode_t mode) |
@@ -1603,6 +1612,13 @@ static int loop_add(struct loop_device **l, int i) | |||
1603 | idr_remove(&loop_index_idr, m); | 1612 | idr_remove(&loop_index_idr, m); |
1604 | err = -EEXIST; | 1613 | err = -EEXIST; |
1605 | } | 1614 | } |
1615 | } else if (i == -1) { | ||
1616 | int m; | ||
1617 | |||
1618 | /* get next free nr */ | ||
1619 | err = idr_get_new(&loop_index_idr, lo, &m); | ||
1620 | if (err >= 0) | ||
1621 | i = m; | ||
1606 | } else { | 1622 | } else { |
1607 | err = -EINVAL; | 1623 | err = -EINVAL; |
1608 | } | 1624 | } |
@@ -1648,16 +1664,41 @@ static void loop_remove(struct loop_device *lo) | |||
1648 | kfree(lo); | 1664 | kfree(lo); |
1649 | } | 1665 | } |
1650 | 1666 | ||
1667 | static int find_free_cb(int id, void *ptr, void *data) | ||
1668 | { | ||
1669 | struct loop_device *lo = ptr; | ||
1670 | struct loop_device **l = data; | ||
1671 | |||
1672 | if (lo->lo_state == Lo_unbound) { | ||
1673 | *l = lo; | ||
1674 | return 1; | ||
1675 | } | ||
1676 | return 0; | ||
1677 | } | ||
1678 | |||
1651 | static int loop_lookup(struct loop_device **l, int i) | 1679 | static int loop_lookup(struct loop_device **l, int i) |
1652 | { | 1680 | { |
1653 | struct loop_device *lo; | 1681 | struct loop_device *lo; |
1654 | int ret = -ENODEV; | 1682 | int ret = -ENODEV; |
1655 | 1683 | ||
1684 | if (i < 0) { | ||
1685 | int err; | ||
1686 | |||
1687 | err = idr_for_each(&loop_index_idr, &find_free_cb, &lo); | ||
1688 | if (err == 1) { | ||
1689 | *l = lo; | ||
1690 | ret = lo->lo_number; | ||
1691 | } | ||
1692 | goto out; | ||
1693 | } | ||
1694 | |||
1695 | /* lookup and return a specific i */ | ||
1656 | lo = idr_find(&loop_index_idr, i); | 1696 | lo = idr_find(&loop_index_idr, i); |
1657 | if (lo) { | 1697 | if (lo) { |
1658 | *l = lo; | 1698 | *l = lo; |
1659 | ret = lo->lo_number; | 1699 | ret = lo->lo_number; |
1660 | } | 1700 | } |
1701 | out: | ||
1661 | return ret; | 1702 | return ret; |
1662 | } | 1703 | } |
1663 | 1704 | ||
@@ -1681,11 +1722,76 @@ static struct kobject *loop_probe(dev_t dev, int *part, void *data) | |||
1681 | return kobj; | 1722 | return kobj; |
1682 | } | 1723 | } |
1683 | 1724 | ||
1725 | static long loop_control_ioctl(struct file *file, unsigned int cmd, | ||
1726 | unsigned long parm) | ||
1727 | { | ||
1728 | struct loop_device *lo; | ||
1729 | int ret = -ENOSYS; | ||
1730 | |||
1731 | mutex_lock(&loop_index_mutex); | ||
1732 | switch (cmd) { | ||
1733 | case LOOP_CTL_ADD: | ||
1734 | ret = loop_lookup(&lo, parm); | ||
1735 | if (ret >= 0) { | ||
1736 | ret = -EEXIST; | ||
1737 | break; | ||
1738 | } | ||
1739 | ret = loop_add(&lo, parm); | ||
1740 | break; | ||
1741 | case LOOP_CTL_REMOVE: | ||
1742 | ret = loop_lookup(&lo, parm); | ||
1743 | if (ret < 0) | ||
1744 | break; | ||
1745 | mutex_lock(&lo->lo_ctl_mutex); | ||
1746 | if (lo->lo_state != Lo_unbound) { | ||
1747 | ret = -EBUSY; | ||
1748 | mutex_unlock(&lo->lo_ctl_mutex); | ||
1749 | break; | ||
1750 | } | ||
1751 | if (lo->lo_refcnt > 0) { | ||
1752 | ret = -EBUSY; | ||
1753 | mutex_unlock(&lo->lo_ctl_mutex); | ||
1754 | break; | ||
1755 | } | ||
1756 | lo->lo_disk->private_data = NULL; | ||
1757 | mutex_unlock(&lo->lo_ctl_mutex); | ||
1758 | idr_remove(&loop_index_idr, lo->lo_number); | ||
1759 | loop_remove(lo); | ||
1760 | break; | ||
1761 | case LOOP_CTL_GET_FREE: | ||
1762 | ret = loop_lookup(&lo, -1); | ||
1763 | if (ret >= 0) | ||
1764 | break; | ||
1765 | ret = loop_add(&lo, -1); | ||
1766 | } | ||
1767 | mutex_unlock(&loop_index_mutex); | ||
1768 | |||
1769 | return ret; | ||
1770 | } | ||
1771 | |||
1772 | static const struct file_operations loop_ctl_fops = { | ||
1773 | .open = nonseekable_open, | ||
1774 | .unlocked_ioctl = loop_control_ioctl, | ||
1775 | .compat_ioctl = loop_control_ioctl, | ||
1776 | .owner = THIS_MODULE, | ||
1777 | .llseek = noop_llseek, | ||
1778 | }; | ||
1779 | |||
1780 | static struct miscdevice loop_misc = { | ||
1781 | .minor = LOOP_CTRL_MINOR, | ||
1782 | .name = "loop-control", | ||
1783 | .fops = &loop_ctl_fops, | ||
1784 | }; | ||
1785 | |||
1786 | MODULE_ALIAS_MISCDEV(LOOP_CTRL_MINOR); | ||
1787 | MODULE_ALIAS("devname:loop-control"); | ||
1788 | |||
1684 | static int __init loop_init(void) | 1789 | static int __init loop_init(void) |
1685 | { | 1790 | { |
1686 | int i, nr; | 1791 | int i, nr; |
1687 | unsigned long range; | 1792 | unsigned long range; |
1688 | struct loop_device *lo; | 1793 | struct loop_device *lo; |
1794 | int err; | ||
1689 | 1795 | ||
1690 | /* | 1796 | /* |
1691 | * loop module now has a feature to instantiate underlying device | 1797 | * loop module now has a feature to instantiate underlying device |
@@ -1702,6 +1808,10 @@ static int __init loop_init(void) | |||
1702 | * device on-demand. | 1808 | * device on-demand. |
1703 | */ | 1809 | */ |
1704 | 1810 | ||
1811 | err = misc_register(&loop_misc); | ||
1812 | if (err < 0) | ||
1813 | return err; | ||
1814 | |||
1705 | part_shift = 0; | 1815 | part_shift = 0; |
1706 | if (max_part > 0) { | 1816 | if (max_part > 0) { |
1707 | part_shift = fls(max_part); | 1817 | part_shift = fls(max_part); |
@@ -1767,6 +1877,8 @@ static void __exit loop_exit(void) | |||
1767 | 1877 | ||
1768 | blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range); | 1878 | blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range); |
1769 | unregister_blkdev(LOOP_MAJOR, "loop"); | 1879 | unregister_blkdev(LOOP_MAJOR, "loop"); |
1880 | |||
1881 | misc_deregister(&loop_misc); | ||
1770 | } | 1882 | } |
1771 | 1883 | ||
1772 | module_init(loop_init); | 1884 | module_init(loop_init); |