Skip to content

Commit c6a4759

Browse files
josefbacikaxboe
authored andcommitted
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 <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 47d902b commit c6a4759

File tree

1 file changed

+86
-18
lines changed

1 file changed

+86
-18
lines changed

drivers/block/nbd.c

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,12 @@ struct nbd_device {
9999

100100
int index;
101101
refcount_t config_refs;
102+
refcount_t refs;
102103
struct nbd_config *config;
103104
struct mutex config_lock;
104105
struct gendisk *disk;
105106

107+
struct list_head list;
106108
struct task_struct *task_recv;
107109
struct task_struct *task_setup;
108110
};
@@ -165,6 +167,28 @@ static struct device_attribute pid_attr = {
165167
.show = pid_show,
166168
};
167169

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+
168192
static int nbd_disconnected(struct nbd_config *config)
169193
{
170194
return test_bit(NBD_DISCONNECTED, &config->runtime_flags) ||
@@ -1005,6 +1029,7 @@ static void nbd_config_put(struct nbd_device *nbd)
10051029
}
10061030
nbd_reset(nbd);
10071031
mutex_unlock(&nbd->config_lock);
1032+
nbd_put(nbd);
10081033
module_put(THIS_MODULE);
10091034
}
10101035
}
@@ -1199,6 +1224,10 @@ static int nbd_open(struct block_device *bdev, fmode_t mode)
11991224
ret = -ENXIO;
12001225
goto out;
12011226
}
1227+
if (!refcount_inc_not_zero(&nbd->refs)) {
1228+
ret = -ENXIO;
1229+
goto out;
1230+
}
12021231
if (!refcount_inc_not_zero(&nbd->config_refs)) {
12031232
struct nbd_config *config;
12041233

@@ -1214,6 +1243,7 @@ static int nbd_open(struct block_device *bdev, fmode_t mode)
12141243
goto out;
12151244
}
12161245
refcount_set(&nbd->config_refs, 1);
1246+
refcount_inc(&nbd->refs);
12171247
mutex_unlock(&nbd->config_lock);
12181248
}
12191249
out:
@@ -1225,6 +1255,7 @@ static void nbd_release(struct gendisk *disk, fmode_t mode)
12251255
{
12261256
struct nbd_device *nbd = disk->private_data;
12271257
nbd_config_put(nbd);
1258+
nbd_put(nbd);
12281259
}
12291260

12301261
static const struct block_device_operations nbd_fops =
@@ -1378,18 +1409,6 @@ static const struct blk_mq_ops nbd_mq_ops = {
13781409
.timeout = nbd_xmit_timeout,
13791410
};
13801411

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-
13931412
static int nbd_dev_add(int index)
13941413
{
13951414
struct nbd_device *nbd;
@@ -1452,6 +1471,8 @@ static int nbd_dev_add(int index)
14521471

14531472
mutex_init(&nbd->config_lock);
14541473
refcount_set(&nbd->config_refs, 0);
1474+
refcount_set(&nbd->refs, 1);
1475+
INIT_LIST_HEAD(&nbd->list);
14551476
disk->major = NBD_MAJOR;
14561477
disk->first_minor = index << part_shift;
14571478
disk->fops = &nbd_fops;
@@ -1549,28 +1570,40 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
15491570
} else {
15501571
nbd = idr_find(&nbd_index_idr, index);
15511572
}
1552-
mutex_unlock(&nbd_index_mutex);
15531573
if (!nbd) {
15541574
printk(KERN_ERR "nbd: couldn't find device at index %d\n",
15551575
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);
15561585
return -EINVAL;
15571586
}
1587+
mutex_unlock(&nbd_index_mutex);
15581588

15591589
mutex_lock(&nbd->config_lock);
15601590
if (refcount_read(&nbd->config_refs)) {
15611591
mutex_unlock(&nbd->config_lock);
1592+
nbd_put(nbd);
15621593
if (index == -1)
15631594
goto again;
15641595
printk(KERN_ERR "nbd: nbd%d already in use\n", index);
15651596
return -EBUSY;
15661597
}
15671598
if (WARN_ON(nbd->config)) {
15681599
mutex_unlock(&nbd->config_lock);
1600+
nbd_put(nbd);
15691601
return -EINVAL;
15701602
}
15711603
config = nbd->config = nbd_alloc_config();
15721604
if (!nbd->config) {
15731605
mutex_unlock(&nbd->config_lock);
1606+
nbd_put(nbd);
15741607
printk(KERN_ERR "nbd: couldn't allocate config\n");
15751608
return -ENOMEM;
15761609
}
@@ -1655,21 +1688,31 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info)
16551688
index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]);
16561689
mutex_lock(&nbd_index_mutex);
16571690
nbd = idr_find(&nbd_index_idr, index);
1658-
mutex_unlock(&nbd_index_mutex);
16591691
if (!nbd) {
1692+
mutex_unlock(&nbd_index_mutex);
16601693
printk(KERN_ERR "nbd: couldn't find device at index %d\n",
16611694
index);
16621695
return -EINVAL;
16631696
}
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);
16651706
return 0;
1707+
}
16661708
mutex_lock(&nbd->config_lock);
16671709
nbd_disconnect(nbd);
16681710
mutex_unlock(&nbd->config_lock);
16691711
if (test_and_clear_bit(NBD_HAS_CONFIG_REF,
16701712
&nbd->config->runtime_flags))
16711713
nbd_config_put(nbd);
16721714
nbd_config_put(nbd);
1715+
nbd_put(nbd);
16731716
return 0;
16741717
}
16751718

@@ -1690,16 +1733,24 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
16901733
index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]);
16911734
mutex_lock(&nbd_index_mutex);
16921735
nbd = idr_find(&nbd_index_idr, index);
1693-
mutex_unlock(&nbd_index_mutex);
16941736
if (!nbd) {
1737+
mutex_unlock(&nbd_index_mutex);
16951738
printk(KERN_ERR "nbd: couldn't find a device at index %d\n",
16961739
index);
16971740
return -EINVAL;
16981741
}
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);
16991749

17001750
if (!refcount_inc_not_zero(&nbd->config_refs)) {
17011751
dev_err(nbd_to_dev(nbd),
17021752
"not configured, cannot reconfigure\n");
1753+
nbd_put(nbd);
17031754
return -EINVAL;
17041755
}
17051756

@@ -1758,6 +1809,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
17581809
out:
17591810
mutex_unlock(&nbd->config_lock);
17601811
nbd_config_put(nbd);
1812+
nbd_put(nbd);
17611813
return ret;
17621814
}
17631815

@@ -2003,16 +2055,32 @@ static int __init nbd_init(void)
20032055

20042056
static int nbd_exit_cb(int id, void *ptr, void *data)
20052057
{
2058+
struct list_head *list = (struct list_head *)data;
20062059
struct nbd_device *nbd = ptr;
2007-
nbd_dev_remove(nbd);
2060+
2061+
refcount_inc(&nbd->refs);
2062+
list_add_tail(&nbd->list, list);
20082063
return 0;
20092064
}
20102065

20112066
static void __exit nbd_cleanup(void)
20122067
{
2068+
struct nbd_device *nbd;
2069+
LIST_HEAD(del_list);
2070+
20132071
nbd_dbg_close();
20142072

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+
20162084
idr_destroy(&nbd_index_idr);
20172085
genl_unregister_family(&nbd_genl_family);
20182086
destroy_workqueue(recv_workqueue);

0 commit comments

Comments
 (0)