Skip to content

Commit 98f6715

Browse files
committed
RDMA/cm: Simplify establishing a listen cm_id
Any manipulation of cm_id->state must be done under the cm_id_priv->lock, the two routines that added listens did not follow this rule, because they never participate in any concurrent access around the state. However, since this exception makes the code hard to understand, simplify the flow so that it can be fully locked: - Move manipulation of listen_sharecount into cm_insert_listen() so it is trivially under the cm.lock without having to expose the cm.lock to the caller. - Push the cm.lock down into cm_insert_listen() and have the function increment the reference count before returning an existing pointer. - Split ib_cm_listen() into an cm_init_listen() and do not call ib_cm_listen() from ib_cm_insert_listen() - Make both ib_cm_listen() and ib_cm_insert_listen() directly call cm_insert_listen() under their cm_id_priv->lock which does both a collision detect and, if needed, the insert (atomically) - Enclose all state manipulation within the cm_id_priv->lock, notice this set can be done safely after cm_insert_listen() as no reader is allowed to read the state without holding the lock. - Do not set the listen cm_id in the xarray, as it is never correct to look it up. This makes the concurrency simpler to understand. Many needless error unwinds are removed in the process. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Leon Romanovsky <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 2305d68 commit 98f6715

File tree

1 file changed

+116
-83
lines changed
  • drivers/infiniband/core

1 file changed

+116
-83
lines changed

drivers/infiniband/core/cm.c

Lines changed: 116 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -620,22 +620,44 @@ static int be64_gt(__be64 a, __be64 b)
620620
return (__force u64) a > (__force u64) b;
621621
}
622622

623-
static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
623+
/*
624+
* Inserts a new cm_id_priv into the listen_service_table. Returns cm_id_priv
625+
* if the new ID was inserted, NULL if it could not be inserted due to a
626+
* collision, or the existing cm_id_priv ready for shared usage.
627+
*/
628+
static struct cm_id_private *cm_insert_listen(struct cm_id_private *cm_id_priv,
629+
ib_cm_handler shared_handler)
624630
{
625631
struct rb_node **link = &cm.listen_service_table.rb_node;
626632
struct rb_node *parent = NULL;
627633
struct cm_id_private *cur_cm_id_priv;
628634
__be64 service_id = cm_id_priv->id.service_id;
629635
__be64 service_mask = cm_id_priv->id.service_mask;
636+
unsigned long flags;
630637

638+
spin_lock_irqsave(&cm.lock, flags);
631639
while (*link) {
632640
parent = *link;
633641
cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
634642
service_node);
635643
if ((cur_cm_id_priv->id.service_mask & service_id) ==
636644
(service_mask & cur_cm_id_priv->id.service_id) &&
637-
(cm_id_priv->id.device == cur_cm_id_priv->id.device))
645+
(cm_id_priv->id.device == cur_cm_id_priv->id.device)) {
646+
/*
647+
* Sharing an ib_cm_id with different handlers is not
648+
* supported
649+
*/
650+
if (cur_cm_id_priv->id.cm_handler != shared_handler ||
651+
cur_cm_id_priv->id.context ||
652+
WARN_ON(!cur_cm_id_priv->id.cm_handler)) {
653+
spin_unlock_irqrestore(&cm.lock, flags);
654+
return NULL;
655+
}
656+
refcount_inc(&cur_cm_id_priv->refcount);
657+
cur_cm_id_priv->listen_sharecount++;
658+
spin_unlock_irqrestore(&cm.lock, flags);
638659
return cur_cm_id_priv;
660+
}
639661

640662
if (cm_id_priv->id.device < cur_cm_id_priv->id.device)
641663
link = &(*link)->rb_left;
@@ -648,9 +670,11 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
648670
else
649671
link = &(*link)->rb_right;
650672
}
673+
cm_id_priv->listen_sharecount++;
651674
rb_link_node(&cm_id_priv->service_node, parent, link);
652675
rb_insert_color(&cm_id_priv->service_node, &cm.listen_service_table);
653-
return NULL;
676+
spin_unlock_irqrestore(&cm.lock, flags);
677+
return cm_id_priv;
654678
}
655679

