Commit 5aa44bb9 authored by Haggai Eran's avatar Haggai Eran Committed by Doug Ledford

IB/core: Add rwsem to allow reading device list or client list

Currently the RDMA subsystem's device list and client list are protected by
a single mutex. This prevents adding user-facing APIs that iterate these
lists, since using them may cause a deadlock. The patch attempts to solve
this problem by adding a read-write semaphore to protect the lists. Readers
now don't need the mutex, and are safe just by read-locking the semaphore.

The ib_register_device, ib_register_client, ib_unregister_device, and
ib_unregister_client functions are modified to lock the semaphore for write
during their respective list modification. Also, in order to make sure
client callbacks are called only between add() and remove() calls, the code
is changed to only add items to the lists after the add() calls and remove
from the lists before the remove() calls.

This patch attempts to solve a similar need [1] that was seen in the RoCE
v2 patch series.

[1] http://www.spinics.net/lists/linux-rdma/msg24733.htmlReviewed-by: default avatarJason Gunthorpe <jgunthorpe@obsidianresearch.com>
Cc: Matan Barak <matanb@mellanox.com>
Signed-off-by: default avatarHaggai Eran <haggaie@mellanox.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 3403051e
...@@ -55,17 +55,24 @@ struct ib_client_data { ...@@ -55,17 +55,24 @@ struct ib_client_data {
struct workqueue_struct *ib_wq; struct workqueue_struct *ib_wq;
EXPORT_SYMBOL_GPL(ib_wq); EXPORT_SYMBOL_GPL(ib_wq);
/* The device_list and client_list contain devices and clients after their
* registration has completed, and the devices and clients are removed
* during unregistration. */
static LIST_HEAD(device_list); static LIST_HEAD(device_list);
static LIST_HEAD(client_list); static LIST_HEAD(client_list);
/* /*
* device_mutex protects access to both device_list and client_list. * device_mutex and lists_rwsem protect access to both device_list and
* There's no real point to using multiple locks or something fancier * client_list. device_mutex protects writer access by device and client
* like an rwsem: we always access both lists, and we're always * registration / de-registration. lists_rwsem protects reader access to
* modifying one list or the other list. In any case this is not a * these lists. Iterators of these lists must lock it for read, while updates
* hot path so there's no point in trying to optimize. * to the lists must be done with a write lock. A special case is when the
* device_mutex is locked. In this case locking the lists for read access is
* not necessary as the device_mutex implies it.
*/ */
static DEFINE_MUTEX(device_mutex); static DEFINE_MUTEX(device_mutex);
static DECLARE_RWSEM(lists_rwsem);
static int ib_device_check_mandatory(struct ib_device *device) static int ib_device_check_mandatory(struct ib_device *device)
{ {
...@@ -305,8 +312,6 @@ int ib_register_device(struct ib_device *device, ...@@ -305,8 +312,6 @@ int ib_register_device(struct ib_device *device,
goto out; goto out;
} }
list_add_tail(&device->core_list, &device_list);
device->reg_state = IB_DEV_REGISTERED; device->reg_state = IB_DEV_REGISTERED;
{ {
...@@ -317,7 +322,10 @@ int ib_register_device(struct ib_device *device, ...@@ -317,7 +322,10 @@ int ib_register_device(struct ib_device *device,
client->add(device); client->add(device);
} }
out: down_write(&lists_rwsem);
list_add_tail(&device->core_list, &device_list);
up_write(&lists_rwsem);
out:
mutex_unlock(&device_mutex); mutex_unlock(&device_mutex);
return ret; return ret;
} }
...@@ -337,12 +345,14 @@ void ib_unregister_device(struct ib_device *device) ...@@ -337,12 +345,14 @@ void ib_unregister_device(struct ib_device *device)
mutex_lock(&device_mutex); mutex_lock(&device_mutex);
down_write(&lists_rwsem);
list_del(&device->core_list);
up_write(&lists_rwsem);
list_for_each_entry_reverse(client, &client_list, list) list_for_each_entry_reverse(client, &client_list, list)
if (client->remove) if (client->remove)
client->remove(device); client->remove(device);
list_del(&device->core_list);
mutex_unlock(&device_mutex); mutex_unlock(&device_mutex);
ib_device_unregister_sysfs(device); ib_device_unregister_sysfs(device);
...@@ -375,11 +385,14 @@ int ib_register_client(struct ib_client *client) ...@@ -375,11 +385,14 @@ int ib_register_client(struct ib_client *client)
mutex_lock(&device_mutex); mutex_lock(&device_mutex);
list_add_tail(&client->list, &client_list);
list_for_each_entry(device, &device_list, core_list) list_for_each_entry(device, &device_list, core_list)
if (client->add && !add_client_context(device, client)) if (client->add && !add_client_context(device, client))
client->add(device); client->add(device);
down_write(&lists_rwsem);
list_add_tail(&client->list, &client_list);
up_write(&lists_rwsem);
mutex_unlock(&device_mutex); mutex_unlock(&device_mutex);
return 0; return 0;
...@@ -402,6 +415,10 @@ void ib_unregister_client(struct ib_client *client) ...@@ -402,6 +415,10 @@ void ib_unregister_client(struct ib_client *client)
mutex_lock(&device_mutex); mutex_lock(&device_mutex);
down_write(&lists_rwsem);
list_del(&client->list);
up_write(&lists_rwsem);
list_for_each_entry(device, &device_list, core_list) { list_for_each_entry(device, &device_list, core_list) {
if (client->remove) if (client->remove)
client->remove(device); client->remove(device);
...@@ -414,7 +431,6 @@ void ib_unregister_client(struct ib_client *client) ...@@ -414,7 +431,6 @@ void ib_unregister_client(struct ib_client *client)
} }
spin_unlock_irqrestore(&device->client_data_lock, flags); spin_unlock_irqrestore(&device->client_data_lock, flags);
} }
list_del(&client->list);
mutex_unlock(&device_mutex); mutex_unlock(&device_mutex);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment