IB/core: Fix mgid key handling in SA agent multicast data-base
authorJack Morgenstein <jackm@dev.mellanox.co.il>
Wed, 19 Nov 2014 09:08:58 +0000 (11:08 +0200)
committerRoland Dreier <roland@purestorage.com>
Tue, 16 Dec 2014 02:10:13 +0000 (18:10 -0800)
Applications can request that the SM assign an MGID by passing a mcast
member request containing MGID = 0. When the SM responds by sending
the allocated MGID, this MGID replaces the 0-MGID in the multicast group.

However, the MGID field in the group is also the key field in the IB
core multicast code rbtree containing the multicast groups for the
port.

Since this is a key field, correct handling requires that the group
entry be deleted from the rbtree and then re-inserted with the new
key, so that the table structure is properly maintained.

The current code does not do this correctly.  Correct operation
requires that if the key-field gid has changed at all, it should be
deleted and re-inserted.

Note that when inserting, if the new MGID is zero (not the case here
but the code should handle this correctly), we allow duplicate entries
for 0-MGIDs.

Signed-off-by: Jack Morgenstein <jackm@dev.mellanox.co.il>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Acked-by: Sean Hefty <sean.hefty@intel.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
drivers/infiniband/core/multicast.c

index d2360a8..fa17b55 100644 (file)
@@ -525,17 +525,22 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
        if (status)
                process_join_error(group, status);
        else {
+               int mgids_changed, is_mgid0;
                ib_find_pkey(group->port->dev->device, group->port->port_num,
                             be16_to_cpu(rec->pkey), &pkey_index);
 
                spin_lock_irq(&group->port->lock);
-               group->rec = *rec;
                if (group->state == MCAST_BUSY &&
                    group->pkey_index == MCAST_INVALID_PKEY_INDEX)
                        group->pkey_index = pkey_index;
-               if (!memcmp(&mgid0, &group->rec.mgid, sizeof mgid0)) {
+               mgids_changed = memcmp(&rec->mgid, &group->rec.mgid,
+                                      sizeof(group->rec.mgid));
+               group->rec = *rec;
+               if (mgids_changed) {
                        rb_erase(&group->node, &group->port->table);
-                       mcast_insert(group->port, group, 1);
+                       is_mgid0 = !memcmp(&mgid0, &group->rec.mgid,
+                                          sizeof(mgid0));
+                       mcast_insert(group->port, group, is_mgid0);
                }
                spin_unlock_irq(&group->port->lock);
        }