File drbd-Fix-locking-for-the-drbd_devices-idr.patch of Package drbd.38639
From f7e77b8e3222dbc62973eb1c7bb9230668877543 Mon Sep 17 00:00:00 2001
From: Philipp Reisner <philipp.reisner@linbit.com>
Date: Wed, 26 May 2021 10:39:46 +0200
Subject: [PATCH] drbd: Fix locking for the drbd_devices idr
The call graphs that modify the drbd_devices idr are:
drbd_adm_new_minor()
drbd_create_device()
idr_alloc(&drbd_devices, )
adm_del_minor()
drbd_unregister_device()
idr_remove(&drbd_devices, )
Since enabling the use of using parallel_ops (August 2016, commit ccb4c56c)
there was no locking that protected idr_alloc() and idr_remove() running in
paralle on multiple CPUs.
---
drbd/drbd_int.h | 2 +-
drbd/drbd_main.c | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drbd/drbd_int.h b/drbd/drbd_int.h
index a4f4ce80..0f7c3b0c 100644
--- a/drbd/drbd_int.h
+++ b/drbd/drbd_int.h
@@ -138,7 +138,7 @@ drbd_insert_fault(struct drbd_device *device, unsigned int type) {
(typecheck(struct drbd_device*, x) && \
((x) ? (((x)->magic ^ DRBD_MAGIC) == (long)(x)) : 0))
-extern struct idr drbd_devices; /* RCU, updates: genl_lock() */
+extern struct idr drbd_devices; /* RCU, updates: drbd_devices_lock */
extern struct list_head drbd_resources; /* RCU, updates: resources_mutex */
extern struct mutex resources_mutex;
diff --git a/drbd/drbd_main.c b/drbd/drbd_main.c
index 0636adf0..1e325aed 100644
--- a/drbd/drbd_main.c
+++ b/drbd/drbd_main.c
@@ -134,6 +134,7 @@ module_param_named(protocol_version_min, drbd_protocol_version_min, drbd_protoco
*/
struct idr drbd_devices;
struct list_head drbd_resources;
+DEFINE_SPINLOCK(drbd_devices_lock);
DEFINE_MUTEX(resources_mutex);
struct kmem_cache *drbd_request_cache;
@@ -3767,7 +3768,9 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
locked = true;
spin_lock_irq(&resource->req_lock);
+ spin_lock(&drbd_devices_lock);
id = idr_alloc(&drbd_devices, device, minor, minor + 1, GFP_NOWAIT);
+ spin_unlock(&drbd_devices_lock);
if (id < 0) {
if (id == -ENOSPC)
err = ERR_MINOR_OR_VOLUME_EXISTS;
@@ -3841,7 +3844,9 @@ out_remove_peer_device:
kref_debug_put(&device->kref_debug, 1);
out_idr_remove_minor:
+ spin_lock(&drbd_devices_lock);
idr_remove(&drbd_devices, minor);
+ spin_unlock(&drbd_devices_lock);
kref_debug_put(&device->kref_debug, 1);
out_no_minor_idr:
if (locked)
@@ -3889,7 +3894,9 @@ void drbd_unregister_device(struct drbd_device *device)
idr_remove(&connection->peer_devices, device->vnr);
}
idr_remove(&resource->devices, device->vnr);
+ spin_lock(&drbd_devices_lock);
idr_remove(&drbd_devices, device->minor);
+ spin_unlock(&drbd_devices_lock);
spin_unlock_irq(&resource->req_lock);
for_each_peer_device(peer_device, device)
--
2.16.4