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

openSUSE Build Service is sponsored by