656680
static struct cm_id_private * cm_find_listen(struct ib_device *device,
@@ -807,9 +831,9 @@ static void cm_reject_sidr_req(struct cm_id_private *cm_id_priv,
807831
ib_send_cm_sidr_rep(&cm_id_priv->id, &param);
808832
}
809833

810-
struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
811-
ib_cm_handler cm_handler,
812-
void *context)
834+
static struct cm_id_private *cm_alloc_id_priv(struct ib_device *device,
835+
ib_cm_handler cm_handler,
836+
void *context)
813837
{
814838
struct cm_id_private *cm_id_priv;
815839
u32 id;
@@ -840,15 +864,37 @@ struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
840864
if (ret)
841865
goto error;
842866
cm_id_priv->id.local_id = (__force __be32)id ^ cm.random_id_operand;
843-
xa_store_irq(&cm.local_id_table, cm_local_id(cm_id_priv->id.local_id),
844-
cm_id_priv, GFP_KERNEL);
845867

846-
return &cm_id_priv->id;
868+
return cm_id_priv;
847869

848870
error:
849871
kfree(cm_id_priv);
850872
return ERR_PTR(ret);
851873
}
874+
875+
/*
876+
* Make the ID visible to the MAD handlers and other threads that use the
877+
* xarray.
878+
*/
879+
static void cm_finalize_id(struct cm_id_private *cm_id_priv)
880+
{
881+
xa_store_irq(&cm.local_id_table, cm_local_id(cm_id_priv->id.local_id),
882+
cm_id_priv, GFP_KERNEL);
883+
}
884+
885+
struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
886+
ib_cm_handler cm_handler,
887+
void *context)
888+
{
889+
struct cm_id_private *cm_id_priv;
890+
891+
cm_id_priv = cm_alloc_id_priv(device, cm_handler, context);
892+
if (IS_ERR(cm_id_priv))
893+
return ERR_CAST(cm_id_priv);
894+
895+
cm_finalize_id(cm_id_priv);
896+
return &cm_id_priv->id;
897+
}
852898
EXPORT_SYMBOL(ib_create_cm_id);
853899

854900
static struct cm_work * cm_dequeue_work(struct cm_id_private *cm_id_priv)
@@ -1092,8 +1138,27 @@ void ib_destroy_cm_id(struct ib_cm_id *cm_id)
10921138
}
10931139
EXPORT_SYMBOL(ib_destroy_cm_id);
10941140

