diff options
| -rw-r--r-- | drivers/block/nbd.c | 104 |
1 files changed, 86 insertions, 18 deletions
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index cb45d799bc5c..4237e7286e99 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c | |||
| @@ -99,10 +99,12 @@ struct nbd_device { | |||
| 99 | 99 | ||
| 100 | int index; | 100 | int index; |
| 101 | refcount_t config_refs; | 101 | refcount_t config_refs; |
| 102 | refcount_t refs; | ||
| 102 | struct nbd_config *config; | 103 | struct nbd_config *config; |
| 103 | struct mutex config_lock; | 104 | struct mutex config_lock; |
| 104 | struct gendisk *disk; | 105 | struct gendisk *disk; |
| 105 | 106 | ||
| 107 | struct list_head list; | ||
| 106 | struct task_struct *task_recv; | 108 | struct task_struct *task_recv; |
| 107 | struct task_struct *task_setup; | 109 | struct task_struct *task_setup; |
| 108 | }; | 110 | }; |
| @@ -165,6 +167,28 @@ static struct device_attribute pid_attr = { | |||
| 165 | .show = pid_show, | 167 | .show = pid_show, |
| 166 | }; | 168 | }; |
| 167 | 169 | ||
| 170 | static void nbd_dev_remove(struct nbd_device *nbd) | ||
| 171 | { | ||
| 172 | struct gendisk *disk = nbd->disk; | ||
| 173 | if (disk) { | ||
| 174 | del_gendisk(disk); | ||
| 175 | blk_cleanup_queue(disk->queue); | ||
| 176 | blk_mq_free_tag_set(&nbd->tag_set); | ||
| 177 | put_disk(disk); | ||
| 178 | } | ||
| 179 | kfree(nbd); | ||
| 180 | } | ||
| 181 | |||
| 182 | static void nbd_put(struct nbd_device *nbd) | ||
| 183 | { | ||
| 184 | if (refcount_dec_and_mutex_lock(&nbd->refs, | ||
| 185 | &nbd_index_mutex)) { | ||
| 186 | idr_remove(&nbd_index_idr, nbd->index); | ||
| 187 | mutex_unlock(&nbd_index_mutex); | ||
| 188 | nbd_dev_remove(nbd); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 168 | static int nbd_disconnected(struct nbd_config *config) | 192 | static int nbd_disconnected(struct nbd_config *config) |
| 169 | { | 193 | { |
| 170 | return test_bit(NBD_DISCONNECTED, &config->runtime_flags) || | 194 | return test_bit(NBD_DISCONNECTED, &config->runtime_flags) || |
| @@ -1005,6 +1029,7 @@ static void nbd_config_put(struct nbd_device *nbd) | |||
| 1005 | } | 1029 | } |
| 1006 | nbd_reset(nbd); | 1030 | nbd_reset(nbd); |
| 1007 | mutex_unlock(&nbd->config_lock); | 1031 | mutex_unlock(&nbd->config_lock); |
| 1032 | nbd_put(nbd); | ||
| 1008 | module_put(THIS_MODULE); | 1033 | module_put(THIS_MODULE); |
| 1009 | } | 1034 | } |
| 1010 | } | 1035 | } |
| @@ -1199,6 +1224,10 @@ static int nbd_open(struct block_device *bdev, fmode_t mode) | |||
| 1199 | ret = -ENXIO; | 1224 | ret = -ENXIO; |
| 1200 | goto out; | 1225 | goto out; |
| 1201 | } | 1226 | } |
| 1227 | if (!refcount_inc_not_zero(&nbd->refs)) { | ||
| 1228 | ret = -ENXIO; | ||
| 1229 | goto out; | ||
| 1230 | } | ||
| 1202 | if (!refcount_inc_not_zero(&nbd->config_refs)) { | 1231 | if (!refcount_inc_not_zero(&nbd->config_refs)) { |
| 1203 | struct nbd_config *config; | 1232 | struct nbd_config *config; |
| 1204 | 1233 | ||
| @@ -1214,6 +1243,7 @@ static int nbd_open(struct block_device *bdev, fmode_t mode) | |||
| 1214 | goto out; | 1243 | goto out; |
| 1215 | } | 1244 | } |
| 1216 | refcount_set(&nbd->config_refs, 1); | 1245 | refcount_set(&nbd->config_refs, 1); |
| 1246 | refcount_inc(&nbd->refs); | ||
| 1217 | mutex_unlock(&nbd->config_lock); | 1247 | mutex_unlock(&nbd->config_lock); |
| 1218 | } | 1248 | } |
| 1219 | out: | 1249 | out: |
| @@ -1225,6 +1255,7 @@ static void nbd_release(struct gendisk *disk, fmode_t mode) | |||
| 1225 | { | 1255 | { |
| 1226 | struct nbd_device *nbd = disk->private_data; | 1256 | struct nbd_device *nbd = disk->private_data; |
| 1227 | nbd_config_put(nbd); | 1257 | nbd_config_put(nbd); |
| 1258 | nbd_put(nbd); | ||
| 1228 | } | 1259 | } |
| 1229 | 1260 | ||
| 1230 | static const struct block_device_operations nbd_fops = | 1261 | static const struct block_device_operations nbd_fops = |
| @@ -1378,18 +1409,6 @@ static const struct blk_mq_ops nbd_mq_ops = { | |||
| 1378 | .timeout = nbd_xmit_timeout, | 1409 | .timeout = nbd_xmit_timeout, |
| 1379 | }; | 1410 | }; |
| 1380 | 1411 | ||
| 1381 | static void nbd_dev_remove(struct nbd_device *nbd) | ||
| 1382 | { | ||
| 1383 | struct gendisk *disk = nbd->disk; | ||
| 1384 | if (disk) { | ||
| 1385 | del_gendisk(disk); | ||
| 1386 | blk_cleanup_queue(disk->queue); | ||
| 1387 | blk_mq_free_tag_set(&nbd->tag_set); | ||
| 1388 | put_disk(disk); | ||
| 1389 | } | ||
| 1390 | kfree(nbd); | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | static int nbd_dev_add(int index) | 1412 | static int nbd_dev_add(int index) |
| 1394 | { | 1413 | { |
| 1395 | struct nbd_device *nbd; | 1414 | struct nbd_device *nbd; |
| @@ -1452,6 +1471,8 @@ static int nbd_dev_add(int index) | |||
| 1452 | 1471 | ||
| 1453 | mutex_init(&nbd->config_lock); | 1472 | mutex_init(&nbd->config_lock); |
| 1454 | refcount_set(&nbd->config_refs, 0); | 1473 | refcount_set(&nbd->config_refs, 0); |
| 1474 | refcount_set(&nbd->refs, 1); | ||
| 1475 | INIT_LIST_HEAD(&nbd->list); | ||
| 1455 | disk->major = NBD_MAJOR; | 1476 | disk->major = NBD_MAJOR; |
| 1456 | disk->first_minor = index << part_shift; | 1477 | disk->first_minor = index << part_shift; |
| 1457 | disk->fops = &nbd_fops; | 1478 | disk->fops = &nbd_fops; |
| @@ -1549,16 +1570,26 @@ again: | |||
| 1549 | } else { | 1570 | } else { |
| 1550 | nbd = idr_find(&nbd_index_idr, index); | 1571 | nbd = idr_find(&nbd_index_idr, index); |
| 1551 | } | 1572 | } |
| 1552 | mutex_unlock(&nbd_index_mutex); | ||
| 1553 | if (!nbd) { | 1573 | if (!nbd) { |
| 1554 | printk(KERN_ERR "nbd: couldn't find device at index %d\n", | 1574 | printk(KERN_ERR "nbd: couldn't find device at index %d\n", |
| 1555 | index); | 1575 | index); |
| 1576 | mutex_unlock(&nbd_index_mutex); | ||
| 1577 | return -EINVAL; | ||
| 1578 | } | ||
| 1579 | if (!refcount_inc_not_zero(&nbd->refs)) { | ||
| 1580 | mutex_unlock(&nbd_index_mutex); | ||
| 1581 | if (index == -1) | ||
| 1582 | goto again; | ||
| 1583 | printk(KERN_ERR "nbd: device at index %d is going down\n", | ||
| 1584 | index); | ||
| 1556 | return -EINVAL; | 1585 | return -EINVAL; |
| 1557 | } | 1586 | } |
| 1587 | mutex_unlock(&nbd_index_mutex); | ||
| 1558 | 1588 | ||
| 1559 | mutex_lock(&nbd->config_lock); | 1589 | mutex_lock(&nbd->config_lock); |
| 1560 | if (refcount_read(&nbd->config_refs)) { | 1590 | if (refcount_read(&nbd->config_refs)) { |
| 1561 | mutex_unlock(&nbd->config_lock); | 1591 | mutex_unlock(&nbd->config_lock); |
| 1592 | nbd_put(nbd); | ||
| 1562 | if (index == -1) | 1593 | if (index == -1) |
| 1563 | goto again; | 1594 | goto again; |
| 1564 | printk(KERN_ERR "nbd: nbd%d already in use\n", index); | 1595 | printk(KERN_ERR "nbd: nbd%d already in use\n", index); |
| @@ -1566,11 +1597,13 @@ again: | |||
| 1566 | } | 1597 | } |
| 1567 | if (WARN_ON(nbd->config)) { | 1598 | if (WARN_ON(nbd->config)) { |
| 1568 | mutex_unlock(&nbd->config_lock); | 1599 | mutex_unlock(&nbd->config_lock); |
| 1600 | nbd_put(nbd); | ||
| 1569 | return -EINVAL; | 1601 | return -EINVAL; |
| 1570 | } | 1602 | } |
| 1571 | config = nbd->config = nbd_alloc_config(); | 1603 | config = nbd->config = nbd_alloc_config(); |
| 1572 | if (!nbd->config) { | 1604 | if (!nbd->config) { |
| 1573 | mutex_unlock(&nbd->config_lock); | 1605 | mutex_unlock(&nbd->config_lock); |
| 1606 | nbd_put(nbd); | ||
| 1574 | printk(KERN_ERR "nbd: couldn't allocate config\n"); | 1607 | printk(KERN_ERR "nbd: couldn't allocate config\n"); |
| 1575 | return -ENOMEM; | 1608 | return -ENOMEM; |
| 1576 | } | 1609 | } |
| @@ -1655,14 +1688,23 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) | |||
| 1655 | index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]); | 1688 | index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]); |
| 1656 | mutex_lock(&nbd_index_mutex); | 1689 | mutex_lock(&nbd_index_mutex); |
| 1657 | nbd = idr_find(&nbd_index_idr, index); | 1690 | nbd = idr_find(&nbd_index_idr, index); |
| 1658 | mutex_unlock(&nbd_index_mutex); | ||
| 1659 | if (!nbd) { | 1691 | if (!nbd) { |
| 1692 | mutex_unlock(&nbd_index_mutex); | ||
| 1660 | printk(KERN_ERR "nbd: couldn't find device at index %d\n", | 1693 | printk(KERN_ERR "nbd: couldn't find device at index %d\n", |
| 1661 | index); | 1694 | index); |
| 1662 | return -EINVAL; | 1695 | return -EINVAL; |
| 1663 | } | 1696 | } |
| 1664 | if (!refcount_inc_not_zero(&nbd->config_refs)) | 1697 | if (!refcount_inc_not_zero(&nbd->refs)) { |
| 1698 | mutex_unlock(&nbd_index_mutex); | ||
| 1699 | printk(KERN_ERR "nbd: device at index %d is going down\n", | ||
| 1700 | index); | ||
| 1701 | return -EINVAL; | ||
| 1702 | } | ||
| 1703 | mutex_unlock(&nbd_index_mutex); | ||
| 1704 | if (!refcount_inc_not_zero(&nbd->config_refs)) { | ||
| 1705 | nbd_put(nbd); | ||
| 1665 | return 0; | 1706 | return 0; |
| 1707 | } | ||
| 1666 | mutex_lock(&nbd->config_lock); | 1708 | mutex_lock(&nbd->config_lock); |
| 1667 | nbd_disconnect(nbd); | 1709 | nbd_disconnect(nbd); |
| 1668 | mutex_unlock(&nbd->config_lock); | 1710 | mutex_unlock(&nbd->config_lock); |
| @@ -1670,6 +1712,7 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) | |||
| 1670 | &nbd->config->runtime_flags)) | 1712 | &nbd->config->runtime_flags)) |
| 1671 | nbd_config_put(nbd); | 1713 | nbd_config_put(nbd); |
| 1672 | nbd_config_put(nbd); | 1714 | nbd_config_put(nbd); |
| 1715 | nbd_put(nbd); | ||
| 1673 | return 0; | 1716 | return 0; |
| 1674 | } | 1717 | } |
| 1675 | 1718 | ||
| @@ -1690,16 +1733,24 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) | |||
| 1690 | index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]); | 1733 | index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]); |
| 1691 | mutex_lock(&nbd_index_mutex); | 1734 | mutex_lock(&nbd_index_mutex); |
| 1692 | nbd = idr_find(&nbd_index_idr, index); | 1735 | nbd = idr_find(&nbd_index_idr, index); |
| 1693 | mutex_unlock(&nbd_index_mutex); | ||
| 1694 | if (!nbd) { | 1736 | if (!nbd) { |
| 1737 | mutex_unlock(&nbd_index_mutex); | ||
| 1695 | printk(KERN_ERR "nbd: couldn't find a device at index %d\n", | 1738 | printk(KERN_ERR "nbd: couldn't find a device at index %d\n", |
| 1696 | index); | 1739 | index); |
| 1697 | return -EINVAL; | 1740 | return -EINVAL; |
| 1698 | } | 1741 | } |
| 1742 | if (!refcount_inc_not_zero(&nbd->refs)) { | ||
| 1743 | mutex_unlock(&nbd_index_mutex); | ||
| 1744 | printk(KERN_ERR "nbd: device at index %d is going down\n", | ||
| 1745 | index); | ||
| 1746 | return -EINVAL; | ||
| 1747 | } | ||
| 1748 | mutex_unlock(&nbd_index_mutex); | ||
| 1699 | 1749 | ||
| 1700 | if (!refcount_inc_not_zero(&nbd->config_refs)) { | 1750 | if (!refcount_inc_not_zero(&nbd->config_refs)) { |
| 1701 | dev_err(nbd_to_dev(nbd), | 1751 | dev_err(nbd_to_dev(nbd), |
| 1702 | "not configured, cannot reconfigure\n"); | 1752 | "not configured, cannot reconfigure\n"); |
| 1753 | nbd_put(nbd); | ||
| 1703 | return -EINVAL; | 1754 | return -EINVAL; |
| 1704 | } | 1755 | } |
| 1705 | 1756 | ||
| @@ -1758,6 +1809,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) | |||
| 1758 | out: | 1809 | out: |
| 1759 | mutex_unlock(&nbd->config_lock); | 1810 | mutex_unlock(&nbd->config_lock); |
| 1760 | nbd_config_put(nbd); | 1811 | nbd_config_put(nbd); |
| 1812 | nbd_put(nbd); | ||
| 1761 | return ret; | 1813 | return ret; |
| 1762 | } | 1814 | } |
| 1763 | 1815 | ||
| @@ -2003,16 +2055,32 @@ static int __init nbd_init(void) | |||
| 2003 | 2055 | ||
| 2004 | static int nbd_exit_cb(int id, void *ptr, void *data) | 2056 | static int nbd_exit_cb(int id, void *ptr, void *data) |
| 2005 | { | 2057 | { |
| 2058 | struct list_head *list = (struct list_head *)data; | ||
| 2006 | struct nbd_device *nbd = ptr; | 2059 | struct nbd_device *nbd = ptr; |
| 2007 | nbd_dev_remove(nbd); | 2060 | |
| 2061 | refcount_inc(&nbd->refs); | ||
| 2062 | list_add_tail(&nbd->list, list); | ||
| 2008 | return 0; | 2063 | return 0; |
| 2009 | } | 2064 | } |
| 2010 | 2065 | ||
| 2011 | static void __exit nbd_cleanup(void) | 2066 | static void __exit nbd_cleanup(void) |
| 2012 | { | 2067 | { |
| 2068 | struct nbd_device *nbd; | ||
| 2069 | LIST_HEAD(del_list); | ||
| 2070 | |||
| 2013 | nbd_dbg_close(); | 2071 | nbd_dbg_close(); |
| 2014 | 2072 | ||
| 2015 | idr_for_each(&nbd_index_idr, &nbd_exit_cb, NULL); | 2073 | mutex_lock(&nbd_index_mutex); |
| 2074 | idr_for_each(&nbd_index_idr, &nbd_exit_cb, &del_list); | ||
| 2075 | mutex_unlock(&nbd_index_mutex); | ||
| 2076 | |||
| 2077 | list_for_each_entry(nbd, &del_list, list) { | ||
| 2078 | if (refcount_read(&nbd->refs) != 2) | ||
| 2079 | printk(KERN_ERR "nbd: possibly leaking a device\n"); | ||
| 2080 | nbd_put(nbd); | ||
| 2081 | nbd_put(nbd); | ||
| 2082 | } | ||
| 2083 | |||
| 2016 | idr_destroy(&nbd_index_idr); | 2084 | idr_destroy(&nbd_index_idr); |
| 2017 | genl_unregister_family(&nbd_genl_family); | 2085 | genl_unregister_family(&nbd_genl_family); |
| 2018 | destroy_workqueue(recv_workqueue); | 2086 | destroy_workqueue(recv_workqueue); |
