Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drbd: fix drbd_delete_device: remove vnr from volumes; idr_remove(); synchronize_rcu(); before cleanup

Still missing: rcu_readlock() on the various call sites that
access/iterate over those idrs.

We don't need a specific write lock, as we only modify from
configuration context, which is already strictly serialized.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>

authored by

Lars Ellenberg and committed by
Philipp Reisner
569083c0 da4a75d2

+24 -18
+24 -18
drivers/block/drbd/drbd_main.c
··· 2124 2124 if (!mdev) 2125 2125 return; 2126 2126 2127 - idr_remove(&mdev->tconn->volumes, minor); 2127 + idr_remove(&mdev->tconn->volumes, mdev->vnr); 2128 + idr_remove(&minors, minor); 2129 + synchronize_rcu(); 2128 2130 2129 2131 /* paranoia asserts */ 2130 2132 D_ASSERT(mdev->open_cnt == 0); ··· 2155 2153 * allocated from drbd_new_device 2156 2154 * and actually free the mdev itself */ 2157 2155 drbd_free_mdev(mdev); 2158 - idr_remove(&minors, minor); 2159 2156 } 2160 2157 2161 2158 static void drbd_cleanup(void) ··· 2332 2331 return ERR_NOMEM; 2333 2332 2334 2333 mdev->tconn = tconn; 2335 - if (!idr_pre_get(&tconn->volumes, GFP_KERNEL)) 2336 - goto out_no_idr; 2337 - if (idr_get_new(&tconn->volumes, mdev, &vnr_got)) 2338 - goto out_no_idr; 2339 - if (vnr_got != vnr) { 2340 - dev_err(DEV, "vnr_got (%d) != vnr (%d)\n", vnr_got, vnr); 2341 - goto out_no_q; 2342 - } 2343 - 2344 2334 mdev->minor = minor; 2345 2335 2346 2336 drbd_init_set_defaults(mdev); ··· 2387 2395 INIT_LIST_HEAD(&mdev->current_epoch->list); 2388 2396 mdev->epochs = 1; 2389 2397 2398 + if (!idr_pre_get(&tconn->volumes, GFP_KERNEL)) 2399 + goto out_no_vol_idr; 2400 + if (idr_get_new(&tconn->volumes, mdev, &vnr_got)) 2401 + goto out_no_vol_idr; 2402 + if (vnr_got != vnr) { 2403 + dev_err(DEV, "vnr_got (%d) != vnr (%d)\n", vnr_got, vnr); 2404 + goto out_idr_remove_vol; 2405 + } 2406 + 2390 2407 if (!idr_pre_get(&minors, GFP_KERNEL)) 2391 - goto out_no_minor_idr; 2408 + goto out_idr_remove_vol; 2392 2409 if (idr_get_new(&minors, mdev, &minor_got)) 2393 - goto out_no_minor_idr; 2410 + goto out_idr_remove_vol; 2394 2411 if (minor_got != minor) { 2395 - idr_remove(&minors, minor_got); 2396 - goto out_no_minor_idr; 2412 + /* minor exists, or other idr strangeness? */ 2413 + dev_err(DEV, "available minor (%d) != requested minor (%d)\n", 2414 + minor_got, minor); 2415 + goto out_idr_remove_minor; 2397 2416 } 2398 2417 add_disk(disk); 2399 2418 2400 2419 return NO_ERROR; 2401 2420 2402 - out_no_minor_idr: 2421 + out_idr_remove_minor: 2422 + idr_remove(&minors, minor_got); 2423 + out_idr_remove_vol: 2424 + idr_remove(&tconn->volumes, vnr_got); 2425 + synchronize_rcu(); 2426 + out_no_vol_idr: 2403 2427 kfree(mdev->current_epoch); 2404 2428 out_no_epoch: 2405 2429 drbd_bm_cleanup(mdev); ··· 2426 2418 out_no_disk: 2427 2419 blk_cleanup_queue(q); 2428 2420 out_no_q: 2429 - idr_remove(&tconn->volumes, vnr_got); 2430 - out_no_idr: 2431 2421 kfree(mdev); 2432 2422 return ERR_NOMEM; 2433 2423 }