aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/core
diff options
context:
space:
mode:
authorHaggai Eran <haggaie@mellanox.com>2015-07-30 10:50:14 -0400
committerDoug Ledford <dledford@redhat.com>2015-08-30 15:48:21 -0400
commit7c1eb45a22d76bb99236e7485958f87ef7c449cf (patch)
tree9c4d29b17d17e99f6cfa7ccc1a542d13f9bb4269 /drivers/infiniband/core
parent5aa44bb90f047662c12c44be1b6de454658632d0 (diff)
IB/core: lock client data with lists_rwsem
An ib_client callback that is called with the lists_rwsem locked only for read is protected from changes to the IB client lists, but not from ib_unregister_device() freeing its client data. This is because ib_unregister_device() will remove the device from the device list with lists_rwsem locked for write, but perform the rest of the cleanup, including the call to remove() without that lock. Mark client data that is undergoing de-registration with a new going_down flag in the client data context. Lock the client data list with lists_rwsem for write in addition to using the spinlock, so that functions calling the callback would be able to lock only lists_rwsem for read and let callbacks sleep. Since ib_unregister_client() now marks the client data context, no need for remove() to search the context again, so pass the client data directly to remove() callbacks. Reviewed-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Signed-off-by: Haggai Eran <haggaie@mellanox.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
Diffstat (limited to 'drivers/infiniband/core')
-rw-r--r--drivers/infiniband/core/cache.c2
-rw-r--r--drivers/infiniband/core/cm.c7
-rw-r--r--drivers/infiniband/core/cma.c7
-rw-r--r--drivers/infiniband/core/device.c53
-rw-r--r--drivers/infiniband/core/mad.c2
-rw-r--r--drivers/infiniband/core/multicast.c7
-rw-r--r--drivers/infiniband/core/sa_query.c6
-rw-r--r--drivers/infiniband/core/ucm.c6
-rw-r--r--drivers/infiniband/core/user_mad.c6
-rw-r--r--drivers/infiniband/core/uverbs_main.c6
10 files changed, 67 insertions, 35 deletions
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index 871da832d016..c93af66cc091 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -394,7 +394,7 @@ err:
394 kfree(device->cache.lmc_cache); 394 kfree(device->cache.lmc_cache);
395} 395}
396 396
397static void ib_cache_cleanup_one(struct ib_device *device) 397static void ib_cache_cleanup_one(struct ib_device *device, void *client_data)
398{ 398{
399 int p; 399 int p;
400 400
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 3a972ebf3c0d..82d5c4362aa8 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -58,7 +58,7 @@ MODULE_DESCRIPTION("InfiniBand CM");
58MODULE_LICENSE("Dual BSD/GPL"); 58MODULE_LICENSE("Dual BSD/GPL");
59 59
60static void cm_add_one(struct ib_device *device); 60static void cm_add_one(struct ib_device *device);
61static void cm_remove_one(struct ib_device *device); 61static void cm_remove_one(struct ib_device *device, void *client_data);
62 62
63static struct ib_client cm_client = { 63static struct ib_client cm_client = {
64 .name = "cm", 64 .name = "cm",
@@ -3886,9 +3886,9 @@ free:
3886 kfree(cm_dev); 3886 kfree(cm_dev);
3887} 3887}
3888 3888
3889static void cm_remove_one(struct ib_device *ib_device) 3889static void cm_remove_one(struct ib_device *ib_device, void *client_data)
3890{ 3890{
3891 struct cm_device *cm_dev; 3891 struct cm_device *cm_dev = client_data;
3892 struct cm_port *port; 3892 struct cm_port *port;
3893 struct ib_port_modify port_modify = { 3893 struct ib_port_modify port_modify = {
3894 .clr_port_cap_mask = IB_PORT_CM_SUP 3894 .clr_port_cap_mask = IB_PORT_CM_SUP
@@ -3896,7 +3896,6 @@ static void cm_remove_one(struct ib_device *ib_device)
3896 unsigned long flags; 3896 unsigned long flags;
3897 int i; 3897 int i;
3898 3898
3899 cm_dev = ib_get_client_data(ib_device, &cm_client);
3900 if (!cm_dev) 3899 if (!cm_dev)
3901 return; 3900 return;
3902 3901
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 4e72e4c16cfe..9664131c4eeb 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -94,7 +94,7 @@ const char *rdma_event_msg(enum rdma_cm_event_type event)
94EXPORT_SYMBOL(rdma_event_msg); 94EXPORT_SYMBOL(rdma_event_msg);
95 95
96static void cma_add_one(struct ib_device *device); 96static void cma_add_one(struct ib_device *device);
97static void cma_remove_one(struct ib_device *device); 97static void cma_remove_one(struct ib_device *device, void *client_data);
98 98
99static struct ib_client cma_client = { 99static struct ib_client cma_client = {
100 .name = "cma", 100 .name = "cma",
@@ -3554,11 +3554,10 @@ static void cma_process_remove(struct cma_device *cma_dev)
3554 wait_for_completion(&cma_dev->comp); 3554 wait_for_completion(&cma_dev->comp);
3555} 3555}
3556 3556
3557static void cma_remove_one(struct ib_device *device) 3557static void cma_remove_one(struct ib_device *device, void *client_data)
3558{ 3558{
3559 struct cma_device *cma_dev; 3559 struct cma_device *cma_dev = client_data;
3560 3560
3561 cma_dev = ib_get_client_data(device, &cma_client);
3562 if (!cma_dev) 3561 if (!cma_dev)
3563 return; 3562 return;
3564 3563
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 0c8fa781538b..ce317e623862 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -50,6 +50,9 @@ struct ib_client_data {
50 struct list_head list; 50 struct list_head list;
51 struct ib_client *client; 51 struct ib_client *client;
52 void * data; 52 void * data;
53 /* The device or client is going down. Do not call client or device
54 * callbacks other than remove(). */
55 bool going_down;
53}; 56};
54 57
55struct workqueue_struct *ib_wq; 58struct workqueue_struct *ib_wq;
@@ -69,6 +72,8 @@ static LIST_HEAD(client_list);
69 * to the lists must be done with a write lock. A special case is when the 72 * to the lists must be done with a write lock. A special case is when the
70 * device_mutex is locked. In this case locking the lists for read access is 73 * device_mutex is locked. In this case locking the lists for read access is
71 * not necessary as the device_mutex implies it. 74 * not necessary as the device_mutex implies it.
75 *
76 * lists_rwsem also protects access to the client data list.
72 */ 77 */
73static DEFINE_MUTEX(device_mutex); 78static DEFINE_MUTEX(device_mutex);
74static DECLARE_RWSEM(lists_rwsem); 79static DECLARE_RWSEM(lists_rwsem);
@@ -210,10 +215,13 @@ static int add_client_context(struct ib_device *device, struct ib_client *client
210 215
211 context->client = client; 216 context->client = client;
212 context->data = NULL; 217 context->data = NULL;
218 context->going_down = false;
213 219
220 down_write(&lists_rwsem);
214 spin_lock_irqsave(&device->client_data_lock, flags); 221 spin_lock_irqsave(&device->client_data_lock, flags);
215 list_add(&context->list, &device->client_data_list); 222 list_add(&context->list, &device->client_data_list);
216 spin_unlock_irqrestore(&device->client_data_lock, flags); 223 spin_unlock_irqrestore(&device->client_data_lock, flags);
224 up_write(&lists_rwsem);
217 225
218 return 0; 226 return 0;
219} 227}
@@ -339,7 +347,6 @@ EXPORT_SYMBOL(ib_register_device);
339 */ 347 */
340void ib_unregister_device(struct ib_device *device) 348void ib_unregister_device(struct ib_device *device)
341{ 349{
342 struct ib_client *client;
343 struct ib_client_data *context, *tmp; 350 struct ib_client_data *context, *tmp;
344 unsigned long flags; 351 unsigned long flags;
345 352
@@ -347,20 +354,29 @@ void ib_unregister_device(struct ib_device *device)
347 354
348 down_write(&lists_rwsem); 355 down_write(&lists_rwsem);
349 list_del(&device->core_list); 356 list_del(&device->core_list);
350 up_write(&lists_rwsem); 357 spin_lock_irqsave(&device->client_data_lock, flags);
358 list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
359 context->going_down = true;
360 spin_unlock_irqrestore(&device->client_data_lock, flags);
361 downgrade_write(&lists_rwsem);
351 362
352 list_for_each_entry_reverse(client, &client_list, list) 363 list_for_each_entry_safe(context, tmp, &device->client_data_list,
353 if (client->remove) 364 list) {
354 client->remove(device); 365 if (context->client->remove)
366 context->client->remove(device, context->data);
367 }
368 up_read(&lists_rwsem);
355 369
356 mutex_unlock(&device_mutex); 370 mutex_unlock(&device_mutex);
357 371
358 ib_device_unregister_sysfs(device); 372 ib_device_unregister_sysfs(device);
359 373
374 down_write(&lists_rwsem);
360 spin_lock_irqsave(&device->client_data_lock, flags); 375 spin_lock_irqsave(&device->client_data_lock, flags);
361 list_for_each_entry_safe(context, tmp, &device->client_data_list, list) 376 list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
362 kfree(context); 377 kfree(context);
363 spin_unlock_irqrestore(&device->client_data_lock, flags); 378 spin_unlock_irqrestore(&device->client_data_lock, flags);
379 up_write(&lists_rwsem);
364 380
365 device->reg_state = IB_DEV_UNREGISTERED; 381 device->reg_state = IB_DEV_UNREGISTERED;
366} 382}
@@ -420,16 +436,35 @@ void ib_unregister_client(struct ib_client *client)
420 up_write(&lists_rwsem); 436 up_write(&lists_rwsem);
421 437
422 list_for_each_entry(device, &device_list, core_list) { 438 list_for_each_entry(device, &device_list, core_list) {
423 if (client->remove) 439 struct ib_client_data *found_context = NULL;
424 client->remove(device);
425 440
441 down_write(&lists_rwsem);
426 spin_lock_irqsave(&device->client_data_lock, flags); 442 spin_lock_irqsave(&device->client_data_lock, flags);
427 list_for_each_entry_safe(context, tmp, &device->client_data_list, list) 443 list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
428 if (context->client == client) { 444 if (context->client == client) {
429 list_del(&context->list); 445 context->going_down = true;
430 kfree(context); 446 found_context = context;
447 break;
431 } 448 }
432 spin_unlock_irqrestore(&device->client_data_lock, flags); 449 spin_unlock_irqrestore(&device->client_data_lock, flags);
450 up_write(&lists_rwsem);
451
452 if (client->remove)
453 client->remove(device, found_context ?
454 found_context->data : NULL);
455
456 if (!found_context) {
457 pr_warn("No client context found for %s/%s\n",
458 device->name, client->name);
459 continue;
460 }
461
462 down_write(&lists_rwsem);
463 spin_lock_irqsave(&device->client_data_lock, flags);
464 list_del(&found_context->list);
465 kfree(found_context);
466 spin_unlock_irqrestore(&device->client_data_lock, flags);
467 up_write(&lists_rwsem);
433 } 468 }
434 469
435 mutex_unlock(&device_mutex); 470 mutex_unlock(&device_mutex);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 786fc51bf04b..66b4b3eb8f67 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -3335,7 +3335,7 @@ error:
3335 } 3335 }
3336} 3336}
3337 3337
3338static void ib_mad_remove_device(struct ib_device *device) 3338static void ib_mad_remove_device(struct ib_device *device, void *client_data)
3339{ 3339{
3340 int i; 3340 int i;
3341 3341
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index 2cb865c7ce7a..d38d8b2b2979 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -43,7 +43,7 @@
43#include "sa.h" 43#include "sa.h"
44 44
45static void mcast_add_one(struct ib_device *device); 45static void mcast_add_one(struct ib_device *device);
46static void mcast_remove_one(struct ib_device *device); 46static void mcast_remove_one(struct ib_device *device, void *client_data);
47 47
48static struct ib_client mcast_client = { 48static struct ib_client mcast_client = {
49 .name = "ib_multicast", 49 .name = "ib_multicast",
@@ -840,13 +840,12 @@ static void mcast_add_one(struct ib_device *device)
840 ib_register_event_handler(&dev->event_handler); 840 ib_register_event_handler(&dev->event_handler);
841} 841}
842 842
843static void mcast_remove_one(struct ib_device *device) 843static void mcast_remove_one(struct ib_device *device, void *client_data)
844{ 844{
845 struct mcast_device *dev; 845 struct mcast_device *dev = client_data;
846 struct mcast_port *port; 846 struct mcast_port *port;
847 int i; 847 int i;
848 848
849 dev = ib_get_client_data(device, &mcast_client);
850 if (!dev) 849 if (!dev)
851 return; 850 return;
852 851
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index ca919f429666..d40be3673b79 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -107,7 +107,7 @@ struct ib_sa_mcmember_query {
107}; 107};
108 108
109static void ib_sa_add_one(struct ib_device *device); 109static void ib_sa_add_one(struct ib_device *device);
110static void ib_sa_remove_one(struct ib_device *device); 110static void ib_sa_remove_one(struct ib_device *device, void *client_data);
111 111
112static struct ib_client sa_client = { 112static struct ib_client sa_client = {
113 .name = "sa", 113 .name = "sa",
@@ -1221,9 +1221,9 @@ free:
1221 return; 1221 return;
1222} 1222}
1223 1223
1224static void ib_sa_remove_one(struct ib_device *device) 1224static void ib_sa_remove_one(struct ib_device *device, void *client_data)
1225{ 1225{
1226 struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client); 1226 struct ib_sa_device *sa_dev = client_data;
1227 int i; 1227 int i;
1228 1228
1229 if (!sa_dev) 1229 if (!sa_dev)
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index 009481073644..8cde48b96f19 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -109,7 +109,7 @@ enum {
109#define IB_UCM_BASE_DEV MKDEV(IB_UCM_MAJOR, IB_UCM_BASE_MINOR) 109#define IB_UCM_BASE_DEV MKDEV(IB_UCM_MAJOR, IB_UCM_BASE_MINOR)
110 110
111static void ib_ucm_add_one(struct ib_device *device); 111static void ib_ucm_add_one(struct ib_device *device);
112static void ib_ucm_remove_one(struct ib_device *device); 112static void ib_ucm_remove_one(struct ib_device *device, void *client_data);
113 113
114static struct ib_client ucm_client = { 114static struct ib_client ucm_client = {
115 .name = "ucm", 115 .name = "ucm",
@@ -1310,9 +1310,9 @@ err:
1310 return; 1310 return;
1311} 1311}
1312 1312
1313static void ib_ucm_remove_one(struct ib_device *device) 1313static void ib_ucm_remove_one(struct ib_device *device, void *client_data)
1314{ 1314{
1315 struct ib_ucm_device *ucm_dev = ib_get_client_data(device, &ucm_client); 1315 struct ib_ucm_device *ucm_dev = client_data;
1316 1316
1317 if (!ucm_dev) 1317 if (!ucm_dev)
1318 return; 1318 return;
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 35567fffaa4e..57f281f8d686 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -133,7 +133,7 @@ static DEFINE_SPINLOCK(port_lock);
133static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS); 133static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS);
134 134
135static void ib_umad_add_one(struct ib_device *device); 135static void ib_umad_add_one(struct ib_device *device);
136static void ib_umad_remove_one(struct ib_device *device); 136static void ib_umad_remove_one(struct ib_device *device, void *client_data);
137 137
138static void ib_umad_release_dev(struct kobject *kobj) 138static void ib_umad_release_dev(struct kobject *kobj)
139{ 139{
@@ -1322,9 +1322,9 @@ free:
1322 kobject_put(&umad_dev->kobj); 1322 kobject_put(&umad_dev->kobj);
1323} 1323}
1324 1324
1325static void ib_umad_remove_one(struct ib_device *device) 1325static void ib_umad_remove_one(struct ib_device *device, void *client_data)
1326{ 1326{
1327 struct ib_umad_device *umad_dev = ib_get_client_data(device, &umad_client); 1327 struct ib_umad_device *umad_dev = client_data;
1328 int i; 1328 int i;
1329 1329
1330 if (!umad_dev) 1330 if (!umad_dev)
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index f6eef2da7097..46c92294afa5 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -128,7 +128,7 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
128}; 128};
129 129
130static void ib_uverbs_add_one(struct ib_device *device); 130static void ib_uverbs_add_one(struct ib_device *device);
131static void ib_uverbs_remove_one(struct ib_device *device); 131static void ib_uverbs_remove_one(struct ib_device *device, void *client_data);
132 132
133static void ib_uverbs_release_dev(struct kref *ref) 133static void ib_uverbs_release_dev(struct kref *ref)
134{ 134{
@@ -948,9 +948,9 @@ err:
948 return; 948 return;
949} 949}
950 950
951static void ib_uverbs_remove_one(struct ib_device *device) 951static void ib_uverbs_remove_one(struct ib_device *device, void *client_data)
952{ 952{
953 struct ib_uverbs_device *uverbs_dev = ib_get_client_data(device, &uverbs_client); 953 struct ib_uverbs_device *uverbs_dev = client_data;
954 954
955 if (!uverbs_dev) 955 if (!uverbs_dev)
956 return; 956 return;