diff options
author | Josef Bacik <josef@toxicpanda.com> | 2017-04-06 17:02:06 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2017-04-17 11:58:42 -0400 |
commit | c6a4759ea0c9a7e7661f34f6943dafb1c6ae1b1c (patch) | |
tree | a2a16f24b4ddf730ab97e385d6f8cd32163a39c1 | |
parent | 47d902b90a32a42a3d33aef3a02170fc6f70aa23 (diff) |
nbd: add device refcounting
In order to support deleting the device on disconnect we need to
refcount the actual nbd_device struct. So add the refcounting framework
and change how we free the normal devices at rmmod time so we can catch
reference leaks.
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
-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); |