1141+
static int cm_init_listen(struct cm_id_private *cm_id_priv, __be64 service_id,
1142+
__be64 service_mask)
1143+
{
1144+
service_mask = service_mask ? service_mask : ~cpu_to_be64(0);
1145+
service_id &= service_mask;
1146+
if ((service_id & IB_SERVICE_ID_AGN_MASK) == IB_CM_ASSIGN_SERVICE_ID &&
1147+
(service_id != IB_CM_ASSIGN_SERVICE_ID))
1148+
return -EINVAL;
1149+
1150+
if (service_id == IB_CM_ASSIGN_SERVICE_ID) {
1151+
cm_id_priv->id.service_id = cpu_to_be64(cm.listen_service_id++);
1152+
cm_id_priv->id.service_mask = ~cpu_to_be64(0);
1153+
} else {
1154+
cm_id_priv->id.service_id = service_id;
1155+
cm_id_priv->id.service_mask = service_mask;
1156+
}
1157+
return 0;
1158+
}
1159+
10951160
/**
1096-
* __ib_cm_listen - Initiates listening on the specified service ID for
1161+
* ib_cm_listen - Initiates listening on the specified service ID for
10971162
* connection and service ID resolution requests.
10981163
* @cm_id: Connection identifier associated with the listen request.
10991164
* @service_id: Service identifier matched against incoming connection
@@ -1105,51 +1170,33 @@ EXPORT_SYMBOL(ib_destroy_cm_id);
11051170
* exactly. This parameter is ignored if %service_id is set to
11061171
* IB_CM_ASSIGN_SERVICE_ID.
11071172
*/
1108-
static int __ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id,
1109-
__be64 service_mask)
1173+
int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask)
11101174
{
1111-
struct cm_id_private *cm_id_priv, *cur_cm_id_priv;
1112-
int ret = 0;
1113-
1114-
service_mask = service_mask ? service_mask : ~cpu_to_be64(0);
1115-
service_id &= service_mask;
1116-
if ((service_id & IB_SERVICE_ID_AGN_MASK) == IB_CM_ASSIGN_SERVICE_ID &&
1117-
(service_id != IB_CM_ASSIGN_SERVICE_ID))
1118-
return -EINVAL;
1119-
1120-
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
1121-
if (cm_id->state != IB_CM_IDLE)
1122-
return -EINVAL;
1123-
1124-
cm_id->state = IB_CM_LISTEN;
1125-
++cm_id_priv->listen_sharecount;
1175+
struct cm_id_private *cm_id_priv =
1176+
container_of(cm_id, struct cm_id_private, id);
1177+
unsigned long flags;
1178+
int ret;
11261179

1127-
if (service_id == IB_CM_ASSIGN_SERVICE_ID) {
1128-
cm_id->service_id = cpu_to_be64(cm.listen_service_id++);
1129-
cm_id->service_mask = ~cpu_to_be64(0);
1130-
} else {
1131-
cm_id->service_id = service_id;
1132-
cm_id->service_mask = service_mask;
1180+
spin_lock_irqsave(&cm_id_priv->lock, flags);
1181+
if (cm_id_priv->id.state != IB_CM_IDLE) {
1182+
ret = -EINVAL;
1183+
goto out;
11331184
}
1134-
cur_cm_id_priv = cm_insert_listen(cm_id_priv);
11351185

1136-
if (cur_cm_id_priv) {
1137-
cm_id->state = IB_CM_IDLE;
1138-
--cm_id_priv->listen_sharecount;
1186+
ret = cm_init_listen(cm_id_priv, service_id, service_mask);
1187+
if (ret)
1188+
goto out;
1189+
1190+
if (!cm_insert_listen(cm_id_priv, NULL)) {
11391191
ret = -EBUSY;
1192+
goto out;
11401193
}
1141-
return ret;
1142-
}
11431194

1144-
int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask)
1145-
{
1146-
unsigned long flags;
1147-
int ret;
1148-
1149-
spin_lock_irqsave(&cm.lock, flags);
1150-
ret = __ib_cm_listen(cm_id, service_id, service_mask);
1151-
spin_unlock_irqrestore(&cm.lock, flags);
1195+
cm_id_priv->id.state = IB_CM_LISTEN;
1196+
ret = 0;
11521197

1198+
out:
1199+
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
11531200
return ret;
11541201
}
11551202
EXPORT_SYMBOL(ib_cm_listen);
@@ -1174,52 +1221,38 @@ struct ib_cm_id *ib_cm_insert_listen(struct ib_device *device,
11741221
ib_cm_handler cm_handler,
11751222
__be64 service_id)
11761223
{
1224+
struct cm_id_private *listen_id_priv;
11771225
struct cm_id_private *cm_id_priv;
1178-
struct ib_cm_id *cm_id;
1179-
unsigned long flags;
11801226
int err = 0;
11811227

11821228
/* Create an ID in advance, since the creation may sleep */
1183-
cm_id = ib_create_cm_id(device, cm_handler, NULL);
1184-
if (IS_ERR(cm_id))
1185-
return cm_id;
1186-
1187-
spin_lock_irqsave(&cm.lock, flags);
1229+
cm_id_priv = cm_alloc_id_priv(device, cm_handler, NULL);
1230+
if (IS_ERR(cm_id_priv))
1231+
return ERR_CAST(cm_id_priv);
11881232

1189-
if (service_id == IB_CM_ASSIGN_SERVICE_ID)
1190-
goto new_id;
1233+
err = cm_init_listen(cm_id_priv, service_id, 0);
1234+
if (err)
1235+
return ERR_PTR(err);
11911236

1192-
/* Find an existing ID */
1193-
cm_id_priv = cm_find_listen(device, service_id);
1194-
if (cm_id_priv) {
1195-
if (cm_id_priv->id.cm_handler != cm_handler ||
1196-
cm_id_priv->id.context) {
1197-
/* Sharing an ib_cm_id with different handlers is not
1198-
* supported */
1199-
spin_unlock_irqrestore(&cm.lock, flags);
1200-
ib_destroy_cm_id(cm_id);
1237+
spin_lock_irq(&cm_id_priv->lock);
1238+
listen_id_priv = cm_insert_listen(cm_id_priv, cm_handler);
1239+
if (listen_id_priv != cm_id_priv) {
1240+
spin_unlock_irq(&cm_id_priv->lock);
1241+
ib_destroy_cm_id(&cm_id_priv->id);
1242+
if (!listen_id_priv)
12011243
return ERR_PTR(-EINVAL);
1202-
}
1203-
refcount_inc(&cm_id_priv->refcount);
1204-
++cm_id_priv->listen_sharecount;
1205-
spin_unlock_irqrestore(&cm.lock, flags);
1206-
1207-
ib_destroy_cm_id(cm_id);
1208-
cm_id = &cm_id_priv->id;
1209-
return cm_id;
1244+
return &listen_id_priv->id;
12101245
}
1246+
cm_id_priv->id.state = IB_CM_LISTEN;
1247+
spin_unlock_irq(&cm_id_priv->lock);
12111248

1212-
new_id:
1213-
/* Use newly created ID */
1214-
err = __ib_cm_listen(cm_id, service_id, 0);
1215-
1216-
spin_unlock_irqrestore(&cm.lock, flags);
1249+
/*
1250+
* A listen ID does not need to be in the xarray since it does not
1251+
* receive mads, is not placed in the remote_id or remote_qpn rbtree,
1252+
* and does not enter timewait.
1253+
*/
12171254

1218-
if (err) {
1219-
ib_destroy_cm_id(cm_id);
1220-
return ERR_PTR(err);
1221-
}
1222-
return cm_id;
1255+
return &cm_id_priv->id;
12231256
}
12241257
EXPORT_SYMBOL(ib_cm_insert_listen);
12251258

0 commit comments

Comments
 (0)